diff --git a/Flight.vcxproj b/Flight.vcxproj
index 91cd780..4b78c28 100644
--- a/Flight.vcxproj
+++ b/Flight.vcxproj
@@ -105,6 +105,7 @@
_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
C:\Users\Cameron\Documents\flight\thirdparty\enet\include;C:\Users\Cameron\Documents\flight\thirdparty\Chipmunk2D\include\chipmunk;C:\Users\Cameron\Documents\flight\thirdparty;C:\Users\Cameron\Documents\flight\thirdparty\Chipmunk2D\include
+ true
Console
@@ -192,4 +193,4 @@
-
+
\ No newline at end of file
diff --git a/Flight.vcxproj.filters b/Flight.vcxproj.filters
index 1820211..8c2885d 100644
--- a/Flight.vcxproj.filters
+++ b/Flight.vcxproj.filters
@@ -15,174 +15,172 @@
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Source Files
-
+
Header Files
-
+
Header Files
-
+
Header Files
-
+
Header Files
-
+
Header Files
-
+
Header Files
-
+
Header Files
-
+
Header Files
-
+
Header Files
-
- Source Files
-
+
\ No newline at end of file
diff --git a/gamestate.c b/gamestate.c
index b6a74a3..18504f9 100644
--- a/gamestate.c
+++ b/gamestate.c
@@ -37,7 +37,7 @@ static cpVect v2_to_cp(V2 v)
return cpv(v.x, v.y);
}
-bool was_entity_deleted(struct GameState* gs, EntityID id)
+bool was_entity_deleted(GameState* gs, EntityID id)
{
if (id.generation == 0) return false; // generation 0 means null entity ID, not a deleted entity
Entity* the_entity = &gs->entities[id.index];
@@ -45,7 +45,7 @@ bool was_entity_deleted(struct GameState* gs, EntityID id)
}
// may return null if it doesn't exist anymore
-Entity* get_entity(struct GameState* gs, EntityID id)
+Entity* get_entity(GameState* gs, EntityID id)
{
if (id.generation == 0)
{
@@ -59,18 +59,18 @@ Entity* get_entity(struct GameState* gs, EntityID id)
return to_return;
}
-EntityID get_id(struct GameState* gs, Entity* e)
+EntityID get_id(GameState* gs, Entity* e)
{
if (e == NULL)
return (EntityID) { 0 };
- size_t index = e - gs->entities;
+ size_t index = (e - gs->entities);
assert(index >= 0);
assert(index < gs->cur_next_entity);
return (EntityID) {
.generation = e->generation,
- .index = index,
+ .index = (unsigned int)index,
};
}
@@ -84,12 +84,12 @@ static Entity* cp_body_entity(cpBody* body)
return (Entity*)cpBodyGetUserData(body);
}
-static struct GameState* cp_space_gs(cpSpace* space)
+static GameState* cp_space_gs(cpSpace* space)
{
- return (struct GameState*)cpSpaceGetUserData(space);
+ return (GameState*)cpSpaceGetUserData(space);
}
-int grid_num_boxes(struct GameState* gs, Entity* e)
+int grid_num_boxes(GameState* gs, Entity* e)
{
assert(e->is_grid);
int to_return = 0;
@@ -122,6 +122,20 @@ void box_remove_from_boxes(GameState* gs, Entity* box)
}
void on_entity_child_shape(cpBody* body, cpShape* shape, void* data);
+
+// gs is for iterating over all child shapes and destroying those, too
+static void destroy_body(GameState* gs, cpBody** body)
+{
+ if (*body != NULL)
+ {
+ cpBodyEachShape(*body, on_entity_child_shape, (void*)gs);
+ cpSpaceRemoveBody(gs->space, *body);
+ cpBodyFree(*body);
+ *body = NULL;
+ }
+ *body = NULL;
+}
+
void entity_destroy(GameState* gs, Entity* e)
{
assert(e->exists);
@@ -142,15 +156,7 @@ void entity_destroy(GameState* gs, Entity* e)
cpShapeFree(e->shape);
e->shape = NULL;
}
- if (e->body != NULL)
- {
- cpBodyEachShape(e->body, on_entity_child_shape, (void*)gs);
- cpSpaceRemoveBody(gs->space, e->body);
- cpBodyFree(e->body);
- e->body = NULL;
- }
- e->body = NULL;
- e->shape = NULL;
+ destroy_body(gs, &e->body);
Entity* front_of_free_list = get_entity(gs, gs->free_list);
if (front_of_free_list != NULL)
@@ -167,7 +173,7 @@ void on_entity_child_shape(cpBody* body, cpShape* shape, void* data)
entity_destroy((GameState*)data, cp_shape_entity(shape));
}
-Entity* new_entity(struct GameState* gs)
+Entity* new_entity(GameState* gs)
{
Entity* to_return = NULL;
if (get_entity(gs, gs->free_list) != NULL)
@@ -188,7 +194,7 @@ Entity* new_entity(struct GameState* gs)
return to_return;
}
-void create_body(struct GameState* gs, Entity* e)
+void create_body(GameState* gs, Entity* e)
{
assert(gs->space != NULL);
@@ -204,7 +210,20 @@ void create_body(struct GameState* gs, Entity* e)
cpBodySetUserData(e->body, (void*)e);
}
-void grid_create(struct GameState* gs, Entity* e)
+V2 player_vel(GameState* gs, Entity* player)
+{
+ assert(player->is_player);
+ Entity* potential_seat = get_entity(gs, player->currently_piloting_seat);
+ if (potential_seat != NULL)
+ {
+ return cp_to_v2(cpBodyGetVelocity(get_entity(gs, potential_seat->shape_parent_entity)->body));
+ }
+ else {
+ return cp_to_v2(cpBodyGetVelocity(player->body));
+ }
+}
+
+void grid_create(GameState* gs, Entity* e)
{
e->is_grid = true;
create_body(gs, e);
@@ -243,7 +262,7 @@ void create_rectangle_shape(GameState* gs, Entity* e, Entity* parent, V2 pos, V2
cpSpaceAddShape(gs->space, e->shape);
}
-void create_player(struct GameState* gs, Entity* e)
+void create_player(GameState* gs, Entity* e)
{
e->is_player = true;
create_body(gs, e);
@@ -253,7 +272,7 @@ void create_player(struct GameState* gs, Entity* e)
// box must be passed as a parameter as the box added to chipmunk uses this pointer in its
// user data. pos is in local coordinates. Adds the box to the grid's chain of boxes
-void box_create(struct GameState* gs, Entity* new_box, Entity* grid, V2 pos)
+void box_create(GameState* gs, Entity* new_box, Entity* grid, V2 pos)
{
new_box->is_box = true;
assert(gs->space != NULL);
@@ -276,7 +295,7 @@ void box_create(struct GameState* gs, Entity* new_box, Entity* grid, V2 pos)
// removes boxes from grid, then ensures that the rule that grids must not have
// holes in them is applied.
-static void grid_remove_box(struct GameState* gs, struct Entity* grid, struct Entity* box)
+static void grid_remove_box(GameState* gs, struct Entity* grid, struct Entity* box)
{
assert(grid->is_grid);
assert(box->is_box);
@@ -341,17 +360,17 @@ static void grid_remove_box(struct GameState* gs, struct Entity* grid, struct En
V2 cur_local_pos = entity_shape_pos(N);
const V2 dirs[] = {
(V2) {
-.x = -1.0f, .y = 0.0f
-},
- (V2) {
-.x = 1.0f, .y = 0.0f
-},
- (V2) {
-.x = 0.0f, .y = 1.0f
-},
- (V2) {
-.x = 0.0f, .y = -1.0f
-},
+ .x = -1.0f, .y = 0.0f
+ },
+ (V2) {
+ .x = 1.0f, .y = 0.0f
+ },
+ (V2) {
+ .x = 0.0f, .y = 1.0f
+ },
+ (V2) {
+ .x = 0.0f, .y = -1.0f
+ },
};
int num_dirs = sizeof(dirs) / sizeof(*dirs);
@@ -364,7 +383,7 @@ static void grid_remove_box(struct GameState* gs, struct Entity* grid, struct En
EntityID box_in_direction = (EntityID){ 0 };
BOXES_ITER(gs, cur, grid)
{
- if (V2cmp(entity_shape_pos(cur), wanted_local_pos, 0.01f))
+ if (V2equal(entity_shape_pos(cur), wanted_local_pos, 0.01f))
{
box_in_direction = get_id(gs, cur);
break;
@@ -428,18 +447,6 @@ static void grid_remove_box(struct GameState* gs, struct Entity* grid, struct En
}
}
-static void postStepRemove(cpSpace* space, void* key, void* data)
-{
- cpShape* b = (cpShape*)key;
- Entity* e = cp_shape_entity(b);
- // @Robust why not just do these deletions in the update loop? save on a lot of complexity
- if (e->damage > 1.0f)
- {
- if (e->is_box)
- grid_remove_box(cp_space_gs(space), cp_body_entity(cpShapeGetBody(b)), e);
- }
-}
-
static cpBool on_damage(cpArbiter* arb, cpSpace* space, cpDataPointer userData)
{
cpShape* a, * b;
@@ -458,28 +465,28 @@ static cpBool on_damage(cpArbiter* arb, cpSpace* space, cpDataPointer userData)
}
// b must be the key passed into the post step removed, the key is cast into its shape
- cpSpaceAddPostStepCallback(space, (cpPostStepFunc)postStepRemove, b, NULL);
- cpSpaceAddPostStepCallback(space, (cpPostStepFunc)postStepRemove, a, NULL);
+ //cpSpaceAddPostStepCallback(space, (cpPostStepFunc)postStepRemove, b, NULL);
+ //cpSpaceAddPostStepCallback(space, (cpPostStepFunc)postStepRemove, a, NULL);
return true; // keep colliding
}
-void initialize(struct GameState* gs, void* entity_arena, int entity_arena_size)
+void initialize(GameState* gs, void* entity_arena, size_t entity_arena_size)
{
- *gs = (struct GameState){ 0 };
+ *gs = (GameState){ 0 };
memset(entity_arena, 0, entity_arena_size); // SUPER critical. Random vals in the entity data causes big problem
gs->entities = (Entity*)entity_arena;
- gs->max_entities = entity_arena_size / sizeof(Entity);
+ gs->max_entities = (unsigned int)(entity_arena_size / sizeof(Entity));
gs->space = cpSpaceNew();
cpSpaceSetUserData(gs->space, (cpDataPointer)gs); // needed in the handler
cpCollisionHandler* handler = cpSpaceAddCollisionHandler(gs->space, 0, 0); // @Robust limit collision type to just blocks that can be damaged
handler->postSolveFunc = on_damage;
}
-void destroy(struct GameState* gs)
+void destroy(GameState* gs)
{
// can't zero out gs data because the entity memory arena is reused
// on deserialization
- for (int i = 0; i < gs->max_entities; i++)
+ for (size_t i = 0; i < gs->max_entities; i++)
{
if (gs->entities[i].exists)
entity_destroy(gs, &gs->entities[i]);
@@ -494,9 +501,11 @@ V2 grid_com(Entity* grid)
return cp_to_v2(cpBodyLocalToWorld(grid->body, cpBodyGetCenterOfGravity(grid->body)));
}
-V2 entity_pos(Entity* grid)
+V2 entity_pos(Entity* e)
{
- return cp_to_v2(cpBodyGetPosition(grid->body));
+ assert(!e->is_box);
+ // @Robust merge entity_pos with box_pos
+ return cp_to_v2(cpBodyGetPosition(e->body));
}
V2 grid_vel(Entity* grid)
{
@@ -579,7 +588,7 @@ void update_from(cpBody* body, struct BodyData* data)
cpBodySetAngularVelocity(body, data->angular_velocity);
}
-#define WRITE_VARNAMES // debugging feature horrible for network
+//#define WRITE_VARNAMES // debugging feature horrible for network
typedef struct SerState
{
char* bytes;
@@ -587,10 +596,11 @@ typedef struct SerState
size_t cursor; // points to next available byte, is the size of current message after serializing something
size_t max_size;
} SerState;
-void ser_var(SerState* ser, char* var_pointer, size_t var_size, const char* name)
+void ser_var(SerState* ser, char* var_pointer, size_t var_size, const char* name, const char* file, int line)
{
- const char* var_name = name;
#ifdef WRITE_VARNAMES
+ char var_name[512] = { 0 };
+ snprintf(var_name, 512, "%d%s", line, name); // can't have separator before the name, when comparing names skips past the digit
size_t var_name_len = strlen(var_name);
#endif
if (ser->serializing)
@@ -611,8 +621,8 @@ void ser_var(SerState* ser, char* var_pointer, size_t var_size, const char* name
{
#ifdef WRITE_VARNAMES
{
- char read_name[1024] = { 0 };
-
+ char read_name[512] = { 0 };
+
for (int i = 0; i < var_name_len; i++)
{
read_name[i] = ser->bytes[ser->cursor];
@@ -620,11 +630,18 @@ void ser_var(SerState* ser, char* var_pointer, size_t var_size, const char* name
assert(ser->cursor < ser->max_size);
}
read_name[var_name_len] = '\0';
- if (strcmp(read_name, var_name) != 0)
+ // advance past digits
+ char* read = read_name;
+ char* var = var_name;
+ while (*read >= '0' && *read <= '9')
+ read++;
+ while (*var >= '0' && *var <= '9')
+ var++;
+ if (strcmp(read, var) != 0)
{
- printf("%s:%d | Expected variable %s but got %sn\n", __FILE__, __LINE__, var_name, read_name);
+ printf("%s:%d | Expected variable %s but got %sn\n", file, line, var_name, read_name);
*(char*)NULL = 0;
- }
+}
}
#endif
for (int b = 0; b < var_size; b++)
@@ -636,7 +653,7 @@ void ser_var(SerState* ser, char* var_pointer, size_t var_size, const char* name
}
}
-#define SER_VAR_NAME(var_pointer, name) ser_var(ser, (char*)var_pointer, sizeof(var_pointer), name)
+#define SER_VAR_NAME(var_pointer, name) ser_var(ser, (char*)var_pointer, sizeof(var_pointer), name, __FILE__, __LINE__)
#define SER_VAR(var_pointer) SER_VAR_NAME(var_pointer, #var_pointer)
void ser_V2(SerState* ser, V2* var)
@@ -661,16 +678,20 @@ void ser_entityid(SerState* ser, EntityID* id)
void ser_inputframe(SerState* ser, struct InputFrame* i)
{
+ SER_VAR(&i->tick);
SER_VAR(&i->movement);
- SER_VAR(&i->inhabit);
- SER_VAR(&i->build);
+
+ SER_VAR(&i->seat_action);
+ ser_entityid(ser, &i->seat_to_inhabit);
+ SER_VAR(&i->hand_pos);
+
SER_VAR(&i->dobuild);
SER_VAR(&i->build_type);
SER_VAR(&i->build_rotation);
ser_entityid(ser, &i->grid_to_build_on);
}
-void ser_player(SerState* ser, struct Player* p)
+void ser_player(SerState* ser, Player* p)
{
SER_VAR(&p->connected);
if (p->connected)
@@ -680,7 +701,7 @@ void ser_player(SerState* ser, struct Player* p)
}
}
-void ser_entity(SerState* ser, struct GameState* gs, Entity* e)
+void ser_entity(SerState* ser, GameState* gs, Entity* e)
{
SER_VAR(&e->generation);
SER_VAR(&e->damage);
@@ -754,13 +775,15 @@ void ser_entity(SerState* ser, struct GameState* gs, Entity* e)
ser_entityid(ser, &e->prev_box);
SER_VAR(&e->compass_rotation);
SER_VAR(&e->thrust);
+ SER_VAR(&e->wanted_thrust);
SER_VAR(&e->energy_used);
+ ser_entityid(ser, &e->piloted_by);
}
}
void ser_server_to_client(SerState* ser, ServerToClient* s)
{
- struct GameState* gs = s->cur_gs;
+ GameState* gs = s->cur_gs;
int cur_next_entity = 0;
if (ser->serializing)
@@ -780,20 +803,22 @@ void ser_server_to_client(SerState* ser, ServerToClient* s)
ser_V2(ser, &gs->goldpos);
- for (int i = 0; i < MAX_PLAYERS; i++)
+ for (size_t i = 0; i < MAX_PLAYERS; i++)
{
ser_player(ser, &gs->players[i]);
}
if (ser->serializing)
{
- for (int i = 0; i < gs->cur_next_entity; i++)
+ bool entities_done = false;
+ for (size_t i = 0; i < gs->cur_next_entity; i++)
{
Entity* e = &gs->entities[i];
if (e->exists)
{
if (e->is_player)
{
+ SER_VAR(&entities_done);
SER_VAR(&i);
ser_entity(ser, gs, e);
}
@@ -801,35 +826,41 @@ void ser_server_to_client(SerState* ser, ServerToClient* s)
{
// serialize boxes always after bodies, so that by the time the boxes
// are loaded in the parent body is loaded in and can be referenced.
+ SER_VAR(&entities_done);
SER_VAR(&i);
ser_entity(ser, gs, e);
BOXES_ITER(gs, cur, e)
{
EntityID cur_id = get_id(gs, cur);
- SER_VAR_NAME(&cur_id.index, "&i");
+ assert(cur_id.index < gs->max_entities);
+ SER_VAR(&entities_done);
+ size_t the_index = (size_t)cur_id.index; // super critical. Type of &i is size_t. @Robust add debug info in serialization for what size the expected type is, maybe string nameof the type
+ SER_VAR_NAME(&the_index, "&i");
ser_entity(ser, gs, cur);
}
}
}
}
- int end_of_entities = -1;
- SER_VAR_NAME(&end_of_entities, "&i");
+ entities_done = true;
+ SER_VAR(&entities_done);
}
else
{
while (true)
{
- int next_index;
- SER_VAR_NAME(&next_index, "&i");
- if (next_index == -1)
+ bool entities_done = false;
+ SER_VAR(&entities_done);
+ if (entities_done)
break;
+ size_t next_index;
+ SER_VAR_NAME(&next_index, "&i");
assert(next_index < gs->max_entities);
Entity* e = &gs->entities[next_index];
e->exists = true;
ser_entity(ser, gs, e);
- gs->cur_next_entity = max(gs->cur_next_entity, next_index + 1);
+ gs->cur_next_entity = (unsigned int)max(gs->cur_next_entity, next_index + 1);
}
- for (int i = 0; i < gs->cur_next_entity; i++)
+ for (size_t i = 0; i < gs->cur_next_entity; i++)
{
Entity* e = &gs->entities[i];
if (!e->exists)
@@ -841,7 +872,7 @@ void ser_server_to_client(SerState* ser, ServerToClient* s)
}
}
-void into_bytes(struct ServerToClient* msg, char* bytes, size_t * out_len, size_t max_len)
+void into_bytes(struct ServerToClient* msg, char* bytes, size_t* out_len, size_t max_len)
{
assert(msg->cur_gs != NULL);
assert(msg != NULL);
@@ -889,7 +920,7 @@ static void closest_point_callback_func(cpShape* shape, cpContactPointSet* point
}
}
-Entity* closest_to_point_in_radius(struct GameState* gs, V2 point, float radius)
+Entity* closest_to_point_in_radius(GameState* gs, V2 point, float radius)
{
closest_to_point_in_radius_result = NULL;
closest_to_point_in_radius_result_largest_dist = 0.0f;
@@ -926,12 +957,12 @@ V2 thruster_force(Entity* box)
return V2scale(thruster_direction(box), -box->thrust * THRUSTER_FORCE);
}
-uint64_t tick(struct GameState* gs)
+uint64_t tick(GameState* gs)
{
return (uint64_t)floor(gs->time / ((double)TIMESTEP));
}
-void process(struct GameState* gs, float dt)
+void process(GameState* gs, float dt)
{
assert(gs->space != NULL);
@@ -956,70 +987,42 @@ void process(struct GameState* gs, float dt)
// update gold win condition
if (V2length(V2sub(cp_to_v2(cpBodyGetPosition(p->body)), gs->goldpos)) < GOLD_COLLECT_RADIUS)
{
- p->goldness += 0.1;
+ p->goldness += 0.1f;
p->spice_taken_away = 0.0f;
- gs->goldpos = (V2){ .x = hash11(gs->time) * 20.0f, .y = hash11(gs->time - 13.6f) * 20.0f };
- }
-
- if (get_entity(gs, p->currently_piloting_seat) == NULL)
- {
- p->currently_piloting_seat = (EntityID){ 0 };
+ gs->goldpos = (V2){ .x = hash11((float)gs->time) * 20.0f, .y = hash11((float)gs->time - 13.6f) * 20.0f };
}
- // @Todo do getting inside pilot seat
-#if 0
- if (p->input.inhabit)
+#if 1
+ if (player->input.seat_action)
{
- p->input.inhabit = false; // "handle" the input
- if (p->currently_inhabiting_index == -1)
+ player->input.seat_action = false; // "handle" the input
+ Entity* the_seat = get_entity(gs, p->currently_piloting_seat);
+ if (the_seat == NULL) // not piloting any seat
{
-
- // @Robust mask to only ship boxes of things the player can inhabit
cpPointQueryInfo query_info = { 0 };
- cpShape* result = cpSpacePointQueryNearest(gs->space, v2_to_cp(p->pos), 0.1f, cpShapeFilterNew(CP_NO_GROUP, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES), &query_info);
+ cpShape* result = cpSpacePointQueryNearest(gs->space, v2_to_cp(player->input.hand_pos), 0.1f, cpShapeFilterNew(CP_NO_GROUP, CP_ALL_CATEGORIES, BOXES), &query_info);
if (result != NULL)
{
- // result is assumed to be a box shape
- struct Grid* g = (struct Grid*)cpBodyGetUserData(cpShapeGetBody(result));
- int ship_to_inhabit = -1;
- for (int ii = 0; ii < MAX_GRIDS; ii++)
- {
- SKIPNULL(gs->grids[ii].body);
- if (&gs->grids[ii] == g)
- {
- ship_to_inhabit = ii;
- break;
- }
- }
-
- // don't allow inhabiting a grid that's already inhabited
- for (int ii = 0; ii < MAX_PLAYERS; ii++)
+ Entity* potential_seat = cp_shape_entity(result);
+ assert(potential_seat->is_box);
+ if (potential_seat->box_type == BoxCockpit)
{
- if (gs->players[ii].currently_inhabiting_index == ship_to_inhabit)
- {
- Log("Attempted to inhabit already taken ship\n");
- ship_to_inhabit = -1;
- }
- }
-
- if (ship_to_inhabit == -1)
- {
- Log("Couldn't find ship to inhabit even though point collision returned something\n");
- }
- else
- {
- p->currently_inhabiting_index = ship_to_inhabit;
+ p->currently_piloting_seat = get_id(gs, potential_seat);
+ potential_seat->piloted_by = get_id(gs, p);
}
}
else
{
- Log("No ship above player at point %f %f\n", p->pos.x, p->pos.y);
+ Log("No ship above player at point %f %f\n", player->input.hand_pos.x, player->input.hand_pos.y);
}
}
else
{
- p->vel = grid_vel(&gs->grids[p->currently_inhabiting_index]);
- p->currently_inhabiting_index = -1;
+ V2 pilot_seat_exit_spot = V2add(box_pos(the_seat), V2rotate((V2) { .x = BOX_SIZE }, rotangle(the_seat->compass_rotation)));
+ cpBodySetPosition(p->body, v2_to_cp(pilot_seat_exit_spot));
+ cpBodySetVelocity(p->body, v2_to_cp(player_vel(gs, p)));
+ the_seat->piloted_by = (EntityID){ 0 };
+ p->currently_piloting_seat = (EntityID){ 0 };
}
}
#endif
@@ -1032,46 +1035,38 @@ void process(struct GameState* gs, float dt)
{
player->input.movement = V2scale(V2normalize(player->input.movement), clamp(V2length(player->input.movement), 0.0f, 1.0f));
}
- cpBodyApplyForceAtWorldPoint(p->body, v2_to_cp(V2scale(player->input.movement, PLAYER_JETPACK_FORCE)), cpBodyGetPosition(p->body));
- p->spice_taken_away += movement_strength * dt * PLAYER_JETPACK_SPICE_PER_SECOND;
- // @Todo do pilot seat
-#if 0
+ Entity* piloting_seat = get_entity(gs, p->currently_piloting_seat);
+
+ if (piloting_seat == NULL)
{
- struct Grid* g = &gs->grids[p->currently_inhabiting_index];
- V2 target_new_pos = V2lerp(p->pos, grid_com(g), dt * 20.0f);
- p->vel = V2scale(V2sub(target_new_pos, p->pos), 1.0f / dt); // set vel correctly so newly built grids have the correct velocity copied from it
+ cpShapeSetFilter(p->shape, CP_SHAPE_FILTER_ALL);
+ cpBodyApplyForceAtWorldPoint(p->body, v2_to_cp(V2scale(player->input.movement, PLAYER_JETPACK_FORCE)), cpBodyGetPosition(p->body));
+ p->spice_taken_away += movement_strength * dt * PLAYER_JETPACK_SPICE_PER_SECOND;
+ }
+ else
+ {
+ cpShapeSetFilter(p->shape, CP_SHAPE_FILTER_NONE); // no collisions while going to pilot seat
+ cpBodySetPosition(p->body, v2_to_cp(box_pos(piloting_seat)));
// set thruster thrust from movement
{
- float energy_available = g->total_energy_capacity;
+ Entity* g = get_entity(gs, piloting_seat->shape_parent_entity);
V2 target_direction = { 0 };
- if (V2length(p->input.movement) > 0.0f)
+ if (V2length(player->input.movement) > 0.0f)
{
- target_direction = V2normalize(p->input.movement);
+ target_direction = V2normalize(player->input.movement);
}
- for (int ii = 0; ii < MAX_BOXES_PER_GRID; ii++)
+ BOXES_ITER(gs, cur, g)
{
- SKIPNULL(g->boxes[ii].shape);
- if (g->boxes[ii].type != BoxThruster)
+ if (cur->box_type != BoxThruster)
continue;
-
- float wanted_thrust = -V2dot(target_direction, thruster_direction(&g->boxes[ii]));
+ float wanted_thrust = -V2dot(target_direction, thruster_direction(cur));
wanted_thrust = clamp01(wanted_thrust);
-
- float needed_energy = wanted_thrust * THRUSTER_ENERGY_USED_PER_SECOND * dt;
- energy_available -= needed_energy;
-
- if (energy_available > 0.0f)
- g->boxes[ii].thrust = wanted_thrust;
- else
- g->boxes[ii].thrust = 0.0f;
+ cur->wanted_thrust = wanted_thrust;
}
}
- // cpBodyApplyForceAtWorldPoint(g->body, v2_to_cp(V2scale(p->input.movement, 5.0f)), v2_to_cp(grid_com(g)));
- // bigger the ship, the more efficient the spice usage
}
-#endif
}
#if 1 // building
@@ -1080,14 +1075,10 @@ void process(struct GameState* gs, float dt)
player->input.dobuild = false; // handle the input. if didn't do this, after destruction of hovered box, would try to build on its grid with grid_index...
cpPointQueryInfo info = { 0 };
- V2 world_build = player->input.build;
+ V2 world_build = player->input.hand_pos;
// @Robust sanitize this input so player can't build on any grid in the world
Entity* target_grid = get_entity(gs, player->input.grid_to_build_on);
- if (target_grid != NULL)
- {
- world_build = grid_local_to_world(target_grid, player->input.build);
- }
cpShape* nearest = cpSpacePointQueryNearest(gs->space, v2_to_cp(world_build), 0.01f, cpShapeFilterNew(CP_NO_GROUP, CP_ALL_CATEGORIES, BOXES), &info);
if (nearest != NULL)
{
@@ -1107,7 +1098,7 @@ void process(struct GameState* gs, float dt)
box_create(gs, new_box, new_grid, (V2) { 0 });
new_box->box_type = player->input.build_type;
new_box->compass_rotation = player->input.build_rotation;
- cpBodySetVelocity(new_grid->body, cpBodyGetVelocity(p->body));
+ cpBodySetVelocity(new_grid->body, v2_to_cp(player_vel(gs, p)));
}
else
{
@@ -1128,59 +1119,42 @@ void process(struct GameState* gs, float dt)
p->spice_taken_away = clamp01(p->spice_taken_away);
}
- // @Todo add thrust from thruster blocks
-#if 0
- for (int i = 0; i < MAX_GRIDS; i++)
- {
- SKIPNULL(gs->grids[i].body);
+ // process grids
+ for (size_t i = 0; i < gs->cur_next_entity; i++) {
+ Entity* e = &gs->entities[i];
+ if (!e->exists)
+ continue;
- struct Box* batteries[MAX_BOXES_PER_GRID] = { 0 };
- int cur_battery = 0;
- for (int ii = 0; ii < MAX_BOXES_PER_GRID; ii++)
+ if (e->is_box)
{
- SKIPNULL(gs->grids[i].boxes[ii].shape);
- if (gs->grids[i].boxes[ii].type == BoxBattery)
+ if (e->damage >= 1.0f)
{
- assert(cur_battery < MAX_BOXES_PER_GRID);
- batteries[cur_battery] = &gs->grids[i].boxes[ii];
- cur_battery++;
+ grid_remove_box(gs, get_entity(gs, e->shape_parent_entity), e);
}
}
- int batteries_len = cur_battery;
-
- float thruster_energy_consumption_per_second = 0.0f;
- for (int ii = 0; ii < MAX_BOXES_PER_GRID; ii++)
+ if (e->is_grid)
{
- SKIPNULL(gs->grids[i].boxes[ii].shape);
- if (gs->grids[i].boxes[ii].type == BoxThruster)
+
+ float thruster_energy_consumption_per_second = 0.0f;
+ BOXES_ITER(gs, cur, e)
{
- float energy_to_consume = gs->grids[i].boxes[ii].thrust * THRUSTER_ENERGY_USED_PER_SECOND * dt;
- struct Box* max_capacity_battery = NULL;
- float max_capacity_battery_energy_used = 1.0f;
- for (int iii = 0; iii < batteries_len; iii++)
+ if (cur->box_type == BoxThruster)
{
- if (batteries[iii]->energy_used < max_capacity_battery_energy_used)
+ float energy_to_consume = cur->wanted_thrust * THRUSTER_ENERGY_USED_PER_SECOND * dt;
+ cur->thrust = 0.0f;
+ BOXES_ITER(gs, possible_battery, e)
{
- max_capacity_battery = batteries[iii];
- max_capacity_battery_energy_used = batteries[iii]->energy_used;
+ if (possible_battery->box_type == BoxBattery && (1.0f - possible_battery->energy_used) > energy_to_consume)
+ {
+ possible_battery->energy_used += energy_to_consume;
+ cur->thrust = cur->wanted_thrust;
+ cpBodyApplyForceAtWorldPoint(e->body, v2_to_cp(thruster_force(cur)), v2_to_cp(box_pos(cur)));
+ }
}
}
-
- if (max_capacity_battery != NULL && (1.0f - max_capacity_battery->energy_used) > energy_to_consume)
- {
- max_capacity_battery->energy_used += energy_to_consume;
- cpBodyApplyForceAtWorldPoint(gs->grids[i].body, v2_to_cp(thruster_force(&gs->grids[i].boxes[ii])), v2_to_cp(box_pos(&gs->grids[i].boxes[ii])));
- }
}
}
-
- gs->grids[i].total_energy_capacity = 0.0f;
- for (int ii = 0; ii < batteries_len; ii++)
- {
- gs->grids[i].total_energy_capacity += 1.0f - batteries[ii]->energy_used;
- }
}
-#endif
cpSpaceStep(gs->space, dt);
}
\ No newline at end of file
diff --git a/loaded/cockpit.png b/loaded/cockpit.png
new file mode 100644
index 0000000..64c4d4c
Binary files /dev/null and b/loaded/cockpit.png differ
diff --git a/loaded/cockpit_used.png b/loaded/cockpit_used.png
new file mode 100644
index 0000000..533c81a
Binary files /dev/null and b/loaded/cockpit_used.png differ
diff --git a/main.c b/main.c
index fe09c56..9e7cd2a 100644
--- a/main.c
+++ b/main.c
@@ -4,27 +4,30 @@
#define SOKOL_IMPL
#define SOKOL_D3D11
-#include "sokol_gfx.h"
-#include "sokol_gp.h"
+#include
+#include // starting server thread
+
+#pragma warning ( disable: 33010 ) // this warning is so broken, doesn't understand assert()
+#pragma warning ( disable: 33010 ) // this warning is so broken, doesn't understand assert()
#include "sokol_app.h"
+#include "sokol_gfx.h"
#include "sokol_glue.h"
+#include "sokol_gp.h"
#include "sokol_time.h"
-#include
-#include // starting server thread
+#pragma warning ( default: 33010 )
+#pragma warning ( disable: 6262 ) // warning about using a lot of stack, lol that's how stb image is
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
-
#include "types.h"
-#include // errno error message on file open
#include
+#include // errno error message on file open
static struct GameState gs = { 0 };
static int myplayer = -1;
static bool right_mouse_down = false;
static bool keydown[SAPP_KEYCODE_MENU] = { 0 };
-typedef struct KeyPressed
-{
+typedef struct KeyPressed {
bool pressed;
uint64_t frame;
} KeyPressed;
@@ -32,8 +35,9 @@ static KeyPressed keypressed[SAPP_KEYCODE_MENU] = { 0 };
static V2 mouse_pos = { 0 };
static bool mouse_pressed = false;
static uint64_t mouse_pressed_frame = 0;
-static bool mouse_frozen = false; // @BeforeShip make this debug only thing
-static float funval = 0.0f; // easy to play with value controlled by left mouse button when held down @BeforeShip remove on release builds
+static bool mouse_frozen = false; // @BeforeShip make this debug only thing
+static float funval = 0.0f; // easy to play with value controlled by left mouse button when held
+// down @BeforeShip remove on release builds
static struct ClientToServer client_to_server = { 0 }; // buffer of inputs
static ENetHost* client;
static ENetPeer* peer;
@@ -43,15 +47,16 @@ static sg_image image_itemframe;
static sg_image image_itemframe_selected;
static sg_image image_thrusterburn;
static sg_image image_player;
+static sg_image image_cockpit_used;
static int cur_editing_boxtype = -1;
static int cur_editing_rotation = 0;
-static struct BoxInfo
-{
+static struct BoxInfo {
enum BoxType type;
const char* image_path;
sg_image image;
-} boxes[] = { // if added to here will show up in toolbar, is placeable
+} boxes[] = {
+ // if added to here will show up in toolbar, is placeable
{
.type = BoxHullpiece,
.image_path = "loaded/hullpiece.png",
@@ -63,13 +68,18 @@ static struct BoxInfo
{
.type = BoxBattery,
.image_path = "loaded/battery.png",
- } };
+ },
+ {
+ .type = BoxCockpit,
+ .image_path = "loaded/cockpit.png",
+ },
+};
const int boxes_len = sizeof(boxes) / sizeof(*boxes);
-struct BoxInfo boxinfo(enum BoxType type)
+struct BoxInfo
+ boxinfo(enum BoxType type)
{
- for (int i = 0; i < boxes_len; i++)
- {
+ for (int i = 0; i < boxes_len; i++) {
if (boxes[i].type == type)
return boxes[i];
}
@@ -77,7 +87,8 @@ struct BoxInfo boxinfo(enum BoxType type)
return (struct BoxInfo) { 0 };
}
-static sg_image load_image(const char* path)
+static sg_image
+load_image(const char* path)
{
sg_image to_return = sg_alloc_image();
@@ -87,65 +98,66 @@ static sg_image load_image(const char* path)
const int desired_channels = 4;
stbi_set_flip_vertically_on_load(true);
stbi_uc* image_data = stbi_load(path, &x, &y, &comp, desired_channels);
- if (!image_data)
- {
+ if (!image_data) {
fprintf(stderr, "Failed to load image: %s\n", stbi_failure_reason());
exit(-1);
}
- sg_init_image(to_return, &(sg_image_desc){
- .width = x,
- .height = y,
- .pixel_format = SG_PIXELFORMAT_RGBA8,
- .min_filter = SG_FILTER_NEAREST,
- .mag_filter = SG_FILTER_NEAREST,
- .data.subimage[0][0] = {
- .ptr = image_data,
- .size = (size_t)(x * y * desired_channels),
- }});
+ sg_init_image(to_return,
+ &(sg_image_desc) {.width = x,
+ .height = y,
+ .pixel_format = SG_PIXELFORMAT_RGBA8,
+ .min_filter = SG_FILTER_NEAREST,
+ .mag_filter = SG_FILTER_NEAREST,
+ .data.subimage[0][0] = {
+ .ptr = image_data,
+ .size = (size_t)(x * y * desired_channels),
+ } });
stbi_image_free(image_data);
return to_return;
}
-static void init(void)
+static void
+init(void)
{
- // @BeforeShip make all fprintf into logging to file, warning dialog grids on failure instead of exit(-1), replace the macros in sokol with this as well, like assert
+ // @BeforeShip make all fprintf into logging to file, warning dialog grids on
+ // failure instead of exit(-1), replace the macros in sokol with this as well,
+ // like assert
Entity* entity_data = malloc(sizeof * entity_data * MAX_ENTITIES);
initialize(&gs, entity_data, sizeof * entity_data * MAX_ENTITIES);
sg_desc sgdesc = { .context = sapp_sgcontext() };
sg_setup(&sgdesc);
- if (!sg_isvalid())
- {
+ if (!sg_isvalid()) {
fprintf(stderr, "Failed to create Sokol GFX context!\n");
exit(-1);
}
sgp_desc sgpdesc = { 0 };
sgp_setup(&sgpdesc);
- if (!sgp_is_valid())
- {
- fprintf(stderr, "Failed to create Sokol GP context: %s\n", sgp_get_error_message(sgp_get_last_error()));
+ if (!sgp_is_valid()) {
+ fprintf(stderr,
+ "Failed to create Sokol GP context: %s\n",
+ sgp_get_error_message(sgp_get_last_error()));
exit(-1);
}
// image loading
{
- for (int i = 0; i < boxes_len; i++)
- {
+ for (int i = 0; i < boxes_len; i++) {
boxes[i].image = load_image(boxes[i].image_path);
}
image_thrusterburn = load_image("loaded/thrusterburn.png");
image_itemframe = load_image("loaded/itemframe.png");
image_itemframe_selected = load_image("loaded/itemframe_selected.png");
image_player = load_image("loaded/player.png");
+ image_cockpit_used = load_image("loaded/cockpit_used.png");
}
// socket initialization
{
- if (enet_initialize() != 0)
- {
+ if (enet_initialize() != 0) {
fprintf(stderr, "An error occurred while initializing ENet.\n");
exit(-1);
}
@@ -154,9 +166,9 @@ static void init(void)
2 /* allow up 2 channels to be used, 0 and 1 */,
0 /* assume any amount of incoming bandwidth */,
0 /* assume any amount of outgoing bandwidth */);
- if (client == NULL)
- {
- fprintf(stderr,
+ if (client == NULL) {
+ fprintf(
+ stderr,
"An error occurred while trying to create an ENet client host.\n");
exit(-1);
}
@@ -166,20 +178,16 @@ static void init(void)
enet_address_set_host(&address, SERVER_ADDRESS);
address.port = SERVER_PORT;
peer = enet_host_connect(client, &address, 2, 0);
- if (peer == NULL)
- {
+ if (peer == NULL) {
fprintf(stderr,
"No available peers for initiating an ENet connection.\n");
exit(-1);
}
// the timeout is the third parameter here
- if (enet_host_service(client, &event, 5000) > 0 &&
- event.type == ENET_EVENT_TYPE_CONNECT)
- {
+ if (enet_host_service(client, &event, 5000) > 0 && event.type == ENET_EVENT_TYPE_CONNECT) {
Log("Connected\n");
}
- else
- {
+ else {
/* Either the 5 seconds are up or a disconnect event was */
/* received. Reset the peer in the event the 5 seconds */
/* had run out without any significant event. */
@@ -190,44 +198,54 @@ static void init(void)
}
}
-#define DeferLoop(start, end) for (int _i_ = ((start), 0); _i_ == 0; _i_ += 1, (end))
+#define DeferLoop(start, end) \
+ for (int _i_ = ((start), 0); _i_ == 0; _i_ += 1, (end))
#define transform_scope DeferLoop(sgp_push_transform(), sgp_pop_transform())
-static void draw_color_rect_centered(V2 center, float size)
+static void
+draw_color_rect_centered(V2 center, float size)
{
float halfbox = size / 2.0f;
sgp_draw_filled_rect(center.x - halfbox, center.y - halfbox, size, size);
}
-static void draw_texture_rectangle_centered(V2 center, V2 width_height)
+static void
+draw_texture_rectangle_centered(V2 center, V2 width_height)
{
V2 halfsize = V2scale(width_height, 0.5f);
- sgp_draw_textured_rect(center.x - halfsize.x, center.y - halfsize.y, width_height.x, width_height.y);
+ sgp_draw_textured_rect(center.x - halfsize.x,
+ center.y - halfsize.y,
+ width_height.x,
+ width_height.y);
}
-static void draw_texture_centered(V2 center, float size)
+static void
+draw_texture_centered(V2 center, float size)
{
draw_texture_rectangle_centered(center, (V2) { size, size });
}
-static void draw_circle(V2 point, float radius)
+static void
+draw_circle(V2 point, float radius)
{
#define POINTS 64
sgp_line lines[POINTS];
- for (int i = 0; i < POINTS; i++)
- {
+ for (int i = 0; i < POINTS; i++) {
float progress = (float)i / (float)POINTS;
float next_progress = (float)(i + 1) / (float)POINTS;
- lines[i].a = (V2){ .x = cos(progress * 2.0f * PI) * radius, .y = sin(progress * 2.0f * PI) * radius };
- lines[i].b = (V2){ .x = cos(next_progress * 2.0f * PI) * radius, .y = sin(next_progress * 2.0f * PI) * radius };
+ lines[i].a = (V2){ .x = cosf(progress * 2.0f * PI) * radius,
+ .y = sinf(progress * 2.0f * PI) * radius };
+ lines[i].b = (V2){ .x = cosf(next_progress * 2.0f * PI) * radius,
+ .y = sinf(next_progress * 2.0f * PI) * radius };
lines[i].a = V2add(lines[i].a, point);
lines[i].b = V2add(lines[i].b, point);
}
sgp_draw_lines(lines, POINTS);
}
-static Entity* myentity()
+static Entity*
+myentity()
{
if (myplayer == -1)
return NULL;
@@ -237,100 +255,107 @@ static Entity* myentity()
return to_return;
}
-static void ui(bool draw, float dt, float width, float height)
+static void
+ui(bool draw, float dt, float width, float height)
{
static float cur_opacity = 1.0f;
cur_opacity = lerp(cur_opacity, myentity() != NULL ? 1.0f : 0.0f, dt * 5.0f);
- if (cur_opacity <= 0.01f)
- {
+ if (cur_opacity <= 0.01f) {
return;
}
- // draw spice bar
if (draw)
- {
+ sgp_push_transform();
+
+ // if(draw) sgp_scale(1.0f, -1.0f);
+ // draw spice bar
+ if (draw) {
static float spice_taken_away = 0.5f;
- if (myentity() != NULL)
- {
+ if (myentity() != NULL) {
spice_taken_away = myentity()->spice_taken_away;
}
sgp_set_color(0.5f, 0.5f, 0.5f, cur_opacity);
- float margin = width * 0.1;
+ float margin = width * 0.1f;
float bar_width = width - margin * 2.0f;
sgp_draw_filled_rect(margin, 80.0f, bar_width, 30.0f);
sgp_set_color(1.0f, 1.0f, 1.0f, cur_opacity);
- sgp_draw_filled_rect(margin, 80.0f, bar_width * (1.0f - spice_taken_away), 30.0f);
+ sgp_draw_filled_rect(
+ margin, 80.0f, bar_width * (1.0f - spice_taken_away), 30.0f);
}
// draw item toolbar
{
- int itemframe_width = sg_query_image_info(image_itemframe).width * 2.0f;
- int itemframe_height = sg_query_image_info(image_itemframe).height * 2.0f;
- int total_width = itemframe_width * boxes_len;
- float item_width = itemframe_width * 0.75;
- float item_height = itemframe_height * 0.75;
+ float itemframe_width = (float)sg_query_image_info(image_itemframe).width * 2.0f;
+ float itemframe_height = (float)sg_query_image_info(image_itemframe).height * 2.0f;
+ float total_width = itemframe_width * boxes_len;
+ float item_width = itemframe_width * 0.75f;
+ float item_height = itemframe_height * 0.75f;
float item_offset_x = (itemframe_width - item_width) / 2.0f;
float item_offset_y = (itemframe_height - item_height) / 2.0f;
- float x = width / 2.0 - total_width / 2.0;
- float y = height - itemframe_height * 1.5;
- for (int i = 0; i < boxes_len; i++)
- {
- if (has_point((AABB) {
+ float x = width / 2.0f - total_width / 2.0f;
+ float y = height - itemframe_height * 1.5f;
+ for (int i = 0; i < boxes_len; i++) {
+ if (has_point(
+ (AABB) {
.x = x,
.y = y,
.width = itemframe_width,
.height = itemframe_height,
},
- mouse_pos) &&
- mouse_pressed)
- {
+ mouse_pos)
+ && mouse_pressed) {
// "handle" mouse pressed
mouse_pressed = false;
cur_editing_boxtype = i;
}
- if (draw)
+
+ if (draw) {
+ sgp_set_color(1.0f, 1.0f, 1.0f, cur_opacity);
+ if (cur_editing_boxtype == i) {
+ sgp_set_image(0, image_itemframe_selected);
+ }
+ else {
+ sgp_set_image(0, image_itemframe);
+ }
+ sgp_draw_textured_rect(x, y, itemframe_width, itemframe_height);
+ sgp_set_image(0, boxinfo((enum BoxType)i).image);
+ transform_scope
{
- sgp_set_color(1.0f, 1.0f, 1.0f, cur_opacity);
- if (cur_editing_boxtype == i)
- {
- sgp_set_image(0, image_itemframe_selected);
- }
- else
- {
- sgp_set_image(0, image_itemframe);
- }
- sgp_draw_textured_rect(x, y, itemframe_width, itemframe_height);
- sgp_set_image(0, boxinfo((enum BoxType)i).image);
- sgp_draw_textured_rect(x + item_offset_x, y + item_offset_y, item_width, item_height);
- sgp_reset_image(0);
+ float item_x = x + item_offset_x;
+ float item_y = y + item_offset_y;
+ sgp_scale_at(1.0f, -1.0f, item_x + item_width / 2.0f, item_y + item_height / 2.0f);
+ //sgp_scale(1.0f, -1.0f);
+ sgp_draw_textured_rect(item_x, item_y, item_width, item_height);
}
-
- x += itemframe_width;
+ sgp_reset_image(0);
+ }
+ x += itemframe_width;
}
}
+
+ if (draw)
+ sgp_pop_transform();
}
-static void frame(void)
+static void
+frame(void)
{
- int width = sapp_width(), height = sapp_height();
- float ratio = width / (float)height;
+ float width = (float)sapp_width(), height = (float)sapp_height();
+ float ratio = width / height;
double time = sapp_frame_count() * sapp_frame_duration();
- float dt = sapp_frame_duration();
+ float dt = (float)sapp_frame_duration();
// pressed input management
{
- for (int i = 0; i < SAPP_KEYCODE_MENU; i++)
- {
- if (keypressed[i].frame < sapp_frame_count())
- {
+ for (int i = 0; i < SAPP_KEYCODE_MENU; i++) {
+ if (keypressed[i].frame < sapp_frame_count()) {
keypressed[i].pressed = false;
}
}
- if (mouse_pressed_frame < sapp_frame_count())
- {
+ if (mouse_pressed_frame < sapp_frame_count()) {
mouse_pressed = false;
}
}
@@ -338,25 +363,24 @@ static void frame(void)
// networking
{
ENetEvent event;
- while (true)
- {
+ while (true) {
int enet_status = enet_host_service(client, &event, 0);
- if (enet_status > 0)
- {
- switch (event.type)
- {
- case ENET_EVENT_TYPE_CONNECT:
- {
+ if (enet_status > 0) {
+ switch (event.type) {
+ case ENET_EVENT_TYPE_CONNECT: {
Log("New client from host %x\n", event.peer->address.host);
break;
}
- case ENET_EVENT_TYPE_RECEIVE:
- {
- // @Robust @BeforeShip use some kind of serialization strategy that checks for out of bounds
- // and other validation instead of just casting to a struct
- // "Alignment of structure members can be different even among different compilers on the same platform, let alone different platforms."
- // ^^ need serialization strategy that accounts for this if multiple platforms is happening https://stackoverflow.com/questions/28455163/how-can-i-portably-send-a-c-struct-through-a-network-socket
+ case ENET_EVENT_TYPE_RECEIVE: {
+ // @Robust @BeforeShip use some kind of serialization strategy that
+ // checks for out of bounds and other validation instead of just
+ // casting to a struct "Alignment of structure members can be
+ // different even among different compilers on the same platform,
+ // let alone different platforms."
+ // ^^ need serialization strategy that accounts for this if multiple
+ // platforms is happening
+ // https://stackoverflow.com/questions/28455163/how-can-i-portably-send-a-c-struct-through-a-network-socket
ServerToClient msg = (ServerToClient){
.cur_gs = &gs,
};
@@ -367,21 +391,17 @@ static void frame(void)
break;
}
- case ENET_EVENT_TYPE_DISCONNECT:
- {
+ case ENET_EVENT_TYPE_DISCONNECT: {
fprintf(stderr, "Disconnected from server\n");
exit(-1);
break;
}
-
}
}
- else if (enet_status == 0)
- {
+ else if (enet_status == 0) {
break;
}
- else if (enet_status < 0)
- {
+ else if (enet_status < 0) {
fprintf(stderr, "Error receiving enet events: %d\n", enet_status);
break;
}
@@ -392,14 +412,15 @@ static void frame(void)
ui(false, dt, width, height); // handle events
V2 build_target_pos = { 0 };
float build_target_rotation = 0.0f;
- static V2 camera_pos = { 0 }; // keeps camera at same position after player death
- V2 world_mouse_pos = mouse_pos;
- struct BuildPreviewInfo
- {
+ static V2 camera_pos = {
+ 0
+ }; // keeps camera at same position after player death
+ V2 world_mouse_pos = mouse_pos; // processed later in scope
+ struct BuildPreviewInfo {
V2 grid_pos;
float grid_rotation;
- V2 pos;
} build_preview = { 0 };
+ V2 hand_pos = { 0 };
bool hand_at_arms_length = false;
{
// interpolate zoom
@@ -407,9 +428,7 @@ static void frame(void)
// calculate world position and camera
{
- if (myentity() != NULL)
- {
-
+ if (myentity() != NULL) {
camera_pos = entity_pos(myentity());
}
world_mouse_pos = V2sub(world_mouse_pos, (V2) { .x = width / 2.0f, .y = height / 2.0f });
@@ -420,106 +439,108 @@ static void frame(void)
// calculate build preview stuff
EntityID grid_to_build_on = (EntityID){ 0 };
- if (myentity() != NULL)
- {
- V2 hand_pos = V2sub(world_mouse_pos, entity_pos(myentity()));
+ if (myentity() != NULL) {
+ hand_pos = V2sub(world_mouse_pos, entity_pos(myentity()));
float hand_len = V2length(hand_pos);
- if (hand_len > MAX_HAND_REACH)
- {
+ if (hand_len > MAX_HAND_REACH) {
hand_at_arms_length = true;
hand_len = MAX_HAND_REACH;
}
- else
- {
+ else {
hand_at_arms_length = false;
}
hand_pos = V2scale(V2normalize(hand_pos), hand_len);
hand_pos = V2add(hand_pos, entity_pos(myentity()));
Entity* placing_grid = closest_to_point_in_radius(&gs, hand_pos, BUILD_BOX_SNAP_DIST_TO_SHIP);
- if (placing_grid == NULL)
- {
+ if (placing_grid == NULL) {
build_preview = (struct BuildPreviewInfo){
.grid_pos = hand_pos,
.grid_rotation = 0.0f,
- .pos = hand_pos,
};
}
- else
- {
+ else {
grid_to_build_on = get_id(&gs, placing_grid);
- V2 pos = grid_snapped_box_pos(placing_grid, hand_pos);
- build_preview = (struct BuildPreviewInfo){
- .grid_pos = entity_pos(placing_grid),
+ hand_pos = grid_snapped_box_pos(placing_grid, hand_pos);
+ build_preview = (struct BuildPreviewInfo){ .grid_pos = entity_pos(placing_grid),
.grid_rotation = entity_rotation(placing_grid),
- .pos = pos };
+ };
}
}
// Create and send input packet
{
- // @Robust accumulate total time and send input at rate like 20 hz, not every frame
+ // @Robust accumulate total time and send input at rate like 20 hz, not
+ // every frame
+ static size_t last_frame_id = 0;
struct InputFrame cur_input_frame = { 0 };
+ cur_input_frame.id = last_frame_id;
V2 input = (V2){
.x = (float)keydown[SAPP_KEYCODE_D] - (float)keydown[SAPP_KEYCODE_A],
.y = (float)keydown[SAPP_KEYCODE_W] - (float)keydown[SAPP_KEYCODE_S],
};
+ if (V2length(input) > 0.0)
+ input = V2normalize(input);
cur_input_frame.movement = input;
- cur_input_frame.inhabit = keypressed[SAPP_KEYCODE_G].pressed;
- if (mouse_pressed && cur_editing_boxtype != -1)
- {
+ cur_input_frame.seat_action = keypressed[SAPP_KEYCODE_G].pressed;
+ cur_input_frame.hand_pos = hand_pos;
+ if (mouse_pressed && cur_editing_boxtype != -1) {
cur_input_frame.dobuild = mouse_pressed;
cur_input_frame.build_type = cur_editing_boxtype;
cur_input_frame.build_rotation = cur_editing_rotation;
cur_input_frame.grid_to_build_on = grid_to_build_on;
- Entity* grid = get_entity(&gs, grid_to_build_on);
- if (grid != NULL)
- {
- cur_input_frame.build = grid_world_to_local(grid, build_preview.pos);
- }
- else
- {
- cur_input_frame.build = build_preview.pos;
- }
}
struct InputFrame latest = client_to_server.inputs[0];
- // if they're not the same
- if (
- !V2cmp(cur_input_frame.movement, latest.movement, 0.01f) ||
- cur_input_frame.inhabit != latest.inhabit ||
- cur_input_frame.dobuild != latest.dobuild ||
- cur_input_frame.grid_to_build_on.generation != latest.grid_to_build_on.generation ||
- cur_input_frame.grid_to_build_on.index != latest.grid_to_build_on.index ||
- !V2cmp(cur_input_frame.build, latest.build, 0.01f))
- {
- for (int i = 0; i < INPUT_BUFFER - 1; i++)
- {
- client_to_server.inputs[i + 1] = client_to_server.inputs[i];
+ // @Robust split this into separate lines and be very careful about testing for inequality
+
+ bool input_differs = false;
+ input_differs = input_differs || !V2equal(cur_input_frame.movement, latest.movement, 0.01f);
+
+ input_differs = input_differs || cur_input_frame.seat_action != latest.seat_action;
+ input_differs = input_differs || !entityids_same(cur_input_frame.seat_to_inhabit, latest.seat_to_inhabit);
+ input_differs = input_differs || !V2equal(cur_input_frame.hand_pos, latest.hand_pos, 0.01f);
+
+ input_differs = input_differs || cur_input_frame.dobuild != latest.dobuild;
+ input_differs = input_differs || cur_input_frame.build_type != latest.build_type;
+ input_differs = input_differs || cur_input_frame.build_rotation != latest.build_rotation;
+ input_differs = input_differs || !entityids_same(cur_input_frame.grid_to_build_on, latest.grid_to_build_on);
+
+ if (input_differs) {
+ struct InputFrame last_frame = client_to_server.inputs[0];
+ for (int i = 0; i < INPUT_BUFFER - 1; i++) {
+ struct InputFrame last_last_frame = last_frame;
+ last_frame = client_to_server.inputs[i + 1];
+ client_to_server.inputs[i + 1] = last_last_frame;
}
cur_input_frame.tick = tick(&gs);
client_to_server.inputs[0] = cur_input_frame;
+ last_frame_id += 1;
}
+ dbg_rect(client_to_server.inputs[0].hand_pos);
static double last_input_sent_time = 0.0;
- if (fabs(last_input_sent_time - time) > TIME_BETWEEN_INPUT_PACKETS)
- {
- ENetPacket* packet = enet_packet_create((void*)&client_to_server, sizeof(client_to_server), ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT);
+ if (fabs(last_input_sent_time - time) > TIME_BETWEEN_INPUT_PACKETS) {
+ ENetPacket* packet = enet_packet_create((void*)&client_to_server,
+ sizeof(client_to_server),
+ ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT);
enet_peer_send(peer, 0, packet);
last_input_sent_time = time;
}
}
- // @BeforeShip client side prediction and rollback to previous server authoritative state, then replay inputs
- // no need to store copies of game state, just player input frame to frame. Then know how many frames ago the server game state arrived, it's that easy!
- // process(&gs, (float)sapp_frame_duration());
+ // @BeforeShip client side prediction and rollback to previous server
+ // authoritative state, then replay inputs no need to store copies of game
+ // state, just player input frame to frame. Then know how many frames ago
+ // the server game state arrived, it's that easy! process(&gs,
+ // (float)sapp_frame_duration());
}
// drawing
{
- sgp_begin(width, height);
- sgp_viewport(0, 0, width, height);
+ sgp_begin((int)width, (int)height);
+ sgp_viewport(0, 0, (int)width, (int)height);
sgp_project(0.0f, width, 0.0f, height);
sgp_set_blend_mode(SGP_BLENDMODE_BLEND);
@@ -529,7 +550,8 @@ static void frame(void)
// sokol drawing library draw in world space
// world space coordinates are +Y up, -Y down. Like normal cartesian coords
- transform_scope{
+ transform_scope
+ {
sgp_translate(width / 2, height / 2);
sgp_scale_at(zoom, -zoom, 0.0f, 0.0f);
@@ -537,10 +559,9 @@ static void frame(void)
sgp_translate(-camera_pos.x, -camera_pos.y);
// hand reached limit circle
- if (myentity() != NULL)
- {
+ if (myentity() != NULL) {
static float hand_reach_alpha = 1.0f;
- hand_reach_alpha = lerp(hand_reach_alpha, hand_at_arms_length ? 1.0f : 0.0f, dt * 5.0);
+ hand_reach_alpha = lerp(hand_reach_alpha, hand_at_arms_length ? 1.0f : 0.0f, dt * 5.0f);
sgp_set_color(1.0f, 1.0f, 1.0f, hand_reach_alpha);
draw_circle(entity_pos(myentity()), MAX_HAND_REACH);
}
@@ -548,10 +569,8 @@ static void frame(void)
// stars
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
const int num = 30;
- for (int x = -num; x < num; x++)
- {
- for (int y = -num; y < num; y++)
- {
+ for (int x = -num; x < num; x++) {
+ for (int y = -num; y < num; y++) {
sgp_draw_point((float)x * 0.1f, (float)y * 0.1f);
}
}
@@ -559,30 +578,29 @@ static void frame(void)
float halfbox = BOX_SIZE / 2.0f;
// mouse
- if (mouse_frozen)
- {
+ if (mouse_frozen) {
sgp_set_color(1.0f, 0.0f, 0.0f, 0.5f);
sgp_draw_filled_rect(world_mouse_pos.x, world_mouse_pos.y, 0.1f, 0.1f);
}
// building preview
- if (cur_editing_boxtype != -1)
- {
- sgp_set_color(0.5f, 0.5f, 0.5f, (sin(time * 9.0f) + 1.0) / 3.0f + 0.2);
+ if (cur_editing_boxtype != -1) {
+ sgp_set_color(0.5f, 0.5f, 0.5f, (sinf((float)time * 9.0f) + 1.0f) / 3.0f + 0.2f);
transform_scope
{
-
sgp_set_image(0, boxinfo(cur_editing_boxtype).image);
- sgp_rotate_at(build_preview.grid_rotation + rotangle(cur_editing_rotation), build_preview.pos.x, build_preview.pos.y);
- draw_texture_centered(build_preview.pos, BOX_SIZE);
- // drawbox(build_preview.pos, build_preview.grid_rotation, 0.0f, cur_editing_boxtype, cur_editing_rotation);
+ sgp_rotate_at(build_preview.grid_rotation + rotangle(cur_editing_rotation),
+ hand_pos.x,
+ hand_pos.y);
+ draw_texture_centered(hand_pos, BOX_SIZE);
+ // drawbox(hand_pos, build_preview.grid_rotation, 0.0f,
+ // cur_editing_boxtype, cur_editing_rotation);
sgp_reset_image(0);
}
}
- for (int i = 0; i < gs.cur_next_entity; i++)
- {
+ for (size_t i = 0; i < gs.cur_next_entity; i++) {
Entity* e = &gs.entities[i];
if (!e->exists)
continue;
@@ -594,45 +612,51 @@ static void frame(void)
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
// debug draw force vectors for thrusters
#if 0
- {
- if (b->type == BoxThruster)
- {
- dbg_rect(box_pos(b));
- dbg_line(box_pos(b), V2add(box_pos(b), V2scale(thruster_force(b), -1.0f)));
- }
- }
+ {
+ if (b->type == BoxThruster)
+ {
+ dbg_rect(box_pos(b));
+ dbg_line(box_pos(b), V2add(box_pos(b), V2scale(thruster_force(b), -1.0f)));
+ }
+ }
#endif
- if (b->box_type == BoxBattery)
- {
+ if (b->box_type == BoxBattery) {
float cur_alpha = sgp_get_color().a;
Color from = WHITE;
Color to = colhex(255, 0, 0);
Color result = Collerp(from, to, b->energy_used);
sgp_set_color(result.r, result.g, result.b, cur_alpha);
}
- transform_scope {
-
- sgp_rotate_at(entity_rotation(g) + rotangle(b->compass_rotation), box_pos(b).x, box_pos(b).y);
+ transform_scope
+ {
+ sgp_rotate_at(entity_rotation(g) + rotangle(b->compass_rotation),
+ box_pos(b).x,
+ box_pos(b).y);
- if (b->box_type == BoxThruster)
- {
+ if (b->box_type == BoxThruster) {
transform_scope
{
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
sgp_set_image(0, image_thrusterburn);
- // float scaling = 1.0 + (hash11(time*3.0)/2.0)*lerp(0.0, 0.07, b->thrust);
- // printf("%f\n", b->thrust);
- float scaling = 0.95 + lerp(0.0, 0.3, b->thrust);
+ // float scaling = 1.0 + (hash11(time*3.0)/2.0)*lerp(0.0,
+ // 0.07, b->thrust); printf("%f\n", b->thrust);
+ float scaling = 0.95f + lerp(0.0f, 0.3f, b->thrust);
// float scaling = 1.1;
// sgp_translate(-(scaling*BOX_SIZE - BOX_SIZE), 0.0);
// sgp_scale(scaling, 1.0);
- sgp_scale_at(scaling, 1.0, box_pos(b).x, box_pos(b).y);
+ sgp_scale_at(scaling, 1.0f, box_pos(b).x, box_pos(b).y);
draw_texture_centered(box_pos(b), BOX_SIZE);
sgp_reset_image(0);
}
}
+ sg_image img = boxinfo(b->box_type).image;
+ if (b->box_type == BoxCockpit)
+ {
+ if (get_entity(&gs, b->piloted_by) != NULL)
+ img = image_cockpit_used;
+ }
- sgp_set_image(0, boxinfo(b->box_type).image);
+ sgp_set_image(0, img);
draw_texture_centered(box_pos(b), BOX_SIZE);
sgp_reset_image(0);
@@ -645,61 +669,33 @@ static void frame(void)
V2 to = V2add(grid_com(g), vel);
sgp_draw_line(grid_com(g).x, grid_com(g).y, to.x, to.y);
}
- if (e->is_player)
- {
- transform_scope
- {
- sgp_rotate_at(entity_rotation(e), entity_pos(e).x, entity_pos(e).y);
- sgp_set_color(1.0f, 0.5f, 0.5f, 1.0f);
- sgp_set_image(0, image_player);
- draw_texture_rectangle_centered(entity_pos(e), PLAYER_SIZE);
- sgp_reset_image(0);
- }
- }
- }
- }
-
- // player
- for (int i = 0; i < MAX_PLAYERS; i++)
- {
- struct Player* player = &gs.players[i];
- if (!player->connected)
- continue;
- Entity* p = get_entity(&gs, player->entity);
- if (p == NULL)
- continue;
- static float opacities[MAX_PLAYERS] = { 1.0f };
- static V2 positions[MAX_PLAYERS] = { 0 };
- opacities[i] = lerp(opacities[i], p != NULL ? 1.0f : 0.1f, dt * 7.0f);
- Color col_to_draw = Collerp(WHITE, GOLD, p->goldness);
- col_to_draw.a = opacities[i];
-
- if (p != NULL)
- {
- positions[i] = entity_pos(p);
- }
-
- set_color(col_to_draw);
- transform_scope
- {
- float psize = 0.1f;
- sgp_draw_filled_rect(positions[i].x - psize / 2.0f, positions[i].y - psize / 2.0f, psize, psize);
+ if (e->is_player && get_entity(&gs, e->currently_piloting_seat) == NULL) {
+
+ transform_scope
+ {
+ sgp_rotate_at(entity_rotation(e), entity_pos(e).x, entity_pos(e).y);
+ sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
+ sgp_set_image(0, image_player);
+ draw_texture_rectangle_centered(entity_pos(e), PLAYER_SIZE);
+ sgp_reset_image(0);
+ }
+ }
}
- }
+ // gold target
+ set_color(GOLD);
+ sgp_draw_filled_rect(gs.goldpos.x, gs.goldpos.y, 0.1f, 0.1f);
- // gold target
- set_color(GOLD);
- sgp_draw_filled_rect(gs.goldpos.x, gs.goldpos.y, 0.1f, 0.1f);
+ sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
+ dbg_drawall();
+ } // world space transform end
- sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
- dbg_drawall();
}
// UI drawn in screen space
ui(true, dt, width, height);
sg_pass_action pass_action = { 0 };
- sg_begin_default_pass(&pass_action, width, height);
+ sg_begin_default_pass(&pass_action, (int)width, (int)height);
sgp_flush();
sgp_end();
sg_end_pass();
@@ -717,38 +713,31 @@ void cleanup(void)
void event(const sapp_event* e)
{
- switch (e->type)
- {
+ switch (e->type) {
case SAPP_EVENTTYPE_KEY_DOWN:
- if (e->key_code == SAPP_KEYCODE_T)
- {
+ if (e->key_code == SAPP_KEYCODE_T) {
mouse_frozen = !mouse_frozen;
}
- if (e->key_code == SAPP_KEYCODE_R)
- {
+ if (e->key_code == SAPP_KEYCODE_R) {
cur_editing_rotation += 1;
cur_editing_rotation %= RotationLast;
}
int key_num = e->key_code - SAPP_KEYCODE_0;
int target_box = key_num - 1;
- if (target_box < BoxLast)
- {
+ if (target_box < BoxLast) {
cur_editing_boxtype = target_box;
}
- if (!mouse_frozen)
- {
+ if (!mouse_frozen) {
keydown[e->key_code] = true;
- if (keypressed[e->key_code].frame == 0)
- {
+ if (keypressed[e->key_code].frame == 0) {
keypressed[e->key_code].pressed = true;
keypressed[e->key_code].frame = e->frame_count;
}
}
break;
case SAPP_EVENTTYPE_KEY_UP:
- if (!mouse_frozen)
- {
+ if (!mouse_frozen) {
keydown[e->key_code] = false;
keypressed[e->key_code].pressed = false;
@@ -760,34 +749,28 @@ void event(const sapp_event* e)
zoom_target = clamp(zoom_target, 0.5f, 900.0f);
break;
case SAPP_EVENTTYPE_MOUSE_DOWN:
- if (e->mouse_button == SAPP_MOUSEBUTTON_LEFT)
- {
+ if (e->mouse_button == SAPP_MOUSEBUTTON_LEFT) {
mouse_pressed = true;
mouse_pressed_frame = e->frame_count;
}
- if (e->mouse_button == SAPP_MOUSEBUTTON_RIGHT)
- {
+ if (e->mouse_button == SAPP_MOUSEBUTTON_RIGHT) {
right_mouse_down = true;
}
break;
case SAPP_EVENTTYPE_MOUSE_UP:
- if (e->mouse_button == SAPP_MOUSEBUTTON_LEFT)
- {
+ if (e->mouse_button == SAPP_MOUSEBUTTON_LEFT) {
mouse_pressed = false;
mouse_pressed_frame = 0;
}
- if (e->mouse_button == SAPP_MOUSEBUTTON_RIGHT)
- {
+ if (e->mouse_button == SAPP_MOUSEBUTTON_RIGHT) {
right_mouse_down = false;
}
break;
case SAPP_EVENTTYPE_MOUSE_MOVE:
- if (!mouse_frozen)
- {
+ if (!mouse_frozen) {
mouse_pos = (V2){ .x = e->mouse_x, .y = e->mouse_y };
}
- if (right_mouse_down)
- {
+ if (right_mouse_down) {
funval += e->mouse_dx;
Log("Funval %f\n", funval);
}
@@ -795,10 +778,10 @@ void event(const sapp_event* e)
}
}
-sapp_desc sokol_main(int argc, char* argv[])
+sapp_desc
+sokol_main(int argc, char* argv[])
{
- if (argc > 1)
- {
+ if (argc > 1) {
_beginthread(server, 0, NULL);
}
(void)argv;
diff --git a/server.c b/server.c
index 172a14d..b0f4da1 100644
--- a/server.c
+++ b/server.c
@@ -60,7 +60,7 @@ void server(void* data)
ENetEvent event;
uint64_t last_processed_time = stm_now();
float total_time = 0.0f;
- uint64_t player_to_latest_tick_processed[MAX_PLAYERS] = { 0 };
+ size_t player_to_latest_id_processed[MAX_PLAYERS] = { 0 };
while (true)
{
// @Speed handle enet messages and simulate gamestate in parallel, then sync... must clone gamestate for this
@@ -123,36 +123,36 @@ void server(void* data)
struct ClientToServer received = { 0 };
memcpy(&received, event.packet->data, length);
int64_t player_slot = (int64_t)event.peer->data;
- uint64_t latest_tick = player_to_latest_tick_processed[player_slot];
+ size_t latest_id = player_to_latest_id_processed[player_slot];
- if (received.inputs[0].tick > latest_tick)
+ if (received.inputs[0].id > latest_id)
{
for (int i = INPUT_BUFFER - 1; i >= 0; i--)
{
if (received.inputs[i].tick == 0) // empty input
continue;
- if (received.inputs[i].tick <= latest_tick)
+ if (received.inputs[i].id <= latest_id)
continue; // don't reprocess inputs already processed
struct InputFrame cur_input = received.inputs[i];
gs.players[player_slot].input.movement = cur_input.movement;
+ gs.players[player_slot].input.hand_pos = cur_input.hand_pos;
// for these "event" inputs, only modify the current input if the event is true.
// while processing the gamestate, will mark it as false once processed. This
// prevents setting the event input to false before it's been processed.
- if (cur_input.inhabit)
+ if (cur_input.seat_action)
{
- gs.players[player_slot].input.inhabit = cur_input.inhabit;
+ gs.players[player_slot].input.seat_action = cur_input.seat_action;
}
if (cur_input.dobuild)
{
gs.players[player_slot].input.grid_to_build_on = cur_input.grid_to_build_on;
- gs.players[player_slot].input.build = cur_input.build;
gs.players[player_slot].input.dobuild = cur_input.dobuild;
gs.players[player_slot].input.build_type = cur_input.build_type;
gs.players[player_slot].input.build_rotation = cur_input.build_rotation;
}
}
- player_to_latest_tick_processed[player_slot] = received.inputs[0].tick;
+ player_to_latest_id_processed[player_slot] = received.inputs[0].id;
}
}
@@ -165,7 +165,7 @@ void server(void* data)
case ENET_EVENT_TYPE_DISCONNECT:
{
- int player_index = (int64_t)event.peer->data;
+ int player_index = (int)(int64_t)event.peer->data;
Log("%" PRId64 " disconnected player index %d.\n", (int64_t)event.peer->data, player_index);
gs.players[player_index].connected = false;
// box_destroy(&gs.players[player_index].box);
@@ -203,7 +203,7 @@ void server(void* data)
to_send.cur_gs = &gs;
to_send.your_player = (int)(int64_t)server->peers[i].data;
- int len = 0;
+ size_t len = 0;
into_bytes(&to_send, bytes_buffer, &len, MAX_BYTES_SIZE);
ENetPacket* gamestate_packet = enet_packet_create((void*)bytes_buffer, len, ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT);
diff --git a/types.h b/types.h
index 01b8b5f..cdce4e8 100644
--- a/types.h
+++ b/types.h
@@ -17,7 +17,7 @@
#define TIMESTEP (1.0f / 60.0f) // not required to simulate at this, but this defines what tick the game is on
#define TIME_BETWEEN_INPUT_PACKETS (1.0f / 20.0f)
#define SERVER_PORT 2551
-#define INPUT_BUFFER 4
+#define INPUT_BUFFER 6
// must make this header and set the target address, just #define SERVER_ADDRESS "127.0.0.1"
#include "ipsettings.h" // don't leak IP!
@@ -36,7 +36,7 @@ void sgp_set_color(float, float, float, float);
// somehow automatically or easily cast to floats
typedef struct sgp_vec2
{
- float x, y;
+ float x, y;
} sgp_vec2;
typedef sgp_vec2 sgp_point;
@@ -68,102 +68,117 @@ typedef sgp_point P2;
enum BoxType
{
- BoxHullpiece,
- BoxThruster,
- BoxBattery,
- BoxCockpit,
- BoxLast,
+ BoxHullpiece,
+ BoxThruster,
+ BoxBattery,
+ BoxCockpit,
+ BoxLast,
};
enum CompassRotation
{
- Right,
- Down,
- Left,
- Up,
- RotationLast,
+ Right,
+ Down,
+ Left,
+ Up,
+ RotationLast,
};
// when generation is 0, invalid ID
typedef struct
{
- unsigned int generation; // if 0 then EntityID points to nothing, generation >= 1
- unsigned int index; // index into the entity arena
+ unsigned int generation; // if 0 then EntityID points to nothing, generation >= 1
+ unsigned int index; // index into the entity arena
} EntityID;
+static bool entityids_same(EntityID a, EntityID b)
+{
+ return (a.generation == b.generation) && (a.index == b.index);
+}
+
+// when updated, must update serialization, AND comparison
+// function in main.c
struct InputFrame
{
- uint64_t tick;
- V2 movement;
- bool inhabit;
+ uint64_t tick;
+ size_t id; // each input has unique, incrementing, I.D, so server doesn't double process inputs. Inputs to server should be ordered from 0-max like biggest id-smallest. This is done so if packet loss server still processes input
+ V2 movement;
- // if grid_index != -1, this is in local coordinates to the grid
- V2 build;
- bool dobuild;
- enum BoxType build_type;
- enum CompassRotation build_rotation;
- EntityID grid_to_build_on;
+ bool seat_action;
+ EntityID seat_to_inhabit;
+ V2 hand_pos; // world coords, world star!
+
+ bool dobuild;
+ enum BoxType build_type;
+ enum CompassRotation build_rotation;
+ EntityID grid_to_build_on;
};
typedef struct Entity
{
- bool exists;
- EntityID next_free_entity;
- unsigned int generation;
-
- float damage; // used by box and player
- cpBody *body; // used by grid, player, and box
- cpShape *shape; // must be a box so shape_size can be set appropriately, and serialized
-
- // for serializing the shape
- EntityID shape_parent_entity; // can't be zero if shape is nonzero
- V2 shape_size;
-
- // player
- bool is_player;
- EntityID currently_piloting_seat;
- float spice_taken_away; // at 1.0, out of spice
- float goldness; // how much the player is a winner
-
- // grids
- bool is_grid;
- float total_energy_capacity;
- EntityID boxes;
-
- // boxes
- bool is_box;
- enum BoxType box_type;
- EntityID next_box;
- EntityID prev_box; // doubly linked so can remove in middle of chain
- enum CompassRotation compass_rotation;
- float thrust; // must be between 0 and 1
- float energy_used; // battery
+ bool exists;
+ EntityID next_free_entity;
+ unsigned int generation;
+
+ float damage; // used by box and player
+ cpBody* body; // used by grid, player, and box
+ cpShape* shape; // must be a box so shape_size can be set appropriately, and serialized
+
+
+ // for serializing the shape
+ // @Robust remove shape_parent_entity from this struct, use the shape's body to figure out
+ // what the shape's parent entity is
+ EntityID shape_parent_entity; // can't be zero if shape is nonzero
+ V2 shape_size;
+
+ // player
+ bool is_player;
+ EntityID currently_piloting_seat;
+ float spice_taken_away; // at 1.0, out of spice
+ float goldness; // how much the player is a winner
+
+ // grids
+ bool is_grid;
+ float total_energy_capacity;
+ EntityID boxes;
+
+ // boxes
+ bool is_box;
+ enum BoxType box_type;
+ EntityID next_box;
+ EntityID prev_box; // doubly linked so can remove in middle of chain
+ enum CompassRotation compass_rotation;
+ float wanted_thrust; // the thrust command applied to the thruster
+ float thrust; // the actual thrust it can provide based on energy sources in the grid
+ float energy_used; // battery
+ EntityID piloted_by;
} Entity;
+typedef struct Player
+{
+ bool connected;
+ EntityID entity;
+ struct InputFrame input;
+} Player;
// gotta update the serialization functions when this changes
typedef struct GameState
{
- cpSpace *space;
+ cpSpace* space;
- double time;
+ double time;
- V2 goldpos;
+ V2 goldpos;
- struct Player
- {
- bool connected;
- EntityID entity;
- struct InputFrame input;
- } players[MAX_PLAYERS];
+ Player players[MAX_PLAYERS];
- // Entity arena
- // ent:ity 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;
- unsigned int max_entities; // maximum number of entities possible in the entities list
- unsigned int cur_next_entity; // next entity to pass on request of a new entity if the free list is empty
- EntityID free_list;
+ // Entity arena
+ // ent:ity 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;
+ unsigned int max_entities; // maximum number of entities possible in the entities list
+ unsigned int cur_next_entity; // next entity to pass on request of a new entity if the free list is empty
+ EntityID free_list;
} GameState;
#define PI 3.14159f
@@ -171,80 +186,80 @@ typedef struct GameState
// returns in radians
static float rotangle(enum CompassRotation rot)
{
- switch (rot)
- {
- case Right:
- return 0.0f;
- break;
- case Down:
- return -PI / 2.0f;
- break;
- case Left:
- return -PI;
- break;
- case Up:
- return -3.0f * PI / 2.0f;
- break;
- default:
- Log("Unknown rotation %d\n", rot);
- return -0.0f;
- break;
- }
+ switch (rot)
+ {
+ case Right:
+ return 0.0f;
+ break;
+ case Down:
+ return -PI / 2.0f;
+ break;
+ case Left:
+ return -PI;
+ break;
+ case Up:
+ return -3.0f * PI / 2.0f;
+ break;
+ default:
+ Log("Unknown rotation %d\n", rot);
+ return -0.0f;
+ break;
+ }
}
typedef struct ServerToClient
{
- struct GameState *cur_gs;
- int your_player;
+ struct GameState* cur_gs;
+ int your_player;
} ServerToClient;
struct ClientToServer
{
- struct InputFrame inputs[INPUT_BUFFER];
+ struct InputFrame inputs[INPUT_BUFFER];
};
// server
-void server(void *data); // data parameter required from thread api...
+void server(void* data); // data parameter required from thread api...
// gamestate
-void initialize(struct GameState *gs, void *entity_arena, int entity_arena_size);
-void destroy(struct GameState *gs);
-void process(struct GameState *gs, float dt); // does in place
-Entity *closest_to_point_in_radius(struct GameState *gs, V2 point, float radius);
-uint64_t tick(struct GameState *gs);
-void into_bytes(struct ServerToClient *gs, char *out_bytes, size_t * out_len, size_t max_len);
-void from_bytes(struct ServerToClient *gs, char *bytes, size_t max_len);
+void initialize(struct GameState* gs, void* entity_arena, size_t entity_arena_size);
+void destroy(struct GameState* gs);
+void process(struct GameState* gs, float dt); // does in place
+Entity* closest_to_point_in_radius(struct GameState* gs, V2 point, float radius);
+uint64_t tick(struct GameState* gs);
+void into_bytes(struct ServerToClient* gs, char* out_bytes, size_t* out_len, size_t max_len);
+void from_bytes(struct ServerToClient* gs, char* bytes, size_t max_len);
// entities
-Entity *get_entity(struct GameState *gs, EntityID id);
-Entity *new_entity(struct GameState *gs);
-EntityID get_id(struct GameState *gs, Entity *e);
-V2 entity_pos(Entity *e);
-void entity_set_pos(Entity *e, V2 pos);
-float entity_rotation(Entity *e);
+Entity* get_entity(struct GameState* gs, EntityID id);
+Entity* new_entity(struct GameState* gs);
+EntityID get_id(struct GameState* gs, Entity* e);
+V2 entity_pos(Entity* e);
+void entity_set_pos(Entity* e, V2 pos);
+float entity_rotation(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)
// player
-void player_destroy(struct Player *p);
-void player_new(struct Player *p);
+void player_destroy(struct Player* p);
+void player_new(struct Player* p);
// grid
-void grid_create(struct GameState *gs, Entity *e);
-void box_create(struct GameState *gs, Entity *new_box, Entity *grid, V2 pos);
-V2 grid_com(Entity *grid);
-V2 grid_vel(Entity *grid);
-V2 grid_local_to_world(Entity *grid, V2 local);
-V2 grid_world_to_local(Entity *grid, V2 world);
-V2 grid_snapped_box_pos(Entity *grid, V2 world); // returns the snapped pos in world coords
-float entity_angular_velocity(Entity *grid);
-V2 entity_shape_pos(Entity *box);
-V2 box_pos(Entity *box);
-float box_rotation(Entity *box);
+void grid_create(struct GameState* gs, Entity* e);
+void box_create(struct GameState* gs, Entity* new_box, Entity* grid, V2 pos);
+V2 grid_com(Entity* grid);
+V2 grid_vel(Entity* grid);
+V2 grid_local_to_world(Entity* grid, V2 local);
+V2 grid_world_to_local(Entity* grid, V2 world);
+V2 grid_snapped_box_pos(Entity* grid, V2 world); // returns the snapped pos in world coords
+float entity_angular_velocity(Entity* grid);
+V2 entity_shape_pos(Entity* box);
+V2 box_pos(Entity* box); // returns in world coords
+float box_rotation(Entity* box);
// thruster
-V2 thruster_direction(Entity *box);
-V2 thruster_force(Entity *box);
+V2 thruster_direction(Entity* box);
+V2 thruster_force(Entity* box);
// debug draw
void dbg_drawall();
@@ -260,154 +275,154 @@ void dbg_rect(V2 center);
typedef struct AABB
{
- float x, y, width, height;
+ float x, y, width, height;
} AABB;
static bool has_point(AABB aabb, V2 point)
{
- return point.x > aabb.x && point.x < aabb.x + aabb.width && point.y > aabb.y && point.y < aabb.y + aabb.height;
+ return point.x > aabb.x && point.x < aabb.x + aabb.width && point.y > aabb.y && point.y < aabb.y + aabb.height;
}
static V2 V2add(V2 a, V2 b)
{
- return (V2){
- .x = a.x + b.x,
- .y = a.y + b.y,
- };
+ return (V2) {
+ .x = a.x + b.x,
+ .y = a.y + b.y,
+ };
}
static V2 V2scale(V2 a, float f)
{
- return (V2){
- .x = a.x * f,
- .y = a.y * f,
- };
+ return (V2) {
+ .x = a.x * f,
+ .y = a.y * f,
+ };
}
static float V2length(V2 v)
{
- return sqrtf(v.x * v.x + v.y * v.y);
+ return sqrtf(v.x * v.x + v.y * v.y);
}
static V2 V2normalize(V2 v)
{
- return V2scale(v, 1.0f / V2length(v));
+ return V2scale(v, 1.0f / V2length(v));
}
static float V2dot(V2 a, V2 b)
{
- return a.x * b.x + a.y * b.y;
+ return a.x * b.x + a.y * b.y;
}
static float V2projectvalue(V2 vec, V2 onto)
{
- float length_onto = V2length(onto);
- return V2dot(vec, onto) / (length_onto * length_onto);
+ float length_onto = V2length(onto);
+ return V2dot(vec, onto) / (length_onto * length_onto);
}
static V2 V2project(V2 vec, V2 onto)
{
- return V2scale(onto, V2projectvalue(vec, onto));
+ return V2scale(onto, V2projectvalue(vec, onto));
}
static V2 V2rotate(V2 vec, float theta)
{
- return (V2){
- .x = vec.x * cosf(theta) - vec.y * sinf(theta),
- .y = vec.x * sinf(theta) + vec.y * cosf(theta),
- };
+ return (V2) {
+ .x = vec.x * cosf(theta) - vec.y * sinf(theta),
+ .y = vec.x * sinf(theta) + vec.y * cosf(theta),
+ };
}
// also known as atan2
static float V2angle(V2 vec)
{
- return atan2f(vec.y, vec.x);
+ return atan2f(vec.y, vec.x);
}
static V2 V2sub(V2 a, V2 b)
{
- return (V2){
- .x = a.x - b.x,
- .y = a.y - b.y,
- };
+ return (V2) {
+ .x = a.x - b.x,
+ .y = a.y - b.y,
+ };
}
-static bool V2cmp(V2 a, V2 b, float eps)
+static bool V2equal(V2 a, V2 b, float eps)
{
- return V2length(V2sub(a, b)) < eps;
+ return V2length(V2sub(a, b)) < eps;
}
static inline float clamp01(float f)
{
- return fmaxf(0.0f, fminf(f, 1.0f));
+ return fmaxf(0.0f, fminf(f, 1.0f));
}
static inline float clamp(float f, float minimum, float maximum)
{
- if (f < minimum)
- return minimum;
- if (f > maximum)
- return maximum;
- return f;
+ if (f < minimum)
+ return minimum;
+ if (f > maximum)
+ return maximum;
+ return f;
}
static float fract(float f)
{
- return f - floorf(f);
+ return f - floorf(f);
}
static float lerp(float a, float b, float f)
{
- return a * (1.0f - f) + (b * f);
+ return a * (1.0f - f) + (b * f);
}
static V2 V2lerp(V2 a, V2 b, float factor)
{
- V2 to_return = {0};
- to_return.x = lerp(a.x, b.x, factor);
- to_return.y = lerp(a.y, b.y, factor);
+ V2 to_return = { 0 };
+ to_return.x = lerp(a.x, b.x, factor);
+ to_return.y = lerp(a.y, b.y, factor);
- return to_return;
+ return to_return;
}
// for random generation
static float hash11(float p)
{
- p = fract(p * .1031f);
- p *= p + 33.33f;
- p *= p + p;
- return fract(p);
+ p = fract(p * .1031f);
+ p *= p + 33.33f;
+ p *= p + p;
+ return fract(p);
}
typedef struct Color
{
- float r, g, b, a;
+ float r, g, b, a;
} Color;
static Color colhex(int r, int g, int b)
{
- return (Color){
- .r = (float)r / 255.0f,
- .g = (float)g / 255.0f,
- .b = (float)b / 255.0f,
- .a = 1.0f,
- };
+ return (Color) {
+ .r = (float)r / 255.0f,
+ .g = (float)g / 255.0f,
+ .b = (float)b / 255.0f,
+ .a = 1.0f,
+ };
}
static Color Collerp(Color a, Color b, float factor)
{
- Color to_return = {0};
- to_return.r = lerp(a.r, b.r, factor);
- to_return.g = lerp(a.g, b.g, factor);
- to_return.b = lerp(a.b, b.b, factor);
- to_return.a = lerp(a.a, b.a, factor);
+ Color to_return = { 0 };
+ to_return.r = lerp(a.r, b.r, factor);
+ to_return.g = lerp(a.g, b.g, factor);
+ to_return.b = lerp(a.b, b.b, factor);
+ to_return.a = lerp(a.a, b.a, factor);
- return to_return;
+ return to_return;
}
static void set_color(Color c)
{
- sgp_set_color(c.r, c.g, c.b, c.a);
+ sgp_set_color(c.r, c.g, c.b, c.a);
}
#define WHITE \