Save world and red vignette on damage

main
Cameron Murphy Reikes 2 years ago
parent dd5731eb45
commit de4de50aac

1
.gitignore vendored

@ -1,3 +1,4 @@
debug_world.bin
flight*.zip flight*.zip
flight_server flight_server
flight.zip flight.zip

@ -275,6 +275,7 @@ void create_rectangle_shape(GameState* gs, Entity* e, Entity* parent, V2 pos, V2
void create_player(GameState* gs, Entity* e) void create_player(GameState* gs, Entity* e)
{ {
e->is_player = true; e->is_player = true;
e->no_save_to_disk = true;
create_body(gs, e); create_body(gs, e);
create_rectangle_shape(gs, e, e, (V2) { 0 }, V2scale(PLAYER_SIZE, 0.5f), PLAYER_MASS); create_rectangle_shape(gs, e, e, (V2) { 0 }, V2scale(PLAYER_SIZE, 0.5f), PLAYER_MASS);
cpShapeSetFilter(e->shape, PLAYER_SHAPE_FILTER); cpShapeSetFilter(e->shape, PLAYER_SHAPE_FILTER);
@ -618,21 +619,24 @@ typedef struct SerState
size_t cursor; // points to next available byte, is the size of current message after serializing something size_t cursor; // points to next available byte, is the size of current message after serializing something
size_t max_size; size_t max_size;
Entity* for_player; Entity* for_player;
bool write_varnames;
bool save_or_load_from_disk;
// output
uint32_t version;
} SerState; } SerState;
void ser_var(SerState* ser, char* var_pointer, size_t var_size, const char* name, const char* file, int line) void ser_var(SerState* ser, char* var_pointer, size_t var_size, const char* name, const char* file, int line)
{ {
#ifdef WRITE_VARNAMES
char var_name[512] = { 0 }; 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 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); size_t var_name_len = strlen(var_name);
#endif
if (ser->serializing) if (ser->serializing)
{ {
if (ser->write_varnames)
#ifdef WRITE_VARNAMES {
memcpy(ser->bytes + ser->cursor, var_name, var_name_len); memcpy(ser->bytes + ser->cursor, var_name, var_name_len);
ser->cursor += var_name_len; ser->cursor += var_name_len;
#endif }
for (int b = 0; b < var_size; b++) for (int b = 0; b < var_size; b++)
{ {
ser->bytes[ser->cursor] = var_pointer[b]; ser->bytes[ser->cursor] = var_pointer[b];
@ -642,15 +646,14 @@ void ser_var(SerState* ser, char* var_pointer, size_t var_size, const char* name
} }
else else
{ {
#ifdef WRITE_VARNAMES if (ser->write_varnames) {
{
char read_name[512] = { 0 }; char read_name[512] = { 0 };
for (int i = 0; i < var_name_len; i++) for (int i = 0; i < var_name_len; i++)
{ {
read_name[i] = ser->bytes[ser->cursor]; read_name[i] = ser->bytes[ser->cursor];
ser->cursor += 1; ser->cursor += 1;
assert(ser->cursor < ser->max_size); assert(ser->cursor <= ser->max_size);
} }
read_name[var_name_len] = '\0'; read_name[var_name_len] = '\0';
// advance past digits // advance past digits
@ -666,12 +669,11 @@ void ser_var(SerState* ser, char* var_pointer, size_t var_size, const char* name
*(char*)NULL = 0; *(char*)NULL = 0;
} }
} }
#endif
for (int b = 0; b < var_size; b++) for (int b = 0; b < var_size; b++)
{ {
var_pointer[b] = ser->bytes[ser->cursor]; var_pointer[b] = ser->bytes[ser->cursor];
ser->cursor += 1; ser->cursor += 1;
assert(ser->cursor < ser->max_size); assert(ser->cursor <= ser->max_size);
} }
} }
@ -679,6 +681,13 @@ 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, __FILE__, __LINE__) #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) #define SER_VAR(var_pointer) SER_VAR_NAME(var_pointer, #var_pointer)
enum GameVersion {
VInitial,
VAddedTest,
VAddedSerToDisk,
VMax, // this minus one will be the version used
};
// @Robust probably get rid of this as separate function, just use SER_VAR // @Robust probably get rid of this as separate function, just use SER_VAR
void ser_V2(SerState* ser, V2* var) void ser_V2(SerState* ser, V2* var)
{ {
@ -728,9 +737,21 @@ void ser_player(SerState* ser, Player* p)
void ser_entity(SerState* ser, GameState* gs, Entity* e) void ser_entity(SerState* ser, GameState* gs, Entity* e)
{ {
SER_VAR(&e->no_save_to_disk);
if (e->no_save_to_disk && ser->save_or_load_from_disk)
{
return;
}
SER_VAR(&e->generation); SER_VAR(&e->generation);
SER_VAR(&e->damage); SER_VAR(&e->damage);
int test;
if (ser->serializing) test = 27;
if (ser->version >= VAddedTest)
SER_VAR(&test);
else test = 27;
assert(test == 27);
bool has_body = ser->serializing && e->body != NULL; bool has_body = ser->serializing && e->body != NULL;
SER_VAR(&has_body); SER_VAR(&has_body);
@ -785,6 +806,7 @@ void ser_entity(SerState* ser, GameState* gs, Entity* e)
SER_VAR(&e->is_player); SER_VAR(&e->is_player);
if (e->is_player) if (e->is_player)
{ {
assert(e->no_save_to_disk);
ser_entityid(ser, &e->currently_inside_of_box); ser_entityid(ser, &e->currently_inside_of_box);
SER_VAR(&e->goldness); SER_VAR(&e->goldness);
} }
@ -824,6 +846,8 @@ void ser_entity(SerState* ser, GameState* gs, Entity* e)
void ser_server_to_client(SerState* ser, ServerToClient* s) void ser_server_to_client(SerState* ser, ServerToClient* s)
{ {
SER_VAR(&ser->version);
GameState* gs = s->cur_gs; GameState* gs = s->cur_gs;
int cur_next_entity = 0; int cur_next_entity = 0;
@ -844,11 +868,14 @@ void ser_server_to_client(SerState* ser, ServerToClient* s)
ser_V2(ser, &gs->goldpos); ser_V2(ser, &gs->goldpos);
if (!ser->save_or_load_from_disk)
{
// @Robust save player data with their ID or something somehow. Like local backup of their account
for (size_t i = 0; i < MAX_PLAYERS; i++) for (size_t i = 0; i < MAX_PLAYERS; i++)
{ {
ser_player(ser, &gs->players[i]); ser_player(ser, &gs->players[i]);
} }
}
if (ser->serializing) if (ser->serializing)
{ {
bool entities_done = false; bool entities_done = false;
@ -928,7 +955,7 @@ void ser_server_to_client(SerState* ser, ServerToClient* s)
} }
// for_this_player can be null then the entire world will be sent // for_this_player can be null then the entire world will be sent
void into_bytes(struct ServerToClient* msg, char* bytes, size_t* out_len, size_t max_len, Entity* for_this_player) void into_bytes(struct ServerToClient* msg, char* bytes, size_t* out_len, size_t max_len, Entity* for_this_player, bool write_varnames)
{ {
assert(msg->cur_gs != NULL); assert(msg->cur_gs != NULL);
assert(msg != NULL); assert(msg != NULL);
@ -939,13 +966,24 @@ void into_bytes(struct ServerToClient* msg, char* bytes, size_t* out_len, size_t
.cursor = 0, .cursor = 0,
.max_size = max_len, .max_size = max_len,
.for_player = for_this_player, .for_player = for_this_player,
.version = VMax - 1,
}; };
if (for_this_player == NULL) // @Robust jank
{
ser.save_or_load_from_disk = true;
}
ser.write_varnames = write_varnames;
#ifdef WRITE_VARNAMES
ser.write_varnames = true;
#endif
ser_server_to_client(&ser, msg); 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.. *out_len = ser.cursor + 1; // @Robust not sure why I need to add one to cursor, ser.cursor should be the length..
} }
void from_bytes(struct ServerToClient* msg, char* bytes, size_t max_len) void from_bytes(struct ServerToClient* msg, char* bytes, size_t max_len, bool write_varnames, bool from_disk)
{ {
assert(msg->cur_gs != NULL); assert(msg->cur_gs != NULL);
assert(msg != NULL); assert(msg != NULL);
@ -955,8 +993,15 @@ void from_bytes(struct ServerToClient* msg, char* bytes, size_t max_len)
.serializing = false, .serializing = false,
.cursor = 0, .cursor = 0,
.max_size = max_len, .max_size = max_len,
.save_or_load_from_disk = from_disk,
}; };
ser.write_varnames = write_varnames;
#ifdef WRITE_VARNAMES
ser.write_varnames = true;
#endif
ser_server_to_client(&ser, msg); ser_server_to_client(&ser, msg);
} }
@ -1083,17 +1128,19 @@ void entity_ensure_in_orbit(Entity* e)
EntityID create_spacestation(GameState* gs) EntityID create_spacestation(GameState* gs)
{ {
#define BOX_AT_TYPE(grid, pos, type) { Entity* box = new_entity(gs); box_create(gs, box, grid, pos); box->box_type = type; box->indestructible = indestructible; box->always_visible = true; } #define BOX_AT_TYPE(grid, pos, type) { Entity* box = new_entity(gs); box_create(gs, box, grid, pos); box->box_type = type; box->indestructible = indestructible; box->always_visible = true; box->no_save_to_disk = true; }
#define BOX_AT(grid, pos) BOX_AT_TYPE(grid, pos, BoxHullpiece) #define BOX_AT(grid, pos) BOX_AT_TYPE(grid, pos, BoxHullpiece)
bool indestructible = false; bool indestructible = false;
Entity* grid = new_entity(gs); Entity* grid = new_entity(gs);
grid_create(gs, grid); grid_create(gs, grid);
grid->no_save_to_disk = true;
entity_set_pos(grid, (V2) { -150.0f, 0.0f }); entity_set_pos(grid, (V2) { -150.0f, 0.0f });
entity_ensure_in_orbit(grid); entity_ensure_in_orbit(grid);
Entity* explosion_box = new_entity(gs); Entity* explosion_box = new_entity(gs);
box_create(gs, explosion_box, grid, (V2) { 0 }); box_create(gs, explosion_box, grid, (V2) { 0 });
explosion_box->is_explosion_unlock = true; explosion_box->is_explosion_unlock = true;
explosion_box->no_save_to_disk = true;
BOX_AT_TYPE(grid, ((V2) { BOX_SIZE, 0 }), BoxExplosive); BOX_AT_TYPE(grid, ((V2) { BOX_SIZE, 0 }), BoxExplosive);
BOX_AT_TYPE(grid, ((V2) { BOX_SIZE * 2, 0 }), BoxHullpiece); BOX_AT_TYPE(grid, ((V2) { BOX_SIZE * 2, 0 }), BoxHullpiece);
BOX_AT_TYPE(grid, ((V2) { BOX_SIZE * 3, 0 }), BoxHullpiece); BOX_AT_TYPE(grid, ((V2) { BOX_SIZE * 3, 0 }), BoxHullpiece);
@ -1321,8 +1368,7 @@ void process(GameState* gs, float dt)
} }
if (sqdist < (SUN_RADIUS * SUN_RADIUS)) if (sqdist < (SUN_RADIUS * SUN_RADIUS))
{ {
entity_destroy(gs, e); e->damage += 10.0f * dt;
continue;
} }
cpVect g = cpvmult(p, -SUN_GRAVITY_STRENGTH / (sqdist * cpfsqrt(sqdist))); cpVect g = cpvmult(p, -SUN_GRAVITY_STRENGTH / (sqdist * cpfsqrt(sqdist)));

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

@ -56,6 +56,7 @@ static sg_image image_sun;
static sg_image image_medbay_used; static sg_image image_medbay_used;
static sg_image image_mystery; static sg_image image_mystery;
static sg_image image_explosion; static sg_image image_explosion;
static sg_image image_low_health;
static int cur_editing_boxtype = -1; static int cur_editing_boxtype = -1;
static int cur_editing_rotation = 0; static int cur_editing_rotation = 0;
@ -181,6 +182,7 @@ init(void)
image_medbay_used = load_image("loaded/medbay_used.png"); image_medbay_used = load_image("loaded/medbay_used.png");
image_mystery = load_image("loaded/mystery.png"); image_mystery = load_image("loaded/mystery.png");
image_explosion = load_image("loaded/explosion.png"); image_explosion = load_image("loaded/explosion.png");
image_low_health = load_image("loaded/low_health.png");
} }
// socket initialization // socket initialization
@ -460,7 +462,7 @@ frame(void)
// @Robust not sure what return_value is, error test on it somehow // @Robust not sure what return_value is, error test on it somehow
if (return_value == LZO_E_OK) if (return_value == LZO_E_OK)
{ {
from_bytes(&msg, decompressed, decompressed_max_len); from_bytes(&msg, decompressed, decompressed_max_len, false, false);
myplayer = msg.your_player; myplayer = msg.your_player;
} }
else { else {
@ -834,7 +836,6 @@ frame(void)
sgp_rotate_at(entity_rotation(e), entity_pos(e).x, entity_pos(e).y); 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_color(1.0f, 1.0f, 1.0f, 1.0f);
sgp_set_image(0, image_player); sgp_set_image(0, image_player);
printf("%f\n", zoom);
draw_texture_rectangle_centered(entity_pos(e), V2scale(PLAYER_SIZE, player_scaling)); draw_texture_rectangle_centered(entity_pos(e), V2scale(PLAYER_SIZE, player_scaling));
sgp_reset_image(0); sgp_reset_image(0);
} }
@ -870,10 +871,18 @@ frame(void)
dbg_drawall(); dbg_drawall();
} // world space transform end } // world space transform end
// low health
if (myentity() != NULL)
{
sgp_set_color(1.0f, 1.0f, 1.0f, myentity()->damage);
sgp_set_image(0, image_low_health);
draw_texture_rectangle_centered((V2) { width / 2.0f, height / 2.0f }, (V2) { width, height });
sgp_reset_image(0);
} }
// UI drawn in screen space // UI drawn in screen space
ui(true, dt, width, height); ui(true, dt, width, height);
}
sg_pass_action pass_action = { 0 }; sg_pass_action pass_action = { 0 };
sg_begin_default_pass(&pass_action, (int)width, (int)height); sg_begin_default_pass(&pass_action, (int)width, (int)height);
@ -968,7 +977,7 @@ sokol_main(int argc, char* argv[])
{ {
bool hosting = false; bool hosting = false;
if (argc > 1) { if (argc > 1) {
_beginthread(server, 0, NULL); _beginthread(server, 0, "debug_world.bin");
hosting = true; hosting = true;
} }
(void)argv; (void)argv;

@ -5,13 +5,14 @@
#include <stdio.h> #include <stdio.h>
#include <inttypes.h> // int64 printing #include <inttypes.h> // int64 printing
#include <stdlib.h> #include <stdlib.h>
#include <string.h> // error string
#include <errno.h>
#include "minilzo.h" #include "minilzo.h"
// started in a thread from host // started in a thread from host
void server(void* data) void server(void* world_save_name)
{ {
(void)data;
stm_setup(); stm_setup();
@ -21,6 +22,36 @@ void server(void* data)
initialize(&gs, entity_data, entities_size); initialize(&gs, entity_data, entities_size);
Log("Allocated %zu bytes for entities\n", entities_size); Log("Allocated %zu bytes for entities\n", entities_size);
if (world_save_name != NULL)
{
size_t read_game_data_buffer_size = entities_size;
char* read_game_data = malloc(read_game_data_buffer_size);
FILE* file = NULL;
fopen_s(&file, (const char*)world_save_name, "rb");
if (file == NULL)
{
Log("Could not read from data file %s: errno %d\n", (const char*)world_save_name, errno);
}
else
{
size_t actual_length = fread(read_game_data, sizeof(char), entities_size, file);
if (actual_length <= 1)
{
Log("Could only read %zu bytes, error: errno %d\n", actual_length, errno);
exit(-1);
}
Log("Read %zu bytes from save file\n", actual_length);
ServerToClient msg = (ServerToClient){
.cur_gs = &gs,
};
from_bytes(&msg, read_game_data, actual_length, true, true);
fclose(file);
}
free(read_game_data);
}
#define BOX_AT_TYPE(grid, pos, type) { Entity* box = new_entity(&gs); box_create(&gs, box, grid, pos); box->box_type = type; } #define BOX_AT_TYPE(grid, pos, type) { Entity* box = new_entity(&gs); box_create(&gs, box, grid, pos); box->box_type = type; }
#define BOX_AT(grid, pos) BOX_AT_TYPE(grid, pos, BoxHullpiece) #define BOX_AT(grid, pos) BOX_AT_TYPE(grid, pos, BoxHullpiece)
@ -81,8 +112,10 @@ void server(void* data)
Log("Serving on port %d...\n", SERVER_PORT); Log("Serving on port %d...\n", SERVER_PORT);
ENetEvent event; ENetEvent event;
uint64_t last_processed_time = stm_now(); uint64_t last_processed_time = stm_now();
uint64_t last_saved_world_time = stm_now();
float total_time = 0.0f; float total_time = 0.0f;
size_t player_to_latest_id_processed[MAX_PLAYERS] = { 0 }; size_t player_to_latest_id_processed[MAX_PLAYERS] = { 0 };
char* world_save_buffer = malloc(entities_size);
while (true) while (true)
{ {
// @Speed handle enet messages and simulate gamestate in parallel, then sync... must clone gamestate for this // @Speed handle enet messages and simulate gamestate in parallel, then sync... must clone gamestate for this
@ -220,6 +253,33 @@ void server(void* data)
total_time -= TIMESTEP; total_time -= TIMESTEP;
} }
if (world_save_name != NULL && (stm_sec(stm_diff(stm_now(), last_saved_world_time))) > TIME_BETWEEN_WORLD_SAVE)
{
last_saved_world_time = stm_now();
ServerToClient msg = (ServerToClient){
.cur_gs = &gs,
};
size_t out_len = 0;
into_bytes(&msg, world_save_buffer, &out_len, entities_size, NULL, true);
FILE* save_file = NULL;
fopen_s(&save_file, (const char*)world_save_name, "wb");
if (save_file == NULL)
{
Log("Could not open save file: errno %d\n", errno);
}
else {
size_t data_written = fwrite(world_save_buffer, sizeof(*world_save_buffer), out_len, save_file);
if (data_written != out_len)
{
Log("Failed to save world data, wanted to write %zu but could only write %zu\n", out_len, data_written);
}
else {
Log("Saved game world to %s\n", (const char*)world_save_name);
}
fclose(save_file);
}
}
if (processed) if (processed)
{ {
static char lzo_working_mem[LZO1X_1_MEM_COMPRESS] = { 0 }; static char lzo_working_mem[LZO1X_1_MEM_COMPRESS] = { 0 };
@ -240,7 +300,7 @@ void server(void* data)
to_send.your_player = this_player_index; to_send.your_player = this_player_index;
size_t len = 0; size_t len = 0;
into_bytes(&to_send, bytes_buffer, &len, MAX_BYTES_SIZE, this_player_entity); into_bytes(&to_send, bytes_buffer, &len, MAX_BYTES_SIZE, this_player_entity, false);
if (len > MAX_BYTES_SIZE - 8) if (len > MAX_BYTES_SIZE - 8)
{ {
Log("Too much data quitting!\n"); Log("Too much data quitting!\n");
@ -268,6 +328,7 @@ void server(void* data)
} }
} }
free(world_save_buffer);
destroy(&gs); destroy(&gs);
free(entity_data); free(entity_data);
enet_host_destroy(server); enet_host_destroy(server);

@ -5,6 +5,6 @@
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
server(0); server("world.bin");
return 0; return 0;
} }

@ -31,6 +31,7 @@
#define EXPLOSION_RADIUS 1.0f #define EXPLOSION_RADIUS 1.0f
#define EXPLOSION_DAMAGE_THRESHOLD 0.2f // how much damage until it explodes #define EXPLOSION_DAMAGE_THRESHOLD 0.2f // how much damage until it explodes
#define GOLD_UNLOCK_RADIUS 1.0f #define GOLD_UNLOCK_RADIUS 1.0f
#define TIME_BETWEEN_WORLD_SAVE 5.0f
#define TIMESTEP (1.0f / 60.0f) // not required to simulate at this, but this defines what tick the game is on #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 TIME_BETWEEN_INPUT_PACKETS (1.0f / 20.0f)
@ -142,6 +143,8 @@ typedef struct Entity
EntityID next_free_entity; EntityID next_free_entity;
unsigned int generation; unsigned int generation;
bool no_save_to_disk; // stuff generated later on, like player's bodies or space stations that respawn.
float damage; // used by box and player float damage; // used by box and player
cpBody* body; // used by grid, player, and box cpBody* body; // used by grid, player, and box
cpShape* shape; // must be a box so shape_size can be set appropriately, and serialized cpShape* shape; // must be a box so shape_size can be set appropriately, and serialized
@ -263,8 +266,8 @@ void destroy(struct GameState* gs);
void process(struct GameState* gs, float dt); // does in place void process(struct GameState* gs, float dt); // does in place
Entity* closest_to_point_in_radius(struct GameState* gs, V2 point, float radius); Entity* closest_to_point_in_radius(struct GameState* gs, V2 point, float radius);
uint64_t tick(struct GameState* gs); uint64_t tick(struct GameState* gs);
void into_bytes(struct ServerToClient* msg, char* bytes, size_t* out_len, size_t max_len, Entity* for_this_player); void into_bytes(struct ServerToClient* msg, char* bytes, size_t* out_len, size_t max_len, Entity* for_this_player, bool write_varnames);
void from_bytes(struct ServerToClient* gs, char* bytes, size_t max_len); void from_bytes(struct ServerToClient* msg, char* bytes, size_t max_len, bool write_varnames, bool from_disk);
// entities // entities
Entity* get_entity(struct GameState* gs, EntityID id); Entity* get_entity(struct GameState* gs, EntityID id);

Loading…
Cancel
Save