diff --git a/gamestate.c b/gamestate.c index e103856..6618b6d 100644 --- a/gamestate.c +++ b/gamestate.c @@ -578,6 +578,9 @@ V2 entity_pos(Entity* e) if (e->is_box) { return V2add(entity_pos(box_grid(e)), V2rotate(entity_shape_pos(e), entity_rotation(box_grid(e)))); } + else if (e->is_explosion) { + return e->explosion_pos; + } else { assert(e->body != NULL); return cp_to_v2(cpBodyGetPosition(e->body)); @@ -608,13 +611,13 @@ void update_from(cpBody* body, struct BodyData* data) cpBodySetAngularVelocity(body, data->angular_velocity); } -//#define WRITE_VARNAMES // debugging feature horrible for network typedef struct SerState { 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; } SerState; void ser_var(SerState* ser, char* var_pointer, size_t var_size, const char* name, const char* file, int line) { @@ -805,6 +808,7 @@ void ser_entity(SerState* ser, GameState* gs, Entity* e) if (e->is_box) { SER_VAR(&e->box_type); + SER_VAR(&e->always_visible); SER_VAR(&e->is_explosion_unlock); ser_entityid(ser, &e->next_box); ser_entityid(ser, &e->prev_box); @@ -851,26 +855,45 @@ void ser_server_to_client(SerState* ser, ServerToClient* s) for (size_t i = 0; i < gs->cur_next_entity; i++) { Entity* e = &gs->entities[i]; - if (e->exists && !e->is_box) // boxes are serialized after their parent entities, their grid. +#define SER_ENTITY() SER_VAR(&entities_done); SER_VAR(&i); ser_entity(ser, gs, e) + if (e->exists) { - SER_VAR(&entities_done); - SER_VAR(&i); - ser_entity(ser, gs, e); + if (!e->is_box && !e->is_grid) + { + SER_ENTITY(); + } if (e->is_grid) { + bool this_grid_is_visible = false; // 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. BOXES_ITER(gs, cur, e) { - EntityID cur_id = get_id(gs, cur); - 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); + bool this_box_in_range = (ser->for_player == NULL || (ser->for_player != NULL && V2distsqr(entity_pos(ser->for_player), entity_pos(cur)) < VISION_RADIUS*VISION_RADIUS)); + if (cur->always_visible) this_box_in_range = true; + if (!this_grid_is_visible && this_box_in_range) + { + SER_ENTITY(); // serializes the grid the box is visible in + this_grid_is_visible = true; + break; + } + } + if (this_grid_is_visible) + { + BOXES_ITER(gs, cur, e) + { + EntityID cur_id = get_id(gs, cur); + 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); + } } + } } +#undef SER_ENTITY } entities_done = true; SER_VAR(&entities_done); @@ -904,7 +927,8 @@ 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) +// 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) { assert(msg->cur_gs != NULL); assert(msg != NULL); @@ -914,6 +938,7 @@ void into_bytes(struct ServerToClient* msg, char* bytes, size_t* out_len, size_t .serializing = true, .cursor = 0, .max_size = max_len, + .for_player = for_this_player, }; ser_server_to_client(&ser, msg); @@ -1058,7 +1083,7 @@ void entity_ensure_in_orbit(Entity* e) 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; } +#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(grid, pos) BOX_AT_TYPE(grid, pos, BoxHullpiece) bool indestructible = false; @@ -1070,27 +1095,27 @@ EntityID create_spacestation(GameState* gs) box_create(gs, explosion_box, grid, (V2) { 0 }); explosion_box->is_explosion_unlock = true; 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*3, 0 }), BoxHullpiece); - BOX_AT_TYPE(grid, ((V2) { BOX_SIZE*4, 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 * 4, 0 }), BoxHullpiece); indestructible = true; for (float y = -BOX_SIZE * 5.0; y <= BOX_SIZE * 5.0; y += BOX_SIZE) { - BOX_AT_TYPE(grid, ((V2) { BOX_SIZE*5.0, y }), BoxHullpiece); + BOX_AT_TYPE(grid, ((V2) { BOX_SIZE * 5.0, y }), BoxHullpiece); } for (float x = -BOX_SIZE * 5.0; x <= BOX_SIZE * 5.0; x += BOX_SIZE) { - BOX_AT_TYPE(grid, ((V2) { x, BOX_SIZE*5.0 }), BoxHullpiece); - BOX_AT_TYPE(grid, ((V2) { x, -BOX_SIZE*5.0 }), BoxHullpiece); + BOX_AT_TYPE(grid, ((V2) { x, BOX_SIZE * 5.0 }), BoxHullpiece); + BOX_AT_TYPE(grid, ((V2) { x, -BOX_SIZE * 5.0 }), BoxHullpiece); } indestructible = false; - BOX_AT_TYPE(grid, ((V2) { -BOX_SIZE*6.0, BOX_SIZE*5.0 }), BoxExplosive); - BOX_AT_TYPE(grid, ((V2) { -BOX_SIZE*6.0, BOX_SIZE*3.0 }), BoxExplosive); - BOX_AT_TYPE(grid, ((V2) { -BOX_SIZE*6.0, BOX_SIZE*1.0 }), BoxExplosive); - BOX_AT_TYPE(grid, ((V2) { -BOX_SIZE*6.0, -BOX_SIZE*2.0 }), BoxExplosive); - BOX_AT_TYPE(grid, ((V2) { -BOX_SIZE*6.0, -BOX_SIZE*3.0 }), BoxExplosive); - BOX_AT_TYPE(grid, ((V2) { -BOX_SIZE*6.0, -BOX_SIZE*5.0 }), BoxExplosive); + BOX_AT_TYPE(grid, ((V2) { -BOX_SIZE * 6.0, BOX_SIZE * 5.0 }), BoxExplosive); + BOX_AT_TYPE(grid, ((V2) { -BOX_SIZE * 6.0, BOX_SIZE * 3.0 }), BoxExplosive); + BOX_AT_TYPE(grid, ((V2) { -BOX_SIZE * 6.0, BOX_SIZE * 1.0 }), BoxExplosive); + BOX_AT_TYPE(grid, ((V2) { -BOX_SIZE * 6.0, -BOX_SIZE * 2.0 }), BoxExplosive); + BOX_AT_TYPE(grid, ((V2) { -BOX_SIZE * 6.0, -BOX_SIZE * 3.0 }), BoxExplosive); + BOX_AT_TYPE(grid, ((V2) { -BOX_SIZE * 6.0, -BOX_SIZE * 5.0 }), BoxExplosive); return get_id(gs, grid); } @@ -1125,7 +1150,6 @@ void process(GameState* gs, float dt) p->damage = 0.0f; gs->goldpos = (V2){ .x = hash11((float)gs->time) * 20.0f, .y = hash11((float)gs->time - 13.6f) * 20.0f }; } - #if 1 V2 world_hand_pos = get_world_hand_pos(gs, &player->input, p); if (player->input.seat_action) @@ -1263,7 +1287,7 @@ void process(GameState* gs, float dt) p->damage = clamp01(p->damage); } - if(get_entity(gs, gs->cur_spacestation) == NULL) + if (get_entity(gs, gs->cur_spacestation) == NULL) { gs->cur_spacestation = create_spacestation(gs); } diff --git a/main.c b/main.c index bf8dedc..d008f31 100644 --- a/main.c +++ b/main.c @@ -398,9 +398,7 @@ static void draw_dots(V2 camera_pos, float gap) for (int x = -num; x < num; x++) { for (int y = -num; y < num; y++) { V2 star = (V2){ (float)x * gap, (float)y * gap }; - if (fabsf(star.x - camera_pos.x) > VISION_RADIUS) - continue; - if (fabsf(star.y - camera_pos.y) > VISION_RADIUS) + if(V2lengthsqr(V2sub(star, camera_pos)) > VISION_RADIUS*VISION_RADIUS) continue; star.x += hash11(star.x * 100.0f + star.y * 67.0f) * gap; @@ -692,6 +690,7 @@ frame(void) sgp_translate(-camera_pos.x, -camera_pos.y); draw_dots(camera_pos, 1.5f); // in plane dots + // hand reached limit circle if (myentity() != NULL) { static float hand_reach_alpha = 1.0f; @@ -700,6 +699,13 @@ frame(void) draw_circle(entity_pos(myentity()), MAX_HAND_REACH); } + // vision circle, what player can see + if (myentity() != NULL) + { + set_color(colhexcode(0x4685e3)); + draw_circle(entity_pos(myentity()), VISION_RADIUS); + } + float halfbox = BOX_SIZE / 2.0f; // mouse frozen, debugging tool @@ -725,6 +731,8 @@ frame(void) } } + static float player_scaling = 1.0f; + player_scaling = lerp(player_scaling, zoom < 6.5f ? 100.0f : 1.0f, dt * 7.0f); for (size_t i = 0; i < gs.cur_next_entity; i++) { Entity* e = &gs.entities[i]; if (!e->exists) @@ -826,7 +834,8 @@ frame(void) 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); + printf("%f\n", zoom); + draw_texture_rectangle_centered(entity_pos(e), V2scale(PLAYER_SIZE, player_scaling)); sgp_reset_image(0); } } diff --git a/server.c b/server.c index 01e4ec1..8c954e1 100644 --- a/server.c +++ b/server.c @@ -229,16 +229,23 @@ void server(void* data) { continue; } + int this_player_index = (int)(int64_t)server->peers[i].data; + Entity* this_player_entity = get_entity(&gs, gs.players[this_player_index].entity); + if (this_player_entity == NULL) continue; // @Speed don't recreate the packet for every peer, gets expensive copying gamestate over and over again char* bytes_buffer = malloc(sizeof *bytes_buffer * MAX_BYTES_SIZE); char* compressed_buffer = malloc(sizeof * compressed_buffer * MAX_BYTES_SIZE); struct ServerToClient to_send; to_send.cur_gs = &gs; - to_send.your_player = (int)(int64_t)server->peers[i].data; + to_send.your_player = this_player_index; size_t len = 0; - into_bytes(&to_send, bytes_buffer, &len, MAX_BYTES_SIZE); - + into_bytes(&to_send, bytes_buffer, &len, MAX_BYTES_SIZE, this_player_entity); + if (len > MAX_BYTES_SIZE - 8) + { + Log("Too much data quitting!\n"); + exit(-1); + } size_t compressed_len = 0; lzo1x_1_compress(bytes_buffer, len, compressed_buffer, &compressed_len, (void*)lzo_working_mem); diff --git a/types.h b/types.h index 0cc3b93..8f66513 100644 --- a/types.h +++ b/types.h @@ -18,7 +18,7 @@ #define VISION_RADIUS 16.0f #define MAX_BYTES_SIZE 1024 * 128 // maximum size of serialized gamestate buffer #define SUN_RADIUS 10.0f -#define INSTANT_DEATH_DISTANCE_FROM_SUN 500.0f +#define INSTANT_DEATH_DISTANCE_FROM_SUN 2000.0f #define SUN_POS ((V2){50.0f,0.0f}) #define SUN_GRAVITY_STRENGTH (9.0e2f) #define SOLAR_ENERGY_PER_SECOND 0.02f @@ -170,6 +170,7 @@ typedef struct Entity // boxes bool is_box; + bool always_visible; // always serialized to the player enum BoxType box_type; bool is_explosion_unlock; EntityID next_box; @@ -262,7 +263,7 @@ 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 into_bytes(struct ServerToClient* msg, char* bytes, size_t* out_len, size_t max_len, Entity* for_this_player); void from_bytes(struct ServerToClient* gs, char* bytes, size_t max_len); // entities @@ -336,9 +337,14 @@ static V2 V2scale(V2 a, float f) }; } +static float V2lengthsqr(V2 v) +{ + return v.x * v.x + v.y * v.y; +} + static float V2length(V2 v) { - return sqrtf(v.x * v.x + v.y * v.y); + return sqrtf(V2lengthsqr(v)); } static V2 V2normalize(V2 v) @@ -346,6 +352,7 @@ static V2 V2normalize(V2 v) return V2scale(v, 1.0f / V2length(v)); } + static float V2dot(V2 a, V2 b) { return a.x * b.x + a.y * b.y; @@ -394,6 +401,11 @@ static inline float clamp01(float f) return fmaxf(0.0f, fminf(f, 1.0f)); } +static float V2distsqr(V2 from, V2 to) +{ + return V2lengthsqr(V2sub(to, from)); +} + static inline float clamp(float f, float minimum, float maximum) { if (f < minimum)