begin structuring sim for networking

This commit is contained in:
jacob 2026-01-12 17:44:10 -06:00
parent 54b1759afe
commit 25a8a3c39a
16 changed files with 898 additions and 493 deletions

View File

@ -750,7 +750,7 @@
#endif #endif
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Basic mixing/hashing //~ Basic mixing
// Based on Jon Maiga's "mx3" // Based on Jon Maiga's "mx3"
// https://jonkagstrom.com/mx3/mx3_rev2.html // https://jonkagstrom.com/mx3/mx3_rev2.html
@ -768,23 +768,6 @@ Inline u64 MixU64s(u64 seed_a, u64 seed_b)
return MixU64((seed_a * 3) + seed_b); return MixU64((seed_a * 3) + seed_b);
} }
#if IsLanguageC
#define Fnv64Basis 0xCBF29CE484222325
// FNV-1a parameters for different hash sizes:
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters
Inline u64 HashFnv64(u64 seed, String str)
{
u64 hash = seed;
for (u64 i = 0; i < str.len; ++i)
{
hash ^= (u8)str.text[i];
hash *= 0x100000001B3;
}
return hash;
}
#endif
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ @hookdecl Core api //~ @hookdecl Core api

View File

@ -58,7 +58,7 @@ void BootstrapCmdline(void)
{ {
CommandlineArg arg = tmp_args[i]; CommandlineArg arg = tmp_args[i];
CommandlineArgNode *n = PushStruct(perm, CommandlineArgNode); CommandlineArgNode *n = PushStruct(perm, CommandlineArgNode);
u64 hash = HashFnv64(Fnv64Basis, arg.name); u64 hash = HashString(arg.name);
u64 bin_idx = hash % countof(Base.cmdline.arg_bins); u64 bin_idx = hash % countof(Base.cmdline.arg_bins);
n->arg = arg; n->arg = arg;
n->hash = hash; n->hash = hash;
@ -85,7 +85,7 @@ String StringFromCommandlineIdx(i32 idx)
CommandlineArg CommandlineArgFromName(String name) CommandlineArg CommandlineArgFromName(String name)
{ {
CommandlineArg result = Zi; CommandlineArg result = Zi;
u64 hash = HashFnv64(Fnv64Basis, name); u64 hash = HashString(name);
for (CommandlineArgNode *n = Base.cmdline.arg_bins[hash % countof(Base.cmdline.arg_bins)]; n; n = n->next) for (CommandlineArgNode *n = Base.cmdline.arg_bins[hash % countof(Base.cmdline.arg_bins)]; n; n = n->next)
{ {
if (n->hash == hash && MatchString(n->arg.name, name)) if (n->hash == hash && MatchString(n->arg.name, name))

View File

@ -19,6 +19,7 @@ void BootstrapResources(u64 archive_strings_count, String *archive_strings)
u64 num_entries = BB_ReadUBits(&br, 64); u64 num_entries = BB_ReadUBits(&br, 64);
for (u64 i = 0; i < num_entries; ++i) for (u64 i = 0; i < num_entries; ++i)
{ {
u64 store_hash = BB_ReadUBits(&br, 64);
u64 name_start = BB_ReadUBits(&br, 64); u64 name_start = BB_ReadUBits(&br, 64);
u64 name_len = BB_ReadUBits(&br, 64); u64 name_len = BB_ReadUBits(&br, 64);
u64 data_start = BB_ReadUBits(&br, 64); u64 data_start = BB_ReadUBits(&br, 64);
@ -27,7 +28,7 @@ void BootstrapResources(u64 archive_strings_count, String *archive_strings)
ResourceEntry *entry = PushStruct(perm, ResourceEntry); ResourceEntry *entry = PushStruct(perm, ResourceEntry);
entry->name = STRING(name_len, archive.text + name_start); entry->name = STRING(name_len, archive.text + name_start);
entry->data = STRING(data_len, archive.text + data_start); entry->data = STRING(data_len, archive.text + data_start);
entry->hash = HashFnv64(Fnv64Basis, entry->name); entry->hash = HashStringEx(store_hash, entry->name);
ResourceEntryBin *bin = &Base.resource.bins[entry->hash % countof(Base.resource.bins)]; ResourceEntryBin *bin = &Base.resource.bins[entry->hash % countof(Base.resource.bins)];
SllQueuePushN(bin->first, bin->last, entry, next_in_bin); SllQueuePushN(bin->first, bin->last, entry, next_in_bin);
@ -50,7 +51,7 @@ b32 IsResourceNil(ResourceKey resource)
ResourceKey ResourceKeyFromStore(ResourceStore *store, String name) ResourceKey ResourceKeyFromStore(ResourceStore *store, String name)
{ {
ResourceKey result = Zi; ResourceKey result = Zi;
result.v = HashFnv64(store->v, name); result.v = HashStringEx(store->v, name);
return result; return result;
} }

View File

@ -502,14 +502,21 @@ String PathFromString(Arena *arena, String str, u8 path_delimiter)
u64 HashStringEx(u64 seed, String str) u64 HashStringEx(u64 seed, String str)
{ {
u64 result = HashFnv64(seed, str); // FNV-1a parameters for different hash sizes:
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters
u64 result = seed + 0xCBF29CE484222325;
for (u64 i = 0; i < str.len; ++i)
{
result ^= (u8)str.text[i];
result *= 0x100000001B3;
}
result = MixU64(result); result = MixU64(result);
return result; return result;
} }
u64 HashString(String str) u64 HashString(String str)
{ {
return HashStringEx(Fnv64Basis, str); return HashStringEx(0, str);
} }
u64 HashF_(String fmt, ...) u64 HashF_(String fmt, ...)

View File

@ -112,7 +112,7 @@ i64 TimeNs(void)
void TrueRand(String buffer) void TrueRand(String buffer)
{ {
BCryptGenRandom(BCRYPT_RNG_ALG_HANDLE, (u8 *)buffer.text, buffer.len, 0); BCryptGenRandom(BCRYPT_RNG_ALG_HANDLE, buffer.text, buffer.len, 0);
} }
CpuTopologyInfo GetCpuTopologyInfo(void) CpuTopologyInfo GetCpuTopologyInfo(void)

View File

@ -111,6 +111,7 @@ EmbedObj Embed(String store_name, String dir_path)
EmbedObj result = Zi; EmbedObj result = Zi;
// Generate resource archive contents // Generate resource archive contents
u64 store_hash = HashString(store_name);
String arc_contents = Zi; String arc_contents = Zi;
{ {
StringList files = Zi; StringList files = Zi;
@ -136,7 +137,6 @@ EmbedObj Embed(String store_name, String dir_path)
entry_name.len -= dir_path.len + 1; entry_name.len -= dir_path.len + 1;
entry_name.text += dir_path.len + 1; entry_name.text += dir_path.len + 1;
} }
entry_name = StringF(perm, "%F/%F", FmtString(store_name), FmtString(entry_name));
for (u64 i = 0; i < entry_name.len; ++i) for (u64 i = 0; i < entry_name.len; ++i)
{ {
if (entry_name.text[i] == '\\') if (entry_name.text[i] == '\\')
@ -165,6 +165,7 @@ EmbedObj Embed(String store_name, String dir_path)
// Reserve entries space // Reserve entries space
u64 entry_size = 0 u64 entry_size = 0
+ 8 // Store hash
+ 8 // Name start + 8 // Name start
+ 8 // Name end + 8 // Name end
+ 8 // Data start + 8 // Data start
@ -196,6 +197,7 @@ EmbedObj Embed(String store_name, String dir_path)
u64 data_len = BB_GetNumBytesWritten(&bw) - data_start; u64 data_len = BB_GetNumBytesWritten(&bw) - data_start;
// Write entry // Write entry
BB_WriteUBits(&entries_bw, store_hash, 64);
BB_WriteUBits(&entries_bw, name_start, 64); BB_WriteUBits(&entries_bw, name_start, 64);
BB_WriteUBits(&entries_bw, name_len, 64); BB_WriteUBits(&entries_bw, name_len, 64);
BB_WriteUBits(&entries_bw, data_start, 64); BB_WriteUBits(&entries_bw, data_start, 64);
@ -457,6 +459,7 @@ void BuildEntryPoint(WaveLaneCtx *lane)
String shader_store_name = Lit("ShadersStore"); String shader_store_name = Lit("ShadersStore");
String c_out_file = F_GetFull(perm, StringF(perm, "%F_gen.c", FmtString(cmdline.leaf_layer_name))); String c_out_file = F_GetFull(perm, StringF(perm, "%F_gen.c", FmtString(cmdline.leaf_layer_name)));
String gpu_out_file = F_GetFull(perm, StringF(perm, "%F_gen.g", FmtString(cmdline.leaf_layer_name))); String gpu_out_file = F_GetFull(perm, StringF(perm, "%F_gen.g", FmtString(cmdline.leaf_layer_name)));
u64 shader_store_hash = HashString(shader_store_name);
if (lane->idx == 0 && GetBuildStatus() == 0) if (lane->idx == 0 && GetBuildStatus() == 0)
{ {
@ -505,8 +508,8 @@ void BuildEntryPoint(WaveLaneCtx *lane)
String full = F_GetFullCrossPlatform(perm, StringF(perm, "%F/%F", FmtString(token_parent_dir), FmtString(arg_dir))); String full = F_GetFullCrossPlatform(perm, StringF(perm, "%F/%F", FmtString(token_parent_dir), FmtString(arg_dir)));
if (F_IsDir(full)) if (F_IsDir(full))
{ {
u64 hash = HashFnv64(Fnv64Basis, StringF(perm, "%F/", FmtString(store_name))); u64 store_hash = HashString(store_name);
String line = StringF(perm, "ResourceStore %F = { 0x%F };", FmtString(store_name), FmtHex(hash)); String line = StringF(perm, "ResourceStore %F = { 0x%F };", FmtString(store_name), FmtHex(store_hash));
PushStringToList(perm, &c_store_lines, line); PushStringToList(perm, &c_store_lines, line);
} }
else else
@ -531,8 +534,8 @@ void BuildEntryPoint(WaveLaneCtx *lane)
: kind == M_EntryKind_ComputeShader ? Lit("ComputeShader") : kind == M_EntryKind_ComputeShader ? Lit("ComputeShader")
: Lit(""); : Lit("");
String shader_name = arg0_tok->s; String shader_name = arg0_tok->s;
u64 hash = HashFnv64(Fnv64Basis, StringF(perm, "%F/%F", FmtString(shader_store_name), FmtString(shader_name))); u64 shader_resource_hash = HashStringEx(shader_store_hash, shader_name);
String line = StringF(perm, "%F %F = { 0x%F };", FmtString(shader_type), FmtString(shader_name), FmtHex(hash)); String line = StringF(perm, "%F %F = { 0x%F };", FmtString(shader_type), FmtString(shader_name), FmtHex(shader_resource_hash));
PushStringToList(perm, &c_shader_lines, line); PushStringToList(perm, &c_shader_lines, line);
} }
else else

View File

@ -97,7 +97,7 @@ M_TokenFileList M_TokensFromSrcDirs(Arena *arena, StringList src_dirs)
if (StringEndsWith(file_name, Lit(".lay"))) if (StringEndsWith(file_name, Lit(".lay")))
{ {
String tmp_full = F_GetFull(scratch.arena, file_name); String tmp_full = F_GetFull(scratch.arena, file_name);
u64 hash = HashFnv64(Fnv64Basis, tmp_full); u64 hash = HashString(tmp_full);
if (DictValueFromHash(dedup_dict, hash) == 0) if (DictValueFromHash(dedup_dict, hash) == 0)
{ {
SetDictValue(scratch.arena, dedup_dict, hash, 1); SetDictValue(scratch.arena, dedup_dict, hash, 1);
@ -255,7 +255,7 @@ M_LayerList M_LayersFromTokenFiles(Arena *arena, M_TokenFileList lexed)
for (u64 i = 0; i < countof(M_entry_kind_rules); ++i) for (u64 i = 0; i < countof(M_entry_kind_rules); ++i)
{ {
char *rule_cstr = M_entry_kind_rules[i]; char *rule_cstr = M_entry_kind_rules[i];
u64 rule_hash = HashFnv64(Fnv64Basis, StringFromCstrNoLimit(rule_cstr)); u64 rule_hash = HashString(StringFromCstrNoLimit(rule_cstr));
SetDictValue(scratch.arena, rules_dict, rule_hash, i + 1); SetDictValue(scratch.arena, rules_dict, rule_hash, i + 1);
} }
@ -280,7 +280,7 @@ M_LayerList M_LayersFromTokenFiles(Arena *arena, M_TokenFileList lexed)
{ {
M_Entry *entry = M_PushEntry(arena, layer, M_EntryKind_Unknown, entry_token, args_count, arg_tokens); M_Entry *entry = M_PushEntry(arena, layer, M_EntryKind_Unknown, entry_token, args_count, arg_tokens);
{ {
u64 entry_hash = HashFnv64(Fnv64Basis, entry_token->s); u64 entry_hash = HashString(entry_token->s);
u64 kind_plus_1 = DictValueFromHash(rules_dict, entry_hash); u64 kind_plus_1 = DictValueFromHash(rules_dict, entry_hash);
if (kind_plus_1 > 0) if (kind_plus_1 > 0)
{ {
@ -390,7 +390,7 @@ M_Layer M_FlattenEntries(Arena *arena, M_LayerList unflattened, StringList start
String name = name_entry->arg_tokens[0]->s; String name = name_entry->arg_tokens[0]->s;
u64 name_hash = HashFnv64(Fnv64Basis, name); u64 name_hash = HashString(name);
DictEntry *ptr_dict_entry = EnsureDictEntry(scratch.arena, layer_ptr_to_state, (u64)layer); DictEntry *ptr_dict_entry = EnsureDictEntry(scratch.arena, layer_ptr_to_state, (u64)layer);
DictEntry *name_dict_entry = EnsureDictEntry(scratch.arena, layer_name_to_state, name_hash); DictEntry *name_dict_entry = EnsureDictEntry(scratch.arena, layer_name_to_state, name_hash);
@ -418,7 +418,7 @@ M_Layer M_FlattenEntries(Arena *arena, M_LayerList unflattened, StringList start
for (StringListNode *sln = starting_layer_names.first; sln; sln = sln->next) for (StringListNode *sln = starting_layer_names.first; sln; sln = sln->next)
{ {
String name = sln->s; String name = sln->s;
u64 name_hash = HashFnv64(Fnv64Basis, name); u64 name_hash = HashString(name);
IterState *state = (IterState *)DictValueOrNilFromHash(layer_name_to_state, name_hash, (u64)&NilIterState); IterState *state = (IterState *)DictValueOrNilFromHash(layer_name_to_state, name_hash, (u64)&NilIterState);
M_Layer *layer = state->layer; M_Layer *layer = state->layer;
if (layer->valid) if (layer->valid)
@ -497,7 +497,7 @@ M_Layer M_FlattenEntries(Arena *arena, M_LayerList unflattened, StringList start
if (should_include) if (should_include)
{ {
String downstream_layer_name = downstream_layer_token->s; String downstream_layer_name = downstream_layer_token->s;
u64 hash = HashFnv64(Fnv64Basis, downstream_layer_name); u64 hash = HashString(downstream_layer_name);
IterState *downstream_layer_state = (IterState *)DictValueOrNilFromHash(layer_name_to_state, hash, (u64)&NilIterState); IterState *downstream_layer_state = (IterState *)DictValueOrNilFromHash(layer_name_to_state, hash, (u64)&NilIterState);
M_Layer *downstream_layer = downstream_layer_state->layer; M_Layer *downstream_layer = downstream_layer_state->layer;
if (downstream_layer->valid) if (downstream_layer->valid)
@ -538,7 +538,7 @@ M_Layer M_FlattenEntries(Arena *arena, M_LayerList unflattened, StringList start
if (dep_token->valid) if (dep_token->valid)
{ {
String dep_name = dep_token->s; String dep_name = dep_token->s;
u64 hash = HashFnv64(Fnv64Basis, dep_name); u64 hash = HashString(dep_name);
IterState *dep_layer_state = (IterState *)DictValueOrNilFromHash(layer_name_to_state, hash, (u64)&NilIterState); IterState *dep_layer_state = (IterState *)DictValueOrNilFromHash(layer_name_to_state, hash, (u64)&NilIterState);
M_Layer *dep_layer = dep_layer_state->layer; M_Layer *dep_layer = dep_layer_state->layer;
if (dep_layer->valid) if (dep_layer->valid)

View File

@ -58,7 +58,7 @@ void N_ReleaseHost(N_Host *host)
u64 N_HashFromAddress(PLT_Address address) u64 N_HashFromAddress(PLT_Address address)
{ {
return HashFnv64(Fnv64Basis, StringFromStruct(&address)); return HashString(StringFromStruct(&address));
} }
N_Channel *N_ChannelFromAddress(N_Host *host, PLT_Address address) N_Channel *N_ChannelFromAddress(N_Host *host, PLT_Address address)
@ -227,9 +227,9 @@ void N_ReleaseChannel(N_Channel *channel)
u64 N_HashFromMsg(N_ChannelId channel_id, u64 msg_id) u64 N_HashFromMsg(N_ChannelId channel_id, u64 msg_id)
{ {
u64 result = Fnv64Basis; u64 result = 0;
result = HashFnv64(result, StringFromStruct(&channel_id)); result = HashStringEx(result, StringFromStruct(&channel_id));
result = HashFnv64(result, StringFromStruct(&msg_id)); result = HashStringEx(result, StringFromStruct(&msg_id));
return result; return result;
} }

View File

@ -6,6 +6,10 @@ Readonly P_Ent P_NilEnt = {
.look = { 0, -1 }, .look = { 0, -1 },
}; };
Readonly P_Frame P_NilFrame = {
0
};
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Bootstrap //~ Bootstrap
@ -37,14 +41,19 @@ b32 P_IsEntNil(P_Ent *ent)
return ent == 0 || ent == &P_NilEnt; return ent == 0 || ent == &P_NilEnt;
} }
b32 P_MatchKey(P_Key a, P_Key b) b32 P_IsFrameNil(P_Frame *frame)
{ {
return a.v == b.v; return frame == 0 || frame == &P_NilFrame;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Key helpers //~ Key helpers
b32 P_MatchKey(P_Key a, P_Key b)
{
return a.v == b.v;
}
P_Key P_RandKey(void) P_Key P_RandKey(void)
{ {
// TODO: Don't use true randomness for entity keys. It's overkill & non-deterministic. // TODO: Don't use true randomness for entity keys. It's overkill & non-deterministic.
@ -73,91 +82,6 @@ String P_NameFromTileKind(P_TileKind kind)
return result; return result;
} }
////////////////////////////////////////////////////////////
//~ Delta helpers
void P_UpdateWorldFromDelta(Arena *arena, P_World *world, P_Delta *delta)
{
// FIXME: Bounds check tile deltas
if (0)
{
}
//- Reset
if (delta->kind == P_DeltaKind_Reset)
{
// FIXME: Free list entities
world->ents_count = 0;
world->first_ent = 0;
world->last_ent = 0;
ZeroStructs(world->tiles, P_TilesCount);
ZeroStructs(world->ent_bins, world->ent_bins_count);
}
//- Raw ent
if (delta->kind == P_DeltaKind_RawEnt)
{
P_Key key = delta->ent.key;
P_Ent *ent = P_EntFromKey(world, key);
if (!ent->valid)
{
// FIXME: Use ent free list
ent = PushStructNoZero(arena, P_Ent);
*ent = P_NilEnt;
ent->key = key;
P_EntBin *bin = &world->ent_bins[ent->key.v % world->ent_bins_count];
DllQueuePushNP(bin->first, bin->last, ent, next_in_bin, prev_in_bin);
DllQueuePush(world->first_ent, world->last_ent, ent);
++world->ents_count;
}
P_Ent *old_next = ent->next;
P_Ent *old_prev = ent->prev;
P_Ent *old_next_in_bin = ent->next_in_bin;
P_Ent *old_prev_in_bin = ent->prev_in_bin;
{
P_Ent *src = &delta->ent;
*ent = *src;
}
ent->next = old_next;
ent->prev = old_prev;
ent->next_in_bin = old_next_in_bin;
ent->prev_in_bin = old_prev_in_bin;
ent->valid = 1;
}
//- Raw tiles
else if (delta->kind == P_DeltaKind_RawTiles)
{
Rng2I32 range = delta->tile_range;
for (i32 tile_y = range.p0.y; tile_y < range.p1.y; ++tile_y)
{
i32 src_tile_y = tile_y - range.p0.y;
for (i32 tile_x = range.p0.x; tile_x < range.p1.x; ++tile_x)
{
i32 src_tile_x = tile_x - range.p0.x;
Vec2 src_tile_pos = VEC2(src_tile_x, src_tile_y);
i32 src_tile_idx = P_TileIdxFromTilePos(src_tile_pos);
u8 src_tile = delta->raw_tiles[src_tile_idx];
Vec2 tile_pos = VEC2(tile_x, tile_y);
i32 tile_idx = P_TileIdxFromTilePos(tile_pos);
world->tiles[tile_idx] = src_tile;
}
}
}
//- Tile range
else if (delta->kind == P_DeltaKind_Tile)
{
P_TileKind tile = delta->tile_kind;
Rng2I32 range = delta->tile_range;
for (i32 tile_y = range.p0.y; tile_y < range.p1.y; ++tile_y)
{
for (i32 tile_x = range.p0.x; tile_x < range.p1.x; ++tile_x)
{
Vec2 tile_pos = VEC2(tile_x, tile_y);
i32 tile_idx = P_TileIdxFromTilePos(tile_pos);
world->tiles[tile_idx] = (u8)tile;
}
}
}
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Shape helpers //~ Shape helpers
@ -1106,12 +1030,14 @@ Vec2 P_EdgePointFromShape(P_Shape shape, Vec2 dir)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Lookup helpers //~ Lookup helpers
P_Ent *P_EntFromKeyEx(P_World *world, P_Key key, i64 tick) P_Ent *P_EntFromKey(P_Frame *frame, P_Key key)
{ {
P_Ent *result = &P_NilEnt; P_Ent *result = &P_NilEnt;
if (!P_IsKeyNil(key) && world->ent_bins_count > 0) P_World *world = frame->world;
if (!P_IsKeyNil(key) && frame->tick > 0 && frame->ents_count > 0 && frame->ent_bins_count > 0)
{ {
P_EntBin *bin = &world->ent_bins[key.v % world->ent_bins_count]; i64 tick = frame->tick;
P_EntBin *bin = &frame->ent_bins[key.v % frame->ent_bins_count];
for (P_Ent *e = bin->first; e; e = e->next_in_bin) for (P_Ent *e = bin->first; e; e = e->next_in_bin)
{ {
if (e->key.v == key.v) if (e->key.v == key.v)
@ -1124,20 +1050,15 @@ P_Ent *P_EntFromKeyEx(P_World *world, P_Key key, i64 tick)
return result; return result;
} }
P_Ent *P_EntFromKey(P_World *world, P_Key key)
{
return P_EntFromKeyEx(world, key, 0);
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Iteration helpers //~ Iteration helpers
P_Ent *P_FirstEnt(P_World *world) P_Ent *P_FirstEnt(P_Frame *frame)
{ {
P_Ent *result = &P_NilEnt; P_Ent *result = &P_NilEnt;
if (world->first_ent) if (frame->first_ent)
{ {
result = world->first_ent; result = frame->first_ent;
} }
return result; return result;
} }
@ -1159,51 +1080,10 @@ P_Ent *P_PushTempEnt(Arena *arena, P_EntList *list)
++list->count; ++list->count;
P_Ent *ent = &n->ent; P_Ent *ent = &n->ent;
*ent = P_NilEnt; *ent = P_NilEnt;
ent->valid = 1;
ent->exists = 1; ent->exists = 1;
return ent; return ent;
} }
void P_SpawnEntsFromList(Arena *arena, P_World *world, P_EntList ents)
{
for (P_EntListNode *n = ents.first; n; n = n->next)
{
P_Ent *src = &n->ent;
P_Key key = src->key;
if (!P_IsKeyNil(src->key))
{
P_EntBin *bin = &world->ent_bins[key.v % world->ent_bins_count];
P_Ent *dst = bin->first;
for (; dst; dst = dst->next_in_bin)
{
if (P_MatchKey(dst->key, key))
{
break;
}
}
if (!dst)
{
// FIXME: Use free list
dst = PushStructNoZero(arena, P_Ent);
DllQueuePush(world->first_ent, world->last_ent, dst);
DllQueuePushNP(bin->first, bin->last, dst, next_in_bin, prev_in_bin);
}
P_Ent *old_next = dst->next;
P_Ent *old_prev = dst->prev;
P_Ent *old_next_in_bin = dst->next_in_bin;
P_Ent *old_prev_in_bin = dst->prev_in_bin;
{
*dst = *src;
}
dst->next = old_next;
dst->prev = old_prev;
dst->next_in_bin = old_next_in_bin;
dst->prev_in_bin = old_prev_in_bin;
++world->ents_count;
}
}
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Debug draw //~ Debug draw
@ -1267,3 +1147,263 @@ void P_DebugDrawShape(P_Shape shape, Vec4 srgb)
P_tl.debug_draw_nodes_count += 1; P_tl.debug_draw_nodes_count += 1;
} }
} }
////////////////////////////////////////////////////////////
//~ World
P_World *P_AcquireWorld(void)
{
P_World *world = 0;
{
Arena *arena = AcquireArena(Gibi(64));
world = PushStruct(arena, P_World);
world->arena = arena;
}
world->frames_arena = AcquireArena(Gibi(64));
world->statics_arena = AcquireArena(Gibi(64));
world->first_frame = &P_NilFrame;
world->last_frame = &P_NilFrame;
world->frame_bins_count = Kibi(1);
world->frame_bins = PushStructs(world->arena, P_FrameBin, world->frame_bins_count);
// TODO
world->tiles = PushStructs(world->arena, u8, P_TilesCount);
TrueRand(StringFromStruct(&world->seed));
return world;
}
void P_UpdateWorldFromSnapshots(P_World *world, P_SnapshotList snapshots)
{
b32 tiles_dirty = 0;
for (P_SnapshotNode *n = snapshots.first; n; n = n->next)
{
P_Snapshot *snapshot = &n->snapshot;
// FIXME: Process intermediate ticks
if (snapshot->tick > world->last_frame->tick)
{
P_Frame *src_frame = P_FrameFromTick(world, snapshot->src_tick);
P_Frame *frame = P_PushFrame(world, src_frame, snapshot->tick);
world->seed = snapshot->world_seed;
frame->time_ns = snapshot->time_ns;
if (frame->ents_count == 0)
{
for (P_DeltaNode *dn = snapshot->first_delta_node; dn; dn = dn->next)
{
P_Delta *delta = &dn->delta;
// FIXME: Bounds check tile deltas
if (0)
{
}
//- Reset
else if (delta->kind == P_DeltaKind_Reset)
{
// FIXME: Free list entities
frame->ents_count = 0;
frame->first_ent = 0;
frame->last_ent = 0;
ZeroStructs(world->tiles, P_TilesCount);
ZeroStructs(frame->ent_bins, frame->ent_bins_count);
tiles_dirty = 1;
}
//- Raw ent
else if (delta->kind == P_DeltaKind_RawEnt)
{
P_Ent tmp_ent = delta->ent;
P_EntListNode tmp_ent_node = Zi;
tmp_ent_node.ent = tmp_ent;
P_EntList ent_list = Zi;
ent_list.first = &tmp_ent_node;
ent_list.last = &tmp_ent_node;
P_SpawnEntsFromList(frame, ent_list);
}
//- Raw tiles
else if (delta->kind == P_DeltaKind_RawTiles)
{
Rng2I32 range = delta->tile_range;
for (i32 tile_y = range.p0.y; tile_y < range.p1.y; ++tile_y)
{
i32 src_tile_y = tile_y - range.p0.y;
for (i32 tile_x = range.p0.x; tile_x < range.p1.x; ++tile_x)
{
i32 src_tile_x = tile_x - range.p0.x;
Vec2 src_tile_pos = VEC2(src_tile_x, src_tile_y);
i32 src_tile_idx = P_TileIdxFromTilePos(src_tile_pos);
u8 src_tile = delta->raw_tiles[src_tile_idx];
Vec2 tile_pos = VEC2(tile_x, tile_y);
i32 tile_idx = P_TileIdxFromTilePos(tile_pos);
world->tiles[tile_idx] = src_tile;
}
}
tiles_dirty = 1;
}
//- Tile range
else if (delta->kind == P_DeltaKind_Tile)
{
P_TileKind tile = delta->tile_kind;
Rng2I32 range = delta->tile_range;
for (i32 tile_y = range.p0.y; tile_y < range.p1.y; ++tile_y)
{
for (i32 tile_x = range.p0.x; tile_x < range.p1.x; ++tile_x)
{
Vec2 tile_pos = VEC2(tile_x, tile_y);
i32 tile_idx = P_TileIdxFromTilePos(tile_pos);
world->tiles[tile_idx] = (u8)tile;
}
}
tiles_dirty = 1;
}
}
// FIXME: Real prune
// for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
// {
// ent->exists = 0;
// }
}
}
}
// TODO: Rehash statics
// if (tiles_dirty)
// {
// }
}
void P_SpawnEntsFromList(P_Frame *frame, P_EntList ents)
{
P_World *world = frame->world;
for (P_EntListNode *n = ents.first; n; n = n->next)
{
P_Ent *src = &n->ent;
P_Key key = src->key;
if (!P_IsKeyNil(src->key))
{
P_EntBin *bin = &frame->ent_bins[key.v % frame->ent_bins_count];
P_Ent *dst = bin->first;
for (; dst; dst = dst->next_in_bin)
{
if (dst->key.v == key.v)
{
break;
}
}
if (!dst)
{
// FIXME: Use free list
dst = PushStructNoZero(world->arena, P_Ent);
DllQueuePush(frame->first_ent, frame->last_ent, dst);
DllQueuePushNP(bin->first, bin->last, dst, next_in_bin, prev_in_bin);
}
P_Ent *old_next = dst->next;
P_Ent *old_prev = dst->prev;
P_Ent *old_next_in_bin = dst->next_in_bin;
P_Ent *old_prev_in_bin = dst->prev_in_bin;
{
*dst = *src;
}
dst->next = old_next;
dst->prev = old_prev;
dst->next_in_bin = old_next_in_bin;
dst->prev_in_bin = old_prev_in_bin;
++frame->ents_count;
}
}
}
P_Frame *P_FrameFromTick(P_World *world, i64 tick)
{
P_Frame *result = &P_NilFrame;
if (world->frame_bins_count > 0)
{
u64 hash = MixU64(tick);
P_FrameBin *bin = &world->frame_bins[hash % world->frame_bins_count];
for (P_Frame *frame = bin->first; frame; frame = frame->next_in_bin)
{
if (frame->tick == tick)
{
result = frame;
break;
}
}
}
return result;
}
void P_ClearFrames(P_World *world, i64 tick_min, i64 tick_max)
{
// TODO: Fast path for when range encompasses all frames in the world
// TODO: Don't need linear search
P_Frame *frame = world->first_frame;
while (!P_IsFrameNil(frame))
{
P_Frame *next_frame = frame->next;
if (frame->tick >= tick_min && frame->tick <= tick_max)
{
// FIXME: Freelist ents
// FIXME: Freelist frame
u64 hash = MixU64(frame->tick);
P_FrameBin *bin = &world->frame_bins[hash % world->frame_bins_count];
DllQueueRemoveNPZ(&P_NilFrame, world->first_frame, world->last_frame, frame, next, prev);
DllQueueRemoveNPZ(0, bin->first, bin->last, frame, next_in_bin, prev_in_bin);
}
else
{
break;
}
frame = next_frame;
}
}
P_Frame *P_PushFrame(P_World *world, P_Frame *src_frame, i64 tick)
{
Assert(!(src_frame->world == world && tick <= src_frame->tick)); // Can't read from tick that is being overwritten by new tick
P_ClearFrames(world, tick, I64Max);
P_Frame *frame = PushStruct(world->frames_arena, P_Frame);
{
// FIXME: Pull from freelist
frame->world = world;
frame->tick = tick;
frame->time_ns = src_frame->time_ns;
frame->ent_bins_count = Kibi(16);
frame->ent_bins = PushStructs(world->frames_arena, P_EntBin, frame->ent_bins_count);
u64 hash = MixU64(tick);
P_FrameBin *bin = &world->frame_bins[hash % world->frame_bins_count];
DllQueuePushNPZ(&P_NilFrame, world->first_frame, world->last_frame, frame, next, prev);
DllQueuePushNPZ(0, bin->first, bin->last, frame, next_in_bin, prev_in_bin);
}
for (P_Ent *src = P_FirstEnt(src_frame); !P_IsEntNil(src); src = P_NextEnt(src))
{
// FIXME: Pull from freelist
P_Ent *dst = PushStruct(world->frames_arena, P_Ent);
*dst = *src;
P_EntBin *bin = &frame->ent_bins[src->key.v % frame->ent_bins_count];
DllQueuePush(frame->first_ent, frame->last_ent, dst);
DllQueuePushNP(bin->first, bin->last, dst, next_in_bin, prev_in_bin);
++frame->ents_count;
}
return frame;
}
////////////////////////////////////////////////////////////
//~ Step
P_Frame *P_StepWorld(P_World *world, P_Frame *prev_frame, P_CmdList cmds)
{
P_Frame *result = &P_NilFrame;
return result;
}

View File

@ -52,7 +52,6 @@ Struct(P_Ent)
//- Persistent data //- Persistent data
P_Key key; P_Key key;
b32 valid;
////////////////////////////// //////////////////////////////
//- Build data //- Build data
@ -62,7 +61,7 @@ Struct(P_Ent)
b32 is_player; b32 is_player;
f32 health; f32 health;
Xform last_xf; Xform prev_xf;
Xform xf; Xform xf;
Vec2 move; Vec2 move;
@ -112,9 +111,22 @@ Struct(P_EntBin)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ World types //~ World types
Struct(P_World) Struct(P_Frame)
{ {
u64 seed; //////////////////////////////
//- Internal world state
struct P_World *world;
P_Frame *next;
P_Frame *prev;
P_Frame *next_in_bin;
P_Frame *prev_in_bin;
//////////////////////////////
//- Frame state
i64 tick; i64 tick;
i64 time_ns; i64 time_ns;
@ -124,6 +136,28 @@ Struct(P_World)
i64 ent_bins_count; i64 ent_bins_count;
P_EntBin *ent_bins; P_EntBin *ent_bins;
};
Struct(P_FrameBin)
{
P_Frame *first;
P_Frame *last;
};
Struct(P_World)
{
Arena *arena;
Arena *frames_arena;
Arena *statics_arena;
u64 seed;
// P_Ent *first_free_ent;
P_Frame *first_frame;
P_Frame *last_frame;
i64 frame_bins_count;
P_FrameBin *frame_bins;
// P_Frame *first_free_frame;
u8 *tiles; u8 *tiles;
}; };
@ -155,7 +189,9 @@ Struct(P_DeltaNode)
Struct(P_Snapshot) Struct(P_Snapshot)
{ {
u64 seed; u64 world_seed;
// i64 src_world_tick;
i64 src_tick;
i64 tick; i64 tick;
i64 time_ns; i64 time_ns;
@ -170,6 +206,13 @@ Struct(P_SnapshotNode)
P_Snapshot snapshot; P_Snapshot snapshot;
}; };
Struct(P_SnapshotList)
{
i64 count;
P_SnapshotNode *first;
P_SnapshotNode *last;
};
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Collision types //~ Collision types
@ -306,6 +349,7 @@ Enum(P_CmdKind)
Struct(P_Cmd) Struct(P_Cmd)
{ {
i64 tick;
P_CmdKind kind; P_CmdKind kind;
// Delta // Delta
@ -325,20 +369,25 @@ Struct(P_CmdNode)
P_Cmd cmd; P_Cmd cmd;
}; };
Struct(P_CmdList)
{
i64 count;
P_CmdNode *first;
P_CmdNode *last;
};
Struct(P_InputState) Struct(P_InputState)
{ {
Arena *arena; Arena *arena;
P_CmdNode *first_cmd_node;
P_CmdNode *last_cmd_node; P_CmdList cmds;
u64 cmds_count;
}; };
Struct(P_OutputState) Struct(P_OutputState)
{ {
Arena *arena; Arena *arena;
P_SnapshotNode *first_snapshot_node;
P_SnapshotNode *last_snapshot_node; P_SnapshotList snapshots;
u64 snapshots_count;
P_DebugDrawNode *first_debug_draw_node; P_DebugDrawNode *first_debug_draw_node;
P_DebugDrawNode *last_debug_draw_node; P_DebugDrawNode *last_debug_draw_node;
@ -363,6 +412,7 @@ Struct(P_Ctx)
Struct(P_ThreadLocalCtx) Struct(P_ThreadLocalCtx)
{ {
// TODO: Move this to world frame
Arena *debug_arena; Arena *debug_arena;
b32 debug_draw_enabled; b32 debug_draw_enabled;
P_DebugDrawNode *first_debug_draw_node; P_DebugDrawNode *first_debug_draw_node;
@ -373,6 +423,9 @@ Struct(P_ThreadLocalCtx)
extern P_Ctx P; extern P_Ctx P;
extern ThreadLocal P_ThreadLocalCtx P_tl; extern ThreadLocal P_ThreadLocalCtx P_tl;
extern Readonly P_Ent P_NilEnt;
extern Readonly P_Frame P_NilFrame;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Bootstrap //~ Bootstrap
@ -383,11 +436,12 @@ void P_Bootstrap(void);
b32 P_IsKeyNil(P_Key key); b32 P_IsKeyNil(P_Key key);
b32 P_IsEntNil(P_Ent *ent); b32 P_IsEntNil(P_Ent *ent);
b32 P_MatchKey(P_Key a, P_Key b); b32 P_IsFrameNil(P_Frame *frame);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Key helpers //~ Key helpers
b32 P_MatchKey(P_Key a, P_Key b);
P_Key P_RandKey(void); P_Key P_RandKey(void);
#define P_FmtKey(key) FmtHandle((key).v) #define P_FmtKey(key) FmtHandle((key).v)
@ -397,11 +451,6 @@ P_Key P_RandKey(void);
String P_NameFromTileKind(P_TileKind kind); String P_NameFromTileKind(P_TileKind kind);
////////////////////////////////////////////////////////////
//~ Delta helpers
void P_UpdateWorldFromDelta(Arena *arena, P_World *world, P_Delta *delta);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Shape helpers //~ Shape helpers
@ -431,20 +480,18 @@ Vec2 P_EdgePointFromShape(P_Shape shape, Vec2 dir);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Lookup helpers //~ Lookup helpers
P_Ent *P_EntFromKeyEx(P_World *world, P_Key key, i64 tick); P_Ent *P_EntFromKey(P_Frame *frame, P_Key key);
P_Ent *P_EntFromKey(P_World *world, P_Key key);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Iteration helpers //~ Iteration helpers
P_Ent *P_FirstEnt(P_World *world); P_Ent *P_FirstEnt(P_Frame *frame);
P_Ent *P_NextEnt(P_Ent *e); P_Ent *P_NextEnt(P_Ent *e);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ List helpers //~ List helpers
P_Ent *P_PushTempEnt(Arena *arena, P_EntList *list); P_Ent *P_PushTempEnt(Arena *arena, P_EntList *list);
void P_SpawnEntsFromList(Arena *arena, P_World *world, P_EntList ents);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Debug draw //~ Debug draw
@ -453,3 +500,19 @@ void P_DebugDrawPoint(Vec2 p, Vec4 srgb);
void P_DebugDrawLine(Vec2 p0, Vec2 p1, Vec4 srgb); void P_DebugDrawLine(Vec2 p0, Vec2 p1, Vec4 srgb);
void P_DebugDrawRect(Rng2 rect, Vec4 srgb); void P_DebugDrawRect(Rng2 rect, Vec4 srgb);
void P_DebugDrawShape(P_Shape shape, Vec4 srgb); void P_DebugDrawShape(P_Shape shape, Vec4 srgb);
////////////////////////////////////////////////////////////
//~ World
P_World *P_AcquireWorld(void);
void P_UpdateWorldFromSnapshots(P_World *world, P_SnapshotList snapshots);
void P_SpawnEntsFromList(P_Frame *frame, P_EntList ents);
P_Frame *P_FrameFromTick(P_World *world, i64 tick);
void P_ClearFrames(P_World *world, i64 tick_min, i64 tick_max);
P_Frame *P_PushFrame(P_World *world, P_Frame *src_frame, i64 tick);
////////////////////////////////////////////////////////////
//~ Step
P_Frame *P_StepWorld(P_World *world, P_Frame *prev_frame, P_CmdList cmds);

View File

@ -24,13 +24,11 @@ void S_TickForever(WaveLaneCtx *lane)
Arena *frame_arena = AcquireArena(Gibi(64)); Arena *frame_arena = AcquireArena(Gibi(64));
P_tl.debug_arena = AcquireArena(Gibi(64)); P_tl.debug_arena = AcquireArena(Gibi(64));
Arena *world_arena = AcquireArena(Gibi(64)); P_World *world = P_AcquireWorld();
P_World *world = 0;
// TODO: Real per-client deltas // TODO: Real per-client deltas
b32 has_sent_initial_tick = 0; b32 has_sent_initial_tick = 0;
////////////////////////////// //////////////////////////////
//- Sim loop //- Sim loop
@ -39,65 +37,69 @@ void S_TickForever(WaveLaneCtx *lane)
{ {
shutdown = Atomic32Fetch(&S.shutdown); shutdown = Atomic32Fetch(&S.shutdown);
P_tl.debug_draw_enabled = TweakBool("Simulation debug draw", 1); P_tl.debug_draw_enabled = TweakBool("Simulation debug draw", 1);
ResetArena(frame_arena); ResetArena(frame_arena);
////////////////////////////// //////////////////////////////
//- Swap //- Swap
{ // {
b32 swapin = IsSwappedIn(); // b32 swapin = IsSwappedIn();
b32 swapout = shutdown && IsSwappingOut(); // b32 swapout = shutdown && IsSwappingOut();
//- Swap in // //- Swap in
if (!world) // if (!world)
{ // {
String packed = Zi; // String packed = Zi;
if (swapin) // if (swapin)
{ // {
packed = SwappedStateFromName(frame_arena, Lit("pp_sim.swp")); // packed = SwappedStateFromName(frame_arena, Lit("pp_sim.swp"));
} // }
P_UnpackedWorld unpacked = P_UnpackWorld(frame_arena, packed); // P_UnpackedWorld unpacked = P_UnpackWorld(frame_arena, packed);
ResetArena(world_arena); // ResetArena(world_arena);
world = PushStruct(world_arena, P_World); // world = PushStruct(world_arena, P_World);
world->seed = unpacked.seed; // world->seed = unpacked.seed;
world->tick = unpacked.tick; // world_frame->tick = unpacked.tick;
world->time_ns = unpacked.time_ns; // world_frame->time_ns = unpacked.time_ns;
if (world->seed == 0) // if (world->seed == 0)
{ // {
TrueRand(StringFromStruct(&world->seed)); // TrueRand(StringFromStruct(&world->seed));
} // }
world->ent_bins_count = Kibi(16); // world_frame->ent_bins_count = Kibi(16);
world->ent_bins = PushStructs(world_arena, P_EntBin, world->ent_bins_count); // world_frame->ent_bins = PushStructs(world_arena, P_EntBin, world_frame->ent_bins_count);
// Copy tiles // // Copy tiles
world->tiles = PushStructsNoZero(world_arena, u8, P_TilesCount); // world->tiles = PushStructsNoZero(world_arena, u8, P_TilesCount);
CopyStructs(world->tiles, unpacked.tiles, P_TilesCount); // CopyStructs(world->tiles, unpacked.tiles, P_TilesCount);
// Copy ents // // Copy ents
P_SpawnEntsFromList(world_arena, world, unpacked.ents); // P_SpawnEntsFromList(world_arena, world, unpacked.ents);
} // }
//- Swap out // //- Swap out
if (swapout) // if (swapout)
{ // {
String packed = P_PackWorld(frame_arena, world); // String packed = P_PackWorld(frame_arena, world);
WriteSwappedState(Lit("pp_sim.swp"), packed); // WriteSwappedState(Lit("pp_sim.swp"), packed);
} // }
} // }
////////////////////////////// //////////////////////////////
//- Begin sim frame //- Begin sim frame
P_Frame *prev_world_frame = world->last_frame;
P_Frame *world_frame = P_PushFrame(world, prev_world_frame, prev_world_frame->tick + 1);
// FIXME: Copy frame
i64 frame_begin_ns = TimeNs(); i64 frame_begin_ns = TimeNs();
i64 sim_dt_ns = NsFromSeconds(1) / SIM_TICKS_PER_SECOND; i64 sim_dt_ns = NsFromSeconds(1) / SIM_TICKS_PER_SECOND;
f64 sim_dt = SecondsFromNs(sim_dt_ns); f64 sim_dt = SecondsFromNs(sim_dt_ns);
world->tick += 1;
////////////////////////////// //////////////////////////////
//- Pop sim commands //- Pop commands
P_InputState *input = 0; P_InputState *input = 0;
LockTicketMutex(&P.sim_input_back_tm); LockTicketMutex(&P.sim_input_back_tm);
@ -114,9 +116,9 @@ void S_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Update double-buffered entity data //- Update double-buffered entity data
for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{ {
ent->last_xf = ent->xf; ent->prev_xf = ent->xf;
} }
////////////////////////////// //////////////////////////////
@ -125,7 +127,7 @@ void S_TickForever(WaveLaneCtx *lane)
// FIXME: Only accept save command from local user // FIXME: Only accept save command from local user
b32 should_save = 0; b32 should_save = 0;
for (P_CmdNode *cmd_node = input->first_cmd_node; cmd_node; cmd_node = cmd_node->next) for (P_CmdNode *cmd_node = input->cmds.first; cmd_node; cmd_node = cmd_node->next)
{ {
P_Cmd *cmd = &cmd_node->cmd; P_Cmd *cmd = &cmd_node->cmd;
if (cmd->kind == P_CmdKind_Save) if (cmd->kind == P_CmdKind_Save)
@ -145,9 +147,11 @@ void S_TickForever(WaveLaneCtx *lane)
// FIXME: Only accept world deltas from users that can edit // FIXME: Only accept world deltas from users that can edit
// FIXME: Only apply relevant cmds based on tick
i64 applied_user_deltas_count = 0; i64 applied_user_deltas_count = 0;
P_Delta **applied_user_deltas = PushStructsNoZero(frame_arena, P_Delta *, input->cmds_count); P_Delta **applied_user_deltas = PushStructsNoZero(frame_arena, P_Delta *, input->cmds.count);
for (P_CmdNode *cmd_node = input->first_cmd_node; cmd_node; cmd_node = cmd_node->next) for (P_CmdNode *cmd_node = input->cmds.first; cmd_node; cmd_node = cmd_node->next)
{ {
P_Cmd *cmd = &cmd_node->cmd; P_Cmd *cmd = &cmd_node->cmd;
if (cmd->kind == P_CmdKind_Delta) if (cmd->kind == P_CmdKind_Delta)
@ -176,7 +180,20 @@ void S_TickForever(WaveLaneCtx *lane)
} }
if (allow) if (allow)
{ {
P_UpdateWorldFromDelta(world_arena, world, delta); P_DeltaNode tmp_delta_node = Zi;
tmp_delta_node.delta = *delta;
P_SnapshotNode tmp_snapshot_node = Zi;
tmp_snapshot_node.snapshot.deltas_count = 1;
tmp_snapshot_node.snapshot.first_delta_node = &tmp_delta_node;
tmp_snapshot_node.snapshot.last_delta_node = &tmp_delta_node;
P_SnapshotList tmp_snapshot_list = Zi;
tmp_snapshot_list.count = 1;
tmp_snapshot_list.first = &tmp_snapshot_node;
tmp_snapshot_list.last = &tmp_snapshot_node;
P_UpdateWorldFromSnapshots(world, tmp_snapshot_list);
} }
} }
} }
@ -184,18 +201,20 @@ void S_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Update ent controls //- Update ent controls
for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) // FIXME: Only apply relevant cmds based on tick
for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{ {
ent->fire_presses = 0; ent->fire_presses = 0;
} }
for (P_CmdNode *cmd_node = input->first_cmd_node; cmd_node; cmd_node = cmd_node->next) for (P_CmdNode *cmd_node = input->cmds.first; cmd_node; cmd_node = cmd_node->next)
{ {
P_Cmd cmd = cmd_node->cmd; P_Cmd cmd = cmd_node->cmd;
if (cmd.kind == P_CmdKind_Control) if (cmd.kind == P_CmdKind_Control)
{ {
P_Ent *target = P_EntFromKey(world, cmd.target); P_Ent *target = P_EntFromKey(world_frame, cmd.target);
if (target->valid) if (!P_IsEntNil(target))
{ {
target->move = ClampVec2Len(cmd.move, 1); target->move = ClampVec2Len(cmd.move, 1);
target->look = cmd.look; target->look = cmd.look;
@ -212,7 +231,7 @@ void S_TickForever(WaveLaneCtx *lane)
// ////////////////////////////// // //////////////////////////////
// //- Push bullets // //- Push bullets
// for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) // for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
// { // {
// if (ent->fire_held) // if (ent->fire_held)
// { // {
@ -227,7 +246,7 @@ void S_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Integrate control forces //- Integrate control forces
for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{ {
// Xform xf = ent->xf; // Xform xf = ent->xf;
// Xform desired_xf = xf; // Xform desired_xf = xf;
@ -300,10 +319,10 @@ void S_TickForever(WaveLaneCtx *lane)
constraints = PushStructs(frame_arena, P_Constraint, max_constraints); constraints = PushStructs(frame_arena, P_Constraint, max_constraints);
} }
for (P_Ent *ent0 = P_FirstEnt(world); ent0->valid; ent0 = P_NextEnt(ent0)) for (P_Ent *ent0 = P_FirstEnt(world_frame); !P_IsEntNil(ent0); ent0 = P_NextEnt(ent0))
{ {
P_Shape shape0 = P_WorldShapeFromEnt(ent0); P_Shape shape0 = P_WorldShapeFromEnt(ent0);
for (P_Ent *ent1 = P_FirstEnt(world); ent1->valid; ent1 = P_NextEnt(ent1)) for (P_Ent *ent1 = P_FirstEnt(world_frame); !P_IsEntNil(ent1); ent1 = P_NextEnt(ent1))
{ {
if (ent1 > ent0) if (ent1 > ent0)
{ {
@ -338,7 +357,7 @@ void S_TickForever(WaveLaneCtx *lane)
} }
if (constraint) if (constraint)
{ {
constraint->last_touched_tick = world->tick; constraint->last_touched_tick = world_frame->tick;
constraint->normal = collision.collision_normal; constraint->normal = collision.collision_normal;
// constraint->friction = SqrtF32(ent0->friction * ent1->friction); // constraint->friction = SqrtF32(ent0->friction * ent1->friction);
constraint->friction = 0; constraint->friction = 0;
@ -416,13 +435,13 @@ void S_TickForever(WaveLaneCtx *lane)
// // Debug draw // // Debug draw
// { // {
// // P_Ent *ent0 = P_EntFromKey(world, constraint->ent0); // // P_Ent *ent0 = P_EntFromKey(world_frame, constraint->ent0);
// // P_Ent *ent1 = P_EntFromKey(world, constraint->ent1); // // P_Ent *ent1 = P_EntFromKey(world_frame, constraint->ent1);
// Vec2 normal = constraint->normal; // Vec2 normal = constraint->normal;
// Vec2 center0 = Zi; // Vec2 center0 = Zi;
// Vec2 center1 = Zi; // Vec2 center1 = Zi;
// if (ent0->valid) center0 = P_WorldShapeFromEnt(ent0).center_of_mass; // if (!P_IsEntNil(ent0)) center0 = P_WorldShapeFromEnt(ent0).center_of_mass;
// if (ent1->valid) center1 = P_WorldShapeFromEnt(ent1).center_of_mass; // if (!P_IsEntNil(ent1)) center1 = P_WorldShapeFromEnt(ent1).center_of_mass;
// Vec2 p0 = AddVec2(center0, vcp0); // Vec2 p0 = AddVec2(center0, vcp0);
// Vec2 p1 = AddVec2(center1, vcp1); // Vec2 p1 = AddVec2(center1, vcp1);
// P_DebugDrawPoint(p0, Color_Cyan); // P_DebugDrawPoint(p0, Color_Cyan);
@ -457,15 +476,15 @@ void S_TickForever(WaveLaneCtx *lane)
// constraints = PushStructs(frame_arena, P_Constraint, max_constraints); // constraints = PushStructs(frame_arena, P_Constraint, max_constraints);
// } // }
// for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) // for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
// { // {
// if (ent->is_player) // if (ent->is_player)
// { // {
// Xform last_xf = ent->last_xf; // Xform prev_xf = ent->prev_xf;
// Xform xf = ent->xf; // Xform xf = ent->xf;
// P_Shape local_shape = P_LocalShapeFromEnt(ent); // P_Shape local_shape = P_LocalShapeFromEnt(ent);
// P_Shape last_world_shape = P_MulXformShape(last_xf, local_shape); // P_Shape last_world_shape = P_MulXformShape(prev_xf, local_shape);
// P_Shape shape0 = P_WorldShapeFromEnt(ent); // P_Shape shape0 = P_WorldShapeFromEnt(ent);
// // TODO: Real constraint data // // TODO: Real constraint data
@ -514,7 +533,7 @@ void S_TickForever(WaveLaneCtx *lane)
// } // }
// if (constraint) // if (constraint)
// { // {
// constraint->last_touched_tick = world->tick; // constraint->last_touched_tick = world_frame->tick;
// constraint->normal = collision.collision_normal; // constraint->normal = collision.collision_normal;
// // TODO: Real masses // // TODO: Real masses
@ -589,13 +608,13 @@ void S_TickForever(WaveLaneCtx *lane)
// // Debug draw // // Debug draw
// { // {
// P_Ent *ent0 = P_EntFromKey(world, constraint->ent0); // P_Ent *ent0 = P_EntFromKey(world_frame, constraint->ent0);
// P_Ent *ent1 = P_EntFromKey(world, constraint->ent1); // P_Ent *ent1 = P_EntFromKey(world_frame, constraint->ent1);
// Vec2 normal = constraint->normal; // Vec2 normal = constraint->normal;
// Vec2 center0 = Zi; // Vec2 center0 = Zi;
// Vec2 center1 = Zi; // Vec2 center1 = Zi;
// if (ent0->valid) center0 = P_WorldShapeFromEnt(ent0).center_of_mass; // if (!P_IsEntNil(ent0)) center0 = P_WorldShapeFromEnt(ent0).center_of_mass;
// if (ent1->valid) center1 = P_WorldShapeFromEnt(ent1).center_of_mass; // if (!P_IsEntNil(ent1)) center1 = P_WorldShapeFromEnt(ent1).center_of_mass;
// Vec2 p0 = AddVec2(center0, vcp0); // Vec2 p0 = AddVec2(center0, vcp0);
// Vec2 p1 = AddVec2(center1, vcp1); // Vec2 p1 = AddVec2(center1, vcp1);
// P_DebugDrawPoint(p0, Color_Cyan); // P_DebugDrawPoint(p0, Color_Cyan);
@ -616,9 +635,11 @@ void S_TickForever(WaveLaneCtx *lane)
{ {
P_Constraint *constraint = &constraints[constraint_idx]; P_Constraint *constraint = &constraints[constraint_idx];
b32 prune = 1; b32 prune = 1;
if (constraint->last_touched_tick == world->tick) if (constraint->last_touched_tick == world_frame->tick)
{ {
if (P_EntFromKey(world, constraint->ent0)->valid || P_EntFromKey(world, constraint->ent1)->valid) P_Ent *ent0 = P_EntFromKey(world_frame, constraint->ent0);
P_Ent *ent1 = P_EntFromKey(world_frame, constraint->ent1);
if (!P_IsEntNil(ent0) && !P_IsEntNil(ent1))
{ {
prune = 0; prune = 0;
} }
@ -691,8 +712,8 @@ void S_TickForever(WaveLaneCtx *lane)
{ {
P_Constraint *constraint = &constraints[constraint_idx]; P_Constraint *constraint = &constraints[constraint_idx];
P_Ent *ent0 = P_EntFromKey(world, constraint->ent0); P_Ent *ent0 = P_EntFromKey(world_frame, constraint->ent0);
P_Ent *ent1 = P_EntFromKey(world, constraint->ent1); P_Ent *ent1 = P_EntFromKey(world_frame, constraint->ent1);
Vec2 v0 = ent0->solved_v; Vec2 v0 = ent0->solved_v;
Vec2 v1 = ent1->solved_v; Vec2 v1 = ent1->solved_v;
@ -716,12 +737,12 @@ void S_TickForever(WaveLaneCtx *lane)
w1 += WedgeVec2(vcp1, impulse) * constraint->inv_i1; w1 += WedgeVec2(vcp1, impulse) * constraint->inv_i1;
} }
if (ent0->valid) if (!P_IsEntNil(ent0))
{ {
ent0->solved_v = v0; ent0->solved_v = v0;
ent0->solved_w = w0; ent0->solved_w = w0;
} }
if (ent1->valid) if (!P_IsEntNil(ent1))
{ {
ent1->solved_v = v1; ent1->solved_v = v1;
ent1->solved_w = w1; ent1->solved_w = w1;
@ -735,8 +756,8 @@ void S_TickForever(WaveLaneCtx *lane)
{ {
P_Constraint *constraint = &constraints[constraint_idx]; P_Constraint *constraint = &constraints[constraint_idx];
P_Ent *ent0 = P_EntFromKey(world, constraint->ent0); P_Ent *ent0 = P_EntFromKey(world_frame, constraint->ent0);
P_Ent *ent1 = P_EntFromKey(world, constraint->ent1); P_Ent *ent1 = P_EntFromKey(world_frame, constraint->ent1);
f32 inv_m0 = constraint->inv_m0; f32 inv_m0 = constraint->inv_m0;
f32 inv_m1 = constraint->inv_m1; f32 inv_m1 = constraint->inv_m1;
@ -749,11 +770,11 @@ void S_TickForever(WaveLaneCtx *lane)
Vec2 center0 = constraint->static_center0; Vec2 center0 = constraint->static_center0;
Vec2 center1 = constraint->static_center1; Vec2 center1 = constraint->static_center1;
if (ent0->valid) if (!P_IsEntNil(ent0))
{ {
center0 = P_WorldShapeFromEnt(ent0).center_of_mass; center0 = P_WorldShapeFromEnt(ent0).center_of_mass;
} }
if (ent1->valid) if (!P_IsEntNil(ent1))
{ {
center1 = P_WorldShapeFromEnt(ent1).center_of_mass; center1 = P_WorldShapeFromEnt(ent1).center_of_mass;
} }
@ -844,12 +865,12 @@ void S_TickForever(WaveLaneCtx *lane)
w1 += WedgeVec2(vcp1, impulse) * inv_i1; w1 += WedgeVec2(vcp1, impulse) * inv_i1;
} }
if (ent0->valid) if (!P_IsEntNil(ent0))
{ {
ent0->solved_v = v0; ent0->solved_v = v0;
ent0->solved_w = w0; ent0->solved_w = w0;
} }
if (ent1->valid) if (!P_IsEntNil(ent1))
{ {
ent1->solved_v = v1; ent1->solved_v = v1;
ent1->solved_w = w1; ent1->solved_w = w1;
@ -859,7 +880,7 @@ void S_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Integrate velocities //- Integrate velocities
for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{ {
Xform xf = ent->xf; Xform xf = ent->xf;
xf.og = AddVec2(xf.og, MulVec2(ent->solved_v, solver_dt)); xf.og = AddVec2(xf.og, MulVec2(ent->solved_v, solver_dt));
@ -884,7 +905,7 @@ void S_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Move bullets //- Move bullets
for (P_Ent *bullet = P_FirstEnt(world); bullet->valid; bullet = P_NextEnt(bullet)) for (P_Ent *bullet = P_FirstEnt(world_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet))
{ {
if (bullet->is_bullet) if (bullet->is_bullet)
{ {
@ -903,12 +924,12 @@ void S_TickForever(WaveLaneCtx *lane)
{ {
P_EntList bullets_to_spawn = Zi; P_EntList bullets_to_spawn = Zi;
for (P_Ent *firer = P_FirstEnt(world); firer->valid; firer = P_NextEnt(firer)) for (P_Ent *firer = P_FirstEnt(world_frame); !P_IsEntNil(firer); firer = P_NextEnt(firer))
{ {
if (firer->fire_held) if (firer->fire_held)
// if (firer->fire_presses) // if (firer->fire_presses)
{ {
// i64 fire_delta_ns = world->time_ns - firer->last_fire_ns; // i64 fire_delta_ns = world_frame->time_ns - firer->last_fire_ns;
// i64 single_bullet_delta_ns = NsFromSeconds(1) / firer->fire_rate; // i64 single_bullet_delta_ns = NsFromSeconds(1) / firer->fire_rate;
@ -920,7 +941,7 @@ void S_TickForever(WaveLaneCtx *lane)
f32 spread = Tau * 0.01; f32 spread = Tau * 0.01;
f32 tweak_speed = TweakFloat("Bullet speed", 100, 1, 100); f32 tweak_speed = TweakFloat("Bullet speed", 100, 1, 100);
b32 can_fire = (firer->last_fire_ns + NsFromSeconds(1.0 / fire_rate)) <= world->time_ns; b32 can_fire = (firer->last_fire_ns + NsFromSeconds(1.0 / fire_rate)) <= world_frame->time_ns;
if (can_fire) if (can_fire)
{ {
i64 tick_bullets_count = bullets_per_fire; i64 tick_bullets_count = bullets_per_fire;
@ -955,11 +976,11 @@ void S_TickForever(WaveLaneCtx *lane)
bullet->bullet_firer = firer->key; bullet->bullet_firer = firer->key;
} }
} }
firer->last_fire_ns = world->time_ns; firer->last_fire_ns = world_frame->time_ns;
} }
} }
} }
P_SpawnEntsFromList(world_arena, world, bullets_to_spawn); P_SpawnEntsFromList(world_frame, bullets_to_spawn);
} }
@ -970,7 +991,7 @@ void S_TickForever(WaveLaneCtx *lane)
// TODO: Separate 'hits' from bullets, so that bullets can have multiple hits // TODO: Separate 'hits' from bullets, so that bullets can have multiple hits
for (P_Ent *bullet = P_FirstEnt(world); bullet->valid; bullet = P_NextEnt(bullet)) for (P_Ent *bullet = P_FirstEnt(world_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet))
{ {
if (bullet->is_bullet) if (bullet->is_bullet)
{ {
@ -984,7 +1005,7 @@ void S_TickForever(WaveLaneCtx *lane)
P_RaycastResult victim_raycast = Zi; P_RaycastResult victim_raycast = Zi;
{ {
f32 closest_len_sq = Inf; f32 closest_len_sq = Inf;
for (P_Ent *victim = P_FirstEnt(world); victim->valid; victim = P_NextEnt(victim)) for (P_Ent *victim = P_FirstEnt(world_frame); !P_IsEntNil(victim); victim = P_NextEnt(victim))
{ {
if (victim->is_player && !P_MatchKey(victim->key, bullet->bullet_firer)) if (victim->is_player && !P_MatchKey(victim->key, bullet->bullet_firer))
{ {
@ -1013,7 +1034,7 @@ void S_TickForever(WaveLaneCtx *lane)
} }
} }
if (closest_victim->valid) if (!P_IsEntNil(closest_victim))
{ {
bullet->has_hit = 1; bullet->has_hit = 1;
bullet->hit_entry = victim_raycast.p; bullet->hit_entry = victim_raycast.p;
@ -1064,12 +1085,12 @@ void S_TickForever(WaveLaneCtx *lane)
// P_Bullet *bullet = &bullets[bullet_idx]; // P_Bullet *bullet = &bullets[bullet_idx];
// // Raycast // // Raycast
// for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) // for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
// { // {
// Xform xf = ent->xf; // Xform xf = ent->xf;
// P_Shape world_shape = P_MulXformShape(xf, ent->local_shape); // P_Shape world_shape = P_MulXformShape(xf, ent->local_shape);
// if (ent == P_FirstEnt(world)) // if (ent == P_FirstEnt(world_frame))
// { // {
// bullet->start = AddVec2(world_shape.centroid, Vec2WithLen(ent->look, world_shape.radius)); // bullet->start = AddVec2(world_shape.centroid, Vec2WithLen(ent->look, world_shape.radius));
// bullet->dir = NormVec2(ent->look); // bullet->dir = NormVec2(ent->look);
@ -1123,12 +1144,12 @@ void S_TickForever(WaveLaneCtx *lane)
// // P_Bullet *bullet = &bullets[bullet_idx]; // // P_Bullet *bullet = &bullets[bullet_idx];
// // // Raycast // // // Raycast
// // for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) // // for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
// // { // // {
// // Xform xf = ent->xf; // // Xform xf = ent->xf;
// // P_Shape world_shape = P_MulXformShape(xf, ent->local_shape); // // P_Shape world_shape = P_MulXformShape(xf, ent->local_shape);
// // if (ent == P_FirstEnt(world)) // // if (ent == P_FirstEnt(world_frame))
// // { // // {
// // bullet->start = AddVec2(world_shape.centroid, Vec2WithLen(ent->look, world_shape.radius)); // // bullet->start = AddVec2(world_shape.centroid, Vec2WithLen(ent->look, world_shape.radius));
// // bullet->dir = NormVec2(ent->look); // // bullet->dir = NormVec2(ent->look);
@ -1164,7 +1185,7 @@ void S_TickForever(WaveLaneCtx *lane)
if (P_tl.debug_draw_enabled) if (P_tl.debug_draw_enabled)
{ {
for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{ {
P_Shape world_shape = P_WorldShapeFromEnt(ent); P_Shape world_shape = P_WorldShapeFromEnt(ent);
@ -1209,12 +1230,12 @@ void S_TickForever(WaveLaneCtx *lane)
P_OutputState *output = &P.sim_output_states[P.sim_output_back_idx]; P_OutputState *output = &P.sim_output_states[P.sim_output_back_idx];
P_SnapshotNode *snapshot_node = PushStruct(output->arena, P_SnapshotNode); P_SnapshotNode *snapshot_node = PushStruct(output->arena, P_SnapshotNode);
P_Snapshot *snapshot = &snapshot_node->snapshot; P_Snapshot *snapshot = &snapshot_node->snapshot;
SllQueuePush(output->first_snapshot_node, output->last_snapshot_node, snapshot_node); SllQueuePush(output->snapshots.first, output->snapshots.last, snapshot_node);
++output->snapshots_count; ++output->snapshots.count;
snapshot->seed = world->seed; snapshot->world_seed = world->seed;
snapshot->tick = world->tick; snapshot->tick = world_frame->tick;
snapshot->time_ns = world->time_ns; snapshot->time_ns = world_frame->time_ns;
// Forward user edit deltas // Forward user edit deltas
for (i64 applied_user_delta_idx = 0; applied_user_delta_idx < applied_user_deltas_count; ++applied_user_delta_idx) for (i64 applied_user_delta_idx = 0; applied_user_delta_idx < applied_user_deltas_count; ++applied_user_delta_idx)
@ -1248,7 +1269,7 @@ void S_TickForever(WaveLaneCtx *lane)
} }
// Push raw entity deltas // Push raw entity deltas
for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{ {
P_Delta *delta = 0; P_Delta *delta = 0;
{ {
@ -1287,8 +1308,8 @@ void S_TickForever(WaveLaneCtx *lane)
{ {
i64 ents_to_prune_count = 0; i64 ents_to_prune_count = 0;
P_Ent **ents_to_prune = PushStructsNoZero(frame_arena, P_Ent *, world->ents_count); P_Ent **ents_to_prune = PushStructsNoZero(frame_arena, P_Ent *, world_frame->ents_count);
for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{ {
if (ent->exists <= 0) if (ent->exists <= 0)
{ {
@ -1299,13 +1320,13 @@ void S_TickForever(WaveLaneCtx *lane)
for (i64 prune_idx = 0; prune_idx < ents_to_prune_count; ++prune_idx) for (i64 prune_idx = 0; prune_idx < ents_to_prune_count; ++prune_idx)
{ {
// FIXME: Add to free list // FIXME: Add to freelist
// FIXME: Ensure sure prunes are received by user // FIXME: Ensure sure prunes are received by user
P_Ent *ent = ents_to_prune[prune_idx]; P_Ent *ent = ents_to_prune[prune_idx];
P_EntBin *bin = &world->ent_bins[ent->key.v % world->ent_bins_count]; P_EntBin *bin = &world_frame->ent_bins[ent->key.v % world_frame->ent_bins_count];
DllQueueRemoveNP(bin->first, bin->last, ent, next_in_bin, prev_in_bin); DllQueueRemoveNP(bin->first, bin->last, ent, next_in_bin, prev_in_bin);
DllQueueRemove(world->first_ent, world->last_ent, ent); DllQueueRemove(world_frame->first_ent, world_frame->last_ent, ent);
world->ents_count -= 1; world_frame->ents_count -= 1;
} }
} }
@ -1321,7 +1342,7 @@ void S_TickForever(WaveLaneCtx *lane)
} }
i64 frame_end_ns = TimeNs(); i64 frame_end_ns = TimeNs();
world->time_ns += sim_dt_ns; world_frame->time_ns += sim_dt_ns;
////////////////////////////// //////////////////////////////
//- Sleep //- Sleep

View File

@ -6,19 +6,20 @@ String P_PackWorld(Arena *arena, P_World *src_world)
String result = Zi; String result = Zi;
result.text = ArenaNext(arena, u8); result.text = ArenaNext(arena, u8);
TempArena scratch = BeginScratch(arena); TempArena scratch = BeginScratch(arena);
P_Frame *src_frame = src_world->last_frame;
result.len += StringF(arena, "version: %F\n", FmtUint(P_Tv_Latest)).len; result.len += StringF(arena, "version: %F\n", FmtUint(P_Tv_Latest)).len;
result.len += StringF(arena, "\n").len; result.len += StringF(arena, "\n").len;
result.len += StringF(arena, "seed: 0x%F\n", FmtHex(src_world->seed)).len; result.len += StringF(arena, "seed: 0x%F\n", FmtHex(src_world->seed)).len;
result.len += StringF(arena, "tick: %F\n", FmtSint(src_world->tick)).len; result.len += StringF(arena, "tick: %F\n", FmtSint(src_frame->tick)).len;
result.len += StringF(arena, "time: %F\n", FmtSint(src_world->time_ns)).len; result.len += StringF(arena, "time: %F\n", FmtSint(src_frame->time_ns)).len;
// Pack entities // Pack entities
// FIXME: Precision // FIXME: Precision
result.len += PushString(arena, Lit("\nentities:\n")).len; result.len += PushString(arena, Lit("\nentities:\n")).len;
result.len += PushString(arena, Lit("{\n")).len; result.len += PushString(arena, Lit("{\n")).len;
for (P_Ent *ent = P_FirstEnt(src_world); ent->valid; ent = P_NextEnt(ent)) for (P_Ent *ent = P_FirstEnt(src_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{ {
// TODO: Pack bullets // TODO: Pack bullets
if (!ent->is_bullet) if (!ent->is_bullet)

View File

@ -43,11 +43,12 @@ V_Cmd *V_PushVisCmd(String name)
P_Cmd *V_PushSimCmd(P_CmdKind kind) P_Cmd *V_PushSimCmd(P_CmdKind kind)
{ {
V_Frame *frame = V_CurrentFrame(); // FIXME: Free list
P_CmdNode *n = PushStruct(frame->arena, P_CmdNode); Arena *perm = PermArena();
P_CmdNode *n = PushStruct(perm, P_CmdNode);
n->cmd.kind = kind; n->cmd.kind = kind;
SllQueuePush(frame->first_sim_cmd_node, frame->last_sim_cmd_node, n); SllQueuePush(V.sim_cmds.first, V.sim_cmds.last, n);
++frame->sim_cmds_count; ++V.sim_cmds.count;
return &n->cmd; return &n->cmd;
} }
@ -340,11 +341,9 @@ void V_TickForever(WaveLaneCtx *lane)
P_DebugDrawNode *first_sim_debug_draw_node = 0; P_DebugDrawNode *first_sim_debug_draw_node = 0;
P_DebugDrawNode *last_sim_debug_draw_node = 0; P_DebugDrawNode *last_sim_debug_draw_node = 0;
Arena *world_arena = AcquireArena(Gibi(64)); P_World *sim_world = P_AcquireWorld();
P_World *world = PushStruct(world_arena, P_World); P_World *predict_world = P_AcquireWorld();
world->ent_bins_count = Kibi(16); P_World *blend_world = P_AcquireWorld();
world->ent_bins = PushStructs(world_arena, P_EntBin, world->ent_bins_count);
world->tiles = PushStructs(world_arena, u8, P_TilesCount);
Vec2I32 tiles_dims = VEC2I32(P_TilesPitch, P_TilesPitch); Vec2I32 tiles_dims = VEC2I32(P_TilesPitch, P_TilesPitch);
Vec2I32 cells_dims = VEC2I32(V_CellsPerMeter * P_WorldPitch, V_CellsPerMeter * P_WorldPitch); Vec2I32 cells_dims = VEC2I32(V_CellsPerMeter * P_WorldPitch, V_CellsPerMeter * P_WorldPitch);
@ -471,7 +470,7 @@ void V_TickForever(WaveLaneCtx *lane)
} }
else else
{ {
u64 hotkey_hash = HashFnv64(Fnv64Basis, StringFromStruct(&hotkey)); u64 hotkey_hash = HashString(StringFromStruct(&hotkey));
V_ShortcutBin *bin = &shortcut_bins[hotkey_hash % shortcut_bins_count]; V_ShortcutBin *bin = &shortcut_bins[hotkey_hash % shortcut_bins_count];
V_Shortcut *shortcut = PushStruct(perm, V_Shortcut); V_Shortcut *shortcut = PushStruct(perm, V_Shortcut);
shortcut->hotkey_hash = hotkey_hash; shortcut->hotkey_hash = hotkey_hash;
@ -632,54 +631,77 @@ void V_TickForever(WaveLaneCtx *lane)
frame->ui_dims.y = MaxF32(frame->ui_dims.y, 64); frame->ui_dims.y = MaxF32(frame->ui_dims.y, 64);
frame->draw_dims = frame->ui_dims; frame->draw_dims = frame->ui_dims;
////////////////////////////// // //////////////////////////////
//- Pop sim output // //- Pop sim output
// P_OutputState *sim_output = 0;
// LockTicketMutex(&P.sim_output_back_tm);
// {
// sim_output = &P.sim_output_states[P.sim_output_back_idx];
// ++P.sim_output_back_idx;
// if (P.sim_output_back_idx >= countof(P.sim_output_states))
// {
// P.sim_output_back_idx = 0;
// }
// }
// UnlockTicketMutex(&P.sim_output_back_tm);
// //////////////////////////////
// //- Apply sim snapshots
// // FIXME: Only apply latest snapshot
// // FIXME: Real ping
// i64 ping_ns = NsFromSeconds(0.250);
// // TODO: Remove this (testing)
// // b32 received_unseen_tick = 0;
// // b32 tiles_dirty = 0;
// // b32 should_clear_particles = 0;
// // for (P_SnapshotNode *n = sim_output->first_snapshot_node; n; n = n->next)
// // {
// // P_Snapshot *snapshot = &n->snapshot;
// // if (snapshot->tick > world->tick)
// // {
// // world->seed = snapshot->seed;
// // world->tick = snapshot->tick;
// // world->time_ns = snapshot->time_ns;
// // for (P_DeltaNode *dn = snapshot->first_delta_node; dn; dn = dn->next)
// // {
// // P_Delta *delta = &dn->delta;
// // if (delta->kind == P_DeltaKind_Reset)
// // {
// // tiles_dirty = 1;
// // should_clear_particles = 1;
// // }
// // if (delta->kind == P_DeltaKind_RawTiles || delta->kind == P_DeltaKind_Tile)
// // {
// // tiles_dirty = 1;
// // }
// // P_UpdateWorldFromDelta(world, delta);
// // }
// // received_unseen_tick = 1;
// // }
// // }
// {
// P_SnapshotList sim_snapshots = sim_output->snapshots;
// P_UpdateWorldFromSnapshots(sim_world, sim_snapshots);
// }
P_OutputState *sim_output = 0;
LockTicketMutex(&P.sim_output_back_tm);
{
sim_output = &P.sim_output_states[P.sim_output_back_idx];
++P.sim_output_back_idx;
if (P.sim_output_back_idx >= countof(P.sim_output_states))
{
P.sim_output_back_idx = 0;
}
}
UnlockTicketMutex(&P.sim_output_back_tm);
//////////////////////////////
//- Apply sim snapshots
// FIXME: Only apply latest snapshot
b32 received_unseen_tick = 0;
b32 tiles_dirty = 0;
b32 should_clear_particles = 0;
for (P_SnapshotNode *n = sim_output->first_snapshot_node; n; n = n->next)
{
P_Snapshot *snapshot = &n->snapshot;
if (snapshot->tick > world->tick)
{
world->seed = snapshot->seed;
world->tick = snapshot->tick;
world->time_ns = snapshot->time_ns;
for (P_DeltaNode *dn = snapshot->first_delta_node; dn; dn = dn->next)
{
P_Delta *delta = &dn->delta;
if (delta->kind == P_DeltaKind_Reset)
{
tiles_dirty = 1;
should_clear_particles = 1;
}
if (delta->kind == P_DeltaKind_RawTiles || delta->kind == P_DeltaKind_Tile)
{
tiles_dirty = 1;
}
P_UpdateWorldFromDelta(world_arena, world, delta);
}
received_unseen_tick = 1;
}
}
// ////////////////////////////// // //////////////////////////////
// //- Update tiles from sim // //- Update tiles from sim
@ -782,7 +804,7 @@ void V_TickForever(WaveLaneCtx *lane)
hotkey.shift = frame->held_buttons[Button_Shift]; hotkey.shift = frame->held_buttons[Button_Shift];
hotkey.alt = frame->held_buttons[Button_Alt]; hotkey.alt = frame->held_buttons[Button_Alt];
{ {
u64 hotkey_hash = HashFnv64(Fnv64Basis, StringFromStruct(&hotkey)); u64 hotkey_hash = HashString(StringFromStruct(&hotkey));
V_ShortcutBin *bin = &shortcut_bins[hotkey_hash % shortcut_bins_count]; V_ShortcutBin *bin = &shortcut_bins[hotkey_hash % shortcut_bins_count];
V_Shortcut *shortcut = bin->first; V_Shortcut *shortcut = bin->first;
for (; shortcut; shortcut = shortcut->next_in_bin) for (; shortcut; shortcut = shortcut->next_in_bin)
@ -848,7 +870,7 @@ void V_TickForever(WaveLaneCtx *lane)
Vec2 look_ratio = Zi; Vec2 look_ratio = Zi;
look_ratio.y = 0.25; look_ratio.y = 0.25;
look_ratio.x = look_ratio.y / (16.0 / 9.0); look_ratio.x = look_ratio.y / (16.0 / 9.0);
P_Ent *player = P_EntFromKey(world, V.player_key); P_Ent *player = P_EntFromKey(blend_world->last_frame, V.player_key);
target_camera_pos = P_WorldShapeFromEnt(player).centroid; target_camera_pos = P_WorldShapeFromEnt(player).centroid;
target_camera_pos = AddVec2(target_camera_pos, MulVec2Vec2(player->look, look_ratio)); target_camera_pos = AddVec2(target_camera_pos, MulVec2Vec2(player->look, look_ratio));
target_camera_zoom = 1; target_camera_zoom = 1;
@ -1001,12 +1023,12 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Query entities //- Query entities
P_Ent *player = P_EntFromKey(world, V.player_key); P_Ent *player = P_EntFromKey(blend_world->last_frame, V.player_key);
P_Ent *hovered_ent = &P_NilEnt; P_Ent *hovered_ent = &P_NilEnt;
{ {
// TODO: Real world query // TODO: Real world query
P_Shape cursor_shape = P_ShapeFromDesc(.count = 1, .points = { frame->world_cursor }); P_Shape cursor_shape = P_ShapeFromDesc(.count = 1, .points = { frame->world_cursor });
for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) for (P_Ent *ent = P_FirstEnt(blend_world->last_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{ {
P_Shape ent_shape = P_WorldShapeFromEnt(ent); P_Shape ent_shape = P_WorldShapeFromEnt(ent);
b32 is_hovered = P_CollisionResultFromShapes(ent_shape, cursor_shape).collision_points_count > 0; b32 is_hovered = P_CollisionResultFromShapes(ent_shape, cursor_shape).collision_points_count > 0;
@ -2212,8 +2234,8 @@ void V_TickForever(WaveLaneCtx *lane)
{ {
UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y);
{ {
UI_BuildLabelF("World seed: 0x%F", FmtHex(world->seed)); UI_BuildLabelF("World seed: 0x%F", FmtHex(blend_world->seed));
UI_BuildLabelF("Entities count: %F", FmtSint(world->ents_count)); UI_BuildLabelF("Entities count: %F", FmtSint(blend_world->last_frame->ents_count));
} }
UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y);
{ {
@ -2535,7 +2557,7 @@ void V_TickForever(WaveLaneCtx *lane)
case V_CmdKind_delete: case V_CmdKind_delete:
{ {
if (hovered_ent->valid) if (!P_IsEntNil(hovered_ent))
{ {
P_Cmd *cmd = V_PushSimCmd(P_CmdKind_Delta); P_Cmd *cmd = V_PushSimCmd(P_CmdKind_Delta);
cmd->delta.kind = P_DeltaKind_RawEnt; cmd->delta.kind = P_DeltaKind_RawEnt;
@ -2553,10 +2575,10 @@ void V_TickForever(WaveLaneCtx *lane)
} }
} break; } break;
case V_CmdKind_clear_particles: // case V_CmdKind_clear_particles:
{ // {
should_clear_particles = 1; // should_clear_particles = 1;
} break; // } break;
} }
} }
@ -2605,23 +2627,189 @@ void V_TickForever(WaveLaneCtx *lane)
cmd->fire_presses = frame->fire_presses; cmd->fire_presses = frame->fire_presses;
} }
//////////////////////////////
//- Pop sim output
P_OutputState *sim_output = 0;
LockTicketMutex(&P.sim_output_back_tm);
{
sim_output = &P.sim_output_states[P.sim_output_back_idx];
++P.sim_output_back_idx;
if (P.sim_output_back_idx >= countof(P.sim_output_states))
{
P.sim_output_back_idx = 0;
}
}
UnlockTicketMutex(&P.sim_output_back_tm);
//////////////////////////////
//- Apply sim snapshots
// FIXME: Only apply latest snapshot
// FIXME: Real ping
i64 ping_ns = NsFromSeconds(0.250);
// TODO: Remove this (testing)
// b32 received_unseen_tick = 0;
// b32 tiles_dirty = 0;
// b32 should_clear_particles = 0;
// for (P_SnapshotNode *n = sim_output->first_snapshot_node; n; n = n->next)
// {
// P_Snapshot *snapshot = &n->snapshot;
// if (snapshot->tick > world->tick)
// {
// world->seed = snapshot->seed;
// world->tick = snapshot->tick;
// world->time_ns = snapshot->time_ns;
// for (P_DeltaNode *dn = snapshot->first_delta_node; dn; dn = dn->next)
// {
// P_Delta *delta = &dn->delta;
// if (delta->kind == P_DeltaKind_Reset)
// {
// tiles_dirty = 1;
// should_clear_particles = 1;
// }
// if (delta->kind == P_DeltaKind_RawTiles || delta->kind == P_DeltaKind_Tile)
// {
// tiles_dirty = 1;
// }
// P_UpdateWorldFromDelta(world, delta);
// }
// received_unseen_tick = 1;
// }
// }
// Apply snapshots to sim world
{
P_SnapshotList sim_snapshots = sim_output->snapshots;
P_UpdateWorldFromSnapshots(sim_world, sim_snapshots);
}
////////////////////////////// //////////////////////////////
//- Submit sim commands //- Submit sim commands
i64 predict_to = sim_world->last_frame->tick + 10;
LockTicketMutex(&P.sim_input_back_tm); LockTicketMutex(&P.sim_input_back_tm);
{ {
P_InputState *v2s = &P.sim_input_states[P.sim_input_back_idx]; P_InputState *v2s = &P.sim_input_states[P.sim_input_back_idx];
for (P_CmdNode *src = frame->first_sim_cmd_node; src; src = src->next) for (P_CmdNode *src = V.sim_cmds.first; src; src = src->next)
{ {
P_CmdNode *cmd_node = PushStruct(v2s->arena, P_CmdNode); P_CmdNode *cmd_node = PushStruct(v2s->arena, P_CmdNode);
cmd_node->cmd = src->cmd; cmd_node->cmd = src->cmd;
SllQueuePush(v2s->first_cmd_node, v2s->last_cmd_node, cmd_node); cmd_node->cmd.tick = predict_to;
++v2s->cmds_count; SllQueuePush(v2s->cmds.first, v2s->cmds.last, cmd_node);
++v2s->cmds.count;
} }
} }
UnlockTicketMutex(&P.sim_input_back_tm); UnlockTicketMutex(&P.sim_input_back_tm);
//////////////////////////////
//- Predict
// TODO: Only predict when new sim snapshot is received
// Predict
P_Frame *predict_frame = 0;
{
// i64 step_count = predict_to - sim_world->last_frame->tick;
// // TODO: Preserve constraints?
// P_ClearFrames(predict_world, I64Min, I64Max);
// P_Frame *base_predict_frame = P_PushFrame(predict_world, sim_world->last_frame, sim_world->last_frame->tick);
// P_Frame *prev_predict_frame = base_predict_frame;
// for (i64 step_idx = 0; step_idx < step_count; ++step_idx)
// {
// // P_CmdList step_cmds = V_StepCmdsFromTick(prev_predict_frame->tick + 1);
// P_CmdList step_cmds = Zi;
// for (P_CmdNode *src = V.sim_cmds.first; src; src = src->next)
// {
// P_CmdNode *n = PushStructNoZero(frame->arena, P_CmdNode);
// *n = *src;
// SllQueuePush(step_cmds.first, step_cmds.last, n);
// ++step_cmds.count;
// }
// P_Frame *stepped = P_StepWorld(predict_world, prev_predict_frame, step_cmds);
// prev_predict_frame = stepped;
// }
predict_frame = sim_world->last_frame;
}
//////////////////////////////
//- Update blended world
// TODO: Remove this
P_Frame *blend_frame = 0;
{
// P_ResetWorldFromFrame(blended_world, predict_world->last_frame);
// P_Frame *blend_frame = blended_world->last_frame;
blend_frame = predict_frame;
}
// FIXME: Compare tile hashes
b32 tiles_dirty = 0;
b32 should_clear_particles = 0;
// {
// i64 delay_ns = NsFromSeconds(100);
// P_Frame *right_frame = &P_NilFrame;
// P_Frame *left_frame = &P_NilFrame;
// for (P_Frame *tmp =
// P_Frame *right_frame = predict_world->last_frame;
// P_Frame *left_frame = predict_world->left_frame;
// }
@ -2636,7 +2824,7 @@ void V_TickForever(WaveLaneCtx *lane)
for (P_Ent *bullet = P_FirstEnt(world); bullet->valid; bullet = P_NextEnt(bullet)) for (P_Ent *bullet = P_FirstEnt(blend_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet))
{ {
if (bullet->is_bullet) if (bullet->is_bullet)
{ {
@ -2816,7 +3004,7 @@ void V_TickForever(WaveLaneCtx *lane)
if (0) if (0)
{ {
for (P_Ent *bullet = P_FirstEnt(world); bullet->valid; bullet = P_NextEnt(bullet)) for (P_Ent *bullet = P_FirstEnt(blend_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet))
{ {
if (bullet->is_bullet && bullet->has_hit) if (bullet->is_bullet && bullet->has_hit)
{ {
@ -2893,7 +3081,7 @@ void V_TickForever(WaveLaneCtx *lane)
// { // {
// for (P_Ent *firer = P_FirstEnt(world); firer->valid; firer = P_NextEnt(firer)) // for (P_Ent *firer = P_FirstEnt(blend_frame); firer->valid; firer = P_NextEnt(firer))
// { // {
// if (firer->fire_held) // if (firer->fire_held)
// { // {
@ -2908,7 +3096,7 @@ void V_TickForever(WaveLaneCtx *lane)
// P_RaycastResult victim_raycast = Zi; // P_RaycastResult victim_raycast = Zi;
// { // {
// f32 closest_len_sq = Inf; // f32 closest_len_sq = Inf;
// for (P_Ent *victim = P_FirstEnt(world); victim->valid; victim = P_NextEnt(victim)) // for (P_Ent *victim = P_FirstEnt(blend_frame); victim->valid; victim = P_NextEnt(victim))
// { // {
// if (victim != firer) // if (victim != firer)
// { // {
@ -2979,7 +3167,7 @@ void V_TickForever(WaveLaneCtx *lane)
// // for (P_QueryResult query = P_FirstRaycast(wrold, ray_start, ray_dir); query. // // for (P_QueryResult query = P_FirstRaycast(wrold, ray_start, ray_dir); query.
// // P_RaycastWorldResult hits = P_RaycastWorld(world, ray_start, ray_dir) // // P_RaycastWorldResult hits = P_RaycastWorld(blend_frame, ray_start, ray_dir)
// // { // // {
// // } // // }
// } // }
@ -3018,91 +3206,91 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Debug draw //- Debug draw
{ // {
// Copy debug draw data from sim // // Copy debug draw data from sim
if (received_unseen_tick) // if (received_unseen_tick)
{ // {
ResetArena(sim_debug_arena); // ResetArena(sim_debug_arena);
first_sim_debug_draw_node = 0; // first_sim_debug_draw_node = 0;
last_sim_debug_draw_node = 0; // last_sim_debug_draw_node = 0;
{ // {
i64 dst_idx = 0; // i64 dst_idx = 0;
P_DebugDrawNode *dst_nodes = PushStructsNoZero(sim_debug_arena, P_DebugDrawNode, sim_output->debug_draw_nodes_count); // P_DebugDrawNode *dst_nodes = PushStructsNoZero(sim_debug_arena, P_DebugDrawNode, sim_output->debug_draw_nodes_count);
for (P_DebugDrawNode *src = sim_output->first_debug_draw_node; src; src = src->next) // for (P_DebugDrawNode *src = sim_output->first_debug_draw_node; src; src = src->next)
{ // {
P_DebugDrawNode *dst = &dst_nodes[dst_idx]; // P_DebugDrawNode *dst = &dst_nodes[dst_idx];
*dst = *src; // *dst = *src;
dst_idx += 1; // dst_idx += 1;
SllQueuePush(first_sim_debug_draw_node, last_sim_debug_draw_node, dst); // SllQueuePush(first_sim_debug_draw_node, last_sim_debug_draw_node, dst);
} // }
} // }
} // }
// Merge vis debug draws with sim debug draws // // Merge vis debug draws with sim debug draws
P_DebugDrawNode *first_debug_draw_node = first_sim_debug_draw_node; // P_DebugDrawNode *first_debug_draw_node = first_sim_debug_draw_node;
P_DebugDrawNode *last_debug_draw_node = last_sim_debug_draw_node; // P_DebugDrawNode *last_debug_draw_node = last_sim_debug_draw_node;
if (P_tl.first_debug_draw_node) // if (P_tl.first_debug_draw_node)
{ // {
if (last_debug_draw_node) // if (last_debug_draw_node)
{ // {
last_debug_draw_node->next = P_tl.first_debug_draw_node; // last_debug_draw_node->next = P_tl.first_debug_draw_node;
} // }
else // else
{ // {
first_debug_draw_node = P_tl.first_debug_draw_node; // first_debug_draw_node = P_tl.first_debug_draw_node;
} // }
last_debug_draw_node = P_tl.last_debug_draw_node; // last_debug_draw_node = P_tl.last_debug_draw_node;
} // }
// Push draws // // Push draws
for (P_DebugDrawNode *n = first_debug_draw_node; n; n = n->next) // for (P_DebugDrawNode *n = first_debug_draw_node; n; n = n->next)
{ // {
Vec4 color = Vec4FromU32(n->srgb32); // Vec4 color = Vec4FromU32(n->srgb32);
i32 detail = 24; // i32 detail = 24;
f32 radius = 5; // f32 radius = 5;
switch(n->kind) // switch(n->kind)
{ // {
case P_DebugDrawKind_Point: // case P_DebugDrawKind_Point:
{ // {
Vec2 ui_p = MulXformV2(frame->xf.world_to_ui, n->point.p); // Vec2 ui_p = MulXformV2(frame->xf.world_to_ui, n->point.p);
V_DrawPoint(ui_p, color); // V_DrawPoint(ui_p, color);
} break; // } break;
case P_DebugDrawKind_Line: // case P_DebugDrawKind_Line:
{ // {
Vec2 ui_p0 = MulXformV2(frame->xf.world_to_ui, n->line.p0); // Vec2 ui_p0 = MulXformV2(frame->xf.world_to_ui, n->line.p0);
Vec2 ui_p1 = MulXformV2(frame->xf.world_to_ui, n->line.p1); // Vec2 ui_p1 = MulXformV2(frame->xf.world_to_ui, n->line.p1);
V_DrawLine(ui_p0, ui_p1, color); // V_DrawLine(ui_p0, ui_p1, color);
} break; // } break;
case P_DebugDrawKind_Rect: // case P_DebugDrawKind_Rect:
{ // {
Rng2 ui_rect = Zi; // Rng2 ui_rect = Zi;
ui_rect.p0 = MulXformV2(frame->xf.world_to_ui, n->rect.p0); // ui_rect.p0 = MulXformV2(frame->xf.world_to_ui, n->rect.p0);
ui_rect.p1 = MulXformV2(frame->xf.world_to_ui, n->rect.p1); // ui_rect.p1 = MulXformV2(frame->xf.world_to_ui, n->rect.p1);
V_DrawRect(ui_rect, color, V_DrawFlag_Line); // V_DrawRect(ui_rect, color, V_DrawFlag_Line);
} break; // } break;
case P_DebugDrawKind_Shape: // case P_DebugDrawKind_Shape:
{ // {
P_Shape ui_shape = P_MulXformShape(frame->xf.world_to_ui, n->shape); // P_Shape ui_shape = P_MulXformShape(frame->xf.world_to_ui, n->shape);
V_DrawShape(ui_shape, color, detail, V_DrawFlag_Line); // V_DrawShape(ui_shape, color, detail, V_DrawFlag_Line);
} break; // } break;
} // }
} // }
// Reset vis debug draws // // Reset vis debug draws
ResetArena(P_tl.debug_arena); // ResetArena(P_tl.debug_arena);
P_tl.first_debug_draw_node = 0; // P_tl.first_debug_draw_node = 0;
P_tl.last_debug_draw_node = 0; // P_tl.last_debug_draw_node = 0;
P_tl.debug_draw_nodes_count = 0; // P_tl.debug_draw_nodes_count = 0;
} // }
////////////////////////////// //////////////////////////////
//- Draw entities //- Draw entities
// for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) // for (P_Ent *ent = P_FirstEnt(blend_frame); ent->valid; ent = P_NextEnt(ent))
// { // {
// Xform ent_to_world_xf = ent->xf; // Xform ent_to_world_xf = ent->xf;
// Xform ent_to_draw_xf = MulXform(frame->xf.world_to_draw, ent_to_world_xf); // Xform ent_to_draw_xf = MulXform(frame->xf.world_to_draw, ent_to_world_xf);
@ -3260,7 +3448,7 @@ void V_TickForever(WaveLaneCtx *lane)
G_CopyCpuToTexture( G_CopyCpuToTexture(
frame->cl, frame->cl,
gpu_tiles, VEC3I32(0, 0, 0), gpu_tiles, VEC3I32(0, 0, 0),
world->tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1), blend_world->tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1),
RNG3I32(VEC3I32(0, 0, 0), VEC3I32(tiles_dims.x, tiles_dims.y, 1)) RNG3I32(VEC3I32(0, 0, 0), VEC3I32(tiles_dims.x, tiles_dims.y, 1))
); );
G_DumbMemoryLayoutSync(frame->cl, gpu_tiles, G_Layout_DirectQueue_ShaderRead); G_DumbMemoryLayoutSync(frame->cl, gpu_tiles, G_Layout_DirectQueue_ShaderRead);
@ -3372,28 +3560,28 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Prune ents //- Prune ents
{ // {
i64 ents_to_prune_count = 0; // i64 ents_to_prune_count = 0;
P_Ent **ents_to_prune = PushStructsNoZero(frame->arena, P_Ent *, world->ents_count); // P_Ent **ents_to_prune = PushStructsNoZero(frame->arena, P_Ent *, world->ents_count);
for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) // for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent))
{ // {
if (ent->exists <= 0) // if (ent->exists <= 0)
{ // {
ents_to_prune[ents_to_prune_count] = ent; // ents_to_prune[ents_to_prune_count] = ent;
ents_to_prune_count += 1; // ents_to_prune_count += 1;
} // }
} // }
for (i64 prune_idx = 0; prune_idx < ents_to_prune_count; ++prune_idx) // for (i64 prune_idx = 0; prune_idx < ents_to_prune_count; ++prune_idx)
{ // {
// FIXME: Add to free list // // FIXME: Add to free list
P_Ent *ent = ents_to_prune[prune_idx]; // P_Ent *ent = ents_to_prune[prune_idx];
P_EntBin *bin = &world->ent_bins[ent->key.v % world->ent_bins_count]; // P_EntBin *bin = &world->ent_bins[ent->key.v % world->ent_bins_count];
DllQueueRemoveNP(bin->first, bin->last, ent, next_in_bin, prev_in_bin); // DllQueueRemoveNP(bin->first, bin->last, ent, next_in_bin, prev_in_bin);
DllQueueRemove(world->first_ent, world->last_ent, ent); // DllQueueRemove(world->first_ent, world->last_ent, ent);
world->ents_count -= 1; // world->ents_count -= 1;
} // }
} // }
////////////////////////////// //////////////////////////////
//- End frame //- End frame

View File

@ -260,7 +260,7 @@ Struct(V_Frame)
Rng2 draw_selection; Rng2 draw_selection;
Rng2 world_selection; Rng2 world_selection;
// Commands // Vis commands
i64 cmds_count; i64 cmds_count;
V_CmdNode *first_cmd_node; V_CmdNode *first_cmd_node;
V_CmdNode *last_cmd_node; V_CmdNode *last_cmd_node;
@ -271,11 +271,6 @@ Struct(V_Frame)
f32 fire_held; f32 fire_held;
f32 fire_presses; f32 fire_presses;
// Sim cmds
u64 sim_cmds_count;
P_CmdNode *first_sim_cmd_node;
P_CmdNode *last_sim_cmd_node;
// Emitters // Emitters
i64 emitters_count; i64 emitters_count;
V_EmitterNode *first_emitter_node; V_EmitterNode *first_emitter_node;
@ -291,6 +286,9 @@ Struct(V_Ctx)
V_Panel *root_panel; V_Panel *root_panel;
V_Window *dragging_window; V_Window *dragging_window;
// Sim commands
P_CmdList sim_cmds;
// Atomic monotonically increasing allocation counter sequence for GPU particle ring buffer // Atomic monotonically increasing allocation counter sequence for GPU particle ring buffer
u32 particle_seq; u32 particle_seq;

View File

@ -94,7 +94,7 @@ TAR_Archive TAR_ArchiveFromString(Arena *arena, String data, String prefix)
archive.lookup = InitDict(arena, (u64)((f64)num_files * TAR_ArchiveLookupTableCapacityFactor)); archive.lookup = InitDict(arena, (u64)((f64)num_files * TAR_ArchiveLookupTableCapacityFactor));
for (TAR_Entry *entry = archive.head; entry; entry = entry->next) for (TAR_Entry *entry = archive.head; entry; entry = entry->next)
{ {
u64 hash = HashFnv64(Fnv64Basis, entry->file_name); u64 hash = HashString(entry->file_name);
SetDictValue(arena, archive.lookup, hash, (u64)entry); SetDictValue(arena, archive.lookup, hash, (u64)entry);
} }
@ -113,7 +113,7 @@ TAR_Archive TAR_ArchiveFromString(Arena *arena, String data, String prefix)
{ {
if (parent_dir_name.text[parent_dir_name.len - 1] == '/') if (parent_dir_name.text[parent_dir_name.len - 1] == '/')
{ {
u64 hash = HashFnv64(Fnv64Basis, parent_dir_name); u64 hash = HashString(parent_dir_name);
parent_entry = (TAR_Entry *)DictValueFromHash(archive.lookup, hash); parent_entry = (TAR_Entry *)DictValueFromHash(archive.lookup, hash);
break; break;
} }
@ -133,7 +133,7 @@ TAR_Archive TAR_ArchiveFromString(Arena *arena, String data, String prefix)
Readonly Global TAR_Entry g_nil_tar_entry = ZI; Readonly Global TAR_Entry g_nil_tar_entry = ZI;
TAR_Entry *TAR_EntryFromName(TAR_Archive *archive, String name) TAR_Entry *TAR_EntryFromName(TAR_Archive *archive, String name)
{ {
u64 hash = HashFnv64(Fnv64Basis, name); u64 hash = HashString(name);
TAR_Entry *lookup = (TAR_Entry *)DictValueFromHash(archive->lookup, hash); TAR_Entry *lookup = (TAR_Entry *)DictValueFromHash(archive->lookup, hash);
return lookup ? lookup : &g_nil_tar_entry; return lookup ? lookup : &g_nil_tar_entry;
} }

View File

@ -24,7 +24,7 @@ UI_Key UI_KeyFromString(String str)
{ {
u64 top_tag = UI_Top(Tag); u64 top_tag = UI_Top(Tag);
UI_Key key = Zi; UI_Key key = Zi;
key.v = HashFnv64(top_tag, str); key.v = HashStringEx(top_tag, str);
return key; return key;
} }
@ -340,7 +340,7 @@ void UI_PushDefaults(void)
case UI_StyleKind_FontSize: { desc.style.FontSize = 16.0f; } break; case UI_StyleKind_FontSize: { desc.style.FontSize = 16.0f; } break;
case UI_StyleKind_Tint: { desc.style.Tint = Color_White; } break; case UI_StyleKind_Tint: { desc.style.Tint = Color_White; } break;
case UI_StyleKind_TextColor: { desc.style.TextColor = Color_White; } break; case UI_StyleKind_TextColor: { desc.style.TextColor = Color_White; } break;
case UI_StyleKind_Tag: { desc.style.Tag = HashFnv64(Fnv64Basis, Lit("root")); } break; case UI_StyleKind_Tag: { desc.style.Tag = HashString(Lit("root")); } break;
case UI_StyleKind_DebugColor: { desc.style.DebugColor = Rgba(1, 0, 1, 0.5); } break; case UI_StyleKind_DebugColor: { desc.style.DebugColor = Rgba(1, 0, 1, 0.5); } break;
case UI_StyleKind_InvisibleDebugColor: { desc.style.InvisibleDebugColor = Rgba(0, 1, 1, 0.25); } break; case UI_StyleKind_InvisibleDebugColor: { desc.style.InvisibleDebugColor = Rgba(0, 1, 1, 0.25); } break;
case UI_StyleKind_BackgroundTextureSliceUv: { desc.style.BackgroundTextureSliceUv = RNG2(VEC2(0, 0), VEC2(1, 1)); } break; case UI_StyleKind_BackgroundTextureSliceUv: { desc.style.BackgroundTextureSliceUv = RNG2(VEC2(0, 0), VEC2(1, 1)); } break;