diff --git a/src/pp/pp.c b/src/pp/pp.c index 601e51cf..a27ebe24 100644 --- a/src/pp/pp.c +++ b/src/pp/pp.c @@ -7,6 +7,10 @@ Readonly P_Ent P_NilEnt = { .health = 1, }; +Readonly P_Constraint P_NilConstraint = { + 0 +}; + Readonly P_Frame P_NilFrame = { .first_ent = &P_NilEnt, .last_ent = &P_NilEnt, @@ -23,7 +27,12 @@ void P_Bootstrap(void) //////////////////////////////////////////////////////////// //~ Nil helpers -b32 P_IsKeyNil(P_Key key) +b32 P_IsEntKeyNil(P_EntKey key) +{ + return key.v == 0; +} + +b32 P_IsConstraintKeyNil(P_ConstraintKey key) { return key.v == 0; } @@ -33,6 +42,11 @@ b32 P_IsEntNil(P_Ent *ent) return ent == 0 || ent == &P_NilEnt; } +b32 P_IsConstraintNil(P_Constraint *constraint) +{ + return constraint == 0 || constraint == &P_NilConstraint; +} + b32 P_IsFrameNil(P_Frame *frame) { return frame == 0 || frame == &P_NilFrame; @@ -41,15 +55,28 @@ b32 P_IsFrameNil(P_Frame *frame) //////////////////////////////////////////////////////////// //~ Key helpers -b32 P_MatchKey(P_Key a, P_Key b) +b32 P_MatchEntKey(P_EntKey a, P_EntKey b) { return a.v == b.v; } -P_Key P_RandKey(void) +b32 P_MatchConstraintKey(P_ConstraintKey a, P_ConstraintKey b) +{ + return a.v == b.v; +} + +P_ConstraintKey P_ConstraintKeyFromU64s(u64 a, u64 b) +{ + return (P_ConstraintKey) { .v = MixU64s(a, b) }; +} + +//////////////////////////////////////////////////////////// +//~ Rand helpers + +P_EntKey P_RandEntKey(void) { // TODO: Don't use true randomness for entity keys. It's overkill & non-deterministic. - P_Key result = Zi; + P_EntKey result = Zi; TrueRand(StringFromStruct(&result)); return result; } @@ -1083,11 +1110,11 @@ Vec2 P_EdgePointFromShape(P_Shape shape, Vec2 dir) //////////////////////////////////////////////////////////// //~ Lookup helpers -P_Ent *P_EntFromKey(P_Frame *frame, P_Key key) +P_Ent *P_EntFromKey(P_Frame *frame, P_EntKey key) { P_Ent *result = &P_NilEnt; P_World *world = frame->world; - if (!P_IsKeyNil(key) && frame->tick > 0 && frame->ents_count > 0 && frame->ent_bins_count > 0) + if (!P_IsEntKeyNil(key) && frame->tick > 0 && frame->ents_count > 0 && frame->ent_bins_count > 0) { i64 tick = frame->tick; P_EntBin *bin = &frame->ent_bins[key.v % frame->ent_bins_count]; @@ -1103,6 +1130,26 @@ P_Ent *P_EntFromKey(P_Frame *frame, P_Key key) return result; } +P_Constraint *P_ConstraintFromKey(P_Frame *frame, P_ConstraintKey key) +{ + P_Constraint *result = &P_NilConstraint; + P_World *world = frame->world; + if (!P_IsConstraintKeyNil(key) && frame->tick > 0 && frame->constraints_count > 0 && frame->constraint_bins_count > 0) + { + i64 tick = frame->tick; + P_ConstraintBin *bin = &frame->constraint_bins[key.v % frame->constraint_bins_count]; + for (P_Constraint *c = bin->first; c; c = c->next_in_bin) + { + if (c->key.v == key.v) + { + result = c; + break; + } + } + } + return result; +} + //////////////////////////////////////////////////////////// //~ Iteration helpers @@ -1126,6 +1173,26 @@ P_Ent *P_NextEnt(P_Ent *e) return result; } +P_Constraint *P_FirstConstraint(P_Frame *frame) +{ + P_Constraint *result = &P_NilConstraint; + if (!P_IsConstraintNil(frame->first_constraint)) + { + result = frame->first_constraint; + } + return result; +} + +P_Constraint *P_NextConstraint(P_Constraint *c) +{ + P_Constraint *result = &P_NilConstraint; + if (!P_IsConstraintNil(c) && !P_IsConstraintNil(c->next)) + { + result = c->next; + } + return result; +} + //////////////////////////////////////////////////////////// //~ List helpers @@ -1294,8 +1361,8 @@ void P_SpawnEntsFromList(P_Frame *frame, P_EntList ents) for (P_EntListNode *n = ents.first; n; n = n->next) { P_Ent *src = &n->ent; - P_Key key = src->key; - if (!P_IsKeyNil(src->key)) + P_EntKey key = src->key; + if (!P_IsEntKeyNil(src->key)) { P_EntBin *bin = &frame->ent_bins[key.v % frame->ent_bins_count]; P_Ent *dst = bin->first; @@ -1338,6 +1405,27 @@ void P_SpawnEntsFromList(P_Frame *frame, P_EntList ents) } } +P_Constraint *P_PushConstraint(P_Frame *frame, P_ConstraintKey key) +{ + P_World *world = frame->world; + P_Constraint *constraint = world->first_free_constraint; + if (constraint) + { + SllStackPop(world->first_free_constraint); + } + else + { + constraint = PushStructNoZero(world->frames_arena, P_Constraint); + } + *constraint = P_NilConstraint; + constraint->key = key; + P_ConstraintBin *bin = &frame->constraint_bins[key.v % frame->constraint_bins_count]; + DllQueuePushNPZ(&P_NilConstraint, frame->first_constraint, frame->last_constraint, constraint, next, prev); + DllQueuePushNP(bin->first, bin->last, constraint, next_in_bin, prev_in_bin); + ++frame->constraints_count; + return constraint; +} + P_Frame *P_FrameFromTick(P_World *world, i64 tick) { P_Frame *result = &P_NilFrame; @@ -1367,17 +1455,25 @@ void P_ClearFrames(P_World *world, i64 tick_min, i64 tick_max) P_Frame *next_frame = frame->next; if (frame->tick >= tick_min && frame->tick <= tick_max) { + // Free ents if (!P_IsEntNil(frame->first_ent)) { frame->last_ent->next = world->first_free_ent; world->first_free_ent = frame->first_ent; } + // Free constraints + if (!P_IsConstraintNil(frame->first_constraint)) + { + frame->last_constraint->next = world->first_free_constraint; + world->first_free_constraint = frame->first_constraint; + } + + // Free frame u64 hash = MixU64(frame->tick); P_FrameBin *bin = &world->frame_bins[hash % world->frame_bins_count]; DllQueueRemoveNPZ(&P_NilFrame, world->first_frame, world->last_frame, frame, next, prev); DllQueueRemoveNPZ(0, bin->first, bin->last, frame, next_in_bin, prev_in_bin); - SllStackPush(world->first_free_frame, frame); } else @@ -1396,16 +1492,17 @@ P_Frame *P_PushFrame(P_World *world, P_Frame *src_frame, i64 tick) SllStackPop(world->first_free_frame); i64 old_ent_bins_count = frame->ent_bins_count; P_EntBin *old_ent_bins = frame->ent_bins; - i64 old_constraints_cap = frame->constraints_cap; - P_Constraint *old_constraints = frame->constraints; + i64 old_constraint_bins_count = frame->constraint_bins_count; + P_ConstraintBin *old_constraint_bins = frame->constraint_bins; { ZeroStruct(frame); } frame->ent_bins_count = old_ent_bins_count; frame->ent_bins = old_ent_bins; - frame->constraints_cap = old_constraints_cap; - frame->constraints = old_constraints; ZeroStructs(frame->ent_bins, frame->ent_bins_count); + frame->constraint_bins_count = old_constraint_bins_count; + frame->constraint_bins = old_constraint_bins; + ZeroStructs(frame->constraint_bins, frame->constraint_bins_count); } else { @@ -1425,11 +1522,12 @@ P_Frame *P_PushFrame(P_World *world, P_Frame *src_frame, i64 tick) frame->ent_bins = PushStructs(world->frames_arena, P_EntBin, frame->ent_bins_count); } - if (!frame->constraints) + frame->first_constraint = &P_NilConstraint; + frame->last_constraint = &P_NilConstraint; + if (!frame->constraint_bins) { - frame->constraints_cap = Kibi(4); - frame->constraints_count = 0; - frame->constraints = PushStructsNoZero(world->frames_arena, P_Constraint, frame->constraints_cap); + frame->constraint_bins_count = Kibi(1); + frame->constraint_bins = PushStructs(world->frames_arena, P_ConstraintBin, frame->constraint_bins_count); } } @@ -1452,6 +1550,26 @@ P_Frame *P_PushFrame(P_World *world, P_Frame *src_frame, i64 tick) ++frame->ents_count; } + // Copy constraints + for (P_Constraint *src = P_FirstConstraint(src_frame); !P_IsConstraintNil(src); src = P_NextConstraint(src)) + { + P_Constraint *dst = world->first_free_constraint; + if (dst) + { + SllStackPop(world->first_free_constraint); + } + else + { + dst = PushStructNoZero(world->frames_arena, P_Constraint); + LogDebugF("RAAAAAAH"); + } + *dst = *src; + P_ConstraintBin *bin = &frame->constraint_bins[src->key.v % frame->constraint_bins_count]; + DllQueuePushNPZ(&P_NilConstraint, frame->first_constraint, frame->last_constraint, dst, next, prev); + DllQueuePushNP(bin->first, bin->last, dst, next_in_bin, prev_in_bin); + ++frame->constraints_count; + } + // Clear frames P_ClearFrames(world, tick, I64Max); @@ -1522,9 +1640,9 @@ void P_StepFrame(P_Frame *frame) { if (player->is_player) { - if (P_IsKeyNil(player->guy)) + if (P_IsEntKeyNil(player->guy)) { - player->guy = P_RandKey(); + player->guy = P_RandEntKey(); } P_Ent *guy = P_EntFromKey(frame, player->guy); if (P_IsEntNil(guy)) @@ -1576,7 +1694,10 @@ void P_StepFrame(P_Frame *frame) { // TODO: Something better than linear distance for scoring f32 score = Vec2Len(SubVec2(ent->xf.t, spawn->ent->xf.t)); - spawn->score = MinF32(spawn->score, score); + if (score < 10) + { + spawn->score = MinF32(spawn->score, score); + } } } } @@ -1585,7 +1706,7 @@ void P_StepFrame(P_Frame *frame) i64 highest_score = -Inf; for (SpawnNode *spawn = first_spawn; spawn; spawn = spawn->next) { - b32 ignore_spawn = spawns_count > 1 && P_MatchKey(spawn->ent->key, player->spawn); + b32 ignore_spawn = spawns_count > 1 && P_MatchEntKey(spawn->ent->key, player->spawn); if (!ignore_spawn) { f32 rand_score_spread = 5; @@ -1686,10 +1807,7 @@ void P_StepFrame(P_Frame *frame) } \ ////////////////////////////// - //- Copy previous frame's constraints - - frame->constraints_count = MinI64(prev_frame->constraints_count, frame->constraints_cap); - CopyStructs(frame->constraints, prev_frame->constraints, frame->constraints_count); + //- Setup constraints store i32 solver_steps_count = 4; f32 solver_dt = sim_dt / solver_steps_count; @@ -1717,6 +1835,8 @@ void P_StepFrame(P_Frame *frame) // TODO: Not like this + + if (!is_predicting) { for (P_Ent *ent0 = P_FirstEnt(frame); !P_IsEntNil(ent0); ent0 = P_NextEnt(ent0)) @@ -1734,31 +1854,15 @@ void P_StepFrame(P_Frame *frame) P_CollisionResult collision = P_CollisionResultFromShapes(shape0, shape1); if (collision.collision_points_count > 0) { - // FIXME: Key lookup - P_Constraint *constraint = 0; + P_ConstraintKey constraint_key = P_ConstraintKeyFromU64s(ent0->key.v, ent1->key.v); + P_Constraint *constraint = P_ConstraintFromKey(frame, constraint_key); + if (constraint->last_touched_tick < frame->tick) { - b32 match = 0; - for (i64 constraint_idx = 0; constraint_idx < frame->constraints_count; ++constraint_idx) + if (P_IsConstraintNil(constraint)) { - constraint = &frame->constraints[constraint_idx]; - if (P_MatchKey(constraint->ent0, ent0->key) && P_MatchKey(constraint->ent1, ent1->key)) - { - match = 1; - break; - } + constraint = P_PushConstraint(frame, constraint_key); } - if (!match) - { - if (frame->constraints_count < frame->constraints_cap) - { - constraint = &frame->constraints[frame->constraints_count]; - frame->constraints_count += 1; - ZeroStruct(constraint); - } - } - } - if (constraint) - { + constraint->flags = P_ConstraintFlag_Gentle | P_ConstraintFlag_NoWarmStart; constraint->last_touched_tick = frame->tick; constraint->normal = collision.collision_normal; @@ -1881,6 +1985,161 @@ void P_StepFrame(P_Frame *frame) + + + ////////////////////////////// + //- Generate solid constraints + + + + // // TODO: Not like this + + // for (P_Ent *ent0 = P_FirstEnt(frame); !P_IsEntNil(ent0); ent0 = P_NextEnt(ent0)) + // { + // if (ent0->is_guy) + // { + // P_Shape shape0 = P_WorldShapeFromEnt(ent0); + // Rng2 aabb = P_BoundingBoxFromShape(shape0); + + // P_Query collisions = P_QueryFromRect(scratch.arena, aabb, P_QueryFlag_IncludeWalls); + + // for (P_QueryItem *item = collisions.first; item; item = item->next) + // { + // P_Shape shape1 = item->shape; + + // // TODO: World query + // P_CollisionResult collision = P_CollisionResultFromShapes(shape0, shape1); + // if (collision.collision_points_count > 0) + // { + // P_Constraint *constraint = P_ConstraintFromKey(world_frame, constraint_key); + // if (P_IsConstraintNil(constraint)) + // { + // constraint = P_PushConstraint(world_frame, constraint_key); + // } + + // if (constraint->last_touched_tick < frame->tick) + // { + // constraint->last_touched_Tick = frame->tick; + // constraint->flags = P_ConstraintFlag_Solid; + // constraint->last_touched_tick = frame->tick; + // constraint->normal = collision.collision_normal; + // // constraint->friction = SqrtF32(ent0->friction * ent1->friction); + // constraint->friction = 0; + + // // TODO: Real masses + // f32 inv_m0 = 10; + // f32 inv_m1 = 10; + // f32 inv_i0 = 0; + // f32 inv_i1 = 0; + + // // Treat non-predicted guys as infinite-mass + // if (is_predicting && ent0 != local_guy) + // { + // inv_m0 = 0; + // } + // if (is_predicting && ent1 != local_guy) + // { + // inv_m1 = 0; + // } + + + // constraint->ent0 = ent0->key; + // constraint->ent1 = ent1->key; + // // constraint->static_center1 = shape1.center_of_mass; + + // constraint->inv_m0 = inv_m0; + // constraint->inv_m1 = inv_m1; + // constraint->inv_i0 = inv_i0; + // constraint->inv_i1 = inv_i1; + + // // Delete old contacts that are no longer present + // for (i32 contact_point_idx = 0; contact_point_idx < constraint->points_count; ++contact_point_idx) + // { + // P_ContactPoint *contact = &constraint->points[contact_point_idx]; + // u32 id = contact->id; + // b32 match = 0; + // for (i32 collision_point_idx = 0; collision_point_idx < collision.collision_points_count; ++collision_point_idx) + // { + // if (collision.collision_points[collision_point_idx].id == id) + // { + // match = 1; + // break; + // } + // } + // if (!match) + // { + // // Delete contact by replacing with last in array + // *contact = constraint->points[constraint->points_count - 1]; + // constraint->points_count -= 1; + // contact_point_idx -= 1; + // } + // } + + // // Create / update contacts from collision + // for (i32 collision_point_idx = 0; collision_point_idx < collision.collision_points_count; ++collision_point_idx) + // { + // P_CollisionPoint collision_point = collision.collision_points[collision_point_idx]; + + // u32 id = collision_point.id; + // P_ContactPoint *contact = 0; + // { + // for (i32 contact_point_idx = 0; contact_point_idx < constraint->points_count; ++contact_point_idx) + // { + // P_ContactPoint *tmp = &constraint->points[contact_point_idx]; + // if (tmp->id == id) + // { + // contact = tmp; + // break; + // } + // } + // if (!contact) + // { + // contact = &constraint->points[constraint->points_count]; + // constraint->points_count += 1; + // ZeroStruct(contact); + // } + // } + // contact->id = id; + + // Vec2 vcp0 = SubVec2(collision_point.p, shape0.center_of_mass); + // Vec2 vcp1 = SubVec2(collision_point.p, shape1.center_of_mass); + + // contact->vcp0 = vcp0; + // contact->vcp1 = vcp1; + // contact->starting_separation = collision_point.separation; + + // // Debug draw + // // { + // // // P_Ent *ent0 = P_EntFromKey(frame, constraint->ent0); + // // // P_Ent *ent1 = P_EntFromKey(frame, constraint->ent1); + // // Vec2 normal = constraint->normal; + // // Vec2 center0 = Zi; + // // Vec2 center1 = Zi; + // // if (!P_IsEntNil(ent0)) center0 = P_WorldShapeFromEnt(ent0).center_of_mass; + // // if (!P_IsEntNil(ent1)) center1 = P_WorldShapeFromEnt(ent1).center_of_mass; + // // Vec2 p0 = AddVec2(center0, vcp0); + // // Vec2 p1 = AddVec2(center1, vcp1); + // // P_DebugDrawPoint(p0, Color_Cyan); + // // P_DebugDrawLine(p0, AddVec2(p0, normal), Color_White); + // // } + // } + // } + // } + // } + // } + // } + + + + + + + + + + + + // ////////////////////////////// // //- Generate solid constraints @@ -1893,6 +2152,7 @@ void P_StepFrame(P_Frame *frame) // if (ent0->is_guy) // { // P_Shape shape0 = P_WorldShapeFromEnt(ent0); + // for (P_Ent *ent1 = P_FirstEnt(frame); !P_IsEntNil(ent1); ent1 = P_NextEnt(ent1)) // { // if (ent1->is_guy && ent1->key.v > ent0->key.v) @@ -1910,7 +2170,7 @@ void P_StepFrame(P_Frame *frame) // for (i64 constraint_idx = 0; constraint_idx < frame->constraints_count; ++constraint_idx) // { // constraint = &frame->constraints[constraint_idx]; - // if (P_MatchKey(constraint->ent0, ent0->key) && P_MatchKey(constraint->ent1, ent1->key)) + // if (P_MatchEntKey(constraint->ent0, ent0->key) && P_MatchEntKey(constraint->ent1, ent1->key)) // { // match = 1; // break; @@ -2045,10 +2305,10 @@ void P_StepFrame(P_Frame *frame) //- Prune constraints { - i64 constraint_idx = 0; - while (constraint_idx < frame->constraints_count) + i64 prune_constraints_count = 0; + P_Constraint **prune_constraints = PushStructsNoZero(scratch.arena, P_Constraint *, frame->constraints_count); + for (P_Constraint *constraint = P_FirstConstraint(frame); !P_IsConstraintNil(constraint); constraint = P_NextConstraint(constraint)) { - P_Constraint *constraint = &frame->constraints[constraint_idx]; b32 prune = 1; if (constraint->last_touched_tick == frame->tick) { @@ -2061,17 +2321,20 @@ void P_StepFrame(P_Frame *frame) } if (prune) { - // Prune by replacing with last constraint - // TODO: Investigate whether the reordering here can degrade stability - P_Constraint *last_constraint = &frame->constraints[frame->constraints_count - 1]; - *constraint = *last_constraint; - frame->constraints_count -= 1; - } - else - { - constraint_idx += 1; + prune_constraints[prune_constraints_count] = constraint; + prune_constraints_count += 1; } } + + for (i64 prune_idx = 0; prune_idx < prune_constraints_count; ++prune_idx) + { + P_Constraint *constraint = prune_constraints[prune_idx]; + P_ConstraintBin *bin = &frame->constraint_bins[constraint->key.v % frame->constraint_bins_count]; + DllQueueRemoveNP(bin->first, bin->last, constraint, next_in_bin, prev_in_bin); + DllQueueRemoveNPZ(&P_NilConstraint, frame->first_constraint, frame->last_constraint, constraint, next, prev); + frame->constraints_count -= 1; + SllStackPush(world->first_free_constraint, constraint); + } } ////////////////////////////// @@ -2082,9 +2345,8 @@ void P_StepFrame(P_Frame *frame) ////////////////////////////// //- Prepare constraints - for (i64 constraint_idx = 0; constraint_idx < frame->constraints_count; ++constraint_idx) + for (P_Constraint *constraint = P_FirstConstraint(frame); !P_IsConstraintNil(constraint); constraint = P_NextConstraint(constraint)) { - P_Constraint *constraint = &frame->constraints[constraint_idx]; Vec2 normal = constraint->normal; Vec2 tangent = PerpVec2(normal); f32 inv_m0 = constraint->inv_m0; @@ -2119,9 +2381,8 @@ void P_StepFrame(P_Frame *frame) ////////////////////////////// //- Warm start constraints - for (i64 constraint_idx = 0; constraint_idx < frame->constraints_count; ++constraint_idx) + for (P_Constraint *constraint = P_FirstConstraint(frame); !P_IsConstraintNil(constraint); constraint = P_NextConstraint(constraint)) { - P_Constraint *constraint = &frame->constraints[constraint_idx]; if (!(constraint->flags & P_ConstraintFlag_NoWarmStart)) { P_Ent *ent0 = P_EntFromKey(frame, constraint->ent0); @@ -2166,10 +2427,8 @@ void P_StepFrame(P_Frame *frame) ////////////////////////////// //- Solve constraints - for (i64 constraint_idx = 0; constraint_idx < frame->constraints_count; ++constraint_idx) + for (P_Constraint *constraint = P_FirstConstraint(frame); !P_IsConstraintNil(constraint); constraint = P_NextConstraint(constraint)) { - P_Constraint *constraint = &frame->constraints[constraint_idx]; - P_Ent *ent0 = P_EntFromKey(frame, constraint->ent0); P_Ent *ent1 = P_EntFromKey(frame, constraint->ent1); @@ -2455,7 +2714,7 @@ void P_StepFrame(P_Frame *frame) { P_Ent *bullet = P_PushTempEnt(scratch.arena, &bullets_to_spawn); bullet->is_bullet = 1; - bullet->key = P_RandKey(); + bullet->key = P_RandEntKey(); f32 rand_speed = Norm24(P_RandU64FromEnt(firer)) - 0.5; f32 rand_angle = Norm24(P_RandU64FromEnt(firer)) - 0.5; @@ -2503,7 +2762,7 @@ void P_StepFrame(P_Frame *frame) f32 closest_len_sq = Inf; for (P_Ent *potential_victim = P_FirstEnt(frame); !P_IsEntNil(potential_victim); potential_victim = P_NextEnt(potential_victim)) { - if (potential_victim->is_guy && !P_MatchKey(potential_victim->key, bullet->bullet_firer)) + if (potential_victim->is_guy && !P_MatchEntKey(potential_victim->key, bullet->bullet_firer)) { P_Shape victim_world_shape = P_WorldShapeFromEnt(potential_victim); diff --git a/src/pp/pp.h b/src/pp/pp.h index c7d6376f..3b066d36 100644 --- a/src/pp/pp.h +++ b/src/pp/pp.h @@ -3,9 +3,15 @@ //////////////////////////////////////////////////////////// //~ Key types -#define P_NilKey ((P_Key) { 0 }) +#define P_NilEntKey ((P_EntKey) { 0 }) +#define P_NilConstraintKey ((P_EntKey) { 0 }) -Struct(P_Key) +Struct(P_EntKey) +{ + u64 v; +}; + +Struct(P_ConstraintKey) { u64 v; }; @@ -99,7 +105,7 @@ Struct(P_Ent) //- Persistent data - P_Key key; + P_EntKey key; u64 rand_seq; i64 created_at_ns; @@ -117,7 +123,7 @@ Struct(P_Ent) i64 last_fire_ns; b32 has_weapon; - P_Key bullet_firer; + P_EntKey bullet_firer; b32 is_bullet; Vec2 bullet_start; Vec2 bullet_end; @@ -132,10 +138,10 @@ Struct(P_Ent) //- Player - P_Key spawn; + P_EntKey spawn; b32 is_player; - P_Key guy; + P_EntKey guy; f32 ping; u8 string_len; @@ -207,11 +213,18 @@ Struct(P_ContactPoint) Struct(P_Constraint) { + P_Constraint *next; + P_Constraint *prev; + + P_Constraint *next_in_bin; + P_Constraint *prev_in_bin; + + P_ConstraintKey key; P_ConstraintFlag flags; i64 last_touched_tick; - P_Key ent0; - P_Key ent1; + P_EntKey ent0; + P_EntKey ent1; Vec2 static_center0; Vec2 static_center1; @@ -228,6 +241,12 @@ Struct(P_Constraint) P_ContactPoint points[2]; }; +Struct(P_ConstraintBin) +{ + P_Constraint *first; + P_Constraint *last; +}; + //////////////////////////////////////////////////////////// //~ World types @@ -250,6 +269,9 @@ Struct(P_Frame) i64 tick; i64 time_ns; + ////////////////////////////// + //- Ents + i64 ents_count; P_Ent *first_ent; P_Ent *last_ent; @@ -257,9 +279,15 @@ Struct(P_Frame) i64 ent_bins_count; P_EntBin *ent_bins; - i64 constraints_cap; + ////////////////////////////// + //- Constraints + i64 constraints_count; - P_Constraint *constraints; + P_Constraint *first_constraint; + P_Constraint *last_constraint; + + i64 constraint_bins_count; + P_ConstraintBin *constraint_bins; ////////////////////////////// //- Snapshot-assembly state @@ -285,6 +313,7 @@ Struct(P_World) RandState rand; P_Ent *first_free_ent; + P_Constraint *first_free_constraint; P_Frame *first_frame; P_Frame *last_frame; @@ -328,7 +357,7 @@ Struct(P_Msg) b32 affect_bots; P_PrefabKind prefab; - P_Key key; + P_EntKey key; Xform xf; P_TileKind tile_kind; @@ -408,6 +437,14 @@ Struct(P_RaycastResult) //////////////////////////////////////////////////////////// //~ State types +Struct(P_SimStatistics) +{ + NET_PipeStatistics pipe; + i64 tick; + i64 ents_count; + i64 constraints_count; +}; + Struct(P_Ctx) { // Sim -> Vis state @@ -420,14 +457,14 @@ Struct(P_Ctx) P_DebugDrawNode *first_debug_draw_node; P_DebugDrawNode *last_debug_draw_node; - NET_PipeStatistics pipe_stats; + P_SimStatistics stats; } s2v; }; Struct(P_ThreadLocalCtx) { b32 is_predicting; - P_Key local_player; + P_EntKey local_player; //- Per-thread debug info Arena *debug_arena; @@ -456,19 +493,28 @@ void P_Bootstrap(void); //////////////////////////////////////////////////////////// //~ Nil helpers -b32 P_IsKeyNil(P_Key key); +b32 P_IsEntKeyNil(P_EntKey key); +b32 P_IsConstraintKeyNil(P_ConstraintKey key); + b32 P_IsEntNil(P_Ent *ent); +b32 P_IsConstraintNil(P_Constraint *constraint); b32 P_IsFrameNil(P_Frame *frame); //////////////////////////////////////////////////////////// //~ Key helpers -b32 P_MatchKey(P_Key a, P_Key b); -P_Key P_RandKey(void); -u64 P_RandU64FromEnt(P_Ent *ent); +b32 P_MatchEntKey(P_EntKey a, P_EntKey b); +b32 P_MatchConstraintKey(P_ConstraintKey a, P_ConstraintKey b); +P_ConstraintKey P_ConstraintKeyFromU64s(u64 a, u64 b); #define P_FmtKey(key) FmtHandle((key).v) +//////////////////////////////////////////////////////////// +//~ Rand helpers + +P_EntKey P_RandEntKey(void); +u64 P_RandU64FromEnt(P_Ent *ent); + //////////////////////////////////////////////////////////// //~ String helpers @@ -519,7 +565,8 @@ Vec2 P_EdgePointFromShape(P_Shape shape, Vec2 dir); //////////////////////////////////////////////////////////// //~ Lookup helpers -P_Ent *P_EntFromKey(P_Frame *frame, P_Key key); +P_Ent *P_EntFromKey(P_Frame *frame, P_EntKey key); +P_Constraint *P_ConstraintFromKey(P_Frame *frame, P_ConstraintKey key); //////////////////////////////////////////////////////////// //~ Iteration helpers @@ -527,6 +574,9 @@ P_Ent *P_EntFromKey(P_Frame *frame, P_Key key); P_Ent *P_FirstEnt(P_Frame *frame); P_Ent *P_NextEnt(P_Ent *e); +P_Constraint *P_FirstConstraint(P_Frame *frame); +P_Constraint *P_NextConstraint(P_Constraint *c); + //////////////////////////////////////////////////////////// //~ List helpers @@ -551,6 +601,7 @@ P_Msg *P_PushMsg(P_MsgKind kind, String data); P_World *P_AcquireWorld(void); void P_SpawnEntsFromList(P_Frame *frame, P_EntList ents); +P_Constraint *P_PushConstraint(P_Frame *frame, P_ConstraintKey key); P_Frame *P_FrameFromTick(P_World *world, i64 tick); void P_ClearFrames(P_World *world, i64 tick_min, i64 tick_max); diff --git a/src/pp/pp_sim/pp_sim_core.c b/src/pp/pp_sim/pp_sim_core.c index c789defe..7f05af57 100644 --- a/src/pp/pp_sim/pp_sim_core.c +++ b/src/pp/pp_sim/pp_sim_core.c @@ -272,9 +272,9 @@ 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)) + if (P_IsEntKeyNil(client->player)) { - client->player = P_RandKey(); + client->player = P_RandEntKey(); } P_Ent *player = P_EntFromKey(world_frame, client->player); if (P_IsEntNil(player)) @@ -587,7 +587,7 @@ void S_TickForever(WaveLaneCtx *lane) if (!guy->is_guy) { guy = P_PushTempEnt(frame_arena, &ents); - guy->key = P_RandKey(); + guy->key = P_RandEntKey(); guy->is_guy = 1; guy->has_weapon = 1; bot->guy = guy->key; @@ -928,7 +928,10 @@ void S_TickForever(WaveLaneCtx *lane) } ResetArena(P.s2v.arena); P.s2v.gen += 1; - P.s2v.pipe_stats = NET_StatsFromPipe(net_pipe); + P.s2v.stats.pipe = NET_StatsFromPipe(net_pipe); + P.s2v.stats.tick = world_frame->tick; + P.s2v.stats.ents_count = world_frame->ents_count; + P.s2v.stats.constraints_count = world_frame->constraints_count; { i64 dst_idx = 0; diff --git a/src/pp/pp_sim/pp_sim_core.h b/src/pp/pp_sim/pp_sim_core.h index e38c4582..6c554b2f 100644 --- a/src/pp/pp_sim/pp_sim_core.h +++ b/src/pp/pp_sim/pp_sim_core.h @@ -12,7 +12,7 @@ Struct(S_Client) NET_Key net_key; P_MsgList out_msgs; - P_Key player; + P_EntKey player; i32 name_len; u8 name_text[P_MaxPlayerNameLen]; diff --git a/src/pp/pp_transcode.c b/src/pp/pp_transcode.c index 299aedbb..83a6f8ab 100644 --- a/src/pp/pp_transcode.c +++ b/src/pp/pp_transcode.c @@ -135,7 +135,7 @@ P_UnpackedWorld P_UnpackWorld(Arena *arena, String packed) for (CR_Item *ent_item = top_item->first; ent_item; ent_item = ent_item->next) { P_Ent *ent = P_PushTempEnt(arena, &result.ents); - ent->key = (P_Key) { .v = CR_IntFromString(ent_item->name) }; + ent->key = (P_EntKey) { .v = CR_IntFromString(ent_item->name) }; for (CR_Item *attr = ent_item->first; attr; attr = attr->next) { if (MatchString(attr->name, Lit("props"))) diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index d774b4be..3b7e2dcd 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -357,7 +357,7 @@ void V_TickForever(WaveLaneCtx *lane) Arena *sim_debug_arena = AcquireArena(Gibi(64)); P_DebugDrawNode *first_sim_debug_draw_node = 0; P_DebugDrawNode *last_sim_debug_draw_node = 0; - NET_PipeStatistics sim_pipe_stats = Zi; + P_SimStatistics sim_stats = Zi; P_World *sim_world = P_AcquireWorld(); P_World *predict_world = P_AcquireWorld(); @@ -576,7 +576,7 @@ void V_TickForever(WaveLaneCtx *lane) frame->dt = SecondsFromNs(frame->dt_ns); frame->rand = prev_frame->rand; - if (P_IsKeyNil(V.player_key)) + if (P_IsEntKeyNil(V.player_key)) { TrueRand(StringFromStruct(&V.player_key)); } @@ -714,7 +714,7 @@ void V_TickForever(WaveLaneCtx *lane) if (P.s2v.gen > s2v_gen) { s2v_gen = P.s2v.gen; - sim_pipe_stats = P.s2v.pipe_stats; + sim_stats = P.s2v.stats; { ResetArena(sim_debug_arena); @@ -1204,7 +1204,7 @@ void V_TickForever(WaveLaneCtx *lane) i64 dst_tick = BB_ReadIV(&bbr); i64 time_ns = BB_ReadIV(&bbr); u64 world_seed = BB_ReadUBits(&bbr, 64); - P_Key player_key = { .v = BB_ReadUBits(&bbr, 64) }; + P_EntKey player_key = { .v = BB_ReadUBits(&bbr, 64) }; i64 tmp_remote_ack = BB_ReadIV(&bbr); i64 tmp_ack_mirror = BB_ReadIV(&bbr); @@ -1881,7 +1881,7 @@ void V_TickForever(WaveLaneCtx *lane) SPR_Sprite wep = SPR_SpriteFromSheet(anim.wep_sheet, anim.span, anim.frame_seq); Vec2 look = ent->control.look; - if (P_MatchKey(ent->key, local_guy->key)) + if (P_MatchEntKey(ent->key, local_guy->key)) { // Late latch local player aim direction for lower-latency look = frame->look; @@ -2046,7 +2046,7 @@ void V_TickForever(WaveLaneCtx *lane) { P_Msg *msg = P_PushMsg(P_MsgKind_Prefab, Zstr); msg->prefab = frame->equipped_prefab; - msg->key = P_RandKey(); + msg->key = P_RandEntKey(); msg->xf.t = frame->world_cursor; } } @@ -4074,17 +4074,24 @@ void V_TickForever(WaveLaneCtx *lane) { UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); { + UI_BuildLabelF("Local server world tick: %F", FmtSint(sim_stats.tick)); + UI_BuildLabelF("Local server world entities: %F", FmtSint(sim_stats.ents_count)); + UI_BuildLabelF("Local server world constraints: %F", FmtSint(sim_stats.constraints_count)); + UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); UI_BuildLabelF("Sim world seed: 0x%F", FmtHex(sim_world->seed)); UI_BuildLabelF("Sim world tick: %F", FmtSint(sim_world->last_frame->tick)); UI_BuildLabelF("Sim world entities: %F", FmtSint(sim_world->last_frame->ents_count)); + UI_BuildLabelF("Sim world constraints: %F", FmtSint(sim_world->last_frame->constraints_count)); UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); UI_BuildLabelF("Predicted world seed: 0x%F", FmtHex(predict_world->seed)); 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 constraints: %F", FmtSint(predict_world->last_frame->constraints_count)); UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); 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_BuildLabelF("Local world constraints: %F", FmtSint(local_world->last_frame->constraints_count)); } UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); { @@ -4113,8 +4120,8 @@ void V_TickForever(WaveLaneCtx *lane) UI_BuildLabelF("RTT: %Fms", FmtFloat(smoothed_rtt * 1000, .p = 3)); UI_BuildLabelF("Client send: %F MiB", FmtFloat(CeilF64((f64)vis_pipe_stats.total_bytes_sent / 1024) / 1024, .p = 3)); UI_BuildLabelF("Client recv: %F MiB", FmtFloat(CeilF64((f64)vis_pipe_stats.total_bytes_received / 1024) / 1024, .p = 3)); - UI_BuildLabelF("Server send: %F MiB", FmtFloat(CeilF64((f64)sim_pipe_stats.total_bytes_sent / 1024) / 1024, .p = 3)); - UI_BuildLabelF("Server recv: %F MiB", FmtFloat(CeilF64((f64)sim_pipe_stats.total_bytes_received / 1024) / 1024, .p = 3)); + UI_BuildLabelF("Server send: %F MiB", FmtFloat(CeilF64((f64)sim_stats.pipe.total_bytes_sent / 1024) / 1024, .p = 3)); + UI_BuildLabelF("Server recv: %F MiB", FmtFloat(CeilF64((f64)sim_stats.pipe.total_bytes_received / 1024) / 1024, .p = 3)); } UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); { @@ -4533,7 +4540,7 @@ void V_TickForever(WaveLaneCtx *lane) { P_Msg *msg = P_PushMsg(P_MsgKind_Prefab, Zstr); msg->prefab = P_PrefabKind_Bot; - msg->key = P_RandKey(); + msg->key = P_RandEntKey(); msg->xf.t = frame->world_cursor; if (kind == V_CmdKind_spawn_tp_bot) { diff --git a/src/pp/pp_vis/pp_vis_core.h b/src/pp/pp_vis/pp_vis_core.h index bf79157c..42ad95b4 100644 --- a/src/pp/pp_vis/pp_vis_core.h +++ b/src/pp/pp_vis/pp_vis_core.h @@ -251,7 +251,7 @@ Struct(V_Frame) Struct(V_Ctx) { - P_Key player_key; + P_EntKey player_key; i64 panels_count; i64 windows_count;