M4:事件层(可按需排空的日志 + 游标)+ 16 项判据
新增 mcfold/events.py:EventLog(环形缓冲 + seq + 游标)
- 不做回调式推送:LLM 是回合制的,回调会让事件在两次观察之间静默丢失
- drain(limit) 只取本轮、剩余留待下次(不丢)
- 细分层(block_changed❌y:z)独立可查,不污染粗粒度流
- render_events() 输出紧凑文本行,省 token
session 接线:block_change / multi_block_change / chat / damage /
entity spawn·move·destroy / held_item_slot,并加 chat()/command() 发送
修的真 bug:
- dropped 恒 0:deque(maxlen=) 丢元素无声 ⇒ 改手动计数(事件丢了必须说)
- 事件键 to_state_id 与渲染器的 to 不一致 ⇒ 显示 None,改为解出方块名;
from 区分「未加载/界外」而非裸 None
- run_tests.sh 只数 ✓ 不数 ★ ⇒ 判据少报(98 修正为 110)
实测:/setblock 后事件正确报 grass_block→stone→air,与状态层一致
判据:110 项全通过;4 项变异全部被抓
This commit is contained in:
@ -146,6 +146,13 @@ class WorldState:
|
||||
self.geometry = WorldGeometry()
|
||||
self.player = PlayerView()
|
||||
self.entities: dict[int, Entity] = {}
|
||||
# ★ 方块变化覆盖层:(x,y,z) -> state_id
|
||||
# 不改写区块(区块是压缩调色板,重建代价高且会被 unload 重发覆盖)
|
||||
self.overrides: dict[tuple[int, int, int], int] = {}
|
||||
# 方块变化流水(供「我改了什么」回放)
|
||||
self.block_log: list[tuple[int, int, int, int]] = []
|
||||
self.bulk_changes = 0
|
||||
self.bulk_change_records: list[int] = []
|
||||
# 服务端给的 11 个动态注册表(权威,保留原始条目)
|
||||
self.registries: dict[str, Any] = {}
|
||||
self.packets_seen: dict[str, int] = {}
|
||||
@ -196,6 +203,33 @@ class WorldState:
|
||||
def apply_registry(self, rid: str, entries: Any) -> None:
|
||||
self.registries[rid] = entries
|
||||
|
||||
# ---------- 方块变化:覆盖层 ----------
|
||||
|
||||
def on_block_change(self, x: int, y: int, z: int, state_id: int | None) -> None:
|
||||
"""服务端说某坐标方块变了 ⇒ 更新我们的世界视图。
|
||||
|
||||
★ 用**覆盖层**(overlay)而不是改写区块:
|
||||
区块的 Paletted Container 调色板是压缩的,改一个值可能要重建整段
|
||||
位数组;而覆盖层天然有界(只存变过的格子)且可回答「原来的值是什么」
|
||||
—— 事件里的 from → to 就需要这个。
|
||||
另一个理由:区块随时可能被 unload 重发,覆盖层能正确丢弃。
|
||||
"""
|
||||
if state_id is None:
|
||||
return
|
||||
self.overrides[(x, y, z)] = int(state_id)
|
||||
self.block_log.append((x, y, z, int(state_id)))
|
||||
|
||||
def on_multi_block_change(self, chunk_x: int, chunk_z: int,
|
||||
records: list[int]) -> None:
|
||||
"""一次多方块变化(爆炸/活塞):records 打包为 varint 数组。
|
||||
|
||||
记录格式(协议):每条 varint,高位是状态 id,低 12 位是局部坐标。
|
||||
具体位宽随协议而变,故以**协议给定**为准读入并显式记录条数;
|
||||
拿不准时不猜坐标,只把条数与方块 id 报给 agent。
|
||||
"""
|
||||
self.bulk_changes += 1
|
||||
self.bulk_change_records = list(records)
|
||||
|
||||
def upsert_entity(self, ent: Entity) -> None:
|
||||
self.entities[ent.entity_id] = ent
|
||||
|
||||
@ -209,6 +243,20 @@ class WorldState:
|
||||
|
||||
def block_at(self, x: int, y: int, z: int) -> dict[str, Any]:
|
||||
"""单个方块。返回来源标记,agent 能分辨「确实没有」与「还没看见」。"""
|
||||
ov = self.overrides.get((x, y, z))
|
||||
if ov is not None:
|
||||
# 覆盖层优先(世界已被服务端改动过)
|
||||
name = None
|
||||
if self.world.registry is not None:
|
||||
name = self.world.registry.name_of(ov)
|
||||
src = "registry" if name else "unknown-state"
|
||||
if name is None:
|
||||
self.world.unknown_state_ids[ov] = \
|
||||
self.world.unknown_state_ids.get(ov, 0) + 1
|
||||
return {
|
||||
"name": name, "state_id": ov, "source": src,
|
||||
"describe": f"{name}" if name else f"unknown({ov}) [已变更]",
|
||||
}
|
||||
hit = self.world.block_at(x, y, z)
|
||||
return {
|
||||
"name": hit.name,
|
||||
|
||||
Reference in New Issue
Block a user