constraints lookup
This commit is contained in:
parent
003bdb2d55
commit
b7f2c3d185
397
src/pp/pp.c
397
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);
|
||||
|
||||
|
||||
87
src/pp/pp.h
87
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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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")))
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -251,7 +251,7 @@ Struct(V_Frame)
|
||||
|
||||
Struct(V_Ctx)
|
||||
{
|
||||
P_Key player_key;
|
||||
P_EntKey player_key;
|
||||
|
||||
i64 panels_count;
|
||||
i64 windows_count;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user