Simplify serialization, write fields once

main
Cameron Murphy Reikes 2 years ago
parent 6cb97c4b3d
commit e05462aa1b

@ -89,8 +89,14 @@ void grid_destroy(cpSpace *space, struct Grid *grid)
// removes boxe from grid, then ensures that the rule that grids must not have
// holes in them is applied.
// uses these forward declared serialization functions to duplicate a box
void ser_box(char **out, struct Box *b);
void des_box(char **in, struct Box *new_box, struct GameState *gs, struct Grid *g);
typedef struct SerState
{
char *bytes;
bool serializing;
int cursor; // points to next available byte, is the size of current message after serializing something
int max_size;
} SerState;
void ser_box(SerState *ser, struct Box *var, struct GameState *gs, struct Grid *g);
static void grid_remove_box(cpSpace *space, struct Grid *grid, struct Box *box)
{
box_destroy(space, box);
@ -252,11 +258,19 @@ static void grid_remove_box(cpSpace *space, struct Grid *grid, struct Box *box)
while (cur_separate_grid[cur_sepgrid_i] != NULL)
{
char box_bytes[128];
char *cur = box_bytes;
// duplicate the box by serializing it then deserializing it
ser_box(&cur, cur_separate_grid[cur_sepgrid_i]);
cur = box_bytes;
des_box(&cur, &new_grid->boxes[new_grid_box_i], gs, new_grid);
SerState ser = (SerState){
.bytes = cur,
.cursor = 0,
.max_size = 128,
.serializing = true,
};
ser_box(&ser, cur_separate_grid[cur_sepgrid_i], gs, grid);
ser.cursor = 0;
ser.serializing = false;
ser_box(&ser, &new_grid->boxes[new_grid_box_i], gs, new_grid);
cur_sepgrid_i++;
new_grid_box_i++;
@ -426,301 +440,221 @@ float box_rotation(struct Box *box)
return cpBodyGetAngle(cpShapeGetBody(box->shape));
}
#define memwrite(out, variable) \
for (char b = 0; b < sizeof(variable); b++) \
{ \
**out = ((char *)&variable)[b]; \
*out += 1; \
}
#define memread(in, variable_pointer) \
for (char b = 0; b < sizeof(*variable_pointer); b++) \
{ \
((char *)variable_pointer)[b] = **in; \
*in += 1; \
#define WRITE_VARNAMES false // good for debugging
#include <string.h>
// assumes SerState *var defined
#define SER_VAR(var_pointer) \
{ \
const char *var_name = #var_pointer; \
size_t var_name_len = 0; \
if (WRITE_VARNAMES) \
{ \
var_name_len = strlen(var_name); \
} \
if (ser->serializing) \
{ \
if (WRITE_VARNAMES) \
{ \
memcpy(ser->bytes + ser->cursor, var_name, var_name_len); \
ser->cursor += var_name_len; \
} \
for (int b = 0; b < sizeof(*var_pointer); b++) \
{ \
ser->bytes[ser->cursor] = ((char *)var_pointer)[b]; \
ser->cursor += 1; \
assert(ser->cursor < ser->max_size); \
} \
} \
else \
{ \
if (WRITE_VARNAMES) \
{ \
char *read_name = malloc(sizeof *read_name * (var_name_len + 1)); \
for (int i = 0; i < var_name_len; i++) \
{ \
read_name[i] = ser->bytes[ser->cursor]; \
ser->cursor += 1; \
assert(ser->cursor < ser->max_size); \
} \
read_name[var_name_len] = '\0'; \
if (strcmp(read_name, var_name) != 0) \
{ \
printf("%s:%d | Expected variable %s but got %s\n", __FILE__, __LINE__, var_name, read_name); \
} \
free(read_name); \
} \
for (int b = 0; b < sizeof(*var_pointer); b++) \
{ \
((char *)var_pointer)[b] = ser->bytes[ser->cursor]; \
ser->cursor += 1; \
assert(ser->cursor < ser->max_size); \
} \
} \
}
void ser_float(char **out, float f)
void ser_V2(SerState *ser, V2 *var)
{
memwrite(out, f);
SER_VAR(&var->x);
SER_VAR(&var->y);
}
void des_float(char **in, float *f)
void ser_box(SerState *ser, struct Box *var, struct GameState *gs, struct Grid *g)
{
memread(in, f);
}
void ser_double(char **out, double d)
{
memwrite(out, d);
}
void des_double(char **in, double *d)
{
memread(in, d);
}
void ser_int(char **out, int i)
{
memwrite(out, i);
}
void des_int(char **in, int *i)
{
memread(in, i);
}
void ser_uint64(char **out, uint64_t i)
{
memwrite(out, i);
}
void des_uint64(char **in, uint64_t *i)
{
memread(in, i);
}
void ser_bool(char **out, bool b)
{
**out = (char)b;
*out += 1;
}
void des_bool(char **in, bool *b)
{
*b = (bool)**in;
*in += 1;
}
void ser_V2(char **out, V2 v)
{
ser_float(out, v.x);
ser_float(out, v.y);
}
void des_V2(char **in, V2 *v)
{
des_float(in, &v->x);
des_float(in, &v->y);
}
void ser_box(char **out, struct Box *b)
{
ser_V2(out, cp_to_v2(cpShapeGetCenterOfGravity(b->shape)));
ser_int(out, b->type); // @Robust separate enum serialization that checks for out of bounds enum
ser_int(out, b->compass_rotation);
ser_float(out, b->thrust);
ser_float(out, b->energy_used);
ser_float(out, b->damage);
}
{
V2 pos;
if (ser->serializing)
{
pos = cp_to_v2(cpShapeGetCenterOfGravity(var->shape));
}
ser_V2(ser, &pos);
if (!ser->serializing)
{
box_new(var, gs, g, pos);
}
}
void des_box(char **in, struct Box *new_box, struct GameState *gs, struct Grid *g)
{
V2 pos = {0};
des_V2(in, &pos);
box_new(new_box, gs, g, pos);
des_int(in, (int *)&new_box->type);
des_int(in, (int *)&new_box->compass_rotation);
des_float(in, &new_box->thrust);
des_float(in, &new_box->energy_used);
des_float(in, &new_box->damage);
SER_VAR(&var->type); // @Rovarust separate enum serialization that checks for out of varounds enum
SER_VAR(&var->compass_rotation);
SER_VAR(&var->thrust);
SER_VAR(&var->energy_used);
SER_VAR(&var->damage);
}
void ser_grid(char **out, struct Grid *g)
void ser_grid(SerState *ser, struct GameState *gs, struct Grid *g)
{
// grid must not be null, dummy!
assert(g->body != NULL);
ser_V2(out, grid_pos(g));
ser_V2(out, grid_vel(g));
ser_float(out, grid_rotation(g));
ser_float(out, grid_angular_velocity(g));
if (ser->serializing)
assert(g->body != NULL);
else
assert(g->body == NULL);
ser_float(out, g->total_energy_capacity);
for (int i = 0; i < MAX_BOXES_PER_GRID; i++)
{
bool exists = g->boxes[i].shape != NULL;
ser_bool(out, exists);
if (exists)
V2 pos = {0};
V2 vel = {0};
float rot = 0.0f;
float angular_vel = 0.0f;
if (ser->serializing)
{
ser_box(out, &g->boxes[i]);
pos = grid_pos(g);
vel = grid_vel(g);
rot = grid_rotation(g);
angular_vel = grid_angular_velocity(g);
}
SER_VAR(&pos);
SER_VAR(&vel);
SER_VAR(&rot);
SER_VAR(&angular_vel);
if (!ser->serializing)
{
grid_new(g, gs, pos);
cpBodySetVelocity(g->body, v2_to_cp(vel));
cpBodySetAngle(g->body, rot);
cpBodySetAngularVelocity(g->body, angular_vel);
}
}
}
// takes gamestate as argument to place box in the gamestates space
void des_grid(char **in, struct Grid *g, struct GameState *gs)
{
assert(g->body == NULL); // destroy the grid before deserializing into it
V2 pos = {0};
V2 vel = {0};
float rot = 0.0f;
float angular_vel = 0.0f;
des_V2(in, &pos);
des_V2(in, &vel);
des_float(in, &rot);
des_float(in, &angular_vel);
grid_new(g, gs, pos);
cpBodySetVelocity(g->body, v2_to_cp(vel));
cpBodySetAngle(g->body, rot);
cpBodySetAngularVelocity(g->body, angular_vel);
SER_VAR(&g->total_energy_capacity);
des_float(in, &g->total_energy_capacity);
// iterate over every box like this so box index is preserved on deserialization
for (int i = 0; i < MAX_BOXES_PER_GRID; i++)
{
bool exists = false;
des_bool(in, &exists);
bool exists;
if (ser->serializing)
exists = g->boxes[i].shape != NULL;
SER_VAR(&exists);
if (exists)
{
des_box(in, &g->boxes[i], gs, g);
ser_box(ser, &g->boxes[i], gs, g);
}
}
}
void ser_inputframe(char **out, struct InputFrame *i)
{
ser_V2(out, i->movement);
ser_bool(out, i->inhabit);
ser_V2(out, i->build);
ser_bool(out, i->dobuild);
ser_int(out, i->build_type);
ser_int(out, i->build_rotation);
ser_int(out, i->grid_index);
}
void des_inputframe(char **in, struct InputFrame *i)
void ser_inputframe(SerState *ser, struct InputFrame *i)
{
des_V2(in, &i->movement);
des_bool(in, &i->inhabit);
des_V2(in, &i->build);
des_bool(in, &i->dobuild);
des_int(in, (int *)&i->build_type);
des_int(in, (int *)&i->build_rotation);
des_int(in, &i->grid_index);
SER_VAR(&i->movement);
SER_VAR(&i->inhabit);
SER_VAR(&i->build);
SER_VAR(&i->dobuild);
SER_VAR(&i->build_type);
SER_VAR(&i->build_rotation);
SER_VAR(&i->grid_index);
}
void ser_player(char **out, struct Player *p)
void ser_player(SerState *ser, struct Player *p)
{
ser_bool(out, p->connected);
SER_VAR(&p->connected);
if (p->connected)
{
ser_int(out, p->currently_inhabiting_index);
ser_V2(out, p->pos);
ser_V2(out, p->vel);
ser_float(out, p->spice_taken_away);
ser_float(out, p->goldness);
ser_inputframe(out, &p->input);
SER_VAR(&p->currently_inhabiting_index);
ser_V2(ser, &p->pos);
ser_V2(ser, &p->vel);
SER_VAR(&p->spice_taken_away);
SER_VAR(&p->goldness);
ser_inputframe(ser, &p->input);
}
}
void des_player(char **in, struct Player *p, struct GameState *gs)
void ser_server_to_client(SerState *ser, ServerToClient *s)
{
des_bool(in, &p->connected);
if (p->connected)
{
des_int(in, &p->currently_inhabiting_index);
des_V2(in, &p->pos);
des_V2(in, &p->vel);
des_float(in, &p->spice_taken_away);
des_float(in, &p->goldness);
struct GameState *gs = s->cur_gs;
des_inputframe(in, &p->input);
if (!ser->serializing)
{
destroy(gs);
initialize(gs);
}
}
// @Robust really think about if <= makes more sense than < here...
#define LEN_CHECK() assert(bytes - original_bytes <= max_len)
void into_bytes(struct ServerToClient *msg, char *bytes, int *out_len, int max_len)
{
assert(msg->cur_gs != NULL);
assert(msg != NULL);
struct GameState *gs = msg->cur_gs;
char *original_bytes = bytes;
ser_int(&bytes, msg->your_player);
LEN_CHECK();
SER_VAR(&s->your_player);
SER_VAR(&gs->tick);
SER_VAR(&gs->time);
ser_uint64(&bytes, gs->tick);
ser_double(&bytes, gs->time);
LEN_CHECK();
ser_V2(&bytes, gs->goldpos);
LEN_CHECK();
ser_V2(ser, &gs->goldpos);
for (int i = 0; i < MAX_PLAYERS; i++)
{
ser_player(&bytes, &gs->players[i]);
LEN_CHECK();
ser_player(ser, &gs->players[i]);
}
// @Robust invalid message on num boxes bigger than max boxes
for (int i = 0; i < MAX_GRIDS; i++)
{
bool exists = gs->grids[i].body != NULL;
ser_bool(&bytes, exists);
LEN_CHECK();
bool exists;
if (ser->serializing)
exists = gs->grids[i].body != NULL;
SER_VAR(&exists);
if (exists)
{
ser_grid(&bytes, &gs->grids[i]);
LEN_CHECK();
ser_grid(ser, gs, &gs->grids[i]);
}
}
*out_len = bytes - original_bytes;
}
void from_bytes(struct ServerToClient *msg, char *bytes, int max_len)
void into_bytes(struct ServerToClient *msg, char *bytes, int *out_len, int max_len)
{
struct GameState *gs = msg->cur_gs;
char *original_bytes = bytes;
destroy(gs);
initialize(gs);
des_int(&bytes, &msg->your_player);
LEN_CHECK();
assert(msg->cur_gs != NULL);
assert(msg != NULL);
des_uint64(&bytes, &gs->tick);
LEN_CHECK();
SerState ser = (SerState){
.bytes = bytes,
.serializing = true,
.cursor = 0,
.max_size = max_len,
};
des_double(&bytes, &gs->time);
LEN_CHECK();
ser_server_to_client(&ser, msg);
*out_len = ser.cursor + 1; // @Robust not sure why I need to add one to cursor, ser.cursor should be the length..
}
des_V2(&bytes, &gs->goldpos);
LEN_CHECK();
void from_bytes(struct ServerToClient *msg, char *bytes, int max_len)
{
assert(msg->cur_gs != NULL);
assert(msg != NULL);
for (int i = 0; i < MAX_PLAYERS; i++)
{
des_player(&bytes, &gs->players[i], gs);
LEN_CHECK();
}
SerState ser = (SerState){
.bytes = bytes,
.serializing = false,
.cursor = 0,
.max_size = max_len,
};
for (int i = 0; i < MAX_GRIDS; i++)
{
bool exists = false;
des_bool(&bytes, &exists);
LEN_CHECK();
if (exists)
{
des_grid(&bytes, &gs->grids[i], gs);
LEN_CHECK();
}
}
ser_server_to_client(&ser, msg);
}
// has to be global var because can only get this information

@ -171,11 +171,11 @@ static float rotangle(enum CompassRotation rot)
}
}
struct ServerToClient
typedef struct ServerToClient
{
struct GameState *cur_gs;
int your_player;
};
} ServerToClient;
struct ClientToServer
{

Loading…
Cancel
Save