Add inventory controls and enhance dashboard
This commit is contained in:
+133
-2
@@ -8,7 +8,7 @@ from minecraft.networking.packets import Packet, clientbound, serverbound
|
||||
from minecraft.networking.types import BlockFace, Byte, Short, VarInt
|
||||
from minecraft.networking.modern import (
|
||||
ClientTickEnd, ContainerClickSwapPacket, PlayerActionPacket,
|
||||
SetCarriedItemPacket, UseItemOnBlockPacket,
|
||||
SetCarriedItemPacket, SetHeldSlotPacket, UseItemOnBlockPacket,
|
||||
)
|
||||
|
||||
from mosfet.protocol.packets import (
|
||||
@@ -45,7 +45,7 @@ class ContainerClickDropPacket(Packet):
|
||||
VarInt.send(self.container_id, packet_buffer)
|
||||
VarInt.send(self.state_id, packet_buffer)
|
||||
Short.send(self.slot, packet_buffer)
|
||||
Byte.send(1, packet_buffer) # drop the whole stack
|
||||
Byte.send(getattr(self, 'button', 1), packet_buffer)
|
||||
VarInt.send(4, packet_buffer) # ContainerInput.THROW
|
||||
VarInt.send(0, packet_buffer) # no changed-slot predictions
|
||||
VarInt.send(0, packet_buffer) # empty carried item stack
|
||||
@@ -166,6 +166,16 @@ class LegacyGame:
|
||||
if new_path:
|
||||
self.g.path = new_path
|
||||
|
||||
def handle_set_held_slot(self, packet):
|
||||
slot = int(packet.slot)
|
||||
if not 0 <= slot <= 8:
|
||||
return
|
||||
self.g.holding_slot = slot
|
||||
self.g.holding_hand = 0
|
||||
item = self.g.inv.get(36 + slot)
|
||||
self.g.holding = (item.item_id if item and
|
||||
getattr(item, 'present', False) else 0)
|
||||
|
||||
def handle_time_update(self, packet):
|
||||
if hasattr(packet, 'time_of_day'):
|
||||
self.g.time = packet.time_of_day % 24000
|
||||
@@ -665,6 +675,8 @@ class Game(LegacyGame):
|
||||
self.handle_set_health, SetHealthPacket)
|
||||
self.g.connection.register_packet_listener(
|
||||
self.handle_time_update, UpdateTimePacket)
|
||||
self.g.connection.register_packet_listener(
|
||||
self.handle_set_held_slot, SetHeldSlotPacket)
|
||||
|
||||
if not self.g.get('health_update_received', False):
|
||||
self.g.health = None
|
||||
@@ -717,6 +729,118 @@ class Game(LegacyGame):
|
||||
ClientCommandPacket(action_id=0), force=True)
|
||||
self.g.connection.write_packet(ClientTickEnd(), force=True)
|
||||
|
||||
def _send_drop_click(self, slot, button=1):
|
||||
state_id = self.g.inventory_state_ids.get(0, 0)
|
||||
self.g.connection.write_packet(ContainerClickDropPacket(
|
||||
container_id=0, state_id=state_id, slot=slot,
|
||||
button=button), force=True)
|
||||
self.g.connection.write_packet(ClientTickEnd(), force=True)
|
||||
return state_id
|
||||
|
||||
def drop_inventory_slot(self, slot, whole_stack=True):
|
||||
"""Drop one item or the full stack from a player inventory slot."""
|
||||
try:
|
||||
slot = int(slot)
|
||||
except (TypeError, ValueError):
|
||||
return False
|
||||
if (not 5 <= slot <= 45 or self.g.get('window') is not None or
|
||||
self.g.get('draining') or self.g.get('dumping') is not None or
|
||||
self.g.get('drop_waiting')):
|
||||
return False
|
||||
item = self.g.inv.get(slot)
|
||||
if not item or not getattr(item, 'present', False):
|
||||
return False
|
||||
count = int(item.item_count or 0)
|
||||
if count <= 0:
|
||||
return False
|
||||
button = 1 if whole_stack else 0
|
||||
state_id = self._send_drop_click(slot, button)
|
||||
self.g.drop_waiting = {
|
||||
'slot': slot, 'item_id': item.item_id,
|
||||
'expected_count': 0 if whole_stack else count - 1,
|
||||
'sent_at': time.monotonic(), 'state_id': state_id,
|
||||
}
|
||||
return True
|
||||
|
||||
def drop_stack(self):
|
||||
"""Drop the full stack in the currently held hand (26.3 click)."""
|
||||
slot = (45 if self.g.get('holding_hand', 0) == 1 else
|
||||
36 + int(self.g.get('holding_slot', 0) or 0))
|
||||
return self.drop_inventory_slot(slot, whole_stack=True)
|
||||
|
||||
def _tick_single_drop(self):
|
||||
waiting = self.g.get('drop_waiting')
|
||||
if not waiting:
|
||||
return
|
||||
item = self.g.inv.get(waiting['slot'])
|
||||
remaining = (item.item_count if item and
|
||||
getattr(item, 'present', False) and
|
||||
item.item_id == waiting['item_id'] else 0)
|
||||
if remaining <= waiting['expected_count']:
|
||||
print('[drop] server confirmed slot {} updated'.format(
|
||||
waiting['slot']), flush=True)
|
||||
self.g.drop_waiting = None
|
||||
elif time.monotonic() - waiting['sent_at'] > 3.0:
|
||||
print('[drop] timed out waiting for inventory update on slot {}'.format(
|
||||
waiting['slot']), flush=True)
|
||||
self.g.drop_waiting = None
|
||||
|
||||
def start_dump(self, item_id):
|
||||
"""Start dropping every stack of an item id from player inventory."""
|
||||
if (self.g.get('window') is not None or self.g.get('draining') or
|
||||
self.g.get('dumping') is not None or self.g.get('drop_waiting')):
|
||||
return None
|
||||
slots = [slot for slot, item in self.g.inv.items()
|
||||
if 5 <= slot <= 45 and getattr(item, 'present', False)
|
||||
and item.item_id == item_id]
|
||||
if not slots:
|
||||
return 0
|
||||
self.g.dumping = item_id
|
||||
self.g.dump_waiting = None
|
||||
self.g.dump_dropped = 0
|
||||
self.g.dump_total = len(slots)
|
||||
self.g.dump_error = None
|
||||
return len(slots)
|
||||
|
||||
def _tick_dump(self):
|
||||
item_id = self.g.get('dumping')
|
||||
if item_id is None:
|
||||
return
|
||||
waiting = self.g.get('dump_waiting')
|
||||
if waiting:
|
||||
item = self.g.inv.get(waiting['slot'])
|
||||
if (not item or not getattr(item, 'present', False) or
|
||||
item.item_id != item_id):
|
||||
self.g.dump_dropped += 1
|
||||
print('[dump] dropped stack from slot {}; {}/{} complete'.format(
|
||||
waiting['slot'], self.g.dump_dropped,
|
||||
self.g.dump_total), flush=True)
|
||||
self.g.dump_waiting = None
|
||||
elif time.monotonic() - waiting['sent_at'] > 3.0:
|
||||
self.g.dump_error = 'server did not clear inventory slot {}'.format(
|
||||
waiting['slot'])
|
||||
print('[dump] stopped: ' + self.g.dump_error, flush=True)
|
||||
self.g.dumping = None
|
||||
self.g.dump_waiting = None
|
||||
return
|
||||
else:
|
||||
return
|
||||
|
||||
for slot in range(5, 46):
|
||||
item = self.g.inv.get(slot)
|
||||
if item and getattr(item, 'present', False) and item.item_id == item_id:
|
||||
state_id = self._send_drop_click(slot)
|
||||
self.g.dump_waiting = {
|
||||
'slot': slot, 'item_count': item.item_count,
|
||||
'sent_at': time.monotonic(), 'state_id': state_id,
|
||||
}
|
||||
return
|
||||
|
||||
print('[dump] complete: dropped {} stacks of item {}'.format(
|
||||
self.g.dump_dropped, item_id), flush=True)
|
||||
self.g.dumping = None
|
||||
self.g.dump_waiting = None
|
||||
|
||||
def leave_bed(self):
|
||||
self._send_action(PlayerCommandPacket(
|
||||
entity_id=self.g.eid, action_id=0, jump_boost=0))
|
||||
@@ -728,6 +852,10 @@ class Game(LegacyGame):
|
||||
pitch=float(self.g.get('pitch', 0.0))))
|
||||
|
||||
def start_drain(self):
|
||||
if (self.g.get('window') is not None or
|
||||
self.g.get('dumping') is not None or
|
||||
self.g.get('drop_waiting')):
|
||||
return None
|
||||
slots = [slot for slot, item in sorted(self.g.inv.items())
|
||||
if 9 <= slot <= 45 and getattr(item, 'present', False)]
|
||||
self.g.drain_slots = slots
|
||||
@@ -787,6 +915,9 @@ class Game(LegacyGame):
|
||||
self.g.drain_dropped), flush=True)
|
||||
|
||||
def tick(self):
|
||||
self._tick_single_drop()
|
||||
if self.g.get('dumping') is not None:
|
||||
self._tick_dump()
|
||||
if self.g.get('draining'):
|
||||
self._tick_drain()
|
||||
if not self.g.get('breaking') or self.break_deadline is None:
|
||||
|
||||
Reference in New Issue
Block a user