Gather sand and sleep at night

This commit is contained in:
Tanner Collin 2020-08-19 14:00:57 -06:00
parent ebc9c5ef1a
commit 49412e0253
3 changed files with 475 additions and 62 deletions

View File

@ -43,12 +43,6 @@ AVOID = [
NON_SOLID = [ NON_SOLID = [
'minecraft:air', 'minecraft:air',
'minecraft:oak_sapling',
'minecraft:spruce_sapling',
'minecraft:birch_sapling',
'minecraft:jungle_sapling',
'minecraft:acacia_sapling',
'minecraft:dark_oak_sapling',
'minecraft:powered_rail', 'minecraft:powered_rail',
'minecraft:detector_rail', 'minecraft:detector_rail',
'minecraft:grass', 'minecraft:grass',

493
bot.py
View File

@ -13,6 +13,7 @@ from minecraft import authentication
from minecraft.exceptions import YggdrasilError from minecraft.exceptions import YggdrasilError
from minecraft.networking.connection import Connection from minecraft.networking.connection import Connection
from minecraft.networking.packets import clientbound, serverbound from minecraft.networking.packets import clientbound, serverbound
from minecraft.networking.types import BlockFace
from minecraft.compat import input from minecraft.compat import input
from minecraft.managers import chunks, ChunksManager from minecraft.managers import chunks, ChunksManager
@ -37,6 +38,7 @@ BLOCK_ABOVE2 = (0, +2, 0)
BLOCK_ABOVE3 = (0, +3, 0) BLOCK_ABOVE3 = (0, +3, 0)
BLOCK_ABOVE4 = (0, +4, 0) BLOCK_ABOVE4 = (0, +4, 0)
BLOCK_BELOW = (0, -1, 0) BLOCK_BELOW = (0, -1, 0)
BLOCK_BELOW2 = (0, -2, 0)
TRAVERSE_NORTH = (0, 0, -1) TRAVERSE_NORTH = (0, 0, -1)
TRAVERSE_SOUTH = (0, 0, +1) TRAVERSE_SOUTH = (0, 0, +1)
@ -152,6 +154,12 @@ def pmul(p, s):
def phyp(p1, p2): def phyp(p1, p2):
return hypot(p1[0] - p2[0], p1[1] - p2[1], p1[2] - p2[2]) return hypot(p1[0] - p2[0], p1[1] - p2[1], p1[2] - p2[2])
def phyp_bias(p1, p2, origin):
origin_distance = phyp(origin, p2)
height_diff = p2[1] - p1[1]
height_diff = height_diff*8 if height_diff < 0 else height_diff*0.5
return hypot(p1[0] - p2[0], height_diff, p1[2] - p2[2]) + origin_distance*1.5
def pint(p): def pint(p):
return (floor(p[0]), floor(p[1]), floor(p[2])) return (floor(p[0]), floor(p[1]), floor(p[2]))
@ -380,11 +388,39 @@ def break_block(connection, coords, time):
s['break_time'] = time s['break_time'] = time
s['break_timeout'] = 0.25 s['break_timeout'] = 0.25
def place_block(connection, pos, face):
packet = serverbound.play.PlayerBlockPlacementPacket()
packet.hand = 0
packet.location = pos
packet.face = face
packet.x = 0.5
packet.y = 0.5
packet.z = 0.5
packet.inside_block = False
connection.write_packet(packet)
def say(connection, message): def say(connection, message):
packet = serverbound.play.ChatPacket() packet = serverbound.play.ChatPacket()
packet.message = message packet.message = message
connection.write_packet(packet) connection.write_packet(packet)
def pick(connection, slot):
packet = custom_packets.PickItemPacket()
packet.slot_to_use = slot
connection.write_packet(packet)
def hold(connection, slot):
packet = custom_packets.HeldItemChangePacket()
packet.slot = slot
connection.write_packet(packet)
def choose_slot(connection, slot):
if slot >= 36:
slot -= 36
hold(connection, slot)
else:
pick(connection, slot)
BLOCK_ABOVE = (0, +1, 0) BLOCK_ABOVE = (0, +1, 0)
BLOCK_BELOW = (0, -1, 0) BLOCK_BELOW = (0, -1, 0)
@ -459,32 +495,90 @@ class MCWorld:
maze_solver = MazeSolver(self.chunks) maze_solver = MazeSolver(self.chunks)
result = [] result = []
# TODO: make sure only non-solid and leaves between
# make sure traversable too
for distance in range(5): for distance in range(5):
for direction in CHECK_DIRECTIONS: for direction in CHECK_DIRECTIONS:
offset = (0, 0, 0) offset = pmul(direction, distance+1)
for _ in range(distance):
offset = padd(offset, direction)
if maze_solver.check_traverse(tree, offset): if maze_solver.check_traverse(tree, offset):
result.append(padd(tree, offset)) result.append(padd(tree, offset))
return result return result
def path_to_opening(self, start, opening): def path_to_place(self, start, place):
maze_solver = MazeSolver(self.chunks) maze_solver = MazeSolver(self.chunks)
try: try:
s = maze_solver.astar(start, opening) s = maze_solver.astar(start, place)
return list(s) if s else None return list(s) if s else None
except AStarTimeout: except AStarTimeout:
return None return None
def path_to_base(self, start, base): def find_bed_areas(self, center, distance):
maze_solver = MazeSolver(self.chunks) air = []
for i in range(5):
check = padd(center, alternate(i, 1))
air.extend(self.find_blocks(check, distance, [0], 200))
try: areas = []
s = maze_solver.astar(start, base) for a in air:
return list(s) if s else None # check for ground around the area
except AStarTimeout: if len(self.find_blocks(padd(a, BLOCK_BELOW), 2, blocks.NON_SOLID_IDS, 9)):
return None continue
# check for air around the area
if len(self.find_blocks(a, 2, [0], 9)) < 9:
continue
# check for air above the area
if len(self.find_blocks(padd(a, BLOCK_ABOVE), 2, [0], 9)) < 9:
continue
areas.append(a)
areas.sort(key=lambda x: phyp(center, x))
return areas
def sand_adjacent_safe(self, sand):
for direction in CHECK_DIRECTIONS:
if self.block_at(*padd(sand, direction)) in blocks.AVOID_IDS:
return False
return True
def find_sand(self, center, distance, origin):
sand = []
for i in range(10):
check = padd(center, alternate(i, 1))
sand.extend(self.find_blocks(check, distance, [66], 20))
safe_sand = []
for s in sand:
# make sure it has solid below
if self.block_at(*padd(s, BLOCK_BELOW)) in blocks.NON_SOLID_IDS:
continue
# make sure it has solid two below - prevent hanging sand
if self.block_at(*padd(s, BLOCK_BELOW2)) in blocks.NON_SOLID_IDS:
continue
# and walkable air above
if self.block_at(*padd(s, BLOCK_ABOVE)) not in blocks.NON_SOLID_IDS:
continue
if not self.sand_adjacent_safe(s):
continue
safe_sand.append(s)
safe_sand.sort(key=lambda x: phyp_bias(center, x, origin))
return safe_sand
def find_bed_openings(self, area):
# returns coords in a cardinal direction where we can stand by bed
result = []
for direction in CHECK_DIRECTIONS:
result.append(padd(area, direction))
return result
class LumberjackStates: class LumberjackStates:
@ -512,24 +606,31 @@ class LumberjackStates:
trees.pop(0) trees.pop(0)
self.tree = trees[0] self.tree = trees[0]
openings = w.find_tree_openings(self.tree) self.openings = w.find_tree_openings(self.tree)
self.state = self.choose_opening
for o in openings: def choose_opening(self):
path = w.path_to_opening(p, o) w = MCWorld(self.player_info.chunks)
self.opening = o p = pint(self.player_info.pos)
if path: break
else: # for print('openings:', self.openings)
if not len(self.openings):
print('Unable to get to tree', self.tree) print('Unable to get to tree', self.tree)
self.bad_trees.append(self.tree) self.bad_trees.append(self.tree)
self.state = self.done self.state = self.cleanup
return return
path = w.path_to_place(p, self.openings[0])
if path:
s['path'] = path s['path'] = path
self.state = self.going_to_tree self.state = self.going_to_tree
else:
self.openings.pop(0)
def going_to_tree(self): def going_to_tree(self):
d = self.player_info.pos - LPoint3f(*self.opening) if pint(self.player_info.pos) == self.openings[0]:
if d.length() < 1:
s['look_at'] = self.tree s['look_at'] = self.tree
self.state = self.clear_leaves self.state = self.clear_leaves
@ -542,13 +643,15 @@ class LumberjackStates:
for z in diffrange(diff[2]): for z in diffrange(diff[2]):
for y in range(2): for y in range(2):
check = padd(p, (x, y, z)) check = padd(p, (x, y, z))
if self.blog(check): if check == self.tree:
self.state = self.clear_trunk_base break
return
if not self.bair(check): if not self.bair(check):
print('Breaking leaf')
s['break'] = (check, 0.5) s['break'] = (check, 0.5)
return return
self.state = self.clear_trunk_base
def clear_trunk_base(self): def clear_trunk_base(self):
if not s['break_timeout']: if not s['break_timeout']:
base = self.tree base = self.tree
@ -563,14 +666,17 @@ class LumberjackStates:
else: else:
w = MCWorld(self.player_info.chunks) w = MCWorld(self.player_info.chunks)
p = pint(self.player_info.pos) p = pint(self.player_info.pos)
path = w.path_to_base(p, self.tree) path = w.path_to_place(p, self.tree)
if path:
s['path'] = path s['path'] = path
self.state = self.going_to_trunk_base self.state = self.going_to_trunk_base
else:
self.openings.pop(0)
self.state = self.choose_opening
def going_to_trunk_base(self): def going_to_trunk_base(self):
d = self.player_info.pos - LPoint3f(*self.opening) if pint(self.player_info.pos) == self.tree:
if d.length() < 1:
s['look_at'] = padd(self.tree, BLOCK_ABOVE2) s['look_at'] = padd(self.tree, BLOCK_ABOVE2)
self.state = self.clear_trunk self.state = self.clear_trunk
@ -589,7 +695,6 @@ class LumberjackStates:
else: else:
print('Finished clearing tree') print('Finished clearing tree')
self.wait_time = 0.5 self.wait_time = 0.5
s['look_at'] = None
self.state = self.wait self.state = self.wait
def wait(self): def wait(self):
@ -597,9 +702,14 @@ class LumberjackStates:
if self.wait_time > 0: if self.wait_time > 0:
self.wait_time -= TICK self.wait_time -= TICK
else: else:
self.state = self.cleanup
def cleanup(self):
s['look_at'] = None
self.state = self.done self.state = self.done
def done(self): def done(self):
# never gets ran, placeholder
return None return None
@ -608,7 +718,7 @@ class LumberjackStates:
self.state = self.idle self.state = self.idle
self.tree = None self.tree = None
self.opening = None self.openings = []
self.bad_trees = [] self.bad_trees = []
self.wait_time = 0 self.wait_time = 0
@ -616,32 +726,283 @@ class LumberjackStates:
self.state() self.state()
class GatherSandStates:
def bair(self, p):
return self.player_info.chunks.get_block_at(*p) in blocks.NON_SOLID_IDS
def bsand(self, p):
return self.player_info.chunks.get_block_at(*p) == 66
def idle(self):
return None
def init(self):
self.state = self.find_new_sand
def find_new_sand(self):
print('Finding new sand...')
w = MCWorld(self.player_info.chunks)
p = pint(self.player_info.pos)
sand = w.find_sand(p, 150, self.origin)
print('Found sand:', sand)
while sand[0] in self.bad_sand:
sand.pop(0)
self.sand = sand[0]
self.state = self.nav_to_sand
def nav_to_sand(self):
w = MCWorld(self.player_info.chunks)
p = pint(self.player_info.pos)
w.chunks.set_block_at(*self.sand, 0)
path = w.path_to_place(p, self.sand)
w.chunks.set_block_at(*self.sand, 66)
if path:
s['path'] = path[:-1]
self.state = self.going_to_sand
else:
self.bad_sand.append(self.sand)
self.state = self.find_new_sand
def going_to_sand(self):
if not len(s['path']):
s['look_at'] = self.sand
self.state = self.dig_sand
def dig_sand(self):
if not s['break_timeout']:
if self.bsand(self.sand):
s['break'] = (self.sand, 0.75)
print('digging sand')
else:
self.state = self.get_sand
def get_sand(self):
w = MCWorld(self.player_info.chunks)
p = pint(self.player_info.pos)
path = w.path_to_place(p, self.sand)
if path:
s['path'] = path
self.state = self.going_to_item
else:
self.bad_sand.append(self.sand)
self.state = self.find_new_sand
def going_to_item(self):
if pint(self.player_info.pos) == self.sand:
s['look_at'] = self.sand
self.state = self.cleanup
def cleanup(self):
s['look_at'] = None
self.state = self.done
def done(self):
# never gets ran, placeholder
return None
def __init__(self, player_info):
self.player_info = player_info
self.state = self.idle
self.origin = pint(self.player_info.pos)
self.sand = None
self.bad_sand = []
self.wait_time = 0
def run(self):
self.state()
class SleepWithBedStates:
def idle(self):
return None
def init(self):
if s['time'] >= 12000:
self.state = self.find_bed_spot
else:
print('Aborting sleep, not night')
self.state = self.cleanup
def find_bed_spot(self):
print('Finding a bed spot...')
w = MCWorld(self.player_info.chunks)
p = pint(self.player_info.pos)
areas = w.find_bed_areas(p, 100)
print('Found areas:', areas)
if len(areas):
while areas[0] in self.bad_areas:
areas.pop(0)
self.area = areas[0]
elif self.last_area:
self.area = self.last_area
else:
print('Unable to find area, and no last area')
self.state = self.cleanup
return
openings = w.find_bed_openings(self.area)
for o in openings:
path = w.path_to_place(p, o)
self.opening = o
if path: break
else: # for
print('Unable to get to bed area', self.area)
self.bad_areas.append(self.area)
self.state = self.cleanup
return
s['path'] = path
self.state = self.going_to_area
self.last_area = self.area
def going_to_area(self):
if pint(self.player_info.pos) == self.opening:
s['look_at'] = self.area
self.state = self.select_bed
def select_bed(self):
for slot, item in self.player_info.inv.items():
if item.item_id in items.BED_IDS:
print('Found bed in slot', slot)
s['look_at'] = padd(self.area, BLOCK_BELOW)
choose_slot(self.connection, slot)
self.state = self.place_bed
break
else: # for
say(self.connection, 'I need a bed')
self.state = self.cleanup
def place_bed(self):
place_block(self.connection, self.area, BlockFace.TOP)
self.state = self.use_bed
def use_bed(self):
if s['time'] >= 12542:
print('Sleeping')
place_block(self.connection, self.area, BlockFace.TOP)
say(self.connection, 'zzz')
self.state = self.sleep_bed
def sleep_bed(self):
if s['time'] < 100:
print('Woke up')
self.state = self.break_bed
def break_bed(self):
s['break'] = (self.area, 0.4)
self.state = self.collect_bed
def collect_bed(self):
if not s['break_timeout']:
s['path'] = [padd(self.area, spiral(n)) for n in range(9)]
self.wait_time = 4
self.state = self.wait
def wait(self):
# wait to pick up bed
if self.wait_time > 0:
self.wait_time -= TICK
else:
self.state = self.cleanup
def cleanup(self):
s['look_at'] = None
self.state = self.done
def done(self):
# never gets ran, placeholder
return None
def __init__(self, player_info, connection):
self.player_info = player_info
self.connection = connection
self.state = self.idle
self.area = None
self.opening = None
self.bad_areas = []
self.last_area = None
self.wait_time = 0
def run(self):
self.state()
class JobStates: class JobStates:
def idle(self): def idle(self):
return None return None
def night_shelter(self): def sleep_with_bed(self):
return None s = self.sleep_with_bed_states
if s.state == s.idle:
s.state = s.init
elif s.state == s.done:
s.state = s.init
# check time, etc
if self.prev_state:
print('Reverting to prev state')
self.state = self.prev_state
return
s.run()
def gather_sand(self):
s = self.gather_sand_states
if s.state == s.idle:
s.state = s.init
elif s.state == s.done:
s.state = s.init
# check time, etc
if self.survive:
self.prev_state = self.gather_sand
self.state = self.sleep_with_bed
return
s.run()
def lumberjack(self): def lumberjack(self):
l = self.lumberjack_states s = self.lumberjack_states
if l.state == l.idle: if s.state == s.idle:
l.state = l.init s.state = s.init
elif l.state == l.done: elif s.state == s.done:
s.state = s.init
# check time, etc # check time, etc
l.state = l.init
l.run() if self.survive:
self.prev_state = self.lumberjack
self.state = self.sleep_with_bed
return
s.run()
def stop(self): def stop(self):
self.lumberjack_states = LumberjackStates(self.player_info) self.lumberjack_states = LumberjackStates(self.player_info)
self.gather_sand_states = GatherSandStates(self.player_info)
self.sleep_with_bed_states = SleepWithBedStates(self.player_info, self.connection)
self.state = self.idle self.state = self.idle
def __init__(self, connection, player_info): def __init__(self, connection, player_info):
# TODO: watch dog if it gets stuck
self.connection = connection self.connection = connection
self.player_info = player_info self.player_info = player_info
self.state = self.idle self.state = self.idle
self.prev_state = None
self.lumberjack_states = LumberjackStates(self.player_info) self.lumberjack_states = LumberjackStates(self.player_info)
self.gather_sand_states = GatherSandStates(self.player_info)
self.sleep_with_bed_states = SleepWithBedStates(self.player_info, self.connection)
self.survive = False self.survive = False
def run(self): def run(self):
@ -744,15 +1105,18 @@ def tick(connection, player_info):
look_at_d = p - look_at look_at_d = p - look_at
if look_at_d.length() > 0.6: if look_at_d.length() > 0.6:
target_yaw = look_at_d.normalized().signedAngleDeg(other=YAW_ANGLE_DIR, ref=YAW_ANGLE_REF)
target_yaw_d = target_yaw - s['yaw']
target_yaw_d = (target_yaw_d + 180) % 360 - 180
s['yaw'] += cap(target_yaw_d, 30)
target_pitch = look_at_d.normalized().angleDeg(PITCH_ANGLE_DIR) target_pitch = look_at_d.normalized().angleDeg(PITCH_ANGLE_DIR)
target_pitch = (target_pitch - 90) * -1 target_pitch = (target_pitch - 90) * -1
target_pitch_d = target_pitch - s['pitch'] target_pitch_d = target_pitch - s['pitch']
s['pitch'] += cap(target_pitch_d, 10) s['pitch'] += cap(target_pitch_d, 10)
# remove vertical component for yaw calculation
look_at_d.y = 0
target_yaw = look_at_d.normalized().signedAngleDeg(other=YAW_ANGLE_DIR, ref=YAW_ANGLE_REF)
target_yaw_d = target_yaw - s['yaw']
target_yaw_d = (target_yaw_d + 180) % 360 - 180
s['yaw'] += cap(target_yaw_d, 30)
else: else:
target_pitch_d = 0 - s['pitch'] target_pitch_d = 0 - s['pitch']
s['pitch'] += cap(target_pitch_d, 10) s['pitch'] += cap(target_pitch_d, 10)
@ -788,6 +1152,8 @@ def tick(connection, player_info):
def init(connection, player_info): def init(connection, player_info):
p = player_info.pos p = player_info.pos
s['time'] = 0
s['path'] = [] s['path'] = []
s['look_at'] = None s['look_at'] = None
s['y_v'] = 0 s['y_v'] = 0
@ -803,6 +1169,7 @@ def init(connection, player_info):
s['jobstate'] = JobStates(connection, player_info) s['jobstate'] = JobStates(connection, player_info)
s['jobstate'].run() s['jobstate'].run()
def main(connection, player_info): def main(connection, player_info):
def handle_join_game(join_game_packet): def handle_join_game(join_game_packet):
print('Connected.') print('Connected.')
@ -850,11 +1217,11 @@ def main(connection, player_info):
connection.register_packet_listener( connection.register_packet_listener(
x, clientbound.play.BlockChangePacket) x, clientbound.play.BlockChangePacket)
#def y(p): def handle_time_update(p):
# print(p) s['time'] = p.time_of_day % 24000
#connection.register_packet_listener( connection.register_packet_listener(
# y, AcknowledgePlayerDiggingPacket) handle_time_update, custom_packets.TimeUpdatePacket)
def handle_set_slot(p): def handle_set_slot(p):
print(p) print(p)
@ -872,6 +1239,9 @@ def main(connection, player_info):
if '!reload' in chat_packet.json_data: if '!reload' in chat_packet.json_data:
global running global running
running = False running = False
elif '!misc' in chat_packet.json_data:
for i in range(9):
print(i, spiral(i))
elif '!afk' in chat_packet.json_data: elif '!afk' in chat_packet.json_data:
say(connection, '/afk') say(connection, '/afk')
elif '!respawn' in chat_packet.json_data: elif '!respawn' in chat_packet.json_data:
@ -919,11 +1289,29 @@ def main(connection, player_info):
for i in player_info.inv.values(): for i in player_info.inv.values():
if i.present: if i.present:
print(items.ITEM_NAMES[i.item_id], 'x', i.item_count) print(items.ITEM_NAMES[i.item_id], 'x', i.item_count)
elif '!spot' in chat_packet.json_data:
mc_world = MCWorld(player_info.chunks)
start = time.time()
coords = mc_world.find_bed_areas(pint(player_info.pos), 100)
print(coords)
print(len(coords))
print(round(time.time() - start, 3), 'seconds')
elif '!echo' in chat_packet.json_data: elif '!echo' in chat_packet.json_data:
parts = chat_packet.json_data.split('\'') parts = chat_packet.json_data.split('\'')
say(connection, parts[1]) say(connection, parts[1])
elif '!sleep' in chat_packet.json_data:
#for i in player_info.inv.values():
# if i.item_id in items.BED_IDS:
# break
#else: # for
# say(connection, 'I need a bed')
# return
s['jobstate'].state = s['jobstate'].sleep_with_bed
s['jobstate'].sleep_with_bed_states.state = s['jobstate'].sleep_with_bed_states.find_bed_spot
elif 'get wood and survive' in chat_packet.json_data: elif 'get wood and survive' in chat_packet.json_data:
for i in player_info.inv.values(): for i in player_info.inv.values():
print(i.item_id)
if i.item_id in items.BED_IDS: if i.item_id in items.BED_IDS:
break break
else: # for else: # for
@ -932,8 +1320,21 @@ def main(connection, player_info):
s['jobstate'].state = s['jobstate'].lumberjack s['jobstate'].state = s['jobstate'].lumberjack
s['jobstate'].survive = True s['jobstate'].survive = True
elif 'get sand and survive' in chat_packet.json_data:
for i in player_info.inv.values():
print(i.item_id)
if i.item_id in items.BED_IDS:
break
else: # for
say(connection, 'I need a bed')
return
s['jobstate'].state = s['jobstate'].gather_sand
s['jobstate'].survive = True
elif 'get wood' in chat_packet.json_data: elif 'get wood' in chat_packet.json_data:
s['jobstate'].state = s['jobstate'].lumberjack s['jobstate'].state = s['jobstate'].lumberjack
elif 'get sand' in chat_packet.json_data:
s['jobstate'].state = s['jobstate'].gather_sand
elif 'stop job' in chat_packet.json_data: elif 'stop job' in chat_packet.json_data:
say(connection, 'ok') say(connection, 'ok')
s['jobstate'].state = s['jobstate'].stop s['jobstate'].state = s['jobstate'].stop

View File

@ -1,7 +1,7 @@
import minecraft.networking.packets import minecraft.networking.packets
from minecraft.networking.packets import Packet from minecraft.networking.packets import Packet
from minecraft.networking.types import BlockFace, VarInt, Position, Boolean, Byte, UnsignedByte, Short, TrailingByteArray from minecraft.networking.types import BlockFace, VarInt, Position, Boolean, Byte, UnsignedByte, Short, TrailingByteArray, Long
from minecraft.networking.types.basic import Type from minecraft.networking.types.basic import Type
#def qot(x): #def qot(x):
@ -15,9 +15,9 @@ class AcknowledgePlayerDiggingPacket(Packet):
id = 0x08 id = 0x08
packet_name = 'acknowledge player digging' packet_name = 'acknowledge player digging'
definition = [ definition = [
{'status': VarInt},
{'location': Position}, {'location': Position},
{'face': VarInt}, {'block': VarInt},
{'status': VarInt},
{'successful': Boolean}, {'successful': Boolean},
] ]
@ -72,8 +72,6 @@ class Slot(Type):
# TODO # TODO
pass pass
class SetSlotPacket(Packet): class SetSlotPacket(Packet):
id = 0x17 id = 0x17
packet_name = 'set slot' packet_name = 'set slot'
@ -83,6 +81,14 @@ class SetSlotPacket(Packet):
{'slot_data': Slot}, {'slot_data': Slot},
] ]
class TimeUpdatePacket(Packet):
id = 0x4F
packet_name = 'time update'
definition = [
{'world_age': Long},
{'time_of_day': Long},
]
@ -93,6 +99,7 @@ def get_packets(old_get_packets):
packets.add(AcknowledgePlayerDiggingPacket) packets.add(AcknowledgePlayerDiggingPacket)
packets.add(BlockBreakAnimationPacket) packets.add(BlockBreakAnimationPacket)
packets.add(SetSlotPacket) packets.add(SetSlotPacket)
packets.add(TimeUpdatePacket)
return packets return packets
return lambda x: wrapper(old_get_packets, x) return lambda x: wrapper(old_get_packets, x)
@ -129,3 +136,14 @@ class PickItemPacket(Packet):
definition = [ definition = [
{'slot_to_use': VarInt}, {'slot_to_use': VarInt},
] ]
class HeldItemChangePacket(Packet):
# Sent when the player changes the slot selection
# https://wiki.vg/Protocol#Held_Item_Change_.28serverbound.29
id = 0x23
packet_name = 'held item change'
definition = [
{'slot': Short},
]