Add KeyedVectorAttr, make adapter.metadata accessible through WorldEditor

Add AnvilWorldMetadata.Spawn
Add KeyedVectorAttr, used for Spawn and TileEntity Position
Remove get/setWorldSpawnPosition
This commit is contained in:
David Vierra 2015-05-22 02:58:06 -10:00
parent 123b4ea063
commit 168bbfa563
8 changed files with 52 additions and 34 deletions

View File

@ -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")

View File

@ -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):

View File

@ -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")

View File

@ -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):
"""

View File

@ -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))

View File

@ -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

View File

@ -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`,

View File

@ -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()