Gather sand and sleep at night

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

503
bot.py
View File

@@ -13,6 +13,7 @@ from minecraft import authentication
from minecraft.exceptions import YggdrasilError
from minecraft.networking.connection import Connection
from minecraft.networking.packets import clientbound, serverbound
from minecraft.networking.types import BlockFace
from minecraft.compat import input
from minecraft.managers import chunks, ChunksManager
@@ -37,6 +38,7 @@ BLOCK_ABOVE2 = (0, +2, 0)
BLOCK_ABOVE3 = (0, +3, 0)
BLOCK_ABOVE4 = (0, +4, 0)
BLOCK_BELOW = (0, -1, 0)
BLOCK_BELOW2 = (0, -2, 0)
TRAVERSE_NORTH = (0, 0, -1)
TRAVERSE_SOUTH = (0, 0, +1)
@@ -152,6 +154,12 @@ def pmul(p, s):
def phyp(p1, p2):
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):
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_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):
packet = serverbound.play.ChatPacket()
packet.message = message
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_BELOW = (0, -1, 0)
@@ -459,32 +495,90 @@ class MCWorld:
maze_solver = MazeSolver(self.chunks)
result = []
# TODO: make sure only non-solid and leaves between
# make sure traversable too
for distance in range(5):
for direction in CHECK_DIRECTIONS:
offset = (0, 0, 0)
for _ in range(distance):
offset = padd(offset, direction)
offset = pmul(direction, distance+1)
if maze_solver.check_traverse(tree, offset):
result.append(padd(tree, offset))
return result
def path_to_opening(self, start, opening):
def path_to_place(self, start, place):
maze_solver = MazeSolver(self.chunks)
try:
s = maze_solver.astar(start, opening)
s = maze_solver.astar(start, place)
return list(s) if s else None
except AStarTimeout:
return None
def path_to_base(self, start, base):
maze_solver = MazeSolver(self.chunks)
def find_bed_areas(self, center, distance):
air = []
for i in range(5):
check = padd(center, alternate(i, 1))
air.extend(self.find_blocks(check, distance, [0], 200))
try:
s = maze_solver.astar(start, base)
return list(s) if s else None
except AStarTimeout:
return None
areas = []
for a in air:
# check for ground around the area
if len(self.find_blocks(padd(a, BLOCK_BELOW), 2, blocks.NON_SOLID_IDS, 9)):
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:
@@ -512,24 +606,31 @@ class LumberjackStates:
trees.pop(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:
path = w.path_to_opening(p, o)
self.opening = o
if path: break
else: # for
def choose_opening(self):
w = MCWorld(self.player_info.chunks)
p = pint(self.player_info.pos)
print('openings:', self.openings)
if not len(self.openings):
print('Unable to get to tree', self.tree)
self.bad_trees.append(self.tree)
self.state = self.done
self.state = self.cleanup
return
s['path'] = path
self.state = self.going_to_tree
path = w.path_to_place(p, self.openings[0])
if path:
s['path'] = path
self.state = self.going_to_tree
else:
self.openings.pop(0)
def going_to_tree(self):
d = self.player_info.pos - LPoint3f(*self.opening)
if d.length() < 1:
if pint(self.player_info.pos) == self.openings[0]:
s['look_at'] = self.tree
self.state = self.clear_leaves
@@ -542,13 +643,15 @@ class LumberjackStates:
for z in diffrange(diff[2]):
for y in range(2):
check = padd(p, (x, y, z))
if self.blog(check):
self.state = self.clear_trunk_base
return
if check == self.tree:
break
if not self.bair(check):
print('Breaking leaf')
s['break'] = (check, 0.5)
return
self.state = self.clear_trunk_base
def clear_trunk_base(self):
if not s['break_timeout']:
base = self.tree
@@ -563,14 +666,17 @@ class LumberjackStates:
else:
w = MCWorld(self.player_info.chunks)
p = pint(self.player_info.pos)
path = w.path_to_base(p, self.tree)
path = w.path_to_place(p, self.tree)
s['path'] = path
self.state = self.going_to_trunk_base
if path:
s['path'] = path
self.state = self.going_to_trunk_base
else:
self.openings.pop(0)
self.state = self.choose_opening
def going_to_trunk_base(self):
d = self.player_info.pos - LPoint3f(*self.opening)
if d.length() < 1:
if pint(self.player_info.pos) == self.tree:
s['look_at'] = padd(self.tree, BLOCK_ABOVE2)
self.state = self.clear_trunk
@@ -589,7 +695,6 @@ class LumberjackStates:
else:
print('Finished clearing tree')
self.wait_time = 0.5
s['look_at'] = None
self.state = self.wait
def wait(self):
@@ -597,9 +702,14 @@ class LumberjackStates:
if self.wait_time > 0:
self.wait_time -= TICK
else:
self.state = self.done
self.state = self.cleanup
def cleanup(self):
s['look_at'] = None
self.state = self.done
def done(self):
# never gets ran, placeholder
return None
@@ -608,7 +718,7 @@ class LumberjackStates:
self.state = self.idle
self.tree = None
self.opening = None
self.openings = []
self.bad_trees = []
self.wait_time = 0
@@ -616,32 +726,283 @@ class LumberjackStates:
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:
def idle(self):
return None
def night_shelter(self):
return None
def sleep_with_bed(self):
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):
l = self.lumberjack_states
if l.state == l.idle:
l.state = l.init
elif l.state == l.done:
s = self.lumberjack_states
if s.state == s.idle:
s.state = s.init
elif s.state == s.done:
s.state = s.init
# 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):
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
def __init__(self, connection, player_info):
# TODO: watch dog if it gets stuck
self.connection = connection
self.player_info = player_info
self.state = self.idle
self.prev_state = None
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
def run(self):
@@ -744,15 +1105,18 @@ def tick(connection, player_info):
look_at_d = p - look_at
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 = (target_pitch - 90) * -1
target_pitch_d = target_pitch - s['pitch']
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:
target_pitch_d = 0 - s['pitch']
s['pitch'] += cap(target_pitch_d, 10)
@@ -788,6 +1152,8 @@ def tick(connection, player_info):
def init(connection, player_info):
p = player_info.pos
s['time'] = 0
s['path'] = []
s['look_at'] = None
s['y_v'] = 0
@@ -803,6 +1169,7 @@ def init(connection, player_info):
s['jobstate'] = JobStates(connection, player_info)
s['jobstate'].run()
def main(connection, player_info):
def handle_join_game(join_game_packet):
print('Connected.')
@@ -850,11 +1217,11 @@ def main(connection, player_info):
connection.register_packet_listener(
x, clientbound.play.BlockChangePacket)
#def y(p):
# print(p)
def handle_time_update(p):
s['time'] = p.time_of_day % 24000
#connection.register_packet_listener(
# y, AcknowledgePlayerDiggingPacket)
connection.register_packet_listener(
handle_time_update, custom_packets.TimeUpdatePacket)
def handle_set_slot(p):
print(p)
@@ -872,6 +1239,9 @@ def main(connection, player_info):
if '!reload' in chat_packet.json_data:
global running
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:
say(connection, '/afk')
elif '!respawn' in chat_packet.json_data:
@@ -919,11 +1289,29 @@ def main(connection, player_info):
for i in player_info.inv.values():
if i.present:
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:
parts = chat_packet.json_data.split('\'')
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:
for i in player_info.inv.values():
print(i.item_id)
if i.item_id in items.BED_IDS:
break
else: # for
@@ -932,8 +1320,21 @@ def main(connection, player_info):
s['jobstate'].state = s['jobstate'].lumberjack
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:
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:
say(connection, 'ok')
s['jobstate'].state = s['jobstate'].stop