pp refactor progress

This commit is contained in:
jacob 2025-08-05 15:47:23 -05:00
parent 568cb0c1ab
commit a66b95fb7b
4 changed files with 159 additions and 182 deletions

View File

@ -1,9 +0,0 @@
{
"files.associations": {
"*.rst": "hlsl",
"*.knl": "hlsl",
"chrono": "c",
"system_error": "c",
"xlocale": "c"
}
}

View File

@ -2147,7 +2147,7 @@ JobDef(SimJob, UNUSED sig, UNUSED id)
BB_Buff msg_writer_bb = AcquireBitbuff(Gibi(64));
BB_Buff snapshot_writer_bb = AcquireBitbuff(Gibi(64));
SimAccel accel = sim_accel_acquire();
SimAccel accel = AcquireSimAccel();
ClientStore *store = sim_client_store_acquire();
Client *user_input_client = sim_client_acquire(store); /* Stores snapshots containing commands to be published to local client */
@ -2496,7 +2496,7 @@ JobDef(SimJob, UNUSED sig, UNUSED id)
Snapshot *prev_world = sim_snapshot_from_tick(local_client, prev_tick);
ctx.world = sim_snapshot_acquire(local_client, prev_world, next_tick);
GenerateuserInputCmds(user_input_client, next_tick);
sim_step(&ctx);
StepSim(&ctx);
}
else if (master_client->valid)
{
@ -2723,7 +2723,7 @@ JobDef(SimJob, UNUSED sig, UNUSED id)
{
sim_snapshot_sync_ents(ctx.world, master_ss, master_player->id, SIM_SYNC_FLAG_NOSYNC_PREDICTABLES);
}
sim_step(&ctx);
StepSim(&ctx);
prev_ss = ctx.world;
++step_tick;
}
@ -2811,7 +2811,7 @@ JobDef(SimJob, UNUSED sig, UNUSED id)
}
sim_client_store_release(store);
sim_accel_release(&accel);
ReleaseSimAccel(&accel);
ReleaseBitbuff(&snapshot_writer_bb);
ReleaseBitbuff(&msg_writer_bb);
N_ReleaseHost(host);

View File

@ -1,20 +1,19 @@
/* ========================== *
* Sim accel
* ========================== */
////////////////////////////////
//~ Sim accel
SimAccel sim_accel_acquire(void)
SimAccel AcquireSimAccel(void)
{
SimAccel accel = ZI;
accel.space = space_acquire(SPACE_CELL_SIZE, SPACE_CELL_BINS_SQRT);
return accel;
}
void sim_accel_release(SimAccel *accel)
void ReleaseSimAccel(SimAccel *accel)
{
space_release(accel->space);
}
void sim_accel_reset(Snapshot *ss, SimAccel *accel)
void ResetSimAccel(Snapshot *ss, SimAccel *accel)
{
space_reset(accel->space);
@ -27,13 +26,12 @@ void sim_accel_reset(Snapshot *ss, SimAccel *accel)
}
}
/* ========================== *
* Test
* ========================== */
////////////////////////////////
//~ Spawn test operations
/* TODO: Remove this */
internal Entity *test_spawn_smg(Entity *parent)
Entity *SpawnTestSmg(Entity *parent)
{
Entity *e = sim_ent_acquire_sync_src(parent);
e->sprite = S_TagFromPath(Lit("sprite/gun.ase"));
@ -49,7 +47,7 @@ internal Entity *test_spawn_smg(Entity *parent)
return e;
}
internal Entity *test_spawn_launcher(Entity *parent)
Entity *SpawnTestLauncher(Entity *parent)
{
Entity *e = sim_ent_acquire_sync_src(parent);
e->sprite = S_TagFromPath(Lit("sprite/gun.ase"));
@ -65,7 +63,7 @@ internal Entity *test_spawn_launcher(Entity *parent)
return e;
}
internal Entity *test_spawn_chucker(Entity *parent)
Entity *SpawnTestChucker(Entity *parent)
{
Entity *chucker = sim_ent_acquire_sync_src(parent);
chucker->sprite = S_TagFromPath(Lit("sprite/gun.ase"));
@ -100,7 +98,7 @@ internal Entity *test_spawn_chucker(Entity *parent)
return chucker;
}
internal Entity *test_spawn_employee(Entity *parent)
Entity *SpawnTestEmployee(Entity *parent)
{
/* Player */
Entity *employee = sim_ent_nil();
@ -170,11 +168,11 @@ internal Entity *test_spawn_employee(Entity *parent)
/* Player weapon */
if (employee->valid) {
LAX test_spawn_smg;
LAX test_spawn_launcher;
LAX test_spawn_chucker;
LAX SpawnTestSmg;
LAX SpawnTestLauncher;
LAX SpawnTestChucker;
Entity *e = test_spawn_chucker(employee);
Entity *e = SpawnTestChucker(employee);
employee->equipped = e->id;
sim_ent_enable_prop(e, SEPROP_LIGHT_TEST);
@ -184,7 +182,7 @@ internal Entity *test_spawn_employee(Entity *parent)
return employee;
}
internal Entity *test_spawn_camera(Entity *parent, Entity *follow)
Entity *SpawnTestCamera(Entity *parent, Entity *follow)
{
Entity *camera_ent = sim_ent_nil();
if (follow->valid) {
@ -203,7 +201,7 @@ internal Entity *test_spawn_camera(Entity *parent, Entity *follow)
return camera_ent;
}
internal Entity *test_spawn_explosion(Entity *parent, Vec2 pos, f32 strength, f32 radius)
Entity *SpawnTestExplosion(Entity *parent, Vec2 pos, f32 strength, f32 radius)
{
Entity *ent = sim_ent_acquire_sync_src(parent);
sim_ent_set_xform(ent, XformFromPos(pos));
@ -219,7 +217,7 @@ internal Entity *test_spawn_explosion(Entity *parent, Vec2 pos, f32 strength, f3
return ent;
}
internal void test_teleport(Entity *ent, Vec2 pos)
void TeleportTest(Entity *ent, Vec2 pos)
{
//++ent->continuity_gen;
Xform xf = sim_ent_get_xform(ent);
@ -227,20 +225,20 @@ internal void test_teleport(Entity *ent, Vec2 pos)
sim_ent_set_xform(ent, xf);
}
internal void test_spawn_entities1(Entity *parent, Vec2 pos)
void SpawnTestEntities1(Entity *parent, Vec2 pos)
{
LAX pos;
/* Enemy */
{
Entity *e = test_spawn_employee(parent);
Entity *e = SpawnTestEmployee(parent);
Xform xf = sim_ent_get_xform(e);
xf.og = pos;
sim_ent_set_xform(e, xf);
}
}
internal void test_spawn_entities2(Entity *parent, Vec2 pos)
void SpawnTestEntities2(Entity *parent, Vec2 pos)
{
LAX pos;
@ -311,7 +309,7 @@ internal void test_spawn_entities2(Entity *parent, Vec2 pos)
#endif
}
internal void test_spawn_entities3(Entity *parent, Vec2 pos)
void SpawnTestEntities3(Entity *parent, Vec2 pos)
{
LAX pos;
@ -336,7 +334,7 @@ internal void test_spawn_entities3(Entity *parent, Vec2 pos)
}
}
internal void test_spawn_entities4(Entity *parent, Vec2 pos)
void SpawnTestEntities4(Entity *parent, Vec2 pos)
{
LAX pos;
@ -358,7 +356,7 @@ internal void test_spawn_entities4(Entity *parent, Vec2 pos)
e->sprite_tint = Rgb32F(1, 1, 1);
}
internal void test_spawn_tile(Snapshot *world, Vec2 world_pos)
void SpawnTestTile(Snapshot *world, Vec2 world_pos)
{
#if 0
Entity *e = sim_ent_acquire_sync_src(parent);
@ -397,15 +395,21 @@ internal void test_spawn_tile(Snapshot *world, Vec2 world_pos)
#endif
}
void ClearLevelTest(SimStepCtx *ctx)
{
Snapshot *world = ctx->world;
for (u64 j = 0; j < world->num_ents_reserved; ++j) {
Entity *ent = &world->ents[j];
if (ent->valid) {
sim_ent_enable_prop(ent, SEPROP_RELEASE);
}
}
}
////////////////////////////////
//~ Tile test operations
internal MergesortCompareFuncDef(tile_chunk_sort_x, arg_a, arg_b, _)
MergesortCompareFuncDef(SortTileXCmp, arg_a, arg_b, _)
{
Entity *a = *(Entity **)arg_a;
Entity *b = *(Entity **)arg_b;
@ -417,7 +421,7 @@ internal MergesortCompareFuncDef(tile_chunk_sort_x, arg_a, arg_b, _)
return result;
}
internal MergesortCompareFuncDef(tile_chunk_sort_y, arg_a, arg_b, _)
MergesortCompareFuncDef(SortTileYCmp, arg_a, arg_b, _)
{
Entity *a = *(Entity **)arg_a;
Entity *b = *(Entity **)arg_b;
@ -429,7 +433,7 @@ internal MergesortCompareFuncDef(tile_chunk_sort_y, arg_a, arg_b, _)
return result;
}
internal void test_generate_walls(Snapshot *world)
void GenerateTestWalls(Snapshot *world)
{
__prof;
TempArena scratch = BeginScratchNoConflict();
@ -460,8 +464,8 @@ internal void test_generate_walls(Snapshot *world)
/* NOTE: We sort x & y separately because it's possible that a wall
* should merge with another wall that was generated from a diagonal chunk. */
Mergesort(x_sorted_tile_chunks, sorted_tile_chunks_count, sizeof(*x_sorted_tile_chunks), tile_chunk_sort_x, 0);
Mergesort(y_sorted_tile_chunks, sorted_tile_chunks_count, sizeof(*y_sorted_tile_chunks), tile_chunk_sort_y, 0);
Mergesort(x_sorted_tile_chunks, sorted_tile_chunks_count, sizeof(*x_sorted_tile_chunks), SortTileXCmp, 0);
Mergesort(y_sorted_tile_chunks, sorted_tile_chunks_count, sizeof(*y_sorted_tile_chunks), SortTileYCmp, 0);
}
struct wall_node {
@ -682,33 +686,10 @@ internal void test_generate_walls(Snapshot *world)
EndScratch(scratch);
}
////////////////////////////////
//~ On collision
internal void test_clear_level(SimStepCtx *ctx)
{
Snapshot *world = ctx->world;
for (u64 j = 0; j < world->num_ents_reserved; ++j) {
Entity *ent = &world->ents[j];
if (ent->valid) {
sim_ent_enable_prop(ent, SEPROP_RELEASE);
}
}
}
/* ========================== *
* Respond to physics collisions
* ========================== */
internal PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, data, step_ctx)
PHYS_COLLISION_CALLBACK_FUNC_DEF(OnEntityCollision, data, step_ctx)
{
Snapshot *world = step_ctx->world;
Entity *e0 = sim_ent_from_id(world, data->e0);
@ -770,7 +751,7 @@ internal PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, data, step_ctx)
/* Create explosion */
if (bullet->bullet_explosion_strength > 0) {
test_spawn_explosion(root, point, bullet->bullet_explosion_strength, bullet->bullet_explosion_radius);
SpawnTestExplosion(root, point, bullet->bullet_explosion_strength, bullet->bullet_explosion_radius);
}
/* Update bullet */
@ -830,11 +811,10 @@ internal PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, data, step_ctx)
return skip_solve;
}
/* ========================== *
* Update
* ========================== */
////////////////////////////////
//~ Step
void sim_step(SimStepCtx *ctx)
void StepSim(SimStepCtx *ctx)
{
__prof;
TempArena scratch = BeginScratchNoConflict();
@ -850,9 +830,7 @@ void sim_step(SimStepCtx *ctx)
i64 sim_dt_ns = ctx->sim_dt_ns;
/* ========================== *
* Begin frame
* ========================== */
//- Begin frame
world->sim_dt_ns = MaxI64(0, sim_dt_ns);
world->sim_time_ns += world->sim_dt_ns;
@ -863,9 +841,7 @@ void sim_step(SimStepCtx *ctx)
Entity *root = sim_ent_from_id(world, SIM_ENT_ROOT_ID);
root->owner = world->client->player_id;
/* ========================== *
* Sync ents from cmd producing clients
* ========================== */
//- Sync ents from cmd producing clients
{
/* FIXME: Ensure only cmds are synced to master player */
@ -940,16 +916,14 @@ void sim_step(SimStepCtx *ctx)
}
}
/* ========================== *
* Release entities at beginning of frame
* ========================== */
//- Release entities at beginning of frame
sim_ent_release_all_with_prop(world, SEPROP_RELEASE);
sim_accel_reset(world, ctx->accel);
ResetSimAccel(world, ctx->accel);
/* ========================== *
* Activate entities
* ========================== */
//- Activate entities
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
Entity *ent = &world->ents[ent_index];
@ -964,9 +938,8 @@ void sim_step(SimStepCtx *ctx)
}
}
/* ========================== *
* Process player cmds
* ========================== */
//- Process player cmds
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
Entity *cmd_ent = &world->ents[ent_index];
@ -1023,7 +996,7 @@ void sim_step(SimStepCtx *ctx)
}
}
if (flags & SIM_CONTROL_FLAG_CLEAR_ALL) {
test_clear_level(ctx);
ClearLevelTest(ctx);
}
if (flags & SIM_CONTROL_FLAG_SPAWN1_TEST) {
P_LogDebugF("Spawn test 1");
@ -1032,7 +1005,7 @@ void sim_step(SimStepCtx *ctx)
for (u32 j = 0; j < count; ++j) {
Vec2 pos = player->player_cursor_pos;
pos.y += (((f32)j / (f32)count) - 0.5) * spread;
test_spawn_entities1(root, pos);
SpawnTestEntities1(root, pos);
}
}
if (flags & SIM_CONTROL_FLAG_SPAWN2_TEST) {
@ -1042,7 +1015,7 @@ void sim_step(SimStepCtx *ctx)
for (u32 j = 0; j < count; ++j) {
Vec2 pos = player->player_cursor_pos;
pos.y += (((f32)j / (f32)count) - 0.5) * spread;
test_spawn_entities2(root, pos);
SpawnTestEntities2(root, pos);
}
}
if (flags & SIM_CONTROL_FLAG_SPAWN3_TEST) {
@ -1052,7 +1025,7 @@ void sim_step(SimStepCtx *ctx)
for (u32 j = 0; j < count; ++j) {
Vec2 pos = player->player_cursor_pos;
pos.y += (((f32)j / (f32)count) - 0.5) * spread;
test_spawn_entities3(root, pos);
SpawnTestEntities3(root, pos);
}
}
if (flags & SIM_CONTROL_FLAG_SPAWN4_TEST) {
@ -1062,22 +1035,22 @@ void sim_step(SimStepCtx *ctx)
for (u32 j = 0; j < count; ++j) {
Vec2 pos = player->player_cursor_pos;
pos.y += (((f32)j / (f32)count) - 0.5) * spread;
test_spawn_entities4(root, pos);
SpawnTestEntities4(root, pos);
}
}
if (flags & SIM_CONTROL_FLAG_WALLS_TEST) {
test_generate_walls(world);
GenerateTestWalls(world);
}
if (flags & SIM_CONTROL_FLAG_EXPLODE_TEST) {
P_LogDebugF("Explosion test");
test_spawn_explosion(root, player->player_cursor_pos, 100, 2);
SpawnTestExplosion(root, player->player_cursor_pos, 100, 2);
}
}
if (flags & SIM_CONTROL_FLAG_TILE_TEST) {
test_spawn_tile(world, player->player_cursor_pos);
SpawnTestTile(world, player->player_cursor_pos);
} else if (old_control.flags & SIM_CONTROL_FLAG_TILE_TEST) {
test_generate_walls(world);
GenerateTestWalls(world);
}
}
} break;
@ -1111,9 +1084,8 @@ void sim_step(SimStepCtx *ctx)
}
}
/* ========================== *
* Update entity control from player control
* ========================== */
//- Update entity control from player control
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
Entity *ent = &world->ents[ent_index];
@ -1127,9 +1099,8 @@ void sim_step(SimStepCtx *ctx)
}
}
/* ========================== *
* Create employees
* ========================== */
//- Create employees
if (is_master) {
for (u64 i = 0; i < world->num_ents_reserved; ++i) {
@ -1139,7 +1110,7 @@ void sim_step(SimStepCtx *ctx)
/* FIXME: Ents never released when client disconnects */
Entity *control_ent = sim_ent_from_id(world, ent->player_control_ent);
if (!control_ent->valid) {
control_ent = test_spawn_employee(root);
control_ent = SpawnTestEmployee(root);
control_ent->predictor = ent->id;
sim_ent_enable_prop(control_ent, SEPROP_CONTROLLED);
ent->player_control_ent = control_ent->id;
@ -1147,7 +1118,7 @@ void sim_step(SimStepCtx *ctx)
}
Entity *camera_ent = sim_ent_from_id(world, ent->player_camera_ent);
if (!camera_ent->valid) {
camera_ent = test_spawn_camera(root, control_ent);
camera_ent = SpawnTestCamera(root, control_ent);
camera_ent->predictor = ent->id;
ent->player_camera_ent = camera_ent->id;
}
@ -1159,9 +1130,8 @@ void sim_step(SimStepCtx *ctx)
}
}
/* ========================== *
* Update entities from sprite
* ========================== */
//- Update entities from sprite
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
Entity *ent = &world->ents[ent_index];
@ -1261,9 +1231,8 @@ void sim_step(SimStepCtx *ctx)
#endif
}
/* ========================== *
* Update attachments
* ========================== */
//- Update attachments
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
Entity *ent = &world->ents[ent_index];
@ -1286,9 +1255,8 @@ void sim_step(SimStepCtx *ctx)
sim_ent_set_local_xform(ent, xf);
}
/* ========================== *
* Process ent control
* ========================== */
//- Process ent control
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
Entity *ent = &world->ents[ent_index];
@ -1310,14 +1278,13 @@ void sim_step(SimStepCtx *ctx)
}
}
if (flags & SIM_CONTROL_FLAG_TELEPORT_TEST) {
test_teleport(ent, control->dbg_cursor);
TeleportTest(ent, control->dbg_cursor);
}
}
}
/* ========================== *
* Process triggered entities
* ========================== */
//- Process triggered entities
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
Entity *ent = &world->ents[ent_index];
@ -1476,9 +1443,8 @@ void sim_step(SimStepCtx *ctx)
}
}
/* ========================== *
* Create & update motor joints from control move
* ========================== */
//- Create & update motor joints from control move
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
Entity *ent = &world->ents[ent_index];
@ -1512,9 +1478,8 @@ void sim_step(SimStepCtx *ctx)
}
}
/* ========================== *
* Create & update motor joints from control focus (aim)
* ========================== */
//- Create & update motor joints from control focus (aim)
#if SIM_PLAYER_AIM
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
@ -1610,9 +1575,8 @@ void sim_step(SimStepCtx *ctx)
}
#endif
/* ========================== *
* Create motor joints from ground friction (gravity)
* ========================== */
//- Create motor joints from ground friction (gravity)
#if 1
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
@ -1641,9 +1605,8 @@ void sim_step(SimStepCtx *ctx)
}
#endif
/* ========================== *
* Create mouse joints from client debug drag
* ========================== */
//- Create mouse joints from client debug drag
if (is_master) {
for (u64 i = 0; i < world->num_ents_reserved; ++i) {
@ -1696,20 +1659,18 @@ void sim_step(SimStepCtx *ctx)
}
}
/* ========================== *
* Physics step
* ========================== */
//- Physics step
{
PhysStepCtx phys = ZI;
phys.sim_step_ctx = ctx;
phys.collision_callback = on_collision;
phys.collision_callback = OnEntityCollision;
phys_step(&phys, sim_dt);
}
/* ========================== *
* Update explosions
* ========================== */
//- Update explosions
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
Entity *ent = &world->ents[ent_index];
@ -1720,9 +1681,8 @@ void sim_step(SimStepCtx *ctx)
sim_ent_disable_prop(ent, SEPROP_SENSOR);
}
/* ========================== *
* Update tracers
* ========================== */
//- Update tracers
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
Entity *ent = &world->ents[ent_index];
@ -1744,9 +1704,8 @@ void sim_step(SimStepCtx *ctx)
ent->tracer_gradient_end = gradient_end;
}
/* ========================== *
* Initialize bullet kinematics from sources
* ========================== */
//- Initialize bullet kinematics from sources
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
Entity *ent = &world->ents[ent_index];
@ -1803,9 +1762,8 @@ void sim_step(SimStepCtx *ctx)
}
}
/* ========================== *
* Update cameras
* ========================== */
//- Update cameras
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
Entity *ent = &world->ents[ent_index];
@ -1859,9 +1817,8 @@ void sim_step(SimStepCtx *ctx)
sim_ent_set_xform(ent, xf);
}
/* ========================== *
* Update quakes
* ========================== */
//- Update quakes
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
Entity *ent = &world->ents[ent_index];
@ -1874,9 +1831,8 @@ void sim_step(SimStepCtx *ctx)
}
}
/* ========================== *
* Update relative layers
* ========================== */
//- Update relative layers
{
TempArena temp = BeginTempArena(scratch.arena);
@ -1903,15 +1859,13 @@ void sim_step(SimStepCtx *ctx)
EndTempArena(temp);
}
/* ========================== *
* Release entities at end of frame
* ========================== */
//- Release entities at end of frame
sim_ent_release_all_with_prop(world, SEPROP_RELEASE);
/* ========================== *
* Sync to publish client
* ========================== */
//- Sync to publish client
if (publish_client->valid && world->tick > publish_client->last_tick) {
Snapshot *prev_pub_world = sim_snapshot_from_tick(publish_client, publish_client->last_tick);
@ -1936,9 +1890,8 @@ void sim_step(SimStepCtx *ctx)
pub_world->local_player = world->local_player;
}
/* ========================== *
* End frame
* ========================== */
//- End frame
S_EndScope(sprite_frame_scope);

View File

@ -1,6 +1,5 @@
/* ========================== *
* Sim accel
* ========================== */
////////////////////////////////
//~ Acceleration structure types
/* Structure used to accelerate up entity lookup (rebuilt every step) */
/* TODO: Remove this and do something better. Just a hack to de-couple old sim ctx from step. */
@ -10,13 +9,8 @@ struct SimAccel {
Space *space;
};
SimAccel sim_accel_acquire(void);
void sim_accel_release(SimAccel *accel);
void sim_accel_reset(Snapshot *ss, SimAccel *accel);
/* ========================== *
* Sim step
* ========================== */
////////////////////////////////
//~ Step ctx
typedef struct SimStepCtx SimStepCtx;
struct SimStepCtx {
@ -32,4 +26,43 @@ struct SimStepCtx {
Client *publish_client; /* The publish client to write syncable state to (nil if skipping publish) */
};
void sim_step(SimStepCtx *ctx);
////////////////////////////////
//~ Accel operations
SimAccel AcquireSimAccel(void);
void ReleaseSimAccel(SimAccel *accel);
void ResetSimAccel(Snapshot *ss, SimAccel *accel);
////////////////////////////////
//~ Spawn test operations
Entity *SpawnTestSmg(Entity *parent);
Entity *SpawnTestLauncher(Entity *parent);
Entity *SpawnTestChucker(Entity *parent);
Entity *SpawnTestEmployee(Entity *parent);
Entity *SpawnTestCamera(Entity *parent, Entity *follow);
Entity *SpawnTestExplosion(Entity *parent, Vec2 pos, f32 strength, f32 radius);
void TeleportTest(Entity *ent, Vec2 pos);
void SpawnTestEntities1(Entity *parent, Vec2 pos);
void SpawnTestEntities2(Entity *parent, Vec2 pos);
void SpawnTestEntities3(Entity *parent, Vec2 pos);
void SpawnTestEntities4(Entity *parent, Vec2 pos);
void SpawnTestTile(Snapshot *world, Vec2 world_pos);
void ClearLevelTest(SimStepCtx *ctx);
////////////////////////////////
//~ Tile test operations
MergesortCompareFuncDef(SortTileXCmp, arg_a, arg_b, _);
MergesortCompareFuncDef(SortTileYCmp, arg_a, arg_b, _);
void GenerateTestWalls(Snapshot *world);
////////////////////////////////
//~ Collision response
PHYS_COLLISION_CALLBACK_FUNC_DEF(OnEntityCollision, data, step_ctx);
////////////////////////////////
//~ Step
void StepSim(SimStepCtx *ctx);