Improve labeled chest caching interactions

This commit is contained in:
Tanner
2026-11-07 18:23:55 +00:00
parent 34d902c46d
commit 688297e968
7 changed files with 240 additions and 25 deletions
+29 -11
View File
@@ -26,7 +26,8 @@ from mosfet.protocol.packets import (
from mosfet.protocol.types import Slot
from mosfet.protocol.modern import (
ClientCommandPacket, PlayerCommandPacket, UpdateTimePacket, UseItemPacket,
ClientCommandPacket, PlayerCommandPacket, PlayerInputPacket,
UpdateTimePacket, UseItemPacket,
)
from mosfet import utils
@@ -386,14 +387,18 @@ class LegacyGame:
def open_container(self, location):
location = tuple(int(value) for value in location)
bid = self.g.chunks.get_block_at(*location)
block_name = blocks.BLOCKS.get(bid)
if (block_name and block_name != 'ender_chest' and
(block_name == 'chest' or block_name.endswith('_chest'))):
self.g.pending_chest_open = {
'position': location,
'requested_at': time.monotonic(),
}
positions = {location}
world = self.g.get('world')
if world and hasattr(world, '_double_chest_positions'):
positions.update(world._double_chest_positions(location))
for offset in ((1, 0, 0), (-1, 0, 0), (0, 0, 1),
(0, 0, -1), (0, 1, 0), (0, -1, 0)):
positions.add(utils.padd(location, offset))
self.g.pending_chest_open = {
'position': location,
'positions': tuple(sorted(positions)),
'requested_at': time.monotonic(),
}
self.place_block(location, BlockFace.TOP)
def handle_window(self, packet):
@@ -1042,7 +1047,8 @@ class Game(LegacyGame):
self.g.connection.write_packet(ClientTickEnd(), force=True)
return True
def place_block(self, location, face, hand=0, cursor_y=None):
def place_block(self, location, face, hand=0, cursor_y=None,
sneaking=False):
location = tuple(int(value) for value in location)
if cursor_y is None:
cursor_y = 0.99 if int(face) == 1 else 0.5
@@ -1051,7 +1057,18 @@ class Game(LegacyGame):
cursor_x=0.5, cursor_y=cursor_y,
cursor_z=0.5, inside_block=False, world_border_hit=False,
sequence=self._next_sequence())
self._send_action(packet)
if sneaking:
self.g.connection.write_packet(PlayerInputPacket(flags=0x20),
force=True)
self.g.connection.write_packet(ClientTickEnd(), force=True)
try:
self._send_action(packet)
finally:
self.g.connection.write_packet(PlayerInputPacket(flags=0),
force=True)
self.g.connection.write_packet(ClientTickEnd(), force=True)
else:
self._send_action(packet)
def count_window_slots(self):
window = self.g.get('window')
@@ -1083,3 +1100,4 @@ class Game(LegacyGame):
self.g.connection.write_packet(ContainerClosePacket(
container_id=window.window_id), force=True)
self.g.window = None
self.g.pending_chest_open = None
+37 -2
View File
@@ -22,6 +22,9 @@ class CacheItemsStates:
def init(self):
self.skip_slots = []
self.skip_items = []
self.failed_transfer_slots = set()
self.pending_transfer = None
self.transferred_to_labeled_chest = False
self.is_labeled_container = False
self.container_item_ids = None
@@ -80,6 +83,10 @@ class CacheItemsStates:
if not len(self.trapped_chests):
self.is_labeled_container = False
self.container_item_ids = None
if self.transferred_to_labeled_chest:
print('Already cached items in labeled chests; skipping new chest')
self.state = self.cleanup
return
print('No labeled or trapped chests')
self.state = self.select_chest
return
@@ -166,11 +173,13 @@ class CacheItemsStates:
def place_chest(self):
support = utils.padd(self.area, path.BLOCK_BELOW)
self.g.game.place_block(support, BlockFace.TOP)
self.g.game.place_block(support, BlockFace.TOP, sneaking=True)
self.state = self.open_chest
def open_chest(self):
print('Opening chest')
self.failed_transfer_slots.clear()
self.pending_transfer = None
self.g.game.open_container(self.area)
self.wait_time = 1
self.state = self.wait
@@ -236,6 +245,21 @@ class CacheItemsStates:
self.state = self.cleanup
return
if self.pending_transfer:
pending = self.pending_transfer
if time.monotonic() < pending['confirm_after']:
return
source = w.contents.get(pending['window_slot'])
moved = (source is None or not source.present or
source.item_id != pending['item_id'] or
source.item_count < pending['item_count'])
if moved:
if pending['is_labeled']:
self.transferred_to_labeled_chest = True
else:
self.failed_transfer_slots.add(pending['window_slot'])
self.pending_transfer = None
w_container_slots = range(w.container_slots)
w_inventory_slots = range(w.container_slots, len(w.contents))
@@ -272,7 +296,8 @@ class CacheItemsStates:
elif slot.item_id in items.NEEDED_ITEMS:
continue
if slot_num in self.skip_slots:
if (slot_num in self.skip_slots or
slot_num in self.failed_transfer_slots):
continue
slot_list.append((slot_num, slot))
@@ -288,6 +313,13 @@ class CacheItemsStates:
print('moving', slot)
self.pending_transfer = {
'window_slot': slot_num,
'item_id': slot.item_id,
'item_count': slot.item_count,
'is_labeled': self.is_labeled_container,
'confirm_after': time.monotonic() + 0.15,
}
self.g.item_lock = True
self.g.game.click_window(slot_num, 0, 1, slot)
return
@@ -325,6 +357,9 @@ class CacheItemsStates:
self.silent = False
self.skip_slots = []
self.failed_transfer_slots = set()
self.pending_transfer = None
self.transferred_to_labeled_chest = False
self.skip_items = []
self.labeled_chests = []
+20 -5
View File
@@ -365,6 +365,7 @@ class ChestOpenManager:
self.g.chest_viewers = self.g.get('chest_viewers') or {}
self.g.chest_open_events = self.g.get('chest_open_events') or []
self.g.last_chest_opened_by_other = None
self._own_open_suppressions = {}
self.g.connection.register_packet_listener(
self.handle_block_event, BlockEventPacket)
@@ -396,17 +397,31 @@ class ChestOpenManager:
else:
self.g.chest_viewers.pop(position, None)
now = time.monotonic()
self._own_open_suppressions = {
pos: expires for pos, expires in self._own_open_suppressions.items()
if expires > now
}
pending = self.g.get('pending_chest_open')
pending_age = (time.monotonic() - pending.get('requested_at', 0)
pending_age = (now - pending.get('requested_at', 0)
if pending is not None else None)
pending_is_fresh = (pending_age is not None and
0 <= pending_age <= self.OWN_OPEN_TIMEOUT)
is_bot_opening = (
pending_is_fresh and
tuple(pending.get('position', ())) == position
pending_positions = (
pending.get('positions') or (pending.get('position', ()),)
if pending is not None else ()
)
if pending is not None and (is_bot_opening or not pending_is_fresh):
pending_positions = {tuple(pos) for pos in pending_positions}
is_bot_opening = (
pending_is_fresh and position in pending_positions
)
if pending is not None and not pending_is_fresh:
self.g.pending_chest_open = None
if is_bot_opening:
for own_position in pending_positions:
self._own_open_suppressions[own_position] = now + 0.5
is_bot_opening = (is_bot_opening or
position in self._own_open_suppressions)
if (viewers <= previous_viewers or
(is_bot_opening and
+10 -1
View File
@@ -4,7 +4,9 @@ import time
from contextlib import nullcontext
from minecraft.networking.packets import Packet
from minecraft.networking.types import Float, Long, VarInt, VarLong
from minecraft.networking.types import (
Float, Long, UnsignedByte, VarInt, VarLong,
)
from minecraft.networking.modern import (
ChatCommand, ClientboundPlayerChatPacket, ServerboundChatPacket, SystemChat,
)
@@ -29,6 +31,13 @@ class UpdateTimePacket(Packet):
})
class PlayerInputPacket(Packet):
"""Serverbound key state, including sneak for container-safe placement."""
id = 0x2B
packet_name = 'player input'
definition = [{'flags': UnsignedByte}]
class PlayerCommandPacket(Packet):
"""Client player-command packet; action 0 stops sleeping (protocol 777)."""
id = 0x2A