Compare commits

..

No commits in common. "c33e1e04b8f0c139f0bc8d40a6e617cc4200efa1" and "c30ac5aefc3fe0225deb1386e410ddf9be6eb393" have entirely different histories.

6 changed files with 68 additions and 186 deletions

View File

@ -15,13 +15,9 @@ 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,8 +32,6 @@ 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()
@ -135,7 +133,6 @@ 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):
@ -153,7 +150,8 @@ def init(global_state):
g.breaking = None g.breaking = None
g.break_time = 0 g.break_time = 0
g.job = jobs.JobStates(g) #g.jobstate = JobStates(connection, player_info)
#g.jobstate.run()
def bot(global_state): def bot(global_state):
g = global_state g = global_state
@ -193,7 +191,6 @@ 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,13 +2,12 @@ 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, HeldItemChangePacket, PickItemPacket from protocol.packets import TimeUpdatePacket, SetSlotPacket, PlayerDiggingPacket, BlockBreakAnimationPacket, AcknowledgePlayerDiggingPacket
import utils import utils
importlib.reload(utils) importlib.reload(utils)
@ -16,8 +15,6 @@ 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):
@ -43,7 +40,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, utils.alternate(i, 3)) check = utils.padd(center, 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 = []
@ -69,37 +66,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: utils.phyp(center, x)) trees.sort(key=lambda x: 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 = path.Pathfinder(self.g.chunks) maze_solver = MazeSolver(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 path.CHECK_DIRECTIONS: for direction in CHECK_DIRECTIONS:
offset = utils.pmul(direction, distance+1) offset = 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 = path.Pathfinder(self.g.chunks) maze_solver = MazeSolver(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 path.AStarTimeout: except 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, utils.alternate(i, 1)) check = utils.padd(center, alternate(i, 1))
air.extend(self.find_blocks(check, distance, [0], 200)) air.extend(self.find_blocks(check, distance, [0], 200))
areas = [] areas = []
@ -118,11 +115,11 @@ class MCWorld:
areas.append(a) areas.append(a)
areas.sort(key=lambda x: utils.phyp(center, x)) areas.sort(key=lambda x: phyp(center, x))
return areas return areas
def sand_adjacent_safe(self, sand): def sand_adjacent_safe(self, sand):
for direction in path.CHECK_DIRECTIONS: for direction in 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
@ -130,8 +127,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, utils.alternate(i, 1)) check = utils.padd(center, alternate(i, 1))
sand.extend(self.find_blocks(check, distance, [blocks.SAND], 20)) sand.extend(self.find_blocks(check, distance, [66], 20))
safe_sand = [] safe_sand = []
for s in sand: for s in sand:
@ -158,7 +155,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 path.CHECK_DIRECTIONS: for direction in CHECK_DIRECTIONS:
result.append(utils.padd(area, direction)) result.append(utils.padd(area, direction))
return result return result
@ -191,11 +188,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(self.g.goal)) solution = path.Pathfinder(self.g.chunks).astar(utils.pint(self.g.pos), utils.pint(g.goal))
if solution: if solution:
solution = list(solution) solution = list(solution)
self.g.path = solution self.g.path = solution
self.g.job.state = self.g.job.stop #g.jobstate.state = self.g.jobstate.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')
@ -264,34 +261,6 @@ 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)
@ -322,7 +291,6 @@ 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
@ -330,8 +298,7 @@ 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()
@ -341,7 +308,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 = location packet.location = pos
packet.face = face packet.face = face
packet.x = 0.5 packet.x = 0.5
packet.y = 0.5 packet.y = 0.5
@ -349,23 +316,6 @@ 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()

View File

@ -1,31 +0,0 @@
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,8 +13,6 @@ 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:
@ -57,10 +55,10 @@ class LumberjackStates:
self.state = self.cleanup self.state = self.cleanup
return return
navpath = w.path_to_place(p, self.openings[0]) path = w.path_to_place(p, self.openings[0])
if navpath: if path:
self.g.path = navpath self.g.path = path
self.state = self.going_to_tree self.state = self.going_to_tree
else: else:
self.openings.pop(0) self.openings.pop(0)
@ -73,10 +71,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 = utils.psub(self.tree, p) diff = psub(self.tree, p)
for x in utils.diffrange(diff[0]): for x in diffrange(diff[0]):
for z in utils.diffrange(diff[2]): for z in 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:
@ -102,10 +100,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)
navpath = w.path_to_place(p, self.tree) path = w.path_to_place(p, self.tree)
if navpath: if path:
self.g.path = navpath self.g.path = path
self.state = self.going_to_trunk_base self.state = self.going_to_trunk_base
else: else:
self.openings.pop(0) self.openings.pop(0)
@ -136,7 +134,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 -= utils.TICK self.wait_time -= TICK
else: else:
self.state = self.cleanup self.state = self.cleanup
@ -151,6 +149,7 @@ 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
@ -180,14 +179,12 @@ 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, 50, self.origin) sand = w.find_sand(p, 150, self.origin)
print('Found sand:', sand) print('Found sand:', sand)
for check in sand: while sand[0] in self.bad_sand:
if check in self.bad_sand: sand.pop(0)
continue self.sand = sand[0]
self.sand = check
break
self.state = self.nav_to_sand self.state = self.nav_to_sand
@ -195,12 +192,12 @@ class GatherSandStates:
w = self.g.world w = self.g.world
p = utils.pint(self.g.pos) p = utils.pint(self.g.pos)
self.g.chunks.set_block_at(*self.sand, blocks.AIR) w.chunks.set_block_at(*self.sand, 0)
navpath = w.path_to_place(p, self.sand) path = w.path_to_place(p, self.sand)
self.g.chunks.set_block_at(*self.sand, blocks.SAND) w.chunks.set_block_at(*self.sand, 66)
if navpath: if path:
self.g.path = navpath[:-1] self.g.path = path[:-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)
@ -222,10 +219,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)
navpath = w.path_to_place(p, self.sand) path = w.path_to_place(p, self.sand)
if navpath: if path:
self.g.path = navpath self.g.path = path
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)
@ -247,6 +244,7 @@ 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)
@ -291,16 +289,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:
navpath = w.path_to_place(p, o) path = w.path_to_place(p, o)
self.opening = o self.opening = o
if navpath: break if path: 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 = navpath self.g.path = path
self.state = self.going_to_area self.state = self.going_to_area
self.last_area = self.area self.last_area = self.area
@ -314,22 +312,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)
self.g.game.choose_slot(slot) choose_slot(self.connection, slot)
self.state = self.place_bed self.state = self.place_bed
break break
else: # for else: # for
self.g.chat.send('I need a bed') say(self.connection, 'I need a bed')
self.state = self.cleanup self.state = self.cleanup
def place_bed(self): def place_bed(self):
self.g.game.place_block(self.area, BlockFace.TOP) place_block(self.connection, 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')
self.g.game.place_block(self.area, BlockFace.TOP) place_block(self.connection, self.area, BlockFace.TOP)
self.g.chat.send('zzz') say(self.connection, 'zzz')
self.state = self.sleep_bed self.state = self.sleep_bed
def sleep_bed(self): def sleep_bed(self):
@ -350,7 +348,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 -= utils.TICK self.wait_time -= TICK
else: else:
self.state = self.cleanup self.state = self.cleanup
@ -364,6 +362,7 @@ 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
@ -403,9 +402,10 @@ class JobStates:
s.state = s.init s.state = s.init
# check time, etc # check time, etc
self.prev_state = self.gather_sand if self.survive:
self.state = self.sleep_with_bed self.prev_state = self.gather_sand
return self.state = self.sleep_with_bed
return
s.run() s.run()
@ -417,9 +417,10 @@ class JobStates:
s.state = s.init s.state = s.init
# check time, etc # check time, etc
self.prev_state = self.lumberjack if self.survive:
self.state = self.sleep_with_bed self.prev_state = self.lumberjack
return self.state = self.sleep_with_bed
return
s.run() s.run()
@ -431,12 +432,14 @@ 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 tick(self): def run(self):
self.state() self.state()

View File

@ -1,5 +1,5 @@
import importlib import importlib
from math import floor, ceil, sqrt, hypot from math import floor, ceil
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*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]))
@ -31,45 +31,12 @@ 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']
material = data.get('material', 'n/a') tool_multipliers = blocks.mcd.materials.get(data['material'], [])
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']