minecraft-bot/mosfet/game.py

552 lines
19 KiB
Python
Raw Normal View History

2020-09-16 06:02:36 +00:00
import re
import time
import random
2020-09-16 20:09:14 +00:00
from math import hypot
2020-09-17 01:12:01 +00:00
from itertools import count
from munch import Munch
2020-09-16 06:02:36 +00:00
from minecraft.networking.packets import Packet, clientbound, serverbound
2020-09-18 00:56:52 +00:00
from minecraft.networking.types import BlockFace
2020-09-16 06:02:36 +00:00
2021-04-22 00:46:54 +00:00
from mosfet.protocol.packets import (
2020-10-15 07:37:47 +00:00
SetSlotPacket, PlayerDiggingPacket,
2020-09-23 21:36:18 +00:00
BlockBreakAnimationPacket, AcknowledgePlayerDiggingPacket,
HeldItemChangePacket, PickItemPacket, OpenWindowPacket,
ClickWindowPacket, CloseWindowPacket, ServerWindowConfirmationPacket,
ClientWindowConfirmationPacket, EntityMetadataPacket,
2020-10-15 07:37:47 +00:00
SpawnLivingEntityPacket, EntityPositionRotationPacket, DestroyEntitiesPacket,
2021-02-24 01:02:00 +00:00
EntityActionPacket, EntityTeleport, InteractEntityPacket, TradeListPacket,
SelectTradePacket, DisconnectPacket,
2020-09-23 21:36:18 +00:00
)
2021-04-22 00:46:54 +00:00
from mosfet.protocol.types import Slot
2020-09-16 06:02:36 +00:00
2021-04-22 00:46:54 +00:00
from mosfet import utils
from mosfet import path
from mosfet import bot
from mosfet import vector
from mosfet.info import blocks
from mosfet.info import items
from mosfet.info import mcdata
from mosfet.info import mobs
2020-09-16 06:02:36 +00:00
class Game:
def __init__(self, global_state):
self.g = global_state
register = self.g.connection.register_packet_listener
register(self.handle_login_success, clientbound.login.LoginSuccessPacket)
2020-09-16 06:02:36 +00:00
register(self.handle_block_change, clientbound.play.BlockChangePacket)
register(self.handle_join_game, clientbound.play.JoinGamePacket)
register(self.handle_position_and_look, clientbound.play.PlayerPositionAndLookPacket)
2020-10-15 07:37:47 +00:00
register(self.handle_time_update, clientbound.play.TimeUpdatePacket)
2020-09-16 06:02:36 +00:00
register(self.handle_set_slot, SetSlotPacket)
register(self.handle_break_animation, BlockBreakAnimationPacket)
register(self.handle_break_ack, AcknowledgePlayerDiggingPacket)
register(self.handle_window, OpenWindowPacket)
2020-09-21 05:41:55 +00:00
register(self.handle_window_confirmation, ClientWindowConfirmationPacket)
2020-09-23 06:00:28 +00:00
register(self.handle_spawn_object, clientbound.play.SpawnObjectPacket)
register(self.handle_entity_metadata, EntityMetadataPacket)
2020-09-23 21:36:18 +00:00
register(self.handle_spawn_living, SpawnLivingEntityPacket)
2020-10-15 07:37:47 +00:00
register(self.handle_entity_position, clientbound.play.EntityPositionDeltaPacket)
2020-09-23 21:36:18 +00:00
register(self.handle_entity_position_rotation, EntityPositionRotationPacket)
register(self.handle_destroy_entities, DestroyEntitiesPacket)
2020-12-14 04:27:06 +00:00
register(self.handle_spawn_player, clientbound.play.SpawnPlayerPacket)
register(self.handle_respawn, clientbound.play.RespawnPacket)
2020-12-13 11:38:30 +00:00
register(self.handle_player_list, clientbound.play.PlayerListItemPacket)
2020-12-14 04:27:06 +00:00
register(self.handle_entity_teleport, EntityTeleport)
2021-01-05 05:57:06 +00:00
register(self.handle_update_health, clientbound.play.UpdateHealthPacket)
2020-10-15 07:37:47 +00:00
#register(self.handle_entity_velocity, clientbound.play.EntityVelocityPacket)
register(self.handle_trade_list, TradeListPacket)
register(self.handle_disconnect, DisconnectPacket)
2020-09-23 06:00:28 +00:00
#register(self.handle_packet, Packet, early=True)
2020-09-16 06:02:36 +00:00
def handle_login_success(self, packet):
print(packet)
self.g.name = packet.Username
2020-09-16 06:02:36 +00:00
def handle_join_game(self, packet):
2021-04-23 00:05:56 +00:00
print('Received join game packet')
2020-09-16 06:02:36 +00:00
print('Connected.')
self.g.info = packet
self.g.eid = packet.entity_id
self.g.dimension = packet.world_name.replace('minecraft:', '')
2020-09-16 06:02:36 +00:00
def handle_block_change(self, packet):
if packet.block_state_id == blocks.SOUL_TORCH:
2020-09-16 06:02:36 +00:00
try:
2021-04-22 00:46:54 +00:00
self.g.goal = vector.Point3D((packet.location[0], packet.location[1], packet.location[2]))
print('new waypoint:', self.g.goal)
2020-09-16 06:02:36 +00:00
start = time.time()
solution = path.Pathfinder(self.g).astar(utils.pint(self.g.pos), utils.pint(self.g.goal))
2020-09-16 06:02:36 +00:00
if solution:
solution = list(solution)
self.g.path = solution
2020-12-12 21:11:00 +00:00
if self.g.job:
self.g.job.stop()
2020-09-16 06:02:36 +00:00
print(len(solution))
print(solution)
print(round(time.time() - start, 3), 'seconds')
else:
print('No path found')
#say(connection, 'No path found')
#g.y_v = 10.0
#g.y_a = -36.0
2020-09-16 06:02:36 +00:00
except BaseException as e:
import traceback
print(traceback.format_exc())
#print(packet)
2020-09-16 06:02:36 +00:00
def handle_position_and_look(self, packet):
print(packet)
2021-04-22 00:46:54 +00:00
p = vector.Point3D((packet.x, packet.y, packet.z))
2020-09-16 06:02:36 +00:00
self.g.pos = p
2020-09-25 21:51:36 +00:00
confirm_packet = serverbound.play.TeleportConfirmPacket()
confirm_packet.teleport_id = packet.teleport_id
self.g.connection.write_packet(confirm_packet)
2020-10-19 21:49:14 +00:00
self.g.correction_count += 1
if self.g.get('path', None) and self.g.correction_count > 5:
self.g.correction_count = 0
dest = self.g.path[-1]
w = self.g.world
p = utils.pint(self.g.pos)
new_path = w.path_to_place(p, dest)
if new_path:
self.g.path = new_path
2020-09-25 21:51:36 +00:00
2020-09-16 06:02:36 +00:00
def handle_time_update(self, packet):
self.g.time = packet.time_of_day % 24000
2020-09-16 06:02:36 +00:00
def handle_set_slot(self, packet):
g = self.g
2020-09-16 06:02:36 +00:00
print(packet)
if packet.window_id == 0:
g.inv[packet.slot] = packet.slot_data
elif g.window:
g.window.contents[packet.slot] = packet.slot_data
2020-09-16 06:02:36 +00:00
2021-04-23 00:05:56 +00:00
if g.item_lock and packet.window_id >= 0 and not packet.slot_data.present:
print('Unlocking item lock')
g.item_lock = False
2020-09-16 06:02:36 +00:00
def break_block(self, location):
2020-12-03 03:30:54 +00:00
p = utils.pint(self.g.pos)
2020-12-04 02:49:22 +00:00
#if utils.phyp(p, location) > blocks.BREAK_DISTANCE + 1:
# return False
2020-12-03 03:30:54 +00:00
bid = self.g.chunks.get_block_at(*location)
2020-12-03 03:30:54 +00:00
if bid == 0:
return False
packet = PlayerDiggingPacket()
packet.status = 0
packet.location = location
packet.face = 1
self.g.connection.write_packet(packet)
2020-09-16 06:02:36 +00:00
2020-12-03 03:30:54 +00:00
self.g.breaking = location
self.g.break_time = time.time() + utils.break_time(bid, self.g.holding)
return True
def break_finish(self):
packet = PlayerDiggingPacket()
packet.status = 2
packet.location = self.g.breaking
packet.face = 1
self.g.connection.write_packet(packet)
2020-12-01 00:48:02 +00:00
#self.g.chunks.set_block_at(*self.g.breaking, 0)
if self.g.chunks.get_block_at(*self.g.breaking) == 0:
self.g.breaking = None
def handle_break_animation(self, packet):
2020-12-04 02:49:22 +00:00
return
print(packet)
def handle_break_ack(self, packet):
2020-09-17 01:12:01 +00:00
#print(packet)
return
2020-09-16 06:02:36 +00:00
def respawn(self):
packet = serverbound.play.ClientStatusPacket()
packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN
self.g.connection.write_packet(packet)
2020-09-16 06:02:36 +00:00
def animate(self):
packet = serverbound.play.AnimationPacket()
packet.hand = packet.HAND_MAIN
self.g.connection.write_packet(packet)
2020-09-16 20:09:14 +00:00
def place_block(self, location, face):
packet = serverbound.play.PlayerBlockPlacementPacket()
packet.hand = 0
2020-09-17 02:11:42 +00:00
packet.location = location
2020-09-16 20:09:14 +00:00
packet.face = face
packet.x = 0.5
packet.y = 0.5
packet.z = 0.5
packet.inside_block = False
self.g.connection.write_packet(packet)
2020-09-17 02:11:42 +00:00
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)
2020-10-18 05:35:43 +00:00
def count_items(self, items):
# count how many items are in inv
count = 0
for slot, item in self.g.inv.items():
if item.item_id in items:
2020-10-18 05:35:43 +00:00
count += item.item_count
return count
def count_inventory_slots(self):
# count how many inventory slots are filled
# excludes armour, crafting slots, off-hand
count = 0
for slot, item in self.g.inv.items():
if item.present and slot >= 9 and slot <= 45:
count += 1
return count
def count_window_slots(self):
# count how many window slots are filled
# excludes player inventory
w = self.g.window
w_info = mcdata.WINDOWS[w.data.window_type]
w_container_slots = w_info.container
count = 0
for slot, item in w.contents.items():
if item.present and slot in w_container_slots:
count += 1
return count
2021-02-24 01:02:00 +00:00
def get_window_slot(self, item_id):
# get the first slot that matches item of a window
window_items = list(self.g.window.contents.items())
for slot, item in window_items:
if not item.present: continue
if item.item_id == item_id:
return slot, item
else: #for
return False, False
def select_item(self, items):
# select the first match from items of inv
2020-10-17 18:41:41 +00:00
# uses smallest stack of that match
2021-02-22 21:31:36 +00:00
# and optionally the most damaged item
2020-10-17 18:41:41 +00:00
inv_items = list(self.g.inv.items())
2021-02-22 21:31:36 +00:00
inv_items.sort(key=lambda x: (x[1].nbt or {}).get('Damage', 0), reverse=True)
2020-10-17 18:41:41 +00:00
inv_items.sort(key=lambda x: x[1].item_count or 0)
for slot, item in inv_items:
if item.item_id in items:
self.g.game.choose_slot(slot)
2020-12-02 11:12:51 +00:00
self.g.holding = item.item_id
return True
else: #for
return False
def select_random_item(self, items):
# select a random match from items of inv
# this is random per item type
# example: 5 stacks wood, 1 stack glass
# -> still 50/50 chance between them
matches = set()
for slot, item in self.g.inv.items():
if item.item_id in items:
matches.add(item.item_id)
if matches:
return self.select_item([random.choice(list(matches))])
else:
return False
2021-02-15 08:56:41 +00:00
def select_next_item(self):
# select the next item slot that has an item
for slot, item in self.g.inv.items():
2021-02-22 01:44:55 +00:00
if slot < 9: continue # skip armour slots
2021-02-15 08:56:41 +00:00
if item.present:
2021-02-22 01:44:55 +00:00
print('slot:', slot, 'item:', item)
2021-02-15 08:56:41 +00:00
self.g.game.choose_slot(slot)
self.g.holding = item.item_id
return True
else: # for
return False
def drop_stack(self):
packet = PlayerDiggingPacket()
packet.status = 3
packet.location = utils.pint(self.g.pos)
packet.face = 1
self.g.connection.write_packet(packet)
def open_container(self, location):
bid = self.g.chunks.get_block_at(*location)
# TODO: check if block is a chest??
self.place_block(location, BlockFace.TOP)
def handle_window(self, packet):
print(packet)
self.g.window = Munch(data=packet, contents=dict(), count=0)
2020-09-21 05:41:55 +00:00
def click_window(self, slot, button, mode, item):
w = self.g.window
packet = ClickWindowPacket()
2020-09-21 05:41:55 +00:00
packet.window_id = w.data.window_id
packet.slot = slot
packet.button = button
2020-09-21 05:41:55 +00:00
packet.action_number = w.count
packet.mode = mode
packet.clicked_item = item
self.g.connection.write_packet(packet)
2020-09-21 05:41:55 +00:00
print('<--', packet)
w.count += 1
def close_window(self):
2021-02-23 07:50:40 +00:00
if self.g.window:
packet = CloseWindowPacket()
packet.window_id = self.g.window.data.window_id
self.g.connection.write_packet(packet)
self.g.window = None
2020-09-21 05:41:55 +00:00
def handle_window_confirmation(self, packet):
print(packet)
packet2 = ServerWindowConfirmationPacket()
packet2.window_id = packet.window_id
packet2.action_number = packet.action_number
packet2.accepted = packet.accepted
self.g.connection.write_packet(packet2)
def handle_spawn_player(self, packet):
print(packet)
self.g.players[packet.entity_id] = Munch(
entity_id=packet.entity_id,
2020-12-14 04:27:06 +00:00
player_uuid=packet.player_UUID,
x=packet.x,
y=packet.y,
z=packet.z,
yaw=packet.yaw,
pitch=packet.pitch,
)
2020-09-23 06:00:28 +00:00
def handle_spawn_object(self, packet):
#return
2020-09-24 22:02:15 +00:00
if packet.type_id != 37: return
2020-10-16 08:56:11 +00:00
#print(packet)
self.g.objects[packet.entity_id] = Munch(
2020-10-15 07:37:47 +00:00
entity_id=packet.entity_id,
x=packet.x,
y=packet.y,
z=packet.z,
velocity_x=packet.velocity_x,
velocity_y=packet.velocity_y,
velocity_z=packet.velocity_z,
)
def check_gapple(self, packet):
current_gapple_chest = self.g.job.find_gapple_states.current_chest
if current_gapple_chest:
for entry in packet.metadata:
if entry.type != 6:
continue
if entry.value.item_id in items.GAPPLE_ID:
self.g.chat.send('gapple found: ' + str(current_gapple_chest)[1:-1])
print('gapple found:', str(current_gapple_chest)[1:-1])
def handle_entity_metadata(self, packet):
if not packet.metadata:
return
if self.g.job and self.g.job.state == self.g.job.find_gapple_states:
self.check_gapple(packet)
obj = self.g.objects.get(packet.entity_id, None)
if obj:
for entry in packet.metadata:
if entry.type != 6:
continue
obj.item_id = entry.value.item_id
obj.item_count = entry.value.item_count
2020-09-23 21:36:18 +00:00
player = self.g.players.get(packet.entity_id, None)
if player:
return
2020-09-23 21:36:18 +00:00
def handle_spawn_living(self, packet):
2020-12-04 02:49:22 +00:00
self.g.mobs[packet.entity_id] = Munch(
entity_id=packet.entity_id,
entity_uuid=packet.entity_uuid,
type=packet.type,
x=packet.x,
y=packet.y,
z=packet.z,
)
2020-09-23 06:00:28 +00:00
2020-09-23 21:36:18 +00:00
def handle_entity_position(self, packet):
2020-12-04 02:49:22 +00:00
mob = self.g.mobs.get(packet.entity_id, None)
if mob:
mob.x += packet.delta_x / 4096.0
mob.y += packet.delta_y / 4096.0
mob.z += packet.delta_z / 4096.0
2020-09-23 21:36:18 +00:00
2020-12-13 11:38:30 +00:00
player = self.g.players.get(packet.entity_id, None)
if player:
player.x += packet.delta_x / 4096.0
player.y += packet.delta_y / 4096.0
player.z += packet.delta_z / 4096.0
2020-12-14 05:40:17 +00:00
#if player.player_uuid == '0c123cfa-1697-4427-9413-4b645dee7ec0': print(packet)
2020-12-13 11:38:30 +00:00
2020-09-23 21:36:18 +00:00
def handle_entity_position_rotation(self, packet):
2020-12-04 02:49:22 +00:00
mob = self.g.mobs.get(packet.entity_id, None)
if mob:
mob.x += packet.delta_x / 4096.0
mob.y += packet.delta_y / 4096.0
mob.z += packet.delta_z / 4096.0
2020-12-13 11:38:30 +00:00
player = self.g.players.get(packet.entity_id, None)
if player:
player.x += packet.delta_x / 4096.0
player.y += packet.delta_y / 4096.0
player.z += packet.delta_z / 4096.0
2020-12-14 05:40:17 +00:00
#if player.player_uuid == '0c123cfa-1697-4427-9413-4b645dee7ec0': print(packet)
2020-12-13 11:38:30 +00:00
2020-12-14 04:27:06 +00:00
def handle_entity_teleport(self, packet):
mob = self.g.mobs.get(packet.entity_id, None)
if mob:
mob.x = packet.x
mob.y = packet.y
mob.z = packet.z
player = self.g.players.get(packet.entity_id, None)
if player:
player.x = packet.x
player.y = packet.y
player.z = packet.z
2020-12-14 05:40:17 +00:00
#if player.player_uuid == '0c123cfa-1697-4427-9413-4b645dee7ec0': print(packet)
2020-12-14 04:27:06 +00:00
def handle_entity_velocity(self, packet):
obj = self.g.objects.get(packet.entity_id, None)
if obj:
print(packet)
#obj.velocity_x = packet.velocity_x
#obj.velocity_y = packet.velocity_y
#obj.velocity_z = packet.velocity_z
def handle_destroy_entities(self, packet):
for eid in packet.entity_ids:
if eid in self.g.objects:
del self.g.objects[eid]
2020-12-04 02:49:22 +00:00
if eid in self.g.mobs:
del self.g.mobs[eid]
2020-12-14 04:27:06 +00:00
if eid in self.g.players:
del self.g.players[eid]
2020-12-04 02:49:22 +00:00
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 handle_respawn(self, packet):
print(packet)
self.g.dimension = packet.world_name.replace('minecraft:', '')
2020-12-13 11:38:30 +00:00
def handle_player_list(self, packet):
for action in packet.actions:
if isinstance(action, packet.AddPlayerAction):
self.g.player_names[action.uuid] = action.name
self.g.player_names[action.name] = action.uuid # porque no los dos?
2020-09-23 06:00:28 +00:00
2021-01-05 05:57:06 +00:00
def handle_update_health(self, packet):
print(packet)
self.g.health = packet.health
self.g.food = packet.food
2021-04-23 00:05:56 +00:00
if packet.health == 0:
print('Died, stopping')
print('Use 1respawn to respawn the bot')
self.close_window()
bot.init(self.g)
2021-01-05 05:57:06 +00:00
def use_item(self, hand):
packet = serverbound.play.UseItemPacket()
packet.hand = hand
self.g.connection.write_packet(packet)
def interact(self, eid):
packet = InteractEntityPacket()
packet.entity_id = eid
packet.type = 0
packet.hand = 0
packet.sneaking = False
self.g.connection.write_packet(packet)
def handle_trade_list(self, packet):
print(packet)
2021-02-23 07:50:40 +00:00
self.g.trades = packet.trades
2021-02-24 01:02:00 +00:00
def select_trade(self, num):
packet = SelectTradePacket()
packet.selected_slot = num
self.g.connection.write_packet(packet)
def handle_disconnect(self, packet):
print(packet)
print('Client disconnected!')
import os
os._exit(1)
2020-09-16 06:02:36 +00:00
def tick(self):
if self.g.breaking:
2020-09-16 06:02:36 +00:00
self.animate()
2020-10-18 05:35:43 +00:00
if time.time() >= self.g.break_time: #- 2*utils.TICK:
self.break_finish()
if self.g.dumping and not self.g.item_lock:
if self.select_item([self.g.dumping]):
self.drop_stack()
self.g.item_lock = True
else:
self.g.dumping = None
2021-02-15 08:56:41 +00:00
if self.g.draining and not self.g.item_lock:
if self.select_next_item():
self.drop_stack()
self.g.item_lock = True
else:
self.g.draining = False
2020-12-04 02:49:22 +00:00
if not self.g.path:
2020-10-19 21:49:14 +00:00
self.g.correction_count = 0