//////////////////////////////////////////////////////////// //~ Key types Struct(PP_EntKey) { Uid uid; }; Struct(PP_ClientKey) { u32 idx; u32 gen; }; #define PP_NilClientKey ((PP_ClientKey) { .gen = 0, .idx = 0 }) //////////////////////////////////////////////////////////// //~ Client store types Struct(PP_ClientLookupBin) { PP_ClientKey first; PP_ClientKey last; }; Struct(PP_ClientStore) { b32 valid; Arena *arena; /* Client lookup */ PP_ClientLookupBin *client_lookup_bins; u64 num_client_lookup_bins; /* Clients */ Arena *clients_arena; struct PP_Client *clients; PP_ClientKey first_free_client; u64 num_clients_allocated; u64 num_clients_reserved; }; Inline PP_ClientStore *PP_NilClientStore(void) { extern Readonly PP_ClientStore **PP_nil_client_store; return *PP_nil_client_store; } //////////////////////////////////////////////////////////// //~ Client types Struct(PP_SnapshotLookupBin) { struct PP_Snapshot *first; struct PP_Snapshot *last; }; Struct(PP_Client) { b32 valid; PP_ClientKey key; PP_ClientStore *store; Arena *snapshots_arena; /* Round trip time of the client (if networked) */ i64 last_rtt_ns; N_ChannelId channel_id; u64 channel_hash; PP_ClientKey next_free; PP_ClientKey next_in_bin; PP_ClientKey prev_in_bin; /* The client's player entity key in the master sim (if relevant) */ PP_EntKey player_id; /* This is the highest confirmed tick of ours that we know this client has received */ u64 ack; // This is the highest confirmed ack of ours that we know this client has received (this // can be used to determine which client ticks will no longer be delta encoded from and // therefore can be released) u64 double_ack; /* This is the highest tick of their's that we have received */ u64 highest_received_tick; /* Snapshots sorted by tick (low to high) */ u64 first_tick; u64 last_tick; u64 num_ticks; struct PP_Snapshot *first_free_snapshot; /* Tick -> snapshot lookup */ u64 num_snapshot_lookup_bins; PP_SnapshotLookupBin *snapshot_lookup_bins; }; Inline PP_Client *PP_NilClient(void) { extern Readonly PP_Client **PP_nil_client; return *PP_nil_client; } Inline b32 PP_EqClientKey(PP_ClientKey a, PP_ClientKey b) { return a.gen == b.gen && a.idx == b.idx; } //////////////////////////////////////////////////////////// //~ Layer types /* Absolute layers */ #define PP_Layer_FloorDecals (-300) #define PP_Layer_Bullets (-200) #define PP_Layer_Tracers (-100) #define PP_Layer_Shoulders (0) #define PP_Layer_Walls (100) /* Relative layers */ #define PP_Layer_DefaultRelative (0) #define PP_Layer_RelativeWeapon (1) //////////////////////////////////////////////////////////// //~ Control types Enum(PP_ControlFlag) { PP_ControlFlag_Fire = 1 << 0, PP_ControlFlag_AltFire = 1 << 1, /* Testing */ PP_ControlFlag_Drag = 1 << 2, PP_ControlFlag_Delete = 1 << 3, PP_ControlFlag_ClearAll = 1 << 4, PP_ControlFlag_SpawnTest1 = 1 << 5, PP_ControlFlag_SpawnTest2 = 1 << 6, PP_ControlFlag_SpawnTest3 = 1 << 7, PP_ControlFlag_SpawnTest4 = 1 << 8, PP_ControlFlag_TestWalls = 1 << 9, PP_ControlFlag_TestTiles = 1 << 10, PP_ControlFlag_TestExplode = 1 << 11, PP_ControlFlag_TestTeleport = 1 << 12, }; Struct(PP_ControlData) { Vec2 move; /* Movement direction vector (speed of 0 -> 1) */ Vec2 focus; /* Focus direction vector (where does the controller want to look) */ Vec2 dbg_cursor; /* Where is the user's cursor in the world (used for things like editing the world) */ PP_ControlFlag flags; }; Enum(PP_CmdKind) { PP_CmdKind_Invalid, PP_CmdKind_Control, PP_CmdKind_Chat }; //////////////////////////////////////////////////////////// //~ Tile types Enum(PP_TileKind) { PP_TileKind_None, PP_TileKind_Wall, PP_TileKind_Count }; StaticAssert(PP_TileKind_Count < 256); /* Tile kind must fit in 8 bits */ //////////////////////////////////////////////////////////// //~ Snapshot types Enum(PP_SyncFlag) { PP_SyncFlag_NoSyncPredictables = 1 << 0 }; Struct(PP_Snapshot) { b32 valid; u64 tick; PP_Client *client; PP_Snapshot *next_free; PP_Snapshot *next_in_bin; PP_Snapshot *prev_in_bin; u64 prev_tick; u64 next_tick; Arena *arena; /* Sim time (guaranteed to increase by sim_dt_ns each step) */ i64 sim_dt_ns; i64 sim_time_ns; /* If != previous tick's continuity then don't lerp */ u64 continuity_gen; /* The last physics iteration (used for tracking contact lifetime) */ u64 phys_iteration; /* The key of the receiver's player in the snapshot */ PP_EntKey local_player; /* Key lookup */ struct PP_EntBin *key_bins; u64 num_key_bins; /* Ents */ Arena *ents_arena; struct PP_Ent *ents; u32 first_free_ent; u32 num_ents_allocated; u32 num_ents_reserved; }; Inline PP_Snapshot *PP_NilSnapshot(void) { extern Readonly PP_Snapshot **PP_nil_snapshot; return *PP_nil_snapshot; } //////////////////////////////////////////////////////////// //~ State types #define PP_ClientLookupBinsCount 127 #define PP_TickLookupBinsCount 127 #define PP_KeyLookupBinsCount 4096 Struct(PP_SharedSimCtx) { Arena *nil_arena; PP_ClientStore *nil_client_store; PP_Client *nil_client; PP_Snapshot *nil_snapshot; struct PP_Ent *nil_ent; } extern PP_shared_sim_ctx; /* Accessed via `PP_NilClientStore()` */ extern Readonly PP_ClientStore **PP_nil_client_store; /* Accessed via `PP_NilClient()` */ extern Readonly PP_Client **PP_nil_client; /* Accessed via `NilSnapshot()` */ extern Readonly PP_Snapshot **PP_nil_snapshot; extern Readonly struct PP_Ent **PP_nil_ent; //////////////////////////////////////////////////////////// //~ Startup void PP_StartupSim(void); //////////////////////////////////////////////////////////// //~ Client store acquire operations PP_ClientStore *PP_AcquireClientStore(void); void PP_ReleaseClientStore(PP_ClientStore *store); //////////////////////////////////////////////////////////// //~ Client acquire operations PP_Client *PP_AcquireClient(PP_ClientStore *store); void PP_ReleaseClient(PP_Client *client); //////////////////////////////////////////////////////////// //~ Client lookup operations u64 PP_ClientChannelHashFromChannelId(N_ChannelId channel_id); void PP_SetClientChannelId(PP_Client *client, N_ChannelId channel_id); PP_Client *PP_ClientFromChannelId(PP_ClientStore *store, N_ChannelId channel_id); PP_Client *PP_ClientFromKey(PP_ClientStore *store, PP_ClientKey key); //////////////////////////////////////////////////////////// //~ Snapshot acquire operations PP_Snapshot *PP_AcquireSnapshot(PP_Client *client, PP_Snapshot *src, u64 tick); void PP_ReleaseSnapshot(PP_Snapshot *ss); void PP_ReleaseSnapshotsInRange(PP_Client *client, u64 start, u64 end); //////////////////////////////////////////////////////////// //~ Snapshot lookup operations PP_Snapshot *PP_SnapshotFromTick(PP_Client *client, u64 tick); PP_Snapshot *PP_SnapshotFromClosestTickLte(PP_Client *client, u64 tick); PP_Snapshot *PP_SnapshotFromClosestTickGte(PP_Client *client, u64 tick); //////////////////////////////////////////////////////////// //~ Tile operations Vec2I32 PP_WorldTileIndexFromPos(Vec2 pos); Vec2 PP_PosFromWorldTileIndex(Vec2I32 world_tile_index); Vec2I32 PP_LocalTileIndexFromWorldTileIndex(Vec2I32 world_tile_index); Vec2I32 PP_WorldTileIndexFromLocalTileIndex(Vec2I32 tile_chunk_index, Vec2I32 local_tile_index); Vec2I32 PP_TileChunkIndexFromWorldTileIndex(Vec2I32 world_tile_index); void PP_SetSnapshotTile(PP_Snapshot *ss, Vec2I32 world_tile_index, PP_TileKind tile_kind); //////////////////////////////////////////////////////////// //~ Snapshot lerp operations PP_Snapshot *PP_AcquireSnapshotFromLerp(PP_Client *client, PP_Snapshot *ss0, PP_Snapshot *ss1, f64 blend); //////////////////////////////////////////////////////////// //~ Snapshot sync operations void PP_SyncSnapshotEnts(PP_Snapshot *local_ss, PP_Snapshot *remote_ss, PP_EntKey remote_player, u32 sync_flags); //////////////////////////////////////////////////////////// //~ Snapshot encode operations void PP_EncodeSnapshot(BB_Writer *bw, PP_Client *receiver, PP_Snapshot *ss0, PP_Snapshot *ss1); //////////////////////////////////////////////////////////// //~ Snapshot decode operations void PP_DecodeSnapshot(BB_Reader *br, PP_Snapshot *ss);