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
////////////////////////////////////////////////////////////
//~ Basic mixing/hashing
//~ Basic mixing
// Based on Jon Maiga's "mx3"
// 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);
}
#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

View File

@ -58,7 +58,7 @@ void BootstrapCmdline(void)
{
CommandlineArg arg = tmp_args[i];
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);
n->arg = arg;
n->hash = hash;
@ -85,7 +85,7 @@ String StringFromCommandlineIdx(i32 idx)
CommandlineArg CommandlineArgFromName(String name)
{
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)
{
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);
for (u64 i = 0; i < num_entries; ++i)
{
u64 store_hash = BB_ReadUBits(&br, 64);
u64 name_start = BB_ReadUBits(&br, 64);
u64 name_len = 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);
entry->name = STRING(name_len, archive.text + name_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)];
SllQueuePushN(bin->first, bin->last, entry, next_in_bin);
@ -50,7 +51,7 @@ b32 IsResourceNil(ResourceKey resource)
ResourceKey ResourceKeyFromStore(ResourceStore *store, String name)
{
ResourceKey result = Zi;
result.v = HashFnv64(store->v, name);
result.v = HashStringEx(store->v, name);
return result;
}

View File

@ -502,14 +502,21 @@ String PathFromString(Arena *arena, String str, u8 path_delimiter)
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);
return result;
}
u64 HashString(String str)
{
return HashStringEx(Fnv64Basis, str);
return HashStringEx(0, str);
}
u64 HashF_(String fmt, ...)

View File

@ -112,7 +112,7 @@ i64 TimeNs(void)
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)

View File

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

View File

@ -97,7 +97,7 @@ M_TokenFileList M_TokensFromSrcDirs(Arena *arena, StringList src_dirs)
if (StringEndsWith(file_name, Lit(".lay")))
{
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)
{
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)
{
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);
}
@ -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);
{
u64 entry_hash = HashFnv64(Fnv64Basis, entry_token->s);
u64 entry_hash = HashString(entry_token->s);
u64 kind_plus_1 = DictValueFromHash(rules_dict, entry_hash);
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;
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 *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)
{
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);
M_Layer *layer = state->layer;
if (layer->valid)
@ -497,7 +497,7 @@ M_Layer M_FlattenEntries(Arena *arena, M_LayerList unflattened, StringList start
if (should_include)
{
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);
M_Layer *downstream_layer = downstream_layer_state->layer;
if (downstream_layer->valid)
@ -538,7 +538,7 @@ M_Layer M_FlattenEntries(Arena *arena, M_LayerList unflattened, StringList start
if (dep_token->valid)
{
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);
M_Layer *dep_layer = dep_layer_state->layer;
if (dep_layer->valid)

View File

@ -58,7 +58,7 @@ void N_ReleaseHost(N_Host *host)
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)
@ -227,9 +227,9 @@ void N_ReleaseChannel(N_Channel *channel)
u64 N_HashFromMsg(N_ChannelId channel_id, u64 msg_id)
{
u64 result = Fnv64Basis;
result = HashFnv64(result, StringFromStruct(&channel_id));
result = HashFnv64(result, StringFromStruct(&msg_id));
u64 result = 0;
result = HashStringEx(result, StringFromStruct(&channel_id));
result = HashStringEx(result, StringFromStruct(&msg_id));
return result;
}

View File

@ -6,6 +6,10 @@ Readonly P_Ent P_NilEnt = {
.look = { 0, -1 },
};
Readonly P_Frame P_NilFrame = {
0
};
////////////////////////////////////////////////////////////
//~ Bootstrap
@ -37,14 +41,19 @@ b32 P_IsEntNil(P_Ent *ent)
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
b32 P_MatchKey(P_Key a, P_Key b)
{
return a.v == b.v;
}
P_Key P_RandKey(void)
{
// 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;
}
////////////////////////////////////////////////////////////
//~ 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
@ -1106,12 +1030,14 @@ Vec2 P_EdgePointFromShape(P_Shape shape, Vec2 dir)
////////////////////////////////////////////////////////////
//~ 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;
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)
{
if (e->key.v == key.v)
@ -1124,20 +1050,15 @@ P_Ent *P_EntFromKeyEx(P_World *world, P_Key key, i64 tick)
return result;
}
P_Ent *P_EntFromKey(P_World *world, P_Key key)
{
return P_EntFromKeyEx(world, key, 0);
}
////////////////////////////////////////////////////////////
//~ Iteration helpers
P_Ent *P_FirstEnt(P_World *world)
P_Ent *P_FirstEnt(P_Frame *frame)
{
P_Ent *result = &P_NilEnt;
if (world->first_ent)
if (frame->first_ent)
{
result = world->first_ent;
result = frame->first_ent;
}
return result;
}
@ -1159,51 +1080,10 @@ P_Ent *P_PushTempEnt(Arena *arena, P_EntList *list)
++list->count;
P_Ent *ent = &n->ent;
*ent = P_NilEnt;
ent->valid = 1;
ent->exists = 1;
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
@ -1267,3 +1147,263 @@ void P_DebugDrawShape(P_Shape shape, Vec4 srgb)
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
P_Key key;
b32 valid;
//////////////////////////////
//- Build data
@ -62,7 +61,7 @@ Struct(P_Ent)
b32 is_player;
f32 health;
Xform last_xf;
Xform prev_xf;
Xform xf;
Vec2 move;
@ -112,9 +111,22 @@ Struct(P_EntBin)
////////////////////////////////////////////////////////////
//~ 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 time_ns;
@ -124,6 +136,28 @@ Struct(P_World)
i64 ent_bins_count;
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;
};
@ -155,7 +189,9 @@ Struct(P_DeltaNode)
Struct(P_Snapshot)
{
u64 seed;
u64 world_seed;
// i64 src_world_tick;
i64 src_tick;
i64 tick;
i64 time_ns;
@ -170,6 +206,13 @@ Struct(P_SnapshotNode)
P_Snapshot snapshot;
};
Struct(P_SnapshotList)
{
i64 count;
P_SnapshotNode *first;
P_SnapshotNode *last;
};
////////////////////////////////////////////////////////////
//~ Collision types
@ -306,6 +349,7 @@ Enum(P_CmdKind)
Struct(P_Cmd)
{
i64 tick;
P_CmdKind kind;
// Delta
@ -325,20 +369,25 @@ Struct(P_CmdNode)
P_Cmd cmd;
};
Struct(P_CmdList)
{
i64 count;
P_CmdNode *first;
P_CmdNode *last;
};
Struct(P_InputState)
{
Arena *arena;
P_CmdNode *first_cmd_node;
P_CmdNode *last_cmd_node;
u64 cmds_count;
P_CmdList cmds;
};
Struct(P_OutputState)
{
Arena *arena;
P_SnapshotNode *first_snapshot_node;
P_SnapshotNode *last_snapshot_node;
u64 snapshots_count;
P_SnapshotList snapshots;
P_DebugDrawNode *first_debug_draw_node;
P_DebugDrawNode *last_debug_draw_node;
@ -363,6 +412,7 @@ Struct(P_Ctx)
Struct(P_ThreadLocalCtx)
{
// TODO: Move this to world frame
Arena *debug_arena;
b32 debug_draw_enabled;
P_DebugDrawNode *first_debug_draw_node;
@ -373,6 +423,9 @@ Struct(P_ThreadLocalCtx)
extern P_Ctx P;
extern ThreadLocal P_ThreadLocalCtx P_tl;
extern Readonly P_Ent P_NilEnt;
extern Readonly P_Frame P_NilFrame;
////////////////////////////////////////////////////////////
//~ Bootstrap
@ -383,11 +436,12 @@ void P_Bootstrap(void);
b32 P_IsKeyNil(P_Key key);
b32 P_IsEntNil(P_Ent *ent);
b32 P_MatchKey(P_Key a, P_Key b);
b32 P_IsFrameNil(P_Frame *frame);
////////////////////////////////////////////////////////////
//~ Key helpers
b32 P_MatchKey(P_Key a, P_Key b);
P_Key P_RandKey(void);
#define P_FmtKey(key) FmtHandle((key).v)
@ -397,11 +451,6 @@ P_Key P_RandKey(void);
String P_NameFromTileKind(P_TileKind kind);
////////////////////////////////////////////////////////////
//~ Delta helpers
void P_UpdateWorldFromDelta(Arena *arena, P_World *world, P_Delta *delta);
////////////////////////////////////////////////////////////
//~ Shape helpers
@ -431,20 +480,18 @@ Vec2 P_EdgePointFromShape(P_Shape shape, Vec2 dir);
////////////////////////////////////////////////////////////
//~ Lookup helpers
P_Ent *P_EntFromKeyEx(P_World *world, P_Key key, i64 tick);
P_Ent *P_EntFromKey(P_World *world, P_Key key);
P_Ent *P_EntFromKey(P_Frame *frame, P_Key key);
////////////////////////////////////////////////////////////
//~ Iteration helpers
P_Ent *P_FirstEnt(P_World *world);
P_Ent *P_FirstEnt(P_Frame *frame);
P_Ent *P_NextEnt(P_Ent *e);
////////////////////////////////////////////////////////////
//~ List helpers
P_Ent *P_PushTempEnt(Arena *arena, P_EntList *list);
void P_SpawnEntsFromList(Arena *arena, P_World *world, P_EntList ents);
////////////////////////////////////////////////////////////
//~ Debug draw
@ -453,3 +500,19 @@ void P_DebugDrawPoint(Vec2 p, Vec4 srgb);
void P_DebugDrawLine(Vec2 p0, Vec2 p1, Vec4 srgb);
void P_DebugDrawRect(Rng2 rect, 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));
P_tl.debug_arena = AcquireArena(Gibi(64));
Arena *world_arena = AcquireArena(Gibi(64));
P_World *world = 0;
P_World *world = P_AcquireWorld();
// TODO: Real per-client deltas
b32 has_sent_initial_tick = 0;
//////////////////////////////
//- Sim loop
@ -39,65 +37,69 @@ void S_TickForever(WaveLaneCtx *lane)
{
shutdown = Atomic32Fetch(&S.shutdown);
P_tl.debug_draw_enabled = TweakBool("Simulation debug draw", 1);
ResetArena(frame_arena);
//////////////////////////////
//- Swap
{
b32 swapin = IsSwappedIn();
b32 swapout = shutdown && IsSwappingOut();
// {
// b32 swapin = IsSwappedIn();
// b32 swapout = shutdown && IsSwappingOut();
//- Swap in
if (!world)
{
String packed = Zi;
if (swapin)
{
packed = SwappedStateFromName(frame_arena, Lit("pp_sim.swp"));
}
P_UnpackedWorld unpacked = P_UnpackWorld(frame_arena, packed);
// //- Swap in
// if (!world)
// {
// String packed = Zi;
// if (swapin)
// {
// packed = SwappedStateFromName(frame_arena, Lit("pp_sim.swp"));
// }
// P_UnpackedWorld unpacked = P_UnpackWorld(frame_arena, packed);
ResetArena(world_arena);
world = PushStruct(world_arena, P_World);
world->seed = unpacked.seed;
world->tick = unpacked.tick;
world->time_ns = unpacked.time_ns;
if (world->seed == 0)
{
TrueRand(StringFromStruct(&world->seed));
}
// ResetArena(world_arena);
// world = PushStruct(world_arena, P_World);
// world->seed = unpacked.seed;
// world_frame->tick = unpacked.tick;
// world_frame->time_ns = unpacked.time_ns;
// if (world->seed == 0)
// {
// TrueRand(StringFromStruct(&world->seed));
// }
world->ent_bins_count = Kibi(16);
world->ent_bins = PushStructs(world_arena, P_EntBin, world->ent_bins_count);
// world_frame->ent_bins_count = Kibi(16);
// world_frame->ent_bins = PushStructs(world_arena, P_EntBin, world_frame->ent_bins_count);
// Copy tiles
world->tiles = PushStructsNoZero(world_arena, u8, P_TilesCount);
CopyStructs(world->tiles, unpacked.tiles, P_TilesCount);
// // Copy tiles
// world->tiles = PushStructsNoZero(world_arena, u8, P_TilesCount);
// CopyStructs(world->tiles, unpacked.tiles, P_TilesCount);
// Copy ents
P_SpawnEntsFromList(world_arena, world, unpacked.ents);
}
// // Copy ents
// P_SpawnEntsFromList(world_arena, world, unpacked.ents);
// }
//- Swap out
if (swapout)
{
String packed = P_PackWorld(frame_arena, world);
WriteSwappedState(Lit("pp_sim.swp"), packed);
}
}
// //- Swap out
// if (swapout)
// {
// String packed = P_PackWorld(frame_arena, world);
// WriteSwappedState(Lit("pp_sim.swp"), packed);
// }
// }
//////////////////////////////
//- 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 sim_dt_ns = NsFromSeconds(1) / SIM_TICKS_PER_SECOND;
f64 sim_dt = SecondsFromNs(sim_dt_ns);
world->tick += 1;
//////////////////////////////
//- Pop sim commands
//- Pop commands
P_InputState *input = 0;
LockTicketMutex(&P.sim_input_back_tm);
@ -114,9 +116,9 @@ void S_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- 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
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;
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 apply relevant cmds based on tick
i64 applied_user_deltas_count = 0;
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)
P_Delta **applied_user_deltas = PushStructsNoZero(frame_arena, P_Delta *, input->cmds.count);
for (P_CmdNode *cmd_node = input->cmds.first; cmd_node; cmd_node = cmd_node->next)
{
P_Cmd *cmd = &cmd_node->cmd;
if (cmd->kind == P_CmdKind_Delta)
@ -176,7 +180,20 @@ void S_TickForever(WaveLaneCtx *lane)
}
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
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;
}
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;
if (cmd.kind == P_CmdKind_Control)
{
P_Ent *target = P_EntFromKey(world, cmd.target);
if (target->valid)
P_Ent *target = P_EntFromKey(world_frame, cmd.target);
if (!P_IsEntNil(target))
{
target->move = ClampVec2Len(cmd.move, 1);
target->look = cmd.look;
@ -212,7 +231,7 @@ void S_TickForever(WaveLaneCtx *lane)
// //////////////////////////////
// //- 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)
// {
@ -227,7 +246,7 @@ void S_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- 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 desired_xf = xf;
@ -300,10 +319,10 @@ void S_TickForever(WaveLaneCtx *lane)
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);
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)
{
@ -338,7 +357,7 @@ void S_TickForever(WaveLaneCtx *lane)
}
if (constraint)
{
constraint->last_touched_tick = world->tick;
constraint->last_touched_tick = world_frame->tick;
constraint->normal = collision.collision_normal;
// constraint->friction = SqrtF32(ent0->friction * ent1->friction);
constraint->friction = 0;
@ -416,13 +435,13 @@ void S_TickForever(WaveLaneCtx *lane)
// // Debug draw
// {
// // P_Ent *ent0 = P_EntFromKey(world, constraint->ent0);
// // P_Ent *ent1 = P_EntFromKey(world, constraint->ent1);
// // P_Ent *ent0 = P_EntFromKey(world_frame, constraint->ent0);
// // P_Ent *ent1 = P_EntFromKey(world_frame, constraint->ent1);
// Vec2 normal = constraint->normal;
// Vec2 center0 = Zi;
// Vec2 center1 = Zi;
// if (ent0->valid) center0 = P_WorldShapeFromEnt(ent0).center_of_mass;
// if (ent1->valid) center1 = P_WorldShapeFromEnt(ent1).center_of_mass;
// if (!P_IsEntNil(ent0)) center0 = P_WorldShapeFromEnt(ent0).center_of_mass;
// if (!P_IsEntNil(ent1)) center1 = P_WorldShapeFromEnt(ent1).center_of_mass;
// Vec2 p0 = AddVec2(center0, vcp0);
// Vec2 p1 = AddVec2(center1, vcp1);
// P_DebugDrawPoint(p0, Color_Cyan);
@ -457,15 +476,15 @@ void S_TickForever(WaveLaneCtx *lane)
// 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)
// {
// Xform last_xf = ent->last_xf;
// Xform prev_xf = ent->prev_xf;
// Xform xf = ent->xf;
// 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);
// // TODO: Real constraint data
@ -514,7 +533,7 @@ void S_TickForever(WaveLaneCtx *lane)
// }
// if (constraint)
// {
// constraint->last_touched_tick = world->tick;
// constraint->last_touched_tick = world_frame->tick;
// constraint->normal = collision.collision_normal;
// // TODO: Real masses
@ -589,13 +608,13 @@ void S_TickForever(WaveLaneCtx *lane)
// // Debug draw
// {
// P_Ent *ent0 = P_EntFromKey(world, constraint->ent0);
// P_Ent *ent1 = P_EntFromKey(world, constraint->ent1);
// P_Ent *ent0 = P_EntFromKey(world_frame, constraint->ent0);
// P_Ent *ent1 = P_EntFromKey(world_frame, constraint->ent1);
// Vec2 normal = constraint->normal;
// Vec2 center0 = Zi;
// Vec2 center1 = Zi;
// if (ent0->valid) center0 = P_WorldShapeFromEnt(ent0).center_of_mass;
// if (ent1->valid) center1 = P_WorldShapeFromEnt(ent1).center_of_mass;
// if (!P_IsEntNil(ent0)) center0 = P_WorldShapeFromEnt(ent0).center_of_mass;
// if (!P_IsEntNil(ent1)) center1 = P_WorldShapeFromEnt(ent1).center_of_mass;
// Vec2 p0 = AddVec2(center0, vcp0);
// Vec2 p1 = AddVec2(center1, vcp1);
// P_DebugDrawPoint(p0, Color_Cyan);
@ -616,9 +635,11 @@ void S_TickForever(WaveLaneCtx *lane)
{
P_Constraint *constraint = &constraints[constraint_idx];
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;
}
@ -691,8 +712,8 @@ void S_TickForever(WaveLaneCtx *lane)
{
P_Constraint *constraint = &constraints[constraint_idx];
P_Ent *ent0 = P_EntFromKey(world, constraint->ent0);
P_Ent *ent1 = P_EntFromKey(world, constraint->ent1);
P_Ent *ent0 = P_EntFromKey(world_frame, constraint->ent0);
P_Ent *ent1 = P_EntFromKey(world_frame, constraint->ent1);
Vec2 v0 = ent0->solved_v;
Vec2 v1 = ent1->solved_v;
@ -716,12 +737,12 @@ void S_TickForever(WaveLaneCtx *lane)
w1 += WedgeVec2(vcp1, impulse) * constraint->inv_i1;
}
if (ent0->valid)
if (!P_IsEntNil(ent0))
{
ent0->solved_v = v0;
ent0->solved_w = w0;
}
if (ent1->valid)
if (!P_IsEntNil(ent1))
{
ent1->solved_v = v1;
ent1->solved_w = w1;
@ -735,8 +756,8 @@ void S_TickForever(WaveLaneCtx *lane)
{
P_Constraint *constraint = &constraints[constraint_idx];
P_Ent *ent0 = P_EntFromKey(world, constraint->ent0);
P_Ent *ent1 = P_EntFromKey(world, constraint->ent1);
P_Ent *ent0 = P_EntFromKey(world_frame, constraint->ent0);
P_Ent *ent1 = P_EntFromKey(world_frame, constraint->ent1);
f32 inv_m0 = constraint->inv_m0;
f32 inv_m1 = constraint->inv_m1;
@ -749,11 +770,11 @@ void S_TickForever(WaveLaneCtx *lane)
Vec2 center0 = constraint->static_center0;
Vec2 center1 = constraint->static_center1;
if (ent0->valid)
if (!P_IsEntNil(ent0))
{
center0 = P_WorldShapeFromEnt(ent0).center_of_mass;
}
if (ent1->valid)
if (!P_IsEntNil(ent1))
{
center1 = P_WorldShapeFromEnt(ent1).center_of_mass;
}
@ -844,12 +865,12 @@ void S_TickForever(WaveLaneCtx *lane)
w1 += WedgeVec2(vcp1, impulse) * inv_i1;
}
if (ent0->valid)
if (!P_IsEntNil(ent0))
{
ent0->solved_v = v0;
ent0->solved_w = w0;
}
if (ent1->valid)
if (!P_IsEntNil(ent1))
{
ent1->solved_v = v1;
ent1->solved_w = w1;
@ -859,7 +880,7 @@ void S_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- 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;
xf.og = AddVec2(xf.og, MulVec2(ent->solved_v, solver_dt));
@ -884,7 +905,7 @@ void S_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- 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)
{
@ -903,12 +924,12 @@ void S_TickForever(WaveLaneCtx *lane)
{
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_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;
@ -920,7 +941,7 @@ void S_TickForever(WaveLaneCtx *lane)
f32 spread = Tau * 0.01;
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)
{
i64 tick_bullets_count = bullets_per_fire;
@ -955,11 +976,11 @@ void S_TickForever(WaveLaneCtx *lane)
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
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)
{
@ -984,7 +1005,7 @@ void S_TickForever(WaveLaneCtx *lane)
P_RaycastResult victim_raycast = Zi;
{
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))
{
@ -1013,7 +1034,7 @@ void S_TickForever(WaveLaneCtx *lane)
}
}
if (closest_victim->valid)
if (!P_IsEntNil(closest_victim))
{
bullet->has_hit = 1;
bullet->hit_entry = victim_raycast.p;
@ -1064,12 +1085,12 @@ void S_TickForever(WaveLaneCtx *lane)
// P_Bullet *bullet = &bullets[bullet_idx];
// // 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;
// 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->dir = NormVec2(ent->look);
@ -1123,12 +1144,12 @@ void S_TickForever(WaveLaneCtx *lane)
// // P_Bullet *bullet = &bullets[bullet_idx];
// // // 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;
// // 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->dir = NormVec2(ent->look);
@ -1164,7 +1185,7 @@ void S_TickForever(WaveLaneCtx *lane)
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);
@ -1209,12 +1230,12 @@ void S_TickForever(WaveLaneCtx *lane)
P_OutputState *output = &P.sim_output_states[P.sim_output_back_idx];
P_SnapshotNode *snapshot_node = PushStruct(output->arena, P_SnapshotNode);
P_Snapshot *snapshot = &snapshot_node->snapshot;
SllQueuePush(output->first_snapshot_node, output->last_snapshot_node, snapshot_node);
++output->snapshots_count;
SllQueuePush(output->snapshots.first, output->snapshots.last, snapshot_node);
++output->snapshots.count;
snapshot->seed = world->seed;
snapshot->tick = world->tick;
snapshot->time_ns = world->time_ns;
snapshot->world_seed = world->seed;
snapshot->tick = world_frame->tick;
snapshot->time_ns = world_frame->time_ns;
// Forward user edit deltas
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
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;
{
@ -1287,8 +1308,8 @@ void S_TickForever(WaveLaneCtx *lane)
{
i64 ents_to_prune_count = 0;
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))
P_Ent **ents_to_prune = PushStructsNoZero(frame_arena, P_Ent *, world_frame->ents_count);
for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{
if (ent->exists <= 0)
{
@ -1302,10 +1323,10 @@ void S_TickForever(WaveLaneCtx *lane)
// FIXME: Add to freelist
// FIXME: Ensure sure prunes are received by user
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);
DllQueueRemove(world->first_ent, world->last_ent, ent);
world->ents_count -= 1;
DllQueueRemove(world_frame->first_ent, world_frame->last_ent, ent);
world_frame->ents_count -= 1;
}
}
@ -1321,7 +1342,7 @@ void S_TickForever(WaveLaneCtx *lane)
}
i64 frame_end_ns = TimeNs();
world->time_ns += sim_dt_ns;
world_frame->time_ns += sim_dt_ns;
//////////////////////////////
//- Sleep

View File

@ -6,19 +6,20 @@ String P_PackWorld(Arena *arena, P_World *src_world)
String result = Zi;
result.text = ArenaNext(arena, u8);
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, "\n").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, "time: %F\n", FmtSint(src_world->time_ns)).len;
result.len += StringF(arena, "tick: %F\n", FmtSint(src_frame->tick)).len;
result.len += StringF(arena, "time: %F\n", FmtSint(src_frame->time_ns)).len;
// Pack entities
// FIXME: Precision
result.len += PushString(arena, Lit("\nentities:\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
if (!ent->is_bullet)

View File

@ -43,11 +43,12 @@ V_Cmd *V_PushVisCmd(String name)
P_Cmd *V_PushSimCmd(P_CmdKind kind)
{
V_Frame *frame = V_CurrentFrame();
P_CmdNode *n = PushStruct(frame->arena, P_CmdNode);
// FIXME: Free list
Arena *perm = PermArena();
P_CmdNode *n = PushStruct(perm, P_CmdNode);
n->cmd.kind = kind;
SllQueuePush(frame->first_sim_cmd_node, frame->last_sim_cmd_node, n);
++frame->sim_cmds_count;
SllQueuePush(V.sim_cmds.first, V.sim_cmds.last, n);
++V.sim_cmds.count;
return &n->cmd;
}
@ -340,11 +341,9 @@ void V_TickForever(WaveLaneCtx *lane)
P_DebugDrawNode *first_sim_debug_draw_node = 0;
P_DebugDrawNode *last_sim_debug_draw_node = 0;
Arena *world_arena = AcquireArena(Gibi(64));
P_World *world = PushStruct(world_arena, P_World);
world->ent_bins_count = Kibi(16);
world->ent_bins = PushStructs(world_arena, P_EntBin, world->ent_bins_count);
world->tiles = PushStructs(world_arena, u8, P_TilesCount);
P_World *sim_world = P_AcquireWorld();
P_World *predict_world = P_AcquireWorld();
P_World *blend_world = P_AcquireWorld();
Vec2I32 tiles_dims = VEC2I32(P_TilesPitch, P_TilesPitch);
Vec2I32 cells_dims = VEC2I32(V_CellsPerMeter * P_WorldPitch, V_CellsPerMeter * P_WorldPitch);
@ -471,7 +470,7 @@ void V_TickForever(WaveLaneCtx *lane)
}
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_Shortcut *shortcut = PushStruct(perm, V_Shortcut);
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->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
@ -782,7 +804,7 @@ void V_TickForever(WaveLaneCtx *lane)
hotkey.shift = frame->held_buttons[Button_Shift];
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_Shortcut *shortcut = bin->first;
for (; shortcut; shortcut = shortcut->next_in_bin)
@ -848,7 +870,7 @@ void V_TickForever(WaveLaneCtx *lane)
Vec2 look_ratio = Zi;
look_ratio.y = 0.25;
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 = AddVec2(target_camera_pos, MulVec2Vec2(player->look, look_ratio));
target_camera_zoom = 1;
@ -1001,12 +1023,12 @@ void V_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- 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;
{
// TODO: Real world query
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);
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_BuildLabelF("World seed: 0x%F", FmtHex(world->seed));
UI_BuildLabelF("Entities count: %F", FmtSint(world->ents_count));
UI_BuildLabelF("World seed: 0x%F", FmtHex(blend_world->seed));
UI_BuildLabelF("Entities count: %F", FmtSint(blend_world->last_frame->ents_count));
}
UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y);
{
@ -2535,7 +2557,7 @@ void V_TickForever(WaveLaneCtx *lane)
case V_CmdKind_delete:
{
if (hovered_ent->valid)
if (!P_IsEntNil(hovered_ent))
{
P_Cmd *cmd = V_PushSimCmd(P_CmdKind_Delta);
cmd->delta.kind = P_DeltaKind_RawEnt;
@ -2553,10 +2575,10 @@ void V_TickForever(WaveLaneCtx *lane)
}
} break;
case V_CmdKind_clear_particles:
{
should_clear_particles = 1;
} break;
// case V_CmdKind_clear_particles:
// {
// should_clear_particles = 1;
// } break;
}
}
@ -2605,23 +2627,189 @@ void V_TickForever(WaveLaneCtx *lane)
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
i64 predict_to = sim_world->last_frame->tick + 10;
LockTicketMutex(&P.sim_input_back_tm);
{
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);
cmd_node->cmd = src->cmd;
SllQueuePush(v2s->first_cmd_node, v2s->last_cmd_node, cmd_node);
++v2s->cmds_count;
cmd_node->cmd.tick = predict_to;
SllQueuePush(v2s->cmds.first, v2s->cmds.last, cmd_node);
++v2s->cmds.count;
}
}
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)
{
@ -2816,7 +3004,7 @@ void V_TickForever(WaveLaneCtx *lane)
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)
{
@ -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)
// {
@ -2908,7 +3096,7 @@ void V_TickForever(WaveLaneCtx *lane)
// P_RaycastResult victim_raycast = Zi;
// {
// 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)
// {
@ -2979,7 +3167,7 @@ void V_TickForever(WaveLaneCtx *lane)
// // 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
{
// Copy debug draw data from sim
if (received_unseen_tick)
{
ResetArena(sim_debug_arena);
first_sim_debug_draw_node = 0;
last_sim_debug_draw_node = 0;
{
i64 dst_idx = 0;
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)
{
P_DebugDrawNode *dst = &dst_nodes[dst_idx];
*dst = *src;
dst_idx += 1;
SllQueuePush(first_sim_debug_draw_node, last_sim_debug_draw_node, dst);
}
}
}
// {
// // Copy debug draw data from sim
// if (received_unseen_tick)
// {
// ResetArena(sim_debug_arena);
// first_sim_debug_draw_node = 0;
// last_sim_debug_draw_node = 0;
// {
// i64 dst_idx = 0;
// 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)
// {
// P_DebugDrawNode *dst = &dst_nodes[dst_idx];
// *dst = *src;
// dst_idx += 1;
// SllQueuePush(first_sim_debug_draw_node, last_sim_debug_draw_node, dst);
// }
// }
// }
// Merge vis debug draws with sim debug draws
P_DebugDrawNode *first_debug_draw_node = first_sim_debug_draw_node;
P_DebugDrawNode *last_debug_draw_node = last_sim_debug_draw_node;
if (P_tl.first_debug_draw_node)
{
if (last_debug_draw_node)
{
last_debug_draw_node->next = P_tl.first_debug_draw_node;
}
else
{
first_debug_draw_node = P_tl.first_debug_draw_node;
}
last_debug_draw_node = P_tl.last_debug_draw_node;
}
// // Merge vis debug draws with sim debug draws
// P_DebugDrawNode *first_debug_draw_node = first_sim_debug_draw_node;
// P_DebugDrawNode *last_debug_draw_node = last_sim_debug_draw_node;
// if (P_tl.first_debug_draw_node)
// {
// if (last_debug_draw_node)
// {
// last_debug_draw_node->next = P_tl.first_debug_draw_node;
// }
// else
// {
// first_debug_draw_node = P_tl.first_debug_draw_node;
// }
// last_debug_draw_node = P_tl.last_debug_draw_node;
// }
// Push draws
for (P_DebugDrawNode *n = first_debug_draw_node; n; n = n->next)
{
Vec4 color = Vec4FromU32(n->srgb32);
i32 detail = 24;
f32 radius = 5;
switch(n->kind)
{
case P_DebugDrawKind_Point:
{
Vec2 ui_p = MulXformV2(frame->xf.world_to_ui, n->point.p);
V_DrawPoint(ui_p, color);
} break;
// // Push draws
// for (P_DebugDrawNode *n = first_debug_draw_node; n; n = n->next)
// {
// Vec4 color = Vec4FromU32(n->srgb32);
// i32 detail = 24;
// f32 radius = 5;
// switch(n->kind)
// {
// case P_DebugDrawKind_Point:
// {
// Vec2 ui_p = MulXformV2(frame->xf.world_to_ui, n->point.p);
// V_DrawPoint(ui_p, color);
// } break;
case P_DebugDrawKind_Line:
{
Vec2 ui_p0 = MulXformV2(frame->xf.world_to_ui, n->line.p0);
Vec2 ui_p1 = MulXformV2(frame->xf.world_to_ui, n->line.p1);
V_DrawLine(ui_p0, ui_p1, color);
} break;
// case P_DebugDrawKind_Line:
// {
// Vec2 ui_p0 = MulXformV2(frame->xf.world_to_ui, n->line.p0);
// Vec2 ui_p1 = MulXformV2(frame->xf.world_to_ui, n->line.p1);
// V_DrawLine(ui_p0, ui_p1, color);
// } break;
case P_DebugDrawKind_Rect:
{
Rng2 ui_rect = Zi;
ui_rect.p0 = MulXformV2(frame->xf.world_to_ui, n->rect.p0);
ui_rect.p1 = MulXformV2(frame->xf.world_to_ui, n->rect.p1);
V_DrawRect(ui_rect, color, V_DrawFlag_Line);
} break;
// case P_DebugDrawKind_Rect:
// {
// Rng2 ui_rect = Zi;
// ui_rect.p0 = MulXformV2(frame->xf.world_to_ui, n->rect.p0);
// ui_rect.p1 = MulXformV2(frame->xf.world_to_ui, n->rect.p1);
// V_DrawRect(ui_rect, color, V_DrawFlag_Line);
// } break;
case P_DebugDrawKind_Shape:
{
P_Shape ui_shape = P_MulXformShape(frame->xf.world_to_ui, n->shape);
V_DrawShape(ui_shape, color, detail, V_DrawFlag_Line);
} break;
}
}
// case P_DebugDrawKind_Shape:
// {
// P_Shape ui_shape = P_MulXformShape(frame->xf.world_to_ui, n->shape);
// V_DrawShape(ui_shape, color, detail, V_DrawFlag_Line);
// } break;
// }
// }
// Reset vis debug draws
ResetArena(P_tl.debug_arena);
P_tl.first_debug_draw_node = 0;
P_tl.last_debug_draw_node = 0;
P_tl.debug_draw_nodes_count = 0;
}
// // Reset vis debug draws
// ResetArena(P_tl.debug_arena);
// P_tl.first_debug_draw_node = 0;
// P_tl.last_debug_draw_node = 0;
// P_tl.debug_draw_nodes_count = 0;
// }
//////////////////////////////
//- 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_draw_xf = MulXform(frame->xf.world_to_draw, ent_to_world_xf);
@ -3260,7 +3448,7 @@ void V_TickForever(WaveLaneCtx *lane)
G_CopyCpuToTexture(
frame->cl,
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))
);
G_DumbMemoryLayoutSync(frame->cl, gpu_tiles, G_Layout_DirectQueue_ShaderRead);
@ -3372,28 +3560,28 @@ void V_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- Prune ents
{
i64 ents_to_prune_count = 0;
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))
{
if (ent->exists <= 0)
{
ents_to_prune[ents_to_prune_count] = ent;
ents_to_prune_count += 1;
}
}
// {
// i64 ents_to_prune_count = 0;
// 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))
// {
// if (ent->exists <= 0)
// {
// ents_to_prune[ents_to_prune_count] = ent;
// ents_to_prune_count += 1;
// }
// }
for (i64 prune_idx = 0; prune_idx < ents_to_prune_count; ++prune_idx)
{
// FIXME: Add to free list
P_Ent *ent = ents_to_prune[prune_idx];
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);
DllQueueRemove(world->first_ent, world->last_ent, ent);
world->ents_count -= 1;
}
}
// for (i64 prune_idx = 0; prune_idx < ents_to_prune_count; ++prune_idx)
// {
// // FIXME: Add to free list
// P_Ent *ent = ents_to_prune[prune_idx];
// 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);
// DllQueueRemove(world->first_ent, world->last_ent, ent);
// world->ents_count -= 1;
// }
// }
//////////////////////////////
//- End frame

View File

@ -260,7 +260,7 @@ Struct(V_Frame)
Rng2 draw_selection;
Rng2 world_selection;
// Commands
// Vis commands
i64 cmds_count;
V_CmdNode *first_cmd_node;
V_CmdNode *last_cmd_node;
@ -271,11 +271,6 @@ Struct(V_Frame)
f32 fire_held;
f32 fire_presses;
// Sim cmds
u64 sim_cmds_count;
P_CmdNode *first_sim_cmd_node;
P_CmdNode *last_sim_cmd_node;
// Emitters
i64 emitters_count;
V_EmitterNode *first_emitter_node;
@ -291,6 +286,9 @@ Struct(V_Ctx)
V_Panel *root_panel;
V_Window *dragging_window;
// Sim commands
P_CmdList sim_cmds;
// Atomic monotonically increasing allocation counter sequence for GPU particle ring buffer
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));
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);
}
@ -113,7 +113,7 @@ TAR_Archive TAR_ArchiveFromString(Arena *arena, String data, String prefix)
{
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);
break;
}
@ -133,7 +133,7 @@ TAR_Archive TAR_ArchiveFromString(Arena *arena, String data, String prefix)
Readonly Global TAR_Entry g_nil_tar_entry = ZI;
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);
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);
UI_Key key = Zi;
key.v = HashFnv64(top_tag, str);
key.v = HashStringEx(top_tag, str);
return key;
}
@ -340,7 +340,7 @@ void UI_PushDefaults(void)
case UI_StyleKind_FontSize: { desc.style.FontSize = 16.0f; } break;
case UI_StyleKind_Tint: { desc.style.Tint = 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_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;