Tap 協議
為 bedboo(傳輸 bedd)提供的客戶端遊戲數據包只讀流。配置 tap
(配置);狀態:tap 操作。
訂閱與幀
{"op":"tap","action":"subscribe"} → {"ok":true},之後每幀一條二進制消息。該連接上不要再發其他內容。第二個訂閱者:
busy;關閉時:disabled。
| 字節(LE) | 字段 |
|---|---|
| u32 | len:其後的字節數,≤ 16 MiB + 20 |
| u8 | kind |
| u8 | flags:1 重放,2 重新序列化(Packet::write),4 本地鏈路,8 已抑制(保留) |
| u16 | 0 |
| u64 | seq(進程級;重放時保持不變) |
| u64 | tUs(單調時鐘 µs) |
| … | 負載 |
| kind | 負載 | |
|---|---|---|
| 1 | CLIENTBOUND | varint 頭(id | senderSub<<10 | targetSub<<12)+ 包體(bedrock-protocol parsePacketBuffer) |
| 2 | SERVERBOUND_ECHO | 同上;從不含 id 1、3、4、193(login、握手、request_network_settings) |
| 4 | MARKER | UTF-8 JSON(忽略其他 kind) |
| 標記 | 字段 |
|---|---|
HELLO(首個) | tap:1、mod、game:"1.26.60.05"、protocol(2225)、pid、link{state, type, address}、journal{frames, bytes, complete} |
LINK_UP | type remote|local、address |
LINK_DOWN | reason |
REPLAY_BEGIN | frames |
REPLAY_END | complete(超過 tapJournalMiB 時為 false;無日誌:空的一對,false) |
DROPPED | count;訂閱者落後 > 32 MiB;隨後斷開(重新訂閱:完整重放) |
實時幀等到 REPLAY_END 之後;按 seq 跳過重複幀。
發佈者共用 src/TapSink.h(代碼規範);鉤子只複製,由訂閱者線程取走。
遠程服務器(NetTap.cpp)
| 鏈路建立 | NetworkSystem::send(首個數據包之前)和 runEvents(每 tick)找到打開的連接 |
| 鏈路斷開 | $onConnectionClosed、closeConnection(1.26.60 未導出 onNewOutgoingConnection、onAllConnectionsClosed、disconnect) |
| 入 | BatchedNetworkPeer::_receivePacket(其後,HasData):已解密、已解壓;flags 0 |
| 出 | NetworkSystem::_sendInternal;flags 0 |
trace | 每條鏈路前 50 個數據包:tap trace #n in|out id len |
| Blob 緩存 | 發送前清除 client_cache_status.enabled,使服務器發送完整子區塊(回顯顯示 false) |
單人世界和局域網主機世界(TapLoopback.cpp,配置 tapLoopback)
默認 true,需要 tap。鉤住 LoopbackPacketSender;數據包重新序列化(頭同上,Packet::write 使用發送方 NetworkSystem
的 ReflectionCtx);flags 2 + 4。
| CLIENTBOUND | 服務端發送方 $sendToClient(兩個)、$sendToClients、$sendBroadcast(兩個)、$sendTo(自 1.26.60 起經虛表調用 $sendTo、$sendBroadcast(except));僅 Local 連接。局域網訪客:計入 guest。廣播在本地玩家的 start_game 之後 |
| SERVERBOUND_ECHO | 客戶端發送方 $sendToServer、$send、$sendTo;從不含 id 1、3、4、193 |
| 區塊 | 每個 subchunk_request/subchunk(一個原點,最多 highest_subchunk_count;client_cache_status.enabled:false) |
| 鏈路 | 僅當 tap::gLinkType 為 local;由客戶端發送方的首個數據包和 $onConnectionClosed 設置。鏈路未知前,Local 客戶端連接計入(從不寫 gLinkType)。遠程捕獲只在 Remote 時發佈:不會重複 |
| 無接收端 | tap::gEmit 為 null:只計數。state.stats.tapLoopback{enabled, hooks, link, clientbound, echo, bytes, unpublished, guest} |
trace | 記錄前 50 幀(trace tap loopback: in|out id= len=);前 4000 幀寫入 mods\BeDD\data\tap-loopback.bin(seq、tUs 為 0) |
加入日誌(TapJournal.cpp)
掛在 bedd::tap::gEmit 鏈上,設置 bedd::tap::gReplay(在 REPLAY 標記之間對每個訂閱者調用)。
| 保留 | 僅 CLIENTBOUND。新鏈路和 start_game(11,首幀)時重置 |
| 每鍵保留最新 | level_chunk(x、z、dim)、update_block(pos、layer)、set_time、set_health、network_chunk_publisher_update、current_structure_feature、inventory_content(window)、inventory_slot(window、slot)、level_event(事件 id)、update_abilities(unique id)、add_*(unique id;add_player 用 runtime id);按 runtime id:set_entity_motion、move_entity、move_player、mob_armor_equipment |
| 按實體 + 字段 | set_entity_data(鍵)、update_attributes(名字)、move_entity_delta(字段)、mob_effect(效果) |
subchunk | 更新的幀覆蓋其全部子區塊後丟棄;無法遍歷的:保留 |
remove_entity(14) | 刪除 add 幀(add_player 按 unique id 字節)和 runtime id 鍵 |
| 不保留 | text、animate、network_stack_latency、play_sound、stop_sound、level_sound_event(v1、v2)、spawn_particle_effect、level_event_generic |
| 上限 | tapJournalMiB(128):超出後 complete:false 直到重置(晚到的訂閱者缺區塊) |
| 統計 | state.stats.journal:enabled, frames, bytes, complete, seen, compacted, pruned, transient, overCap, resets, replays |