From 225f813d2871b1de8245a19db2714398f7f0b33c Mon Sep 17 00:00:00 2001 From: David Vierra Date: Sat, 23 Jul 2011 21:59:18 -1000 Subject: [PATCH] put everything through pydev's code auto-formatter --- blockrotation.py | 68 ++--- entity.py | 18 +- indev.py | 160 +++++------ items.py | 34 +-- level.py | 636 ++++++++++++++++++++--------------------- materials.py | 576 ++++++++++++++++++------------------- mce.py | 584 ++++++++++++++++++------------------- mclevel.py | 86 +++--- mclevelbase.py | 4 +- nbt.py | 248 ++++++++-------- run_regression_test.py | 40 +-- schematic.py | 1 + 12 files changed, 1228 insertions(+), 1227 deletions(-) diff --git a/blockrotation.py b/blockrotation.py index 0f32a71..5e65ff9 100644 --- a/blockrotation.py +++ b/blockrotation.py @@ -35,7 +35,7 @@ class Torch: alphaMaterials.RedstoneTorchOn.ID, alphaMaterials.RedstoneTorchOff.ID, ] - + South = 1 North = 2 West = 3 @@ -45,7 +45,7 @@ genericFlipRotation(Torch) class Ladder: blocktypes = [alphaMaterials.Ladder.ID] - + East = 2 West = 3 North = 4 @@ -57,7 +57,7 @@ class Stair: alphaMaterials.WoodenStairs.ID, alphaMaterials.StoneStairs.ID, ] - + South = 0 North = 1 West = 2 @@ -66,13 +66,13 @@ genericFlipRotation(Stair) class WallSign: blocktypes = [alphaMaterials.WallSign.ID] - + East = 2 West = 3 North = 4 South = 5 genericFlipRotation(WallSign) - + class Furnace: blocktypes = [ alphaMaterials.Furnace.ID, @@ -87,15 +87,15 @@ genericFlipRotation(Furnace) class Dispenser(Furnace): blocktypes = [ alphaMaterials.Dispenser.ID, - ] + ] genericFlipRotation(Dispenser) - + class Pumpkin: blocktypes = [ alphaMaterials.Pumpkin.ID, alphaMaterials.JackOLantern.ID, ] - + East = 0 South = 1 West = 2 @@ -104,14 +104,14 @@ genericFlipRotation(Pumpkin) class Rail: blocktypes = [alphaMaterials.Rail.ID] - + EastWest = 0 NorthSouth = 1 South = 2 North = 3 East = 4 West = 5 - + Northeast = 6 Southeast = 7 Southwest = 8 @@ -143,7 +143,7 @@ def applyBit8(array): def applyBit4(array): array[4:8] = array[0:4] | 0x4 array[12:16] = array[8:12] | 0x4 - + applyThrownBit = applyBit8 class PoweredDetectorRail(Rail): @@ -161,7 +161,7 @@ applyThrownBit(PoweredDetectorRail.flipEastWest) applyThrownBit(PoweredDetectorRail.flipNorthSouth) rotationClasses.append(PoweredDetectorRail) - + class Lever: blocktypes = [alphaMaterials.Lever.ID] ThrownBit = 0x8 @@ -199,11 +199,11 @@ rotationClasses.append(Button) class SignPost: blocktypes = [alphaMaterials.Sign.ID] #west is 0, increasing clockwise - + rotateLeft = arange(16, dtype='uint8') rotateLeft -= 4 rotateLeft &= 0xf - + flipEastWest = arange(16, dtype='uint8') flipNorthSouth = arange(16, dtype='uint8') pass @@ -217,14 +217,14 @@ class Door: ] TopHalfBit = 0x8 SwungCCWBit = 0x4 - + Northeast = 0 Southeast = 1 Southwest = 2 Northwest = 3 - + rotateLeft = arange(16, dtype='uint8') - + Door.rotateLeft[Door.Northeast] = Door.Northwest Door.rotateLeft[Door.Southeast] = Door.Northeast Door.rotateLeft[Door.Southwest] = Door.Southeast @@ -260,14 +260,14 @@ class RedstoneRepeater: blocktypes = [ alphaMaterials.RedstoneRepeaterOff.ID, alphaMaterials.RedstoneRepeaterOn.ID, - + ] - + East = 0 South = 1 West = 2 North = 3 - + genericFlipRotation(RedstoneRepeater) #high bits of the repeater indicate repeater delay, and should be preserved @@ -285,7 +285,7 @@ applyOpenedBit = applyBit4 class Trapdoor: blocktypes = [alphaMaterials.Trapdoor.ID] - + West = 0 East = 1 South = 2 @@ -299,7 +299,7 @@ applyOpenedBit(Trapdoor.flipNorthSouth) applyPistonBit = applyBit8 class PistonBody: blocktypes = [alphaMaterials.StickyPiston.ID, alphaMaterials.Piston.ID] - + East = 2 West = 3 North = 4 @@ -308,36 +308,36 @@ genericFlipRotation(PistonBody) applyPistonBit(PistonBody.rotateLeft) applyPistonBit(PistonBody.flipEastWest) applyPistonBit(PistonBody.flipNorthSouth) - + class PistonHead(PistonBody): blocktypes = [alphaMaterials.PistonHead.ID] -rotationClasses.append(PistonHead) - +rotationClasses.append(PistonHead) + def masterRotationTable(rotationFunc): # compute a 256x16 table mapping each possible blocktype/data combination to # the resulting data when the block is rotated - table = zeros( (256, 16), dtype='uint8') + table = zeros((256, 16), dtype='uint8') table[:] = arange(16, dtype='uint8') for cls in rotationClasses: for blocktype in cls.blocktypes: table[blocktype] = rotationFunc(cls) - - return table - + + return table + class BlockRotation: rotateLeft = masterRotationTable(lambda cls:cls.rotateLeft); flipEastWest = masterRotationTable(lambda cls:cls.flipEastWest); flipNorthSouth = masterRotationTable(lambda cls:cls.flipNorthSouth); - - - + + + def FlipNorthSouth(blocks, data): data[:] = BlockRotation.flipNorthSouth[blocks, data] - + def FlipEastWest(blocks, data): data[:] = BlockRotation.flipEastWest[blocks, data] - + def RotateLeft(blocks, data): data[:] = BlockRotation.rotateLeft[blocks, data] diff --git a/entity.py b/entity.py index 15dbc9f..46d7664 100644 --- a/entity.py +++ b/entity.py @@ -11,7 +11,7 @@ class TileEntity(object): @classmethod def pos(cls, tag): return [tag[a].value for a in 'xyz'] - + @classmethod def setpos(cls, tag, pos): for a, p in zip('xyz', pos): @@ -23,21 +23,21 @@ class Entity(object): if "Pos" not in tag: print tag return [a.value for a in tag["Pos"]] - + @classmethod def setpos(cls, tag, pos): tag["Pos"] = TAG_List([TAG_Int(p) for p in pos]) - + @classmethod - def copyWithOffset(cls, entity, copyOffset): + def copyWithOffset(cls, entity, copyOffset): eTag = deepcopy(entity) - - positionTags = map(lambda p, co: nbt.TAG_Double(p.value+co), eTag["Pos"], copyOffset) + + positionTags = map(lambda p, co: nbt.TAG_Double(p.value + co), eTag["Pos"], copyOffset) eTag["Pos"] = TAG_List(positionTags) - + if eTag["id"].value == "Painting": eTag["TileX"].value += copyOffset[0] eTag["TileY"].value += copyOffset[1] eTag["TileZ"].value += copyOffset[2] - - return eTag \ No newline at end of file + + return eTag diff --git a/indev.py b/indev.py index 637dacc..74878c6 100644 --- a/indev.py +++ b/indev.py @@ -86,115 +86,115 @@ __all__ = ["MCIndevLevel"] class MCIndevLevel(MCLevel): - + """ IMPORTANT: self.Blocks and self.Data are indexed with [x,z,y] via axis swapping to be consistent with infinite levels.""" hasEntities = True - - def setPlayerSpawnPosition(self, pos, player = None): + + def setPlayerSpawnPosition(self, pos, player=None): assert len(pos) == 3 self.Spawn = array(pos); - def playerSpawnPosition(self, player = None): + def playerSpawnPosition(self, player=None): return self.Spawn; - - def setPlayerPosition(self, pos, player = "Ignored"): + + def setPlayerPosition(self, pos, player="Ignored"): for x in self.root_tag["Entities"]: if x["id"].value == "LocalPlayer": x["Pos"] = TAG_List([TAG_Float(p) for p in pos]) - - def getPlayerPosition(self, player = "Ignored"): + + def getPlayerPosition(self, player="Ignored"): for x in self.root_tag["Entities"]: if x["id"].value == "LocalPlayer": return array(map(lambda x:x.value, x["Pos"])); - - def setPlayerOrientation(self, yp, player = "Ignored"): + + def setPlayerOrientation(self, yp, player="Ignored"): for x in self.root_tag["Entities"]: if x["id"].value == "LocalPlayer": x["Rotation"] = TAG_List([TAG_Float(p) for p in yp]) - def playerOrientation(self, player = "Ignored"): + def playerOrientation(self, player="Ignored"): """ returns (yaw, pitch) """ for x in self.root_tag["Entities"]: if x["id"].value == "LocalPlayer": return array(map(lambda x:x.value, x["Rotation"])); - - def setBlockDataAt(self, x,y,z, newdata): - if x<0 or y<0 or z<0: return 0 - if x>=self.Width or y>=self.Height or z>=self.Length: return 0; - self.Data[x,z,y] = (newdata & 0xf); + + def setBlockDataAt(self, x, y, z, newdata): + if x < 0 or y < 0 or z < 0: return 0 + if x >= self.Width or y >= self.Height or z >= self.Length: return 0; + self.Data[x, z, y] = (newdata & 0xf); def blockDataAt(self, x, y, z): - if x<0 or y<0 or z<0: return 0 - if x>=self.Width or y>=self.Height or z>=self.Length: return 0; - return self.Data[x,z,y]; - + if x < 0 or y < 0 or z < 0: return 0 + if x >= self.Width or y >= self.Height or z >= self.Length: return 0; + return self.Data[x, z, y]; + def blockLightAt(self, x, y, z): - if x<0 or y<0 or z<0: return 0 - if x>=self.Width or y>=self.Height or z>=self.Length: return 0; - return self.BlockLight[x,z,y]; - + if x < 0 or y < 0 or z < 0: return 0 + if x >= self.Width or y >= self.Height or z >= self.Length: return 0; + return self.BlockLight[x, z, y]; + def __repr__(self): return u"MCIndevLevel({0}): {1}W {2}L {3}H".format(self.filename, self.Width, self.Length, self.Height) - + @classmethod def _isTagLevel(cls, root_tag): return "MinecraftLevel" == root_tag.name - - def __init__(self, root_tag = None, filename = ""): + + def __init__(self, root_tag=None, filename=""): self.Width = 0 self.Height = 0 self.Length = 0 self.Blocks = array([], uint8) self.Data = array([], uint8) - self.Spawn = (0,0,0) + self.Spawn = (0, 0, 0) self.filename = filename; - - + + if root_tag: - + self.root_tag = root_tag; mapTag = root_tag[Map]; self.Width = mapTag[Width].value self.Length = mapTag[Length].value self.Height = mapTag[Height].value - - mapTag[Blocks].value.shape = (self.Height, self.Length, self.Width) - - self.Blocks = swapaxes(mapTag[Blocks].value, 0, 2) - - mapTag[Data].value.shape = (self.Height, self.Length, self.Width) - - self.Data = swapaxes(mapTag[Data].value, 0, 2) - - + mapTag[Blocks].value.shape = (self.Height, self.Length, self.Width) + + self.Blocks = swapaxes(mapTag[Blocks].value, 0, 2) + + mapTag[Data].value.shape = (self.Height, self.Length, self.Width) + + self.Data = swapaxes(mapTag[Data].value, 0, 2) + + + self.BlockLight = self.Data & 0xf - + self.Data >>= 4 - + self.Spawn = [mapTag[Spawn][i].value for i in range(3)]; - - if not Entities in root_tag: + + if not Entities in root_tag: root_tag[Entities] = TAG_List(); self.Entities = root_tag[Entities] - if not TileEntities in root_tag: + if not TileEntities in root_tag: root_tag[TileEntities] = TAG_List(); self.TileEntities = root_tag[TileEntities] - - if len(filter(lambda x:x['id'].value=='LocalPlayer', root_tag[Entities])) == 0: #omen doesn't make a player entity - p=TAG_Compound() + + if len(filter(lambda x:x['id'].value == 'LocalPlayer', root_tag[Entities])) == 0: #omen doesn't make a player entity + p = TAG_Compound() p['id'] = TAG_String('LocalPlayer') p['Pos'] = TAG_List([TAG_Float(0.), TAG_Float(64.), TAG_Float(0.)]) p['Rotation'] = TAG_List([TAG_Float(0.), TAG_Float(45.)]) - + root_tag[Entities].append(p) #self.saveInPlace(); - + else: - info( u"Creating new Indev levels is not yet implemented.!" ) + info(u"Creating new Indev levels is not yet implemented.!") raise ValueError, "Can't do that yet" # self.SurroundingGroundHeight = root_tag[Environment][SurroundingGroundHeight].value # self.SurroundingGroundType = root_tag[Environment][SurroundingGroundType].value @@ -212,65 +212,65 @@ class MCIndevLevel(MCLevel): # self.Author = self.root_tag[About][Author].value # self.CreatedOn = self.root_tag[About][CreatedOn].value - - + + def rotateLeft(self): MCLevel.rotateLeft(self); - - self.Data = swapaxes(self.Data, 1, 0)[:,::-1,:]; #x=y; y=-x - + + self.Data = swapaxes(self.Data, 1, 0)[:, ::-1, :]; #x=y; y=-x + torchRotation = array([0, 4, 3, 1, 2, 5, - 6, 7, - + 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15]); - + torchIndexes = (self.Blocks == self.materials.Torch.ID) - info( u"Rotating torches: {0}".format( len(torchIndexes.nonzero()[0]) ) ) + info(u"Rotating torches: {0}".format(len(torchIndexes.nonzero()[0]))) self.Data[torchIndexes] = torchRotation[self.Data[torchIndexes]] - - - def saveToFile(self, filename = None): + + + def saveToFile(self, filename=None): if filename == None: filename = self.filename; if filename == None: - warn( u"Attempted to save an unnamed file in place" ) + warn(u"Attempted to save an unnamed file in place") return; #you fool! - + self.Data <<= 4; self.Data |= (self.BlockLight & 0xf) - + self.Blocks = swapaxes(self.Blocks, 0, 2) self.Data = swapaxes(self.Data, 0, 2) - - mapTag = TAG_Compound( name=Map ); + + mapTag = TAG_Compound(name=Map); mapTag[Width] = TAG_Short(self.Width); mapTag[Height] = TAG_Short(self.Height); mapTag[Length] = TAG_Short(self.Length); mapTag[Blocks] = TAG_Byte_Array(self.Blocks); - mapTag[Data] = TAG_Byte_Array(self.Data); - + mapTag[Data] = TAG_Byte_Array(self.Data); + self.Blocks = swapaxes(self.Blocks, 0, 2) self.Data = swapaxes(self.Data, 0, 2) - - mapTag[Spawn] = TAG_List([TAG_Short(i) for i in self.Spawn]) + + mapTag[Spawn] = TAG_List([TAG_Short(i) for i in self.Spawn]) self.root_tag[Map] = mapTag; self.root_tag[Map] #output_file = gzip.open(self.filename, "wb", compresslevel=1) try: os.rename(filename, filename + ".old"); - except Exception,e: + except Exception, e: pass - + try: self.root_tag.saveGzipped(filename); except: os.rename(filename + ".old", filename); - + try: os.remove(filename + ".old"); - except Exception,e: + except Exception, e: pass - + self.BlockLight = self.Data & 0xf - + self.Data >>= 4 - + diff --git a/items.py b/items.py index 0683f5d..cae4032 100644 --- a/items.py +++ b/items.py @@ -257,20 +257,20 @@ items_txt = """ """ class ItemType (object): - def __init__(self, id, name, imagefile = None, imagecoords = None, maxdamage = 0, damagevalue = 0, stacksize = 64): - self.id=id - self.name=name - self.imagefile=imagefile - self.imagecoords=imagecoords - self.maxdamage=maxdamage + def __init__(self, id, name, imagefile=None, imagecoords=None, maxdamage=0, damagevalue=0, stacksize=64): + self.id = id + self.name = name + self.imagefile = imagefile + self.imagecoords = imagecoords + self.maxdamage = maxdamage def __repr__(self): return "ItemType({0}, '{1}')".format(self.id, self.name) def __str__(self): return "ItemType {0}: {1}".format(self.id, self.name) - + class Items (object): items_txt = items_txt - def __init__(self, filename = None): + def __init__(self, filename=None): if filename is None: items_txt = self.items_txt else: @@ -281,9 +281,9 @@ class Items (object): print "Error reading items.txt: ", e; print "Using internal data." items_txt = self.items_txt - + self.itemtypes = {}; - + for line in items_txt.split("\n"): try: line = line.strip() @@ -293,7 +293,7 @@ class Items (object): stacksize = 64 damagevalue = None maxdamage = 0 - + fields = line.split(); if len(fields) >= 4: maxdamage = None; @@ -309,25 +309,25 @@ class Items (object): id = int(id); name = name.replace("_", " "); imagecoords = imagecoords.split(","); - + self.itemtypes[(id, damagevalue)] = ItemType(id, name, imagefile, imagecoords, maxdamage, damagevalue, stacksize) except Exception, e: print "Error reading line:", e print "Line: ", line print - + self.names = dict((item.name, item.id) for item in self.itemtypes.itervalues()) - + def findItem(self, id=0, damage=None): item = self.itemtypes.get((id, damage)) if item: return item - + item = self.itemtypes.get((id, None)) if item: return item - + item = self.itemtypes.get((id, 0)) if item: return item - + raise ItemNotFound, "Item {0}:{1} not found".format(id, damage) class ItemNotFound(KeyError): pass diff --git a/level.py b/level.py index 32ec1ad..3416198 100644 --- a/level.py +++ b/level.py @@ -23,21 +23,21 @@ class MCLevel(object): MCLevel subclasses must have Width, Length, and Height attributes. The first two are always zero for infinite levels. Subclasses must also have Blocks, and optionally Data and BlockLight. """ - + ###common to Creative, Survival and Indev. these routines assume ###self has Width, Height, Length, and Blocks - + materials = classicMaterials; isInfinite = False - + hasEntities = False; compressedTag = None root_tag = None - + Height = None Length = None Width = None - + players = ["Player"] dimNo = 0; parentWorld = None @@ -48,63 +48,63 @@ class MCLevel(object): by this class. Returns True or False. Subclasses should implement _isLevel, _isDataLevel, or _isTagLevel. - """ + """ if hasattr(cls, "_isLevel"): return cls._isLevel(filename); - + with file(filename) as f: data = f.read(); - + if hasattr(cls, "_isDataLevel"): return cls._isDataLevel(data); - + if hasattr(cls, "_isTagLevel"): try: root_tag = nbt.load(filename, data) except: return False; - + return cls._isTagLevel(root_tag); - + return False - + def getWorldBounds(self): - return BoundingBox( (0,0,0), self.size ) - + return BoundingBox((0, 0, 0), self.size) + @property def displayName(self): return os.path.basename(self.filename) - - @property + + @property def size(self): "Returns the level's dimensions as a tuple (X,Y,Z)" return (self.Width, self.Height, self.Length) - + @property def bounds(self): - return BoundingBox( (0,0,0), self.size ) - + return BoundingBox((0, 0, 0), self.size) + def packChunkData(self): """called before compression""" pass def unpackChunkData(self): """called when accessing attributes decorated with @unpack_first""" pass - + def shapeChunkData(self): """called during the default decompress(), should assign a shape to the Blocks, Data, Light, SkyLight, HeightMap attributes if present """ pass - + def close(self): pass - + def compress(self): pass def decompress(self): pass - - + + def compressChunk(self, cx, cz): pass def entitiesAt(self, x, y, z): return None @@ -115,28 +115,28 @@ class MCLevel(object): @property def loadedChunks(self): - return itertools.product(xrange(0, self.Width+15>>4), xrange(0, self.Length+15>>4)) - + return itertools.product(xrange(0, self.Width + 15 >> 4), xrange(0, self.Length + 15 >> 4)) + @property def presentChunks(self): - """Returns self.allChunks for compatibility""" + """Returns self.allChunks for compatibility""" return self.allChunks #backward compatibility - + @property def chunkCount(self): - return (self.Width+15>>4) * (self.Length+15>>4) - + return (self.Width + 15 >> 4) * (self.Length + 15 >> 4) + @property def allChunks(self): """Returns a synthetic list of chunk positions (xPos, zPos), to fake being a chunked level format.""" return self.loadedChunks - + def getChunk(self, cx, cz): """Synthesize a FakeChunk object representing the chunk at the given position. Subclasses override fakeBlocksForChunk and fakeDataForChunk to fill in the chunk arrays""" - + class FakeChunk: def load(self):pass def compress(self):pass @@ -144,45 +144,45 @@ class MCLevel(object): def chunkChanged(self):pass @property def materials(self): return self.world.materials - + f = FakeChunk() f.world = self; - f.chunkPosition = (cx,cz) - + f.chunkPosition = (cx, cz) + f.Blocks = self.fakeBlocksForChunk(cx, cz) - - + + f.Data = self.fakeDataForChunk(cx, cz) - + whiteLight = zeros_like(f.Blocks); whiteLight[:] = 15; - + f.BlockLight = whiteLight f.SkyLight = whiteLight f.Entities = [] f.TileEntities = [] - - + + f.root_tag = TAG_Compound(); - + return f - + def getAllChunkSlices(self): - slices = ( slice(None),slice(None),slice(None), ) + slices = (slice(None), slice(None), slice(None),) box = self.bounds x, y, z = box.origin - - for cpos in self.allChunks: + + for cpos in self.allChunks: xPos, zPos = cpos try: chunk = self.getChunk(xPos, zPos) except (ChunkMalformed, ChunkNotPresent): continue - - - yield ( chunk, slices, (xPos * 16 - x, 0, zPos * 16 - z) ) - - + + + yield (chunk, slices, (xPos * 16 - x, 0, zPos * 16 - z)) + + def getChunkSlices(self, box): """ call this method to iterate through a large slice of the world by visiting each chunk and indexing its data with a subslice. @@ -198,165 +198,165 @@ class MCLevel(object): In all other places, including an entity's 'Pos', the order is x,y,z. """ level = self - + #when yielding slices of chunks on the edge of the box, adjust the #slices by an offset - minxoff, minzoff = box.minx-(box.mincx<<4), box.minz-(box.mincz<<4); - maxxoff, maxzoff = box.maxx-(box.maxcx<<4)+16, box.maxz-(box.maxcz<<4)+16; - - + minxoff, minzoff = box.minx - (box.mincx << 4), box.minz - (box.mincz << 4); + maxxoff, maxzoff = box.maxx - (box.maxcx << 4) + 16, box.maxz - (box.maxcz << 4) + 16; + + for cx in range(box.mincx, box.maxcx): - localMinX=0 - localMaxX=16 - if cx==box.mincx: - localMinX=minxoff - - if cx==box.maxcx-1: - localMaxX=maxxoff + localMinX = 0 + localMaxX = 16 + if cx == box.mincx: + localMinX = minxoff + + if cx == box.maxcx - 1: + localMaxX = maxxoff newMinX = localMinX + (cx << 4) - box.minx newMaxX = localMaxX + (cx << 4) - box.minx - - + + for cz in range(box.mincz, box.maxcz): - localMinZ=0 - localMaxZ=16 - if cz==box.mincz: - localMinZ=minzoff - if cz==box.maxcz-1: - localMaxZ=maxzoff + localMinZ = 0 + localMaxZ = 16 + if cz == box.mincz: + localMinZ = minzoff + if cz == box.maxcz - 1: + localMaxZ = maxzoff newMinZ = localMinZ + (cz << 4) - box.minz newMaxZ = localMaxZ + (cz << 4) - box.minz try: ch = level.getChunk(cx, cz) except ChunkNotPresent, e: continue; - + yield (ch, - (slice(localMinX,localMaxX),slice(localMinZ,localMaxZ),slice(box.miny,box.maxy)), + (slice(localMinX, localMaxX), slice(localMinZ, localMaxZ), slice(box.miny, box.maxy)), (newMinX, 0, newMinZ)) - + ch.compress() - - + + def containsPoint(self, x, y, z): - return (x >=0 and x < self.Width and - y >=0 and y < self.Height and - z >=0 and z < self.Length ) + return (x >= 0 and x < self.Width and + y >= 0 and y < self.Height and + z >= 0 and z < self.Length) def containsChunk(self, cx, cz): #w+15 to allow non 16 aligned schematics - return (cx >=0 and cx < (self.Width+15 >> 4) and - cz >=0 and cz < (self.Length+15 >> 4)) + return (cx >= 0 and cx < (self.Width + 15 >> 4) and + cz >= 0 and cz < (self.Length + 15 >> 4)) def chunkIsLoaded(self, cx, cz): - return self.containsChunk(cx,cz) - + return self.containsChunk(cx, cz) + def chunkIsCompressed(self, cx, cz): return False - + def chunkIsDirty(self, cx, cz): return True - - + + def fakeBlocksForChunk(self, cx, cz): #return a 16x16xH block array for rendering. Alpha levels can #just return the chunk data. other levels need to reorder the #indices and return a slice of the blocks. - + cxOff = cx << 4 czOff = cz << 4 - b = self.Blocks[cxOff:cxOff+16, czOff:czOff+16, 0:self.Height, ]; + b = self.Blocks[cxOff:cxOff + 16, czOff:czOff + 16, 0:self.Height, ]; #(w, l, h) = b.shape #if w<16 or l<16: # b = resize(b, (16,16,h) ) return b; - + def fakeDataForChunk(self, cx, cz): #Data is emulated for flexibility cxOff = cx << 4 czOff = cz << 4 - + if hasattr(self, "Data"): - return self.Data[cxOff:cxOff+16, czOff:czOff+16, 0:self.Height, ]; - + return self.Data[cxOff:cxOff + 16, czOff:czOff + 16, 0:self.Height, ]; + else: return zeros(shape=(16, 16, self.Height), dtype='uint8') - + def skylightAt(self, *args): return 15 def setSkylightAt(self, *args): pass - def setBlockDataAt(self, x,y,z, newdata): pass + def setBlockDataAt(self, x, y, z, newdata): pass def blockDataAt(self, x, y, z): return 0; - + def blockLightAt(self, x, y, z): return 15; def blockAt(self, x, y, z): - if x<0 or y<0 or z<0: return 0 - if x>=self.Width or y>=self.Height or z>=self.Length: return 0; - return self.Blocks[x,z,y] - + if x < 0 or y < 0 or z < 0: return 0 + if x >= self.Width or y >= self.Height or z >= self.Length: return 0; + return self.Blocks[x, z, y] + def setBlockAt(self, x, y, z, blockID): - if x<0 or y<0 or z<0: return 0 - if x>=self.Width or y>=self.Height or z>=self.Length: return 0; - self.Blocks[x,z,y] = blockID - - + if x < 0 or y < 0 or z < 0: return 0 + if x >= self.Width or y >= self.Height or z >= self.Length: return 0; + self.Blocks[x, z, y] = blockID + + def blocksInRanges(self, origin, size): # origin is (x,y,z), size is (w,h,l) - (x,y,z) = origin - (w,h,l) = size + (x, y, z) = origin + (w, h, l) = size # end = tuple([o+s for o,s in zip(origin,size)]) - return self.Blocks[x:x+w,z:z+l,y:y+h] - + return self.Blocks[x:x + w, z:z + l, y:y + h] + def blockReplaceTable(self, blocksToReplace): - blocktable = zeros( (256, 16), dtype='bool') + blocktable = zeros((256, 16), dtype='bool') for b in blocksToReplace: if b.hasAlternate: - blocktable[b.ID,b.blockData] = True + blocktable[b.ID, b.blockData] = True else: blocktable[b.ID] = True - + return blocktable - - def fillBlocks(self, box, blockInfo, blocksToReplace = []): - - if box is None: + + def fillBlocks(self, box, blockInfo, blocksToReplace=[]): + + if box is None: box = self.bounds else: box = box.intersect(self.bounds) - - info( u"Filling blocks in {0} with {1}, replacing{2}".format(box, blockInfo, blocksToReplace) ) - + + info(u"Filling blocks in {0} with {1}, replacing{2}".format(box, blockInfo, blocksToReplace)) + slices = map(slice, box.origin, box.maximum) - - blocks = self.Blocks[slices[0],slices[2],slices[1]] + + blocks = self.Blocks[slices[0], slices[2], slices[1]] if len(blocksToReplace): blocktable = self.blockReplaceTable(blocksToReplace) - + if hasattr(self, "Data"): - data = self.Data[slices[0],slices[2],slices[1]] - mask = blocktable[blocks,data] - + data = self.Data[slices[0], slices[2], slices[1]] + mask = blocktable[blocks, data] + data[mask] = blockInfo.blockData; else: - mask = blocktable[blocks,0] - + mask = blocktable[blocks, 0] + blocks[mask] = blockInfo.ID; - + else: blocks[:] = blockInfo.ID; if hasattr(self, "Data"): - self.Data[slices[0],slices[2],slices[1]] = blockInfo.blockData; - + self.Data[slices[0], slices[2], slices[1]] = blockInfo.blockData; + #self.saveInPlace(); classicWoolMask = zeros((256,), dtype='bool') classicWoolMask[range(21, 37)] = True; - - classicToAlphaWoolTypes = range(21)+[ + + classicToAlphaWoolTypes = range(21) + [ 0xE, #"Red", (21) 0x1, #"Orange", 0x4, #"Yellow", @@ -375,362 +375,362 @@ class MCLevel(object): 0x0, #"White", ] classicToAlphaWoolTypes = array(classicToAlphaWoolTypes, dtype='uint8') - + def convertBlocksFromLevel(self, sourceLevel, blocks, blockData): convertedBlocks = sourceLevel.materials.conversionTables[self.materials][blocks] if blockData is None: blockData = zeros_like(convertedBlocks) - + convertedBlockData = array(blockData) if sourceLevel.materials is classicMaterials and self.materials is alphaMaterials: woolMask = self.classicWoolMask[blocks] woolBlocks = blocks[woolMask] convertedBlockData[woolMask] = self.classicToAlphaWoolTypes[woolBlocks] - + return convertedBlocks, convertedBlockData - + def rotateLeft(self): - self.Blocks = swapaxes(self.Blocks, 1, 0)[:,::-1,:]; #x=z; z=-x + self.Blocks = swapaxes(self.Blocks, 1, 0)[:, ::-1, :]; #x=z; z=-x pass; - + def roll(self): - self.Blocks = swapaxes(self.Blocks, 2, 0)[:,:,::-1]; #x=y; y=-x - pass - + self.Blocks = swapaxes(self.Blocks, 2, 0)[:, :, ::-1]; #x=y; y=-x + pass + def flipVertical(self): - self.Blocks = self.Blocks[:,:,::-1]; #y=-y - pass - + self.Blocks = self.Blocks[:, :, ::-1]; #y=-y + pass + def flipNorthSouth(self): - self.Blocks = self.Blocks[::-1,:,:]; #x=-x - pass - + self.Blocks = self.Blocks[::-1, :, :]; #x=-x + pass + def flipEastWest(self): - self.Blocks = self.Blocks[:,::-1,:]; #z=-z - pass - - - + self.Blocks = self.Blocks[:, ::-1, :]; #z=-z + pass + + + def copyBlocksFromFiniteToFinite(self, sourceLevel, sourceBox, destinationPoint, blocksToCopy): # assume destinationPoint is entirely within this level, and the size of sourceBox fits entirely within it. sourcex, sourcey, sourcez = map(slice, sourceBox.origin, sourceBox.maximum) - destCorner2 = map(lambda a,b:a+b, sourceBox.size, destinationPoint) + destCorner2 = map(lambda a, b:a + b, sourceBox.size, destinationPoint) destx, desty, destz = map(slice, destinationPoint, destCorner2) - + sourceData = None if hasattr(sourceLevel, 'Data'): sourceData = sourceLevel.Data[sourcex, sourcez, sourcey] convertedSourceBlocks, convertedSourceData = self.convertBlocksFromLevel(sourceLevel, sourceLevel.Blocks[sourcex, sourcez, sourcey], sourceData) - - + + blocks = self.Blocks[destx, destz, desty] - + mask = slice(None, None) - + if not (blocksToCopy is None): - typemask = zeros( (256) , dtype='bool') + typemask = zeros((256) , dtype='bool') typemask[blocksToCopy] = True; mask = typemask[convertedSourceBlocks] - + blocks[mask] = convertedSourceBlocks[mask] if hasattr(self, 'Data'): data = self.Data[destx, destz, desty] data[mask] = convertedSourceData[mask] - - + + def copyBlocksFromInfinite(self, sourceLevel, sourceBox, destinationPoint, blocksToCopy): - + chunkIterator = sourceLevel.getChunkSlices(sourceBox) - - + + if blocksToCopy is not None: - typemask = zeros( (256) , dtype='bool') + typemask = zeros((256) , dtype='bool') typemask[blocksToCopy] = True; - + for (chunk, slices, point) in chunkIterator: - point = map(lambda a,b:a+b, point, destinationPoint) + point = map(lambda a, b:a + b, point, destinationPoint) point = point[0], point[2], point[1] mask = slice(None, None) - + convertedSourceBlocks, convertedSourceData = self.convertBlocksFromLevel(sourceLevel, chunk.Blocks[slices], chunk.Data[slices]) - - - destSlices = [slice(p, p+s.stop-s.start) for p,s in zip(point,slices) ] - + + + destSlices = [slice(p, p + s.stop - s.start) for p, s in zip(point, slices) ] + blocks = self.Blocks[ destSlices ]; - + if blocksToCopy is not None: mask = typemask[convertedSourceBlocks] - + blocks[mask] = convertedSourceBlocks[mask] - + if hasattr(self, 'Data'): data = self.Data[ destSlices ]; data[mask] = convertedSourceData[mask] - + #self.Data[ destSlices ][mask] = chunk.Data[slices][mask] - - + + chunk.compress(); - - + + def adjustCopyParameters(self, sourceLevel, sourceBox, destinationPoint): - + # if the destination box is outside the level, it and the source corners are moved inward to fit. # ValueError is raised if the source corners are outside sourceLevel - (x,y,z) = map(int, destinationPoint) - + (x, y, z) = map(int, destinationPoint) + sourceBox = BoundingBox(sourceBox.origin, sourceBox.size) - - (lx,ly,lz) = sourceBox.size; - debug(u"Asked to copy {0} blocks \n\tfrom {1} in {3}\n\tto {2} in {4}" .format (ly*lz*lx,sourceBox, destinationPoint, sourceLevel, self)) - + + (lx, ly, lz) = sourceBox.size; + debug(u"Asked to copy {0} blocks \n\tfrom {1} in {3}\n\tto {2} in {4}" .format (ly * lz * lx, sourceBox, destinationPoint, sourceLevel, self)) + #clip the source ranges to this level's edges. move the destination point as needed. #xxx abstract this - if y<0: - sourceBox.origin[1] -=y + if y < 0: + sourceBox.origin[1] -= y sourceBox.size[1] += y y = 0; - if y+sourceBox.size[1]>self.Height: - sourceBox.size[1] -=y+sourceBox.size[1]-self.Height - y=self.Height-sourceBox.size[1] - + if y + sourceBox.size[1] > self.Height: + sourceBox.size[1] -= y + sourceBox.size[1] - self.Height + y = self.Height - sourceBox.size[1] + #for infinite levels, don't clip along those dimensions because the #infinite copy func will just skip missing chunks if self.Width != 0: - if x<0: - sourceBox.origin[0] -=x + if x < 0: + sourceBox.origin[0] -= x sourceBox.size[0] += x x = 0; - if x+sourceBox.size[0]>self.Width: - sourceBox.size[0] -=x+sourceBox.size[0]-self.Width + if x + sourceBox.size[0] > self.Width: + sourceBox.size[0] -= x + sourceBox.size[0] - self.Width #x=self.Width-sourceBox.size[0] - + if self.Length != 0: - if z<0: - sourceBox.origin[2] -=z + if z < 0: + sourceBox.origin[2] -= z sourceBox.size[2] += z z = 0; - if z+sourceBox.size[2]>self.Length: - sourceBox.size[2] -=z+sourceBox.size[2]-self.Length + if z + sourceBox.size[2] > self.Length: + sourceBox.size[2] -= z + sourceBox.size[2] - self.Length #z=self.Length-sourceBox.size[2] - - destinationPoint = (x,y,z) - + + destinationPoint = (x, y, z) + return sourceBox, destinationPoint - - def copyBlocksFrom(self, sourceLevel, sourceBox, destinationPoint, blocksToCopy = None): + + def copyBlocksFrom(self, sourceLevel, sourceBox, destinationPoint, blocksToCopy=None): if (not sourceLevel.isInfinite) and not( sourceLevel.containsPoint(*sourceBox.origin) and - sourceLevel.containsPoint(*map(lambda x:x-1, sourceBox.maximum))): - raise ValueError, "{0} cannot provide blocks between {1}".format(sourceLevel, sourceBox) - - + sourceLevel.containsPoint(*map(lambda x:x - 1, sourceBox.maximum))): + raise ValueError, "{0} cannot provide blocks between {1}".format(sourceLevel, sourceBox) + + sourceBox, destinationPoint = self.adjustCopyParameters(sourceLevel, sourceBox, destinationPoint) - - if min(sourceBox.size) <= 0: + + if min(sourceBox.size) <= 0: print "Empty source box, aborting" return; - - info( u"Copying {0} blocks from {1} to {2}" .format (sourceBox.volume,sourceBox, destinationPoint) ) - + + info(u"Copying {0} blocks from {1} to {2}" .format (sourceBox.volume, sourceBox, destinationPoint)) + if not (sourceLevel.isInfinite): self.copyBlocksFromFiniteToFinite(sourceLevel, sourceBox, destinationPoint, blocksToCopy) else: self.copyBlocksFromInfinite(sourceLevel, sourceBox, destinationPoint, blocksToCopy) - - + + self.copyEntitiesFrom(sourceLevel, sourceBox, destinationPoint) def saveInPlace(self): self.saveToFile(self.filename); @classmethod - - def setPlayerPosition(self, pos, player = "Player"): + + def setPlayerPosition(self, pos, player="Player"): pass; - def getPlayerPosition(self, player = "Player"): - return (8,self.Height*0.75,8); - - def getPlayerDimension(self, player = "Player"): return 0; - def setPlayerDimension(self, d, player = "Player"): return; - - def setPlayerSpawnPosition(self, pos, player = None): + def getPlayerPosition(self, player="Player"): + return (8, self.Height * 0.75, 8); + + def getPlayerDimension(self, player="Player"): return 0; + def setPlayerDimension(self, d, player="Player"): return; + + def setPlayerSpawnPosition(self, pos, player=None): pass; - def playerSpawnPosition(self, player = None): + def playerSpawnPosition(self, player=None): return self.getPlayerPosition(); - def setPlayerOrientation(self, yp, player = "Player"): + def setPlayerOrientation(self, yp, player="Player"): pass - def playerOrientation(self, player = "Player"): - return (-45.,0.) + def playerOrientation(self, player="Player"): + return (-45., 0.) def copyEntityWithOffset(self, entity, copyOffset): return Entity.copyWithOffset(entity, copyOffset) - - + + def copyTileEntityWithOffset(self, tileEntity, copyOffset): eTag = deepcopy(tileEntity) - eTag['x'] = TAG_Int(tileEntity['x'].value+copyOffset[0]) - eTag['y'] = TAG_Int(tileEntity['y'].value+copyOffset[1]) - eTag['z'] = TAG_Int(tileEntity['z'].value+copyOffset[2]) + eTag['x'] = TAG_Int(tileEntity['x'].value + copyOffset[0]) + eTag['y'] = TAG_Int(tileEntity['y'].value + copyOffset[1]) + eTag['z'] = TAG_Int(tileEntity['z'].value + copyOffset[2]) return eTag - + def copyEntitiesFromInfinite(self, sourceLevel, sourceBox, destinationPoint): chunkIterator = sourceLevel.getChunkSlices(sourceBox); - + for (chunk, slices, point) in chunkIterator: #remember, slices are ordered x,z,y so you can subscript them like so: chunk.Blocks[slices] - cx,cz = chunk.chunkPosition - wx,wz = cx<<4, cz<<4 - - copyOffset = map(lambda x,y:x-y, destinationPoint, sourceBox.origin) + cx, cz = chunk.chunkPosition + wx, wz = cx << 4, cz << 4 + + copyOffset = map(lambda x, y:x - y, destinationPoint, sourceBox.origin) for entity in chunk.Entities: - x,y,z = map(lambda x:x.value, entity[Pos]) - - if x-wx=slices[0].stop: continue - if y=slices[2].stop: continue - if z-wz=slices[1].stop: continue - + x, y, z = map(lambda x:x.value, entity[Pos]) + + if x - wx < slices[0].start or x - wx >= slices[0].stop: continue + if y < slices[2].start or y >= slices[2].stop: continue + if z - wz < slices[1].start or z - wz >= slices[1].stop: continue + eTag = self.copyEntityWithOffset(entity, copyOffset) - + self.addEntity(eTag); - + for tileEntity in chunk.TileEntities: if not 'x' in tileEntity: continue - - x,y,z = tileEntity['x'].value, tileEntity['y'].value, tileEntity['z'].value - if x-wx=slices[0].stop: continue - if y=slices[2].stop: continue - if z-wz=slices[1].stop: continue - + + x, y, z = tileEntity['x'].value, tileEntity['y'].value, tileEntity['z'].value + if x - wx < slices[0].start or x - wx >= slices[0].stop: continue + if y < slices[2].start or y >= slices[2].stop: continue + if z - wz < slices[1].start or z - wz >= slices[1].stop: continue + eTag = self.copyTileEntityWithOffset(tileEntity, copyOffset) - + self.addTileEntity(eTag) - + chunk.compress(); - - + + def copyEntitiesFrom(self, sourceLevel, sourceBox, destinationPoint): #assume coords have already been adjusted by copyBlocks if not self.hasEntities or not sourceLevel.hasEntities: return; sourcePoint0 = sourceBox.origin; sourcePoint1 = sourceBox.maximum; - + if sourceLevel.isInfinite: self.copyEntitiesFromInfinite(sourceLevel, sourceBox, destinationPoint) else: entsCopied = 0; tileEntsCopied = 0; - copyOffset = map(lambda x,y:x-y, destinationPoint, sourcePoint0) + copyOffset = map(lambda x, y:x - y, destinationPoint, sourcePoint0) for entity in getEntitiesInRange(sourceBox, sourceLevel.Entities): eTag = self.copyEntityWithOffset(entity, copyOffset) - + self.addEntity(eTag) entsCopied += 1; - - + + for entity in getTileEntitiesInRange(sourceBox, sourceLevel.TileEntities): if not 'x' in entity: continue - - x,y,z = entity['x'].value, entity['y'].value, entity['z'].value - + + x, y, z = entity['x'].value, entity['y'].value, entity['z'].value + eTag = deepcopy(entity) - eTag['x'] = TAG_Int(x+copyOffset[0]) - eTag['y'] = TAG_Int(y+copyOffset[1]) - eTag['z'] = TAG_Int(z+copyOffset[2]) + eTag['x'] = TAG_Int(x + copyOffset[0]) + eTag['y'] = TAG_Int(y + copyOffset[1]) + eTag['z'] = TAG_Int(z + copyOffset[2]) try: self.addTileEntity(eTag) tileEntsCopied += 1; except ChunkNotPresent: pass - debug( u"Copied {0} entities, {1} tile entities".format(entsCopied, tileEntsCopied) ) + debug(u"Copied {0} entities, {1} tile entities".format(entsCopied, tileEntsCopied)) + - def removeEntitiesInBox(self, box): - + if not self.hasEntities: return 0; newEnts = []; for ent in self.Entities: - if map(lambda x:x.value, ent["Pos"]) in box: + if map(lambda x:x.value, ent["Pos"]) in box: continue; newEnts.append(ent); - + entsRemoved = len(self.Entities) - len(newEnts); - debug( "Removed {0} entities".format(entsRemoved)) - + debug("Removed {0} entities".format(entsRemoved)) + self.Entities.value[:] = newEnts - + return entsRemoved - + def removeTileEntitiesInBox(self, box): - + if not hasattr(self, "TileEntities"): return; newEnts = []; for ent in self.TileEntities: if not "x" in ent: continue - if map(lambda x:x.value, (ent[a] for a in "xyz")) in box: + if map(lambda x:x.value, (ent[a] for a in "xyz")) in box: continue; newEnts.append(ent); - + entsRemoved = len(self.TileEntities) - len(newEnts); - debug( "Removed {0} tile entities".format(entsRemoved)) - + debug("Removed {0} tile entities".format(entsRemoved)) + self.TileEntities.value[:] = newEnts - - return entsRemoved - - def generateLights(self, dirtyChunks = None): + + return entsRemoved + + def generateLights(self, dirtyChunks=None): pass; - + def adjustExtractionParameters(self, box): - x,y,z = box.origin - w,h,l = box.size + x, y, z = box.origin + w, h, l = box.size destX = destY = destZ = 0; - - if y<0: + + if y < 0: destY -= y h += y y = 0; - + if y >= self.Height: return; - - if y+h>=self.Height: - h -=y+h-self.Height - y=self.Height-h - - if h<=0: return - + + if y + h >= self.Height: + h -= y + h - self.Height + y = self.Height - h + + if h <= 0: return + if self.Width: if x < 0: w += x destX -= x; x = 0; if x >= self.Width: return; - + if x + w >= self.Width: w = self.Width - x - + if w <= 0: return - + if z < 0: l += z destZ -= z; z = 0; - + if z >= self.Length: return; - + if z + l >= self.Length: l = self.Length - z - + if l <= 0: return - - box = BoundingBox ( (x,y,z), (w,h,l) ) - + + box = BoundingBox ((x, y, z), (w, h, l)) + return box, (destX, destY, destZ) @@ -738,19 +738,19 @@ def getEntitiesInRange(sourceBox, entities): entsInRange = []; for entity in entities: dir() - x,y,z = Entity.pos(entity) - if not (x,y,z) in sourceBox: continue + x, y, z = Entity.pos(entity) + if not (x, y, z) in sourceBox: continue entsInRange.append(entity) - + return entsInRange def getTileEntitiesInRange(sourceBox, tileEntities): entsInRange = []; for tileEntity in tileEntities: if not 'x' in tileEntity: continue - - x,y,z = TileEntity.pos(tileEntity) - if not (x,y,z) in sourceBox: continue + + x, y, z = TileEntity.pos(tileEntity) + if not (x, y, z) in sourceBox: continue entsInRange.append(tileEntity) - + return entsInRange diff --git a/materials.py b/materials.py index 7b8f638..2a94f9f 100644 --- a/materials.py +++ b/materials.py @@ -6,7 +6,7 @@ NOTEX = (0xB0, 0x80) class Block(object): - def __init__(self, materials, blockID, blockData = 0, **kw): + def __init__(self, materials, blockID, blockData=0, **kw): """ Defines a blocktype. Keyword parameters: @@ -23,52 +23,52 @@ class Block(object): object.__init__(self) self.materials = materials self.name = kw.pop('name', materials.names[blockID][blockData]) - - - self.brightness = kw.pop('brightness', materials.defaultBrightness) + + + self.brightness = kw.pop('brightness', materials.defaultBrightness) self.opacity = kw.pop('opacity', materials.defaultOpacity) self.aka = kw.pop('aka', "") - - self.color = kw.pop('color', materials.flatColors[blockID,blockData]) - + + self.color = kw.pop('color', materials.flatColors[blockID, blockData]) + self.ID = blockID self.blockData = blockData - - + + def __str__(self): return "".format( - name=self.name,id=self.ID,data=self.blockData,ha=self.hasAlternate) - + name=self.name, id=self.ID, data=self.blockData, ha=self.hasAlternate) + def __repr__(self): - return str(self) + return str(self) hasAlternate = False wildcard = False - + def anySubtype(self): - bl = Block( self.materials, self.ID, self.blockData ) + bl = Block(self.materials, self.ID, self.blockData) bl.wildcard = True return bl class MCMaterials(object): defaultBrightness = 0 defaultOpacity = 15 defaultTexture = NOTEX - def __init__(self, defaultName = "Unused Block"): + def __init__(self, defaultName="Unused Block"): object.__init__(self) self.defaultName = defaultName - + self.blockTextures = zeros((256, 16, 6, 2), dtype='uint8') self.blockTextures[:] = self.defaultTexture self.names = [[defaultName] * 16 for i in range(256)] self.aka = [""] * 256 self.allBlocks = [] self.blocksByID = {} - + self.lightEmission = zeros(256, dtype='uint8') self.lightAbsorption = zeros(256, dtype='uint8') self.lightAbsorption[:] = self.defaultOpacity self.flatColors = zeros((256, 16, 4), dtype='uint8') self.flatColors[:] = (0xc9, 0x77, 0xf0, 0xff) - + #flat colors borrowed from c10t. https://github.com/udoprog/c10t defaultColors = array([ (255, 255, 255, 0), #Air @@ -167,817 +167,817 @@ class MCMaterials(object): (201, 119, 240, 85), #Redstone Repeater (Off) (201, 119, 240, 85), #Redstone Repeater (On) ]) - self.flatColors[:len(defaultColors),:,:] = array(defaultColors)[:,newaxis,:] - + self.flatColors[:len(defaultColors), :, :] = array(defaultColors)[:, newaxis, :] + def __repr__(self): return "".format(self.name) - + def blocksMatching(self, name): name = name.lower() return [v for v in self.allBlocks if name in v.name.lower() or name in v.aka.lower()] - - def blockWithID(self, id, data = 0): - if (id,data) in self.blocksByID: - return self.blocksByID[id,data] + + def blockWithID(self, id, data=0): + if (id, data) in self.blocksByID: + return self.blocksByID[id, data] else: bl = Block(self, id, blockData=data) bl.hasAlternate = True return bl - - def Block(self, blockID, blockData = 0, **kw): + + def Block(self, blockID, blockData=0, **kw): block = Block(self, blockID, blockData, **kw) - + self.lightEmission[blockID] = block.brightness self.lightAbsorption[blockID] = block.opacity self.aka[blockID] = block.aka - + self.flatColors[blockID, (blockData or slice(None))] = block.color - + texture = kw.pop('texture', None) - + if texture: - self.blockTextures[blockID,(blockData or slice(None))] = texture - + self.blockTextures[blockID, (blockData or slice(None))] = texture + if blockData is 0: self.names[blockID] = [block.name] * 16 else: self.names[blockID][blockData] = block.name - + if block.name is not self.defaultName: self.allBlocks.append(block) - - + + if (blockID, 0) in self.blocksByID: self.blocksByID[blockID, 0].hasAlternate = True block.hasAlternate = True - + self.blocksByID[blockID, blockData] = block - - return block - + + return block + def __cmp__(self, rhs): - return (self.ID, self.blockData).__cmp__( (rhs.ID, rhs.blockData) ) - + return (self.ID, self.blockData).__cmp__((rhs.ID, rhs.blockData)) + ### ### MATERIALS for the latest version of the game ### ### -materials = MCMaterials(defaultName = "Future Block!"); +materials = MCMaterials(defaultName="Future Block!"); materials.name = "Alpha" am = materials -am.Air = am.Block(0, +am.Air = am.Block(0, name="Air", - texture=(0x80,0xB0), + texture=(0x80, 0xB0), opacity=0, ) -am.Stone = am.Block(1, +am.Stone = am.Block(1, name="Stone", - texture=(0x10,0x00), + texture=(0x10, 0x00), ) -am.Grass = am.Block(2, +am.Grass = am.Block(2, name="Grass", - texture=((0x30,0x00), (0x30,0x00), (0x00,0x00), (0x20,0x00), (0x30,0x00), (0x30,0x00)), + texture=((0x30, 0x00), (0x30, 0x00), (0x00, 0x00), (0x20, 0x00), (0x30, 0x00), (0x30, 0x00)), ) -am.Dirt = am.Block(3, +am.Dirt = am.Block(3, name="Dirt", - texture=(0x20,0x00), + texture=(0x20, 0x00), ) -am.Cobblestone = am.Block(4, +am.Cobblestone = am.Block(4, name="Cobblestone", - texture=(0x00,0x10), + texture=(0x00, 0x10), ) -am.WoodPlanks = am.Block(5, +am.WoodPlanks = am.Block(5, name="Wood Planks", - texture=(0x40,0x00), + texture=(0x40, 0x00), ) -am.Sapling = am.Block(6, +am.Sapling = am.Block(6, name="Sapling", - texture=(0xF0,0x00), + texture=(0xF0, 0x00), opacity=0, ) - + am.SpruceSapling = am.Block(6, blockData=1, name="Spruce Sapling", - texture=(0xF0,0x30), + texture=(0xF0, 0x30), opacity=0, ) am.BirchSapling = am.Block(6, blockData=2, name="Birch Sapling", - texture=(0xF0,0x40), + texture=(0xF0, 0x40), opacity=0, ) -am.Bedrock = am.Block(7, +am.Bedrock = am.Block(7, name="Bedrock", aka="Adminium", - texture=(0x10,0x10), + texture=(0x10, 0x10), ) -am.WaterActive = am.Block(8, +am.WaterActive = am.Block(8, name="Water (active)", - texture=(0xF0,0xD0), + texture=(0xF0, 0xD0), opacity=3, ) -am.WaterStill = am.Block(9, +am.WaterStill = am.Block(9, name="Water (still)", - texture=(0xF0,0xD0), + texture=(0xF0, 0xD0), opacity=3, ) -am.LavaActive = am.Block(10, +am.LavaActive = am.Block(10, name="Lava (active)", - texture=(0xF0,0xF0), + texture=(0xF0, 0xF0), brightness=15, ) -am.LavaStill = am.Block(11, +am.LavaStill = am.Block(11, name="Lava (still)", - texture=(0xF0,0xF0), + texture=(0xF0, 0xF0), brightness=15, ) -am.Sand = am.Block(12, +am.Sand = am.Block(12, name="Sand", - texture=(0x20,0x10), + texture=(0x20, 0x10), ) -am.Gravel = am.Block(13, +am.Gravel = am.Block(13, name="Gravel", - texture=(0x30,0x10), + texture=(0x30, 0x10), ) -am.GoldOre = am.Block(14, +am.GoldOre = am.Block(14, name="Gold Ore", - texture=(0x00,0x20), + texture=(0x00, 0x20), ) -am.IronOre = am.Block(15, +am.IronOre = am.Block(15, name="Iron Ore", - texture=(0x10,0x20), + texture=(0x10, 0x20), ) -am.CoalOre = am.Block(16, +am.CoalOre = am.Block(16, name="Coal Ore", - texture=(0x20,0x20), + texture=(0x20, 0x20), ) -am.Wood = am.Block(17, +am.Wood = am.Block(17, name="Wood", - texture=((0x40,0x10), (0x40,0x10), (0x50,0x10), (0x50,0x10), (0x40,0x10), (0x40,0x10)), + texture=((0x40, 0x10), (0x40, 0x10), (0x50, 0x10), (0x50, 0x10), (0x40, 0x10), (0x40, 0x10)), ) -am.Ironwood = am.Block(17, blockData=1, +am.Ironwood = am.Block(17, blockData=1, name="Ironwood", aka="Redwood", - texture=((0x40,0x70), (0x40,0x70), (0x50,0x10), (0x50,0x10), (0x40,0x70), (0x40,0x70)), + texture=((0x40, 0x70), (0x40, 0x70), (0x50, 0x10), (0x50, 0x10), (0x40, 0x70), (0x40, 0x70)), ) - -am.BirchWood = am.Block(17, blockData=2, + +am.BirchWood = am.Block(17, blockData=2, name="Birch Wood", - texture=((0x50,0x70), (0x50,0x70), (0x50,0x10), (0x50,0x10), (0x50,0x70), (0x50,0x70)), + texture=((0x50, 0x70), (0x50, 0x70), (0x50, 0x10), (0x50, 0x10), (0x50, 0x70), (0x50, 0x70)), ) -am.Leaves = am.Block(18, +am.Leaves = am.Block(18, name="Leaves", - texture=(0x50,0x30), + texture=(0x50, 0x30), opacity=1, color=(99, 188, 76, 128), ) -am.PineLeaves = am.Block(18, blockData=1, +am.PineLeaves = am.Block(18, blockData=1, name="Pine Leaves", - texture=(0x50,0x80), + texture=(0x50, 0x80), opacity=1, color=(74, 131, 66, 128), ) -am.BirchLeaves = am.Block(18, blockData=2, +am.BirchLeaves = am.Block(18, blockData=2, name="Birch Leaves", - texture=(0x50,0x30), + texture=(0x50, 0x30), opacity=1, color=(89, 151, 76, 128), ) -am.LeavesDecaying = am.Block(18, blockData=0 | 4, +am.LeavesDecaying = am.Block(18, blockData=0 | 4, name="Leaves (Decaying)", - texture=(0x50,0x30), + texture=(0x50, 0x30), opacity=1, ) -am.PineLeavesDecaying = am.Block(18, blockData=1 | 4, +am.PineLeavesDecaying = am.Block(18, blockData=1 | 4, name="Pine Leaves (Decaying)", - texture=(0x50,0x80), + texture=(0x50, 0x80), opacity=1, color=am.PineLeaves.color ) -am.BirchLeavesDecaying = am.Block(18, blockData=2 | 4, +am.BirchLeavesDecaying = am.Block(18, blockData=2 | 4, name="Birch Leaves (Decaying)", - texture=(0x50,0x30), + texture=(0x50, 0x30), opacity=1, color=am.BirchLeaves.color - ) - -am.Sponge = am.Block(19, - name="Sponge", - texture=(0x00,0x30), ) -am.Glass = am.Block(20, +am.Sponge = am.Block(19, + name="Sponge", + texture=(0x00, 0x30), + ) + +am.Glass = am.Block(20, name="Glass", - texture=(0x10,0x30), + texture=(0x10, 0x30), opacity=0, ) -am.LapisLazuliOre = am.Block(21, +am.LapisLazuliOre = am.Block(21, name="Lapis Lazuli Ore", - texture=(0x00,0xA0), + texture=(0x00, 0xA0), ) -am.LapisLazuliBlock = am.Block(22, +am.LapisLazuliBlock = am.Block(22, name="Lapis Lazuli Block", - texture=(0x00,0x90), + texture=(0x00, 0x90), ) -am.Dispenser = am.Block(23, +am.Dispenser = am.Block(23, name="Dispenser", - texture=((0xE0,0x20), (0xE0,0x20), (0xE0,0x30), (0x10,0x00), (0xD0,0x20), (0xD0,0x20)), + texture=((0xE0, 0x20), (0xE0, 0x20), (0xE0, 0x30), (0x10, 0x00), (0xD0, 0x20), (0xD0, 0x20)), ) -am.Sandstone = am.Block(24, +am.Sandstone = am.Block(24, name="Sandstone", - texture=((0x00,0xC0), (0x00,0xC0), (0x00,0xB0), (0x00,0xD0), (0x00,0xC0), (0x00,0xC0)), + texture=((0x00, 0xC0), (0x00, 0xC0), (0x00, 0xB0), (0x00, 0xD0), (0x00, 0xC0), (0x00, 0xC0)), ) -am.NoteBlock = am.Block(25, +am.NoteBlock = am.Block(25, name="Note Block", - texture=(0xA0,0x40), + texture=(0xA0, 0x40), ) -am.Bed = am.Block(26, +am.Bed = am.Block(26, name="Bed", texture=(0x60, 0x80), opacity=0, ) -am.PoweredRail = am.Block(27, +am.PoweredRail = am.Block(27, name="Powered Rail", texture=(0x30, 0xA0), opacity=0, ) am.blockTextures[am.PoweredRail.ID][8:] = (0x30, 0xB0) -am.DetectorRail = am.Block(28, +am.DetectorRail = am.Block(28, name="Detector Rail", texture=(0x30, 0xC0), opacity=0, ) -am.StickyPiston = am.Block(29, +am.StickyPiston = am.Block(29, name="Sticky Piston", texture=(0xA0, 0x60), opacity=15, ) -am.Web = am.Block(30, +am.Web = am.Block(30, name="Web", texture=(0xB0, 0x00), opacity=0, ) -am.UnusedShrub = am.Block(31, blockData = 0, +am.UnusedShrub = am.Block(31, blockData=0, name="[Unused Shrub]", texture=(0x80, 0x30), opacity=0, ) -am.TallGrass = am.Block(31, blockData = 1, +am.TallGrass = am.Block(31, blockData=1, name="Tall Grass", texture=(0x70, 0x20), opacity=0, ) -am.Shrub = am.Block(31, blockData = 2, +am.Shrub = am.Block(31, blockData=2, name="Shrub", texture=(0x80, 0x30), opacity=0, ) - -am.DesertShrub2 = am.Block(32, + +am.DesertShrub2 = am.Block(32, name="Dead Shrub", texture=(0x70, 0x30), opacity=0, ) -am.Piston = am.Block(33, +am.Piston = am.Block(33, name="Piston", texture=(0xB0, 0x60), opacity=15, ) -am.PistonHead = am.Block(34, +am.PistonHead = am.Block(34, name="Piston Head", texture=NOTEX, opacity=15, ) - -am.WhiteWool = am.Block(35, + +am.WhiteWool = am.Block(35, name="White Wool", texture=(0x00, 0x40), color=(0xff, 0xff, 0xff, 0xff) ) -am.OrangeWool = am.Block(35, blockData = 1, +am.OrangeWool = am.Block(35, blockData=1, name="Orange Wool", texture=(0x20, 0xD0), color=(0xea, 0x7f, 0x37, 0xff) ) -am.MagentaWool = am.Block(35, blockData = 2, +am.MagentaWool = am.Block(35, blockData=2, name="Magenta Wool", texture=(0x20, 0xC0), color=(0xbf, 0x4b, 0xc9, 0xff) ) - -am.LightBlueWool = am.Block(35, blockData = 3, + +am.LightBlueWool = am.Block(35, blockData=3, name="Light Blue Wool", texture=(0x20, 0xB0), color=(0x68, 0x8b, 0xd4, 0xff) ) -am.YellowWool = am.Block(35, blockData = 4, +am.YellowWool = am.Block(35, blockData=4, name="Yellow Wool", texture=(0x20, 0xA0), color=(0xc2, 0xb5, 0x1c, 0xff) ) -am.LightGreenWool = am.Block(35, blockData = 5, +am.LightGreenWool = am.Block(35, blockData=5, name="Light Green Wool", texture=(0x20, 0x90), color=(0x3b, 0xbd, 0x30, 0xff) ) -am.PinkWool = am.Block(35, blockData = 6, +am.PinkWool = am.Block(35, blockData=6, name="Pink Wool", texture=(0x20, 0x80), color=(0xd9, 0x83, 0x9b, 0xff) ) -am.GrayWool = am.Block(35, blockData = 7, +am.GrayWool = am.Block(35, blockData=7, name="Gray Wool", texture=(0x20, 0x70), color=(0x42, 0x42, 0x42, 0xff) ) -am.LightGrayWool = am.Block(35, blockData = 8, +am.LightGrayWool = am.Block(35, blockData=8, name="Light Gray Wool", texture=(0x10, 0xE0), color=(0x9e, 0xa6, 0xa6, 0xff) ) -am.CyanWool = am.Block(35, blockData = 9, +am.CyanWool = am.Block(35, blockData=9, name="Cyan Wool", texture=(0x10, 0xD0), color=(0x27, 0x75, 0x95, 0xff) ) -am.PurpleWool = am.Block(35, blockData = 10, +am.PurpleWool = am.Block(35, blockData=10, name="Purple Wool", texture=(0x10, 0xC0), color=(0x81, 0x36, 0xc4, 0xff) ) -am.BlueWool = am.Block(35, blockData = 11, +am.BlueWool = am.Block(35, blockData=11, name="Blue Wool", texture=(0x10, 0xB0), color=(0x27, 0x33, 0xa1, 0xff) ) -am.BrownWool = am.Block(35, blockData = 12, +am.BrownWool = am.Block(35, blockData=12, name="Brown Wool", texture=(0x10, 0xA0), color=(0x56, 0x33, 0x1c, 0xff) ) -am.DarkGreenWool = am.Block(35, blockData = 13, +am.DarkGreenWool = am.Block(35, blockData=13, name="Dark Green Wool", texture=(0x10, 0x90), color=(0x38, 0x4d, 0x18, 0xff) ) -am.RedWool = am.Block(35, blockData = 14, +am.RedWool = am.Block(35, blockData=14, name="Red Wool", texture=(0x10, 0x80), color=(0xa4, 0x2d, 0x29, 0xff) ) -am.BlackWool = am.Block(35, blockData = 15, +am.BlackWool = am.Block(35, blockData=15, name="Black Wool", texture=(0x10, 0x70), - color = (0, 0, 0, 0xff) + color=(0, 0, 0, 0xff) ) -am.Flower = am.Block(37, +am.Flower = am.Block(37, name="Flower", - texture=(0xD0,0x00), + texture=(0xD0, 0x00), opacity=0, ) -am.Rose = am.Block(38, +am.Rose = am.Block(38, name="Rose", - texture=(0xC0,0x00), + texture=(0xC0, 0x00), opacity=0, ) -am.BrownMushroom = am.Block(39, +am.BrownMushroom = am.Block(39, name="Brown Mushroom", - texture=(0xD0,0x10), + texture=(0xD0, 0x10), opacity=0, brightness=1, ) -am.RedMushroom = am.Block(40, +am.RedMushroom = am.Block(40, name="Red Mushroom", - texture=(0xC0,0x10), + texture=(0xC0, 0x10), opacity=0, ) -am.BlockofGold = am.Block(41, +am.BlockofGold = am.Block(41, name="Block of Gold", - texture=(0x70,0x10), + texture=(0x70, 0x10), ) -am.BlockofIron = am.Block(42, +am.BlockofIron = am.Block(42, name="Block of Iron", - texture=(0x60,0x10), + texture=(0x60, 0x10), ) -am.DoubleStoneSlab = am.Block(43, +am.DoubleStoneSlab = am.Block(43, name="Double Stone Slab", - texture=((0x50,0x00), (0x50,0x00), (0x60,0x00), (0x60,0x00), (0x50,0x00), (0x50,0x00)), + texture=((0x50, 0x00), (0x50, 0x00), (0x60, 0x00), (0x60, 0x00), (0x50, 0x00), (0x50, 0x00)), ) am.DoubleSandstoneSlab = am.Block(43, blockData=1, name="Double Sandstone Slab", - texture=((0x00,0xC0), (0x00,0xC0), (0x00,0xB0), (0x00,0xD0), (0x00,0xC0), (0x00,0xC0)), + texture=((0x00, 0xC0), (0x00, 0xC0), (0x00, 0xB0), (0x00, 0xD0), (0x00, 0xC0), (0x00, 0xC0)), color=am.Sandstone.color, ) am.DoubleWoodenSlab = am.Block(43, blockData=2, name="Double Wooden Slab", - texture=(0x40,0x00), + texture=(0x40, 0x00), color=am.WoodPlanks.color ) - + am.DoubleCobblestoneSlab = am.Block(43, blockData=3, name="Double Cobblestone Slab", - texture=(0x00,0x10), - ) + texture=(0x00, 0x10), + ) -am.StoneSlab = am.Block(44, +am.StoneSlab = am.Block(44, name="Stone Slab", - texture=((0x50,0x00), (0x50,0x00), (0x60,0x00), (0x60,0x00), (0x50,0x00), (0x50,0x00)), + texture=((0x50, 0x00), (0x50, 0x00), (0x60, 0x00), (0x60, 0x00), (0x50, 0x00), (0x50, 0x00)), ) am.SandstoneSlab = am.Block(44, blockData=1, name="Sandstone Slab", - texture=((0x00,0xC0), (0x00,0xC0), (0x00,0xB0), (0x00,0xD0), (0x00,0xC0), (0x00,0xC0)), + texture=((0x00, 0xC0), (0x00, 0xC0), (0x00, 0xB0), (0x00, 0xD0), (0x00, 0xC0), (0x00, 0xC0)), color=am.Sandstone.color, ) am.WoodenSlab = am.Block(44, blockData=2, name="Wooden Slab", - texture=(0x40,0x00), + texture=(0x40, 0x00), color=am.WoodPlanks.color ) - + am.CobblestoneSlab = am.Block(44, blockData=3, name="Cobblestone Slab", - texture=(0x00,0x10), - ) - - -am.Brick = am.Block(45, + texture=(0x00, 0x10), + ) + + +am.Brick = am.Block(45, name="Brick", - texture=(0x70,0x00), + texture=(0x70, 0x00), ) -am.TNT = am.Block(46, +am.TNT = am.Block(46, name="TNT", - texture=((0x80,0x00), (0x80,0x00), (0x90,0x00), (0xA0,0x00), (0x80,0x00), (0x80,0x00)), + texture=((0x80, 0x00), (0x80, 0x00), (0x90, 0x00), (0xA0, 0x00), (0x80, 0x00), (0x80, 0x00)), ) -am.Bookshelf = am.Block(47, +am.Bookshelf = am.Block(47, name="Bookshelf", - texture=((0x30,0x20), (0x30,0x20), (0x40,0x00), (0x40,0x00), (0x30,0x20), (0x30,0x20)), + texture=((0x30, 0x20), (0x30, 0x20), (0x40, 0x00), (0x40, 0x00), (0x30, 0x20), (0x30, 0x20)), ) -am.MossStone = am.Block(48, +am.MossStone = am.Block(48, name="Moss Stone", aka="Mossy Cobblestone", - texture=(0x40,0x20), + texture=(0x40, 0x20), ) -am.Obsidian = am.Block(49, +am.Obsidian = am.Block(49, name="Obsidian", - texture=(0x50,0x20), + texture=(0x50, 0x20), ) -am.Torch = am.Block(50, +am.Torch = am.Block(50, name="Torch", - texture=(0x00,0x50), + texture=(0x00, 0x50), brightness=14, opacity=0, ) -am.Fire = am.Block(51, +am.Fire = am.Block(51, name="Fire", - texture=(0xF0,0x10), + texture=(0xF0, 0x10), brightness=15, ) -am.MonsterSpawner = am.Block(52, +am.MonsterSpawner = am.Block(52, name="Monster Spawner", aka="Mob Cage", - texture=(0x10,0x40), + texture=(0x10, 0x40), opacity=0, ) -am.WoodenStairs = am.Block(53, +am.WoodenStairs = am.Block(53, name="Wooden Stairs", - texture=(0x40,0x00), + texture=(0x40, 0x00), opacity=0, ) -am.Chest = am.Block(54, +am.Chest = am.Block(54, name="Chest", - texture=((0xA0,0x10), (0xA0,0x10), (0xA0,0x10), (0xB0,0x10), (0x90,0x10), (0x90,0x10)), + texture=((0xA0, 0x10), (0xA0, 0x10), (0xA0, 0x10), (0xB0, 0x10), (0x90, 0x10), (0x90, 0x10)), ) -am.RedstoneWire = am.Block(55, +am.RedstoneWire = am.Block(55, name="Redstone Wire", - texture=(0x40,0xA0), #note: as of 1.5 the texture is unsaturated like leaves + texture=(0x40, 0xA0), #note: as of 1.5 the texture is unsaturated like leaves opacity=0, ) -am.DiamondOre = am.Block(56, +am.DiamondOre = am.Block(56, name="Diamond Ore", - texture=(0x20,0x30), + texture=(0x20, 0x30), ) -am.BlockofDiamond = am.Block(57, +am.BlockofDiamond = am.Block(57, name="Block of Diamond", - texture=(0x80,0x10), + texture=(0x80, 0x10), ) -am.CraftingTable = am.Block(58, +am.CraftingTable = am.Block(58, name="Crafting Table", aka="Workbench", - texture=((0xB0,0x30), (0xB0,0x30), (0xB0,0x20), (0x40,0x10), (0xC0,0x30), (0xC0,0x30)), + texture=((0xB0, 0x30), (0xB0, 0x30), (0xB0, 0x20), (0x40, 0x10), (0xC0, 0x30), (0xC0, 0x30)), ) -am.Crops = am.Block(59, +am.Crops = am.Block(59, name="Crops", aka="Wheat", - texture=(0xF0,0x50), + texture=(0xF0, 0x50), opacity=0, ) -am.Farmland = am.Block(60, +am.Farmland = am.Block(60, name="Farmland", aka="Soil", - texture=(0x60,0x50), + texture=(0x60, 0x50), ) -am.Furnace = am.Block(61, +am.Furnace = am.Block(61, name="Furnace", - texture=((0xD0,0x20), (0xD0,0x20), (0x10,0x00), (0x10,0x00), (0xC0,0x20), (0xC0,0x20)), + texture=((0xD0, 0x20), (0xD0, 0x20), (0x10, 0x00), (0x10, 0x00), (0xC0, 0x20), (0xC0, 0x20)), ) -am.LitFurnace = am.Block(62, +am.LitFurnace = am.Block(62, name="Lit Furnace", - texture=((0xD0,0x20), (0xD0,0x20), (0x10,0x00), (0x10,0x00), (0xD0,0x30), (0xD0,0x30)), + texture=((0xD0, 0x20), (0xD0, 0x20), (0x10, 0x00), (0x10, 0x00), (0xD0, 0x30), (0xD0, 0x30)), brightness=14, ) -am.Sign = am.Block(63, +am.Sign = am.Block(63, name="Sign", - texture=(0x80,0xB0), + texture=(0x80, 0xB0), opacity=0, ) -am.WoodenDoor = am.Block(64, +am.WoodenDoor = am.Block(64, name="Wooden Door", - texture=(0x10,0x50), + texture=(0x10, 0x50), opacity=0, ) -am.Ladder = am.Block(65, +am.Ladder = am.Block(65, name="Ladder", - texture=(0x30,0x50), + texture=(0x30, 0x50), opacity=0, ) -am.Rail = am.Block(66, +am.Rail = am.Block(66, name="Rail", aka="Minecart Track", - texture=(0x00,0x80), + texture=(0x00, 0x80), opacity=0, ) -am.StoneStairs = am.Block(67, +am.StoneStairs = am.Block(67, name="Stone Stairs", - texture=(0x00,0x10), + texture=(0x00, 0x10), opacity=0, ) -am.WallSign = am.Block(68, +am.WallSign = am.Block(68, name="Wall Sign", - texture=(0x80,0xB0), + texture=(0x80, 0xB0), opacity=0, ) -am.Lever = am.Block(69, +am.Lever = am.Block(69, name="Lever", aka="Switch", - texture=(0x80,0xB0), + texture=(0x80, 0xB0), opacity=0, ) -am.StoneFloorPlate = am.Block(70, +am.StoneFloorPlate = am.Block(70, name="Stone Floor Plate", - texture=(0x80,0xB0), + texture=(0x80, 0xB0), opacity=0, ) -am.IronDoor = am.Block(71, +am.IronDoor = am.Block(71, name="Iron Door", - texture=(0x20,0x50), + texture=(0x20, 0x50), opacity=0, ) -am.WoodFloorPlate = am.Block(72, +am.WoodFloorPlate = am.Block(72, name="Wood Floor Plate", - texture=(0x80,0xB0), + texture=(0x80, 0xB0), opacity=0, ) -am.RedstoneOre = am.Block(73, +am.RedstoneOre = am.Block(73, name="Redstone Ore", - texture=(0x30,0x30), + texture=(0x30, 0x30), ) -am.RedstoneOreGlowing = am.Block(74, +am.RedstoneOreGlowing = am.Block(74, name="Redstone Ore (glowing)", - texture=(0x30,0x30), + texture=(0x30, 0x30), brightness=9, ) -am.RedstoneTorchOff = am.Block(75, +am.RedstoneTorchOff = am.Block(75, name="Redstone Torch (off)", - texture=(0x30,0x70), + texture=(0x30, 0x70), opacity=0, ) -am.RedstoneTorchOn = am.Block(76, +am.RedstoneTorchOn = am.Block(76, name="Redstone Torch (on)", - texture=(0x30,0x60), + texture=(0x30, 0x60), opacity=0, brightness=7, ) -am.Button = am.Block(77, +am.Button = am.Block(77, name="Button", - texture=(0x80,0xB0), + texture=(0x80, 0xB0), opacity=0, ) -am.SnowLayer = am.Block(78, +am.SnowLayer = am.Block(78, name="Snow Layer", - texture=(0x20,0x40), + texture=(0x20, 0x40), opacity=0, ) -am.Ice = am.Block(79, +am.Ice = am.Block(79, name="Ice", - texture=(0x30,0x40), + texture=(0x30, 0x40), opacity=3, ) -am.Snow = am.Block(80, +am.Snow = am.Block(80, name="Snow", - texture=(0x20,0x40), + texture=(0x20, 0x40), ) -am.Cactus = am.Block(81, +am.Cactus = am.Block(81, name="Cactus", - texture=((0x60,0x40), (0x60,0x40), (0x70,0x40), (0x50,0x40), (0x60,0x40), (0x60,0x40)), + texture=((0x60, 0x40), (0x60, 0x40), (0x70, 0x40), (0x50, 0x40), (0x60, 0x40), (0x60, 0x40)), ) -am.Clay = am.Block(82, +am.Clay = am.Block(82, name="Clay", - texture=(0x80,0x40), + texture=(0x80, 0x40), ) -am.SugarCane = am.Block(83, +am.SugarCane = am.Block(83, name="Sugar Cane", aka="Reeds, Papyrus", - texture=(0x90,0x40), + texture=(0x90, 0x40), opacity=0, ) -am.Jukebox = am.Block(84, +am.Jukebox = am.Block(84, name="Jukebox", - texture=((0xA0,0x40), (0xA0,0x40), (0xA0,0x40), (0xB0,0x40), (0xA0,0x40), (0xA0,0x40)), + texture=((0xA0, 0x40), (0xA0, 0x40), (0xA0, 0x40), (0xB0, 0x40), (0xA0, 0x40), (0xA0, 0x40)), ) -am.Fence = am.Block(85, +am.Fence = am.Block(85, name="Fence", - texture=(0x80,0xB0), + texture=(0x80, 0xB0), opacity=0, ) -am.Pumpkin = am.Block(86, +am.Pumpkin = am.Block(86, name="Pumpkin", - texture=((0x70,0x70), (0x60,0x70), (0x60,0x60), (0x60,0x70), (0x60,0x70), (0x60,0x70)), + texture=((0x70, 0x70), (0x60, 0x70), (0x60, 0x60), (0x60, 0x70), (0x60, 0x70), (0x60, 0x70)), color=(0xcc, 0x77, 0x18, 0xFF) ) -am.Netherrack = am.Block(87, +am.Netherrack = am.Block(87, name="Netherrack", aka="Bloodstone", - texture=(0x70,0x60), + texture=(0x70, 0x60), ) -am.SoulSand = am.Block(88, +am.SoulSand = am.Block(88, name="Soul Sand", aka="Slow Sand", - texture=(0x80,0x60), + texture=(0x80, 0x60), ) -am.Glowstone = am.Block(89, +am.Glowstone = am.Block(89, name="Glowstone", - texture=(0x90,0x60), + texture=(0x90, 0x60), brightness=15, color=(0xFF, 0xEE, 0x00, 0xFF) ) -am.NetherPortal = am.Block(90, +am.NetherPortal = am.Block(90, name="Nether Portal", - texture=(0x80,0xB0), + texture=(0x80, 0xB0), opacity=0, brightness=11, ) -am.JackOLantern = am.Block(91, +am.JackOLantern = am.Block(91, name="Jack-o'-Lantern", - texture=((0x80,0x70), (0x60,0x70), (0x60,0x60), (0x60,0x70), (0x60,0x70), (0x60,0x70)), + texture=((0x80, 0x70), (0x60, 0x70), (0x60, 0x60), (0x60, 0x70), (0x60, 0x70), (0x60, 0x70)), brightness=15, color=(0xcc, 0x77, 0x18, 0xFF) ) -am.Cake = am.Block(92, +am.Cake = am.Block(92, name="Cake", - texture=((0xA0,0x70), (0xA0,0x70), (0x90,0x70), (0xC0,0x70), (0xA0,0x70), (0xA0,0x70)), + texture=((0xA0, 0x70), (0xA0, 0x70), (0x90, 0x70), (0xC0, 0x70), (0xA0, 0x70), (0xA0, 0x70)), opacity=0, ) -am.RedstoneRepeaterOff = am.Block(93, +am.RedstoneRepeaterOff = am.Block(93, name="Redstone Repeater (Off)", texture=(0x30, 0x80), opacity=0, ) -am.RedstoneRepeaterOn = am.Block(94, +am.RedstoneRepeaterOn = am.Block(94, name="Redstone Repeater (On)", texture=(0x30, 0x90), opacity=0, ) -am.AprilFoolsChest = am.Block(95, +am.AprilFoolsChest = am.Block(95, name="April Fools Chest", - texture=((0xA0,0x10), (0xA0,0x10), (0xA0,0x10), (0xB0,0x10), (0x90,0x10), (0x90,0x10)), + texture=((0xA0, 0x10), (0xA0, 0x10), (0xA0, 0x10), (0xB0, 0x10), (0x90, 0x10), (0x90, 0x10)), ) - -am.Trapdoor = am.Block(96, + +am.Trapdoor = am.Block(96, name="Trapdoor", - texture=(0x10,0x50), + texture=(0x10, 0x50), opacity=0, ) - + del am from classicmaterials import classicMaterials @@ -987,18 +987,18 @@ classicMaterials.lightAbsorption = materials.lightAbsorption classicMaterials.lightEmission = materials.lightEmission -namedMaterials = dict( (i.name, i) for i in (materials, classicMaterials) ) +namedMaterials = dict((i.name, i) for i in (materials, classicMaterials)) #filters certain block types in old maps to types available in /game/ blockFilterClassicToAlpha = arange(256, dtype=uint8) b = blockFilterClassicToAlpha -b[8]=9; #water to still water -b[10]=11; #lava to still lava -b[36]=35; # the new white cloth -b[52]=9; # infinite water source - now mob spawner -b[53]=11; # infinite lava source - now wooden stair -b[55]=35; # cog - 55 is now red wire +b[8] = 9; #water to still water +b[10] = 11; #lava to still lava +b[36] = 35; # the new white cloth +b[52] = 9; # infinite water source - now mob spawner +b[53] = 11; # infinite lava source - now wooden stair +b[55] = 35; # cog - 55 is now red wire del b; for i in range(21, 35): blockFilterClassicToAlpha[i] = 35; # recolor all cloth to white @@ -1009,7 +1009,7 @@ blockFilterAlphaToClassic[50:255] = 36 # change all blocks from torch (50) on up ### xxx add filter tables for indev blocks -nullConversionTable = arange(256, dtype=uint8) +nullConversionTable = arange(256, dtype=uint8) materials.conversionTables = { @@ -1020,7 +1020,7 @@ materials.conversionTables = { classicMaterials.conversionTables = { materials: blockFilterClassicToAlpha, classicMaterials: nullConversionTable, - + }; diff --git a/mce.py b/mce.py index cb2e40f..1ff8183 100644 --- a/mce.py +++ b/mce.py @@ -17,7 +17,7 @@ try: import readline except: pass - + class UsageError(RuntimeError): pass class BlockMatchError(RuntimeError): pass class PlayerNotFound(RuntimeError): pass @@ -80,116 +80,116 @@ class mce(object): last_played = os.getenv("MCE_LAST_PLAYED", None) def commandUsage(self, command): " returns usage info for the named command - just give the docstring for the handler func " - func = getattr(self, "_"+command) + func = getattr(self, "_" + command) return func.__doc__ - + commands = [ "clone", "fill", "replace", - + "export", "execute", "import", - + "createchest", - + "player", "spawn", - + "removeentities", "dumpsigns", "dumpchests", - + "createchunks", "deletechunks", "prune", "relight", - + "create", "degrief", "time", "worldsize", "heightmap", "randomseed", - + "save", "load", "reload", "dimension", "repair", - + "quit", "exit", - + "help", "blocks", "analyze", - + "debug", "log", "box", ] debug = False needsSave = False; - + def readInt(self, command): try: val = int(command.pop(0)) except ValueError: raise UsageError, "Cannot understand numeric input" return val - + def prettySplit(self, command): cmdstring = " ".join(command); - + lex = shlex.shlex(cmdstring); lex.whitespace_split = True; lex.whitespace += "()," - + command[:] = list(lex) - + def readBox(self, command): self.prettySplit(command) - + sourcePoint = self.readIntPoint(command) if command[0].lower() == "to": command.pop(0) sourcePoint2 = self.readIntPoint(command) sourceSize = map(operator.sub, sourcePoint2, sourcePoint) else: - sourceSize = self.readIntPoint(command, isPoint = False) + sourceSize = self.readIntPoint(command, isPoint=False) if len([p for p in sourceSize if p <= 0]): raise UsageError, "Box size cannot be zero or negative" box = BoundingBox(sourcePoint, sourceSize) return box - - def readIntPoint(self, command, isPoint = True): + + def readIntPoint(self, command, isPoint=True): point = self.readPoint(command, isPoint) point = map(int, map(floor, point)) return point - - def readPoint(self, command, isPoint = True): + + def readPoint(self, command, isPoint=True): self.prettySplit(command) try: word = command.pop(0) if isPoint and (word in self.level.players): - x,y,z = self.level.getPlayerPosition(word) + x, y, z = self.level.getPlayerPosition(word) if len(command) and command[0].lower() == "delta": command.pop(0) try: x += int(command.pop(0)) y += int(command.pop(0)) z += int(command.pop(0)) - + except ValueError: raise UsageError, "Error decoding point input (expected a number)." - return (x,y,z) - + return (x, y, z) + except IndexError: raise UsageError, "Error decoding point input (expected more values)." - - + + try: try: x = float(word) @@ -203,15 +203,15 @@ class mce(object): raise UsageError, "Error decoding point input (expected a number)." except IndexError: raise UsageError, "Error decoding point input (expected more values)." - - return (x,y,z) - + + return (x, y, z) + def readBlockInfo(self, command): keyword = command.pop(0) - + matches = self.level.materials.blocksMatching(keyword) blockInfo = None - + if len(matches): if len(matches) == 1: blockInfo = matches[0] @@ -219,8 +219,8 @@ class mce(object): #eat up more words that possibly specify a block. stop eating when 0 matching blocks. while len(command): newMatches = self.level.materials.blocksMatching(keyword + " " + command[0]); - - + + if len(newMatches) == 1: blockInfo = newMatches[0] if len(newMatches) > 0: @@ -228,9 +228,9 @@ class mce(object): keyword = keyword + " " + command.pop(0) else: break; - - - + + + else: try: data = 0 @@ -239,12 +239,12 @@ class mce(object): else: blockID = int(keyword) blockInfo = self.level.materials.blockWithID(blockID, data) - + except ValueError: blockInfo = None; - + if blockInfo is None: - print "Ambiguous block specifier: ", keyword + print "Ambiguous block specifier: ", keyword if len(matches): print "Matches: " for m in matches: @@ -253,9 +253,9 @@ class mce(object): else: print "No blocks matched." raise BlockMatchError - + return blockInfo - + def readBlocksToCopy(self, command): blocksToCopy = range(256); while len(command): @@ -265,9 +265,9 @@ class mce(object): if word == "nowater": blocksToCopy.remove(8); blocksToCopy.remove(9); - + return blocksToCopy - + def _box(self, command): """ Boxes: @@ -321,8 +321,8 @@ class mce(object): raise UsageError, "Cannot understand numeric input." else: print "Log level: {0}".format(logging.getLogger().level) - - + + def _clone(self, command): """ clone [noair] [nowater] @@ -334,20 +334,20 @@ class mce(object): if len(command) == 0: self.printUsage("clone") return; - + box = self.readBox(command); - + destPoint = self.readPoint(command) - + destPoint = map(int, map(floor, destPoint)) blocksToCopy = self.readBlocksToCopy(command); - + tempSchematic = self.level.extractSchematic(box); - self.level.copyBlocksFrom(tempSchematic, BoundingBox((0,0,0), box.origin), destPoint, blocksToCopy); - + self.level.copyBlocksFrom(tempSchematic, BoundingBox((0, 0, 0), box.origin), destPoint, blocksToCopy); + self.needsSave = True; - print "Cloned 0 blocks." - + print "Cloned 0 blocks." + def _fill(self, command): """ fill [ ] @@ -360,22 +360,22 @@ class mce(object): if len(command) == 0: self.printUsage("fill") return; - + blockInfo = self.readBlockInfo(command) - + if len(command): box = self.readBox(command) else: box = None - + print "Filling with {0}".format(blockInfo.name) - + self.level.fillBlocks(box, blockInfo) - - + + self.needsSave = True; print "Filled {0} blocks.".format("all" if box is None else box.volume) - + def _replace(self, command): """ replace [with] [ ] @@ -389,23 +389,23 @@ class mce(object): if len(command) == 0: self.printUsage("replace") return; - + blockInfo = self.readBlockInfo(command) - - if command[0].lower() == "with": + + if command[0].lower() == "with": command.pop(0) newBlockInfo = self.readBlockInfo(command) - + if len(command): box = self.readBox(command) else: box = None - + print "Replacing {0} with {1}".format(blockInfo.name, newBlockInfo.name) - - self.level.fillBlocks(box, newBlockInfo, blocksToReplace = [blockInfo]) - + + self.level.fillBlocks(box, newBlockInfo, blocksToReplace=[blockInfo]) + self.needsSave = True; print "Done." @@ -421,11 +421,11 @@ class mce(object): count = 64; if len(command): count = self.readInt(command) - + chest = mclevel.MCSchematic.chestWithItemID(itemID, count); self.level.copyBlocksFrom(chest, chest.bounds, point); self.needsSave = True; - + def _analyze(self, command): """ analyze @@ -434,27 +434,27 @@ class mce(object): Also updates the level's 'SizeOnDisk' field, correcting its size in the world select menu. """ - blockCounts = zeros( (4096,), 'uint64') + blockCounts = zeros((4096,), 'uint64') sizeOnDisk = 0; - + print "Analyzing {0} chunks...".format(self.level.chunkCount) #for input to bincount, create an array of uint16s by #shifting the data left and adding the blocks - - + + for i, cPos in enumerate(self.level.allChunks, 1): ch = self.level.getChunk(*cPos); btypes = numpy.array(ch.Data.ravel(), dtype='uint16') btypes <<= 8 btypes += ch.Blocks.ravel() counts = bincount(btypes) - + blockCounts[:counts.shape[0]] += counts sizeOnDisk += ch.compressedSize(); ch.unload(); if i % 100 == 0: - logging.info( "Chunk {0}...".format( i ) ) - + logging.info("Chunk {0}...".format(i)) + for blockID in range(256): block = self.level.materials.blockWithID(blockID, 0) if block.hasAlternate: @@ -462,21 +462,21 @@ class mce(object): i = (data << 8) + blockID if blockCounts[i]: idstring = "({id}:{data})".format(id=blockID, data=data) - + print "{idstring:9} {name:30}: {count:<10}".format( idstring=idstring, name=self.level.materials.blockWithID(blockID, data).name, count=blockCounts[i]); - + else: - count = int(sum( blockCounts[(d<<8)+blockID] for d in range(16) )) + count = int(sum(blockCounts[(d << 8) + blockID] for d in range(16))) if count: idstring = "({id})".format(id=blockID) print "{idstring:9} {name:30}: {count:<10}".format( idstring=idstring, name=self.level.materials.blockWithID(blockID, 0).name, count=count); - + print "Size on disk: {0:.3}MB".format(sizeOnDisk / 1048576.0) self.level.SizeOnDisk = sizeOnDisk self.needsSave = True - + def _export(self, command): """ export @@ -487,16 +487,16 @@ class mce(object): if len(command) == 0: self.printUsage("export") return; - + filename = command.pop(0) box = self.readBox(command) - + tempSchematic = self.level.extractSchematic(box); - + tempSchematic.saveToFile(filename) - - print "Exported {0} blocks.".format(tempSchematic.bounds.volume) - + + print "Exported {0} blocks.".format(tempSchematic.bounds.volume) + def _import(self, command): """ import [noair] [nowater] @@ -514,22 +514,22 @@ class mce(object): if len(command) == 0: self.printUsage("import") return; - + filename = command.pop(0) destPoint = self.readPoint(command) blocksToCopy = self.readBlocksToCopy(command) - + importLevel = mclevel.fromFile(filename) - + destBox = BoundingBox(destPoint, importLevel.size) self.level.createChunksInBox(destBox); - + self.level.copyBlocksFrom(importLevel, importLevel.bounds, destPoint, blocksToCopy); - - + + self.needsSave = True; - print "Imported {0} blocks.".format(importLevel.bounds.volume) - + print "Imported {0} blocks.".format(importLevel.bounds.volume) + def _player(self, command): """ player [ [ ] ] @@ -545,36 +545,36 @@ class mce(object): for player in self.level.players: print " {0}: {1}".format(player, self.level.getPlayerPosition(player)) return; - + player = command.pop(0) if len(command) == 0: print "Player {0}: {1}".format(player, self.level.getPlayerPosition(player)) return; - + point = self.readPoint(command) self.level.setPlayerPosition(point, player) - + self.needsSave = True; print "Moved player {0} to {1}".format(player, point) - + def _spawn(self, command): """ spawn [ ] Move the world's spawn point. Without a point, prints the world's spawn point. - """ + """ if len(command): point = self.readPoint(command) point = map(int, map(floor, point)) - + self.level.setPlayerSpawnPosition(point); - + self.needsSave = True; print "Moved spawn point to ", point else: - print "Spawn point: ", self.level.playerSpawnPosition(); - + print "Spawn point: ", self.level.playerSpawnPosition(); + def _dumpsigns(self, command): """ dumpSigns [ ] @@ -599,33 +599,33 @@ class mce(object): filename = command[0] else: filename = self.level.displayName + ".signs" - - outFile = codecs.open(filename, "w", encoding = 'utf-8'); - + + outFile = codecs.open(filename, "w", encoding='utf-8'); + print "Dumping signs..." signCount = 0; - + for i, cPos in enumerate(self.level.allChunks): try: chunk = self.level.getChunk(*cPos); except mclevel.ChunkMalformed: continue; - + for tileEntity in chunk.TileEntities: if tileEntity["id"].value == "Sign": signCount += 1; - + outFile.write(str(map(lambda x:tileEntity[x].value, "xyz")) + "\n"); for i in range(4): - outFile.write(tileEntity["Text{0}".format(i+1)].value + u"\n"); - + outFile.write(tileEntity["Text{0}".format(i + 1)].value + u"\n"); + if i % 100 == 0: print "Chunk {0}...".format(i) - + chunk.unload(); - + print "Dumped {0} signs to {1}".format(signCount, filename); - + outFile.close(); def _repair(self, command): @@ -646,8 +646,8 @@ class mce(object): self.level.preloadRegions() for rf in self.level.regionFiles.itervalues(): rf.repair() - - + + def _dumpchests(self, command): """ dumpChests [ ] @@ -673,22 +673,22 @@ class mce(object): filename = command[0] else: filename = self.level.displayName + ".chests" - + outFile = file(filename, "w"); - + print "Dumping chests..." chestCount = 0; - + for i, cPos in enumerate(self.level.allChunks): try: chunk = self.level.getChunk(*cPos); except mclevel.ChunkMalformed: continue; - + for tileEntity in chunk.TileEntities: if tileEntity["id"].value == "Chest": chestCount += 1; - + outFile.write(str(map(lambda x:tileEntity[x].value, "xyz")) + "\n"); itemsTag = tileEntity["Items"] if len(itemsTag): @@ -697,24 +697,24 @@ class mce(object): id = itemTag["id"].value damage = itemTag["Damage"].value item = items.findItem(id, damage) - itemname=item.name + itemname = item.name except KeyError: - itemname="Unknown Item {0}".format(itemTag) + itemname = "Unknown Item {0}".format(itemTag) except Exception, e: itemname = repr(e) - outFile.write("{0} {1}\n".format(itemTag["Count"].value,itemname)); + outFile.write("{0} {1}\n".format(itemTag["Count"].value, itemname)); else: outFile.write("Empty Chest\n") - + if i % 100 == 0: print "Chunk {0}...".format(i) - + chunk.unload(); - + print "Dumped {0} chests to {1}".format(chestCount, filename); - + outFile.close(); - + def _removeentities(self, command): """ removeEntities [ [except] [ [ ... ] ] ] @@ -737,7 +737,7 @@ class mce(object): Known Dynamic Tile Entity IDs: PrimedTnt FallingSand """ removedEntities = {}; - + if len(command): if command[0].lower() == "except": command.pop(0); @@ -751,40 +751,40 @@ class mce(object): matchWords = map(lambda x:x.lower(), command) - + else: print "Removing all entities except Painting..." def match(entityID): return entityID != "Painting"; - - for cx,cz in self.level.allChunks: - chunk = self.level.getChunk(cx,cz) + + for cx, cz in self.level.allChunks: + chunk = self.level.getChunk(cx, cz) entitiesRemoved = 0; - + for entity in list(chunk.Entities): entityID = entity["id"].value - + if match(entityID): removedEntities[entityID] = removedEntities.get(entityID, 0) + 1; - + chunk.Entities.remove(entity) entitiesRemoved += 1; - + if entitiesRemoved: chunk.chunkChanged(False) - + chunk.compress(); chunk.save(); chunk.unload(); - + if len(removedEntities) == 0: print "No entities to remove." else: print "Removed entities:" for entityID in sorted(removedEntities.keys()): print " {0}: {1:6}".format(entityID, removedEntities[entityID]); - + self.needsSave = True; - + def _createchunks(self, command): """ createChunks @@ -796,13 +796,13 @@ class mce(object): if len(command) == 0: self.printUsage("createchunks") return; - + box = self.readBox(command) - + chunksCreated = self.level.createChunksInBox(box) - + print "Created {0} chunks." .format(len(chunksCreated)) - + self.needsSave = True; def _deletechunks(self, command): @@ -815,13 +815,13 @@ class mce(object): if len(command) == 0: self.printUsage("deletechunks") return; - + box = self.readBox(command) - + deletedChunks = self.level.deleteChunksInBox(box) - + print "Deleted {0} chunks." .format(len(deletedChunks)) - + def _prune(self, command): """ prune @@ -832,17 +832,17 @@ class mce(object): if len(command) == 0: self.printUsage("prune") return; - + box = self.readBox(command) - - i=0; - for cx,cz in list(self.level.allChunks): + + i = 0; + for cx, cz in list(self.level.allChunks): if cx < box.mincx or cx >= box.maxcx or cz < box.mincz or cz >= box.maxcz: - self.level.deleteChunk(cx,cz) - i+=1; - + self.level.deleteChunk(cx, cz) + i += 1; + print "Pruned {0} chunks." .format(i) - + def _relight(self, command): """ relight [ ] @@ -852,16 +852,16 @@ class mce(object): """ if len(command): box = self.readBox(command) - chunks = itertools.product(range(box.mincx, box.maxcx),range(box.mincz, box.maxcz)) - + chunks = itertools.product(range(box.mincx, box.maxcx), range(box.mincz, box.maxcz)) + else: chunks = self.level.allChunks - + self.level.generateLights(chunks) - - print "Relit 0 chunks." + + print "Relit 0 chunks." self.needsSave = True; - + def _create(self, command): """ create [ ] @@ -876,22 +876,22 @@ class mce(object): """ if len(command) < 1: raise UsageError, "Expected a filename" - + filename = command[0]; if not os.path.exists(filename): os.mkdir(filename); if not os.path.isdir(filename): raise IOError, "{0} already exists".format(filename) - + if mclevel.MCInfdevOldLevel.isLevel(filename): raise IOError, "{0} is already a Minecraft Alpha world".format(filename) - - level = mclevel.MCInfdevOldLevel(filename, create = True); - + + level = mclevel.MCInfdevOldLevel(filename, create=True); + self.level = level; - - + + def _degrief(self, command): """ degrief [ ] @@ -912,9 +912,9 @@ class mce(object): box.miny = int(command[0]) except ValueError: pass - + print "Removing grief matter and surface lava above height {0}...".format(box.miny) - + self.level.fillBlocks(box, self.level.materials.Air, blocksToReplace=[self.level.materials.Bedrock, @@ -925,7 +925,7 @@ class mce(object): ] ) self.needsSave = True; - + def _time(self, command): """ time [time of day] @@ -937,21 +937,21 @@ class mce(object): timeOfDay = ticks % 24000; ageInTicks = ticks - timeOfDay; if len(command) == 0: - + days = ageInTicks / 24000 hours = timeOfDay / 1000; clockHours = (hours + 6) % 24 - + ampm = ("AM", "PM")[clockHours > 11] - + minutes = (timeOfDay % 1000) / 60; - - print "It is {0}:{1:02} {2} on Day {3}".format(clockHours%12 or 12, minutes, ampm, days) + + print "It is {0}:{1:02} {2} on Day {3}".format(clockHours % 12 or 12, minutes, ampm, days) else: times = { "morning":6, "noon":12, "evening":18, "midnight":24 } word = command[0]; minutes = 0; - + if word in times: hours = times[word] else: @@ -964,19 +964,19 @@ class mce(object): hours = int(word) except Exception, e: raise UsageError, ("Cannot interpret time, ", e); - + if len(command) > 1: if command[1].lower() == "pm": hours += 12; - + ticks = ageInTicks + hours * 1000 + minutes * 1000 / 60 - 6000; if ticks < 0: ticks += 18000 - + ampm = ("AM", "PM")[hours > 11 and hours < 24] - print "Changed time to {0}:{1:02} {2}".format(hours%12 or 12, minutes, ampm) + print "Changed time to {0}:{1:02} {2}".format(hours % 12 or 12, minutes, ampm) self.level.Time = ticks self.needsSave = True; - + def _randomseed(self, command): """ randomseed [ ] @@ -989,12 +989,12 @@ class mce(object): seed = long(command[0]); except ValueError: raise UsageError, "Expected a long integer." - + self.level.RandomSeed = seed; self.needsSave = True else: print "Random Seed: ", self.level.RandomSeed - + def _worldsize(self, command): """ worldsize @@ -1006,11 +1006,11 @@ class mce(object): if isinstance(self.level, mclevel.MCInfdevOldLevel): print "\nWorld size: \n {0[0]:7} north to south\n {0[2]:7} east to west\n".format(bounds.size); print "Smallest and largest points: ({0[0]},{0[2]}), ({1[0]},{1[2]})".format(bounds.origin, bounds.maximum); - + else: print "\nWorld size: \n {0[0]:7} wide\n {0[1]:7} tall\n {0[2]:7} long\n".format(bounds.size); - - def _heightmap(self,command): + + def _heightmap(self, command): """ heightmap @@ -1033,74 +1033,74 @@ class mce(object): if not sys.stdin.isatty() or raw_input( "This will destroy a large portion of the map and may take a long time. Did you really want to do this?" ).lower() in ("yes", "y", "1", "true"): - - + + from PIL import Image import datetime filename = command.pop(0) imgobj = Image.open(filename) - + greyimg = imgobj.convert("L") #luminance del imgobj - + width, height = greyimg.size - - water_level = 64 - - xchunks = (height+15)/16 - zchunks = (width+15)/16 + + water_level = 64 + + xchunks = (height + 15) / 16 + zchunks = (width + 15) / 16 start = datetime.datetime.now() for cx in range(xchunks): for cz in range(zchunks): try: - self.level.createChunk(cx,cz) + self.level.createChunk(cx, cz) except: pass - c = self.level.getChunk(cx,cz) - - imgarray = numpy.asarray(greyimg.crop( (cz*16, cx*16, cz*16+16, cx*16+16) )) + c = self.level.getChunk(cx, cz) + + imgarray = numpy.asarray(greyimg.crop((cz * 16, cx * 16, cz * 16 + 16, cx * 16 + 16))) imgarray = imgarray / 2; #scale to 0-127 - + for x in range(16): for z in range(16): - if z+(cz*16) < width-1 and x+(cx*16) < height-1: + if z + (cz * 16) < width - 1 and x + (cx * 16) < height - 1: #world dimension X goes north-south #first array axis goes up-down - - h = imgarray[x,z] - - c.Blocks[x,z,h+1:] = 0 #air - c.Blocks[x,z,h:h+1] = 2 #grass - c.Blocks[x,z,h-4:h] = 3 #dirt - c.Blocks[x,z,:h-4] = 1 #rock + + h = imgarray[x, z] + + c.Blocks[x, z, h + 1:] = 0 #air + c.Blocks[x, z, h:h + 1] = 2 #grass + c.Blocks[x, z, h - 4:h] = 3 #dirt + c.Blocks[x, z, :h - 4] = 1 #rock if h < water_level: - c.Blocks[x,z,h+1:water_level] = 9 #water - if h < water_level+2: - c.Blocks[x,z,h-2:h+1] = 12 #sand if it's near water level - - c.Blocks[x,z,0] = 7 #bedrock - + c.Blocks[x, z, h + 1:water_level] = 9 #water + if h < water_level + 2: + c.Blocks[x, z, h - 2:h + 1] = 12 #sand if it's near water level + + c.Blocks[x, z, 0] = 7 #bedrock + c.chunkChanged() c.TerrainPopulated = False #the quick lighting from chunkChanged has already lit this simple terrain completely - c.needsLighting = False - - logging.info( "%s Just did chunk %d,%d" % (datetime.datetime.now().strftime("[%H:%M:%S]"),cx,cz) ) + c.needsLighting = False - logging.info( "Done with mapping!" ) + logging.info("%s Just did chunk %d,%d" % (datetime.datetime.now().strftime("[%H:%M:%S]"), cx, cz)) + + logging.info("Done with mapping!") self.needsSave = True; stop = datetime.datetime.now() - logging.info( "Took %s." % str(stop-start) ) - + logging.info("Took %s." % str(stop - start)) + spawnz = width / 2 spawnx = height / 2; spawny = greyimg.getpixel((spawnx, spawnz)) - logging.info( "You probably want to change your spawn point. I suggest {0}".format( (spawnx, spawny, spawnz) ) ) - + logging.info("You probably want to change your spawn point. I suggest {0}".format((spawnx, spawny, spawnz))) + def _execute(self, command): """ execute @@ -1109,13 +1109,13 @@ class mce(object): if len(command) == 0: print "You must give the file with commands to execute" else: - commandFile = open(command[0],"r") + commandFile = open(command[0], "r") commandsFromFile = commandFile.readlines() for commandFromFile in commandsFromFile: print commandFromFile self.processCommand(commandFromFile) self._save("") - + def _quit(self, command): """ quit [ yes | no ] @@ -1125,7 +1125,7 @@ class mce(object): In batch mode, an end of file automatically saves the level. """ - if len(command) == 0 or not (command[0].lower() in ("yes", "no")): + if len(command) == 0 or not (command[0].lower() in ("yes", "no")): if raw_input("Save before exit? ").lower() in ("yes", "y", "1", "true"): self._save(command); raise SystemExit; @@ -1133,29 +1133,29 @@ class mce(object): self._save(command); raise SystemExit - + def _exit(self, command): self._quit(command) - + def _save(self, command): if self.needsSave: self.level.generateLights() self.level.saveInPlace(); self.needsSave = False; - + def _load(self, command): """ load [ | ] Loads another world, discarding all changes to this world. - """ + """ if len(command) == 0: self.printUsage("load") self.loadWorld(command[0]) - + def _reload(self, command): self.level = mclevel.fromFile(self.level.filename); - + def _dimension(self, command): """ dimension [ ] @@ -1166,7 +1166,7 @@ class mce(object): nether, hell, slip: DIM-1 earth, overworld, parent: parent world """ - + if len(command): if command[0].lower() in ("earth", "overworld", "parent"): if self.level.parentWorld: @@ -1175,30 +1175,30 @@ class mce(object): else: print "You are already on earth." return; - + elif command[0].lower() in ("hell", "nether", "slip"): dimNo = -1 else: dimNo = self.readInt(command); - + if dimNo in self.level.dimensions: self.level = self.level.dimensions[dimNo] return; - + if self.level.parentWorld: print u"Parent world: {0} ('dimension parent' to return)".format(self.level.parentWorld.displayName); - + if len(self.level.dimensions): print u"Dimensions in {0}:".format(self.level.displayName) for k in self.level.dimensions: print "{0}: {1}".format(k, mclevel.MCAlphaDimension.dimensionNames.get(k, "Unknown")); - + def _help(self, command): if len(command): self.printUsage(command[0]) else: self.printUsage() - + def _blocks(self, command): """ blocks [ | ] @@ -1206,40 +1206,40 @@ class mce(object): Prints block IDs matching the name, or the name matching the ID. With nothing, prints a list of all blocks. """ - + searchName = None if len(command): searchName = " ".join(command) try: searchNumber = int(searchName) - except ValueError: + except ValueError: searchNumber = None else: print "{0:3}: {1}".format(searchNumber, self.level.materials.names[searchNumber]) return - + matches = self.level.materials.blocksMatching(searchName) else: matches = self.level.materials.allBlocks - + print "{id:9} : {name} {aka}".format(id="(ID:data)", name="Block name", aka="[Other names]") for b in sorted(matches): idstring = "({ID}:{data})".format(ID=b.ID, data=b.blockData) aka = b.aka and " [{aka}]".format(aka=b.aka) or "" - + print "{idstring:9} : {name} {aka}".format(idstring=idstring, name=b.name, aka=aka) - - def printUsage(self, command = ""): + + def printUsage(self, command=""): if command.lower() in self.commands: print "Usage: ", self.commandUsage(command.lower()); else: - print self.__doc__.format(commandPrefix=("","mce.py ")[not self.batchMode]); - - + print self.__doc__.format(commandPrefix=("", "mce.py ")[not self.batchMode]); + + def printUsageAndQuit(self): self.printUsage(); raise SystemExit; - + def loadWorld(self, world): worldpath = os.path.expanduser(world) @@ -1247,40 +1247,40 @@ class mce(object): self.level = mclevel.fromFile(worldpath) else: self.level = mclevel.loadWorld(world) - - - - - - - level = None - + + + + + + + level = None + batchMode = False; def run(self): logging.basicConfig(format=u'%(levelname)s:%(message)s') logging.getLogger().level = logging.INFO - + appPath = sys.argv.pop(0) - + if len(sys.argv): world = sys.argv.pop(0) - + if world.lower() in ("-h", "--help"): self.printUsageAndQuit() - + if len(sys.argv) and sys.argv[0].lower() == "create": #accept the syntax, "mce world3 create" self._create([world]); print "Created world {0}".format(world); - + sys.exit(0) else: self.loadWorld(world) else: self.batchMode = True; self.printUsage(); - + while True: try: world = raw_input("Please enter world name or path to world folder: ") @@ -1292,10 +1292,10 @@ class mce(object): print "Cannot open {0}: {1}".format(world, e); else: break; - - - - + + + + if len(sys.argv): #process one command from command line try: @@ -1303,7 +1303,7 @@ class mce(object): except UsageError: self.printUsageAndQuit(); self._save([]); - + else: #process many commands on standard input, maybe interactively command = [""] @@ -1313,7 +1313,7 @@ class mce(object): command = raw_input(u"{0}> ".format(self.level.displayName)) print self.processCommand(command) - + except EOFError, e: print "End of file. Saving automatically." self._save([]); @@ -1323,19 +1323,19 @@ class mce(object): traceback.print_exc(); print 'Exception during command: {0!r}'.format(e); print "Use 'debug' to enable tracebacks." - + #self.printUsage(); - - + + def processCommand(self, command): command = command.strip(); - + if len(command) == 0: return - + if command[0] == "#": return - + commandWords = command.split() - + keyword = commandWords.pop(0).lower() if not keyword in self.commands: matches = filter(lambda x:x.startswith(keyword), self.commands); @@ -1344,13 +1344,13 @@ class mce(object): elif len(matches): print "Ambiguous command. Matches: " for k in matches: - print " ",k; + print " ", k; return; else: raise UsageError, "Command {0} not recognized.".format(keyword) - + func = getattr(self, "_" + keyword) - + try: func(commandWords) except PlayerNotFound, e: @@ -1362,7 +1362,7 @@ class mce(object): if self.debug: traceback.print_exc() self.printUsage(keyword) - + def main(argv): profile = os.getenv("MCE_PROFILE", None) @@ -1375,7 +1375,7 @@ def main(argv): editor.run() return 0 - + if __name__ == '__main__': sys.exit(main(sys.argv)) - + diff --git a/mclevel.py b/mclevel.py index 4e4277a..8850524 100644 --- a/mclevel.py +++ b/mclevel.py @@ -192,12 +192,12 @@ import sys # we need to decode file paths from environment variables or else we get an error # if they are formatted or joined to a unicode string - + if sys.platform == "win32": #not sure why win32com is needed if the %APPDATA% var is available - try: + try: import win32com.client - + objShell = win32com.client.Dispatch("WScript.Shell") minecraftDir = os.path.join(objShell.SpecialFolders("AppData"), u".minecraft") except Exception, e: @@ -211,9 +211,9 @@ elif sys.platform == "darwin": else: minecraftDir = os.path.expanduser("~/.minecraft") minecraftDir.decode(sys.getfilesystemencoding()); - + saveFileDir = os.path.join(minecraftDir, u"saves") - + #if sys.platform == "win32": # from win32com.shell import shell, shellcon # saveFileDir = shell.SHGetPathFromIDListEx ( @@ -221,53 +221,53 @@ saveFileDir = os.path.join(minecraftDir, u"saves") # ) # - + def fromFile(filename, loadInfinite=True): ''' The preferred method for loading Minecraft levels of any type. pass False to loadInfinite if you'd rather not load infdev levels. ''' - info( u"Identifying " + filename ) - + info(u"Identifying " + filename) + class LoadingError(RuntimeError): pass - - + + if not filename: - raise IOError, "File not found: "+filename + raise IOError, "File not found: " + filename if not os.path.exists(filename): - raise IOError, "File not found: "+filename - + raise IOError, "File not found: " + filename + if (ZipSchematic._isLevel(filename)): - info( "Zipfile found, attempting zipped infinite level" ) + info("Zipfile found, attempting zipped infinite level") lev = ZipSchematic(filename); - info( "Detected zipped Infdev level" ) + info("Detected zipped Infdev level") return lev - + if (MCInfdevOldLevel._isLevel(filename)): - info( u"Detected Infdev level.dat" ) + info(u"Detected Infdev level.dat") if (loadInfinite): return MCInfdevOldLevel(filename=filename); else: raise ValueError, "Asked to load {0} which is an infinite level, loadInfinite was False".format(os.path.basename(filename)); - + if os.path.isdir(filename): raise ValueError, "Folder {0} was not identified as a Minecraft level.".format(os.path.basename(filename)); - + f = file(filename, 'rb'); rawdata = f.read() f.close() if len(rawdata) < 4: raise ValueError, "{0} is too small! ({1}) ".format(filename, len(rawdata)) - - - - + + + + data = fromstring(rawdata, dtype='uint8') if not data.any(): raise ValueError, "{0} contains only zeroes. This file is damaged beyond repair." - - + + if MCJavaLevel._isDataLevel(data): - info( u"Detected Java-style level" ) + info(u"Detected Java-style level") lev = MCJavaLevel(filename, data); lev.compressed = False; return lev; @@ -277,16 +277,16 @@ def fromFile(filename, loadInfinite=True): unzippedData = None; try: unzippedData = gunzip(rawdata) - except Exception,e: - info( u"Exception during Gzip operation, assuming {0} uncompressed: {1!r}".format(filename, e) ) + except Exception, e: + info(u"Exception during Gzip operation, assuming {0} uncompressed: {1!r}".format(filename, e)) if unzippedData is None: compressed = False; unzippedData = rawdata - + data = fromstring(unzippedData, dtype='uint8') - + if MCJavaLevel._isDataLevel(data): - info( u"Detected compressed Java-style level" ) + info(u"Detected compressed Java-style level") lev = MCJavaLevel(filename, data); lev.compressed = compressed; return lev; @@ -294,37 +294,37 @@ def fromFile(filename, loadInfinite=True): try: root_tag = nbt.load(buf=data); except Exception, e: - info( u"Error during NBT load: {0!r}".format(e) ) - info( u"Fallback: Detected compressed flat block array, yzx ordered " ) + info(u"Error during NBT load: {0!r}".format(e)) + info(u"Fallback: Detected compressed flat block array, yzx ordered ") try: lev = MCJavaLevel(filename, data); lev.compressed = compressed; return lev; except Exception, e2: raise LoadingError, ("Multiple errors encountered", e, e2) - + else: if(MCIndevLevel._isTagLevel(root_tag)): - info( u"Detected Indev .mclevel" ) + info(u"Detected Indev .mclevel") return MCIndevLevel(root_tag, filename) if(MCSchematic._isTagLevel(root_tag)): - info( u"Detected Schematic." ) + info(u"Detected Schematic.") return MCSchematic(root_tag=root_tag, filename=filename) - + if (INVEditChest._isTagLevel(root_tag)): - info( u"Detected INVEdit inventory file" ) + info(u"Detected INVEdit inventory file") return INVEditChest(root_tag=root_tag, filename=filename); - - + + #it must be a plain array of blocks. see if MCJavaLevel handles it. - + raise IOError, "Cannot detect file type." - + def loadWorld(name): filename = os.path.join(saveFileDir, name) return fromFile(filename) - + def loadWorldNumber(i): #deprecated filename = u"{0}{1}{2}{3}{1}".format(saveFileDir, os.sep, u"World", i) diff --git a/mclevelbase.py b/mclevelbase.py index a660eff..7d46c91 100644 --- a/mclevelbase.py +++ b/mclevelbase.py @@ -60,8 +60,8 @@ def unpack_first(func): return func(self, *args, **kw); upk_first.__doc__ = func.__doc__ return upk_first - -class PlayerNotFound(Exception): pass + +class PlayerNotFound(Exception): pass class ChunkNotPresent(Exception): pass class RegionMalformed(Exception): pass class ChunkMalformed(ChunkNotPresent): pass diff --git a/nbt.py b/nbt.py index 0893bf6..50fd22e 100644 --- a/nbt.py +++ b/nbt.py @@ -33,17 +33,17 @@ class NBTFormatError(RuntimeError): pass class TAG_Value(object): """Simple values. Subclasses override fmt to change the type and size. Subclasses may set dataType instead of overriding setValue for automatic data type coercion""" - + fmt = ">b"; tag = -1; #error! - + _value = None def getValue(self): return self._value def setValue(self, newVal): self._value = self.dataType(newVal) value = property(getValue, setValue, None, "Change the TAG's value. Data types are checked and coerced if needed.") - + _name = None def getName(self): return self._name @@ -52,29 +52,29 @@ class TAG_Value(object): def delName(self): self._name = "" name = property(getName, setName, delName, "Change the TAG's name. Coerced to a string.") - + def __init__(self, value=0, name=None, data=""): self.name = name if(data == ""): self.value = value - else: + else: (self.value,) = struct.unpack_from(self.fmt, data); - + def __repr__(self): return "%s( \"%s\" ): %s" % (str(self.__class__), self.name, repr(self.value)) - + def __str__(self): return self.pretty_string() - + def pretty_string(self, indent=0): if self.name: - return " "*indent + "%s( \"%s\" ): %s" % (str(self.__class__.__name__), self.name, self.value) + return " " * indent + "%s( \"%s\" ): %s" % (str(self.__class__.__name__), self.name, self.value) else: - return " "*indent + "%s: %s" % (str(self.__class__.__name__), self.value) - + return " " * indent + "%s: %s" % (str(self.__class__.__name__), self.value) + def nbt_length(self): return struct.calcsize(self.fmt); - + def write_tag(self, buf): buf.write(struct.pack(TAGfmt, self.tag)) def write_name(self, buf): @@ -82,7 +82,7 @@ class TAG_Value(object): TAG_String(self.name).write_value(buf) def write_value(self, buf): buf.write(struct.pack(self.fmt, self.value)) - + def save(self, filename="", buf=None): if(filename): self.saveGzipped(filename); @@ -100,7 +100,7 @@ class TAG_Value(object): except Exception, e: #print "Atomic Save: No existing file to rename" pass - + with closing(gzip.GzipFile(fileobj=sio, mode="wb", compresslevel=compresslevel)) as outputGz: self.save(buf=outputGz); outputGz.flush(); @@ -115,27 +115,27 @@ class TAG_Value(object): except Exception, e: print e; return - + try: os.remove(filename + ".old"); except Exception, e: #print "Atomic Save: No old file to remove" pass; - + class TAG_Byte(TAG_Value): tag = 1; fmt = ">b"; dataType = int - + class TAG_Short(TAG_Value): tag = 2; fmt = ">h"; dataType = int - + class TAG_Int(TAG_Value): tag = 3; fmt = ">i"; dataType = int - + class TAG_Long(TAG_Value): tag = 4; fmt = ">q"; @@ -146,7 +146,7 @@ class TAG_Float(TAG_Value): fmt = ">f"; dataType = float - + class TAG_Double(TAG_Value): tag = 6; fmt = ">d"; @@ -162,22 +162,22 @@ class TAG_Byte_Array(TAG_Value): def dataType(self, value): return array(value, uint8) - + def __repr__(self): return "<%s: length %d> ( %s )" % (self.__class__, len(self.value), self.name) - - + + def pretty_string(self, indent=0): if self.name: - return " "*indent + "%s( \"%s\" ): shape=%s dtype=%s %s" % ( - str(self.__class__.__name__), - self.name, + return " " * indent + "%s( \"%s\" ): shape=%s dtype=%s %s" % ( + str(self.__class__.__name__), + self.name, str(self.value.shape), - str(self.value.dtype), + str(self.value.dtype), self.value) else: - return " "*indent + "%s: %s %s" % (str(self.__class__.__name__), str(self.value.shape), self.value) - + return " " * indent + "%s: %s %s" % (str(self.__class__.__name__), str(self.value.shape), self.value) + def __init__(self, value=zeros(0, uint8), name=None, data=""): self.name = name if(data == ""): @@ -185,21 +185,21 @@ class TAG_Byte_Array(TAG_Value): else: (string_len,) = struct.unpack_from(">I", data); self.value = fromstring(data[4:string_len + 4], 'uint8'); - + def nbt_length(self) : return len(self.value) + 4; - + def write_value(self, buf): #print self.value valuestr = self.value.tostring() buf.write(struct.pack(self.fmt % (len(valuestr),), len(valuestr), valuestr)) - + class TAG_Int_Array(TAG_Byte_Array): """An array of ints""" tag = 11; def dataType(self, value): return array(value, '>u4') - + def __init__(self, value=zeros(0, ">u4"), name=None, data=""): self.name = name if(data == ""): @@ -207,22 +207,22 @@ class TAG_Int_Array(TAG_Byte_Array): else: (string_len,) = struct.unpack_from(">I", data); self.value = fromstring(data[4:string_len * 4 + 4], '>u4') - - + + def nbt_length(self) : return len(self.value) * 4 + 4; - + def write_value(self, buf): #print self.value valuestr = self.value.tostring() - buf.write(struct.pack(self.fmt % (len(valuestr),), len(valuestr)/4, valuestr)) + buf.write(struct.pack(self.fmt % (len(valuestr),), len(valuestr) / 4, valuestr)) class TAG_Short_Array(TAG_Int_Array): """An array of ints""" tag = 12; def dataType(self, value): return array(value, '>u2') - + def __init__(self, value=zeros(0, ">u2"), name=None, data=""): self.name = name if(data == ""): @@ -230,25 +230,25 @@ class TAG_Short_Array(TAG_Int_Array): else: (string_len,) = struct.unpack_from(">I", data); self.value = fromstring(data[4:string_len * 2 + 4], '>u2') - - + + def nbt_length(self) : return len(self.value) * 2 + 4; - + def write_value(self, buf): #print self.value valuestr = self.value.tostring() - buf.write(struct.pack(self.fmt % (len(valuestr),), len(valuestr)/2, valuestr)) - + buf.write(struct.pack(self.fmt % (len(valuestr),), len(valuestr) / 2, valuestr)) + class TAG_String(TAG_Value): """String in UTF-8 The data parameter should either be a 'unicode' or an ascii-encoded 'str' """ - + tag = 8; fmt = ">h%ds" dataType = unicode - + def __init__(self, value="", name=None, data=""): self.name = name if(data == ""): @@ -263,7 +263,7 @@ class TAG_String(TAG_Value): def write_value(self, buf): u8value = self.value.encode('utf-8') buf.write(struct.pack(self.fmt % (len(u8value),), len(u8value), u8value)) - + @@ -273,29 +273,29 @@ class TAG_Compound(TAG_Value, collections.MutableMapping): operator []. This will automatically name the tags.""" tag = 10; - + def dataType(self, val): for i in val: assert isinstance(i, TAG_Value) assert i.name return list(val) - + def __repr__(self): return "%s( %s ): %s" % (str(self.__class__.__name__), self.name, self.value) - + def pretty_string(self, indent=0): if self.name: - pretty = " "*indent + "%s( \"%s\" ):\n" % (str(self.__class__.__name__), self.name) + pretty = " " * indent + "%s( \"%s\" ):\n" % (str(self.__class__.__name__), self.name) else: - pretty = " "*indent + "%s():\n" % (str(self.__class__.__name__)) + pretty = " " * indent + "%s():\n" % (str(self.__class__.__name__)) indent += 4 for tag in self.value: pretty += tag.pretty_string(indent) + "\n" return pretty - - + + def __init__(self, value=[], name="", data=""): - + self.name = name; if value.__class__ == ''.__class__: self.name = value; @@ -306,7 +306,7 @@ class TAG_Compound(TAG_Value, collections.MutableMapping): else: data_cursor = 0; - + while data_cursor < len(data): tag_type = data[data_cursor]; data_cursor += 1; @@ -314,21 +314,21 @@ class TAG_Compound(TAG_Value, collections.MutableMapping): break; assert_type(tag_type, data_cursor) - - + + data_cursor, tag = load_named(data, data_cursor, tag_type) - + self.value.append(tag); - - + + def nbt_length(self): return sum(x.nbt_length() + len(x.name) + 3 for x in self.value) + 1; - + def write_value(self, buf): for i in self.value: i.save(buf=buf) buf.write("\x00") - + "collection functions" def __getitem__(self, k): #hits=filter(lambda x:x.name==k, self.value); @@ -336,11 +336,11 @@ class TAG_Compound(TAG_Value, collections.MutableMapping): for key in self.value: if key.name == k: return key raise KeyError("Key {0} not found".format(k)); - + def __iter__(self): return itertools.imap(lambda x:x.name, self.value); def __contains__(self, k):return k in map(lambda x:x.name, self.value); def __len__(self): return self.value.__len__() - + def __setitem__(self, k, v): if not (v.__class__ in tag_handlers.values()): raise TypeError("Invalid type %s for TAG_Compound" % (v.__class__)) @@ -349,14 +349,14 @@ class TAG_Compound(TAG_Value, collections.MutableMapping): for i in olditems: self.value.remove(i) self.value.append(v); v.name = k; - + def __delitem__(self, k): self.value.__delitem__(self.value.index(self[k])); def add(self, v): self[v.name] = v; - + class TAG_List(TAG_Value, collections.MutableSequence): - + """A homogenous list of unnamed data of a single TAG_* type. Once created, the type can only be changed by emptying the list and adding an element of the new type. If created with no arguments, @@ -372,53 +372,53 @@ class TAG_List(TAG_Value, collections.MutableSequence): # FIXME: This is kinda weird; None as the empty tag name? assert all(isinstance(x, listType) and x.name in ("", "None") for x in val) return list(val) - + def __repr__(self): return "%s( %s ): %s" % (self.__class__.__name__, self.name, self.value) - - + + def pretty_string(self, indent=0): if self.name: - pretty = " "*indent + "%s( \"%s\" ):\n" % (str(self.__class__.__name__), self.name) + pretty = " " * indent + "%s( \"%s\" ):\n" % (str(self.__class__.__name__), self.name) else: - pretty = " "*indent + "%s():\n" % (str(self.__class__.__name__), ) - + pretty = " " * indent + "%s():\n" % (str(self.__class__.__name__),) + indent += 4 for tag in self.value: pretty += tag.pretty_string(indent) + "\n" return pretty - + def __init__(self, value=[], name=None, data=None, list_type=TAG_Compound): #can be created from a list of tags in value, with an optional #name, or created from raw tag data, or created with list_type #taken from a TAG class or instance - + self.name = name self.value = []; self.list_type = list_type.tag - + if(data == None): if(len(value)): self.list_type = value[0].tag; value = filter(lambda x:x.__class__ == value[0].__class__, value) - + self.value = value - else: + else: data_cursor = 0; self.list_type = data[data_cursor]; assert_type(self.list_type, data_cursor); - + data_cursor += 1; list_length = TAG_Int(data=data[data_cursor:]) data_cursor += list_length.nbt_length() list_length = list_length.value - + for i in range(list_length): - + tag = tag_handlers[self.list_type](data=data[data_cursor:]) self.append(tag); data_cursor += tag.nbt_length() @@ -428,35 +428,35 @@ class TAG_List(TAG_Value, collections.MutableSequence): def __contains__(self, k):return k in self.value; def __getitem__(self, i): return self.value[i]; def __len__(self): return len(self.value) - + def __setitem__(self, i, v): - if v.__class__ != tag_handlers[self.list_type]: + if v.__class__ != tag_handlers[self.list_type]: raise TypeError("Invalid type %s for TAG_List(%s)" % (v.__class__, tag_handlers[self.list_type])) v.name = "" self.value[i] = v; - - def __delitem__(self, i): + + def __delitem__(self, i): del self.value[i] - + def insert(self, i, v): if not v.tag in tag_handlers: raise TypeError("Not a tag type: %s" % (v,)) - if len(self) == 0: - self.list_type = v.tag + if len(self) == 0: + self.list_type = v.tag else: if v.__class__ != tag_handlers[self.list_type]: raise TypeError("Invalid type %s for TAG_List(%s)" % (v.__class__, tag_handlers[self.list_type])) - + v.name = "" self.value.insert(i, v); - + def nbt_length(self): return 5 + sum(x.nbt_length() for x in self.value) - + def write_value(self, buf): buf.write(struct.pack(TAGfmt, self.list_type)) TAG_Int(len(self)).write_value(buf) for i in self.value: i.write_value(buf) - + tag_handlers = { 1 : TAG_Byte, @@ -476,11 +476,11 @@ tag_handlers = { def assert_type(t, offset) : if not t in tag_handlers: raise ValueError("Unexpected tag type %d at %d" % (t, offset)); -import zlib +import zlib def gunzip(data): #strip off the header and use negative WBITS to tell zlib there's no header return zlib.decompress(data[10:], -zlib.MAX_WBITS) - + def loadFile(filename): #sio = StringIO.StringIO(); with file(filename, "rb") as f: @@ -491,14 +491,14 @@ def loadFile(filename): data = gunzip(inputdata) except IOError: print "File %s not zipped" % filename - + return load(buf=fromstring(data, 'uint8')); def load_named(data, data_cursor, tag_type): tag_name = TAG_String(data=data[data_cursor:]) data_cursor += tag_name.nbt_length() tag_name = tag_name.value - + tag = tag_handlers[tag_type](data=data[data_cursor:], name=tag_name) data_cursor += tag.nbt_length() return data_cursor, tag @@ -507,7 +507,7 @@ def load(filename="", buf=None): """Unserialize data from an entire NBT file and return the root TAG_Compound object. Argument can be a string containing a filename or an array of integers containing TAG_Compound data. """ - + if filename and isinstance(filename, (str, unicode)): return loadFile(filename) if isinstance(buf, str): buf = fromstring(buf, uint8) @@ -537,17 +537,17 @@ def loadtest(): """Use the [] operator to look up subtags of a TAG_Compound.""" print level["Environment"]["SurroundingGroundHeight"].value; - - + + """Numeric, string, and bytearray types have a value that can be accessed and changed. """ print level["Map"]["Blocks"].value - + return level; def createtest(): "Create an indev level." - + "The root of an NBT file is always a TAG_Compound." level = TAG_Compound(name="MinecraftLevel") @@ -558,52 +558,52 @@ def createtest(): level["Environment"] = TAG_Compound() level["Environment"]["SkyBrightness"] = TAG_Byte(16) level["Environment"]["SurroundingWaterHeight"] = TAG_Short(32) - + "You can also create and name a tag before adding it to the compound." spawn = TAG_List((TAG_Short(100), TAG_Short(45), TAG_Short(55))) spawn.name = "Spawn" - + mapTag = TAG_Compound() mapTag.add(spawn); mapTag.name = "Map" level.add(mapTag) - + "I think it looks more familiar with [] syntax." - + l, w, h = 128, 128, 128 mapTag["Height"] = TAG_Short(h) # y dimension mapTag["Length"] = TAG_Short(l) # z dimension mapTag["Width"] = TAG_Short(w) # x dimension - + "Byte arrays are stored as numpy.uint8 arrays. " - + mapTag["Blocks"] = TAG_Byte_Array() mapTag["Blocks"].value = zeros(l * w * h, dtype=uint8) #create lots of air! - + "The blocks array is indexed (y,z,x) for indev levels, so reshape the blocks" mapTag["Blocks"].value.shape = (h, l, w); - + "Replace the bottom layer of the indev level with wood" mapTag["Blocks"].value[0, :, :] = 5; - + "This is a great way to learn the power of numpy array slicing and indexing." - - + + return level; def modifytest(): level = createtest(); - + "Most of the value types work as expected. Here, we replace the entire tag with a TAG_String" level["About"]["Author"] = TAG_String("YARRR~!"); - + "Because the tag type usually doesn't change, " "we can replace the string tag's value instead of replacing the entire tag." level["About"]["Author"].value = "Stew Pickles" - + "Remove members of a TAG_Compound using del, similar to a python dict." del(level["About"]); - + "Replace all of the wood blocks with gold using a boolean index array" blocks = level["Map"]["Blocks"].value blocks[blocks == 5] = 41; @@ -613,10 +613,10 @@ def savetest(): level = createtest() level["Environment"]["SurroundingWaterHeight"].value += 6; - + "Save the entire TAG structure to a different file." level.save("atlantis.mclevel") - + level = createtest(); level.save("synthetic.mclevel"); @@ -626,7 +626,7 @@ def abusetest(): named list elements are not allowed by the NBT spec, so we must discard any names when writing a list. """ - + level = createtest(); level["Map"]["Spawn"][0].name = "Torg Potter" sio = StringIO.StringIO() @@ -647,9 +647,9 @@ def abusetest(): pass else: assert False - - -def runtests(): + + +def runtests(): loadtest(); createtest(); modifytest(); @@ -661,5 +661,5 @@ if(__name__ == "__main__") : runtests() __all__ = [a.__name__ for a in tag_handlers.itervalues()] + ["loadFile", "gunzip"] - - + + diff --git a/run_regression_test.py b/run_regression_test.py index 16c5c57..9b423bf 100755 --- a/run_regression_test.py +++ b/run_regression_test.py @@ -19,7 +19,7 @@ def generate_file_list(directory): yield os.path.join(dirpath, filename) def sha1_file(name, checksum=None): - CHUNKSIZE=1024 + CHUNKSIZE = 1024 if checksum is None: checksum = hashlib.sha1() if fnmatch.fnmatch(name, "*.dat"): @@ -71,17 +71,17 @@ def untared_content(src): f.extractall(dest) yield dest -def launch_subprocess(directory, arguments, env = {}): +def launch_subprocess(directory, arguments, env={}): #my python breaks with an empty environ, i think it wants PATH #if sys.platform == "win32": newenv = {} newenv.update(os.environ) newenv.update(env); - + proc = subprocess.Popen((["python.exe"] if sys.platform == "win32" else []) + [ "./mce.py", directory] + arguments, stdin=subprocess.PIPE, stdout=subprocess.PIPE, env=newenv) - + return proc class RegressionError(Exception): pass @@ -129,7 +129,7 @@ def do_test_match_output(test_data, result_check, arguments=[]): proc.stdin.close() output = proc.stdout.read() proc.wait() - + if proc.returncode: raise RegressionError("Program execution failed!") @@ -146,13 +146,13 @@ def do_test_match_output(test_data, result_check, arguments=[]): alpha_tests = [ - (do_test, 'baseline', '9e7460d39c8e0456789cf89fee45276db2719aaa', []), - (do_test, 'degrief', '403e6c6147cf1f8d73377b18bbf5e4973606a311', ['degrief']), - (do_test_match_output, 'analyze', '89ae362dec7f6c0fd743d6ed4e3957459cb3c34d', ['analyze']), - (do_test, 'relight', 'e0cf60c62adfdb313f198af5314c31f89d158c12', ['relight']), - (do_test, 'replace', 'd73767293e903b6d1c49c1838eb1849b69d83ad8', ['replace', 'Water (active)', 'with', 'Lava (active)']), - (do_test, 'fill', 'f4f57c3d902b6894031d416cb9279232e7e24bd7', ['fill', 'Water (active)']), - (do_test, 'heightmap', '9e7460d39c8e0456789cf89fee45276db2719aaa', ['heightmap', 'regression_test/mars.png']), + (do_test, 'baseline', '9e7460d39c8e0456789cf89fee45276db2719aaa', []), + (do_test, 'degrief', '403e6c6147cf1f8d73377b18bbf5e4973606a311', ['degrief']), + (do_test_match_output, 'analyze', '89ae362dec7f6c0fd743d6ed4e3957459cb3c34d', ['analyze']), + (do_test, 'relight', 'e0cf60c62adfdb313f198af5314c31f89d158c12', ['relight']), + (do_test, 'replace', 'd73767293e903b6d1c49c1838eb1849b69d83ad8', ['replace', 'Water (active)', 'with', 'Lava (active)']), + (do_test, 'fill', 'f4f57c3d902b6894031d416cb9279232e7e24bd7', ['fill', 'Water (active)']), + (do_test, 'heightmap', '9e7460d39c8e0456789cf89fee45276db2719aaa', ['heightmap', 'regression_test/mars.png']), ] import optparse @@ -172,26 +172,26 @@ def main(argv): test_data = os.path.join(directory, "alpha") passes = [] fails = [] - + for func, name, sha, args in alpha_tests: - print "Starting regression {0} ({1})".format( name, args ) - + print "Starting regression {0} ({1})".format(name, args) + if any(fnmatch.fnmatch(name, x) for x in do_these_regressions): if options.profile: - print >>sys.stderr, "Starting to profile to %s.profile" % name + print >> sys.stderr, "Starting to profile to %s.profile" % name os.environ['MCE_PROFILE'] = '%s.profile' % name try: func(test_data, sha, args) except RegressionError, e: - fails.append( "Regression {0} failed: {1}".format(name, e) ) + fails.append("Regression {0} failed: {1}".format(name, e)) print fails[-1] else: - passes.append( "Regression {0!r} complete.".format(name) ) + passes.append("Regression {0!r} complete.".format(name)) print passes[-1] - + print "{0} tests passed.".format(len(passes)) for line in fails: print line; - + if __name__ == '__main__': sys.exit(main(sys.argv)) diff --git a/schematic.py b/schematic.py index 2ca351d..e2d24c4 100644 --- a/schematic.py +++ b/schematic.py @@ -468,6 +468,7 @@ def extractSchematicFrom(sourceLevel, box): return tempSchematic +import tempfile def extractZipSchematicFrom(sourceLevel, box, zipfilename): #converts classic blocks to alpha #probably should only apply to alpha levels