def position_look(self, container): oldx, chaff, oldz, chaff = split_coords(self.player.location.x, self.player.location.z) self.player.location.load_from_packet(container) pos = (self.player.location.x, self.player.location.y, self.player.location.z) x, chaff, z, chaff = split_coords(pos[0], pos[2]) if oldx != x or oldz != z: self.update_chunks() for entity in self.factory.entities_near(pos[0] * 32, self.player.location.y * 32, pos[2] * 32, 64): packet = make_packet("pickup", type=entity.entity_type, quantity=entity.quantity, wear=0) self.transport.write(packet) packet = make_packet("destroy", id=entity.id) self.transport.write(packet) self.factory.destroy_entity(entity)
def digging(self, container): if container.state != 3: return bigx, smallx, bigz, smallz = split_coords(container.x, container.z) try: chunk = self.chunks[bigx, bigz] except KeyError: self.error("Couldn't dig in chunk (%d, %d)!" % (bigx, bigz)) return # XXX something to consider: All of this handler's functionality is # based on the chunk, the coords, and the block type, which means that # it could all be implemented as IDigHooks. Hmm.... oldblock = chunk.get_block((smallx, container.y, smallz)) newblock = blocks[oldblock].replace chunk.set_block((smallx, container.y, smallz), newblock) dropblock = blocks[oldblock].drop if dropblock != 0: coords = (container.x * 32 + 16, container.y * 32, container.z * 32 + 16) self.factory.give(coords, dropblock, 1) for hook in self.dig_hooks: hook.dig_hook(chunk, smallx, container.y, smallz, oldblock) if chunk.is_damaged(): packet = chunk.get_damage_packet() self.factory.broadcast_for_chunk(packet, bigx, bigz) chunk.clear_damage()
def send_initial_chunk_and_location(self): bigx, smallx, bigz, smallz = split_coords(self.player.location.x, self.player.location.z) self.enable_chunk(bigx, bigz) chunk = self.chunks[bigx, bigz] # This may not play well with recent Alpha clients, which have an # unfortunate bug at maximum heights. We have yet to ascertain whether # the bug is server-side or client-side. height = chunk.height_at(smallx, smallz) + 2 self.player.location.y = height packet = self.player.location.save_to_packet() self.transport.write(packet)
def build(self, container): # Ignore clients that think -1 is placeable. if container.block == 65535: return x = container.x y = container.y z = container.z # Special case when face is -1: Update the status of the currently # held block rather than placing a new block. if container.face == -1: return # Offset coords according to face. if container.face == 0: y -= 1 elif container.face == 1: y += 1 elif container.face == 2: z -= 1 elif container.face == 3: z += 1 elif container.face == 4: x -= 1 elif container.face == 5: x += 1 bigx, smallx, bigz, smallz = split_coords(x, z) try: chunk = self.chunks[bigx, bigz] except KeyError: self.error("Couldn't build in chunk (%d, %d)!" % (bigx, bigz)) return chunk.set_block((smallx, y, smallz), container.block) if chunk.is_damaged(): packet = chunk.get_damage_packet() self.factory.broadcast_for_chunk(packet, bigx, bigz) chunk.clear_damage()
def update_chunks(self): print "Sending chunks..." x, chaff, z, chaff = split_coords(self.player.location.x, self.player.location.z) new = set(product(xrange(x - 10, x + 10), xrange(z - 10, z + 10))) old = set(self.chunks.iterkeys()) added = new - old discarded = old - new # Perhaps some explanation is in order. # The generator expressions are stored in the protocol instance. If we # need to cancel them, we can call their close() method, which causes # them to become inert. This is incredibly important because we want # to cancel all previously pending chunk changes when a new set of # chunk changes is requested. # The coiterate() function iterates over the iterable it is fed, # without tying up the reactor, by yielding after each iteration. The # inner part of the generator expression generates all of the chunks # around the currently needed chunk, and it sorts them by distance to # the current chunk. The end result is that we load chunks one-by-one, # nearest to furthest, without stalling other clients. if self.chunk_generators: for generator in self.chunk_generators: generator.close() self.chunk_generators = [ ( self.enable_chunk(i, j) for i, j in sorted(added, key=lambda t: (t[0] - x)**2 + (t[1] - z)**2) ), (self.disable_chunk(i, j) for i, j in discarded) ] for generator in self.chunk_generators: coiterate(generator)