From 38cd052bb03dc1a03f5d3e60b08e0fd6f70d342d Mon Sep 17 00:00:00 2001 From: David Vierra Date: Fri, 26 Oct 2012 17:24:21 -1000 Subject: [PATCH] Refactor: Reorder members and methods of MCInfdevOldLevel to place related methods closer together. Change version to use TagProperty. Remove some backward compatibility from MCServerChunkGenerator Add # --- Subheaders --- to mark related method groups Fix whitespace according to pep8 --- infiniteworld.py | 447 ++++++++++++++++++++++---------------------- minecraft_server.py | 1 - 2 files changed, 220 insertions(+), 228 deletions(-) diff --git a/infiniteworld.py b/infiniteworld.py index 88052f9..98bb471 100644 --- a/infiniteworld.py +++ b/infiniteworld.py @@ -10,6 +10,7 @@ import itertools from logging import getLogger from math import floor import os +import re import random import time import traceback @@ -39,6 +40,7 @@ DIM_END = 1 __all__ = ["ZeroChunk", "AnvilChunk", "ChunkedLevelMixin", "MCInfdevOldLevel", "MCAlphaDimension", "ZipSchematic"] _zeros = {} + def ZeroChunk(height=512): z = _zeros.get(height) if z is None: @@ -46,7 +48,6 @@ def ZeroChunk(height=512): return z - class _ZeroChunk(ChunkBase): " a placebo for neighboring-chunk routines " @@ -86,7 +87,6 @@ class AnvilChunk(LightedChunk): for better handling. """ @property - def filename(self): cx, cz = self.chunkPosition rx, rz = cx >> 5, cz >> 5 @@ -99,7 +99,6 @@ class AnvilChunk(LightedChunk): index=4 * ((cx & 0x1f) + ((cz & 0x1f) * 32)) ) - root_tag = None def __init__(self, world, chunkPosition, create=False): @@ -114,13 +113,11 @@ class AnvilChunk(LightedChunk): self.SkyLight = zeros((16, 16, self.Height), 'uint8') self.SkyLight[:] = 15 - if create: self._create() else: self._load() - def _create(self): (cx, cz) = self.chunkPosition chunkTag = nbt.TAG_Compound() @@ -162,7 +159,6 @@ class AnvilChunk(LightedChunk): arr[..., y:y + 16] = secarray.swapaxes(0, 2) - def save(self): """ does not recalculate any data or light """ @@ -213,7 +209,6 @@ class AnvilChunk(LightedChunk): self.Blocks[:, :, 1:][badsnow] = self.materials.Air.ID - def __str__(self): return u"AnvilChunk, coords:{0}, world: {1}, D:{2}, L:{3}".format(self.chunkPosition, self.world.displayName, self.dirty, self.needsLighting) @@ -234,7 +229,6 @@ class AnvilChunk(LightedChunk): else: computeChunkHeightMap(self.materials, self.Blocks, self.HeightMap) - def addEntity(self, entityTag): def doubleize(name): @@ -256,7 +250,6 @@ class AnvilChunk(LightedChunk): self.dirty = True return super(AnvilChunk, self).removeTileEntitiesInBox(box) - @property def HeightMap(self): return self.root_tag["Level"]["HeightMap"].value.reshape((16, 16)) @@ -315,6 +308,7 @@ def deflate(data): # return zdata return zlib.compress(data) + def inflate(data): return zlib.decompress(data) @@ -532,7 +526,6 @@ class ChunkedLevelMixin(MCLevel): if i: info("Finished {2} chunks in {0} ({1} per chunk)".format(d, d / i, i)) - def copyBlocksFromInfiniteIter(self, sourceLevel, sourceBox, destinationPoint, blocksToCopy, create=False): """ copy blocks between two infinite levels by looping through the destination's chunks. make a sub-box of the source level for each chunk @@ -846,7 +839,6 @@ class ChunkedLevelMixin(MCLevel): progressInfo = u"{0} Pass {1}: {2} chunks".format(light, i, len(newDirtyChunks)) info(progressInfo) - # propagate light! # for each of the six cardinal directions, figure a new light value for # adjoining blocks by reducing this chunk's light by light absorption and fall off. @@ -872,9 +864,9 @@ class ChunkedLevelMixin(MCLevel): neighboringChunks = {} for dir, dx, dz in ((FaceXDecreasing, -1, 0), - (FaceXIncreasing, 1, 0), - (FaceZDecreasing, 0, -1), - (FaceZIncreasing, 0, 1)): + (FaceXIncreasing, 1, 0), + (FaceZDecreasing, 0, -1), + (FaceZIncreasing, 0, 1)): try: neighboringChunks[dir] = self.getChunk(cx + dx, cz + dz) except (ChunkNotPresent, ChunkMalformed): @@ -1027,6 +1019,7 @@ class ChunkedLevelMixin(MCLevel): for ch in startingDirtyChunks: ch.needsLighting = False + def TagProperty(tagName, tagType, defaultValueFunc=lambda self: None): def getter(self): if tagName not in self.root_tag["Data"]: @@ -1038,152 +1031,8 @@ def TagProperty(tagName, tagType, defaultValueFunc=lambda self: None): return property(getter, setter) + class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel): - materials = alphaMaterials - isInfinite = True - parentWorld = None - dimNo = 0 - Height = 256 - - @property - def displayName(self): - # shortname = os.path.basename(self.filename) - # if shortname == "level.dat": - shortname = os.path.basename(os.path.dirname(self.filename)) - - return shortname - - @classmethod - def _isLevel(cls, filename): - join = os.path.join - exists = os.path.exists - - if exists(join(filename, "chunks.dat")): - return False # exclude Pocket Edition folders - - if not os.path.isdir(filename): - f = os.path.basename(filename) - if f not in ("level.dat", "level.dat_old"): - return False - filename = os.path.dirname(filename) - - files = os.listdir(filename) - if "level.dat" in files or "level.dat_old" in files: - return True - - return False - - def getWorldBounds(self): - if self.chunkCount == 0: - return BoundingBox((0, 0, 0), (0, 0, 0)) - - allChunks = array(list(self.allChunks)) - mincx = (allChunks[:, 0]).min() - maxcx = (allChunks[:, 0]).max() - mincz = (allChunks[:, 1]).min() - maxcz = (allChunks[:, 1]).max() - - origin = (mincx << 4, 0, mincz << 4) - size = ((maxcx - mincx + 1) << 4, self.Height, (maxcz - mincz + 1) << 4) - - return BoundingBox(origin, size) - - def __str__(self): - return "MCInfdevOldLevel(\"" + os.path.split(self.worldDir)[1] + "\")" - - - - SizeOnDisk = TagProperty('SizeOnDisk', nbt.TAG_Long) - RandomSeed = TagProperty('RandomSeed', nbt.TAG_Long) - Time = TagProperty('Time', nbt.TAG_Long) # Age of the world in ticks. 20 ticks per second; 24000 ticks per day. - LastPlayed = TagProperty('LastPlayed', nbt.TAG_Long, lambda self: long(time.time() * 1000)) - - LevelName = TagProperty('LevelName', nbt.TAG_String, lambda self: self.displayName) - - MapFeatures = TagProperty('MapFeatures', nbt.TAG_Byte, lambda self: 1) - - GameType = TagProperty('GameType', nbt.TAG_Int, lambda self: 0) # 0 for survival, 1 for creative - GAMETYPE_SURVIVAL = 0 - GAMETYPE_CREATIVE = 1 - - VERSION_MCR = 19132 - VERSION_ANVIL = 19133 - - _bounds = None - - @property - def bounds(self): - if self._bounds is None: - self._bounds = self.getWorldBounds() - return self._bounds - - @property - def size(self): - return self.bounds.size - - def close(self): - for rf in (self.regionFiles or {}).values(): - rf.close() - - self.regionFiles = {} - - self._allChunks = None - self._loadedChunks = {} - - def _create(self, filename, random_seed, last_played): - - # create a new level - root_tag = nbt.TAG_Compound() - root_tag["Data"] = nbt.TAG_Compound() - root_tag["Data"]["SpawnX"] = nbt.TAG_Int(0) - root_tag["Data"]["SpawnY"] = nbt.TAG_Int(2) - root_tag["Data"]["SpawnZ"] = nbt.TAG_Int(0) - - if last_played is None: - last_played = long(time.time() * 1000) - if random_seed is None: - random_seed = long(random.random() * 0xffffffffffffffffL) - 0x8000000000000000L - - self.root_tag = root_tag - root_tag["Data"]['version'] = nbt.TAG_Int(self.VERSION_ANVIL) - - self.LastPlayed = long(last_played) - self.RandomSeed = long(random_seed) - self.SizeOnDisk = 0 - self.Time = 1 - self.LevelName = os.path.basename(self.worldDir) - - ### if singleplayer: - - self.createPlayer("Player") - - if not os.path.exists(self.worldDir): - os.mkdir(self.worldDir) - - def createPlayer(self, playerName): - if playerName == "Player": - playerTag = self.root_tag["Data"].setdefault(playerName, nbt.TAG_Compound()) - else: - playerTag = nbt.TAG_Compound() - - playerTag['Air'] = nbt.TAG_Short(300) - playerTag['AttackTime'] = nbt.TAG_Short(0) - playerTag['DeathTime'] = nbt.TAG_Short(0) - playerTag['Fire'] = nbt.TAG_Short(-20) - playerTag['Health'] = nbt.TAG_Short(20) - playerTag['HurtTime'] = nbt.TAG_Short(0) - playerTag['Score'] = nbt.TAG_Int(0) - playerTag['FallDistance'] = nbt.TAG_Float(0) - playerTag['OnGround'] = nbt.TAG_Byte(0) - - playerTag["Inventory"] = nbt.TAG_List() - - playerTag['Motion'] = nbt.TAG_List([nbt.TAG_Double(0) for i in range(3)]) - playerTag['Pos'] = nbt.TAG_List([nbt.TAG_Double([0.5, 2.8, 0.5][i]) for i in range(3)]) - playerTag['Rotation'] = nbt.TAG_List([nbt.TAG_Float(0), nbt.TAG_Float(0)]) - - if playerName != "Player": - playerTag.save(self.getPlayerPath(playerName)) def __init__(self, filename=None, create=False, random_seed=None, last_played=None): """ @@ -1250,7 +1099,38 @@ class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel): self.players.append("Player") self.preloadDimensions() - # self.preloadChunkPositions() + + # --- Load, save, create --- + + def _create(self, filename, random_seed, last_played): + + # create a new level + root_tag = nbt.TAG_Compound() + root_tag["Data"] = nbt.TAG_Compound() + root_tag["Data"]["SpawnX"] = nbt.TAG_Int(0) + root_tag["Data"]["SpawnY"] = nbt.TAG_Int(2) + root_tag["Data"]["SpawnZ"] = nbt.TAG_Int(0) + + if last_played is None: + last_played = long(time.time() * 1000) + if random_seed is None: + random_seed = long(random.random() * 0xffffffffffffffffL) - 0x8000000000000000L + + self.root_tag = root_tag + root_tag["Data"]['version'] = nbt.TAG_Int(self.VERSION_ANVIL) + + self.LastPlayed = long(last_played) + self.RandomSeed = long(random_seed) + self.SizeOnDisk = 0 + self.Time = 1 + self.LevelName = os.path.basename(self.worldDir) + + ### if singleplayer: + + self.createPlayer("Player") + + if not os.path.exists(self.worldDir): + os.mkdir(self.worldDir) def loadLevelDat(self, create=False, random_seed=None, last_played=None): @@ -1273,6 +1153,128 @@ class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel): info("Error loading level.dat_old. Initializing with defaults.") self._create(self.filename, random_seed, last_played) + def saveInPlace(self): + for level in self.dimensions.itervalues(): + level.saveInPlace(True) + + dirtyChunkCount = 0 + if self._loadedChunks: + for chunk in self._loadedChunks.itervalues(): + if chunk.dirty: + dirtyChunkCount += 1 + chunk.save() + + for path, tag in self.playerTagCache.iteritems(): + tag.save(path) + + self.playerTagCache = {} + + self.root_tag.save(self.filename) + info(u"Saved {0} chunks".format(dirtyChunkCount)) + + def close(self): + for rf in (self.regionFiles or {}).values(): + rf.close() + + self.regionFiles = {} + + self._allChunks = None + self._loadedChunks = {} + + # --- Constants --- + + GAMETYPE_SURVIVAL = 0 + GAMETYPE_CREATIVE = 1 + + VERSION_MCR = 19132 + VERSION_ANVIL = 19133 + + # --- Instance variables --- + + materials = alphaMaterials + isInfinite = True + parentWorld = None + dimNo = 0 + Height = 256 + _bounds = None + + # --- NBT Tag variables --- + + SizeOnDisk = TagProperty('SizeOnDisk', nbt.TAG_Long) + RandomSeed = TagProperty('RandomSeed', nbt.TAG_Long) + Time = TagProperty('Time', nbt.TAG_Long) # Age of the world in ticks. 20 ticks per second; 24000 ticks per day. + LastPlayed = TagProperty('LastPlayed', nbt.TAG_Long, lambda self: long(time.time() * 1000)) + + LevelName = TagProperty('LevelName', nbt.TAG_String, lambda self: self.displayName) + + MapFeatures = TagProperty('MapFeatures', nbt.TAG_Byte, lambda self: 1) + + GameType = TagProperty('GameType', nbt.TAG_Int, lambda self: 0) # 0 for survival, 1 for creative + + version = TagProperty('version', nbt.TAG_Int, lambda s: MCInfdevOldLevel.VERSION_ANVIL) + + # --- World info --- + + def __str__(self): + return "MCInfdevOldLevel(\"" + os.path.split(self.worldDir)[1] + "\")" + + @property + def displayName(self): + # shortname = os.path.basename(self.filename) + # if shortname == "level.dat": + shortname = os.path.basename(os.path.dirname(self.filename)) + + return shortname + + @property + def bounds(self): + if self._bounds is None: + self._bounds = self.getWorldBounds() + return self._bounds + + def getWorldBounds(self): + if self.chunkCount == 0: + return BoundingBox((0, 0, 0), (0, 0, 0)) + + allChunks = array(list(self.allChunks)) + mincx = (allChunks[:, 0]).min() + maxcx = (allChunks[:, 0]).max() + mincz = (allChunks[:, 1]).min() + maxcz = (allChunks[:, 1]).max() + + origin = (mincx << 4, 0, mincz << 4) + size = ((maxcx - mincx + 1) << 4, self.Height, (maxcz - mincz + 1) << 4) + + return BoundingBox(origin, size) + + @property + def size(self): + return self.bounds.size + + # --- Format detection --- + + @classmethod + def _isLevel(cls, filename): + join = os.path.join + exists = os.path.exists + + if exists(join(filename, "chunks.dat")): + return False # exclude Pocket Edition folders + + if not os.path.isdir(filename): + f = os.path.basename(filename) + if f not in ("level.dat", "level.dat_old"): + return False + filename = os.path.dirname(filename) + + files = os.listdir(filename) + if "level.dat" in files or "level.dat_old" in files: + return True + + return False + + # --- Dimensions --- + def preloadDimensions(self): worldDirs = os.listdir(self.worldDir) @@ -1280,10 +1282,10 @@ class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel): if dirname.startswith("DIM"): try: intInDirname = re.findall("\\d+", dirname) - if len(intInDirname)>0: + if len(intInDirname) > 0: dimNo = int(intInDirname[-1]) else: - dimNo = 999 #identical dimNo should not matter + dimNo = 999 # identical dimNo should not matter info("Found dimension {0}".format(dirname)) dim = MCAlphaDimension(self, dimNo, dirname) self.dimensions[dirname] = dim @@ -1292,7 +1294,7 @@ class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel): def getDimension(self, dimNo, dirname=None): if dirname is None: - dirname="DIM" + str(int(dimNo)) + dirname = "DIM" + str(int(dimNo)) if self.dimNo != 0: return self.parentWorld.getDimension(dimNo, dirname) @@ -1307,15 +1309,11 @@ class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel): self.dimensions[dirname] = dim return dim - def getRegionForChunk(self, cx, cz): - rx = cx >> 5 - rz = cz >> 5 - return self.getRegionFile(rx, rz) + # --- Region I/O --- def preloadChunkPositions(self): self.preloadRegions() - def findRegionFiles(self): regionDir = os.path.join(self.worldDir, "region") if not os.path.exists(regionDir): @@ -1353,6 +1351,11 @@ class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel): self.regionFiles[rx, rz] = regionFile return regionFile + def getRegionForChunk(self, cx, cz): + rx = cx >> 5 + rz = cz >> 5 + return self.getRegionFile(rx, rz) + def unloadRegions(self): self.close() @@ -1383,21 +1386,7 @@ class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel): regionFile.close() os.unlink(regionFile.path) - @property - def version(self): - if 'version' in self.root_tag['Data']: - return self.root_tag['Data']['version'].value - else: - return None - - @version.setter - def version(self, val): - if 'version' in self.root_tag['Data']: - self.root_tag['Data']['version'].value = val - - @version.deleter - def version(self): - self.root_tag['Data'].pop('version') + # --- Chunk I/O --- def _loadChunk(self, cx, cz): """ load the chunk data from disk, and return its root tag as an NBT_Compound""" @@ -1437,14 +1426,9 @@ class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel): def chunkFilename(self, cx, cz): s = os.path.join(self.worldDir, self.dirhash(cx), self.dirhash(cz), - "c.%s.%s.dat" % (base36(cx), base36(cz))) + "c.%s.%s.dat" % (base36(cx), base36(cz))) return s - def chunkFilenameAt(self, x, y, z): - cx = x >> 4 - cz = z >> 4 - return self._loadedChunks.get((cx, cz)).filename - def extractChunksInBox(self, box, parentFolder): for cx, cz in box.chunkPositions: if self.containsChunk(cx, cz): @@ -1462,19 +1446,6 @@ class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel): with file(outputFile, "wb") as f: chunk.root_tag.save(buf=f) - def heightMapAt(self, x, z): - zc = z >> 4 - xc = x >> 4 - xInChunk = x & 0xf - zInChunk = z & 0xf - - ch = self.getChunk(xc, zc) - - heightMap = ch.HeightMap - - return heightMap[zInChunk, xInChunk] - # the heightmap is ordered differently because in minecraft it is a flat array - @property def chunkCount(self): """Returns the number of chunks in the level. May initiate a costly @@ -1491,13 +1462,12 @@ class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel): self.preloadChunkPositions() return self._allChunks.__iter__() - def getChunk(self, cx, cz): """ read the chunk from disk, load it, and return it.""" chunk = self._loadedChunks.get((cx, cz)) if chunk is None: - chunk = self.chunkClass(self, (cx, cz)) + chunk = self.chunkClass(self, (cx, cz)) self._loadedChunks[cx, cz] = chunk return chunk @@ -1511,24 +1481,21 @@ class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel): for cx, cz in box.chunkPositions: self.markDirtyChunk(cx, cz) - def saveInPlace(self): - for level in self.dimensions.itervalues(): - level.saveInPlace(True) + # --- HeightMaps --- - dirtyChunkCount = 0 - if self._loadedChunks: - for chunk in self._loadedChunks.itervalues(): - if chunk.dirty: - dirtyChunkCount += 1 - chunk.save() + def heightMapAt(self, x, z): + zc = z >> 4 + xc = x >> 4 + xInChunk = x & 0xf + zInChunk = z & 0xf - for path, tag in self.playerTagCache.iteritems(): - tag.save(path) + ch = self.getChunk(xc, zc) - self.playerTagCache = {} + heightMap = ch.HeightMap - self.root_tag.save(self.filename) - info(u"Saved {0} chunks".format(dirtyChunkCount)) + return heightMap[zInChunk, xInChunk] # HeightMap indices are backwards + + # --- Entities and TileEntities --- def addEntity(self, entityTag): assert isinstance(entityTag, nbt.TAG_Compound) @@ -1583,10 +1550,7 @@ class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel): info("Removed {0} tile entities".format(count)) return count - def containsPoint(self, x, y, z): - if y < 0 or y > 127: - return False - return self.containsChunk(x >> 4, z >> 4) + # --- Chunk manipulation --- def containsChunk(self, cx, cz): if self._allChunks is not None: @@ -1601,6 +1565,11 @@ class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel): return self.getRegionFile(rx, rz).containsChunk(cx, cz) + def containsPoint(self, x, y, z): + if y < 0 or y > 127: + return False + return self.containsChunk(x >> 4, z >> 4) + def createChunk(self, cx, cz): if self.containsChunk(cx, cz): raise ValueError("{0}:Chunk {1} already present!".format(self, (cx, cz))) @@ -1670,7 +1639,7 @@ class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel): return ret - spawnxyz = ["SpawnX", "SpawnY", "SpawnZ"] + # --- Player and spawn manipulation --- def playerSpawnPosition(self, player=None): """ @@ -1683,7 +1652,7 @@ class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel): else: playerSpawnTag = self.getPlayerTag(player) - return [playerSpawnTag.get(i, dataTag[i]).value for i in self.spawnxyz] + return [playerSpawnTag.get(i, dataTag[i]).value for i in ("SpawnX", "SpawnY", "SpawnZ")] def setPlayerSpawnPosition(self, pos, player=None): """ xxx if player is None then it sets the default spawn position for the world """ @@ -1691,7 +1660,7 @@ class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel): playerSpawnTag = self.root_tag["Data"] else: playerSpawnTag = self.getPlayerTag(player) - for name, val in zip(self.spawnxyz, pos): + for name, val in zip(("SpawnX", "SpawnY", "SpawnZ"), pos): playerSpawnTag[name] = nbt.TAG_Int(val) def getPlayerPath(self, player): @@ -1795,6 +1764,31 @@ class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel): playerTag = self.getPlayerTag(player) return playerTag["playerGameType"].value + def createPlayer(self, playerName): + if playerName == "Player": + playerTag = self.root_tag["Data"].setdefault(playerName, nbt.TAG_Compound()) + else: + playerTag = nbt.TAG_Compound() + + playerTag['Air'] = nbt.TAG_Short(300) + playerTag['AttackTime'] = nbt.TAG_Short(0) + playerTag['DeathTime'] = nbt.TAG_Short(0) + playerTag['Fire'] = nbt.TAG_Short(-20) + playerTag['Health'] = nbt.TAG_Short(20) + playerTag['HurtTime'] = nbt.TAG_Short(0) + playerTag['Score'] = nbt.TAG_Int(0) + playerTag['FallDistance'] = nbt.TAG_Float(0) + playerTag['OnGround'] = nbt.TAG_Byte(0) + + playerTag["Inventory"] = nbt.TAG_List() + + playerTag['Motion'] = nbt.TAG_List([nbt.TAG_Double(0) for i in range(3)]) + playerTag['Pos'] = nbt.TAG_List([nbt.TAG_Double([0.5, 2.8, 0.5][i]) for i in range(3)]) + playerTag['Rotation'] = nbt.TAG_List([nbt.TAG_Float(0), nbt.TAG_Float(0)]) + + if playerName != "Player": + playerTag.save(self.getPlayerPath(playerName)) + class MCAlphaDimension (MCInfdevOldLevel): def __init__(self, parentWorld, dimNo, dirname, create=False): @@ -1874,7 +1868,6 @@ class ZipSchematic (MCInfdevOldLevel): self.Height = 128 self.Length = 0 - def close(self): MCInfdevOldLevel.close(self) self.zipfile.close() @@ -1920,5 +1913,5 @@ class ZipSchematic (MCInfdevOldLevel): def chunkFilename(self, x, z): s = "/".join((self.dirhash(x), self.dirhash(z), - "c.%s.%s.dat" % (base36(x), base36(z)))) + "c.%s.%s.dat" % (base36(x), base36(z)))) return s diff --git a/minecraft_server.py b/minecraft_server.py index 7ec8eb2..337f034 100644 --- a/minecraft_server.py +++ b/minecraft_server.py @@ -315,7 +315,6 @@ class MCServerChunkGenerator(object): tempWorldDir = os.path.join(tempDir, worldName) tempWorld = infiniteworld.MCInfdevOldLevel(tempWorldDir, create=True, random_seed=level.RandomSeed) - del tempWorld.version # for compatibility with older servers. newer ones will set it again without issue. self.tempWorldCache[self.serverVersion, level.RandomSeed] = tempWorld