client -> server control snapshots

This commit is contained in:
jacob 2026-01-18 18:14:46 -06:00
parent 1c0009fb21
commit 746ef6913b
10 changed files with 452 additions and 157 deletions

View File

@ -442,7 +442,12 @@ D3D12_BARRIER_LAYOUT G_D12_BarrierLayoutFromLayout(G_Layout layout)
[G_Layout_ComputeQueue_CopyRead] = D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_SOURCE, [G_Layout_ComputeQueue_CopyRead] = D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_SOURCE,
[G_Layout_ComputeQueue_CopyWrite] = D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_DEST, [G_Layout_ComputeQueue_CopyWrite] = D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_DEST,
}; };
return translate[layout]; D3D12_BARRIER_LAYOUT result = D3D12_BARRIER_LAYOUT_UNDEFINED;
if (layout >= 0 && layout < countof(translate))
{
result = translate[layout];
}
return result;
}; };
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -103,7 +103,6 @@ u32 NET_W32_ChecksumFromPacketString(String str)
// Skip the first 4 bytes of packet since it contains the checksum itself // Skip the first 4 bytes of packet since it contains the checksum itself
str.text += 4; str.text += 4;
str.len -= 4; str.len -= 4;
// https://en.wikipedia.org/wiki/Adler-32 // https://en.wikipedia.org/wiki/Adler-32
u32 a = 1; u32 a = 1;
u32 b = 0; u32 b = 0;

View File

@ -1464,54 +1464,80 @@ void P_StepFrame(P_Frame *frame)
// } // }
////////////////////////////// //////////////////////////////
//- Integrate control forces //- Update guy controls from player controls
for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) for (P_Ent *guy = P_FirstEnt(frame); !P_IsEntNil(guy); guy = P_NextEnt(guy))
{ {
// Xform xf = ent->xf; if (guy->is_guy)
{
ZeroStruct(&guy->control);
}
}
for (P_Ent *player = P_FirstEnt(frame); !P_IsEntNil(player); player = P_NextEnt(player))
{
if (player->is_player)
{
P_Ent *guy = P_EntFromKey(frame, player->guy);
if (!P_IsEntNil(guy))
{
guy->control = player->control;
}
}
}
//////////////////////////////
//- Integrate guy control forces
for (P_Ent *guy = P_FirstEnt(frame); !P_IsEntNil(guy); guy = P_NextEnt(guy))
{
if (guy->is_guy)
{
P_Control control = guy->control;
// Xform xf = guy->xf;
// Xform desired_xf = xf; // Xform desired_xf = xf;
// if (!IsVec2Zero(ent->control.look)) // if (!IsVec2Zero(control.look))
// { // {
// desired_xf = XformWithWorldRotation(xf, AngleFromVec2(ent->control.look)); // desired_xf = XformWithWorldRotation(xf, AngleFromVec2(control.look));
// } // }
// f32 move_speed = TweakFloat("Guy move speed", 6.5, 0, 20); // f32 move_speed = TweakFloat("Guy move speed", 6.5, 0, 20);
// desired_xf.og = AddVec2(xf.og, MulVec2(ent->control.move, move_speed * sim_dt)); // desired_xf.og = AddVec2(xf.og, MulVec2(control.move, move_speed * sim_dt));
// Vec2 pos_diff = SubVec2(desired_xf.og, xf.og); // Vec2 pos_diff = SubVec2(desired_xf.og, xf.og);
// f32 angle_diff = UnwindAngleF32(RotationFromXform(desired_xf) - RotationFromXform(xf)); // f32 angle_diff = UnwindAngleF32(RotationFromXform(desired_xf) - RotationFromXform(xf));
// ent->solved_v = pos_diff; // guy->solved_v = pos_diff;
// ent->solved_w = angle_diff; // guy->solved_w = angle_diff;
{ {
// f32 damp_vel = damp_force * sim_dt; // f32 damp_vel = damp_force * sim_dt;
if (Vec2Len(ent->solved_v) > 0.001) if (Vec2Len(guy->solved_v) > 0.001)
{ {
f32 damp_force = TweakFloat("Guy damp force", 50, 0, 100); f32 damp_force = TweakFloat("Guy damp force", 50, 0, 100);
Vec2 damp = MulVec2(NegVec2(ent->solved_v), damp_force * sim_dt); Vec2 damp = MulVec2(NegVec2(guy->solved_v), damp_force * sim_dt);
ent->solved_v = AddVec2(ent->solved_v, damp); guy->solved_v = AddVec2(guy->solved_v, damp);
} }
else else
{ {
ent->solved_v = VEC2(0, 0); guy->solved_v = VEC2(0, 0);
} }
} }
{ {
f32 move_force = TweakFloat("Guy move force", 400, 0, 400); f32 move_force = TweakFloat("Guy move force", 400, 0, 400);
f32 max_speed = TweakFloat("Guy max speed", 10, 0, 20); f32 max_speed = TweakFloat("Guy max speed", 10, 0, 20);
Vec2 new_velocity = ent->solved_v; Vec2 new_velocity = guy->solved_v;
new_velocity = AddVec2(new_velocity, MulVec2(ent->control.move, move_force * sim_dt)); new_velocity = AddVec2(new_velocity, MulVec2(control.move, move_force * sim_dt));
// if (Vec2Len(new_velocity) > max_speed) // if (Vec2Len(new_velocity) > max_speed)
// { // {
// new_velocity = Vec2WithLen(new_velocity, max_speed); // new_velocity = Vec2WithLen(new_velocity, max_speed);
// } // }
ent->solved_v = new_velocity; guy->solved_v = new_velocity;
}
} }
} }

View File

@ -74,6 +74,9 @@ Struct(P_DebugDrawNode)
Struct(P_Control) Struct(P_Control)
{ {
i64 tick;
i64 orig_tick; // Will differ from tick if this control was propagated from the control of another tick
Vec2 move; Vec2 move;
Vec2 look; Vec2 look;
f32 fire_held; f32 fire_held;
@ -119,6 +122,8 @@ Struct(P_Ent)
Vec2 hit_entry; Vec2 hit_entry;
Vec2 hit_entry_normal; Vec2 hit_entry_normal;
//- Player / Guy
P_Control control; P_Control control;
//- Player //- Player
@ -253,52 +258,6 @@ Struct(P_World)
u8 *tiles; u8 *tiles;
}; };
////////////////////////////////////////////////////////////
//~ Sim snapshot types
Enum(P_SimDeltaKind)
{
P_SimDeltaKind_None,
P_SimDeltaKind_RawEnt,
};
Struct(P_SimDelta)
{
P_SimDeltaKind kind;
P_Ent ent;
};
Struct(P_SimDeltaNode)
{
P_SimDeltaNode *next;
P_SimDelta delta;
};
Struct(P_SimSnapshot)
{
u64 world_seed;
i64 src_tick;
i64 tick;
i64 time_ns;
i64 deltas_count;
P_SimDeltaNode *first_delta_node;
P_SimDeltaNode *last_delta_node;
};
////////////////////////////////////////////////////////////
//~ Player snapshot types
Struct(P_PlayerSnapshot)
{
i64 tick;
Vec2 move;
Vec2 look;
b32 fire_held;
i32 fire_presses;
};
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Message types //~ Message types

View File

@ -202,26 +202,35 @@ void S_TickForever(WaveLaneCtx *lane)
if (S_IsClientNil(client)) if (S_IsClientNil(client))
{ {
String address_str = NET_StringFromKey(frame_arena, net_key); String address_str = NET_StringFromKey(frame_arena, net_key);
u64 hash = NET_HashFromKey(net_key);
S_ClientBin *bin = &S.client_bins[hash % S.client_bins_count];
String orig_src_name = msg->data; String orig_src_name = msg->data;
String src_name = TrimWhitespace(orig_src_name); String src_name = TrimWhitespace(orig_src_name);
{
if (src_name.len < P_MinPlayerNameLen) if (src_name.len < P_MinPlayerNameLen)
{ {
src_name = Lit("Player"); src_name = Lit("Player");
} }
src_name.len = MinI64(src_name.len, P_MaxPlayerNameLen); src_name.len = MinI64(src_name.len, P_MaxPlayerNameLen);
}
// FIXME: Freelist client // FIXME: Freelist client
client = PushStruct(perm, S_Client); client = PushStruct(perm, S_Client);
{
u64 hash = NET_HashFromKey(net_key);
S_ClientBin *bin = &S.client_bins[hash % S.client_bins_count];
client->hash = hash; client->hash = hash;
client->net_key = net_key; client->net_key = net_key;
client->name_len = MinI64(countof(client->name_text), src_name.len); client->name_len = MinI64(countof(client->name_text), src_name.len);
CopyBytes(client->name_text, src_name.text, client->name_len); CopyBytes(client->name_text, src_name.text, client->name_len);
client->max_controls = SIM_MAX_PING * SIM_TICKS_PER_SECOND;
client->controls = PushStructs(perm, P_Control, client->max_controls);
DllQueuePushNPZ(&S_NilClient, S.first_client, S.last_client, client, next, prev); DllQueuePushNPZ(&S_NilClient, S.first_client, S.last_client, client, next, prev);
DllQueuePushNP(bin->first, bin->last, client, next_in_bin, prev_in_bin); DllQueuePushNP(bin->first, bin->last, client, next_in_bin, prev_in_bin);
}
// Acknowledge player connection // Acknowledge player connection
{ {
@ -258,15 +267,15 @@ void S_TickForever(WaveLaneCtx *lane)
P_EntList new_players = Zi; P_EntList new_players = Zi;
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)
{ {
if (P_IsKeyNil(client->player_key)) if (P_IsKeyNil(client->player))
{ {
client->player_key = P_RandKey(); client->player = P_RandKey();
} }
P_Ent *player = P_EntFromKey(world_frame, client->player_key); P_Ent *player = P_EntFromKey(world_frame, client->player);
if (P_IsEntNil(player)) if (P_IsEntNil(player))
{ {
player = P_PushTempEnt(frame_arena, &new_players); player = P_PushTempEnt(frame_arena, &new_players);
player->key = client->player_key; player->key = client->player;
} }
player->is_player = 1; player->is_player = 1;
// Update name // Update name
@ -307,6 +316,136 @@ void S_TickForever(WaveLaneCtx *lane)
P_SpawnEntsFromList(world_frame, new_guys); P_SpawnEntsFromList(world_frame, new_guys);
} }
//////////////////////////////
//- Read client snapshots
for (P_MsgNode *msg_node = in_msgs.first; msg_node; msg_node = msg_node->next)
{
P_Msg *msg = &msg_node->msg;
if (msg->kind == P_MsgKind_Raw)
{
S_Client *client = S_ClientFromNetKey(msg->src);
if (!S_IsClientNil(client))
{
BB_Buff bb = BB_BuffFromString(msg->data);
BB_Reader bbr = BB_ReaderFromBuff(&bb);
//- Read header
BB_ReadBit(&bbr); // Raw
i64 remote_ack = BB_ReadIV(&bbr);
//- Read control
b32 done = 0;
while (!done)
{
// TODO: Delta compress
P_Control *raw_control = (P_Control *)BB_ReadBytesRaw(&bbr, sizeof(P_Control));
if (raw_control)
{
i64 control_tick = raw_control->tick;
P_Control *control = &client->controls[control_tick % client->max_controls];
{
*control = *raw_control;
}
//- Propagate control
//
// In case future snapshots aren't received, the last-received control should be used, so we copy this control forward here
//
// 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)
{
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;
}
}
}
//- Update ack
if (control->tick < world_frame->tick)
{
// If the control is outdated, go ahead and just ack it
if (control->tick > client->ack)
{
client->ack = control->tick;
}
}
else
{
// Acknowledge the highest contiguously received control tick starting with the current frame
i64 max_ack_count = SIM_TICKS_PER_SECOND * SIM_MAX_PING;
i64 ack_tick = client->ack;
for (i64 check_ack_tick = world_frame->tick; check_ack_tick < world_frame->tick + max_ack_count; ++check_ack_tick)
{
P_Control *ack_control = &client->controls[check_ack_tick % client->max_controls];
if (ack_control->orig_tick == check_ack_tick)
{
ack_tick = check_ack_tick;
}
else
{
break;
}
}
client->ack = ack_tick;
}
}
else
{
done = 1;
}
}
}
}
}
//////////////////////////////
//- Apply client controls
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))
{
player->control = *control;
}
}
////////////////////////////// //////////////////////////////
//- Process connection messages //- Process connection messages
@ -703,7 +842,7 @@ void S_TickForever(WaveLaneCtx *lane)
BB_ResetWriter(&packer_bbw); BB_ResetWriter(&packer_bbw);
u64 delta_start = BB_GetNumBytesWritten(&packer_bbw); u64 delta_start = BB_GetNumBytesWritten(&packer_bbw);
{ {
// TODO: Compress // TODO: Delta compress
should_transmit = 1; should_transmit = 1;
BB_WriteBytes(&packer_bbw, StringFromStruct(ent)); BB_WriteBytes(&packer_bbw, StringFromStruct(ent));
} }
@ -722,7 +861,7 @@ void S_TickForever(WaveLaneCtx *lane)
} }
} }
// Collect deltas into snapshots //- Collect & send snapshots
{ {
u64 max_snapshot_size = NET_PacketSize / 2; u64 max_snapshot_size = NET_PacketSize / 2;
PackedDeltaNode *delta_node = first_delta_node; PackedDeltaNode *delta_node = first_delta_node;
@ -745,7 +884,8 @@ void S_TickForever(WaveLaneCtx *lane)
BB_WriteIV(&packer_bbw, world_frame->tick); BB_WriteIV(&packer_bbw, world_frame->tick);
BB_WriteIV(&packer_bbw, world_frame->time_ns); BB_WriteIV(&packer_bbw, world_frame->time_ns);
BB_WriteUBits(&packer_bbw, world->seed, 64); BB_WriteUBits(&packer_bbw, world->seed, 64);
BB_WriteUBits(&packer_bbw, client->player_key.v, 64); BB_WriteUBits(&packer_bbw, client->player.v, 64);
BB_WriteIV(&packer_bbw, client->ack);
} }
//- Append packed delta //- Append packed delta

View File

@ -12,11 +12,16 @@ Struct(S_Client)
NET_Key net_key; NET_Key net_key;
P_MsgList out_msgs; P_MsgList out_msgs;
P_Key player_key; P_Key player;
i32 name_len; i32 name_len;
u8 name_text[P_MaxPlayerNameLen]; u8 name_text[P_MaxPlayerNameLen];
i64 max_controls;
P_Control *controls;
i64 ack;
u64 remote_tiles_hash; u64 remote_tiles_hash;
}; };

View File

@ -360,7 +360,15 @@ void V_TickForever(WaveLaneCtx *lane)
P_World *sim_world = P_AcquireWorld(); P_World *sim_world = P_AcquireWorld();
P_World *predict_world = P_AcquireWorld(); P_World *predict_world = P_AcquireWorld();
P_World *blend_world = P_AcquireWorld(); P_World *local_world = P_AcquireWorld();
i64 max_local_controls = SIM_MAX_PING * SIM_TICKS_PER_SECOND;
P_Control *local_controls = PushStructs(perm, P_Control, max_local_controls);
i64 known_sim_tick = 0;
i64 remote_ack_received_at_ns = 0;
i64 remote_ack = 0;
i64 prev_snapshot_sent_at_ns = 0;
Vec2I32 tiles_dims = VEC2I32(P_TilesPitch, P_TilesPitch); Vec2I32 tiles_dims = VEC2I32(P_TilesPitch, P_TilesPitch);
Vec2I32 cells_dims = VEC2I32(V_CellsPerMeter * P_WorldPitch, V_CellsPerMeter * P_WorldPitch); Vec2I32 cells_dims = VEC2I32(V_CellsPerMeter * P_WorldPitch, V_CellsPerMeter * P_WorldPitch);
@ -819,8 +827,8 @@ void V_TickForever(WaveLaneCtx *lane)
Vec2 look_ratio = Zi; Vec2 look_ratio = Zi;
look_ratio.y = 0.5; look_ratio.y = 0.5;
look_ratio.x = look_ratio.y / (16.0 / 9.0); look_ratio.x = look_ratio.y / (16.0 / 9.0);
P_Ent *player = P_EntFromKey(blend_world->last_frame, V.player_key); P_Ent *player = P_EntFromKey(local_world->last_frame, V.player_key);
P_Ent *guy = P_EntFromKey(blend_world->last_frame, player->guy); P_Ent *guy = P_EntFromKey(local_world->last_frame, player->guy);
Vec2 guy_center = P_WorldShapeFromEnt(guy).centroid; Vec2 guy_center = P_WorldShapeFromEnt(guy).centroid;
Vec2 ui_center = MulVec2(frame->ui_dims, 0.5); Vec2 ui_center = MulVec2(frame->ui_dims, 0.5);
Vec2 look = MulXformBasisV2(prev_frame->xf.ui_to_world, SubVec2(ui_frame->cursor_pos, ui_center)); Vec2 look = MulXformBasisV2(prev_frame->xf.ui_to_world, SubVec2(ui_frame->cursor_pos, ui_center));
@ -929,11 +937,6 @@ void V_TickForever(WaveLaneCtx *lane)
frame->is_selecting = 1; frame->is_selecting = 1;
// frame->equipped_tile = P_TileKind_Floor; // frame->equipped_tile = P_TileKind_Floor;
} }
else if (m2_held)
{
frame->is_selecting = 1;
// frame->equipped_tile = P_TileKind_Empty;
}
if (frame->is_selecting && prev_frame->is_selecting) if (frame->is_selecting && prev_frame->is_selecting)
{ {
@ -975,13 +978,13 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Query entities //- Query entities
P_Ent *local_player = P_EntFromKey(blend_world->last_frame, V.player_key); P_Ent *local_player = P_EntFromKey(local_world->last_frame, V.player_key);
P_Ent *local_guy = P_EntFromKey(blend_world->last_frame, local_player->guy); P_Ent *local_guy = P_EntFromKey(local_world->last_frame, local_player->guy);
P_Ent *hovered_ent = &P_NilEnt; P_Ent *hovered_ent = &P_NilEnt;
{ {
// TODO: Real world query // TODO: Real world query
P_Shape cursor_shape = P_ShapeFromDesc(.count = 1, .points = { frame->world_cursor }); P_Shape cursor_shape = P_ShapeFromDesc(.count = 1, .points = { frame->world_cursor });
for (P_Ent *ent = P_FirstEnt(blend_world->last_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) for (P_Ent *ent = P_FirstEnt(local_world->last_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{ {
P_Shape ent_shape = P_WorldShapeFromEnt(ent); P_Shape ent_shape = P_WorldShapeFromEnt(ent);
b32 is_hovered = P_CollisionResultFromShapes(ent_shape, cursor_shape).collision_points_count > 0; b32 is_hovered = P_CollisionResultFromShapes(ent_shape, cursor_shape).collision_points_count > 0;
@ -2320,9 +2323,9 @@ void V_TickForever(WaveLaneCtx *lane)
UI_BuildLabelF("Predicted world tick: %F", FmtSint(predict_world->last_frame->tick)); UI_BuildLabelF("Predicted world tick: %F", FmtSint(predict_world->last_frame->tick));
UI_BuildLabelF("Predicted world entities: %F", FmtSint(predict_world->last_frame->ents_count)); UI_BuildLabelF("Predicted world entities: %F", FmtSint(predict_world->last_frame->ents_count));
UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y);
UI_BuildLabelF("Blended world seed: 0x%F", FmtHex(blend_world->seed)); UI_BuildLabelF("Local world seed: 0x%F", FmtHex(local_world->seed));
UI_BuildLabelF("Blended world tick: %F", FmtSint(blend_world->last_frame->tick)); UI_BuildLabelF("Local world tick: %F", FmtSint(local_world->last_frame->tick));
UI_BuildLabelF("Blended world entities: %F", FmtSint(blend_world->last_frame->ents_count)); UI_BuildLabelF("Local world entities: %F", FmtSint(local_world->last_frame->ents_count));
} }
UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y);
{ {
@ -2703,21 +2706,19 @@ void V_TickForever(WaveLaneCtx *lane)
frame->desired_sim_key = NET_KeyFromString(Lit("127.0.0.1"), Lit("22121")); frame->desired_sim_key = NET_KeyFromString(Lit("127.0.0.1"), Lit("22121"));
if (!NET_MatchKey(frame->sim_key, frame->desired_sim_key)) if (!NET_MatchKey(frame->sim_key, frame->desired_sim_key))
{ {
i64 now_ns = TimeNs(); i64 retry_rate_ns = NsFromSeconds(0.100);
// i64 retry_rate_ns = NsFromSeconds(0.100); // i64 retry_rate_ns = NsFromSeconds(0);
i64 retry_rate_ns = NsFromSeconds(0);
b32 should_try = 0; b32 should_try = 0;
if (!NET_MatchKey(prev_frame->desired_sim_key, frame->desired_sim_key)) if (!NET_MatchKey(prev_frame->desired_sim_key, frame->desired_sim_key))
{ {
should_try = 1; should_try = 1;
} }
if (V.connect_try_ns == 0 || now_ns - V.connect_try_ns > retry_rate_ns) if (V.connect_try_ns == 0 || frame->time_ns - V.connect_try_ns > retry_rate_ns)
{ {
should_try = 1; should_try = 1;
} }
// if (should_try && now_ns > NsFromSeconds(1.5))
if (should_try) if (should_try)
{ {
P_MsgNode tmp_msg_node = Zi; P_MsgNode tmp_msg_node = Zi;
@ -2733,7 +2734,7 @@ void V_TickForever(WaveLaneCtx *lane)
String packed = P_PackMessages(&packer_bbw, tmp_msglist); String packed = P_PackMessages(&packer_bbw, tmp_msglist);
NET_Send(net_pipe, frame->desired_sim_key, packed, NET_SendFlag_Raw); NET_Send(net_pipe, frame->desired_sim_key, packed, NET_SendFlag_Raw);
V.connect_try_ns = now_ns; V.connect_try_ns = frame->time_ns;
} }
} }
@ -2793,9 +2794,6 @@ void V_TickForever(WaveLaneCtx *lane)
for (P_MsgNode *msg_node = in_msgs.first; msg_node; msg_node = msg_node->next) for (P_MsgNode *msg_node = in_msgs.first; msg_node; msg_node = msg_node->next)
{ {
P_Msg *msg = &msg_node->msg; P_Msg *msg = &msg_node->msg;
//- Snapshot
if (msg->kind == P_MsgKind_Raw) if (msg->kind == P_MsgKind_Raw)
{ {
BB_Buff bb = BB_BuffFromString(msg->data); BB_Buff bb = BB_BuffFromString(msg->data);
@ -2808,6 +2806,14 @@ void V_TickForever(WaveLaneCtx *lane)
i64 time_ns = BB_ReadIV(&bbr); i64 time_ns = BB_ReadIV(&bbr);
u64 world_seed = BB_ReadUBits(&bbr, 64); u64 world_seed = BB_ReadUBits(&bbr, 64);
P_Key player_key = { .v = BB_ReadUBits(&bbr, 64) }; P_Key player_key = { .v = BB_ReadUBits(&bbr, 64) };
i64 tmp_remote_ack = BB_ReadIV(&bbr);
if (dst_tick > known_sim_tick)
{
known_sim_tick = dst_tick;
remote_ack_received_at_ns = frame->time_ns;
remote_ack = tmp_remote_ack;
}
P_Frame *src_frame = P_FrameFromTick(sim_world, src_tick); P_Frame *src_frame = P_FrameFromTick(sim_world, src_tick);
if (src_frame->tick == src_tick) if (src_frame->tick == src_tick)
@ -2828,11 +2834,12 @@ void V_TickForever(WaveLaneCtx *lane)
b32 done = 0; b32 done = 0;
while (!done && BB_NumBitsRemaining(&bbr) > 0) while (!done && BB_NumBitsRemaining(&bbr) > 0)
{ {
P_Ent *src_ent = (P_Ent *)BB_ReadBytesRaw(&bbr, sizeof(P_Ent)); // TODO: Delta compress
if (src_ent) P_Ent *raw_ent = (P_Ent *)BB_ReadBytesRaw(&bbr, sizeof(P_Ent));
if (raw_ent)
{ {
P_EntListNode tmp_ent_node = Zi; P_EntListNode tmp_ent_node = Zi;
CopyStruct(&tmp_ent_node.ent, src_ent); CopyStruct(&tmp_ent_node.ent, raw_ent);
P_EntList ent_list = Zi; P_EntList ent_list = Zi;
ent_list.first = &tmp_ent_node; ent_list.first = &tmp_ent_node;
ent_list.last = &tmp_ent_node; ent_list.last = &tmp_ent_node;
@ -2933,14 +2940,168 @@ void V_TickForever(WaveLaneCtx *lane)
} }
////////////////////////////// //////////////////////////////
//- Push player snapshots //- Determine local simulation bounds
// FIXME: Real ping // FIXME: Real ping
// f64 ping = 0.250; // f64 ping = 0.250;
f64 ping = 0; f64 ping = 0;
i64 ping_ns = NsFromSeconds(ping); i64 ping_ns = NsFromSeconds(ping);
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;
//////////////////////////////
//- Create player control
if (frame->predict_to > prev_frame->predict_to)
{
i64 control_tick = frame->predict_to;
P_Control control = Zi;
control.tick = control_tick;
control.orig_tick = control_tick;
control.move = frame->move;
control.look = frame->look;
control.fire_held = frame->fire_held;
control.fire_presses = frame->fire_presses;
//- Fill controls buffer backwards
i64 max_fill_count = SIM_TICKS_PER_SECOND / 4;
for (i64 dst_tick = control_tick; dst_tick > MaxI64(prev_frame->predict_to, (dst_tick - max_local_controls)); --dst_tick)
{
P_Control *dst_control = &local_controls[dst_tick % max_local_controls];
if (dst_tick > dst_control->orig_tick)
{
*dst_control = control;
dst_control->tick = dst_tick;
dst_control->orig_tick = control_tick;
}
else
{
break;
}
}
}
//////////////////////////////
//- Send player snapshots
{
i64 snapshot_send_threshold_ns = NsFromSeconds(1.0) / SIM_TICKS_PER_SECOND;
if (prev_snapshot_sent_at_ns == 0 || (frame->time_ns - prev_snapshot_sent_at_ns) > snapshot_send_threshold_ns)
{
// FIXME: Base this on ping
i64 max_control_sends = SIM_TICKS_PER_SECOND;
//- Pack control deltas
Struct(PackedDeltaNode)
{
PackedDeltaNode *next;
String packed;
};
i64 total_delta_bytes = 0;
PackedDeltaNode *first_delta_node = 0;
PackedDeltaNode *last_delta_node = 0;
if (frame->predict_to >= known_sim_tick)
{
i64 last_send_tick = frame->predict_to;
i64 first_send_tick = MaxI64(known_sim_tick, remote_ack + 1);
first_send_tick = MaxI64(first_send_tick, last_send_tick - max_control_sends);
for (i64 send_tick = first_send_tick; send_tick <= last_send_tick; ++send_tick)
{
b32 should_transmit = 0;
BB_ResetWriter(&packer_bbw);
u64 delta_start = BB_GetNumBytesWritten(&packer_bbw);
{
// TODO: Delta compress
should_transmit = 1;
P_Control *control = &local_controls[send_tick % max_local_controls];
BB_WriteBytes(&packer_bbw, StringFromStruct(control));
}
u64 delta_end = BB_GetNumBytesWritten(&packer_bbw);
if (should_transmit)
{
String packed = Zi;
packed.text = BB_GetWrittenRaw(&packer_bbw) + delta_start;
packed.len = delta_end - delta_start;
{
PackedDeltaNode *delta_node = PushStruct(frame->arena, PackedDeltaNode);
delta_node->packed = PushString(frame->arena, packed);
SllQueuePush(first_delta_node, last_delta_node, delta_node);
total_delta_bytes += packed.len;
}
}
}
}
//- Collect & send snapshots
{
u64 max_snapshot_size = NET_PacketSize / 2;
PackedDeltaNode *delta_node = first_delta_node;
u64 snapshot_start = 0;
b32 new_snapshot = 1;
b32 done = 0;
while (!done)
{
PackedDeltaNode *next_delta_node = delta_node ? delta_node->next : 0;
{
//- Init snapshot
if (new_snapshot)
{
new_snapshot = 0;
BB_ResetWriter(&packer_bbw);
snapshot_start = BB_GetNumBytesWritten(&packer_bbw);
BB_WriteBit(&packer_bbw, 1); // Raw
BB_WriteIV(&packer_bbw, known_sim_tick);
}
//- Append packed delta
BB_WriteAlignToNextByte(&packer_bbw);
if (delta_node)
{
BB_WriteBytes(&packer_bbw, delta_node->packed);
}
//- Submit snapshot
{
if (!delta_node || !next_delta_node)
{
new_snapshot = 1;
done = 1;
}
u64 next_len = 0;
if (next_delta_node)
{
next_len = next_delta_node->packed.len;
}
u64 cur_snapshot_len = BB_GetNumBytesWritten(&packer_bbw);
if ((cur_snapshot_len - snapshot_start) + next_len >= max_snapshot_size)
{
new_snapshot = 1;
}
}
if (new_snapshot)
{
u64 snapshot_end = BB_GetNumBytesWritten(&packer_bbw);
String snapshot = Zi;
snapshot.text = BB_GetWrittenRaw(&packer_bbw) + snapshot_start;
snapshot.len = snapshot_end - snapshot_start;
// FIXME: Send raw snapshots
// NET_Send(net_pipe, frame->sim_key, snapshot, NET_SendFlag_Raw);
NET_Send(net_pipe, frame->sim_key, snapshot, NET_SendFlag_None);
}
}
delta_node = next_delta_node;
}
}
prev_snapshot_sent_at_ns = frame->time_ns;
}
}
// if (frame->predict_to != prev_frame->predict_to) // if (frame->predict_to != prev_frame->predict_to)
// { // {
// { // {
@ -3031,21 +3192,21 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Update blended world //- Update local world
// TODO: Remove this // TODO: Remove this
P_Frame *blend_frame = &P_NilFrame; P_Frame *local_frame = &P_NilFrame;
{ {
if (blend_world->tiles_hash != predict_world->tiles_hash) if (local_world->tiles_hash != predict_world->tiles_hash)
{ {
blend_world->tiles_hash = predict_world->tiles_hash; local_world->tiles_hash = predict_world->tiles_hash;
CopyStructs(blend_world->tiles, predict_world->tiles, P_TilesCount); CopyStructs(local_world->tiles, predict_world->tiles, P_TilesCount);
tiles_dirty = 1; tiles_dirty = 1;
} }
blend_world->seed = predict_world->seed; local_world->seed = predict_world->seed;
P_ClearFrames(blend_world, I64Min, I64Max); P_ClearFrames(local_world, I64Min, I64Max);
blend_frame = P_PushFrame(blend_world, predict_world->last_frame, predict_world->last_frame->tick); local_frame = P_PushFrame(local_world, predict_world->last_frame, predict_world->last_frame->tick);
} }
@ -3087,7 +3248,7 @@ void V_TickForever(WaveLaneCtx *lane)
for (P_Ent *bullet = P_FirstEnt(blend_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet)) for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet))
{ {
if (bullet->is_bullet) if (bullet->is_bullet)
{ {
@ -3267,7 +3428,7 @@ void V_TickForever(WaveLaneCtx *lane)
if (0) if (0)
{ {
for (P_Ent *bullet = P_FirstEnt(blend_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet)) for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet))
{ {
if (bullet->is_bullet && bullet->has_hit) if (bullet->is_bullet && bullet->has_hit)
{ {
@ -3344,7 +3505,7 @@ void V_TickForever(WaveLaneCtx *lane)
// { // {
// for (P_Ent *firer = P_FirstEnt(blend_frame); firer->valid; firer = P_NextEnt(firer)) // for (P_Ent *firer = P_FirstEnt(local_frame); firer->valid; firer = P_NextEnt(firer))
// { // {
// if (firer->fire_held) // if (firer->fire_held)
// { // {
@ -3359,7 +3520,7 @@ void V_TickForever(WaveLaneCtx *lane)
// P_RaycastResult victim_raycast = Zi; // P_RaycastResult victim_raycast = Zi;
// { // {
// f32 closest_len_sq = Inf; // f32 closest_len_sq = Inf;
// for (P_Ent *victim = P_FirstEnt(blend_frame); victim->valid; victim = P_NextEnt(victim)) // for (P_Ent *victim = P_FirstEnt(local_frame); victim->valid; victim = P_NextEnt(victim))
// { // {
// if (victim != firer) // if (victim != firer)
// { // {
@ -3430,7 +3591,7 @@ void V_TickForever(WaveLaneCtx *lane)
// // for (P_QueryResult query = P_FirstRaycast(wrold, ray_start, ray_dir); query. // // for (P_QueryResult query = P_FirstRaycast(wrold, ray_start, ray_dir); query.
// // P_RaycastWorldResult hits = P_RaycastWorld(blend_frame, ray_start, ray_dir) // // P_RaycastWorldResult hits = P_RaycastWorld(local_frame, ray_start, ray_dir)
// // { // // {
// // } // // }
// } // }
@ -3639,7 +3800,7 @@ void V_TickForever(WaveLaneCtx *lane)
G_CopyCpuToTexture( G_CopyCpuToTexture(
frame->cl, frame->cl,
gpu_tiles, VEC3I32(0, 0, 0), gpu_tiles, VEC3I32(0, 0, 0),
blend_world->tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1), local_world->tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1),
RNG3I32(VEC3I32(0, 0, 0), VEC3I32(tiles_dims.x, tiles_dims.y, 1)) RNG3I32(VEC3I32(0, 0, 0), VEC3I32(tiles_dims.x, tiles_dims.y, 1))
); );
G_DumbMemoryLayoutSync(frame->cl, gpu_tiles, G_Layout_DirectQueue_ShaderRead); G_DumbMemoryLayoutSync(frame->cl, gpu_tiles, G_Layout_DirectQueue_ShaderRead);

View File

@ -63,7 +63,7 @@ ComputeShader(V_ClearParticlesCS, 64)
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Backdrop shader //~ Backdrop shaders
ComputeShader2D(V_BackdropCS, 8, 8) ComputeShader2D(V_BackdropCS, 8, 8)
{ {
@ -270,7 +270,7 @@ ComputeShader2D(V_BackdropCS, 8, 8)
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Quad shader //~ Quad shaders
////////////////////////////// //////////////////////////////
//- Vertex shader //- Vertex shader
@ -461,7 +461,7 @@ ComputeShader(V_SimParticlesCS, 64)
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Shape shader //~ Shape shaders
////////////////////////////// //////////////////////////////
//- Vertex shader //- Vertex shader
@ -493,7 +493,7 @@ PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input)
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Overlay shader //~ Overlay shaders
////////////////////////////// //////////////////////////////
//- Vertex shader //- Vertex shader

View File

@ -53,10 +53,10 @@ Vec4 V_DryColor(Vec4 color, f32 dryness);
ComputeShader2D(V_ClearCellsCS, 8, 8); ComputeShader2D(V_ClearCellsCS, 8, 8);
ComputeShader(V_ClearParticlesCS, 64); ComputeShader(V_ClearParticlesCS, 64);
//- Backdrop shader //- Backdrop shaders
ComputeShader2D(V_BackdropCS, 8, 8); ComputeShader2D(V_BackdropCS, 8, 8);
//- Quad shader //- Quad shaders
VertexShader(V_DQuadVS, V_DQuadPSInput); VertexShader(V_DQuadVS, V_DQuadPSInput);
PixelShader(V_DQuadPS, V_DQuadPSOutput, V_DQuadPSInput input); PixelShader(V_DQuadPS, V_DQuadPSOutput, V_DQuadPSInput input);
@ -64,10 +64,10 @@ PixelShader(V_DQuadPS, V_DQuadPSOutput, V_DQuadPSInput input);
ComputeShader(V_EmitParticlesCS, 64); ComputeShader(V_EmitParticlesCS, 64);
ComputeShader(V_SimParticlesCS, 64); ComputeShader(V_SimParticlesCS, 64);
//- Shape shader //- Shape shaders
VertexShader(V_DVertVS, V_DVertPSInput); VertexShader(V_DVertVS, V_DVertPSInput);
PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input); PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input);
//- Overlay shader //- Overlay shaders
VertexShader(V_OverlayVS, V_OverlayPSInput); VertexShader(V_OverlayVS, V_OverlayPSInput);
PixelShader(V_OverlayPS, V_OverlayPSOutput, V_OverlayPSInput input); PixelShader(V_OverlayPS, V_OverlayPSOutput, V_OverlayPSInput input);

View File

@ -1,5 +1,5 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Rect shader //~ Rect shaders
////////////////////////////// //////////////////////////////
//- Vertex shader //- Vertex shader
@ -115,7 +115,7 @@ PixelShader(UI_DRectPS, UI_DRectPSOutput, UI_DRectPSInput input)
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Blit shader //~ Blit shaders
////////////////////////////// //////////////////////////////
//- Vertex shader //- Vertex shader