Compare commits

..

5 Commits

Author SHA1 Message Date
c33e1e04b8 Adjust find sand algo 2020-09-17 03:11:08 -06:00
ce22946023 Fix more sand bugs 2020-09-17 00:01:10 -06:00
5c21634d16 Fix sand gather job bugs 2020-09-16 20:31:46 -06:00
f2e0b162f9 Port over job sleeping 2020-09-16 20:11:42 -06:00
58458a561f Fix jobstate bugs 2020-09-16 19:12:01 -06:00
6 changed files with 186 additions and 68 deletions

View File

@ -15,9 +15,13 @@ for name, data in JSON_BLOCKS.items():
for state in data['states']: for state in data['states']:
BLOCKS[state['id']] = name.replace('minecraft:', '') BLOCKS[state['id']] = name.replace('minecraft:', '')
AIR = 0
SAND = 66
SINGLE_SNOW = 3921 SINGLE_SNOW = 3921
SOUL_TORCH = 4008 SOUL_TORCH = 4008
AVOID = [ AVOID = [
'lava', 'lava',
'water', 'water',

7
bot.py
View File

@ -32,6 +32,8 @@ import utils
importlib.reload(utils) importlib.reload(utils)
import path import path
importlib.reload(path) importlib.reload(path)
import jobs
importlib.reload(jobs)
last_tick = time.time() last_tick = time.time()
@ -133,6 +135,7 @@ def tick(global_state):
g.connection.write_packet(packet, force=True) g.connection.write_packet(packet, force=True)
g.game.tick() g.game.tick()
g.job.tick()
def init(global_state): def init(global_state):
@ -150,8 +153,7 @@ def init(global_state):
g.breaking = None g.breaking = None
g.break_time = 0 g.break_time = 0
#g.jobstate = JobStates(connection, player_info) g.job = jobs.JobStates(g)
#g.jobstate.run()
def bot(global_state): def bot(global_state):
g = global_state g = global_state
@ -191,6 +193,7 @@ def bot(global_state):
print('Chunks loaded.') print('Chunks loaded.')
init(g) init(g)
print('Initialized.')
while g.running: while g.running:
tick(g) tick(g)

86
game.py
View File

@ -2,12 +2,13 @@ import re
import time import time
import importlib import importlib
from math import hypot from math import hypot
from itertools import count
from panda3d.core import LPoint3f from panda3d.core import LPoint3f
from minecraft.networking.packets import Packet, clientbound, serverbound from minecraft.networking.packets import Packet, clientbound, serverbound
from protocol.packets import TimeUpdatePacket, SetSlotPacket, PlayerDiggingPacket, BlockBreakAnimationPacket, AcknowledgePlayerDiggingPacket from protocol.packets import TimeUpdatePacket, SetSlotPacket, PlayerDiggingPacket, BlockBreakAnimationPacket, AcknowledgePlayerDiggingPacket, HeldItemChangePacket, PickItemPacket
import utils import utils
importlib.reload(utils) importlib.reload(utils)
@ -15,6 +16,8 @@ import path
importlib.reload(path) importlib.reload(path)
import blocks import blocks
importlib.reload(blocks) importlib.reload(blocks)
import items
importlib.reload(items)
class MCWorld: class MCWorld:
def __init__(self, global_state): def __init__(self, global_state):
@ -40,7 +43,7 @@ class MCWorld:
def find_trees(self, center, distance): def find_trees(self, center, distance):
logs = [] logs = []
for i in range(5): for i in range(5):
check = utils.padd(center, alternate(i, 3)) check = utils.padd(center, utils.alternate(i, 3))
logs.extend(self.find_blocks(check, distance, blocks.LOG_IDS, 50)) logs.extend(self.find_blocks(check, distance, blocks.LOG_IDS, 50))
trees = [] trees = []
@ -66,37 +69,37 @@ class MCWorld:
log = utils.padd(log, path.BLOCK_BELOW) log = utils.padd(log, path.BLOCK_BELOW)
trees.append(log) trees.append(log)
trees.sort(key=lambda x: phyp(center, x)) trees.sort(key=lambda x: utils.phyp(center, x))
return trees return trees
def find_tree_openings(self, tree): def find_tree_openings(self, tree):
# returns coords in a cardinal direction where we can stand by tree # returns coords in a cardinal direction where we can stand by tree
maze_solver = MazeSolver(self.g.chunks) maze_solver = path.Pathfinder(self.g.chunks)
result = [] result = []
# TODO: make sure only non-solid and leaves between # TODO: make sure only non-solid and leaves between
# make sure traversable too # make sure traversable too
for distance in range(5): for distance in range(5):
for direction in CHECK_DIRECTIONS: for direction in path.CHECK_DIRECTIONS:
offset = pmul(direction, distance+1) offset = utils.pmul(direction, distance+1)
if maze_solver.check_traverse(tree, offset): if maze_solver.check_traverse(tree, offset):
result.append(utils.padd(tree, offset)) result.append(utils.padd(tree, offset))
return result return result
def path_to_place(self, start, place): def path_to_place(self, start, place):
maze_solver = MazeSolver(self.g.chunks) maze_solver = path.Pathfinder(self.g.chunks)
try: try:
s = maze_solver.astar(start, place) s = maze_solver.astar(start, place)
return list(s) if s else None return list(s) if s else None
except AStarTimeout: except path.AStarTimeout:
return None return None
def find_bed_areas(self, center, distance): def find_bed_areas(self, center, distance):
air = [] air = []
for i in range(5): for i in range(5):
check = utils.padd(center, alternate(i, 1)) check = utils.padd(center, utils.alternate(i, 1))
air.extend(self.find_blocks(check, distance, [0], 200)) air.extend(self.find_blocks(check, distance, [0], 200))
areas = [] areas = []
@ -115,11 +118,11 @@ class MCWorld:
areas.append(a) areas.append(a)
areas.sort(key=lambda x: phyp(center, x)) areas.sort(key=lambda x: utils.phyp(center, x))
return areas return areas
def sand_adjacent_safe(self, sand): def sand_adjacent_safe(self, sand):
for direction in CHECK_DIRECTIONS: for direction in path.CHECK_DIRECTIONS:
if self.block_at(*utils.padd(sand, direction)) in blocks.AVOID_IDS: if self.block_at(*utils.padd(sand, direction)) in blocks.AVOID_IDS:
return False return False
return True return True
@ -127,8 +130,8 @@ class MCWorld:
def find_sand(self, center, distance, origin): def find_sand(self, center, distance, origin):
sand = [] sand = []
for i in range(10): for i in range(10):
check = utils.padd(center, alternate(i, 1)) check = utils.padd(center, utils.alternate(i, 1))
sand.extend(self.find_blocks(check, distance, [66], 20)) sand.extend(self.find_blocks(check, distance, [blocks.SAND], 20))
safe_sand = [] safe_sand = []
for s in sand: for s in sand:
@ -155,7 +158,7 @@ class MCWorld:
# returns coords in a cardinal direction where we can stand by bed # returns coords in a cardinal direction where we can stand by bed
result = [] result = []
for direction in CHECK_DIRECTIONS: for direction in path.CHECK_DIRECTIONS:
result.append(utils.padd(area, direction)) result.append(utils.padd(area, direction))
return result return result
@ -188,11 +191,11 @@ class Game:
print('new waypoint:', self.g.goal) print('new waypoint:', self.g.goal)
start = time.time() start = time.time()
solution = path.Pathfinder(self.g.chunks).astar(utils.pint(self.g.pos), utils.pint(g.goal)) solution = path.Pathfinder(self.g.chunks).astar(utils.pint(self.g.pos), utils.pint(self.g.goal))
if solution: if solution:
solution = list(solution) solution = list(solution)
self.g.path = solution self.g.path = solution
#g.jobstate.state = self.g.jobstate.stop self.g.job.state = self.g.job.stop
print(len(solution)) print(len(solution))
print(solution) print(solution)
print(round(time.time() - start, 3), 'seconds') print(round(time.time() - start, 3), 'seconds')
@ -261,6 +264,34 @@ class Game:
self.break_block((616, 78, 496)) self.break_block((616, 78, 496))
reply = 'ok' reply = 'ok'
if command == 'gather' and data:
if data == 'wood':
self.g.job.state = self.g.job.lumberjack
reply = 'ok'
elif data == 'sand':
self.g.job.state = self.g.job.gather_sand
reply = 'ok'
if reply:
for i in self.g.inv.values():
print(i.item_id)
if i.item_id in items.BED_IDS:
break
else:
reply += ', I need a bed'
if command == 'stop':
self.g.job.state = self.g.job.stop
reply = 'ok'
if command == 'inv':
for i in self.g.inv.values():
if i.present:
print(items.ITEM_NAMES[i.item_id], 'x', i.item_count)
if command == 'time':
reply = str(self.g.time)
if reply: if reply:
print(reply) print(reply)
self.g.chat.send(reply) self.g.chat.send(reply)
@ -291,6 +322,7 @@ class Game:
packet.location = self.g.breaking packet.location = self.g.breaking
packet.face = 1 packet.face = 1
self.g.connection.write_packet(packet) self.g.connection.write_packet(packet)
self.g.chunks.set_block_at(*self.g.breaking, 0)
self.g.breaking = None self.g.breaking = None
@ -298,7 +330,8 @@ class Game:
print(packet) print(packet)
def handle_break_ack(self, packet): def handle_break_ack(self, packet):
print(packet) #print(packet)
return
def animate(self): def animate(self):
packet = serverbound.play.AnimationPacket() packet = serverbound.play.AnimationPacket()
@ -308,7 +341,7 @@ class Game:
def place_block(self, location, face): def place_block(self, location, face):
packet = serverbound.play.PlayerBlockPlacementPacket() packet = serverbound.play.PlayerBlockPlacementPacket()
packet.hand = 0 packet.hand = 0
packet.location = pos packet.location = location
packet.face = face packet.face = face
packet.x = 0.5 packet.x = 0.5
packet.y = 0.5 packet.y = 0.5
@ -316,6 +349,23 @@ class Game:
packet.inside_block = False packet.inside_block = False
self.g.connection.write_packet(packet) self.g.connection.write_packet(packet)
def pick(self, slot):
packet = PickItemPacket()
packet.slot_to_use = slot
self.g.connection.write_packet(packet)
def hold(self, slot):
packet = HeldItemChangePacket()
packet.slot = slot
self.g.connection.write_packet(packet)
def choose_slot(self, slot):
if slot >= 36:
slot -= 36
self.hold(slot)
else:
self.pick(slot)
def tick(self): def tick(self):
if self.g.breaking: if self.g.breaking:
self.animate() self.animate()

31
items.py Normal file
View File

@ -0,0 +1,31 @@
import json
with open('mcdata/registries.json') as f:
ITEMS = json.load(f)['minecraft:item']['entries']
BEDS = [
'white_bed',
'orange_bed',
'magenta_bed',
'light_blue_bed',
'yellow_bed',
'lime_bed',
'pink_bed',
'gray_bed',
'light_gray_bed',
'cyan_bed',
'purple_bed',
'blue_bed',
'brown_bed',
'green_bed',
'red_bed',
'black_bed',
]
BED_IDS = set()
for item_name in BEDS:
BED_IDS.add(ITEMS['minecraft:'+item_name]['protocol_id'])
ITEM_NAMES = {}
for item_name, item in ITEMS.items():
ITEM_NAMES[ITEMS[item_name]['protocol_id']] = item_name.replace('minecraft:', '')

87
jobs.py
View File

@ -13,6 +13,8 @@ import path
importlib.reload(path) importlib.reload(path)
import blocks import blocks
importlib.reload(blocks) importlib.reload(blocks)
import items
importlib.reload(items)
class LumberjackStates: class LumberjackStates:
@ -55,10 +57,10 @@ class LumberjackStates:
self.state = self.cleanup self.state = self.cleanup
return return
path = w.path_to_place(p, self.openings[0]) navpath = w.path_to_place(p, self.openings[0])
if path: if navpath:
self.g.path = path self.g.path = navpath
self.state = self.going_to_tree self.state = self.going_to_tree
else: else:
self.openings.pop(0) self.openings.pop(0)
@ -71,10 +73,10 @@ class LumberjackStates:
def clear_leaves(self): def clear_leaves(self):
if not self.g.breaking: if not self.g.breaking:
p = utils.pint(self.g.pos) p = utils.pint(self.g.pos)
diff = psub(self.tree, p) diff = utils.psub(self.tree, p)
for x in diffrange(diff[0]): for x in utils.diffrange(diff[0]):
for z in diffrange(diff[2]): for z in utils.diffrange(diff[2]):
for y in range(2): for y in range(2):
check = utils.padd(p, (x, y, z)) check = utils.padd(p, (x, y, z))
if check == self.tree: if check == self.tree:
@ -100,10 +102,10 @@ class LumberjackStates:
else: else:
w = self.g.world w = self.g.world
p = utils.pint(self.g.pos) p = utils.pint(self.g.pos)
path = w.path_to_place(p, self.tree) navpath = w.path_to_place(p, self.tree)
if path: if navpath:
self.g.path = path self.g.path = navpath
self.state = self.going_to_trunk_base self.state = self.going_to_trunk_base
else: else:
self.openings.pop(0) self.openings.pop(0)
@ -134,7 +136,7 @@ class LumberjackStates:
def wait(self): def wait(self):
# wait for the last log to fall # wait for the last log to fall
if self.wait_time > 0: if self.wait_time > 0:
self.wait_time -= TICK self.wait_time -= utils.TICK
else: else:
self.state = self.cleanup self.state = self.cleanup
@ -149,7 +151,6 @@ class LumberjackStates:
def __init__(self, global_state): def __init__(self, global_state):
self.g = global_state self.g = global_state
self.l = self.g.local_state
self.state = self.idle self.state = self.idle
self.tree = None self.tree = None
@ -179,12 +180,14 @@ class GatherSandStates:
w = self.g.world w = self.g.world
p = utils.pint(self.g.pos) p = utils.pint(self.g.pos)
sand = w.find_sand(p, 150, self.origin) sand = w.find_sand(p, 50, self.origin)
print('Found sand:', sand) print('Found sand:', sand)
while sand[0] in self.bad_sand: for check in sand:
sand.pop(0) if check in self.bad_sand:
self.sand = sand[0] continue
self.sand = check
break
self.state = self.nav_to_sand self.state = self.nav_to_sand
@ -192,12 +195,12 @@ class GatherSandStates:
w = self.g.world w = self.g.world
p = utils.pint(self.g.pos) p = utils.pint(self.g.pos)
w.chunks.set_block_at(*self.sand, 0) self.g.chunks.set_block_at(*self.sand, blocks.AIR)
path = w.path_to_place(p, self.sand) navpath = w.path_to_place(p, self.sand)
w.chunks.set_block_at(*self.sand, 66) self.g.chunks.set_block_at(*self.sand, blocks.SAND)
if path: if navpath:
self.g.path = path[:-1] self.g.path = navpath[:-1]
self.state = self.going_to_sand self.state = self.going_to_sand
else: else:
self.bad_sand.append(self.sand) self.bad_sand.append(self.sand)
@ -219,10 +222,10 @@ class GatherSandStates:
def get_sand(self): def get_sand(self):
w = self.g.world w = self.g.world
p = utils.pint(self.g.pos) p = utils.pint(self.g.pos)
path = w.path_to_place(p, self.sand) navpath = w.path_to_place(p, self.sand)
if path: if navpath:
self.g.path = path self.g.path = navpath
self.state = self.going_to_item self.state = self.going_to_item
else: else:
self.bad_sand.append(self.sand) self.bad_sand.append(self.sand)
@ -244,7 +247,6 @@ class GatherSandStates:
def __init__(self, global_state): def __init__(self, global_state):
self.g = global_state self.g = global_state
self.l = self.g.local_state
self.state = self.idle self.state = self.idle
self.origin = utils.pint(self.g.pos) self.origin = utils.pint(self.g.pos)
@ -289,16 +291,16 @@ class SleepWithBedStates:
openings = w.find_bed_openings(self.area) openings = w.find_bed_openings(self.area)
for o in openings: for o in openings:
path = w.path_to_place(p, o) navpath = w.path_to_place(p, o)
self.opening = o self.opening = o
if path: break if navpath: break
else: # for else: # for
print('Unable to get to bed area', self.area) print('Unable to get to bed area', self.area)
self.bad_areas.append(self.area) self.bad_areas.append(self.area)
self.state = self.cleanup self.state = self.cleanup
return return
self.g.path = path self.g.path = navpath
self.state = self.going_to_area self.state = self.going_to_area
self.last_area = self.area self.last_area = self.area
@ -312,22 +314,22 @@ class SleepWithBedStates:
if item.item_id in items.BED_IDS: if item.item_id in items.BED_IDS:
print('Found bed in slot', slot) print('Found bed in slot', slot)
self.g.look_at = utils.padd(self.area, path.BLOCK_BELOW) self.g.look_at = utils.padd(self.area, path.BLOCK_BELOW)
choose_slot(self.connection, slot) self.g.game.choose_slot(slot)
self.state = self.place_bed self.state = self.place_bed
break break
else: # for else: # for
say(self.connection, 'I need a bed') self.g.chat.send('I need a bed')
self.state = self.cleanup self.state = self.cleanup
def place_bed(self): def place_bed(self):
place_block(self.connection, self.area, BlockFace.TOP) self.g.game.place_block(self.area, BlockFace.TOP)
self.state = self.use_bed self.state = self.use_bed
def use_bed(self): def use_bed(self):
if self.g.time >= 12542: if self.g.time >= 12542:
print('Sleeping') print('Sleeping')
place_block(self.connection, self.area, BlockFace.TOP) self.g.game.place_block(self.area, BlockFace.TOP)
say(self.connection, 'zzz') self.g.chat.send('zzz')
self.state = self.sleep_bed self.state = self.sleep_bed
def sleep_bed(self): def sleep_bed(self):
@ -348,7 +350,7 @@ class SleepWithBedStates:
def wait(self): def wait(self):
# wait to pick up bed # wait to pick up bed
if self.wait_time > 0: if self.wait_time > 0:
self.wait_time -= TICK self.wait_time -= utils.TICK
else: else:
self.state = self.cleanup self.state = self.cleanup
@ -362,7 +364,6 @@ class SleepWithBedStates:
def __init__(self, global_state): def __init__(self, global_state):
self.g = global_state self.g = global_state
self.l = self.g.local_state
self.state = self.idle self.state = self.idle
self.area = None self.area = None
@ -402,10 +403,9 @@ class JobStates:
s.state = s.init s.state = s.init
# check time, etc # check time, etc
if self.survive: self.prev_state = self.gather_sand
self.prev_state = self.gather_sand self.state = self.sleep_with_bed
self.state = self.sleep_with_bed return
return
s.run() s.run()
@ -417,10 +417,9 @@ class JobStates:
s.state = s.init s.state = s.init
# check time, etc # check time, etc
if self.survive: self.prev_state = self.lumberjack
self.prev_state = self.lumberjack self.state = self.sleep_with_bed
self.state = self.sleep_with_bed return
return
s.run() s.run()
@ -432,14 +431,12 @@ class JobStates:
def __init__(self, global_state): def __init__(self, global_state):
self.g = global_state self.g = global_state
self.l = self.g.local_state
self.state = self.idle self.state = self.idle
self.prev_state = None self.prev_state = None
self.lumberjack_states = LumberjackStates(self.g) self.lumberjack_states = LumberjackStates(self.g)
self.gather_sand_states = GatherSandStates(self.g) self.gather_sand_states = GatherSandStates(self.g)
self.sleep_with_bed_states = SleepWithBedStates(self.g) self.sleep_with_bed_states = SleepWithBedStates(self.g)
self.survive = False
def run(self): def tick(self):
self.state() self.state()

View File

@ -1,5 +1,5 @@
import importlib import importlib
from math import floor, ceil from math import floor, ceil, sqrt, hypot
import blocks import blocks
importlib.reload(blocks) importlib.reload(blocks)
@ -22,7 +22,7 @@ def phyp_bias(p1, p2, origin):
origin_distance = phyp(origin, p2) origin_distance = phyp(origin, p2)
height_diff = p2[1] - p1[1] height_diff = p2[1] - p1[1]
height_diff = height_diff*8 if height_diff < 0 else height_diff*0.5 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 return hypot(p1[0] - p2[0], height_diff, p1[2] - p2[2]) + origin_distance*0.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]))
@ -31,12 +31,45 @@ def cap(x, amount):
sign = 1 if x >= 0 else -1 sign = 1 if x >= 0 else -1
return sign * min(abs(x), amount) return sign * min(abs(x), amount)
def spiral(n):
# return x, 0, z coords along a spiral at step n
# I forget where I found this
n += 1
k = ceil((sqrt(n)-1)/2)
t = 2 * k + 1
m = t**2
t = t - 1
if n >= m-t:
return k-(m-n), 0, -k
else:
m = m-t
if n >= m-t:
return -k, 0, -k+(m-n)
else:
m = m-t
if n >= m-t:
return -k+(m-n), 0, k
else:
return k, 0, k-(m-n-t)
def alternate(n, amount):
# return 0, y, 0 where y alternates +/- by amount
# example: 0, 2, -2, 4, -4, 6, -6 for amount = 2
sign = 1 if n % 2 else -1
return (0, ceil(n/2) * sign * amount, 0)
def diffrange(n):
# same as range(n+1) but can go negative
sign = 1 if n >= 0 else -1
return range(0, n+sign, sign)
def break_time(block_id, held_item=0, in_water=False, on_ground=True, enchantments=[], effects={}): def break_time(block_id, held_item=0, in_water=False, on_ground=True, enchantments=[], effects={}):
# from PrismarineJS/prismarine-block # from PrismarineJS/prismarine-block
data = blocks.get(block_id) data = blocks.get(block_id)
can_harvest = 'harvestTools' not in data or str(held_item) in data['harvestTools'] can_harvest = 'harvestTools' not in data or str(held_item) in data['harvestTools']
tool_multipliers = blocks.mcd.materials.get(data['material'], []) material = data.get('material', 'n/a')
tool_multipliers = blocks.mcd.materials.get(material, [])
is_best_tool = held_item in tool_multipliers is_best_tool = held_item in tool_multipliers
time = data['hardness'] time = data['hardness']