refragment simulation snapshots to ensure client frames advance atomically
This commit is contained in:
parent
64aff1893d
commit
327a0e4af4
@ -1,3 +1,5 @@
|
|||||||
|
#define P_MaxFrameSnapshotFragments Kibi(4)
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Key types
|
//~ Key types
|
||||||
|
|
||||||
@ -240,6 +242,13 @@ Struct(P_Frame)
|
|||||||
i64 max_constraints;
|
i64 max_constraints;
|
||||||
i64 constraints_count;
|
i64 constraints_count;
|
||||||
P_Constraint *constraints;
|
P_Constraint *constraints;
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//- Snapshot-assembly state
|
||||||
|
|
||||||
|
u64 fragments_count;
|
||||||
|
u64 received_fragments_count;
|
||||||
|
u64 received_fragment_bits[(P_MaxFrameSnapshotFragments + 63) / 64];
|
||||||
};
|
};
|
||||||
|
|
||||||
Struct(P_FrameBin)
|
Struct(P_FrameBin)
|
||||||
|
|||||||
@ -861,7 +861,7 @@ void S_TickForever(WaveLaneCtx *lane)
|
|||||||
PackedDeltaNode *next;
|
PackedDeltaNode *next;
|
||||||
String packed;
|
String packed;
|
||||||
};
|
};
|
||||||
i64 total_delta_bytes = 0;
|
i64 total_delta_accum = 0;
|
||||||
PackedDeltaNode *first_delta_node = 0;
|
PackedDeltaNode *first_delta_node = 0;
|
||||||
PackedDeltaNode *last_delta_node = 0;
|
PackedDeltaNode *last_delta_node = 0;
|
||||||
|
|
||||||
@ -887,30 +887,49 @@ void S_TickForever(WaveLaneCtx *lane)
|
|||||||
PackedDeltaNode *delta_node = PushStruct(frame_arena, PackedDeltaNode);
|
PackedDeltaNode *delta_node = PushStruct(frame_arena, PackedDeltaNode);
|
||||||
delta_node->packed = PushString(frame_arena, packed);
|
delta_node->packed = PushString(frame_arena, packed);
|
||||||
SllQueuePush(first_delta_node, last_delta_node, delta_node);
|
SllQueuePush(first_delta_node, last_delta_node, delta_node);
|
||||||
total_delta_bytes += packed.len;
|
total_delta_accum += packed.len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//- Collect & send snapshots
|
//- Count fragments
|
||||||
|
i64 fragment_delta_accum_cutoff_threshold = NET_PacketSize / 2;
|
||||||
|
i64 fragments_count = 0;
|
||||||
|
{
|
||||||
|
i64 cur_fragment_delta_accum = 0;
|
||||||
|
for (PackedDeltaNode *delta_node = first_delta_node; delta_node; delta_node = delta_node->next)
|
||||||
|
{
|
||||||
|
cur_fragment_delta_accum += delta_node->packed.len;
|
||||||
|
if (!delta_node->next || (i64)(cur_fragment_delta_accum + delta_node->next->packed.len) >= fragment_delta_accum_cutoff_threshold)
|
||||||
|
{
|
||||||
|
fragments_count += 1;
|
||||||
|
cur_fragment_delta_accum = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//- Collect & send fragments
|
||||||
{
|
{
|
||||||
u64 snapshot_cutoff_threshold = NET_PacketSize / 2;
|
|
||||||
PackedDeltaNode *delta_node = first_delta_node;
|
PackedDeltaNode *delta_node = first_delta_node;
|
||||||
|
|
||||||
u64 snapshot_start = 0;
|
u64 fragment_start = 0;
|
||||||
b32 new_snapshot = 1;
|
i64 fragment_idx = 0;
|
||||||
|
i64 cur_fragment_delta_accum = 0;
|
||||||
|
b32 new_fragment = 1;
|
||||||
b32 done = 0;
|
b32 done = 0;
|
||||||
while (!done)
|
while (!done)
|
||||||
{
|
{
|
||||||
PackedDeltaNode *next_delta_node = delta_node ? delta_node->next : 0;
|
PackedDeltaNode *next_delta_node = delta_node ? delta_node->next : 0;
|
||||||
{
|
{
|
||||||
//- Init snapshot
|
//- Init fragment
|
||||||
if (new_snapshot)
|
if (new_fragment)
|
||||||
{
|
{
|
||||||
new_snapshot = 0;
|
new_fragment = 0;
|
||||||
BB_ResetWriter(&packer_bbw);
|
BB_ResetWriter(&packer_bbw);
|
||||||
snapshot_start = BB_GetNumBytesWritten(&packer_bbw);
|
fragment_start = BB_GetNumBytesWritten(&packer_bbw);
|
||||||
BB_WriteBit(&packer_bbw, 1); // Raw
|
BB_WriteBit(&packer_bbw, 1); // Raw
|
||||||
|
BB_WriteIV(&packer_bbw, fragment_idx);
|
||||||
|
BB_WriteIV(&packer_bbw, fragments_count);
|
||||||
BB_WriteIV(&packer_bbw, src_frame->tick);
|
BB_WriteIV(&packer_bbw, src_frame->tick);
|
||||||
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);
|
||||||
@ -924,43 +943,45 @@ void S_TickForever(WaveLaneCtx *lane)
|
|||||||
if (delta_node)
|
if (delta_node)
|
||||||
{
|
{
|
||||||
BB_WriteBytes(&packer_bbw, delta_node->packed);
|
BB_WriteBytes(&packer_bbw, delta_node->packed);
|
||||||
|
cur_fragment_delta_accum += delta_node->packed.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
//- Submit snapshot
|
//- Submit fragment
|
||||||
{
|
{
|
||||||
if (!delta_node || !next_delta_node)
|
if (!delta_node || !next_delta_node)
|
||||||
{
|
{
|
||||||
new_snapshot = 1;
|
new_fragment = 1;
|
||||||
done = 1;
|
done = 1;
|
||||||
}
|
}
|
||||||
u64 next_len = 0;
|
i64 next_delta_size = 0;
|
||||||
if (next_delta_node)
|
if (next_delta_node)
|
||||||
{
|
{
|
||||||
next_len = next_delta_node->packed.len;
|
next_delta_size = next_delta_node->packed.len;
|
||||||
}
|
}
|
||||||
u64 cur_snapshot_len = BB_GetNumBytesWritten(&packer_bbw);
|
if (cur_fragment_delta_accum + next_delta_size >= fragment_delta_accum_cutoff_threshold)
|
||||||
if ((cur_snapshot_len - snapshot_start) + next_len >= snapshot_cutoff_threshold)
|
|
||||||
{
|
{
|
||||||
new_snapshot = 1;
|
new_fragment = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (new_snapshot)
|
if (new_fragment)
|
||||||
{
|
{
|
||||||
u64 snapshot_end = BB_GetNumBytesWritten(&packer_bbw);
|
u64 fragment_end = BB_GetNumBytesWritten(&packer_bbw);
|
||||||
String snapshot = Zi;
|
String fragment = Zi;
|
||||||
snapshot.text = BB_GetWrittenRaw(&packer_bbw) + snapshot_start;
|
fragment.text = BB_GetWrittenRaw(&packer_bbw) + fragment_start;
|
||||||
snapshot.len = snapshot_end - snapshot_start;
|
fragment.len = fragment_end - fragment_start;
|
||||||
NET_Send(net_pipe, client->net_key, snapshot, NET_SendFlag_Raw);
|
NET_Send(net_pipe, client->net_key, fragment, NET_SendFlag_Raw);
|
||||||
|
|
||||||
// Sanity check to catch whenever we end up packing too much information
|
// Sanity check to catch whenever we end up packing too much information
|
||||||
// into a single raw snapshot, causing it to drop. This isn't necessarily
|
// into a single raw fragment, causing it to drop. This isn't necessarily
|
||||||
// an error, but signals we may have botched the packing code or need tighter
|
// an error, but signals we may have botched the packing code or need tighter
|
||||||
// compression
|
// compression
|
||||||
Assert(snapshot.len <= NET_PacketSize);
|
Assert(fragment.len <= NET_PacketSize);
|
||||||
|
fragment_idx += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delta_node = next_delta_node;
|
delta_node = next_delta_node;
|
||||||
}
|
}
|
||||||
|
Assert(fragment_idx == fragments_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -371,7 +371,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
|||||||
i64 remote_ack = 0;
|
i64 remote_ack = 0;
|
||||||
i64 prev_snapshot_sent_at_ns = 0;
|
i64 prev_snapshot_sent_at_ns = 0;
|
||||||
|
|
||||||
i64 prev_known_sim_tick = 0;
|
i64 ready_sim_tick = 0;
|
||||||
i64 known_sim_tick = 0;
|
i64 known_sim_tick = 0;
|
||||||
|
|
||||||
Vec2I32 tiles_dims = VEC2I32(P_TilesPitch, P_TilesPitch);
|
Vec2I32 tiles_dims = VEC2I32(P_TilesPitch, P_TilesPitch);
|
||||||
@ -2872,6 +2872,8 @@ void V_TickForever(WaveLaneCtx *lane)
|
|||||||
|
|
||||||
//- Read header
|
//- Read header
|
||||||
BB_ReadBit(&bbr); // Raw
|
BB_ReadBit(&bbr); // Raw
|
||||||
|
i64 fragment_idx = BB_ReadIV(&bbr);
|
||||||
|
i64 fragments_count = BB_ReadIV(&bbr);
|
||||||
i64 src_tick = BB_ReadIV(&bbr);
|
i64 src_tick = BB_ReadIV(&bbr);
|
||||||
i64 dst_tick = BB_ReadIV(&bbr);
|
i64 dst_tick = BB_ReadIV(&bbr);
|
||||||
i64 time_ns = BB_ReadIV(&bbr);
|
i64 time_ns = BB_ReadIV(&bbr);
|
||||||
@ -2886,44 +2888,67 @@ void V_TickForever(WaveLaneCtx *lane)
|
|||||||
remote_ack = tmp_remote_ack;
|
remote_ack = tmp_remote_ack;
|
||||||
}
|
}
|
||||||
|
|
||||||
P_Frame *src_frame = P_FrameFromTick(sim_world, src_tick);
|
if (dst_tick > ready_sim_tick && fragments_count < P_MaxFrameSnapshotFragments)
|
||||||
if (src_frame->tick == src_tick)
|
|
||||||
{
|
{
|
||||||
V.player_key = player_key;
|
P_Frame *src_frame = P_FrameFromTick(sim_world, src_tick);
|
||||||
P_tl.local_player = player_key;
|
if (src_frame->tick == src_tick)
|
||||||
|
|
||||||
P_Frame *dst_frame = P_FrameFromTick(sim_world, dst_tick);
|
|
||||||
if (P_IsFrameNil(dst_frame))
|
|
||||||
{
|
{
|
||||||
dst_frame = P_PushFrame(sim_world, src_frame, dst_tick);
|
V.player_key = player_key;
|
||||||
}
|
P_tl.local_player = player_key;
|
||||||
dst_frame->time_ns = time_ns;
|
|
||||||
sim_world->seed = world_seed;
|
|
||||||
|
|
||||||
//- Read deltas
|
P_Frame *dst_frame = P_FrameFromTick(sim_world, dst_tick);
|
||||||
BB_ReadAlignToNextByte(&bbr);
|
if (P_IsFrameNil(dst_frame))
|
||||||
|
|
||||||
b32 done = 0;
|
|
||||||
while (!done && BB_NumBitsRemaining(&bbr) > 0)
|
|
||||||
{
|
|
||||||
// TODO: Delta compress
|
|
||||||
P_Ent *raw_ent = (P_Ent *)BB_ReadBytesRaw(&bbr, sizeof(P_Ent));
|
|
||||||
if (raw_ent)
|
|
||||||
{
|
{
|
||||||
P_EntListNode tmp_ent_node = Zi;
|
dst_frame = P_PushFrame(sim_world, src_frame, dst_tick);
|
||||||
CopyStruct(&tmp_ent_node.ent, raw_ent);
|
|
||||||
P_EntList ent_list = Zi;
|
|
||||||
ent_list.first = &tmp_ent_node;
|
|
||||||
ent_list.last = &tmp_ent_node;
|
|
||||||
++ent_list.count;
|
|
||||||
P_SpawnEntsFromList(dst_frame, ent_list);
|
|
||||||
}
|
}
|
||||||
else
|
dst_frame->fragments_count = fragments_count;
|
||||||
|
dst_frame->time_ns = time_ns;
|
||||||
|
sim_world->seed = world_seed;
|
||||||
|
|
||||||
|
if (fragment_idx < P_MaxFrameSnapshotFragments)
|
||||||
{
|
{
|
||||||
done = 1;
|
u64 *frag_bit_chunk = &dst_frame->received_fragment_bits[fragment_idx / 64];
|
||||||
|
u64 frag_bit = (u64)1 << fragment_idx;
|
||||||
|
b32 is_new = !(*frag_bit_chunk & frag_bit);
|
||||||
|
if (is_new)
|
||||||
|
{
|
||||||
|
*frag_bit_chunk |= frag_bit;
|
||||||
|
dst_frame->received_fragments_count += 1;
|
||||||
|
|
||||||
|
//- Read deltas
|
||||||
|
BB_ReadAlignToNextByte(&bbr);
|
||||||
|
|
||||||
|
b32 done = 0;
|
||||||
|
while (!done && BB_NumBitsRemaining(&bbr) > 0)
|
||||||
|
{
|
||||||
|
// 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, raw_ent);
|
||||||
|
P_EntList ent_list = Zi;
|
||||||
|
ent_list.first = &tmp_ent_node;
|
||||||
|
ent_list.last = &tmp_ent_node;
|
||||||
|
++ent_list.count;
|
||||||
|
P_SpawnEntsFromList(dst_frame, ent_list);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
done = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark snapshot as ready if assembled
|
||||||
|
if (dst_frame->received_fragments_count >= dst_frame->fragments_count)
|
||||||
|
{
|
||||||
|
ready_sim_tick = dst_tick;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2973,7 +2998,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
|||||||
|
|
||||||
// TODO: Remove this
|
// TODO: Remove this
|
||||||
// TODO: Delete all frames except for prediction base & remote ack
|
// 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, ready_sim_tick - SIM_TICKS_PER_SECOND);
|
||||||
// P_ClearFrames(sim_world, I64Min, sim_world->last_frame->tick - 1);
|
// 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;
|
||||||
|
|
||||||
@ -3031,9 +3056,7 @@ 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 = ready_sim_tick + 10;
|
||||||
|
|
||||||
frame->predict_to = known_sim_tick + 20;
|
|
||||||
|
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
//- Create player control
|
//- Create player control
|
||||||
@ -3087,10 +3110,10 @@ void V_TickForever(WaveLaneCtx *lane)
|
|||||||
i64 total_delta_bytes = 0;
|
i64 total_delta_bytes = 0;
|
||||||
PackedDeltaNode *first_delta_node = 0;
|
PackedDeltaNode *first_delta_node = 0;
|
||||||
PackedDeltaNode *last_delta_node = 0;
|
PackedDeltaNode *last_delta_node = 0;
|
||||||
if (frame->predict_to >= known_sim_tick)
|
if (frame->predict_to >= ready_sim_tick)
|
||||||
{
|
{
|
||||||
i64 last_send_tick = frame->predict_to;
|
i64 last_send_tick = frame->predict_to;
|
||||||
i64 first_send_tick = MaxI64(known_sim_tick, remote_ack + 1);
|
i64 first_send_tick = MaxI64(ready_sim_tick, remote_ack + 1);
|
||||||
first_send_tick = MaxI64(first_send_tick, last_send_tick - max_control_sends);
|
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)
|
for (i64 send_tick = first_send_tick; send_tick <= last_send_tick; ++send_tick)
|
||||||
{
|
{
|
||||||
@ -3138,7 +3161,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
|||||||
BB_ResetWriter(&packer_bbw);
|
BB_ResetWriter(&packer_bbw);
|
||||||
snapshot_start = BB_GetNumBytesWritten(&packer_bbw);
|
snapshot_start = BB_GetNumBytesWritten(&packer_bbw);
|
||||||
BB_WriteBit(&packer_bbw, 1); // Raw
|
BB_WriteBit(&packer_bbw, 1); // Raw
|
||||||
BB_WriteIV(&packer_bbw, known_sim_tick);
|
BB_WriteIV(&packer_bbw, ready_sim_tick);
|
||||||
}
|
}
|
||||||
|
|
||||||
//- Append packed delta
|
//- Append packed delta
|
||||||
@ -3233,7 +3256,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
|||||||
// FIXME: Not like this
|
// FIXME: Not like this
|
||||||
i64 max_predict_ticks = SIM_TICKS_PER_SECOND;
|
i64 max_predict_ticks = SIM_TICKS_PER_SECOND;
|
||||||
last_predict_tick = frame->predict_to;
|
last_predict_tick = frame->predict_to;
|
||||||
first_predict_tick = known_sim_tick - 2;
|
first_predict_tick = ready_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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user