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);
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);
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 };
}
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);
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->next_in_bin = old_next_in_bin;
dst->prev_in_bin = old_prev_in_bin;
dst->created_at_ns = frame->time_ns;
++frame->ents_count;
}
}
@ -1436,6 +1437,33 @@ void P_StepFrame(P_Frame *frame)
i64 sim_dt_ns = NsFromSeconds(1) / SIM_TICKS_PER_SECOND;
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
@ -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;
u64 rand_seq;
i64 created_at_ns;
//- Build data
@ -287,6 +288,8 @@ Struct(P_Msg)
NET_Key src;
NET_Key dst;
b32 affect_dummies;
P_TileKind tile_kind;
Rng2I32 tile_range;
u64 tiles_hash;

View File

@ -57,7 +57,7 @@ void S_TickForever(WaveLaneCtx *lane)
Arena *perm = PermArena();
Arena *frame_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_World *world = P_AcquireWorld();
@ -102,7 +102,7 @@ void S_TickForever(WaveLaneCtx *lane)
// TODO: Keep old frames around for player snapshot deltas
P_ClearFrames(world, I64Min, prev_world_frame->tick - 1);
i64 frame_begin_ns = TimeNs();
i64 time_ns = TimeNs();
//////////////////////////////
//- Swap
@ -159,7 +159,7 @@ void S_TickForever(WaveLaneCtx *lane)
//- Pop messages
u64 port = 22121;
NET_Bind(net_pipe, port);
NET_Listen(net_pipe, port);
P_MsgList in_msgs = Zi;
{
@ -352,31 +352,16 @@ void S_TickForever(WaveLaneCtx *lane)
//- 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
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
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];
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->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)
{
P_Control *control = &client->controls[world_frame->tick % client->max_controls];
P_Ent *player = P_EntFromKey(world_frame, client->player);
if (!P_IsEntNil(player))
if (control->tick == world_frame->tick)
{
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
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;
}
@ -642,24 +643,41 @@ void S_TickForever(WaveLaneCtx *lane)
}
else if (MatchString(name, Lit("guy")))
{
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->has_weapon = 1;
ent->exists = 1;
P_Ent *guy = P_PushTempEnt(frame_arena, &ents);
*guy = P_NilEnt;
guy->key = msg->key;
guy->xf = XformFromPos(msg->pos);
guy->is_guy = 1;
guy->has_weapon = 1;
guy->exists = 1;
}
else if (MatchString(name, 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_Ent *guy = P_PushTempEnt(frame_arena, &ents);
*guy = P_NilEnt;
guy->key = P_RandKey();
guy->xf = XformFromPos(msg->pos);
guy->is_guy = 1;
guy->has_weapon = 1;
guy->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);
} break;
@ -1165,29 +1183,29 @@ void S_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- Prune ents
{
i64 ents_to_prune_count = 0;
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)
{
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_frame->ents_count);
// for (P_Ent *ent = P_FirstEnt(world_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 = &world_frame->ent_bins[ent->key.v % world_frame->ent_bins_count];
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);
world_frame->ents_count -= 1;
SllStackPush(world->first_free_ent, ent);
}
}
// 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 = &world_frame->ent_bins[ent->key.v % world_frame->ent_bins_count];
// 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);
// world_frame->ents_count -= 1;
// SllStackPush(world->first_free_ent, ent);
// }
// }
//////////////////////////////
//- Publish Sim -> User data
@ -1247,7 +1265,7 @@ void S_TickForever(WaveLaneCtx *lane)
if (!shutdown)
{
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;
UI_SetNext(Width, UI_GROW(1, 0));
UI_SetNext(Height, UI_FNT(2.5, 0));
UI_SetNext(Tint, 0);
UI_SetNext(ChildAlignment, UI_Region_Left);
UI_PushCP(UI_BuildRow());
{
UI_SetNext(FontSize, UI_Top(FontSize) * theme.h2);
@ -2714,6 +2717,7 @@ void V_TickForever(WaveLaneCtx *lane)
if (kind == V_CmdKind_reset_world)
{
P_Msg *msg = P_PushMsg(P_MsgKind_ResetWorld, Zstr);
msg->affect_dummies = 1;
}
else
{
@ -2964,7 +2968,9 @@ void V_TickForever(WaveLaneCtx *lane)
}
// 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;
//////////////////////////////
@ -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 = known_sim_tick + 10;
// frame->predict_to = known_sim_tick + 10;
frame->predict_to = known_sim_tick + 20;
//////////////////////////////
//- Create player control
@ -3209,6 +3217,13 @@ void V_TickForever(WaveLaneCtx *lane)
// Predict
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
i64 max_predict_ticks = SIM_TICKS_PER_SECOND;
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);
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)
{
@ -3250,35 +3260,11 @@ void V_TickForever(WaveLaneCtx *lane)
}
}
P_tl.debug_draw_enabled = 0;
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;
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
@ -4010,28 +4054,28 @@ void V_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- Prune sim ents
{
i64 ents_to_prune_count = 0;
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))
{
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 *, sim_frame->ents_count);
// for (P_Ent *ent = P_FirstEnt(sim_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)
{
P_Ent *ent = ents_to_prune[prune_idx];
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);
DllQueueRemoveNPZ(&P_NilEnt, sim_frame->first_ent, sim_frame->last_ent, ent, next, prev);
sim_frame->ents_count -= 1;
SllStackPush(sim_world->first_free_ent, ent);
}
}
// for (i64 prune_idx = 0; prune_idx < ents_to_prune_count; ++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];
// 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);
// sim_frame->ents_count -= 1;
// SllStackPush(sim_world->first_free_ent, ent);
// }
// }
//////////////////////////////
//- Send messages