跳到正文
本頁目錄

Tap 協議

為 bedboo(傳輸 bedd)提供的客戶端遊戲數據包只讀流。配置 tap (配置);狀態:tap 操作。

訂閱與幀

{"op":"tap","action":"subscribe"} → {"ok":true},之後每幀一條二進制消息。該連接上不要再發其他內容。第二個訂閱者: busy;關閉時:disabled。

字節(LE)字段
u32len:其後的字節數,≤ 16 MiB + 20
u8kind
u8flags:1 重放,2 重新序列化(Packet::write),4 本地鏈路,8 已抑制(保留)
u160
u64seq(進程級;重放時保持不變)
u64tUs(單調時鐘 µs)
…負載
kind負載
1CLIENTBOUNDvarint 頭(id | senderSub<<10 | targetSub<<12)+ 包體(bedrock-protocol parsePacketBuffer)
2SERVERBOUND_ECHO同上;從不含 id 1、3、4、193(login、握手、request_network_settings)
4MARKERUTF-8 JSON(忽略其他 kind)
標記字段
HELLO(首個)tap:1、mod、game:"1.26.60.05"、protocol(2225)、pid、link{state, type, address}、journal{frames, bytes, complete}
LINK_UPtype remote|local、address
LINK_DOWNreason
REPLAY_BEGINframes
REPLAY_ENDcomplete(超過 tapJournalMiB 時為 false;無日誌:空的一對,false)
DROPPEDcount;訂閱者落後 > 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