From 168bbfa563525085e6bc2012394b56f400d6951e Mon Sep 17 00:00:00 2001 From: David Vierra Date: Fri, 22 May 2015 02:58:06 -1000 Subject: [PATCH] Add KeyedVectorAttr, make adapter.metadata accessible through WorldEditor Add AnvilWorldMetadata.Spawn Add KeyedVectorAttr, used for Spawn and TileEntity Position Remove get/setWorldSpawnPosition --- src/mcedit2/editorsession.py | 2 +- src/mcedit2/panels/worldinfo.py | 4 ++-- src/mcedit2/worldlist.py | 2 +- src/mceditlib/anvil/adapter.py | 15 +++++---------- src/mceditlib/anvil/entities.py | 10 +--------- src/mceditlib/minecraft_server.py | 2 +- src/mceditlib/nbtattr.py | 31 +++++++++++++++++++++++++++++++ src/mceditlib/worldeditor.py | 20 ++++++++++---------- 8 files changed, 52 insertions(+), 34 deletions(-) diff --git a/src/mcedit2/editorsession.py b/src/mcedit2/editorsession.py index ad71e28..5dcbb03 100644 --- a/src/mcedit2/editorsession.py +++ b/src/mcedit2/editorsession.py @@ -693,7 +693,7 @@ class EditorSession(QtCore.QObject): pass except PlayerNotFound: try: - center = self.worldEditor.worldSpawnPosition() + center = self.worldEditor.getWorldMetadata().Spawn log.info("Centering on spawn position.") except AttributeError: log.info("Centering on world center") diff --git a/src/mcedit2/panels/worldinfo.py b/src/mcedit2/panels/worldinfo.py index 2d25dca..650614e 100644 --- a/src/mcedit2/panels/worldinfo.py +++ b/src/mcedit2/panels/worldinfo.py @@ -90,7 +90,7 @@ class WorldInfoPanel(QtGui.QWidget): self.generatorSeedLineEdit.setText(str(self.worldMeta.RandomSeed)) self.generatorOptionsLineEdit.setText(self.worldMeta.generatorOptions) - sx, sy, sz = self.worldMeta.worldSpawnPosition() + sx, sy, sz = self.worldMeta.Spawn self.spawnX.setValue(sx) self.spawnY.setValue(sy) self.spawnZ.setValue(sz) @@ -201,7 +201,7 @@ class WorldInfoPanel(QtGui.QWidget): command = WorldMetaEditCommand(self.editorSession, self.tr('Change Spawn Coordinates')) with command.begin(): - self.worldMeta.setWorldSpawnPosition(self.spawnX.value(), self.spawnY.value(), self.spawnZ.value()) + self.worldMeta.Spawn = self.spawnX.value(), self.spawnY.value(), self.spawnZ.value() self.editorSession.pushCommand(command) def timeChanged(self): diff --git a/src/mcedit2/worldlist.py b/src/mcedit2/worldlist.py index 6054d5d..c9e1b88 100644 --- a/src/mcedit2/worldlist.py +++ b/src/mcedit2/worldlist.py @@ -384,7 +384,7 @@ class WorldListWidget(QtGui.QDialog): log.info("Centering on single-player player.") except PlayerNotFound: try: - center = worldEditor.worldSpawnPosition() + center = worldEditor.getWorldMetadata().Spawn log.info("Centering on spawn position.") except AttributeError: log.info("Centering on world center") diff --git a/src/mceditlib/anvil/adapter.py b/src/mceditlib/anvil/adapter.py index 9a21ff9..570807d 100644 --- a/src/mceditlib/anvil/adapter.py +++ b/src/mceditlib/anvil/adapter.py @@ -374,25 +374,20 @@ class AnvilWorldMetadata(object): version = nbtattr.NBTAttr('version', nbt.TAG_Int, VERSION_ANVIL) - def worldSpawnPosition(self): - return Vector(*[self.rootTag[i].value for i in ("SpawnX", "SpawnY", "SpawnZ")]) - - def setWorldSpawnPosition(self, pos): - for name, val in zip(("SpawnX", "SpawnY", "SpawnZ"), pos): - self.rootTag[name] = nbt.TAG_Int(val) + Spawn = nbtattr.KeyedVectorAttr('SpawnX', 'SpawnY', 'SpawnZ', nbt.TAG_Int) def is1_8World(self): # Minecraft 1.8 adds a dozen tags to level.dat/Data. These tags are removed if # the world is played in 1.7 (and all of the items are removed too!) # Use some of these tags to decide whether to use 1.7 format ItemStacks or 1.8 format ones. - # In 1.8, the stack's "id" is a string, but in 1.7 it is an int. - tags = (t in self.rootTag for t in ( + # In 1.8, the stack's "id" is a TAG_String, but in 1.7 it is a TAG_Short. + tags = ( 'BorderCenterX', 'BorderCenterZ', 'BorderDamagePerBlock', 'BorderSafeZone', 'BorderSize' - )) - return any(tags) + ) + return any(tag in self.rootTag for tag in tags) class AnvilWorldAdapter(object): """ diff --git a/src/mceditlib/anvil/entities.py b/src/mceditlib/anvil/entities.py index 8dada65..e854604 100644 --- a/src/mceditlib/anvil/entities.py +++ b/src/mceditlib/anvil/entities.py @@ -63,15 +63,7 @@ class PCTileEntityRefBase(object): return self.rootTag id = nbtattr.NBTAttr("id", nbt.TAG_String) - - @property - def Position(self): - return Vector(*[self.rootTag[c].value for c in 'xyz']) - - @Position.setter - def Position(self, pos): - for a, p in zip('xyz', pos): - self.rootTag[a] = nbt.TAG_Int(p) + Position = nbtattr.KeyedVectorAttr('x', 'y', 'z', nbt.TAG_Int, 0) def copy(self): return self.copyWithOffset(Vector(0, 0, 0)) diff --git a/src/mceditlib/minecraft_server.py b/src/mceditlib/minecraft_server.py index 57e6d53..9f8a785 100644 --- a/src/mceditlib/minecraft_server.py +++ b/src/mceditlib/minecraft_server.py @@ -356,7 +356,7 @@ class MCServerChunkGenerator(object): def generateAtPositionIter(self, tempWorld, tempDir, cx, cz, simulate=False): tempWorldRW = worldeditor.WorldEditor(tempWorld.filename) - tempWorldRW.setWorldSpawnPosition((cx * 16, 64, cz * 16)) + tempWorldRW.getWorldMetadata().Spawn = cx * 16, 64, cz * 16 tempWorldRW.saveChanges() tempWorldRW.close() del tempWorldRW diff --git a/src/mceditlib/nbtattr.py b/src/mceditlib/nbtattr.py index 40dfb00..868f03f 100644 --- a/src/mceditlib/nbtattr.py +++ b/src/mceditlib/nbtattr.py @@ -275,6 +275,37 @@ class NBTVectorAttr(NBTListAttr): return Vector(*val) +class KeyedVectorAttr(object): + """ + This attr is useful when a Vector is represented as a trio of named tags in a compound + instead of as a list of tags. For example, the world spawn position (SpawnX, SpawnY, + SpawnZ) and a TileEntity's position (x, y, z). + """ + def __init__(self, xKey, yKey, zKey, tagType, default=None): + self.tagType = tagType + self.default = default + self.keys = xKey, yKey, zKey + + def __get__(self, instance, owner): + tag = instance.rootTag + for key in self.keys: + if key not in tag: + tag[key] = self.tagType(value=self.default) + + return Vector(*[tag[k].value for k in self.keys]) + + def __set__(self, instance, value): + tag = instance.rootTag + for key, val in zip(self.keys, value): + if key not in tag: + tag[key] = self.tagType(val) + else: + tag[key].value = val + + instance.dirty = True + + + def SetNBTDefaults(ref): """ Given an object whose class has several members of type `NBT[*]Attr`, diff --git a/src/mceditlib/worldeditor.py b/src/mceditlib/worldeditor.py index 7d88653..6306603 100644 --- a/src/mceditlib/worldeditor.py +++ b/src/mceditlib/worldeditor.py @@ -529,21 +529,21 @@ class WorldEditor(object): if self._allChunks is not None: self._allChunks[dimName].discard((cx, cz)) - # --- Player and spawn manipulation --- + # --- World metadata --- - def worldSpawnPosition(self): + def getWorldMetadata(self): """ - Return the world's default spawn position. - """ - return self.adapter.metadata.worldSpawnPosition() + Return an object containing global info about the world. - def setWorldSpawnPosition(self, pos): - """ - Change the world's default spawn position. + Different level formats can return different objects for the world metadata. + At the very least, you can expect the object to have Spawn and Seed attributes. - :param pos: (x, y, z) coordinates + Currently, only AnvilWorldMetadata is ever returned. + :return: """ - self.adapter.metadata.setWorldSpawnPosition(pos) + return self.adapter.metadata + + # --- Players --- def listPlayers(self): return self.adapter.listPlayers()