Safe sun and delete at end of processing

main
parent 3890722c65
commit 8ca9f2257e

@ -18,15 +18,15 @@
// Intensive profiling means profiling a lot of little tiny stuff. Not always enabled because tanks performance
// #define INTENSIVE_PROFILING
// #define DEBUG_RENDERING
// #define DEBUG_WORLD
// #define UNLOCK_ALL
#define DEBUG_WORLD
#define UNLOCK_ALL
#define TIME_BETWEEN_WORLD_SAVE 1000000.0f
// #define TIME_BETWEEN_WORLD_SAVE 1.0f
// #define INFINITE_RESOURCES
#define INFINITE_RESOURCES
#define DEBUG_TOOLS
#define CHIPMUNK_INTEGRITY_CHECK
// #define FAT_THRUSTERS
// #define NO_GRAVITY
#define NO_GRAVITY
// #define NO_SUNS
#else

Binary file not shown.

@ -407,70 +407,64 @@ LauncherTarget missile_launcher_target(GameState *gs, Entity *launcher)
return (LauncherTarget){.target_found = target_found, .facing_angle = to_face};
}
void on_entity_child_shape(cpBody *body, cpShape *shape, void *data); // declared here bc entity_destroy circular dependency
// gs is for iterating over all child shapes and destroying those, too
static void destroy_body(GameState *gs, cpBody **body)
void destroy_child_shape(cpBody *body, cpShape *shape, void *data)
{
if (*body != NULL)
GameState *gs = (GameState *)data;
if (cp_shape_entity(shape) == NULL)
{
// support the case where no parent entity *SPECIFICALLY* for grid_correct_for_holes,
// where the entities that are part of the old grid are reused so entityids are preserved
cpSpaceRemoveShape(gs->space, shape);
cpShapeFree(shape);
}
else
{
cpBodyEachShape(*body, on_entity_child_shape, (void *)gs);
cpSpaceRemoveBody(gs->space, *body);
cpBodyFree(*body);
*body = NULL;
entity_memory_free(gs, cp_shape_entity(shape));
}
*body = NULL;
}
// will destroy all shapes which are attached to the body in the entity
void entity_destroy(GameState *gs, Entity *e)
// Destroys the entity and puts its slot back into the free list. Doesn't obey game rules
// like making sure grids don't have holes in them, for that you want entity_destroy.
// *Does* free all owned memory/entities though, e.g grids free the boxes they own.
void entity_memory_free(GameState *gs, Entity *e)
{
flight_assert(e->exists);
if (e->is_grid)
{
BOXES_ITER(gs, cur, e)
entity_destroy(gs, cur);
{
entity_memory_free(gs, cur);
}
}
if (e->is_box)
{
box_remove_from_boxes(gs, e);
}
if (e->shape != NULL)
{
cpSpaceRemoveShape(gs->space, e->shape);
cpShapeFree(e->shape);
e->shape = NULL;
}
destroy_body(gs, &e->body);
if (e->body != NULL)
{
cpBodyEachShape(e->body, destroy_child_shape, (void *)gs);
cpSpaceRemoveBody(gs->space, e->body);
cpBodyFree(e->body);
e->body = NULL;
}
Entity *front_of_free_list = get_entity(gs, gs->free_list);
if (front_of_free_list != NULL)
flight_assert(!front_of_free_list->exists);
int gen = e->generation;
unsigned int gen = e->generation;
*e = (Entity){0};
e->generation = gen;
e->next_free_entity = gs->free_list;
gs->free_list = get_id(gs, e);
}
void on_entity_child_shape(cpBody *body, cpShape *shape, void *data)
{
GameState *gs = (GameState *)data;
if (cp_shape_entity(shape) == NULL)
{
// support the case where no parent entity *SPECIFICALLY* for grid_correct_for_holes,
// where the entities that are part of the old grid are reused so entityids are preserved
cpSpaceRemoveShape(gs->space, shape);
cpShapeFree(shape);
}
else
{
entity_destroy(gs, cp_shape_entity(shape));
}
}
Entity *new_entity(GameState *gs)
{
Entity *to_return = NULL;
@ -828,7 +822,7 @@ static void grid_correct_for_holes(GameState *gs, struct Entity *grid)
int num_boxes = grid_num_boxes(gs, grid);
if (num_boxes == 0)
{
entity_destroy(gs, grid);
entity_memory_free(gs, grid);
return;
}
if (num_boxes == 1)
@ -966,15 +960,7 @@ static void grid_correct_for_holes(GameState *gs, struct Entity *grid)
}
// destroys all the box shapes and the entities attached to those shapes
entity_destroy(gs, grid);
}
static void grid_remove_box(GameState *gs, struct Entity *grid, struct Entity *box)
{
flight_assert(grid->is_grid);
flight_assert(box->is_box);
entity_destroy(gs, box);
grid_correct_for_holes(gs, grid);
entity_memory_free(gs, grid);
}
static void on_damage(cpArbiter *arb, cpSpace *space, cpDataPointer userData)
@ -1055,7 +1041,7 @@ void destroy(GameState *gs)
{
if (gs->entities[i].exists)
{
entity_destroy(gs, &gs->entities[i]);
entity_memory_free(gs, &gs->entities[i]);
gs->entities[i] = (Entity){0};
}
}
@ -1304,6 +1290,7 @@ enum GameVersion
{
VInitial,
VNoGold,
VSafeSun,
VMax, // this minus one will be the version used
};
@ -1539,6 +1526,8 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e)
SER_MAYBE_RETURN(ser_V2(ser, &e->sun_pos));
SER_MAYBE_RETURN(ser_f(ser, &e->sun_mass));
SER_MAYBE_RETURN(ser_f(ser, &e->sun_radius));
if (ser->version >= VSafeSun)
SER_VAR(&e->sun_is_safe);
}
SER_VAR(&e->is_grid);
@ -2390,12 +2379,12 @@ void create_initial_world(GameState *gs)
{
const double mass_multiplier = 10.0;
EntityID suns[] = {
create_sun(gs, new_entity(gs), ((cpVect){500.0, 0.0}), ((cpVect){0.0, 0.0}), 1000000.0 * mass_multiplier, 30.0),
create_sun(gs, new_entity(gs), ((cpVect){500.0, 100.0}), ((cpVect){30.0, 0.0}), 10000.0 * mass_multiplier, 20.0),
create_sun(gs, new_entity(gs), ((cpVect){500.0, -100.0}), ((cpVect){-30.0, 0.0}), 10000.0 * mass_multiplier, 20.0),
create_sun(gs, new_entity(gs), ((cpVect){500.0, 0.0}), ((cpVect){0.0, 0.0}), 1000000.0 * mass_multiplier, 70.0),
create_sun(gs, new_entity(gs), ((cpVect){-7000.0, -50.0}), ((cpVect){0.0, 0.0}), 100000.0 * mass_multiplier, 20.0),
};
get_entity(gs, suns[0])->sun_is_safe = true;
for (int i = 0; i < ARRLEN(suns); i++)
{
gs->suns[i] = suns[i];
@ -2417,10 +2406,23 @@ void create_initial_world(GameState *gs)
create_bomb_station(gs, cpvadd(SUN_POS(0), cpv(0.0, 300.0)), BoxExplosive);
create_bomb_station(gs, cpvadd(SUN_POS(0), cpv(0.0, -300.0)), BoxCloaking);
create_bomb_station(gs, cpvadd(SUN_POS(0), cpv(300.0, 0.0)), BoxMissileLauncher);
create_bomb_station(gs, cpvadd(SUN_POS(3), cpv(0.0, 300.0)), BoxMerge);
create_bomb_station(gs, cpvadd(SUN_POS(1), cpv(0.0, 300.0)), BoxMerge);
#undef SUN_POS
#else
#if 1 // basic test
{
bool indestructible = false;
enum CompassRotation rot = Right;
Entity *grid = new_entity(gs);
grid_create(gs, grid);
entity_set_pos(grid, cpv(-1.5, 0.0));
BOX_AT_TYPE(grid, cpv(0.0, 0.0), BoxHullpiece);
BOX_AT_TYPE(grid, cpv(BOX_SIZE, 0.0), BoxHullpiece);
entity_ensure_in_orbit(gs, grid);
}
#endif
#if 1 // present the stations
Log("Creating debug world\n");
@ -2551,13 +2553,16 @@ void process(struct GameState *gs, double dt)
}
}
#ifndef NO_GRAVITY
from_sun->sun_vel = cpvadd(from_sun->sun_vel, cpvmult(accel, dt));
from_sun->sun_pos = cpvadd(from_sun->sun_pos, cpvmult(from_sun->sun_vel, dt));
if (cpvlength(from_sun->sun_pos) >= INSTANT_DEATH_DISTANCE_FROM_CENTER)
if (!from_sun->sun_is_safe)
{
from_sun->sun_vel = cpvmult(from_sun->sun_vel, -0.8);
from_sun->sun_pos = cpvmult(cpvnormalize(from_sun->sun_pos), INSTANT_DEATH_DISTANCE_FROM_CENTER);
from_sun->sun_vel = cpvadd(from_sun->sun_vel, cpvmult(accel, dt));
from_sun->sun_pos = cpvadd(from_sun->sun_pos, cpvmult(from_sun->sun_vel, dt));
if (cpvlength(from_sun->sun_pos) >= INSTANT_DEATH_DISTANCE_FROM_CENTER)
{
from_sun->sun_vel = cpvmult(from_sun->sun_vel, -0.8);
from_sun->sun_pos = cpvmult(cpvnormalize(from_sun->sun_pos), INSTANT_DEATH_DISTANCE_FROM_CENTER);
}
}
#endif
}
@ -2800,9 +2805,8 @@ void process(struct GameState *gs, double dt)
Entity *cur_box = cp_shape_entity(maybe_box_to_destroy);
if (!cur_box->indestructible && !cur_box->is_platonic)
{
Entity *cur_grid = cp_body_entity(cpShapeGetBody(maybe_box_to_destroy));
p->damage -= DAMAGE_TO_PLAYER_PER_BLOCK * ((BATTERY_CAPACITY - cur_box->energy_used) / BATTERY_CAPACITY);
grid_remove_box(gs, cur_grid, cur_box);
cur_box->flag_for_destruction = true;
}
}
else if (box_unlocked(player, player->input.build_type))
@ -2840,7 +2844,7 @@ void process(struct GameState *gs, double dt)
#endif
if (p->damage >= 1.0)
{
entity_destroy(gs, p);
p->flag_for_destruction = true;
player->entity = (EntityID){0};
}
@ -2850,12 +2854,8 @@ void process(struct GameState *gs, double dt)
PROFILE_SCOPE("process entities")
{
for (size_t i = 0; i < gs->cur_next_entity; i++)
ENTITIES_ITER(gs, e)
{
Entity *e = &gs->entities[i];
if (!e->exists)
continue;
if (e->body != NULL && cpvlengthsq((entity_pos(e))) > (INSTANT_DEATH_DISTANCE_FROM_CENTER * INSTANT_DEATH_DISTANCE_FROM_CENTER))
{
#ifdef INTENSIVE_PROFILING
@ -2883,14 +2883,12 @@ void process(struct GameState *gs, double dt)
}
else
{
entity_destroy(gs, e);
continue;
e->flag_for_destruction = true;
}
continue;
}
}
// sun processing for this current entity
// sun processing for this current entity
#ifndef NO_SUNS
PROFILE_SCOPE("this entity sun processing")
{
@ -2899,13 +2897,24 @@ void process(struct GameState *gs, double dt)
cpVect pos_rel_sun = (cpvsub(entity_pos(e), (entity_pos(i.sun))));
cpFloat sqdist = cpvlengthsq(pos_rel_sun);
bool is_entity_dangerous = false;
is_entity_dangerous |= e->is_missile;
if (e->is_box)
{
is_entity_dangerous |= e->box_type == BoxExplosive;
}
if (is_entity_dangerous && sqdist < sun_dist_no_gravity(i.sun) * sun_dist_no_gravity(i.sun))
{
e->flag_for_destruction = true;
break;
}
if (!e->is_grid) // grids aren't damaged (this edge case sucks!)
{
#ifdef INTENSIVE_PROFILING
PROFILE_SCOPE("Grid processing")
#endif
{
sqdist = cpvlengthsq(cpvsub((entity_pos(e)), (entity_pos(i.sun))));
if (sqdist < (i.sun->sun_radius * i.sun->sun_radius))
{
e->damage += 10.0 * dt;
@ -2938,8 +2947,7 @@ void process(struct GameState *gs, double dt)
do_explosion(gs, e, dt);
if (e->explosion_progress >= EXPLOSION_TIME)
{
entity_destroy(gs, e);
continue;
e->flag_for_destruction = true;
}
}
}
@ -2987,7 +2995,7 @@ void process(struct GameState *gs, double dt)
explosion->explosion_vel = cpBodyGetVelocity(e->body);
explosion->explosion_push_strength = MISSILE_EXPLOSION_PUSH;
explosion->explosion_radius = MISSILE_EXPLOSION_RADIUS;
entity_destroy(gs, e);
e->flag_for_destruction = true;
continue;
}
}
@ -2999,11 +3007,6 @@ void process(struct GameState *gs, double dt)
PROFILE_SCOPE("Box processing")
#endif
{
if (e->is_platonic)
{
e->damage = 0.0;
gs->platonic_positions[(int)e->box_type] = entity_pos(e);
}
if (e->box_type == BoxExplosive && e->damage >= EXPLOSION_DAMAGE_THRESHOLD)
{
Entity *explosion = new_entity(gs);
@ -3013,7 +3016,12 @@ void process(struct GameState *gs, double dt)
explosion->explosion_push_strength = BOMB_EXPLOSION_PUSH;
explosion->explosion_radius = BOMB_EXPLOSION_RADIUS;
if (!e->is_platonic)
grid_remove_box(gs, get_entity(gs, e->shape_parent_entity), e);
e->flag_for_destruction = true;
}
if (e->is_platonic)
{
e->damage = 0.0;
gs->platonic_positions[(int)e->box_type] = entity_pos(e);
}
if (e->box_type == BoxMerge)
{
@ -3091,7 +3099,7 @@ void process(struct GameState *gs, double dt)
flight_assert(box_grid(cur) == box_grid(from_merge));
cur = next;
}
entity_destroy(gs, other_grid);
other_grid->flag_for_destruction = true;
}
}
}
@ -3099,7 +3107,7 @@ void process(struct GameState *gs, double dt)
}
if (e->damage >= 1.0)
{
grid_remove_box(gs, get_entity(gs, e->shape_parent_entity), e);
e->flag_for_destruction = true;
}
}
}
@ -3160,7 +3168,7 @@ void process(struct GameState *gs, double dt)
rect_query(gs->space, (BoxCentered){
.pos = cpvadd(entity_pos(cur_box), cpvmult(box_facing_vector(cur_box), BOX_SIZE)),
.rotation = box_rotation(cur_box),
.size = cpv(BOX_SIZE/2.0 - 0.03, BOX_SIZE/2.0 - 0.03),
.size = cpv(BOX_SIZE / 2.0 - 0.03, BOX_SIZE / 2.0 - 0.03),
});
QUEUE_ITER(&query_result, QueryResult, res)
{
@ -3397,6 +3405,22 @@ void process(struct GameState *gs, double dt)
}
}
PROFILE_SCOPE("Delete entities")
{
ENTITIES_ITER(gs, e)
{
if (e->flag_for_destruction)
{
Entity *grid = NULL;
if (e->is_box)
grid = box_grid(e);
entity_memory_free(gs, e);
if (grid != NULL)
grid_correct_for_holes(gs, grid);
}
}
}
PROFILE_SCOPE("chipmunk physics processing")
{
cpSpaceStep(gs->space, dt);

@ -233,10 +233,6 @@ static struct BoxInfo
.image_path = "loaded/merge.png",
},
};
#define ENTITIES_ITER(cur) \
for (Entity *cur = gs.entities; cur < gs.entities + gs.cur_next_entity; \
cur++) \
if (cur->exists)
// suppress compiler warning about ^^ above used in floating point context
#define ARRLENF(arr) ((float)sizeof(arr) / sizeof(*arr))
static struct SquadMeta
@ -299,7 +295,7 @@ static void new_particle(cpVect pos, cpVect vel)
}
if (!created)
{
Log("TOO MANY PARTICLES");
Log("TOO MANY PARTICLES\n");
}
}
@ -472,6 +468,11 @@ void recalculate_camera_pos()
{ \
.r = 0.0f, .g = 0.0f, .b = 1.0f, .a = 1.0f \
}
#define GREEN \
(Color) \
{ \
.r = 0.0f, .g = 1.0f, .b = 0.0f, .a = 1.0f \
}
#define GOLD colhex(255, 215, 0)
typedef struct Color
@ -957,7 +958,7 @@ cpVect pointV2(sgp_point p)
static void draw_circle(cpVect point, double radius)
{
#define POINTS 64
#define POINTS 128
sgp_line lines[POINTS];
for (int i = 0; i < POINTS; i++)
{
@ -2049,7 +2050,7 @@ static void frame(void)
{
if (p->alive)
{
p->alive_for += dt*1.5;
p->alive_for += dt * 1.5;
p->pos = cpvadd(p->pos, cpvmult(p->vel, dt));
if (p->alive_for > 1.0)
{
@ -2332,7 +2333,7 @@ static void frame(void)
hovering_this_player = (EntityID){0};
// draw all types of entities
ENTITIES_ITER(e)
ENTITIES_ITER(&gs, e)
{
// draw grid
if (e->is_grid)
@ -2734,8 +2735,10 @@ static void frame(void)
// can draw at 0,0 because everything relative to sun now!
// sun DEATH RADIUS
set_color(BLUE);
if (i.sun->sun_is_safe)
set_color(GREEN);
else
set_color(BLUE);
draw_circle((cpVect){0}, sun_dist_no_gravity(i.sun));
}
}

@ -282,7 +282,7 @@ void server(void *info_raw)
Entity *player_body = get_entity(&gs, gs.players[player_index].entity);
if (player_body != NULL)
{
entity_destroy(&gs, player_body);
entity_memory_free(&gs, player_body);
}
opus_encoder_destroy(player_encoders[player_index]);
player_encoders[player_index] = NULL;

@ -130,7 +130,8 @@
#include "cpVect.h" // offers vector functions and types for the structs
#include "miniaudio.h" // @Robust BAD. using miniaudio mutex construct for server thread synchronization. AWFUL!
#include <math.h> // sqrt and cos vector functions
#define _USE_MATH_DEFINES
#include <math.h> // sqrt and cos vector functions and PI
#include <stdint.h> // tick is unsigned integer
#include <stdio.h> // logging on errors for functions
@ -219,8 +220,8 @@ typedef struct EntityID
static inline bool entityids_same(EntityID a, EntityID b)
{
return (a.generation == b.generation) && (a.index == b.index);
return (a.generation == b.generation) && (a.index == b.index);
}
enum ScannerPointKind
@ -237,7 +238,7 @@ typedef struct InputFrame
uint64_t tick;
bool been_processed; // not serialized, used by server just to keep track of what inputs have been processed
cpVect movement;
double rotation;
@ -263,6 +264,7 @@ typedef struct PlatonicDetection
typedef struct Entity
{
bool exists;
bool flag_for_destruction;
EntityID next_free_entity;
unsigned int generation;
bool always_visible; // always serialized to the player.
@ -291,7 +293,7 @@ typedef struct Entity
enum Squad owning_squad; // also controls what the player can see, because of cloaking!
EntityID currently_inside_of_box;
enum Squad squad_invited_to; // if squad none, then no squad invite
// explosion
bool is_explosion;
cpVect explosion_pos;
@ -306,6 +308,7 @@ typedef struct Entity
cpVect sun_pos;
double sun_mass;
double sun_radius;
bool sun_is_safe;
// missile
bool is_missile;
@ -322,9 +325,9 @@ typedef struct Entity
// boxes
bool is_box;
enum BoxType box_type;
bool is_platonic; // can't be destroyed, unaffected by physical forces
EntityID next_box; // for the grid!
EntityID prev_box; // doubly linked so can remove in middle of chain
bool is_platonic; // can't be destroyed, unaffected by physical forces
EntityID next_box; // for the grid!
EntityID prev_box; // doubly linked so can remove in middle of chain
enum CompassRotation compass_rotation;
bool indestructible;
@ -397,6 +400,10 @@ typedef struct SunIter
for (SunIter i = {0}; i.i < MAX_SUNS; i.i++) \
if ((i.sun = get_entity(gs_ptr, (gs_ptr)->suns[i.i])) != NULL)
#define ENTITIES_ITER(gs, cur) \
for (Entity *cur = (gs)->entities; cur < (gs)->entities + (gs)->cur_next_entity; cur++) \
if (cur->exists)
// gotta update the serialization functions when this changes
typedef struct GameState
{
@ -413,7 +420,7 @@ typedef struct GameState
bool server_side_computing; // some things only the server should know and calculate, like platonic locations
// Entity arena
// ent:ity pointers can't move around because of how the physics engine handles user data.
// entity pointers can't move around because of how the physics engine handles user data.
// if you really need this, potentially refactor to store entity IDs instead of pointers
// in the shapes and bodies of chipmunk. Would require editing the library I think
Entity *entities;
@ -426,8 +433,8 @@ typedef struct GameState
for (Player *cur = players; cur < players + MAX_PLAYERS; cur++) \
if (cur->connected)
#define PI 3.14159f
#define TAU (PI * 2.0f)
#define PI M_PI
#define TAU (M_PI * 2.0)
// returns in radians
static inline double rotangle(enum CompassRotation rot)
@ -539,7 +546,7 @@ bool could_learn_from_scanner(Player *for_player, Entity *box);
void entity_set_pos(Entity *e, cpVect pos);
double entity_rotation(Entity *e);
void entity_ensure_in_orbit(GameState *gs, Entity *e);
void entity_destroy(GameState *gs, Entity *e);
void entity_memory_free(GameState *gs, Entity *e);
#define BOX_CHAIN_ITER(gs, cur, starting_box) for (Entity *cur = get_entity(gs, starting_box); cur != NULL; cur = get_entity(gs, cur->next_box))
#define BOXES_ITER(gs, cur, grid_entity_ptr) BOX_CHAIN_ITER(gs, cur, (grid_entity_ptr)->boxes)
typedef struct LauncherTarget
@ -705,5 +712,5 @@ static inline double deg2rad(double deg)
return (deg / 360.0f) * 2.0f * PI;
}
#define min(X,Y) (((X) < (Y)) ? (X) : (Y))
#define max(X,Y) (((X) > (Y)) ? (X) : (Y))
#define min(X, Y) (((X) < (Y)) ? (X) : (Y))
#define max(X, Y) (((X) > (Y)) ? (X) : (Y))
Loading…
Cancel
Save