Skip to content

Commit dc93384

Browse files
committed
Merge pull request #214 from Gjum/progress
implement block entity data and signs, improve Vector3 usage
2 parents 8d1e2f7 + 3e25e58 commit dc93384

File tree

12 files changed

+288
-122
lines changed

12 files changed

+288
-122
lines changed

spockbot/mcdata/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151
ENTITY_ACTION_OPEN_INVENTORY = 6
5252

5353
# the six faces of a block
54+
FACE_BOTTOM = 0
55+
FACE_TOP = 1
5456
FACE_Y_NEG = 0
5557
FACE_Y_POS = 1
5658
FACE_Z_NEG = 2

spockbot/mcdata/recipes.py

Lines changed: 53 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,62 @@
44

55

66
RecipeItem = namedtuple('RecipeItem', 'id meta amount')
7-
Recipe = namedtuple('Recipe', 'result ingredients in_shape out_shape')
8-
# TODO make Recipe a class, make the helpers its methods
97

108

11-
def to_recipe(raw):
12-
def reformat_item(raw, default_meta=None):
13-
if isinstance(raw, dict):
14-
raw = raw.copy() # do not modify arg
15-
if 'metadata' not in raw:
16-
raw['metadata'] = default_meta
17-
if 'count' not in raw:
18-
raw['count'] = 1
19-
return RecipeItem(raw['id'], raw['metadata'], raw['count'])
20-
elif isinstance(raw, list):
21-
return RecipeItem(raw[0], raw[1], 1)
22-
else: # single ID or None
23-
return RecipeItem(raw or None, default_meta, 1)
9+
class Recipe(object):
10+
def __init__(self, raw):
11+
self.result = reformat_item(raw['result'], None)
12+
if 'ingredients' in raw:
13+
self.ingredients = [reformat_item(item, 0)
14+
for item in raw['ingredients']]
15+
self.in_shape = None
16+
self.out_shape = None
17+
else:
18+
self.in_shape = reformat_shape(raw['inShape'])
19+
self.out_shape = reformat_shape(raw['outShape']) \
20+
if 'outShape' in raw else None
21+
self.ingredients = [item for row in self.in_shape for item in row]
2422

25-
def reformat_shape(shape):
26-
return [[reformat_item(item, None) for item in row] for row in shape]
23+
@property
24+
def total_ingredient_amounts(self):
25+
"""
26+
Returns:
27+
dict: In the form { (item_id, metadata) -> amount }
28+
"""
29+
totals = defaultdict(int)
30+
for id, meta, amount in self.ingredients:
31+
totals[(id, meta)] += amount
32+
return totals
2733

28-
result = reformat_item(raw['result'], None)
29-
if 'ingredients' in raw:
30-
ingredients = [reformat_item(item, 0) for item in raw['ingredients']]
31-
in_shape = out_shape = None
32-
else:
33-
in_shape = reformat_shape(raw['inShape'])
34-
out_shape = reformat_shape(raw['outShape']) \
35-
if 'outShape' in raw else None
36-
ingredients = [item for row in in_shape for item in row] # flatten
37-
return Recipe(result, ingredients, in_shape, out_shape)
34+
@property
35+
def ingredient_positions(self):
36+
"""
37+
Returns:
38+
dict: In the form { (item_id, metadata) -> [(x, y, amount), ...] }
39+
"""
40+
positions = defaultdict(list)
41+
for y, row in enumerate(self.in_shape):
42+
for x, (item_id, metadata, amount) in enumerate(row):
43+
positions[(item_id, metadata)].append((x, y, amount))
44+
return positions
45+
46+
47+
def reformat_item(raw, default_meta=None):
48+
if isinstance(raw, dict):
49+
raw = raw.copy() # do not modify arg
50+
if 'metadata' not in raw:
51+
raw['metadata'] = default_meta
52+
if 'count' not in raw:
53+
raw['count'] = 1
54+
return RecipeItem(raw['id'], raw['metadata'], raw['count'])
55+
elif isinstance(raw, list):
56+
return RecipeItem(raw[0], raw[1], 1)
57+
else: # single ID or None
58+
return RecipeItem(raw or None, default_meta, 1)
59+
60+
61+
def reformat_shape(shape):
62+
return [[reformat_item(item, None) for item in row] for row in shape]
3863

3964

4065
def iter_recipes(item_id, meta=None):
@@ -46,7 +71,7 @@ def iter_recipes(item_id, meta=None):
4671
return # no recipe found, do not yield anything
4772
else:
4873
for raw in recipes_for_item:
49-
recipe = to_recipe(raw)
74+
recipe = Recipe(raw)
5075
if meta is None or meta == recipe.result.meta:
5176
yield recipe
5277

@@ -56,22 +81,3 @@ def get_any_recipe(item, meta=None):
5681
for matching in iter_recipes(item, meta):
5782
return matching
5883
return None
59-
60-
61-
def total_ingredient_amounts(recipe):
62-
totals = defaultdict(int)
63-
for id, meta, amount in recipe.ingredients:
64-
totals[(id, meta)] += amount
65-
return totals
66-
67-
68-
def ingredient_positions(recipe):
69-
"""
70-
Returns:
71-
dict: In the form { (item_id, metadata) -> [ (x, y, amount), ... ] }
72-
"""
73-
positions = defaultdict(list)
74-
for y, row in enumerate(recipe.in_shape):
75-
for x, (item_id, metadata, amount) in enumerate(row):
76-
positions[(item_id, metadata)].append((x, y, amount))
77-
return positions

spockbot/mcp/proto.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@
154154
0x14: 'Entity',
155155
0x15: 'Entity Relative Move',
156156
0x16: 'Entity Look',
157-
0x17: 'Entity Look and Relative Move',
157+
0x17: 'Entity Look And Relative Move',
158158
0x18: 'Entity Teleport',
159159
0x19: 'Entity Head Look',
160160
0x1A: 'Entity Status',
@@ -480,7 +480,7 @@
480480
(MC_BYTE, 'pitch'),
481481
(MC_BOOL, 'on_ground'),
482482
),
483-
# Entity Look and Relative Move
483+
# Entity Look And Relative Move
484484
0x17: (
485485
(MC_VARINT, 'eid'),
486486
(MC_FP_BYTE, 'dx'),

spockbot/plugins/core/event.py

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,32 @@
11
"""
22
Provides the core event loop
33
"""
4-
import copy
54
import logging
65
import signal
76
from collections import defaultdict
7+
from copy import deepcopy
88

99
from spockbot.plugins.base import pl_announce
1010
from spockbot.plugins.tools.event import EVENT_UNREGISTER
1111

1212
logger = logging.getLogger('spockbot')
1313

1414

15-
class EventCore(object):
16-
def __init__(self):
15+
@pl_announce('Event')
16+
class EventPlugin(object):
17+
def __init__(self, ploader, settings):
18+
ploader.provides('Event', self)
1719
self.has_run = False
1820
self.kill_event = False
1921
self.event_handlers = defaultdict(list)
2022
signal.signal(signal.SIGINT, self.kill)
2123
signal.signal(signal.SIGTERM, self.kill)
2224

23-
def event_loop(self, continuous=True):
24-
if continuous:
25-
self.run_continuous()
26-
else:
25+
def event_loop(self, once=False):
26+
if once:
2727
self.run_once()
28+
else:
29+
self.run_continuous()
2830

2931
def run_continuous(self):
3032
if not self.has_run and not self.kill_event:
@@ -52,23 +54,17 @@ def unreg_event_handler(self, event, handler):
5254
self.event_handlers[event].remove(handler)
5355

5456
def emit(self, event, data=None):
55-
to_remove = []
56-
# reversed, because handlers can register themselves
57-
# for the same event they handle, and the new handler
58-
# is appended to the end of the iterated handler list
59-
# and immediately run, so an infinite loop can be created
60-
for handler in reversed(self.event_handlers[event]):
61-
d = data.clone() if hasattr(data, 'clone') else copy.deepcopy(data)
62-
if handler(event, d) == EVENT_UNREGISTER:
63-
to_remove.append(handler)
64-
for handler in to_remove:
65-
self.event_handlers[event].remove(handler)
57+
# the handler list of this event can change during handler execution,
58+
# so we loop over a copy
59+
try:
60+
for handler in self.event_handlers[event][:]:
61+
d = data.clone() if hasattr(data, 'clone') else deepcopy(data)
62+
if handler(event, d) == EVENT_UNREGISTER:
63+
self.event_handlers[event].remove(handler)
64+
except:
65+
logger.debug('EVENTCORE: Exception while emitting %s %s',
66+
event, data)
67+
raise
6668

6769
def kill(self, *args):
6870
self.kill_event = True
69-
70-
71-
@pl_announce('Event')
72-
class EventPlugin(object):
73-
def __init__(self, ploader, settings):
74-
ploader.provides('Event', EventCore())

spockbot/plugins/helpers/craft.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
"""
44
from math import ceil
55

6-
from spockbot.mcdata.recipes import get_any_recipe, ingredient_positions, \
7-
total_ingredient_amounts
6+
from spockbot.mcdata.recipes import get_any_recipe
87
from spockbot.plugins.base import PluginBase, pl_announce
98
from spockbot.plugins.tools.task import TaskFailed
109

@@ -67,7 +66,7 @@ def craft_task(self, recipe, amount=1):
6766
storage_slots = inv.window.persistent_slots
6867

6968
# check ingredients for recipe
70-
total_amounts_needed = total_ingredient_amounts(recipe)
69+
total_amounts_needed = recipe.total_ingredient_amounts
7170
for ingredient, needed in total_amounts_needed.items():
7271
needed *= craft_times
7372
stored = inv.total_stored(ingredient, storage_slots)
@@ -76,7 +75,7 @@ def craft_task(self, recipe, amount=1):
7675
% ('%s:%s' % ingredient, stored, needed))
7776

7877
# put ingredients into crafting grid
79-
for ingredient, p in ingredient_positions(recipe).items():
78+
for ingredient, p in recipe.ingredient_positions.items():
8079
for (x, y, ingredient_amount) in p:
8180
slot = grid_slots[x + y * grid_width]
8281
for i in range(ingredient_amount * craft_times):
@@ -101,7 +100,7 @@ def craft_task(self, recipe, amount=1):
101100
while amount > crafted_amt + inv.cursor_slot.amount:
102101
yield inv.async.click_slot(result_slot)
103102
# TODO check that cursor is non-empty, otherwise we did not craft
104-
result_stack_size = inv.cursor_slot.stack_size
103+
result_stack_size = inv.cursor_slot.item.stack_size
105104
if inv.cursor_slot.amount in (prev_cursor_amt, result_stack_size):
106105
# cursor full, put away
107106
crafted_amt += inv.cursor_slot.amount

spockbot/plugins/helpers/entities.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ class EntitiesPlugin(PluginBase):
9696
'PLAY<Entity Velocity': 'handle_velocity',
9797
'PLAY<Entity Relative Move': 'handle_relative_move',
9898
'PLAY<Entity Look': 'handle_set_dict',
99-
'PLAY<Entity Look and Relative Move': 'handle_relative_move',
99+
'PLAY<Entity Look And Relative Move': 'handle_relative_move',
100100
'PLAY<Entity Teleport': 'handle_set_dict',
101101
'PLAY<Entity Head Look': 'handle_set_dict',
102102
'PLAY<Entity Status': 'handle_set_dict',

0 commit comments

Comments
 (0)