From b72c335ff78b2b301341d84c71fc881963202256 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sat, 20 Feb 2021 23:04:48 +0000 Subject: [PATCH] Add packets for entity interaction and trade list --- game.py | 27 +++++++++++++++++++++++++- monkey_patch.py | 1 + protocol/packets.py | 47 ++++++++++++++++++++++++++++++++++++++++++++- protocol/types.py | 39 +++++++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 2 deletions(-) diff --git a/game.py b/game.py index 5ecc8a6..b73b1f9 100644 --- a/game.py +++ b/game.py @@ -20,7 +20,7 @@ from protocol.packets import ( ClickWindowPacket, CloseWindowPacket, ServerWindowConfirmationPacket, ClientWindowConfirmationPacket, EntityMetadataPacket, SpawnLivingEntityPacket, EntityPositionRotationPacket, DestroyEntitiesPacket, - EntityActionPacket, EntityTeleport, + EntityActionPacket, EntityTeleport, InteractEntityPacket, TradeListPacket ) from protocol.types import Slot @@ -338,6 +338,7 @@ class Game: register(self.handle_entity_teleport, EntityTeleport) register(self.handle_update_health, clientbound.play.UpdateHealthPacket) #register(self.handle_entity_velocity, clientbound.play.EntityVelocityPacket) + register(self.handle_trade_list, TradeListPacket) #register(self.handle_packet, Packet, early=True) @@ -535,6 +536,16 @@ class Game: print(str(k) + ':', v, mobs.MOB_NAMES[v.type]) reply = str(count) + ' monsters' + if command == 'villagers': + all_mobs = sorted(list(self.g.mobs.items()), key=lambda x: x[1].type) + count = 0 + for k, v in all_mobs: + type_name = mobs.MOB_NAMES[v.type] + if type_name != 'villager' : continue + count += 1 + print(str(k) + ':', v, type_name) + reply = str(count) + ' villagers' + if command == 'threats': distance = int(data) if data else 20 p = utils.pint(self.g.pos) @@ -757,6 +768,9 @@ class Game: if command == 'use': self.use_item(0) + if command == 'interact' and data: + self.interact(int(data)) + if command == 'test': reply = 'ok' self.select_next_item() @@ -1115,6 +1129,17 @@ class Game: 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) + def tick(self): if self.g.breaking: self.animate() diff --git a/monkey_patch.py b/monkey_patch.py index 6bb7aa9..1388658 100644 --- a/monkey_patch.py +++ b/monkey_patch.py @@ -18,6 +18,7 @@ def get_packets(old_get_packets): mc_packets.add(packets.EntityPositionRotationPacket) mc_packets.add(packets.DestroyEntitiesPacket) mc_packets.add(packets.EntityTeleport) + mc_packets.add(packets.TradeListPacket) return mc_packets diff --git a/protocol/packets.py b/protocol/packets.py index 3c60920..edbd472 100644 --- a/protocol/packets.py +++ b/protocol/packets.py @@ -8,7 +8,7 @@ from minecraft.networking.types import ( Float, Direction, PositionAndLook ) -from protocol.types import Nbt, Slot, Entry +from protocol.types import Nbt, Slot, Entry, Trade import blocks @@ -391,3 +391,48 @@ class EntityActionPacket(Packet): {'action_id': VarInt}, {'jump_boost': VarInt}, ] + +class InteractEntityPacket(Packet): + # Sent when the client attacks or right-clicks another entity + # https://wiki.vg/Protocol#Interact_Entity + + id = 0x0E + packet_name = 'interact entity' + + definition = [ + {'entity_id': VarInt}, + {'type': VarInt}, + #{'target_x': Float}, + #{'target_y': Float}, + #{'target_z': Float}, + {'hand': VarInt}, + {'sneaking': Boolean}, + ] + +class TradeListPacket(Packet): + # The list of trades a villager NPC is offering. + # https://wiki.vg/Protocol#Trade_List + + id = 0x26 + packet_name = 'trade list' + fields = ( + 'window_id', + 'size', + 'trades', + 'villager_level', + 'experience', + 'is_regular_villager', + 'can_restock', + ) + + def read(self, file_object): + self.window_id = VarInt.read(file_object) + self.size = Byte.read(file_object) + self.trades = [] + for _ in range(self.size): + trade = Trade.read(file_object) + self.trades.append(trade) + self.villager_level = VarInt.read(file_object) + self.experience = VarInt.read(file_object) + self.is_regular_villager = Boolean.read(file_object) + self.can_restock = Boolean.read(file_object) diff --git a/protocol/types.py b/protocol/types.py index ea1744a..967c2b0 100644 --- a/protocol/types.py +++ b/protocol/types.py @@ -172,3 +172,42 @@ class Entry(Type): except KeyError: return None return Entry(index, type, value) + + +class Trade(Type): + fields = ( + 'input_item_1', + 'output_item', + 'has_second_item', + 'input_item_2', + 'trade_disabled', + 'num_uses', + 'max_num_uses', + 'xp', + 'special_price', + 'price_multiplier', + 'demand', + ) + + def __str__(self): + return str(self.__dict__) + def __repr__(self): + inner_str = ', '.join('%s=%s' % (a, getattr(self, a, None)) for a in self.fields if hasattr(self, a)) + return 'Trade(%s)' % inner_str + + @staticmethod + def read(file_object): + trade = Trade() + trade.input_item_1 = Slot.read(file_object) + trade.output_item = Slot.read(file_object) + trade.has_second_item = Boolean.read(file_object) + if trade.has_second_item: + trade.input_item_2 = Slot.read(file_object) + trade.trade_disabled = Boolean.read(file_object) + trade.num_uses = Integer.read(file_object) + trade.max_num_uses = Integer.read(file_object) + trade.xp = Integer.read(file_object) + trade.special_price = Integer.read(file_object) + trade.price_multiplier = Float.read(file_object) + trade.demand = Integer.read(file_object) + return trade