using Ananta.SDK.Serialization; namespace Ananta.Server.Protocol.Client4229938; /// /// Writer for the 4229938 story channel (SyncStoryCoreServerInfo, 68629349) used by /// the vehicle ride story (S011). Ported from the proven 4091149 codec; command framing /// (Create=3, Delete=4, ConfirmRpc=2, Enable=6, Validate=9) and payload tags /// (Bool=5, Int=12, ULong=44) verified against the current dump.cs story classes. /// 4229938 delta applied: CreateClientNodeServerCommand gained StoryboardGuid /// (dump TypeDefIndex 69592) — sent as EMPTY string: the Lua writer marks the /// field non-nullable, so a null marker (0x00) corrupts the whole Create parse /// client-side and the S011 node is never built (F-enter: 缺少S011PlayerNet节点). /// Base fields (RpcId, HasRpcId) trail each command, matching the proven layout. /// internal static class StoryS011Codec4229938 { internal const string RootType = "StoryServer.Story.World.S011.S011PlayerNet"; internal const string EnterChildType = "StoryServer.Story.World.S011.SVehicleEnterBoardingNet"; internal const string ExitChildType = "StoryServer.Story.World.S011.SVehicleExitBoardingNet"; private static readonly string[] RootMessengers = [ "S011PlayerNetEnterVehicleRequest", "S011PlayerNetExitVehicleRequest", "S011PlayerNetForceEnterVehicleRequest", "S011PlayerNetForceExitVehicleRequest", "S011PlayerNetEnterVehicleIndoorRequest", "S011PlayerNetExitVehicleIndoorRequest", "S011PlayerNetForceShiftSeatRequest", ]; /// Atomic S011 root bootstrap: Create + Validate + Enable + ConfirmRpc. internal static byte[] BuildRootBootstrap(uint rootNid, long confirmRpcId) { var w = new UxWriter(); ListHeader(w, 4); Create(w, rootNid, RootType); Validate(w, rootNid, messengers: RootMessengers); EnableEmpty(w, rootNid); ConfirmRpc(w, confirmRpcId); return w.ToArray(); } /// Enter child bootstrap with its three replicables and phase messenger. internal static byte[] BuildEnterChildBootstrap(uint childNid, ulong vehicleId, byte seat) { var w = new UxWriter(); ListHeader(w, 3); Create(w, childNid, EnterChildType); Validate(w, childNid, replicables: ["vehicleEntityId", "seatIndex", "requisitionFailed"], messengers: ["MsgrChangePhaseRequest"]); var enable = new UxWriter(); ListHeader(enable, 3); NamedPayload(enable, "vehicleEntityId", b => { PayloadTag(b, 44); b.U64(vehicleId); }); NamedPayload(enable, "seatIndex", b => { PayloadTag(b, 12); b.I32(seat); }); NamedPayload(enable, "requisitionFailed", b => { PayloadTag(b, 5); b.Bool(false); }); Enable(w, childNid, enable.ToArray()); return w.ToArray(); } /// Exit child bootstrap with its four replicables and phase messenger. internal static byte[] BuildExitChildBootstrap(uint childNid, ulong vehicleId, byte seat, bool stopBeforeLeave) { var w = new UxWriter(); ListHeader(w, 3); Create(w, childNid, ExitChildType); Validate(w, childNid, replicables: ["vehicleEntityId", "seatIndex", "StopBeforeLeave", "IsEscortExit"], messengers: ["MsgrChangePhaseRequest"]); var enable = new UxWriter(); ListHeader(enable, 4); NamedPayload(enable, "vehicleEntityId", b => { PayloadTag(b, 44); b.U64(vehicleId); }); NamedPayload(enable, "seatIndex", b => { PayloadTag(b, 12); b.I32(seat); }); NamedPayload(enable, "StopBeforeLeave", b => { PayloadTag(b, 5); b.Bool(stopBeforeLeave); }); NamedPayload(enable, "IsEscortExit", b => { PayloadTag(b, 5); b.Bool(false); }); Enable(w, childNid, enable.ToArray()); return w.ToArray(); } /// ConfirmRpc(tag 2) with the cumulative committed id. internal static byte[] BuildConfirmRpc(long confirmRpcId) { var w = new UxWriter(); ListHeader(w, 1); ConfirmRpc(w, confirmRpcId); return w.ToArray(); } /// DeleteClientNode(tag 4) for a finished boarding child. internal static byte[] BuildDeleteNode(uint nid) { var w = new UxWriter(); ListHeader(w, 1); w.U8(4); // DeleteClientNodeCommand w.U32(nid); w.I64(0); // RpcId w.Bool(false); return w.ToArray(); } private static void Create(UxWriter w, uint nid, string type) { w.U8(3); // CreateClientNodeServerCommand w.U32(nid); w.UxString(type); w.UxString(string.Empty); // StoryboardGuid: none for server-made nodes (empty, NOT null) w.Bool(false); // Sync w.I64(0); // RpcId w.Bool(false); // HasRpcId } private static void Validate(UxWriter w, uint nid, string[]? observables = null, string[]? replicables = null, string[]? messengers = null, string[]? syncReplicables = null) { w.U8(9); // ValidateClientNodeServerCommand w.U32(nid); StringList(w, observables); StringList(w, replicables); StringList(w, messengers); StringList(w, syncReplicables); w.I64(0); w.Bool(false); } private static void EnableEmpty(UxWriter w, uint nid) { w.U8(6); // EnableClientNodeCommand w.U32(nid); w.U8(0); // Observables: null w.U8(0); // Replicables: null w.U8(0); // SyncReplicables: null w.I64(0); w.Bool(false); } private static void Enable(UxWriter w, uint nid, byte[] replicablesBody) { w.U8(6); w.U32(nid); w.U8(0); // Observables: null w.Raw(replicablesBody); w.U8(0); // SyncReplicables: null w.I64(0); w.Bool(false); } private static void ConfirmRpc(UxWriter w, long confirmRpcId) { w.U8(2); // ConfirmRpcServerCommand w.I64(confirmRpcId); w.I64(0); // RpcId w.Bool(false); } private static void PayloadTag(UxWriter w, byte tag) => w.U8(tag); /// NamedPayload complex: FF + Name + complex payload (tag byte + fields). private static void NamedPayload(UxWriter w, string name, Action writePayload) { w.U8(0xFF); w.UxString(name); writePayload(w); } private static void StringList(UxWriter w, string[]? values) { if (values is null) { w.U8(0); return; } w.U8(0xFF); WriteCount(w, values.Length); foreach (var v in values) w.UxString(v); } private static void ListHeader(UxWriter w, int count) { w.U8(0xFF); WriteCount(w, count); } // UxWriter.Int7 already applies the +1 wire bias (reader returns v-1). private static void WriteCount(UxWriter w, int count) => w.Int7(count); }