make dummies into quasi-players

This commit is contained in:
jacob 2026-01-18 23:12:57 -06:00
parent 6a0fef0b1d
commit a529ea8c5b
6 changed files with 216 additions and 123 deletions

View File

@ -74,7 +74,7 @@ u64 NET_HashFromKey(NET_Key key);
NET_PipeHandle NET_AcquirePipe(void); NET_PipeHandle NET_AcquirePipe(void);
void NET_Bind(NET_PipeHandle pipe_handle, u64 port); void NET_Listen(NET_PipeHandle pipe_handle, u64 port);
NET_MsgList NET_Swap(Arena *arena, NET_PipeHandle pipe_handle); NET_MsgList NET_Swap(Arena *arena, NET_PipeHandle pipe_handle);
void NET_Send(NET_PipeHandle pipe_handle, NET_Key dst, String data, NET_SendFlag flags); void NET_Send(NET_PipeHandle pipe_handle, NET_Key dst, String data, NET_SendFlag flags);

View File

@ -281,7 +281,7 @@ NET_PipeHandle NET_AcquirePipe(void)
return (NET_PipeHandle) { .v = (u64) pipe }; return (NET_PipeHandle) { .v = (u64) pipe };
} }
void NET_Bind(NET_PipeHandle pipe_handle, u64 port) void NET_Listen(NET_PipeHandle pipe_handle, u64 port)
{ {
NET_W32_Pipe *pipe = NET_W32_PipeFromHandle(pipe_handle); NET_W32_Pipe *pipe = NET_W32_PipeFromHandle(pipe_handle);
u64 prev_desired_port = Atomic64FetchSet(&pipe->desired_port, port); u64 prev_desired_port = Atomic64FetchSet(&pipe->desired_port, port);

View File

@ -1293,6 +1293,7 @@ void P_SpawnEntsFromList(P_Frame *frame, P_EntList ents)
dst->prev = old_prev; dst->prev = old_prev;
dst->next_in_bin = old_next_in_bin; dst->next_in_bin = old_next_in_bin;
dst->prev_in_bin = old_prev_in_bin; dst->prev_in_bin = old_prev_in_bin;
dst->created_at_ns = frame->time_ns;
++frame->ents_count; ++frame->ents_count;
} }
} }
@ -1436,6 +1437,33 @@ void P_StepFrame(P_Frame *frame)
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);
//////////////////////////////
//- Prune ents
{
i64 ents_to_prune_count = 0;
P_Ent **ents_to_prune = PushStructsNoZero(scratch.arena, P_Ent *, frame->ents_count);
for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); 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: Ensure sure prunes are received by player
P_Ent *ent = ents_to_prune[prune_idx];
P_EntBin *bin = &frame->ent_bins[ent->key.v % frame->ent_bins_count];
DllQueueRemoveNP(bin->first, bin->last, ent, next_in_bin, prev_in_bin);
DllQueueRemoveNPZ(&P_NilEnt, frame->first_ent, frame->last_ent, ent, next, prev);
frame->ents_count -= 1;
SllStackPush(world->first_free_ent, ent);
}
}
////////////////////////////// //////////////////////////////
//- Update double-buffered entity data //- Update double-buffered entity data
@ -1544,7 +1572,7 @@ void P_StepFrame(P_Frame *frame)
////////////////////////////// //////////////////////////////
//- Generate guy wall constraints //- Generate guy-on-guy constraints

View File

@ -97,6 +97,7 @@ Struct(P_Ent)
P_Key key; P_Key key;
u64 rand_seq; u64 rand_seq;
i64 created_at_ns;
//- Build data //- Build data
@ -287,6 +288,8 @@ Struct(P_Msg)
NET_Key src; NET_Key src;
NET_Key dst; NET_Key dst;
b32 affect_dummies;
P_TileKind tile_kind; P_TileKind tile_kind;
Rng2I32 tile_range; Rng2I32 tile_range;
u64 tiles_hash; u64 tiles_hash;

View File

@ -57,7 +57,7 @@ void S_TickForever(WaveLaneCtx *lane)
Arena *perm = PermArena(); Arena *perm = PermArena();
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));
P_tl.debug_tint = VEC4(0.2, 0.4, 0.2, 0.75); P_tl.debug_tint = VEC4(0.2, 0.8, 0.2, 0.75);
P_tl.out_msgs_arena = AcquireArena(Gibi(64)); P_tl.out_msgs_arena = AcquireArena(Gibi(64));
P_World *world = P_AcquireWorld(); P_World *world = P_AcquireWorld();
@ -102,7 +102,7 @@ void S_TickForever(WaveLaneCtx *lane)
// TODO: Keep old frames around for player snapshot deltas // TODO: Keep old frames around for player snapshot deltas
P_ClearFrames(world, I64Min, prev_world_frame->tick - 1); P_ClearFrames(world, I64Min, prev_world_frame->tick - 1);
i64 frame_begin_ns = TimeNs(); i64 time_ns = TimeNs();
////////////////////////////// //////////////////////////////
//- Swap //- Swap
@ -159,7 +159,7 @@ void S_TickForever(WaveLaneCtx *lane)
//- Pop messages //- Pop messages
u64 port = 22121; u64 port = 22121;
NET_Bind(net_pipe, port); NET_Listen(net_pipe, port);
P_MsgList in_msgs = Zi; P_MsgList in_msgs = Zi;
{ {
@ -352,31 +352,16 @@ void S_TickForever(WaveLaneCtx *lane)
//- Propagate control //- Propagate control
// //
// In case future snapshots aren't received, the last-received control should be used, so we copy this control forward here // In case previous snapshots weren't received, backfill with newest snapshot
// //
// TODO: Not like this // TODO: Not like this
i64 max_propagate_count = SIM_TICKS_PER_SECOND / 4; i64 max_propagate_count = SIM_TICKS_PER_SECOND / 4;
{ {
// Propagate forwards
for (i64 prop_tick = control_tick; prop_tick < prop_tick + max_propagate_count; ++prop_tick)
{
P_Control *prop_control = &client->controls[prop_tick % client->max_controls];
if (prop_tick > prop_control->orig_tick)
{
*prop_control = *control;
prop_control->tick = prop_tick;
prop_control->orig_tick = control_tick;
}
else
{
break;
}
}
// Propagate backwards // Propagate backwards
for (i64 prop_tick = control_tick - 1; prop_tick > MaxI64(prop_tick - max_propagate_count, 0); --prop_tick) for (i64 prop_tick = control_tick - 1; prop_tick >= MaxI64(prop_tick - max_propagate_count, world_frame->tick); --prop_tick)
{ {
P_Control *prop_control = &client->controls[prop_tick % client->max_controls]; P_Control *prop_control = &client->controls[prop_tick % client->max_controls];
if (prop_tick > prop_control->orig_tick) if (prop_control->orig_tick != prop_control->tick && control_tick < prop_control->orig_tick)
{ {
*prop_control = *control; *prop_control = *control;
prop_control->tick = prop_tick; prop_control->tick = prop_tick;
@ -433,11 +418,27 @@ void S_TickForever(WaveLaneCtx *lane)
for (S_Client *client = S.first_client; !S_IsClientNil(client); client = client->next) for (S_Client *client = S.first_client; !S_IsClientNil(client); client = client->next)
{ {
P_Control *control = &client->controls[world_frame->tick % client->max_controls]; P_Control *control = &client->controls[world_frame->tick % client->max_controls];
if (control->tick == world_frame->tick)
P_Ent *player = P_EntFromKey(world_frame, client->player);
if (!P_IsEntNil(player))
{ {
player->control = *control; P_Ent *player = P_EntFromKey(world_frame, client->player);
if (!P_IsEntNil(player))
{
player->control = *control;
}
}
}
//////////////////////////////
//- Apply dummy controls
for (P_Ent *dummy = P_FirstEnt(world_frame); !P_IsEntNil(dummy); dummy = P_NextEnt(dummy))
{
if (dummy->is_dummy)
{
i64 alive_time_ns = time_ns - dummy->created_at_ns;
i64 frequency_ns = NsFromSeconds(0.1);
dummy->control.move.x = SinF32((f64)alive_time_ns / (f64)frequency_ns);
} }
} }
@ -625,7 +626,7 @@ void S_TickForever(WaveLaneCtx *lane)
// TODO: Real reset // TODO: Real reset
for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); 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 || (msg->affect_dummies && ent->is_dummy))
{ {
ent->exists = 0; ent->exists = 0;
} }
@ -642,24 +643,41 @@ void S_TickForever(WaveLaneCtx *lane)
} }
else if (MatchString(name, Lit("guy"))) else if (MatchString(name, Lit("guy")))
{ {
P_Ent *ent = P_PushTempEnt(frame_arena, &ents); P_Ent *guy = P_PushTempEnt(frame_arena, &ents);
*ent = P_NilEnt; *guy = P_NilEnt;
ent->key = msg->key; guy->key = msg->key;
ent->xf = XformFromPos(msg->pos); guy->xf = XformFromPos(msg->pos);
ent->is_guy = 1; guy->is_guy = 1;
ent->has_weapon = 1; guy->has_weapon = 1;
ent->exists = 1; guy->exists = 1;
} }
else if (MatchString(name, Lit("dummy"))) else if (MatchString(name, Lit("dummy")))
{ {
P_Ent *ent = P_PushTempEnt(frame_arena, &ents); P_Ent *guy = P_PushTempEnt(frame_arena, &ents);
*ent = P_NilEnt; *guy = P_NilEnt;
ent->key = msg->key; guy->key = P_RandKey();
ent->xf = XformFromPos(msg->pos); guy->xf = XformFromPos(msg->pos);
ent->is_guy = 1; guy->is_guy = 1;
ent->is_dummy = 1; guy->has_weapon = 1;
ent->has_weapon = 1; guy->exists = 1;
ent->exists = 1;
P_Ent *dummy = P_PushTempEnt(frame_arena, &ents);
*dummy = P_NilEnt;
dummy->key = msg->key;
dummy->is_player = 1;
dummy->is_dummy = 1;
dummy->exists = 1;
dummy->guy = guy->key;
P_SetEntString(dummy, Lit("Dummy"));
// P_Ent *ent = P_PushTempEnt(frame_arena, &ents);
// *ent = P_NilEnt;
// ent->key = msg->key;
// ent->xf = XformFromPos(msg->pos);
// ent->is_guy = 1;
// ent->is_dummy = 1;
// ent->has_weapon = 1;
// ent->exists = 1;
} }
P_SpawnEntsFromList(world_frame, ents); P_SpawnEntsFromList(world_frame, ents);
} break; } break;
@ -1165,29 +1183,29 @@ void S_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_frame->ents_count); // 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)) // for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); 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: Ensure sure prunes are received by player // // FIXME: Ensure sure prunes are received by player
P_Ent *ent = ents_to_prune[prune_idx]; // P_Ent *ent = ents_to_prune[prune_idx];
P_EntBin *bin = &world_frame->ent_bins[ent->key.v % world_frame->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);
DllQueueRemoveNPZ(&P_NilEnt, world_frame->first_ent, world_frame->last_ent, ent, next, prev); // DllQueueRemoveNPZ(&P_NilEnt, world_frame->first_ent, world_frame->last_ent, ent, next, prev);
world_frame->ents_count -= 1; // world_frame->ents_count -= 1;
SllStackPush(world->first_free_ent, ent); // SllStackPush(world->first_free_ent, ent);
} // }
} // }
////////////////////////////// //////////////////////////////
//- Publish Sim -> User data //- Publish Sim -> User data
@ -1247,7 +1265,7 @@ void S_TickForever(WaveLaneCtx *lane)
if (!shutdown) if (!shutdown)
{ {
i64 step_dt_ns = NsFromSeconds(1) / SIM_TICKS_PER_SECOND; i64 step_dt_ns = NsFromSeconds(1) / SIM_TICKS_PER_SECOND;
PLT_SleepFrame(frame_begin_ns, step_dt_ns); PLT_SleepFrame(time_ns, step_dt_ns);
} }
} }

View File

@ -2430,7 +2430,10 @@ void V_TickForever(WaveLaneCtx *lane)
{ {
String name = board_row->name; String name = board_row->name;
UI_SetNext(Width, UI_GROW(1, 0));
UI_SetNext(Height, UI_FNT(2.5, 0));
UI_SetNext(Tint, 0); UI_SetNext(Tint, 0);
UI_SetNext(ChildAlignment, UI_Region_Left);
UI_PushCP(UI_BuildRow()); UI_PushCP(UI_BuildRow());
{ {
UI_SetNext(FontSize, UI_Top(FontSize) * theme.h2); UI_SetNext(FontSize, UI_Top(FontSize) * theme.h2);
@ -2714,6 +2717,7 @@ void V_TickForever(WaveLaneCtx *lane)
if (kind == V_CmdKind_reset_world) if (kind == V_CmdKind_reset_world)
{ {
P_Msg *msg = P_PushMsg(P_MsgKind_ResetWorld, Zstr); P_Msg *msg = P_PushMsg(P_MsgKind_ResetWorld, Zstr);
msg->affect_dummies = 1;
} }
else else
{ {
@ -2964,7 +2968,9 @@ void V_TickForever(WaveLaneCtx *lane)
} }
// TODO: Remove this // TODO: Remove this
P_ClearFrames(sim_world, I64Min, sim_world->last_frame->tick - 1); // TODO: Delete all frames except for prediction base & remote ack
P_ClearFrames(sim_world, I64Min, known_sim_tick - SIM_TICKS_PER_SECOND);
// P_ClearFrames(sim_world, I64Min, sim_world->last_frame->tick - 1);
P_Frame *sim_frame = sim_world->last_frame; P_Frame *sim_frame = sim_world->last_frame;
////////////////////////////// //////////////////////////////
@ -3021,7 +3027,9 @@ void V_TickForever(WaveLaneCtx *lane)
// frame->predict_to = sim_world->last_frame->tick + MaxF64(CeilF64(ping * SIM_TICKS_PER_SECOND), 1.0); // frame->predict_to = sim_world->last_frame->tick + MaxF64(CeilF64(ping * SIM_TICKS_PER_SECOND), 1.0);
frame->predict_to = known_sim_tick + 10; // frame->predict_to = known_sim_tick + 10;
frame->predict_to = known_sim_tick + 20;
////////////////////////////// //////////////////////////////
//- Create player control //- Create player control
@ -3209,6 +3217,13 @@ void V_TickForever(WaveLaneCtx *lane)
// Predict // Predict
P_Frame *predict_frame = &P_NilFrame; P_Frame *predict_frame = &P_NilFrame;
{ {
@ -3221,20 +3236,15 @@ void V_TickForever(WaveLaneCtx *lane)
// P_ClearFrames(predict_world, I64Min, I64Max);
// predict_frame = P_PushFrame(predict_world, sim_world->last_frame, sim_world->last_frame->tick);
// FIXME: Not like this // FIXME: Not like this
i64 max_predict_ticks = SIM_TICKS_PER_SECOND; i64 max_predict_ticks = SIM_TICKS_PER_SECOND;
i64 last_predict_tick = frame->predict_to; i64 last_predict_tick = frame->predict_to;
i64 first_predict_tick = sim_world->last_frame->tick; i64 first_predict_tick = known_sim_tick - 2;
first_predict_tick = MaxI64(first_predict_tick, last_predict_tick - max_predict_ticks); first_predict_tick = MaxI64(first_predict_tick, last_predict_tick - max_predict_ticks);
P_ClearFrames(predict_world, I64Min, first_predict_tick - 1); P_ClearFrames(predict_world, I64Min, first_predict_tick - 1);
predict_frame = P_PushFrame(predict_world, sim_world->last_frame, first_predict_tick); predict_frame = P_PushFrame(predict_world, P_FrameFromTick(sim_world, first_predict_tick), first_predict_tick);
for (i64 predict_tick = first_predict_tick + 1; predict_tick <= last_predict_tick; ++predict_tick) for (i64 predict_tick = first_predict_tick + 1; predict_tick <= last_predict_tick; ++predict_tick)
{ {
@ -3250,35 +3260,11 @@ void V_TickForever(WaveLaneCtx *lane)
} }
} }
P_tl.debug_draw_enabled = 0;
P_StepFrame(predict_frame); P_StepFrame(predict_frame);
P_tl.debug_draw_enabled = 1;
} }
// // We want to keep previously predicted ticks to preserve constraints
// P_ClearFrames(predict_world, I64Min, sim_world->last_frame->tick - 2);
// i64 step_count = frame->predict_to - sim_world->last_frame->tick;
// P_Frame *base_predict_frame = P_PushFrame(predict_world, sim_world->last_frame, sim_world->last_frame->tick);
// for (i64 step_idx = 0; step_idx < step_count; ++step_idx)
// {
// P_Frame *step_frame = P_PushFrame(predict_world, predict_world->last_frame, predict_world->last_frame->tick + 1);
// // FIXME: Cmds
// P_MsgList step_cmds = Zi;
// for (P_MsgNode *src_cmd_node = V.sim_cmds.first; src_cmd_node; src_cmd_node = src_cmd_node->next)
// {
// if (src_cmd_node->cmd.predicted && src_cmd_node->cmd.tick == step_frame->tick)
// {
// P_MsgNode *dst_cmd_node = PushStruct(frame->arena, P_MsgNode);
// DllQueuePush(step_cmds.first, step_cmds.last, dst_cmd_node);
// ++step_cmds.count;
// }
// }
// P_StepFrame(step_frame, step_cmds);
// }
predict_frame = predict_world->last_frame; predict_frame = predict_world->last_frame;
P_DebugDrawFrame(predict_frame); P_DebugDrawFrame(predict_frame);
@ -3289,6 +3275,64 @@ void V_TickForever(WaveLaneCtx *lane)
// // Predict
// P_Frame *predict_frame = &P_NilFrame;
// {
// if (predict_world->tiles_hash != sim_world->tiles_hash)
// {
// predict_world->tiles_hash = sim_world->tiles_hash;
// CopyStructs(predict_world->tiles, sim_world->tiles, P_TilesCount);
// }
// predict_world->seed = sim_world->seed;
// // P_ClearFrames(predict_world, I64Min, I64Max);
// // predict_frame = P_PushFrame(predict_world, sim_world->last_frame, sim_world->last_frame->tick);
// // FIXME: Not like this
// i64 max_predict_ticks = SIM_TICKS_PER_SECOND;
// i64 last_predict_tick = frame->predict_to;
// i64 first_predict_tick = sim_world->last_frame->tick;
// first_predict_tick = MaxI64(first_predict_tick, last_predict_tick - max_predict_ticks);
// P_ClearFrames(predict_world, I64Min, first_predict_tick - 1);
// predict_frame = P_PushFrame(predict_world, sim_world->last_frame, first_predict_tick);
// for (i64 predict_tick = first_predict_tick + 1; predict_tick <= last_predict_tick; ++predict_tick)
// {
// predict_frame = P_PushFrame(predict_world, predict_world->last_frame, predict_tick);
// P_Ent *predict_player = P_EntFromKey(predict_frame, local_player->key);
// if (!P_IsEntNil(predict_player))
// {
// P_Control *predict_control = &local_controls[predict_tick % max_local_controls];
// if (predict_control->tick == predict_tick)
// {
// predict_player->control = *predict_control;
// }
// }
// P_StepFrame(predict_frame);
// }
// predict_frame = predict_world->last_frame;
// P_DebugDrawFrame(predict_frame);
// // TODO: Extract information that occurred between first & last prediction, like bullet hits etc?
// }
////////////////////////////// //////////////////////////////
//- Update local world //- Update local world
@ -4010,28 +4054,28 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Prune sim ents //- Prune sim ents
{ // {
i64 ents_to_prune_count = 0; // i64 ents_to_prune_count = 0;
P_Ent **ents_to_prune = PushStructsNoZero(frame->arena, P_Ent *, sim_frame->ents_count); // P_Ent **ents_to_prune = PushStructsNoZero(frame->arena, P_Ent *, sim_frame->ents_count);
for (P_Ent *ent = P_FirstEnt(sim_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) // for (P_Ent *ent = P_FirstEnt(sim_frame); !P_IsEntNil(ent); 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)
{ // {
P_Ent *ent = ents_to_prune[prune_idx]; // P_Ent *ent = ents_to_prune[prune_idx];
P_EntBin *bin = &sim_frame->ent_bins[ent->key.v % sim_frame->ent_bins_count]; // P_EntBin *bin = &sim_frame->ent_bins[ent->key.v % sim_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);
DllQueueRemoveNPZ(&P_NilEnt, sim_frame->first_ent, sim_frame->last_ent, ent, next, prev); // DllQueueRemoveNPZ(&P_NilEnt, sim_frame->first_ent, sim_frame->last_ent, ent, next, prev);
sim_frame->ents_count -= 1; // sim_frame->ents_count -= 1;
SllStackPush(sim_world->first_free_ent, ent); // SllStackPush(sim_world->first_free_ent, ent);
} // }
} // }
////////////////////////////// //////////////////////////////
//- Send messages //- Send messages