#define SIM_CLIENT_NIL_HANDLE ((ClientHandle) { .gen = 0, .idx = 0 }) /* Absolute layers */ #define SIM_LAYER_FLOOR_DECALS (-300) #define SIM_LAYER_BULLETS (-200) #define SIM_LAYER_TRACERS (-100) #define SIM_LAYER_SHOULDERS (0) #define SIM_LAYER_WALLS (100) /* Relative layers */ #define SIM_LAYER_RELATIVE_DEFAULT (0) #define SIM_LAYER_RELATIVE_WEAPON (1) Struct(EntId) { UID uid; }; Struct(ClientHandle) { u32 idx; u32 gen; }; /* ========================== * * Startup * ========================== */ Struct(SimStartupReceipt) { i32 _; }; SimStartupReceipt sim_startup(void); /* ========================== * * Client store * ========================== */ 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; } ClientStore *sim_client_store_alloc(void); void sim_client_store_release(ClientStore *store); /* ========================== * * Client * ========================== */ 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) */ EntId 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 *sim_client_nil(void) { extern Readonly Client **_g_sim_client_nil; return *_g_sim_client_nil; } Inline b32 sim_client_handle_eq(ClientHandle a, ClientHandle b) { return a.gen == b.gen && a.idx == b.idx; } Client *sim_client_alloc(ClientStore *store); void sim_client_release(Client *client); Client *sim_client_from_channel_id(ClientStore *store, N_ChannelId channel_id); void sim_client_set_channel_id(Client *client, N_ChannelId channel_id); Client *sim_client_from_handle(ClientStore *store, ClientHandle handle); /* ========================== * * Snapshot * ========================== */ typedef i32 SyncFlag; enum { SIM_SYNC_FLAG_NOSYNC_PREDICTABLES = 1 << 0 }; typedef i32 ControlFlag; enum { SIM_CONTROL_FLAG_FIRE = 1 << 0, SIM_CONTROL_FLAG_FIRE_ALT = 1 << 1, /* Testing */ SIM_CONTROL_FLAG_DRAG = 1 << 2, SIM_CONTROL_FLAG_DELETE = 1 << 3, SIM_CONTROL_FLAG_CLEAR_ALL = 1 << 4, SIM_CONTROL_FLAG_SPAWN1_TEST = 1 << 5, SIM_CONTROL_FLAG_SPAWN2_TEST = 1 << 6, SIM_CONTROL_FLAG_SPAWN3_TEST = 1 << 7, SIM_CONTROL_FLAG_SPAWN4_TEST = 1 << 8, SIM_CONTROL_FLAG_WALLS_TEST = 1 << 9, SIM_CONTROL_FLAG_TILE_TEST = 1 << 10, SIM_CONTROL_FLAG_EXPLODE_TEST = 1 << 11, SIM_CONTROL_FLAG_TELEPORT_TEST = 1 << 12, }; Struct(ControlData) { V2 move; /* Movement direction vector (speed of 0 -> 1) */ V2 focus; /* Focus direction vector (where does the controller want to look) */ V2 dbg_cursor; /* Where is the user's cursor in the world (used for things like editing the world) */ u32 flags; }; typedef i32 CmdKind; enum { SIM_CMD_KIND_INVALID, SIM_CMD_KIND_CONTROL, SIM_CMD_KIND_CHAT }; typedef i32 TileKind; enum { SIM_TILE_KIND_NONE, SIM_TILE_KIND_WALL, NUM_SIM_TILE_KINDS }; StaticAssert(NUM_SIM_TILE_KINDS < 256); /* Tile kind must fit in 8 bits */ 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 */ EntId local_player; /* Id lookup */ struct EntBin *id_bins; u64 num_id_bins; /* Entities */ Arena *ents_arena; struct Ent *ents; u32 first_free_ent; u32 num_ents_allocated; u32 num_ents_reserved; }; Inline Snapshot *sim_snapshot_nil(void) { extern Readonly Snapshot **_g_sim_snapshot_nil; return *_g_sim_snapshot_nil; } /* Alloc */ Snapshot *sim_snapshot_alloc(Client *client, Snapshot *src, u64 tick); void sim_snapshot_release(Snapshot *sim_snapshot); void sim_snapshot_release_ticks_in_range(Client *client, u64 start, u64 end); /* Lookup */ Snapshot *sim_snapshot_from_tick(Client *client, u64 tick); Snapshot *sim_snapshot_from_closest_tick_lte(Client *client, u64 tick); Snapshot *sim_snapshot_from_closest_tick_gte(Client *client, u64 tick); /* Tile */ V2i32 sim_world_tile_index_from_pos(V2 pos); V2 sim_pos_from_world_tile_index(V2i32 world_tile_index); V2i32 sim_local_tile_index_from_world_tile_index(V2i32 world_tile_index); V2i32 sim_world_tile_index_from_local_tile_index(V2i32 tile_chunk_index, V2i32 local_tile_index); V2i32 sim_tile_chunk_index_from_world_tile_index(V2i32 world_tile_index); void sim_snapshot_set_tile(Snapshot *ss, V2i32 world_tile_index, TileKind tile_kind); /* Lerp */ Snapshot *sim_snapshot_alloc_from_lerp(Client *client, Snapshot *ss0, Snapshot *ss1, f64 blend); /* Sync */ void sim_snapshot_sync_ents(Snapshot *local_ss, Snapshot *remote_ss, EntId remote_player, u32 sync_flags); /* Encode / decode */ void sim_snapshot_encode(BB_Writer *bw, Client *receiver, Snapshot *ss0, Snapshot *ss1); void sim_snapshot_decode(BB_Reader *br, Snapshot *ss);