Track reopened supply chests and fix API snapshots
This commit is contained in:
+4
-1
@@ -87,12 +87,15 @@ def main(global_state=None, *, server=None, port=None, username=None,
|
||||
from mosfet.world import World
|
||||
from mosfet.commands import Commands
|
||||
from mosfet.protocol.modern import ModernChatManager
|
||||
from mosfet.protocol.managers import EntityManager, InventoryManager
|
||||
from mosfet.protocol.managers import (
|
||||
ChestOpenManager, EntityManager, InventoryManager,
|
||||
)
|
||||
from mosfet.bot import start_automation
|
||||
global_state.world = World(global_state)
|
||||
global_state.chat = ModernChatManager(global_state)
|
||||
global_state.entities = EntityManager(global_state)
|
||||
global_state.inventory = InventoryManager(global_state)
|
||||
global_state.chest_open_manager = ChestOpenManager(global_state)
|
||||
global_state.commands = Commands(global_state)
|
||||
start_automation(global_state)
|
||||
|
||||
|
||||
+8
-1
@@ -385,8 +385,15 @@ class LegacyGame:
|
||||
self.g.connection.write_packet(packet)
|
||||
|
||||
def open_container(self, location):
|
||||
location = tuple(int(value) for value in location)
|
||||
bid = self.g.chunks.get_block_at(*location)
|
||||
# TODO: check if block is a chest??
|
||||
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(),
|
||||
}
|
||||
self.place_block(location, BlockFace.TOP)
|
||||
|
||||
def handle_window(self, packet):
|
||||
|
||||
@@ -204,6 +204,28 @@ class GrabSuppliesStates:
|
||||
self.ground_position = None
|
||||
self.state = self.find_ground_items
|
||||
|
||||
@staticmethod
|
||||
def _chest_event_key(event):
|
||||
return (event.get('timestamp'), tuple(event.get('position', ())))
|
||||
|
||||
def _refresh_useless_chests(self):
|
||||
events = self.g.get('chest_open_events', ())
|
||||
current_events = {self._chest_event_key(event) for event in events}
|
||||
for event in events:
|
||||
event_key = self._chest_event_key(event)
|
||||
if event_key in self._processed_chest_open_events:
|
||||
continue
|
||||
self._processed_chest_open_events.add(event_key)
|
||||
position = tuple(event.get('position', ()))
|
||||
reopened = [key for key in self.useless_chests
|
||||
if position in key]
|
||||
for key in reopened:
|
||||
self.useless_chests.remove(key)
|
||||
self.checked_supply_chests.discard(key)
|
||||
print('Supply chest was opened by another player; checking it again:',
|
||||
position)
|
||||
self._processed_chest_open_events.intersection_update(current_events)
|
||||
|
||||
def find_supply_chests(self):
|
||||
print('Finding chests labeled for this bot\'s supplies...')
|
||||
p = utils.pint(self.g.pos)
|
||||
@@ -215,10 +237,11 @@ class GrabSuppliesStates:
|
||||
def choose_supply_chest(self):
|
||||
print('Choosing a supply-labeled chest...')
|
||||
for container in self.supply_chests:
|
||||
key = container['positions']
|
||||
if key in self.checked_supply_chests:
|
||||
key = tuple(container['positions'])
|
||||
if key in self.useless_chests or key in self.checked_supply_chests:
|
||||
continue
|
||||
self.checked_supply_chests.add(key)
|
||||
self.current_supply_chest_key = key
|
||||
self.chest = container['position']
|
||||
self.area = self.chest
|
||||
print('Chose supply chest:', self.chest)
|
||||
@@ -286,6 +309,9 @@ class GrabSuppliesStates:
|
||||
return
|
||||
|
||||
print('No matching items in this supply chest')
|
||||
key = getattr(self, 'current_supply_chest_key', None)
|
||||
if key is not None and key not in self.useless_chests:
|
||||
self.useless_chests.append(key)
|
||||
self.state = self.close_supply_chest
|
||||
|
||||
def grab_items(self):
|
||||
@@ -355,6 +381,12 @@ class GrabSuppliesStates:
|
||||
self.supplies = self.default_supplies()
|
||||
self.supply_chests = []
|
||||
self.checked_supply_chests = set()
|
||||
self.useless_chests = []
|
||||
self._processed_chest_open_events = {
|
||||
self._chest_event_key(event)
|
||||
for event in self.g.get('chest_open_events', ())
|
||||
}
|
||||
self.current_supply_chest_key = None
|
||||
self.supply_chest = None
|
||||
self.checked_supplies = []
|
||||
self.target_items = None
|
||||
@@ -365,4 +397,5 @@ class GrabSuppliesStates:
|
||||
self.wait_time = 0
|
||||
|
||||
def run(self):
|
||||
self._refresh_useless_chests()
|
||||
self.state()
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import json
|
||||
import time
|
||||
from math import floor
|
||||
from munch import Munch
|
||||
|
||||
from minecraft.networking.packets import clientbound, serverbound
|
||||
from minecraft.networking.modern import (
|
||||
AddEntityPacket, BlockEntityDataPacket, ContainerOpenPacket,
|
||||
AddEntityPacket, BlockEntityDataPacket, BlockEventPacket, ContainerOpenPacket,
|
||||
ContainerSetContentPacket, ContainerSetSlotPacket, SetEntityDataPacket,
|
||||
nbt_plain,
|
||||
PaletteSection,
|
||||
@@ -355,6 +356,78 @@ class WorldState:
|
||||
self.center, self.radius)
|
||||
|
||||
|
||||
class ChestOpenManager:
|
||||
"""Track chest viewer-count events and report openings by other players."""
|
||||
OWN_OPEN_TIMEOUT = 3.0
|
||||
|
||||
def __init__(self, global_state):
|
||||
self.g = global_state
|
||||
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.g.connection.register_packet_listener(
|
||||
self.handle_block_event, BlockEventPacket)
|
||||
|
||||
def _block_name(self, packet):
|
||||
registry = getattr(self.g.connection, 'registry_ids', {}).get(
|
||||
'minecraft:block', {})
|
||||
name = next((name for name, block_id in registry.items()
|
||||
if block_id == packet.block_id), None)
|
||||
if name is not None:
|
||||
return name.removeprefix('minecraft:')
|
||||
location = packet.location
|
||||
state_id = self.g.chunks.get_block_at(
|
||||
location.x, location.y, location.z)
|
||||
return blocks.BLOCKS.get(state_id)
|
||||
|
||||
def handle_block_event(self, packet):
|
||||
block_name = self._block_name(packet)
|
||||
if (block_name is None or block_name == 'ender_chest' or
|
||||
not (block_name == 'chest' or block_name.endswith('_chest')) or
|
||||
packet.action != 1):
|
||||
return
|
||||
|
||||
location = packet.location
|
||||
position = (location.x, location.y, location.z)
|
||||
viewers = packet.parameter
|
||||
previous_viewers = self.g.chest_viewers.get(position, 0)
|
||||
if viewers:
|
||||
self.g.chest_viewers[position] = viewers
|
||||
else:
|
||||
self.g.chest_viewers.pop(position, None)
|
||||
|
||||
pending = self.g.get('pending_chest_open')
|
||||
pending_age = (time.monotonic() - 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
|
||||
)
|
||||
if pending is not None and (is_bot_opening or not pending_is_fresh):
|
||||
self.g.pending_chest_open = None
|
||||
|
||||
if (viewers <= previous_viewers or
|
||||
(is_bot_opening and
|
||||
viewers <= max(1, previous_viewers))):
|
||||
return
|
||||
|
||||
event = {
|
||||
'position': position,
|
||||
'block': block_name,
|
||||
'viewers': viewers,
|
||||
'previous_viewers': previous_viewers,
|
||||
'timestamp': time.time(),
|
||||
}
|
||||
self.g.last_chest_opened_by_other = event
|
||||
self.g.chest_open_events.append(event)
|
||||
del self.g.chest_open_events[:-100]
|
||||
print('[chest] another player opened {} at {} ({} viewer{})'.format(
|
||||
block_name, position, viewers, '' if viewers == 1 else 's'),
|
||||
flush=True)
|
||||
|
||||
|
||||
class EntityManager:
|
||||
def __init__(self, global_state):
|
||||
self.g = global_state
|
||||
|
||||
Reference in New Issue
Block a user