|
|
|
@ -7,13 +7,85 @@ from itertools import count |
|
|
|
|
|
|
|
|
|
import blocks |
|
|
|
|
|
|
|
|
|
import minecraft |
|
|
|
|
from minecraft import authentication |
|
|
|
|
from minecraft.exceptions import YggdrasilError |
|
|
|
|
from minecraft.networking.connection import Connection |
|
|
|
|
from minecraft.networking.packets import Packet, clientbound, serverbound |
|
|
|
|
from minecraft.networking.types import BlockFace, VarInt, Position, Boolean, Byte |
|
|
|
|
from minecraft.compat import input |
|
|
|
|
from minecraft.managers import ChunksManager |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#class AcknowledgePlayerDiggingPacket(Packet): |
|
|
|
|
# @staticmethod |
|
|
|
|
# def get_id(context): |
|
|
|
|
# return 0x08 |
|
|
|
|
# |
|
|
|
|
# packet_name = 'acknowledge player digging' |
|
|
|
|
# definition = [ |
|
|
|
|
# {'status': VarInt}, |
|
|
|
|
# {'location': Position}, |
|
|
|
|
# {'face': VarInt}, |
|
|
|
|
# {'successful': Boolean}, |
|
|
|
|
# ] |
|
|
|
|
# |
|
|
|
|
#class BlockBreakAnimationPacket(Packet): |
|
|
|
|
# @staticmethod |
|
|
|
|
# def get_id(context): |
|
|
|
|
# return 0x09 |
|
|
|
|
# |
|
|
|
|
# packet_name = 'block break animation' |
|
|
|
|
# definition = [ |
|
|
|
|
# {'entity_id': VarInt}, |
|
|
|
|
# {'location': Position}, |
|
|
|
|
# {'destroy_stage': Byte}, |
|
|
|
|
# ] |
|
|
|
|
# |
|
|
|
|
#def get_packets(old_get_packets): |
|
|
|
|
# def wrapper(func, context): |
|
|
|
|
# packets = func(context) |
|
|
|
|
# packets.add(AcknowledgePlayerDiggingPacket) |
|
|
|
|
# packets.add(BlockBreakAnimationPacket) |
|
|
|
|
# print(packets) |
|
|
|
|
# return packets |
|
|
|
|
# return lambda x: wrapper(old_get_packets, x) |
|
|
|
|
# |
|
|
|
|
#minecraft.networking.packets.clientbound.play.get_packets = get_packets(minecraft.networking.packets.clientbound.play.get_packets) |
|
|
|
|
# |
|
|
|
|
#def qot(x): |
|
|
|
|
# print('qot.') |
|
|
|
|
# return set() |
|
|
|
|
# |
|
|
|
|
#minecraft.networking.packets.clientbound.play.get_packets = qot |
|
|
|
|
|
|
|
|
|
class PlayerDiggingPacket(Packet): |
|
|
|
|
# used when player mines / breaks blocks |
|
|
|
|
# https://wiki.vg/Protocol#Player_Digging |
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
|
def get_id(context): |
|
|
|
|
return 0x1A |
|
|
|
|
|
|
|
|
|
packet_name = 'player digging' |
|
|
|
|
|
|
|
|
|
definition = [ |
|
|
|
|
{'status': VarInt}, |
|
|
|
|
{'location': Position}, |
|
|
|
|
{'face': VarInt}, |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
STARTED = 0 |
|
|
|
|
CANCELLED = 1 |
|
|
|
|
FINISHED = 2 |
|
|
|
|
|
|
|
|
|
# PlayerBlockPlacementPacket.Face is an alias for BlockFace. |
|
|
|
|
Face = BlockFace |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AStarTimeout(Exception): |
|
|
|
|
pass |
|
|
|
|
|
|
|
|
@ -142,6 +214,9 @@ HYPOT_LUT = { |
|
|
|
|
def padd(p1, p2): |
|
|
|
|
return (p1[0] + p2[0], p1[1] + p2[1], p1[2] + p2[2]) |
|
|
|
|
|
|
|
|
|
def psub(p1, p2): |
|
|
|
|
return (p1[0] - p2[0], p1[1] - p2[1], p1[2] - p2[2]) |
|
|
|
|
|
|
|
|
|
def pint(p): |
|
|
|
|
return (int(p[0]), int(p[1]), int(p[2])) |
|
|
|
|
|
|
|
|
@ -347,6 +422,28 @@ def alternate(n, amount): |
|
|
|
|
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_block(connection, coords, time): |
|
|
|
|
packet = PlayerDiggingPacket() |
|
|
|
|
packet.status = 0 |
|
|
|
|
packet.location = coords |
|
|
|
|
packet.face = 1 |
|
|
|
|
connection.write_packet(packet) |
|
|
|
|
|
|
|
|
|
s['break_finished_packet'] = PlayerDiggingPacket() |
|
|
|
|
s['break_finished_packet'].status = 2 |
|
|
|
|
s['break_finished_packet'].location = coords |
|
|
|
|
s['break_finished_packet'].face = 1 |
|
|
|
|
|
|
|
|
|
s['break_time'] = time |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BLOCK_ABOVE = (0, +1, 0) |
|
|
|
|
BLOCK_BELOW = (0, -1, 0) |
|
|
|
|
|
|
|
|
@ -391,11 +488,13 @@ class MCWorld: |
|
|
|
|
|
|
|
|
|
for log in logs: |
|
|
|
|
# crawl to the top log |
|
|
|
|
log_count = 1 |
|
|
|
|
while self.block_at(*padd(log, BLOCK_ABOVE)) in blocks.LOG_IDS: |
|
|
|
|
log = padd(log, BLOCK_ABOVE) |
|
|
|
|
log_count += 1 |
|
|
|
|
|
|
|
|
|
# make sure it's a tree |
|
|
|
|
if self.block_at(*padd(log, BLOCK_ABOVE)) in blocks.LEAF_IDS: |
|
|
|
|
# make sure it's a good tree |
|
|
|
|
if self.block_at(*padd(log, BLOCK_ABOVE)) in blocks.LEAF_IDS and log_count > 2: |
|
|
|
|
break |
|
|
|
|
else: # for |
|
|
|
|
return None |
|
|
|
@ -420,14 +519,155 @@ class MCWorld: |
|
|
|
|
result.append(padd(tree, offset)) |
|
|
|
|
return result |
|
|
|
|
|
|
|
|
|
def navigate_to_opening(self, start, opening): |
|
|
|
|
def path_to_opening(self, start, opening): |
|
|
|
|
maze_solver = MazeSolver(self.chunks) |
|
|
|
|
|
|
|
|
|
try: |
|
|
|
|
return list(maze_solver.astar(start, opening)) |
|
|
|
|
s = maze_solver.astar(start, opening) |
|
|
|
|
return list(s) if s else None |
|
|
|
|
except AStarTimeout: |
|
|
|
|
return None |
|
|
|
|
|
|
|
|
|
def path_to_base(self, start, base): |
|
|
|
|
maze_solver = MazeSolver(self.chunks) |
|
|
|
|
|
|
|
|
|
try: |
|
|
|
|
s = maze_solver.astar(start, base) |
|
|
|
|
return list(s) if s else None |
|
|
|
|
except AStarTimeout: |
|
|
|
|
return None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LumberjackStates: |
|
|
|
|
def bair(self, p): |
|
|
|
|
return self.player_info.chunks.get_block_at(*p) in blocks.NON_SOLID_IDS |
|
|
|
|
|
|
|
|
|
def blog(self, p): |
|
|
|
|
return self.player_info.chunks.get_block_at(*p) in blocks.LOG_IDS |
|
|
|
|
|
|
|
|
|
def idle(self): |
|
|
|
|
return None |
|
|
|
|
|
|
|
|
|
def find_new_tree(self): |
|
|
|
|
print('Finding new tree...') |
|
|
|
|
w = MCWorld(self.player_info.chunks) |
|
|
|
|
p = pint(self.player_info.pos) |
|
|
|
|
|
|
|
|
|
self.tree = w.find_tree(p, 100) |
|
|
|
|
print('Found tree at:', self.tree) |
|
|
|
|
|
|
|
|
|
openings = w.find_tree_openings(self.tree) |
|
|
|
|
|
|
|
|
|
for o in openings: |
|
|
|
|
path = w.path_to_opening(p, o) |
|
|
|
|
self.opening = o |
|
|
|
|
if path: break |
|
|
|
|
else: # for |
|
|
|
|
print('Unable to get to tree') |
|
|
|
|
self.state = self.finished |
|
|
|
|
|
|
|
|
|
s['path'] = path |
|
|
|
|
self.state = self.going_to_tree |
|
|
|
|
|
|
|
|
|
def going_to_tree(self): |
|
|
|
|
d = self.player_info.pos - LPoint3f(*self.opening) |
|
|
|
|
if d.length() < 1: |
|
|
|
|
s['look_at'] = LPoint3f(*self.tree) |
|
|
|
|
self.state = self.clear_leaves |
|
|
|
|
|
|
|
|
|
def clear_leaves(self): |
|
|
|
|
if not s['break_finished_packet']: |
|
|
|
|
p = pint(self.player_info.pos) |
|
|
|
|
diff = psub(self.tree, p) |
|
|
|
|
|
|
|
|
|
for x in diffrange(diff[0]): |
|
|
|
|
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 not self.bair(check): |
|
|
|
|
s['break'] = (check, 0.5) |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
def clear_trunk_base(self): |
|
|
|
|
if not s['break_finished_packet']: |
|
|
|
|
base = self.tree |
|
|
|
|
above = padd(self.tree, BLOCK_ABOVE) |
|
|
|
|
|
|
|
|
|
if self.blog(base): |
|
|
|
|
s['break'] = (base, 2) |
|
|
|
|
return |
|
|
|
|
elif self.blog(above): |
|
|
|
|
s['break'] = (above, 2) |
|
|
|
|
return |
|
|
|
|
else: |
|
|
|
|
w = MCWorld(self.player_info.chunks) |
|
|
|
|
p = pint(self.player_info.pos) |
|
|
|
|
path = w.path_to_base(p, self.tree) |
|
|
|
|
|
|
|
|
|
s['path'] = path |
|
|
|
|
self.state = self.going_to_trunk_base |
|
|
|
|
|
|
|
|
|
def going_to_trunk_base(self): |
|
|
|
|
d = self.player_info.pos - LPoint3f(*self.opening) |
|
|
|
|
if d.length() < 1: |
|
|
|
|
s['pitch'] = -90 |
|
|
|
|
self.state = self.clear_trunk |
|
|
|
|
|
|
|
|
|
def clear_trunk(self): |
|
|
|
|
if not s['break_finished_packet']: |
|
|
|
|
check = self.tree |
|
|
|
|
|
|
|
|
|
count = 0 |
|
|
|
|
while self.bair(check) and count < 6: |
|
|
|
|
check = padd(check, BLOCK_ABOVE) |
|
|
|
|
count += 1 |
|
|
|
|
|
|
|
|
|
if self.blog(check): |
|
|
|
|
s['break'] = (check, 2) |
|
|
|
|
else: |
|
|
|
|
print('Finished clearing tree') |
|
|
|
|
self.state = self.finished |
|
|
|
|
|
|
|
|
|
def finished(self): |
|
|
|
|
s['pitch'] = 0 # todo, calc with look_at |
|
|
|
|
s['look_at'] = None |
|
|
|
|
return None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, player_info): |
|
|
|
|
self.player_info = player_info |
|
|
|
|
self.state = self.idle |
|
|
|
|
|
|
|
|
|
self.tree = None |
|
|
|
|
self.opening = None |
|
|
|
|
|
|
|
|
|
def run(self): |
|
|
|
|
self.state() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class JobStates: |
|
|
|
|
def idle(self): |
|
|
|
|
return None |
|
|
|
|
|
|
|
|
|
def night_shelter(self): |
|
|
|
|
return None |
|
|
|
|
|
|
|
|
|
def lumberjack(self): |
|
|
|
|
l = self.lumberjack_states |
|
|
|
|
if l.state == l.idle: |
|
|
|
|
l.state = l.find_new_tree |
|
|
|
|
l.run() |
|
|
|
|
|
|
|
|
|
def __init__(self, player_info): |
|
|
|
|
self.player_info = player_info |
|
|
|
|
self.state = self.idle |
|
|
|
|
self.lumberjack_states = LumberjackStates(player_info) |
|
|
|
|
|
|
|
|
|
def run(self): |
|
|
|
|
self.state() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TICK = 0.05 |
|
|
|
@ -454,6 +694,8 @@ def cap(x, amount): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def tick(connection, player_info): |
|
|
|
|
s['jobstate'].run() |
|
|
|
|
|
|
|
|
|
target = None |
|
|
|
|
|
|
|
|
|
p = player_info.pos |
|
|
|
@ -494,12 +736,15 @@ def tick(connection, player_info): |
|
|
|
|
else: |
|
|
|
|
s['y_a'] = -36.0 |
|
|
|
|
|
|
|
|
|
look_at = None |
|
|
|
|
|
|
|
|
|
if len(s['path']) > YAW_LOOK_AHEAD: |
|
|
|
|
if s['look_at']: |
|
|
|
|
look_at = LPoint3f(s['look_at']) |
|
|
|
|
elif len(s['path']) > YAW_LOOK_AHEAD: |
|
|
|
|
look_at = LPoint3f(s['path'][YAW_LOOK_AHEAD]) |
|
|
|
|
elif len(s['path']): |
|
|
|
|
look_at = LPoint3f(s['path'][-1]) |
|
|
|
|
else: |
|
|
|
|
look_at = None |
|
|
|
|
|
|
|
|
|
if look_at: |
|
|
|
|
look_at.x += 0.5 |
|
|
|
@ -517,15 +762,40 @@ def tick(connection, player_info): |
|
|
|
|
packet = serverbound.play.PositionAndLookPacket(x=p.x, feet_y=p.y, z=p.z, pitch=s['pitch'], yaw=s['yaw'], on_ground=True) |
|
|
|
|
connection.write_packet(packet, force=True) |
|
|
|
|
|
|
|
|
|
if s['break']: |
|
|
|
|
break_block(connection, *s['break']) |
|
|
|
|
s['break'] = None |
|
|
|
|
|
|
|
|
|
if s['break_time'] > 0: |
|
|
|
|
packet = serverbound.play.AnimationPacket() |
|
|
|
|
packet.hand = packet.HAND_MAIN |
|
|
|
|
connection.write_packet(packet) |
|
|
|
|
|
|
|
|
|
s['break_time'] -= TICK |
|
|
|
|
elif s['break_finished_packet']: |
|
|
|
|
connection.write_packet(s['break_finished_packet']) |
|
|
|
|
s['break_finished_packet'] = None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def init(connection, player_info): |
|
|
|
|
p = player_info.pos |
|
|
|
|
|
|
|
|
|
s['path'] = [] |
|
|
|
|
s['look_at'] = None |
|
|
|
|
s['y_v'] = 0 |
|
|
|
|
s['y_a'] = 0 |
|
|
|
|
s['yaw'] = 360 |
|
|
|
|
s['pitch'] = 0 |
|
|
|
|
|
|
|
|
|
s['break'] = None |
|
|
|
|
s['break_time'] = 0 |
|
|
|
|
s['break_finished_packet'] = None |
|
|
|
|
|
|
|
|
|
s['jobstate'] = JobStates(player_info) |
|
|
|
|
s['jobstate'].run() |
|
|
|
|
|
|
|
|
|
def main(connection, player_info): |
|
|
|
|
def handle_join_game(join_game_packet): |
|
|
|
|
print('Connected.') |
|
|
|
@ -556,7 +826,7 @@ def main(connection, player_info): |
|
|
|
|
solution = MazeSolver(player_info.chunks).astar(pint(player_info.pos), pint(s['goal'])) |
|
|
|
|
if solution: |
|
|
|
|
solution = list(solution) |
|
|
|
|
#s['path'] = solution |
|
|
|
|
s['path'] = solution |
|
|
|
|
print(len(solution)) |
|
|
|
|
print(round(time.time() - start, 3), 'seconds') |
|
|
|
|
else: |
|
|
|
@ -573,6 +843,18 @@ def main(connection, player_info): |
|
|
|
|
connection.register_packet_listener( |
|
|
|
|
x, clientbound.play.BlockChangePacket) |
|
|
|
|
|
|
|
|
|
#def y(p): |
|
|
|
|
# print(p) |
|
|
|
|
|
|
|
|
|
#connection.register_packet_listener( |
|
|
|
|
# y, AcknowledgePlayerDiggingPacket) |
|
|
|
|
|
|
|
|
|
#def z(p): |
|
|
|
|
# print(p) |
|
|
|
|
|
|
|
|
|
#connection.register_packet_listener( |
|
|
|
|
# z, BlockBreakAnimationPacket) |
|
|
|
|
|
|
|
|
|
def print_chat(chat_packet): |
|
|
|
|
print("Message (%s): %s" % ( |
|
|
|
|
chat_packet.field_string('position'), chat_packet.json_data)) |
|
|
|
@ -603,7 +885,7 @@ def main(connection, player_info): |
|
|
|
|
start = time.time() |
|
|
|
|
solution = MazeSolver(player_info.chunks).astar(pint(player_info.pos), pint(s['goal'])) |
|
|
|
|
solution = list(solution) |
|
|
|
|
#s['path'] = solution |
|
|
|
|
s['path'] = solution |
|
|
|
|
print(len(solution)) |
|
|
|
|
print(round(time.time() - start, 3), 'seconds') |
|
|
|
|
except BaseException as e: |
|
|
|
@ -623,6 +905,31 @@ def main(connection, player_info): |
|
|
|
|
except BaseException as e: |
|
|
|
|
import traceback |
|
|
|
|
print(traceback.format_exc()) |
|
|
|
|
elif '!break' in chat_packet.json_data: |
|
|
|
|
try: |
|
|
|
|
coords = pint(player_info.pos) |
|
|
|
|
coords = padd(coords, CHECK_NORTH) |
|
|
|
|
|
|
|
|
|
break_block(connection, coords, 2.5) |
|
|
|
|
#break_block(connection, coords, 0.35) |
|
|
|
|
except BaseException as e: |
|
|
|
|
import traceback |
|
|
|
|
print(traceback.format_exc()) |
|
|
|
|
elif '!echo' in chat_packet.json_data: |
|
|
|
|
try: |
|
|
|
|
parts = chat_packet.json_data.split('\'') |
|
|
|
|
packet = serverbound.play.ChatPacket() |
|
|
|
|
packet.message = parts[1] |
|
|
|
|
connection.write_packet(packet) |
|
|
|
|
except BaseException as e: |
|
|
|
|
import traceback |
|
|
|
|
print(traceback.format_exc()) |
|
|
|
|
elif 'get wood' in chat_packet.json_data: |
|
|
|
|
print('setting job state to lumberjack') |
|
|
|
|
s['jobstate'].state = s['jobstate'].lumberjack |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
connection.register_packet_listener( |
|
|
|
|