def __init__(self, folder): """ Load a world from disk. :Parameters: folder : str The directory containing the world. """ self.folder = FilePath(folder) if not self.folder.exists(): self.folder.makedirs() self.chunk_cache = weakref.WeakValueDictionary() self.dirty_chunk_cache = dict() self._pending_chunks = dict() self.spawn = (0, 0, 0) self.seed = random.randint(0, sys.maxint) level = self.folder.child("level%s" % extension()) if level.exists() and level.getsize(): self.load_from_tag(read_from_file(level.open("r"))) write_to_file(self.save_to_tag(), level.open("w")) self.chunk_management_loop = LoopingCall(self.sort_chunks) self.chunk_management_loop.start(1)
def load_chunk(self, x, z): """ Retrieve a ``Chunk`` synchronously. This method does lots of automatic caching of chunks to ensure that disk I/O is kept to a minimum. """ if (x, z) in self.chunk_cache: return self.chunk_cache[x, z] elif (x, z) in self.dirty_chunk_cache: return self.dirty_chunk_cache[x, z] chunk = Chunk(x, z) first, second, filename = names_for_chunk(x, z) f = self.folder.child(first).child(second) if not f.exists(): f.makedirs() f = f.child(filename) if f.exists() and f.getsize(): chunk.load_from_tag(read_from_file(f.open("r"))) if chunk.populated: self.chunk_cache[x, z] = chunk else: self.populate_chunk(chunk) chunk.populated = True chunk.dirty = True self.dirty_chunk_cache[x, z] = chunk # Apply the current season to the chunk. if self.season: self.season.transform(chunk) # Since this chunk hasn't been given to any player yet, there's no # conceivable way that any meaningful damage has been accumulated; # anybody loading any part of this chunk will want the entire thing. # Thus, it should start out undamaged. chunk.clear_damage() return chunk
def load_player(self, username): """ Retrieve player data. """ player = self.factory.create_entity(self.spawn[0], self.spawn[1], self.spawn[2], "Player", username=username) player.location.stance = self.spawn[1] player.username = username f = self.folder.child("players") if not f.exists(): f.makedirs() f = f.child("%s%s" % (username, extension())) if f.exists() and f.getsize(): player.load_from_tag(read_from_file(f.open("r"))) return player
def request_chunk(self, x, z): """ Request a ``Chunk`` to be delivered later. :returns: Deferred that will be called with the Chunk """ if not async: return deferLater(reactor, 0.000001, self.factory.world.load_chunk, x, z) if (x, z) in self.chunk_cache: return succeed(self.chunk_cache[x, z]) elif (x, z) in self.dirty_chunk_cache: return succeed(self.dirty_chunk_cache[x, z]) elif (x, z) in self._pending_chunks: # Rig up another Deferred and wrap it up in a to-go box. d = Deferred() self._pending_chunks[x, z].chainDeferred(d) return d chunk = Chunk(x, z) first, second, filename = names_for_chunk(x, z) f = self.folder.child(first).child(second) if not f.exists(): f.makedirs() f = f.child(filename) if f.exists() and f.getsize(): chunk.load_from_tag(read_from_file(f.open("r"))) if chunk.populated: self.chunk_cache[x, z] = chunk return succeed(chunk) d = deferToAMPProcess(MakeChunk, x=x, z=z, seed=self.seed, generators=configuration.getlist("bravo", "generators")) self._pending_chunks[x, z] = d def pp(kwargs): chunk.blocks = fromstring(kwargs["blocks"], dtype=uint8).reshape(chunk.blocks.shape) chunk.heightmap = fromstring(kwargs["heightmap"], dtype=uint8).reshape(chunk.heightmap.shape) chunk.metadata = fromstring(kwargs["metadata"], dtype=uint8).reshape(chunk.metadata.shape) chunk.skylight = fromstring(kwargs["skylight"], dtype=uint8).reshape(chunk.skylight.shape) chunk.blocklight = fromstring(kwargs["blocklight"], dtype=uint8).reshape(chunk.blocklight.shape) chunk.populated = True chunk.dirty = True # Apply the current season to the chunk. if self.season: self.season.transform(chunk) # Since this chunk hasn't been given to any player yet, there's no # conceivable way that any meaningful damage has been accumulated; # anybody loading any part of this chunk will want the entire thing. # Thus, it should start out undamaged. chunk.clear_damage() self.dirty_chunk_cache[x, z] = chunk del self._pending_chunks[x, z] return chunk # Set up callbacks. d.addCallback(pp) # Multiple people might be subscribed to this pending callback. We're # going to keep it for ourselves and fork off another Deferred for our # caller. forked = Deferred() d.chainDeferred(forked) forked.addCallback(lambda none: chunk) return forked