Compare commits

...

2 Commits

Author SHA1 Message Date
b8952db742 Detect monsters and flee to safety 2020-12-04 02:49:22 +00:00
5a7a2f0113 Simplify state machines 2020-12-03 03:30:54 +00:00
8 changed files with 528 additions and 295 deletions

View File

@ -16,10 +16,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:', '')
BREAK_DISTANCE = 6
AIR = 0 AIR = 0
SAND = 66 SAND = 66
SINGLE_SNOW = 3921 SINGLE_SNOW = 3921
SOUL_TORCH = 4008 SOUL_TORCH = 4008
EMERALD_BLOCK = 5407
TEST_BLOCK = (616, 78, 496) TEST_BLOCK = (616, 78, 496)
@ -244,6 +247,7 @@ TRAPPED_CHESTS = [
INDEXED = [ INDEXED = [
'chest', 'chest',
'trapped_chest', 'trapped_chest',
'emerald_block',
] ]

2
bot.py
View File

@ -203,6 +203,8 @@ def init(global_state):
g.job = jobs.JobStates(g) g.job = jobs.JobStates(g)
g.chopped_tree = False g.chopped_tree = False
g.queue_afk = False
def bot(global_state): def bot(global_state):
g = global_state g = global_state

440
game.py
View File

@ -2,6 +2,7 @@ import re
import time import time
import importlib import importlib
import random import random
import functools
from math import hypot from math import hypot
from itertools import count from itertools import count
from munch import Munch from munch import Munch
@ -19,6 +20,7 @@ from protocol.packets import (
ClickWindowPacket, CloseWindowPacket, ServerWindowConfirmationPacket, ClickWindowPacket, CloseWindowPacket, ServerWindowConfirmationPacket,
ClientWindowConfirmationPacket, EntityMetadataPacket, ClientWindowConfirmationPacket, EntityMetadataPacket,
SpawnLivingEntityPacket, EntityPositionRotationPacket, DestroyEntitiesPacket, SpawnLivingEntityPacket, EntityPositionRotationPacket, DestroyEntitiesPacket,
EntityActionPacket,
) )
from protocol.types import Slot from protocol.types import Slot
@ -33,6 +35,8 @@ import items
importlib.reload(items) importlib.reload(items)
import data import data
importlib.reload(data) importlib.reload(data)
import mobs
importlib.reload(mobs)
class MCWorld: class MCWorld:
def __init__(self, global_state): def __init__(self, global_state):
@ -41,6 +45,13 @@ class MCWorld:
def block_at(self, x, y, z): def block_at(self, x, y, z):
return self.g.chunks.get_block_at(x, y, z) return self.g.chunks.get_block_at(x, y, z)
def check_air_column(self, pos, distance):
for i in range(distance):
check = utils.padd(pos, (0, i, 0))
if self.block_at(*check) not in blocks.NON_SOLID_IDS:
return False
return True
def find_blocks_3d(self, center, block_ids, distance=0, y_limit=0): def find_blocks_3d(self, center, block_ids, distance=0, y_limit=0):
for offset in utils.search_3d(distance, y_limit): for offset in utils.search_3d(distance, y_limit):
check = utils.padd(center, offset) check = utils.padd(center, offset)
@ -189,10 +200,9 @@ class MCWorld:
return safe_sand return safe_sand
def check_sand_slice(self, center): def check_sand_slice(self, center):
# checks if a 5x5x1 slice has diggable sand in it # checks if a 5x5x1 slice has sand in it
for i in range(9): for i in range(9):
s = utils.padd(center, utils.spiral(i)) s = utils.padd(center, utils.spiral(i))
if self.block_at(*s) != blocks.SAND: if self.block_at(*s) != blocks.SAND:
continue continue
# make sure it has solid below # make sure it has solid below
@ -206,18 +216,17 @@ class MCWorld:
continue continue
if not self.sand_adjacent_safe(s): if not self.sand_adjacent_safe(s):
continue continue
return True return True
return False return False
def find_sand_slice(self, center, distance, bad_slices=[]): def find_sand_slice(self, center, distance, y_limit=0, bad_slices=[], prev_layer=0):
# returns the centre coord of the next 5x5x1 slice that still has # returns the centre coord of the next 5x5x1 slice that still has
# diggable sand in it. lower slices are only valid if there's an # diggable sand in it. lower slices are only valid if there's an
# adjacent slice farther at the same level. this should ensure an # adjacent slice farther at the same level. this should ensure an
# upside down pyramid gets excavated so the edges are still climbable # upside down pyramid gets excavated so the edges are still climbable
for v in count(): for v in count(prev_layer):
peak = utils.padd(center, (0, 20-v, 0)) peak = utils.padd(center, (0, 10-v, 0))
slices = [] slices = []
layer = 0 layer = 0
@ -228,14 +237,16 @@ class MCWorld:
check = utils.padd(peak, offset) check = utils.padd(peak, offset)
check = utils.padd(check, (0, layer, 0)) check = utils.padd(check, (0, layer, 0))
if utils.phyp(center, check) >= distance: if y_limit and check[1] - center[1] > y_limit:
break
if utils.phyp_king(center, check) > distance:
break break
if self.check_sand_slice(check) and check not in bad_slices: if self.check_sand_slice(check) and check not in bad_slices:
slices.append(check) slices.append(check)
if len(slices): if len(slices):
return slices[-1] return v, slices[-1]
elif v > 40: elif v > 40:
return None, None return None, None
@ -262,6 +273,31 @@ class MCWorld:
for a in self.find_blocks_3d(center, blocks.LEAF_IDS, distance, 10): for a in self.find_blocks_3d(center, blocks.LEAF_IDS, distance, 10):
yield a yield a
def find_monsters(self, center, distance):
# finds monsters within distance
result = []
for eid, mob in self.g.mobs.items():
if mob.type not in mobs.EVIL_IDS:
continue
pos = utils.pint((mob.x, mob.y, mob.z))
if utils.phyp(center, pos) > distance:
continue
result.append(mob)
return result
def find_threats(self, center, distance):
# finds monsters on the surface within distance
monsters = self.find_monsters(center, distance)
result = []
for mob in monsters:
pos = utils.pint((mob.x, mob.y, mob.z))
# check distance number of blocks above, close enough?
if not self.check_air_column(pos, distance):
continue
result.append(mob)
return result
class Game: class Game:
def __init__(self, global_state): def __init__(self, global_state):
@ -306,7 +342,7 @@ class Game:
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 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')
@ -350,6 +386,11 @@ class Game:
self.g.command_lock = False self.g.command_lock = False
return return
if text == 'You are now AFK.':
self.g.afk = True
elif text == 'You are no longer AFK.':
self.g.afk = False
match1 = re.match(r'<(\w+)> (.*)', text) match1 = re.match(r'<(\w+)> (.*)', text)
match2 = re.match(r'\[(\w+) -> me] (.*)', text) match2 = re.match(r'\[(\w+) -> me] (.*)', text)
if match1: if match1:
@ -360,6 +401,9 @@ class Game:
else: else:
return return
if text.startswith('zzz'):
text = '!zzz'
if text.startswith('! '): if text.startswith('! '):
text = text[2:] text = text[2:]
elif text.startswith('!'): elif text.startswith('!'):
@ -374,153 +418,205 @@ class Game:
command = text command = text
data = None data = None
if command == 'ping': try:
reply = 'pong' if command == 'ping':
reply = 'pong'
if command == 'echo' and data: if command == 'echo' and data:
reply = data reply = data
if command == 'respawn': if command == 'respawn':
packet = serverbound.play.ClientStatusPacket() packet = serverbound.play.ClientStatusPacket()
packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN
self.g.connection.write_packet(packet) self.g.connection.write_packet(packet)
reply = 'ok'
if command == 'pos':
reply = str(utils.pint(self.g.pos))[1:-1]
if command == 'afk':
reply = '/afk'
if command == 'error':
reply = 'ok'
raise
if command == 'break':
self.break_block(blocks.TEST_BLOCK)
reply = 'ok'
if command == 'gather' and data:
if data == 'wood':
self.g.job.state = self.g.job.gather_wood
reply = 'ok'
elif data == 'sand':
self.g.job.state = self.g.job.gather_sand
reply = 'ok' reply = 'ok'
if reply: if command == 'pos':
reply = str(utils.pint(self.g.pos))[1:-1]
if command == 'afk':
reply = '/afk'
if command == 'error':
reply = 'ok'
raise
if command == 'break':
self.break_block(blocks.TEST_BLOCK)
reply = 'ok'
if command == 'gather' and data:
if data == 'wood':
self.g.job.state = self.g.job.gather_wood
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 == 'farm' and data:
if data == 'wood':
self.g.job.state = self.g.job.farm_wood
reply = 'ok'
elif data == 'sand':
self.g.job.state = self.g.job.farm_sand
reply = 'ok'
if reply:
for i in self.g.inv.values():
if i.item_id in items.BED_IDS:
break
else:
reply += ', I need a bed'
if command == 'stop':
self.g.job.stop()
self.g.path = []
self.g.look_at = None
reply = 'ok'
if command == 'inv':
inv_list = []
for i in self.g.inv.values(): for i in self.g.inv.values():
print(i.item_id) if i.present:
if i.item_id in items.BED_IDS: inv_list.append('{}:{} x {}'.format(items.ITEM_NAMES[i.item_id], str(i.item_id), i.item_count))
break inv_list.sort()
result = '\n'.join(inv_list)
print(result or 'Empty')
if command == 'drop':
self.drop_stack()
if command == 'time':
reply = str(self.g.time)
if command == 'select' and data:
item = int(data)
if self.select_item([item]):
reply = 'ok'
else: else:
reply += ', I need a bed' reply = 'not found'
if command == 'farm' and data: if command == 'dump' and data:
if data == 'wood': item = int(data)
self.g.job.state = self.g.job.farm_wood if self.count_items([item]):
reply = 'ok' self.g.dumping = item
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: else:
reply += ', I need a bed' reply = 'not found'
if command == 'stop': if command == 'count' and data:
self.g.job.state = self.g.job.stop item = int(data)
self.g.path = [] reply = str(self.count_items([item]))
reply = 'ok'
if command == 'inv': if command == 'open':
inv_list = [] self.open_container(blocks.TEST_BLOCK)
for i in self.g.inv.values():
if i.present:
inv_list.append('{}:{} x {}'.format(items.ITEM_NAMES[i.item_id], str(i.item_id), i.item_count))
inv_list.sort()
result = '\n'.join(inv_list)
print(result or 'Empty')
if command == 'drop': if command == 'close':
self.drop_stack() if self.g.window:
self.close_window()
else:
reply = 'nothing open'
if command == 'time': if command == 'click' and data:
reply = str(self.g.time) if self.g.window:
slot, button, mode = [int(x) for x in data.split(' ')]
try:
item = self.g.window.contents[slot]
except KeyError:
item = Slot(present=False, item_id=None, item_count=None, nbt=None)
print(item)
self.click_window(slot, button, mode, item)
else:
reply = 'nothing open'
if command == 'select' and data: if command == 'loaded':
item = int(data) reply = str(self.g.chunks.get_loaded_area())
if self.select_item([item]):
if command == 'gapple':
self.g.job.state = self.g.job.find_gapple
if data:
self.g.job.find_gapple_states.count = int(data)
reply = 'ok' reply = 'ok'
else:
reply = 'not found'
if command == 'dump' and data: if command == 'objects':
item = int(data) if data == 'clear':
if self.count_items([item]): self.g.objects = {}
self.g.dumping = item reply = 'ok'
else:
for k, v in self.g.objects.items():
if data and v.item_id != int(data): continue
print(str(k) + ':', v, items.ITEM_NAMES[v.item_id])
if command == 'mobs':
if data == 'clear':
self.g.mobs = {}
reply = 'ok'
else:
all_mobs = sorted(list(self.g.mobs.items()), key=lambda x: x[1].type)
for k, v in all_mobs:
if data and v.type != int(data): continue
print(str(k) + ':', v, mobs.MOB_NAMES[v.type])
reply = str(len(all_mobs)) + ' mobs'
if command == 'monsters':
monsters = sorted(list(self.g.mobs.items()), key=lambda x: x[1].type)
count = 0
for k, v in monsters:
if v.type not in mobs.EVIL_IDS: continue
if data and v.type != int(data): continue
count += 1
print(str(k) + ':', v, mobs.MOB_NAMES[v.type])
reply = str(count) + ' monsters'
if command == 'threats':
distance = int(data) if data else 20
p = utils.pint(self.g.pos)
threats = self.g.world.find_threats(p, distance)
for t in threats:
print(str(t.entity_id) + ':', t, mobs.MOB_NAMES[t.type])
reply = str(len(threats)) + ' threats'
if command == 'cache':
self.g.job.state = self.g.job.cache_items
self.g.job.cache_items_states.minimum = 0
self.g.job.cache_items_states.silent = True
reply = 'ok' reply = 'ok'
else:
reply = 'not found'
if command == 'count' and data: if command == 'spiral' and data:
item = int(data) for i in range(int(data)):
reply = str(self.count_items([item])) print(utils.spiral(i))
if command == 'open': if command == 'sand_slice':
self.open_container(blocks.TEST_BLOCK)
if command == 'close':
if self.g.window:
self.close_window()
else:
reply = 'nothing open'
if command == 'click' and data:
if self.g.window:
slot, button, mode = [int(x) for x in data.split(' ')]
try:
item = self.g.window.contents[slot]
except KeyError:
item = Slot(present=False, item_id=None, item_count=None, nbt=None)
print(item)
self.click_window(slot, button, mode, item)
else:
reply = 'nothing open'
if command == 'loaded':
reply = str(self.g.chunks.get_loaded_area())
if command == 'gapple':
self.g.job.state = self.g.job.find_gapple
if data:
self.g.job.find_gapple_states.count = int(data)
reply = 'ok'
if command == 'objects':
for k, v in self.g.objects.items():
if data and v.item_id != int(data): continue
print(str(k) + ':', v)
if command == 'cache':
self.g.job.state = self.g.job.cache_items
self.g.job.cache_items_states.minimum = 0
self.g.job.cache_items_states.silent = True
reply = 'ok'
if command == 'spiral' and data:
for i in range(int(data)):
print(utils.spiral(i))
if command == 'sand_slice':
try:
result = self.g.world.find_sand_slice(utils.pint(self.g.pos), 50) result = self.g.world.find_sand_slice(utils.pint(self.g.pos), 50)
reply = str(result) reply = str(result)
except:
import traceback if command == 'zzz':
print(traceback.format_exc()) if not self.g.afk:
reply = 'error' if self.g.path:
travel_time = int(len(self.g.path) * 0.4) + 2
reply = 'give me ' + str(travel_time) + ' secs, moving'
self.g.queue_afk = True
else:
reply = '/afk'
if command == 'print' and sender == 'tanner6':
data = data.replace('^', '.')
reply = str(eval(data))
except BaseException as e:
import traceback
print(traceback.format_exc())
reply = 'Error: {} - {}\n'.format(e.__class__.__name__, e)
pass
if reply: if reply:
print(reply) print(reply)
@ -545,16 +641,23 @@ class Game:
g.item_lock = False g.item_lock = False
def break_block(self, location): def break_block(self, location):
bid = self.g.chunks.get_block_at(*location) p = utils.pint(self.g.pos)
if bid != 0: #if utils.phyp(p, location) > blocks.BREAK_DISTANCE + 1:
packet = PlayerDiggingPacket() # return False
packet.status = 0
packet.location = location
packet.face = 1
self.g.connection.write_packet(packet)
self.g.breaking = location bid = self.g.chunks.get_block_at(*location)
self.g.break_time = time.time() + utils.break_time(bid, self.g.holding) if bid == 0:
return False
packet = PlayerDiggingPacket()
packet.status = 0
packet.location = location
packet.face = 1
self.g.connection.write_packet(packet)
self.g.breaking = location
self.g.break_time = time.time() + utils.break_time(bid, self.g.holding)
return True
def break_finish(self): def break_finish(self):
packet = PlayerDiggingPacket() packet = PlayerDiggingPacket()
@ -568,6 +671,7 @@ class Game:
self.g.breaking = None self.g.breaking = None
def handle_break_animation(self, packet): def handle_break_animation(self, packet):
return
print(packet) print(packet)
def handle_break_ack(self, packet): def handle_break_ack(self, packet):
@ -733,22 +837,28 @@ class Game:
obj.item_count = entry.value.item_count obj.item_count = entry.value.item_count
def handle_spawn_living(self, packet): def handle_spawn_living(self, packet):
return self.g.mobs[packet.entity_id] = Munch(
print(packet) entity_id=packet.entity_id,
entity_uuid=packet.entity_uuid,
type=packet.type,
x=packet.x,
y=packet.y,
z=packet.z,
)
def handle_entity_position(self, packet): def handle_entity_position(self, packet):
obj = self.g.objects.get(packet.entity_id, None) mob = self.g.mobs.get(packet.entity_id, None)
if obj: if mob:
pass mob.x += packet.delta_x / 4096.0
#obj.x += packet.delta_x mob.y += packet.delta_y / 4096.0
#obj.y += packet.delta_y mob.z += packet.delta_z / 4096.0
#obj.z += packet.delta_z
def handle_entity_position_rotation(self, packet): def handle_entity_position_rotation(self, packet):
obj = self.g.objects.get(packet.entity_id, None) mob = self.g.mobs.get(packet.entity_id, None)
if obj: if mob:
print('object rotation found:', packet) mob.x += packet.delta_x / 4096.0
raise mob.y += packet.delta_y / 4096.0
mob.z += packet.delta_z / 4096.0
def handle_entity_velocity(self, packet): def handle_entity_velocity(self, packet):
obj = self.g.objects.get(packet.entity_id, None) obj = self.g.objects.get(packet.entity_id, None)
@ -762,6 +872,16 @@ class Game:
for eid in packet.entity_ids: for eid in packet.entity_ids:
if eid in self.g.objects: if eid in self.g.objects:
del self.g.objects[eid] del self.g.objects[eid]
if eid in self.g.mobs:
del self.g.mobs[eid]
def leave_bed(self):
packet = EntityActionPacket()
packet.entity_id = self.g.eid
packet.action_id = 2
packet.jump_boost = 0
self.g.connection.write_packet(packet)
def tick(self): def tick(self):
if self.g.breaking: if self.g.breaking:
@ -777,6 +897,10 @@ class Game:
else: else:
self.g.dumping = None self.g.dumping = None
if not len(self.g.path): if not self.g.path:
self.g.correction_count = 0 self.g.correction_count = 0
if self.g.queue_afk:
self.g.chat.send('/afk')
self.g.queue_afk = False

311
jobs.py
View File

@ -20,8 +20,8 @@ import items
importlib.reload(items) importlib.reload(items)
import data import data
importlib.reload(data) importlib.reload(data)
import mobs
BREAK_DISTANCE = 5 importlib.reload(mobs)
class FindGappleStates: class FindGappleStates:
@ -310,12 +310,13 @@ class GatherSandStates:
w = self.g.world w = self.g.world
print('using origin', self.origin) print('using origin', self.origin)
s = w.find_sand_slice(self.origin, 50, self.bad_slices) start = time.time()
print('Found slice:', s) self.prev_layer, s = w.find_sand_slice(self.origin, 200, 10, self.bad_slices, self.prev_layer)
print('Found slice:', s, 'in', time.time() - start, 'seconds')
if s: if s:
self.slice = s self.slice = s
#self.bad_slices.append(s) self.bad_slices.append(s)
self.state = self.find_new_sand self.state = self.find_new_sand
else: else:
print('No slices remaining.') print('No slices remaining.')
@ -325,24 +326,23 @@ class GatherSandStates:
print('Finding new sand...') print('Finding new sand...')
w = self.g.world w = self.g.world
p = utils.pint(self.g.pos) p = utils.pint(self.g.pos)
head = utils.padd(p, path.BLOCK_ABOVE)
sand = w.find_sand(self.slice, 2, p) for sand in w.find_sand(self.slice, 2, p):
print('Found sand:', sand) if sand not in self.bad_sand:
print('Found sand:', sand)
if not len(sand): break
else: # for
print('No good sands left, aborting.')
self.state = self.cleanup self.state = self.cleanup
return return
for check in sand: self.sand = sand
if check in self.bad_sand:
continue
self.sand = check
break
if utils.phyp(p, self.sand) > BREAK_DISTANCE: if utils.phyp(head, self.sand) < blocks.BREAK_DISTANCE:
self.state = self.nav_to_sand
else:
self.state = self.dig_sand self.state = self.dig_sand
else:
self.state = self.nav_to_sand
def nav_to_sand(self): def nav_to_sand(self):
w = self.g.world w = self.g.world
@ -358,6 +358,7 @@ class GatherSandStates:
self.g.path = navpath[:-1] self.g.path = navpath[:-1]
self.state = self.going_to_sand self.state = self.going_to_sand
else: else:
print('Cant get to that sand')
self.bad_sand.append(self.sand) self.bad_sand.append(self.sand)
self.state = self.find_new_sand self.state = self.find_new_sand
@ -389,8 +390,10 @@ class GatherSandStates:
self.state = self.idle self.state = self.idle
self.origin = utils.pint(self.g.pos) self.origin = utils.pint(self.g.pos)
self.origin = (2019, 64, 238)
self.slice = None self.slice = None
self.bad_slices = [] self.bad_slices = []
self.prev_layer = 0
self.sand = None self.sand = None
self.bad_sand = [] self.bad_sand = []
self.wait_time = 0 self.wait_time = 0
@ -521,7 +524,7 @@ class SleepWithBedStates:
def going_to_area(self): def going_to_area(self):
if utils.pint(self.g.pos) == self.opening: if utils.pint(self.g.pos) == self.opening:
self.g.look_at = self.area self.g.look_at = utils.padd(self.area, path.BLOCK_BELOW)
self.state = self.place_bed self.state = self.place_bed
def place_bed(self): def place_bed(self):
@ -537,8 +540,16 @@ class SleepWithBedStates:
self.state = self.sleep_bed self.state = self.sleep_bed
def sleep_bed(self): def sleep_bed(self):
if self.g.time < 100: w = self.g.world
print('Woke up') p = utils.pint(self.g.pos)
threats = w.find_threats(p, 30)
if threats:
print('Waking up due to threats')
self.g.game.leave_bed()
self.state = self.break_bed
elif self.g.time < 100:
print('Woke up time')
self.state = self.break_bed self.state = self.break_bed
def break_bed(self): def break_bed(self):
@ -895,7 +906,7 @@ class ClearLeavesStates:
w = self.g.world w = self.g.world
p = utils.pint(self.g.pos) p = utils.pint(self.g.pos)
for l in w.find_leaves(p, BREAK_DISTANCE): for l in w.find_leaves(p, blocks.BREAK_DISTANCE):
self.leaves.append(l) self.leaves.append(l)
self.state = self.break_leaves self.state = self.break_leaves
@ -1006,137 +1017,101 @@ class GrabSaplingStates:
self.state() self.state()
class JobStates: class CheckThreatsStates:
def idle(self): def idle(self):
return None return None
def cache_items(self): def init(self):
s1 = self.cache_items_states self.state = self.find_threats
print('Checking for threats')
if s1.state == s1.idle: def find_threats(self):
s1.state = s1.init w = self.g.world
elif s1.state == s1.done: p = utils.pint(self.g.pos)
self.state = self.idle
s1.run() threats = w.find_threats(p, 40)
def find_gapple(self): if threats:
s1 = self.find_gapple_states print('Found', len(threats), 'threats, fleeing')
self.state = self.find_safety
else:
print('Aborting, no threats')
self.state = self.cleanup
if s1.state == s1.idle: def find_safety(self):
s1.state = s1.init w = self.g.world
elif s1.state == s1.done: p = utils.pint(self.g.pos)
s1.state = s1.tp_to_coord
s1.run() safety = w.find_blocks_indexed(p, [blocks.EMERALD_BLOCK])
def gather_sand(self): if not safety:
s1 = self.gather_sand_states print('No emerald blocks found, aborting')
s2 = self.grab_sand_states self.state = self.cleanup
s3 = self.sleep_with_bed_states
s4 = self.cache_items_states
if s1.state == s1.idle:
s1.state = s1.init
s2.state = s2.init
s3.state = s3.init
s4.state = s4.init
elif s1.state == s1.done:
if s2.state != s2.done:
s2.run()
return
if s3.state != s3.done:
s3.run()
return
if s4.state != s4.done:
s4.run()
return
s1.state = s1.init
s2.state = s2.init
s3.state = s3.init
s4.state = s4.init
return return
s1.run() safety.sort(key=lambda s: utils.phyp(p, s))
print('Found emerald blocks:', safety)
def gather_wood(self): for s in safety:
s1 = self.gather_wood_states s = utils.padd(s, path.BLOCK_ABOVE)
s2 = self.sleep_with_bed_states navpath = w.path_to_place(p, s)
s3 = self.cache_items_states
if s1.state == s1.idle: if navpath:
s1.state = s1.init self.g.path = navpath
s2.state = s2.init self.state = self.going_to_safety
s3.state = s3.init self.safety = s
elif s1.state == s1.done: print('Going to safety', self.safety)
if s2.state != s2.done:
s2.run()
return return
else:
print('Cant get to safety', self.safety)
if s3.state != s3.done: print('Cant get to safety, aborting')
s3.run() self.state = self.cleanup
return
s1.state = s1.init def going_to_safety(self):
s2.state = s2.init if utils.pint(self.g.pos) == self.safety:
s3.state = s3.init print('At safety spot, waiting to be moved')
return self.state = self.wait_for_move
s1.run() def wait_for_move(self):
# wait for the server to move the bot when it's safe
# ie. a piston + daylight sensor
if utils.pint(self.g.pos) != self.safety:
print('Moved, resuming job')
self.state = self.wait
self.wait_time = 3
def farm_wood(self): def wait(self):
self.sleep_with_bed_states.silent = True # wait to land, etc
self.cache_items_states.silent = True if self.wait_time > 0:
self.wait_time -= utils.TICK
else:
self.state = self.cleanup
s1 = self.gather_wood_states def cleanup(self):
s2 = self.plant_tree_states self.g.look_at = None
s3 = self.clear_leaves_states self.state = self.done
s4 = self.grab_sapling_states
s5 = self.sleep_with_bed_states
s6 = self.cache_items_states
if s1.state == s1.idle: def done(self):
s1.state = s1.init # never gets ran, placeholder
s2.state = s2.init return None
s3.state = s3.init
s4.state = s4.init
s5.state = s5.init
s6.state = s6.init
elif s1.state == s1.done:
if s2.state != s2.done:
s2.run()
return
if s3.state != s3.done: def __init__(self, global_state):
s3.run() self.g = global_state
return self.state = self.idle
if s4.state != s4.done: self.safety = None
s4.run() self.wait_time = 0
return
if s5.state != s5.done: def run(self):
s5.run() self.state()
return
if s6.state != s6.done:
s6.run()
return
s1.state = s1.init class JobStates:
s2.state = s2.init def idle(self):
s3.state = s3.init return []
s4.state = s4.init
s5.state = s5.init
s6.state = s6.init
return
s1.run() def init_machines(self):
def stop(self):
self.gather_wood_states = GatherWoodStates(self.g) self.gather_wood_states = GatherWoodStates(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)
@ -1146,22 +1121,84 @@ class JobStates:
self.clear_leaves_states = ClearLeavesStates(self.g) self.clear_leaves_states = ClearLeavesStates(self.g)
self.grab_sapling_states = GrabSaplingStates(self.g) self.grab_sapling_states = GrabSaplingStates(self.g)
self.grab_sand_states = GrabSandStates(self.g) self.grab_sand_states = GrabSandStates(self.g)
self.check_threats_states = CheckThreatsStates(self.g)
def run_machines(self, machines):
for m in machines:
if m.state == m.idle:
continue
if m.state != m.done:
m.run()
return
# if we went through them all
for m in machines:
m.state = m.init
def gather_sand(self):
machines = [
self.gather_sand_states,
self.grab_sand_states,
self.cache_items_states,
self.sleep_with_bed_states,
]
return machines
def farm_sand(self):
machines = [
self.check_threats_states,
self.gather_sand_states,
self.grab_sand_states,
self.cache_items_states,
self.sleep_with_bed_states,
]
self.sleep_with_bed_states.silent = True
self.cache_items_states.silent = True
return machines
def cache_items(self):
machines = [
self.cache_items_states,
]
return machines
def find_gapple(self):
machines = [
self.find_gapple_states,
]
return machines
def gather_wood(self):
machines = [
self.gather_wood_states,
self.sleep_with_bed_states,
self.cache_items_states,
]
return machines
def farm_wood(self):
machines = [
self.gather_wood_states,
self.plant_tree_states,
self.clear_leaves_states,
self.grab_sapling_states,
self.sleep_with_bed_states,
self.cache_items_states,
]
self.sleep_with_bed_states.silent = True
self.cache_items_states.silent = True
return machines
def stop(self):
self.init_machines()
self.state = self.idle self.state = self.idle
def __init__(self, global_state): def __init__(self, global_state):
self.g = global_state self.g = global_state
self.init_machines()
self.state = self.idle self.state = self.idle
self.prev_state = None
self.gather_wood_states = GatherWoodStates(self.g)
self.gather_sand_states = GatherSandStates(self.g)
self.sleep_with_bed_states = SleepWithBedStates(self.g)
self.cache_items_states = CacheItemsStates(self.g)
self.find_gapple_states = FindGappleStates(self.g)
self.plant_tree_states = PlantTreeStates(self.g)
self.clear_leaves_states = ClearLeavesStates(self.g)
self.grab_sapling_states = GrabSaplingStates(self.g)
self.grab_sand_states = GrabSandStates(self.g)
def tick(self): def tick(self):
self.state() self.run_machines(self.state())

View File

@ -20,10 +20,12 @@ g.mcdata = False
g.pos = False g.pos = False
g.inv = {} g.inv = {}
g.objects = {} g.objects = {}
g.mobs = {}
g.window = None g.window = None
g.job = None g.job = None
g.correction_count = 0 g.correction_count = 0
g.holding = 0 g.holding = 0
g.afk = False
@app.route('/') @app.route('/')
def hello_world(): def hello_world():

50
mobs.py Normal file
View File

@ -0,0 +1,50 @@
import json
with open('mcdata/registries.json') as f:
MOBS = json.load(f)['minecraft:entity_type']['entries']
EVIL = [
'blaze',
'cave_spider',
'creeper',
'drowned',
'elder_guardian',
'ender_dragon',
'enderman',
'endermite',
'evoker',
'ghast',
'giant',
'guardian',
'hoglin',
'husk',
'illusioner',
'magma_cube',
'phantom',
'piglin',
'piglin_brute',
'pillager',
'ravager',
'shulker',
'silverfish',
'skeleton',
'skeleton_horse',
'slime',
'spider',
'stray',
'vex',
'vindicator',
'witch',
'wither',
'zoglin',
'zombie',
'zombie_villager',
]
EVIL_IDS = set()
for mob_name in EVIL:
EVIL_IDS.add(MOBS['minecraft:'+mob_name]['protocol_id'])
MOB_NAMES = {}
for mob_name, mob in MOBS.items():
MOB_NAMES[MOBS[mob_name]['protocol_id']] = mob_name.replace('minecraft:', '')

View File

@ -23,6 +23,7 @@ def get_packets(old_get_packets):
mc_packets.add(packets.SpawnLivingEntityPacket) mc_packets.add(packets.SpawnLivingEntityPacket)
mc_packets.add(packets.EntityPositionRotationPacket) mc_packets.add(packets.EntityPositionRotationPacket)
mc_packets.add(packets.DestroyEntitiesPacket) mc_packets.add(packets.DestroyEntitiesPacket)
mc_packets.add(packets.EntityActionPacket)
return mc_packets return mc_packets
return lambda x: wrapper(old_get_packets, x) return lambda x: wrapper(old_get_packets, x)

View File

@ -361,3 +361,16 @@ class DestroyEntitiesPacket(Packet):
for _ in range(self.count): for _ in range(self.count):
eid = VarInt.read(file_object) eid = VarInt.read(file_object)
self.entity_ids.append(eid) self.entity_ids.append(eid)
class EntityActionPacket(Packet):
# Sent by the client when it performs an action
# https://wiki.vg/Protocol#Entity_Action
id = 0x1C
packet_name = 'entity action'
definition = [
{'entity_id': VarInt},
{'action_id': VarInt},
{'jump_boost': VarInt},
]