//////////////////////////////////////////////////////////// //~ Id types Struct(EntityId) { Uid uid; }; Struct(ClientHandle) { u32 idx; u32 gen; }; #define NilClientHandle ((ClientHandle) { .gen = 0, .idx = 0 }) //////////////////////////////////////////////////////////// //~ Client store types Struct(ClientLookupBin) { struct ClientHandle first; struct ClientHandle last; }; Struct(ClientStore) { b32 valid; Arena *arena; /* Client lookup */ ClientLookupBin *client_lookup_bins; u64 num_client_lookup_bins; /* Clients */ Arena *clients_arena; struct Client *clients; ClientHandle first_free_client; u64 num_clients_allocated; u64 num_clients_reserved; }; Inline ClientStore *sim_client_store_nil(void) { extern Readonly ClientStore **_g_sim_client_store_nil; return *_g_sim_client_store_nil; } //////////////////////////////////////////////////////////// //~ Client types Struct(SnapshotLookupBin) { struct Snapshot *first; struct Snapshot *last; }; Struct(Client) { b32 valid; ClientHandle handle; ClientStore *store; Arena *snapshots_arena; /* Round trip time of the client (if networked) */ i64 last_rtt_ns; N_ChannelId channel_id; u64 channel_hash; ClientHandle next_free; ClientHandle next_in_bin; ClientHandle prev_in_bin; /* The client's player entity id in the master sim (if relevant) */ EntityId 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 Snapshot *first_free_snapshot; /* Tick -> snapshot lookup */ u64 num_snapshot_lookup_bins; SnapshotLookupBin *snapshot_lookup_bins; }; Inline Client *NilClient(void) { extern Readonly Client **_g_sim_client_nil; return *_g_sim_client_nil; } Inline b32 EqClientHandle(ClientHandle a, ClientHandle b) { return a.gen == b.gen && a.idx == b.idx; } //////////////////////////////////////////////////////////// //~ Layer types /* Absolute layers */ #define Layer_FloorDecals (-300) #define Layer_Bullets (-200) #define Layer_Tracers (-100) #define Layer_Shoulders (0) #define Layer_Walls (100) /* Relative layers */ #define Layer_DefaultRelative (0) #define Layer_RelativeWeapon (1) //////////////////////////////////////////////////////////// //~ Control types Enum(ControlFlag) { ControlFlag_Fire = 1 << 0, ControlFlag_AltFire = 1 << 1, /* Testing */ ControlFlag_Drag = 1 << 2, ControlFlag_Delete = 1 << 3, ControlFlag_ClearAll = 1 << 4, ControlFlag_SpawnTest1 = 1 << 5, ControlFlag_SpawnTest2 = 1 << 6, ControlFlag_SpawnTest3 = 1 << 7, ControlFlag_SpawnTest4 = 1 << 8, ControlFlag_TestWalls = 1 << 9, ControlFlag_TestTiles = 1 << 10, ControlFlag_TestExplode = 1 << 11, ControlFlag_TestTeleport = 1 << 12, }; Struct(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) */ ControlFlag flags; }; Enum(CmdKind) { CmdKind_Invalid, CmdKind_Control, CmdKind_Chat }; //////////////////////////////////////////////////////////// //~ Tile types Enum(TileKind) { TileKind_None, TileKind_Wall, TileKind_Count }; StaticAssert(TileKind_Count < 256); /* Tile kind must fit in 8 bits */ //////////////////////////////////////////////////////////// //~ Snapshot types Enum(SyncFlag) { SyncFlag_NoSyncPredictables = 1 << 0 }; Struct(Snapshot) { b32 valid; u64 tick; Client *client; Snapshot *next_free; Snapshot *next_in_bin; 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 id of the receiver's player in the snapshot */ EntityId local_player; /* Id lookup */ struct EntBin *id_bins; u64 num_id_bins; /* Entities */ Arena *ents_arena; struct Entity *ents; u32 first_free_ent; u32 num_ents_allocated; u32 num_ents_reserved; }; Inline Snapshot *NilSnapshot(void) { extern Readonly Snapshot **_g_sim_snapshot_nil; return *_g_sim_snapshot_nil; } //////////////////////////////////////////////////////////// //~ State types #define ClientLookupBinsCount 127 #define TickLookupBinsCount 127 #define IdLookupBinsCount 4096 Struct(SharedSimCtx) { Arena *nil_arena; ClientStore *nil_client_store; Client *nil_client; Snapshot *nil_snapshot; struct Entity *nil_ent; }; extern SharedSimCtx shared_sim_ctx; /* Accessed via `sim_client_store_nil()` */ extern Readonly ClientStore **_g_sim_client_store_nil; /* Accessed via `NilClient()` */ extern Readonly Client **_g_sim_client_nil; /* Accessed via `NilSnapshot()` */ extern Readonly Snapshot **_g_sim_snapshot_nil; extern Readonly struct Entity **_g_sim_ent_nil; //////////////////////////////////////////////////////////// //~ Startup void StartupSim(void); //////////////////////////////////////////////////////////// //~ Client store acquire operations ClientStore *AcquireClientStore(void); void ReleaseClientStore(ClientStore *store); //////////////////////////////////////////////////////////// //~ Client acquire operations Client *AcquireClient(ClientStore *store); void ReleaseClient(Client *client); //////////////////////////////////////////////////////////// //~ Client lookup operations u64 ClientChannelHashFromChannelId(N_ChannelId channel_id); void SetClientChannelId(Client *client, N_ChannelId channel_id); Client *ClientFromChannelId(ClientStore *store, N_ChannelId channel_id); Client *ClientFromHandle(ClientStore *store, ClientHandle handle); //////////////////////////////////////////////////////////// //~ Snapshot acquire operations Snapshot *AcquireSnapshot(Client *client, Snapshot *src, u64 tick); void ReleaseSnapshot(Snapshot *ss); void ReleaseSnapshotsInRange(Client *client, u64 start, u64 end); //////////////////////////////////////////////////////////// //~ Snapshot lookup operations Snapshot *SnapshotFromTick(Client *client, u64 tick); Snapshot *SnapshotFromClosestTickLte(Client *client, u64 tick); Snapshot *SnapshotFromClosestTickGte(Client *client, u64 tick); //////////////////////////////////////////////////////////// //~ Tile operations Vec2I32 WorldTileIndexFromPos(Vec2 pos); Vec2 PosFromWorldTileIndex(Vec2I32 world_tile_index); Vec2I32 LocalTileIndexFromWorldTileIndex(Vec2I32 world_tile_index); Vec2I32 WorldTileIndexFromLocalTileIndex(Vec2I32 tile_chunk_index, Vec2I32 local_tile_index); Vec2I32 TileChunkIndexFromWorldTileIndex(Vec2I32 world_tile_index); void SetSnapshotTile(Snapshot *ss, Vec2I32 world_tile_index, TileKind tile_kind); //////////////////////////////////////////////////////////// //~ Snapshot lerp operations Snapshot *AcquireSnapshotFromLerp(Client *client, Snapshot *ss0, Snapshot *ss1, f64 blend); //////////////////////////////////////////////////////////// //~ Snapshot sync operations void SyncSnapshotEntities(Snapshot *local_ss, Snapshot *remote_ss, EntityId remote_player, u32 sync_flags); //////////////////////////////////////////////////////////// //~ Snapshot encode operations void EncodeSnapshot(BB_Writer *bw, Client *receiver, Snapshot *ss0, Snapshot *ss1); //////////////////////////////////////////////////////////// //~ Snapshot decode operations void DecodeSnapshot(BB_Reader *br, Snapshot *ss);