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_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
str.text += 4;
str.len -= 4;
// https://en.wikipedia.org/wiki/Adler-32
u32 a = 1;
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;
// Xform desired_xf = xf;
// if (!IsVec2Zero(ent->control.look))
// {
// desired_xf = XformWithWorldRotation(xf, AngleFromVec2(ent->control.look));
// }
// 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));
// Vec2 pos_diff = SubVec2(desired_xf.og, xf.og);
// f32 angle_diff = UnwindAngleF32(RotationFromXform(desired_xf) - RotationFromXform(xf));
// ent->solved_v = pos_diff;
// ent->solved_w = angle_diff;
if (guy->is_guy)
{
// f32 damp_vel = damp_force * sim_dt;
if (Vec2Len(ent->solved_v) > 0.001)
{
f32 damp_force = TweakFloat("Guy damp force", 50, 0, 100);
Vec2 damp = MulVec2(NegVec2(ent->solved_v), damp_force * sim_dt);
ent->solved_v = AddVec2(ent->solved_v, damp);
}
else
{
ent->solved_v = VEC2(0, 0);
}
ZeroStruct(&guy->control);
}
}
for (P_Ent *player = P_FirstEnt(frame); !P_IsEntNil(player); player = P_NextEnt(player))
{
if (player->is_player)
{
f32 move_force = TweakFloat("Guy move force", 400, 0, 400);
f32 max_speed = TweakFloat("Guy max speed", 10, 0, 20);
P_Ent *guy = P_EntFromKey(frame, player->guy);
if (!P_IsEntNil(guy))
{
guy->control = player->control;
}
}
}
Vec2 new_velocity = ent->solved_v;
new_velocity = AddVec2(new_velocity, MulVec2(ent->control.move, move_force * sim_dt));
//////////////////////////////
//- Integrate guy control forces
// if (Vec2Len(new_velocity) > max_speed)
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;
// if (!IsVec2Zero(control.look))
// {
// new_velocity = Vec2WithLen(new_velocity, max_speed);
// desired_xf = XformWithWorldRotation(xf, AngleFromVec2(control.look));
// }
// f32 move_speed = TweakFloat("Guy move speed", 6.5, 0, 20);
// desired_xf.og = AddVec2(xf.og, MulVec2(control.move, move_speed * sim_dt));
ent->solved_v = new_velocity;
// Vec2 pos_diff = SubVec2(desired_xf.og, xf.og);
// f32 angle_diff = UnwindAngleF32(RotationFromXform(desired_xf) - RotationFromXform(xf));
// guy->solved_v = pos_diff;
// guy->solved_w = angle_diff;
{
// f32 damp_vel = damp_force * sim_dt;
if (Vec2Len(guy->solved_v) > 0.001)
{
f32 damp_force = TweakFloat("Guy damp force", 50, 0, 100);
Vec2 damp = MulVec2(NegVec2(guy->solved_v), damp_force * sim_dt);
guy->solved_v = AddVec2(guy->solved_v, damp);
}
else
{
guy->solved_v = VEC2(0, 0);
}
}
{
f32 move_force = TweakFloat("Guy move force", 400, 0, 400);
f32 max_speed = TweakFloat("Guy max speed", 10, 0, 20);
Vec2 new_velocity = guy->solved_v;
new_velocity = AddVec2(new_velocity, MulVec2(control.move, move_force * sim_dt));
// if (Vec2Len(new_velocity) > max_speed)
// {
// new_velocity = Vec2WithLen(new_velocity, max_speed);
// }
guy->solved_v = new_velocity;
}
}
}

View File

@ -74,6 +74,9 @@ Struct(P_DebugDrawNode)
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 look;
f32 fire_held;
@ -119,6 +122,8 @@ Struct(P_Ent)
Vec2 hit_entry;
Vec2 hit_entry_normal;
//- Player / Guy
P_Control control;
//- Player
@ -253,52 +258,6 @@ Struct(P_World)
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

View File

@ -202,26 +202,35 @@ void S_TickForever(WaveLaneCtx *lane)
if (S_IsClientNil(client))
{
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 src_name = TrimWhitespace(orig_src_name);
if (src_name.len < P_MinPlayerNameLen)
{
src_name = Lit("Player");
if (src_name.len < P_MinPlayerNameLen)
{
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
client = PushStruct(perm, S_Client);
client->hash = hash;
client->net_key = net_key;
client->name_len = MinI64(countof(client->name_text), src_name.len);
CopyBytes(client->name_text, src_name.text, client->name_len);
{
u64 hash = NET_HashFromKey(net_key);
S_ClientBin *bin = &S.client_bins[hash % S.client_bins_count];
client->hash = hash;
client->net_key = net_key;
client->name_len = MinI64(countof(client->name_text), src_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);
DllQueuePushNP(bin->first, bin->last, client, next_in_bin, prev_in_bin);
}
DllQueuePushNPZ(&S_NilClient, S.first_client, S.last_client, client, next, prev);
DllQueuePushNP(bin->first, bin->last, client, next_in_bin, prev_in_bin);
// Acknowledge player connection
{
@ -258,15 +267,15 @@ void S_TickForever(WaveLaneCtx *lane)
P_EntList new_players = Zi;
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))
{
player = P_PushTempEnt(frame_arena, &new_players);
player->key = client->player_key;
player->key = client->player;
}
player->is_player = 1;
// Update name
@ -307,6 +316,136 @@ void S_TickForever(WaveLaneCtx *lane)
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
@ -703,7 +842,7 @@ void S_TickForever(WaveLaneCtx *lane)
BB_ResetWriter(&packer_bbw);
u64 delta_start = BB_GetNumBytesWritten(&packer_bbw);
{
// TODO: Compress
// TODO: Delta compress
should_transmit = 1;
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;
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->time_ns);
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

View File

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

View File

@ -360,7 +360,15 @@ void V_TickForever(WaveLaneCtx *lane)
P_World *sim_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 cells_dims = VEC2I32(V_CellsPerMeter * P_WorldPitch, V_CellsPerMeter * P_WorldPitch);
@ -819,8 +827,8 @@ void V_TickForever(WaveLaneCtx *lane)
Vec2 look_ratio = Zi;
look_ratio.y = 0.5;
look_ratio.x = look_ratio.y / (16.0 / 9.0);
P_Ent *player = P_EntFromKey(blend_world->last_frame, V.player_key);
P_Ent *guy = P_EntFromKey(blend_world->last_frame, player->guy);
P_Ent *player = P_EntFromKey(local_world->last_frame, V.player_key);
P_Ent *guy = P_EntFromKey(local_world->last_frame, player->guy);
Vec2 guy_center = P_WorldShapeFromEnt(guy).centroid;
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));
@ -929,11 +937,6 @@ void V_TickForever(WaveLaneCtx *lane)
frame->is_selecting = 1;
// 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)
{
@ -975,13 +978,13 @@ void V_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- Query entities
P_Ent *local_player = P_EntFromKey(blend_world->last_frame, V.player_key);
P_Ent *local_guy = P_EntFromKey(blend_world->last_frame, local_player->guy);
P_Ent *local_player = P_EntFromKey(local_world->last_frame, V.player_key);
P_Ent *local_guy = P_EntFromKey(local_world->last_frame, local_player->guy);
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(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);
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 entities: %F", FmtSint(predict_world->last_frame->ents_count));
UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y);
UI_BuildLabelF("Blended world seed: 0x%F", FmtHex(blend_world->seed));
UI_BuildLabelF("Blended world tick: %F", FmtSint(blend_world->last_frame->tick));
UI_BuildLabelF("Blended world entities: %F", FmtSint(blend_world->last_frame->ents_count));
UI_BuildLabelF("Local world seed: 0x%F", FmtHex(local_world->seed));
UI_BuildLabelF("Local world tick: %F", FmtSint(local_world->last_frame->tick));
UI_BuildLabelF("Local world entities: %F", FmtSint(local_world->last_frame->ents_count));
}
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"));
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);
i64 retry_rate_ns = NsFromSeconds(0.100);
// i64 retry_rate_ns = NsFromSeconds(0);
b32 should_try = 0;
if (!NET_MatchKey(prev_frame->desired_sim_key, frame->desired_sim_key))
{
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;
}
// if (should_try && now_ns > NsFromSeconds(1.5))
if (should_try)
{
P_MsgNode tmp_msg_node = Zi;
@ -2733,7 +2734,7 @@ void V_TickForever(WaveLaneCtx *lane)
String packed = P_PackMessages(&packer_bbw, tmp_msglist);
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)
{
P_Msg *msg = &msg_node->msg;
//- Snapshot
if (msg->kind == P_MsgKind_Raw)
{
BB_Buff bb = BB_BuffFromString(msg->data);
@ -2808,6 +2806,14 @@ void V_TickForever(WaveLaneCtx *lane)
i64 time_ns = BB_ReadIV(&bbr);
u64 world_seed = 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);
if (src_frame->tick == src_tick)
@ -2828,11 +2834,12 @@ void V_TickForever(WaveLaneCtx *lane)
b32 done = 0;
while (!done && BB_NumBitsRemaining(&bbr) > 0)
{
P_Ent *src_ent = (P_Ent *)BB_ReadBytesRaw(&bbr, sizeof(P_Ent));
if (src_ent)
// TODO: Delta compress
P_Ent *raw_ent = (P_Ent *)BB_ReadBytesRaw(&bbr, sizeof(P_Ent));
if (raw_ent)
{
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;
ent_list.first = &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
// f64 ping = 0.250;
f64 ping = 0;
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)
// {
// {
@ -3031,21 +3192,21 @@ void V_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- Update blended world
//- Update local world
// 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;
CopyStructs(blend_world->tiles, predict_world->tiles, P_TilesCount);
local_world->tiles_hash = predict_world->tiles_hash;
CopyStructs(local_world->tiles, predict_world->tiles, P_TilesCount);
tiles_dirty = 1;
}
blend_world->seed = predict_world->seed;
local_world->seed = predict_world->seed;
P_ClearFrames(blend_world, I64Min, I64Max);
blend_frame = P_PushFrame(blend_world, predict_world->last_frame, predict_world->last_frame->tick);
P_ClearFrames(local_world, I64Min, I64Max);
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)
{
@ -3267,7 +3428,7 @@ void V_TickForever(WaveLaneCtx *lane)
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)
{
@ -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)
// {
@ -3359,7 +3520,7 @@ void V_TickForever(WaveLaneCtx *lane)
// P_RaycastResult victim_raycast = Zi;
// {
// 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)
// {
@ -3430,7 +3591,7 @@ void V_TickForever(WaveLaneCtx *lane)
// // 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(
frame->cl,
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))
);
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)
{
@ -270,7 +270,7 @@ ComputeShader2D(V_BackdropCS, 8, 8)
}
////////////////////////////////////////////////////////////
//~ Quad shader
//~ Quad shaders
//////////////////////////////
//- Vertex shader
@ -461,7 +461,7 @@ ComputeShader(V_SimParticlesCS, 64)
}
////////////////////////////////////////////////////////////
//~ Shape shader
//~ Shape shaders
//////////////////////////////
//- Vertex shader
@ -493,7 +493,7 @@ PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input)
}
////////////////////////////////////////////////////////////
//~ Overlay shader
//~ Overlay shaders
//////////////////////////////
//- Vertex shader

View File

@ -53,10 +53,10 @@ Vec4 V_DryColor(Vec4 color, f32 dryness);
ComputeShader2D(V_ClearCellsCS, 8, 8);
ComputeShader(V_ClearParticlesCS, 64);
//- Backdrop shader
//- Backdrop shaders
ComputeShader2D(V_BackdropCS, 8, 8);
//- Quad shader
//- Quad shaders
VertexShader(V_DQuadVS, V_DQuadPSInput);
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_SimParticlesCS, 64);
//- Shape shader
//- Shape shaders
VertexShader(V_DVertVS, V_DVertPSInput);
PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input);
//- Overlay shader
//- Overlay shaders
VertexShader(V_OverlayVS, V_OverlayPSInput);
PixelShader(V_OverlayPS, V_OverlayPSOutput, V_OverlayPSInput input);

View File

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