Skip to content

Commit e93b109

Browse files
committed
Implement block entity data interface
1 parent 84dff18 commit e93b109

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed

spockbot/plugins/helpers/world.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from spockbot.mcdata import constants as const
99
from spockbot.plugins.base import PluginBase, pl_announce
1010
from spockbot.plugins.tools import smpmap
11+
from spockbot.vector import Vector3
1112

1213

1314
class WorldData(smpmap.Dimension):
@@ -38,6 +39,8 @@ class WorldPlugin(PluginBase):
3839
'PLAY<Multi Block Change': 'handle_multi_block_change',
3940
'PLAY<Block Change': 'handle_block_change',
4041
'PLAY<Map Chunk Bulk': 'handle_map_chunk_bulk',
42+
'PLAY<Update Sign': 'handle_update_sign',
43+
'PLAY<Update Block Entity': 'handle_update_block_entity',
4144
'net_disconnect': 'handle_disconnect',
4245
}
4346

@@ -89,6 +92,27 @@ def handle_map_chunk_bulk(self, name, packet):
8992
"""Map Chunk Bulk - Update World state"""
9093
self.world.unpack_bulk(packet.data)
9194

95+
def handle_update_sign(self, event, packet):
96+
location = Vector3(packet.data['location'])
97+
sign_data = smpmap.SignData(packet.data)
98+
old_data = self.world.set_block_entity_data(location, data=sign_data)
99+
self.event.emit('world_block_entity_data', {
100+
'location': location,
101+
'data': sign_data,
102+
'old_data': old_data,
103+
})
104+
105+
def handle_update_block_entity(self, event, packet):
106+
location = Vector3(packet.data['location'])
107+
block_entity_class = smpmap.block_entities[packet.data['action']]
108+
data = block_entity_class(packet.data['nbt'])
109+
old_data = self.world.set_block_entity_data(location, data=data)
110+
self.event.emit('world_block_entity_data', {
111+
'location': location,
112+
'data': data,
113+
'old_data': old_data,
114+
})
115+
92116
def handle_disconnect(self, name, data):
93117
self.world.reset()
94118
self.event.emit('world_reset')

spockbot/plugins/tools/smpmap.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,69 @@ def mapshort2id(data):
3030
return data >> 4, data & 0x0F
3131

3232

33+
class BlockEntityData(object):
34+
def __init__(self, nbt):
35+
self.nbt = nbt
36+
37+
def __getattr__(self, key):
38+
return getattr(self.nbt, key)
39+
40+
def __str__(self):
41+
return str(self.nbt)
42+
43+
def __repr__(self):
44+
return repr(self.nbt)
45+
46+
47+
class SpawnerData(BlockEntityData):
48+
pass # TODO get entity class from mcdata?
49+
50+
51+
class CommandBlockData(BlockEntityData):
52+
pass
53+
54+
55+
class BeaconData(BlockEntityData):
56+
pass
57+
58+
59+
class HeadData(BlockEntityData):
60+
pass
61+
62+
63+
class FlowerPotData(BlockEntityData):
64+
def __init__(self, nbt):
65+
super(FlowerPotData, self).__init__(nbt)
66+
self.block = nbt['Item'].value, nbt['Data'].value
67+
# TODO get block instance from mcdata?
68+
69+
70+
class BannerData(BlockEntityData):
71+
pass
72+
73+
74+
class SignData(BlockEntityData):
75+
def __init__(self, line_data):
76+
super(SignData, self).__init__(self)
77+
self.lines = [line_data['line_%i' % (i + 1)] for i in range(4)]
78+
79+
def __str__(self):
80+
return 'Sign%s' % str(self.lines)
81+
82+
def __repr__(self):
83+
return '<SignData %s>' % repr(self.lines)
84+
85+
86+
block_entities = {
87+
1: SpawnerData,
88+
2: CommandBlockData,
89+
3: BeaconData,
90+
4: HeadData,
91+
5: FlowerPotData,
92+
6: BannerData,
93+
}
94+
95+
3396
class ChunkData(object):
3497
length = 16 * 16 * 16
3598
ty = 'B'
@@ -132,6 +195,9 @@ def __init__(self, dimension):
132195
self.dimension = dimension
133196
self.columns = {} # chunk columns are address by a tuple (x, z)
134197

198+
# BlockEntityData subclass instances, adressed by (x,y,z)
199+
self.block_entities = {}
200+
135201
def unpack_bulk(self, data):
136202
bbuff = BoundBuffer(data['data'])
137203
skylight = data['sky_light']
@@ -192,6 +258,34 @@ def set_block(self, pos_or_x, y=None, z=None,
192258
data = (block_id << 4) | (meta & 0x0F)
193259
chunk.block_data.set(rx, ry, rz, data)
194260

261+
def get_block_entity_data(self, pos_or_x, y=None, z=None):
262+
"""
263+
Access block entity data.
264+
265+
Returns:
266+
BlockEntityData subclass instance or
267+
None if no block entity data is stored for that location.
268+
"""
269+
if None not in (y, z): # x y z supplied
270+
pos_or_x = pos_or_x, y, z
271+
coord_tuple = tuple(floor(c) for c in pos_or_x)
272+
return self.block_entities.get(coord_tuple, None)
273+
274+
def set_block_entity_data(self, pos_or_x, y=None, z=None, data=None):
275+
"""
276+
Update block entity data.
277+
278+
Returns:
279+
Old data if block entity data was already stored for that location,
280+
None otherwise.
281+
"""
282+
if None not in (y, z): # x y z supplied
283+
pos_or_x = pos_or_x, y, z
284+
coord_tuple = tuple(floor(c) for c in pos_or_x)
285+
old_data = self.block_entities.get(coord_tuple, None)
286+
self.block_entities[coord_tuple] = data
287+
return old_data
288+
195289
def get_light(self, pos_or_x, y=None, z=None):
196290
if None in (y, z): # pos supplied
197291
pos_or_x, y, z = pos_or_x

0 commit comments

Comments
 (0)