diff --git a/flight.rdbg b/flight.rdbg index 6ed3c1e..d36c7a0 100644 Binary files a/flight.rdbg and b/flight.rdbg differ diff --git a/gamestate.c b/gamestate.c index 9645894..5c4cec1 100644 --- a/gamestate.c +++ b/gamestate.c @@ -1152,28 +1152,6 @@ void update_from(cpBody *body, struct BodyData *data) cpBodySetAngularVelocity(body, data->angular_velocity); } -typedef struct SerState -{ - unsigned char *bytes; - bool serializing; - size_t cursor; // points to next available byte, is the size of current message after serializing something - size_t max_size; - Entity *for_player; - size_t max_entity_index; // for error checking - bool write_varnames; - bool save_or_load_from_disk; - - // output - uint32_t version; - uint32_t git_release_tag; -} SerState; - -typedef struct SerMaybeFailure -{ - bool failed; - int line; - const char *expression; -} SerMaybeFailure; const static SerMaybeFailure ser_ok = {0}; #define SER_ASSERT(cond) \ if (!(cond)) \ @@ -1424,6 +1402,7 @@ SerMaybeFailure ser_player(SerState *ser, Player *p) SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e) { SER_VAR(&e->no_save_to_disk); + SER_VAR(&e->always_visible); SER_VAR(&e->generation); SER_MAYBE_RETURN(ser_f(ser, &e->damage)); @@ -1561,7 +1540,6 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e) SER_VAR(&e->owning_squad); - SER_VAR(&e->always_visible); SER_MAYBE_RETURN(ser_entityid(ser, &e->next_box)); SER_MAYBE_RETURN(ser_entityid(ser, &e->prev_box)); SER_VAR(&e->compass_rotation); @@ -1853,6 +1831,14 @@ SerMaybeFailure ser_server_to_client(SerState *ser, ServerToClient *s) return ser_ok; } +// On serialize: +// 1. put struct's data into bytes, for a specific player or not, and to disk or not +// 2. output the number of bytes it took to put the struct's data into bytes + +// On deserialize: +// 1. put bytes into a struct for a specific player, from disk or not +// 2. perform operations on those bytes to initialize the struct + // for_this_player can be null then the entire world will be sent bool server_to_client_serialize(struct ServerToClient *msg, unsigned char *bytes, size_t *out_len, size_t max_len, Entity *for_this_player, bool to_disk) { @@ -1965,60 +1951,47 @@ SerMaybeFailure ser_client_to_server(SerState *ser, ClientToServer *msg) } return ser_ok; } +size_t ser_size(SerState *ser) +{ + flight_assert(ser->cursor > 0); // if this fails, haven't serialized anything yet! + return ser->cursor + 1; +} -bool client_to_server_serialize(GameState *gs, struct ClientToServer *msg, unsigned char *bytes, size_t *out_len, size_t max_len) +SerState init_serializing(GameState *gs, unsigned char *bytes, size_t max_size, Entity *for_player, bool to_disk) { - SerState ser = (SerState){ + bool write_varnames = to_disk; +#ifdef WRITE_VARNAMES + write_varnames = true; +#endif + return (SerState){ .bytes = bytes, .serializing = true, .cursor = 0, - .max_size = max_len, - .for_player = NULL, .max_entity_index = gs->cur_next_entity, + .max_size = max_size, + .for_player = for_player, .version = VMax - 1, + .save_or_load_from_disk = to_disk, + .write_varnames = write_varnames, }; -#ifdef WRITE_VARNAMES - ser.write_varnames = true; -#endif - - SerMaybeFailure result = ser_client_to_server(&ser, msg); - *out_len = ser.cursor + 1; // see other comment for server to client - if (result.failed) - { - Log("Failed to serialize client to server because %s was false, line %d\n", result.expression, result.line); - return false; - } - else - { - return true; - } } -bool client_to_server_deserialize(GameState *gs, struct ClientToServer *msg, unsigned char *bytes, size_t max_len) +SerState init_deserializing(GameState *gs, unsigned char *bytes, size_t max_size, bool from_disk) { - SerState servar = (SerState){ + bool has_varnames = from_disk; +#ifdef WRITE_VARNAMES + has_varnames = true; +#endif + return (SerState){ .bytes = bytes, .serializing = false, .cursor = 0, - .max_size = max_len, - .max_entity_index = gs->cur_next_entity, - .save_or_load_from_disk = false, + .max_size = max_size, + .max_entity_index = gs->max_entities, + .for_player = NULL, + .save_or_load_from_disk = from_disk, + .write_varnames = has_varnames, }; -#ifdef WRITE_VARNAMES - servar.write_varnames = true; -#endif - - SerState *ser = &servar; - SerMaybeFailure result = ser_client_to_server(ser, msg); - if (result.failed) - { - Log("Failed to deserialize client to server on line %d because of %s\n", result.line, result.expression); - return false; - } - else - { - return true; - } } // filter func null means everything is ok, if it's not null and returns false, that means @@ -2434,10 +2407,19 @@ void create_initial_world(GameState *gs) entity_set_pos(grid, cpv(1.5, 0.0)); BOX_AT_TYPE(grid, cpv(0.0, 0.0), BoxExplosive); BOX_AT_TYPE(grid, cpv(-BOX_SIZE, 0.0), BoxScanner); - BOX_AT_TYPE(grid, cpv(-BOX_SIZE, BOX_SIZE), BoxSolarPanel); - BOX_AT_TYPE(grid, cpv(-BOX_SIZE, BOX_SIZE * 2.0), BoxSolarPanel); + BOX_AT_TYPE(grid, cpv(-BOX_SIZE*2.0, 0.0), BoxSolarPanel); + BOX_AT_TYPE(grid, cpv(-BOX_SIZE*3.0, 0.0), BoxSolarPanel); + entity_ensure_in_orbit(gs, grid); + } + + { + Entity *grid = new_entity(gs); + grid_create(gs, grid); + entity_set_pos(grid, cpv(0.0, 10.0)); + BOX_AT_TYPE(grid, cpv(0.0, 0.0), BoxHullpiece); entity_ensure_in_orbit(gs, grid); } + #endif #if 0 // merge box diff --git a/main.c b/main.c index 0ae54b9..7ff8a9d 100644 --- a/main.c +++ b/main.c @@ -14,11 +14,11 @@ #define SOKOL_IMPL #define SOKOL_D3D11 #include "sokol_app.h" +#include "sokol_args.h" #include "sokol_gfx.h" #include "sokol_glue.h" #include "sokol_gp.h" #include "sokol_time.h" -#include "sokol_args.h" #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 @@ -59,17 +59,17 @@ typedef struct KeyPressed } KeyPressed; static KeyPressed keypressed[MAX_KEYDOWN] = {0}; static cpVect mouse_pos = {0}; -static FILE * record_inputs_to = NULL; +static FILE *record_inputs_to = NULL; static bool fullscreened = false; static bool picking_new_boxtype = false; static double exec_time = 0.0; // cosmetic bouncing, network stats -static float iTime = 0.0; // fmodded to 1000, shader trick http://the-witness.net/news/2022/02/a-shader-trick/ +static float iTime = 0.0; // fmodded to 1000, shader trick http://the-witness.net/news/2022/02/a-shader-trick/ // for network statistics, printed to logs with F3 static uint64_t total_bytes_sent = 0; static uint64_t total_bytes_received = 0; +static double dilating_time_factor = 1.0; static bool build_pressed = false; -static double dilating_time_factor = 1.0; static double time_to_process = 0.0; static bool interact_pressed = false; #define MAX_MOUSEBUTTON (SAPP_MOUSEBUTTON_MIDDLE + 1) @@ -80,6 +80,7 @@ typedef struct MousePressed uint64_t frame; } MousePressed; static MousePressed mousepressed[MAX_MOUSEBUTTON] = {0}; + static EntityID maybe_inviting_this_player = {0}; static EntityID hovering_this_player = {0}; bool confirm_invite_this_player = false; @@ -511,23 +512,23 @@ static void init(void) // commandline { printf( - "Usage: astris.exe [option]=data , the =stuff is required\n" - "host - hosts a server locally if exists in commandline, like `astris.exe host=yes`\n" - "record_inputs_to - records inputs to the file specified" - ); - if(sargs_exists("host")) + "Usage: astris.exe [option]=data , the =stuff is required\n" + "host - hosts a server locally if exists in commandline, like `astris.exe host=yes`\n" + "record_inputs_to - records inputs to the file specified"); + if (sargs_exists("host")) { server_thread_handle = (void *)_beginthread(server, 0, (void *)&server_info); sapp_set_window_title("Flight Hosting"); } - if(sargs_exists("record_inputs_to")) + if (sargs_exists("record_inputs_to")) { const char *filename = sargs_value("record_inputs_to"); - if(filename == NULL){ + if (filename == NULL) + { quit_with_popup("Failed to record inputs, filename not specified", "Failed to record inputs"); } fopen_s(&record_inputs_to, filename, "wb"); - if(record_inputs_to == NULL) + if (record_inputs_to == NULL) { quit_with_popup("Failed to open file to record inputs into", "Failed to record inputs"); } @@ -650,7 +651,7 @@ static void init(void) quit_with_popup("Couldn't make a shader! Uhhh ooooohhhhhh!!!", "Shader error BONED"); } } - + { sgp_pipeline_desc pip_desc = { .shader = *lightning_program_shader_desc(sg_query_backend()), @@ -1585,8 +1586,12 @@ static void frame(void) { PROFILE_SCOPE("Deserializing data") { - server_to_client_deserialize(&msg, decompressed, - decompressed_max_len, false); + SerState ser = init_deserializing(&gs, decompressed, decompressed_max_len, false); + SerMaybeFailure maybe_fail = ser_server_to_client(&ser, &msg); + if (maybe_fail.failed) + { + Log("Failed to deserialize game state packet line %d %s\n", maybe_fail.line, maybe_fail.expression); + } applied_gamestate_packet = true; } my_player_index = msg.your_player; @@ -1857,9 +1862,10 @@ static void frame(void) .input_data = &input_queue, }; unsigned char serialized[MAX_CLIENT_TO_SERVER] = {0}; - size_t out_len = 0; - if (client_to_server_serialize(&gs, &to_send, serialized, &out_len, - MAX_CLIENT_TO_SERVER)) + SerState ser = init_serializing(&gs, serialized, MAX_CLIENT_TO_SERVER, NULL, false); + SerMaybeFailure maybe_fail = ser_client_to_server(&ser, &to_send); + size_t out_len = ser_size(&ser); + if (!maybe_fail.failed) { unsigned char compressed[MAX_CLIENT_TO_SERVER] = {0}; char lzo_working_mem[LZO1X_1_MEM_COMPRESS] = {0}; @@ -1885,7 +1891,7 @@ static void frame(void) } else { - Log("Failed to serialize client to server!\n"); + Log("Failed to serialize client to server: %d %s\n", maybe_fail.line, maybe_fail.expression); } ma_mutex_unlock(&send_packets_mutex); } @@ -2166,10 +2172,10 @@ static void frame(void) { sgp_set_image(0, (sg_image){0}); lightning_uniforms_t uniform = { - .iTime = iTime, + .iTime = iTime, }; sgp_set_uniform(&uniform, sizeof(uniform)); - draw_color_rect_centered(entity_pos(b), BOX_SIZE*2.0); + draw_color_rect_centered(entity_pos(b), BOX_SIZE * 2.0); sgp_reset_image(0); } } @@ -2254,7 +2260,7 @@ static void frame(void) sgp_set_image(0, image_radardot); for (int i = 0; i < SCANNER_MAX_POINTS; i++) { - if (b->scanner_points[i].x != 0 && b->scanner_points[i].y != 0) + if (b->scanner_points[i].x != 0 || b->scanner_points[i].y != 0) { struct ScannerPoint point = b->scanner_points[i]; switch (point.kind) @@ -2593,8 +2599,7 @@ sapp_desc sokol_main(int argc, char *argv[]) { sargs_setup(&(sargs_desc){ .argc = argc, - .argv = argv - }); + .argv = argv}); stm_setup(); ma_mutex_init(&server_info.info_mutex); diff --git a/server.c b/server.c index 1180e6e..6cddb16 100644 --- a/server.c +++ b/server.c @@ -22,6 +22,12 @@ #include "profiling.h" +static void panicquit() +{ + flight_assert(false); + exit(-1); +} + // started in a thread from host void server(void *info_raw) { @@ -39,7 +45,6 @@ void server(void *info_raw) gs.server_side_computing = true; Log("Allocated %zu bytes for entities\n", entities_size); - create_initial_world(&gs); // inputs @@ -56,9 +61,6 @@ void server(void *info_raw) OpusEncoder *player_encoders[MAX_PLAYERS] = {0}; OpusDecoder *player_decoders[MAX_PLAYERS] = {0}; -#ifdef DEBUG_WORLD - world_save_name = NULL; -#endif if (world_save_name != NULL) { size_t read_game_data_buffer_size = entities_size; @@ -76,13 +78,18 @@ void server(void *info_raw) if (actual_length <= 1) { Log("Could only read %zu bytes, error: errno %d\n", actual_length, errno); - exit(-1); + panicquit(); } Log("Read %zu bytes from save file\n", actual_length); ServerToClient msg = (ServerToClient){ .cur_gs = &gs, }; - server_to_client_deserialize(&msg, read_game_data, actual_length, true); + SerState ser = init_deserializing(&gs, read_game_data, actual_length, true); + SerMaybeFailure maybe_fail = ser_server_to_client(&ser, &msg); + if (maybe_fail.failed) + { + Log("Failed to deserialize game world from save file: %d %s\n", maybe_fail.line, maybe_fail.expression); + } fclose(file); } @@ -97,11 +104,10 @@ void server(void *info_raw) } #define BOX_AT(grid, pos) BOX_AT_TYPE(grid, pos, BoxHullpiece) - if (enet_initialize() != 0) { fprintf(stderr, "An error occurred while initializing ENet.\n"); - exit(-1); + panicquit(); } ENetAddress address; @@ -122,7 +128,7 @@ void server(void *info_raw) { fprintf(stderr, "An error occurred while trying to create an ENet server host.\n"); - exit(-1); + panicquit(); } Log("Serving on port %d...\n", SERVER_PORT); @@ -229,9 +235,11 @@ void server(void *info_raw) if (return_value == LZO_E_OK) { - if (!client_to_server_deserialize(&gs, &received, decompressed, decompressed_max_len)) + SerState ser = init_deserializing(&gs, decompressed, decompressed_max_len, false); + SerMaybeFailure maybe_fail = ser_client_to_server(&ser, &received); + if (maybe_fail.failed) { - Log("Bad packet from client %d\n", (int)player_slot); + Log("Bad packet from client %d | %d %s\n", (int)player_slot, maybe_fail.line, maybe_fail.expression); } } else @@ -309,8 +317,10 @@ void server(void *info_raw) ServerToClient msg = (ServerToClient){ .cur_gs = &gs, }; - size_t out_len = 0; - if (server_to_client_serialize(&msg, world_save_buffer, &out_len, entities_size, NULL, true)) + SerState ser = init_serializing(&gs, world_save_buffer, entities_size, NULL, true); + SerMaybeFailure maybe_fail = ser_server_to_client(&ser, &msg); + size_t out_len = ser_size(&ser); + if (!maybe_fail.failed) { FILE *save_file = NULL; fopen_s(&save_file, (const char *)world_save_name, "wb"); @@ -334,7 +344,7 @@ void server(void *info_raw) } else { - Log("URGENT: FAILED TO SAVE WORLD FILE!\n"); + Log("URGENT: FAILED TO SAVE WORLD FILE! Failed at line %d expression %s\n", maybe_fail.line, maybe_fail.expression); } } } @@ -431,13 +441,15 @@ void server(void *info_raw) .audio_playback_buffer = &buffer_to_play, }; - size_t len = 0; - if (server_to_client_serialize(&to_send, bytes_buffer, &len, MAX_SERVER_TO_CLIENT, this_player_entity, false)) + SerState ser = init_serializing(&gs, bytes_buffer, MAX_SERVER_TO_CLIENT, this_player_entity, false); + SerMaybeFailure maybe_fail = ser_server_to_client(&ser, &to_send); + size_t len = ser_size(&ser); + if (!maybe_fail.failed) { if (len > MAX_SERVER_TO_CLIENT - 8) { Log("Too much data quitting!\n"); - exit(-1); + panicquit(); } size_t compressed_len = 0; diff --git a/types.h b/types.h index 6a4210b..e63df02 100644 --- a/types.h +++ b/types.h @@ -253,6 +253,7 @@ typedef struct Entity bool exists; EntityID next_free_entity; unsigned int generation; + bool always_visible; // always serialized to the player. bool no_save_to_disk; // stuff generated later on, like player's bodies or space stations that respawn. @@ -310,7 +311,6 @@ typedef struct Entity bool is_box; enum BoxType box_type; bool is_platonic; // can't be destroyed, unaffected by physical forces - bool always_visible; // always serialized to the player. @Robust check if not used EntityID next_box; // for the grid! EntityID prev_box; // doubly linked so can remove in middle of chain enum CompassRotation compass_rotation; @@ -481,11 +481,36 @@ double sun_dist_no_gravity(Entity *sun); void quit_with_popup(const char *message_utf8, const char *title_utf8); +// serialization stuff +typedef struct SerState +{ + unsigned char *bytes; + bool serializing; + size_t cursor; // points to next available byte, is the size of current message after serializing something + size_t max_size; + Entity *for_player; + size_t max_entity_index; // for error checking + bool write_varnames; + bool save_or_load_from_disk; + + // output + uint32_t version; + uint32_t git_release_tag; // release tag, unlike version, is about the game version not the serialization verson +} SerState; + +typedef struct SerMaybeFailure +{ + bool failed; + int line; + const char *expression; +} SerMaybeFailure; + // all of these return if successful or not -bool server_to_client_serialize(struct ServerToClient *msg, unsigned char *bytes, size_t *out_len, size_t max_len, Entity *for_this_player, bool to_disk); -bool server_to_client_deserialize(struct ServerToClient *msg, unsigned char *bytes, size_t max_len, bool from_disk); -bool client_to_server_deserialize(GameState *gs, struct ClientToServer *msg, unsigned char *bytes, size_t max_len); -bool client_to_server_serialize(GameState *gs, struct ClientToServer *msg, unsigned char *bytes, size_t *out_len, size_t max_len); +size_t ser_size(SerState *ser); +SerState init_serializing(GameState *gs, unsigned char *bytes, size_t max_size, Entity *for_player, bool to_disk); +SerState init_deserializing(GameState *gs, unsigned char *bytes, size_t max_size, bool from_disk); +SerMaybeFailure ser_server_to_client(SerState *ser, ServerToClient *s); +SerMaybeFailure ser_client_to_server(SerState *ser, ClientToServer *msg); // entities bool is_burning(Entity *missile);