Skip to main content

FigNet Porting Guide

This guide documents the current FigNet + Entangle protocol as implemented in the active C# runtime.

It is meant for developers building:

  • a non-C# client
  • a new provider
  • a protocol debugger or packet inspector

Scope

This guide is based on the active runtime projects only:

  • FigNet
  • ENetProvider
  • LiteNetLibProvider
  • WebSocketCoreProvider
  • EntangleCommon
  • Entangle
  • EntangleServer

This guide intentionally ignores legacy or non-canonical folders for protocol decisions:

  • Benchmarking
  • CppClient
  • DemoClient
  • DemoServer
  • entangle-admin-dashboard
  • EntangleCpp
  • FigNet_AssetStore
  • Template
  • FigNet_UnitTest
  • FigNet_UnityPackage
  • Unity .meta files

If code and older docs disagree, trust the C# files above.

1. Protocol Layers

There are two protocol layers in the current implementation:

  1. FigNet core envelope This is the 3-byte header, optional 2-byte magic prefix, callback flag packing, and optional XOR payload encryption.
  2. Payload codec Core built-in messages use the legacy BitBuffer codec. Entangle messages use MemoryPack.

Source of truth:

  • FigNet/Core/MessageHeader.cs
  • FigNet/Core/Message.cs
  • FigNet/Core/Internals.cs
  • FigNet/Messages/Operations/RegisterIdentity.cs
  • FigNet/Messages/Operations/Ping.cs
  • EntangleCommon/Serializer/MemoryPack_Serializer.cs
  • EntangleCommon/Data/Model/EntangleModels.cs

2. Wire Envelope

All numeric fields in the FigNet envelope are little-endian.

2.1 Without EnableCheckSum

Despite the config name, no checksum is added. The frame layout is:

0..1 msg_id ushort
2 flags byte
3..N payload

2.2 With EnableCheckSum

In the active providers, EnableCheckSum means "prepend a 2-byte magic number".

0..1 magic ushort = 0xC0DE
2..3 msg_id ushort
4 flags byte
5..N payload

Important:

  • Internals.MAGIC_NUMBER is 0xC0DE
  • MessageHeader.HASH_SIZE is 2
  • the name HASH_SIZE is legacy naming; it is a magic prefix size, not a hash
  • active providers validate the magic number only

Source of truth:

  • FigNet/Core/MessageHeader.cs
  • FigNet/Core/Internals.cs
  • ENetProvider/ClientSocketENet.cs
  • ENetProvider/ServerSocketEnet.cs
  • LiteNetLibProvider/ClientSocketLiteNetLib.cs
  • LiteNetLibProvider/ServerSocketLiteNetLib.cs
  • WebSocketCoreProvider/ClientSocketWS.cs
  • WebSocketCoreProvider/ServerSocketWS.cs

2.3 Flags byte

bit 7 IsEncrypted
bits 0..6 callbackId

Rules:

  • callbackId == 0 means fire-and-forget
  • callbackId is limited to 1..127
  • client sockets allocate callback IDs automatically for SendMessage(message, response)
  • server handlers usually echo the incoming message.callbackId

3. Encryption

Encrypted datagram payloads use an app-layer secure session (ECDH on P-256 + HKDF-SHA256 + ChaCha20-Poly1305 AEAD), not XOR. FigNet/Core/XorEncryption.cs is legacy/unused code - it is not referenced by any active provider (ENetProvider, LiteNetLibProvider, WebSocketCoreProvider) and must not be implemented for compatibility.

For the full handshake, wire format, and a porting checklist for this layer, see Secure Session.

Source of truth:

  • FigNet/Core/SecureSessionContext.cs
  • FigNet/Core/SecureSessionProtocol.cs
  • ENetProvider/ClientSocketENet.cs
  • LiteNetLibProvider/ClientSocketLiteNetLib.cs

4. Transport and Delivery Behavior

The envelope is transport-agnostic. The active providers are:

  • WebSockets
  • ENet
  • LiteNetLib

4.0 WebSocket-specific connection details

For the current WebSocket provider:

  • client connects to /FigNet
  • client requests subprotocol FigNet
  • server advertises supported subprotocol FigNet
  • TLS is optional and controlled by IsSecure

Source of truth:

  • WebSocketCoreProvider/ClientSocketWS.cs
  • WebSocketCoreProvider/ServerSocketWS.cs

4.1 Delivery methods

FigNet exposes:

  • Unreliable
  • Reliable
  • ReliableUnordered
  • Sequenced

Provider semantics are not perfectly identical, so ports should match behavior, not just enum names.

Source of truth:

  • FigNet/Core/DeliveryMode.cs
  • ENetProvider/ClientSocketENet.cs
  • LiteNetLibProvider/ClientSocketLiteNetLib.cs
  • WebSocketCoreProvider/ClientSocketWS.cs

4.2 Entangle channel conventions

Entangle uses these fixed channel IDs:

ChannelConstantTypical use
0RELAIBLE_DATA_CHANNELcontrol and reliable room data
1ENTITY_STATE_CHANNELentity state deltas
2ROOM_EVENT_CHANNELroom events and cached-event traffic
3UPDATE_CHANNELtransform/entity update stream

Notes:

  • the constant name RELAIBLE_DATA_CHANNEL contains a typo in code and should be preserved when referencing current implementation
  • room/entity replication relies on both channel ID and delivery mode

Source of truth:

  • EntangleCommon/Misc/FNConstants.cs
  • Entangle/EN.cs
  • Entangle/NetworkEntity.cs
  • Entangle/Room.cs
  • EntangleServer/Modules/Lobby/Components/RoomStateManager.cs
  • EntangleServer/Modules/Lobby/AreaOfInterest/AOIGroup.cs

5. Current Handshake and Session Flow

The current runtime does not use the newer conceptual WSS/auth ticket flow described in older docs. The live flow is:

  1. Connect transport.
  2. Client auto-sends 60000 RegisterIdentity.
  3. Server validates AppName, AppSecretKey, and PeerType.
  4. Server replies with the same message ID and same callback ID.
  5. Entangle client sends 60304 AppKey.
  6. Server replies with IsSuccess.
  7. Lobby operations start: room list, create room, join room, room traffic.

Important security note:

  • RegisterIdentity includes the app secret in the payload
  • it is not automatically marked IsEncrypted in the active FigNet/Messages/Operations/RegisterIdentity.cs
  • if you run over plain ENet/LiteNetLib or plain ws, that secret is not transport-protected

Server-side timeout note:

  • newly connected peers are disconnected if they fail to register within about 5 seconds

Source of truth:

  • FigNet/Messages/Operations/RegisterIdentity.cs
  • FigNet/Messages/Handlers/RegisterIdentityHandler.cs
  • FigNet/Core/Internals.cs
  • Entangle/EN.cs
  • EntangleCommon/Data/Operation/RegisterAppIdOperation.cs
  • EntangleServer/Modules/Lobby/Handlers/OnAppKeyHandler.cs

6. Core Message IDs

IDNameDirectionPayloadNotes
60000RegisterIdentityclient -> server, response server -> clientRegisterIdentityModelcurrent bootstrap message
60001ValidateIdentityreserved legacynone / incompleteclient handler exists, active server path does not use it
60002Pingrequest/responsePingModelechoes peer type and callback ID

6.1 RegisterIdentityModel

Legacy BitBuffer layout:

FieldTypeNotes
peerTypebyte0=Server, 1=Client, 2=Unknown
SecretKeyASCII stringshort length prefix, then bytes
AppNameASCII stringshort length prefix, then bytes

Failure response behavior:

  • server responds on the same message ID
  • SecretKey becomes "invalid"
  • AppName contains the error text

Source of truth:

  • FigNet/Messages/Operations/RegisterIdentity.cs
  • FigNet/Messages/Handlers/RegisterIdentityHandler.cs
  • FigNet/Core/BitBuffer.cs
  • FigNet/Core/PeerStatus.cs

6.2 PingModel

Legacy BitBuffer layout:

FieldType
peerTypebyte

Source of truth:

  • FigNet/Messages/Operations/Ping.cs
  • FigNet/Messages/Handlers/PingHandler.cs

7. Entangle Message ID Registry

EntangleCommon/OperationCode.cs is the canonical ID registry for the active Entangle stack.

7.1 Lobby and control

IDNameTypical directionRequest payloadResponse/Event payload
60300CreateRoomclient <-> serverCreateRoomDataCreateRoomResponseData
60301JoinRoomclient <-> serverRoomJoinOperationDataRoomJoinOperationData
60302GetRoomListclient <-> serverGetRoomListOperationDataRoomListData
60303LeaveRoomclient <-> serverLeaveRoomOperationDataLeaveRoomOperationData
60304AppKeyclient <-> serverRegisterAppIdOperationDataRegisterAppIdOperationData
60305ReJoinRoomclient <-> serverRoomReJoinOperationDataRoomReJoinOperationData
60306ForcedSyncRoomStateclient -> serverNullOperationDataroom state replay sequence

7.2 Room membership and replication

IDNameTypical directionRequest payloadResponse/Event payload
60400OnPlayerJoinRoomserver -> clientn/aOnPlayerJoinRoomOperationData
60401OnPlayerLeftRoomserver -> clientn/aOnPlayerLeaveRoomOperationData
60402OnEntityStateclient -> server, server -> clientEntityStateChangeOperationDataEntityStateChangeResponseOperationData
60403OnMasterClientChangeserver -> clientn/aMasterClientChangeOperationData
60404InstantiateEntityclient -> server, server -> clientInstantiateOperationDataInstantiateOperationResponseData
60405DeleteEntityclient -> server, server -> clientDeleteEntityDataDeleteEntityData
60406RoomEventclient -> server, server -> clientRoomEventDataRoomEventResponseData
60407RequestOwnerShipclient -> server, server -> clientRequestOwnershipOperationDataRequestOwnershipOperationData
60408ClearOwnerShipclient -> server, server -> clientClearOwnershipDataClearOwnershipData
60409OnAgentOwnerShipChangeserver -> clientn/aAgentOwnershipChangeOperationData
60410RoomStateChangeserver -> clientn/aRoomStateChangeOperationData
60411PreRoomStateReceivedserver -> clientn/aNullOperationData
60412PostRoomStateReceivedserver -> clientn/aNullOperationData
60413EntityUpdateclient -> server, server -> clientEntityUpdateOperationDataEntityUpdateResponseOperationData
60414PresenterModeclient -> server, response server -> clientPresenterModeDataPresenterModeData
60415OnCachedEventRemovedserver -> clientn/aRoomEventData
60416CustomGroupOperationclient -> server, server -> clientCustomGroupOperationDataCustomGroupOperationData
60417Heartbeatclient -> serverNullOperationDatanone
60418UpdateSeatclient -> serverUpdateSeatOperationDataroom-property/state propagation

7.3 Game extension

IDNameTypical directionPayload
60500GameMessageclient <-> server/moduleGameMessage
60501GameStateclient <-> server/moduleGameState

7.4 Infrastructure and server-side IDs

These exist in the registry but are usually not needed by a normal gameplay client port.

ID rangeUsage
60050-60055load balancer / room registry
62000-62002admin panel

Source of truth:

  • EntangleCommon/OperationCode.cs
  • EntangleAdminPanel/OperationCode.cs

8. Key Payload Shapes

Most Entangle payloads are declared in one file:

  • EntangleCommon/Data/Model/EntangleModels.cs

8.1 CreateRoomData

FieldType
RoomNamestring
MaxPlayersushort
Passwordstring
CellSizeushort
Widthushort
Heightushort
AoiActivationCountbyte
LodLevelbyte
ForcedRoomStateSyncIntervalshort
PresenterInGroupCastbool
FetchCachedEventsDelayushort

8.2 CreateRoomResponseData

FieldType
RoomIduint
RoomNamestring
ResponseCodeRoomResponseCode

8.3 RoomJoinOperationData

Request fields:

FieldType
RoomIduint
TeamIduint
Passwordstring
UserNamestring

Response fields used by the current client:

FieldMeaning in response
ResponseCodejoin result
RoomIdcurrent player ID, not room ID
RoomAuthTokenroom rejoin token
RoomNameroom display name

8.4 RoomReJoinOperationData

FieldType
RoomIduint
TeamIduint
Passwordstring
IsRejoinbool
RoomAuthTokenstring
PeerIduint
ResponseCodeRoomResponseCode
RoomNamestring

8.5 RoomEventData

FieldMeaning
RoomIdroom ID on request, sender peer ID on server->client event messages
EventIduser-defined event code
DeliveryMethodDeliveryMethod encoded as byte
Tagarbitrary grouping/removal tag
IsCachedcache in room for late joiners
UniqueIdroom-assigned ID for cached-event removal
Dataraw event payload bytes

8.6 InstantiateOperationData

FieldType
RoomIduint
EntityIdshort
EntityTypeEntityType
TransLiteTransformLite

8.7 InstantiateOperationResponseData

FieldType
RoomIduint
OwnerIduint
NetworkIduint
EntityIdshort
EntityTypeEntityType
InitialStateEntityDefaultStateData

8.8 EntityUpdateOperationData

FieldType
RoomIduint
EntityNetIduint
EntityTypeEntityType
TransLiteTransformLite

EntityUpdateResponseOperationData is just a batch wrapper:

FieldType
EntitiesUpdateList<EntityUpdateOperationData>

8.9 EntityStateChangeOperationData

FieldType
RoomIduint
NetworkIduint
EntityTypeEntityType
StateEntangleEntityState

EntityStateChangeResponseOperationData is a batch wrapper:

FieldType
StatesBatchList<EntityStateChangeOperationData>

8.10 RequestOwnershipOperationData

Request meaning:

FieldMeaning
RoomIdroom ID
IsLockedlock requested ownership
EntityNetIdtarget entity
EntityTypetarget type

Broadcast response meaning:

FieldMeaning
RoomIdowner ID, not room ID
EntityNetIdtarget entity
IsLockedownership lock state
EntityTypetarget type

8.11 ClearOwnershipData

Request meaning:

FieldMeaning
RoomIdroom ID
EntityNetIdtarget entity

Broadcast response meaning:

FieldMeaning
RoomIdowner ID of the clearer, not room ID
EntityNetIdtarget entity

9. Transform and State Internals

9.1 TransformLite

TransformLite is a MemoryPack object containing:

  • TransformData : byte[]
  • ConfigFlags : ushort
  • SequenceNumber : ushort

Wire-breaking change (Advanced NetworkTransform): ConfigFlags was widened byte -> ushort (MemoryPack now writes 2 bytes for it). Client and server must be built/deployed from the same commit - an old runtime mis-parses every TransformLite. Bits 0-7 are unchanged, so existing serialized flag values round-trip; only the field width and the new high bits are new.

TransformData is a custom packed little-endian blob.

ConfigFlags bits:

BitMeaning
0include Z position
1include rotation
2include scale X
3include scale Y
4include scale Z
5half precision scale 1000
6half precision scale 100
7half precision scale 10
8include linear velocity
9include angular velocity
10velocity half precision (short scale 100, else 1000)
11-15reserved

Rules:

  • X and Y position are always present
  • Z is optional
  • rotation is four short values and always uses scale 1000
  • scale axes are optional
  • if no precision flag is set, position/scale values are written as 32-bit floats
  • if a precision flag is set, position/scale values are written as scaled short
  • linear/angular velocity are each three short values, always short-scaled (never float), and are appended after scale - so position/rotation/scale offsets are unchanged whether or not velocity is present (the server reads Position from the front regardless)
  • velocity scale is 1000 (range +/-32.7, 1 mm/s) unless bit 10 is set, then 100 (range +/-327, 1 cm/s)

Source of truth:

  • EntangleCommon/Data/Model/EntangleModels.cs (server) - mirrored in FigNetUnity/.../Entangle/FigNetCommon/Data/Model/EntangleModels.cs (client)

9.2 EntangleEntityState

Entity state replication is a batch of property deltas:

FieldType
DeliveryMethodbyte
DeltaList<NetworkedObjectBinary>
SequenceNumberushort

Each NetworkedObjectBinary contains:

FieldType
Keybyte
Databyte[]

Meaning:

  • Key is the [Networked(...)] property ID
  • Data is a MemoryPack serialization of the concrete NetworkSerializable payload

Important porting note:

  • server currently registers a dynamic union only for RoomSeatsData
  • custom NetworkSerializable subtypes must be registered consistently on every runtime

Source of truth:

  • EntangleCommon/Data/Model/EntangleEntityState.cs
  • EntangleCommon/Data/NetworkedObjectBinary.cs
  • EntangleCommon/Data/NetworkSerializable.cs
  • EntangleCommon/Data/Attributes.cs
  • EntangleServer/Modules/Lobby/FNELobby.cs

10. Compatibility Quirks You Must Preserve

These are easy to miss and matter for client ports:

  1. EnableCheckSum adds a 2-byte magic number, not a checksum hash.
  2. RoomJoinOperationData.RoomId is reused as the joining player's ID in the join response.
  3. RoomEventData.RoomId is reused as sender ID in server->client event traffic.
  4. RequestOwnershipOperationData.RoomId is reused as owner ID in the broadcast response.
  5. ClearOwnershipData.RoomId is reused as owner ID in the broadcast response.
  6. RegisterIdentity and Ping use the legacy BitBuffer codec, not MemoryPack.
  7. Most Entangle messages use MemoryPack, so ports need either MemoryPack compatibility or a controlled replacement.
  8. ValidateIdentity (60001) is reserved but not part of the active built-in handshake.
  9. Server-side peer IDs are generated by FigNet and should be treated as opaque uint values.

11. Porting Checklist

  • Implement the exact FigNet envelope, including the optional 0xC0DE prefix.
  • Treat all header integers as little-endian.
  • Pack callback ID in the low 7 bits and encryption in the high bit.
  • If you need IsEncrypted compatibility, implement the secure-session handshake (see Secure Session), not XOR - XOR is legacy and unused on the active path.
  • Implement 60000 RegisterIdentity with the legacy BitBuffer codec.
  • Ignore 60001 ValidateIdentity unless you are intentionally reproducing legacy behavior.
  • Implement 60304 AppKey after successful identity registration if you want Entangle compatibility.
  • Preserve the response field quirks documented above.
  • Support MemoryPack payloads for Entangle operation and model classes.
  • Preserve Entangle channels 0/1/2/3 and their current use.

If a port author wants to trace behavior from top to bottom, read these in order:

  1. FigNet/Core/MessageHeader.cs
  2. FigNet/Core/Message.cs
  3. FigNet/Messages/Operations/RegisterIdentity.cs
  4. FigNet/Messages/Handlers/RegisterIdentityHandler.cs
  5. EntangleCommon/OperationCode.cs
  6. EntangleCommon/Data/Model/EntangleModels.cs
  7. Entangle/EN.cs
  8. Entangle/Lobby.cs
  9. Entangle/NetworkEntity.cs
  10. EntangleServer/Modules/Lobby/Handlers/*
  11. EntangleServer/Modules/Lobby/Components/*