From 1c53c39feed656194daf2fc24ddc4d2ee6abf728 Mon Sep 17 00:00:00 2001 From: Lizzy Fleckenstein Date: Sat, 13 Jul 2024 13:26:12 +0200 Subject: server: handle entity z coordinate Signed-off-by: Lizzy Fleckenstein --- src/server.c | 126 ++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 78 insertions(+), 48 deletions(-) diff --git a/src/server.c b/src/server.c index 108ae0d..bab8c03 100644 --- a/src/server.c +++ b/src/server.c @@ -24,6 +24,11 @@ #include "sig.h" #include "chunk.h" +#define MAYBE(T) \ + typedef struct { bool present; T value; } maybe_##T; \ + maybe_##T some_##T(T value) { return (maybe_##T) { true, value }; } \ + maybe_##T none_##T = { 0 }; + typedef enum { MAP_VALLEY, MAP_ISLAND, @@ -32,6 +37,18 @@ typedef enum { MAP_NUM, } map_type; +typedef struct { + entity base; + int8_t z; +} srv_entity; + +typedef struct { + vec2 xy; + int8_t z; +} pos; + +MAYBE(pos) + typedef struct { const char *filename; str name; @@ -44,7 +61,6 @@ typedef struct { bool auth; str name; entity_id id; - int8_t z; // TODO: move to entity peer conn; } player; @@ -54,7 +70,7 @@ typedef struct { str passphrase; array(map) maps; arraybuf(player) players; - arraybuf(entity) entities; + arraybuf(srv_entity) entities; entity_id next_entity; } game; @@ -66,37 +82,42 @@ player *player_from_id(game *g, entity_id id) return NULL; } -entity *entity_from_id(game *g, entity_id id) +srv_entity *entity_from_id(game *g, entity_id id) { for (size_t i = 0; i < g->entities.len; i++) - if (g->entities.data[i].id == id) + if (g->entities.data[i].base.id == id) return &g->entities.data[i]; return NULL; } +pos entity_pos(srv_entity *e) +{ + return (pos) { e->base.pos, e->z }; +} + box2 player_sight(vec2 center) { return box2_around(center, SIGHT_RANGE); } -void ser_entity_update(strbuf *w, entity *e, entity_cmd cmd) +void ser_entity_update(strbuf *w, srv_entity *e, entity_cmd cmd) { - ser_entity_id(w, e->id); + ser_entity_id(w, e->base.id); ser_entity_cmd(w, cmd); switch (cmd) { case ENTITY_ADD: - ser_entity_type(w, e->type); - ser_vec2(w, e->pos); - switch (e->type) { + ser_entity_type(w, e->base.type); + ser_vec2(w, e->base.pos); + switch (e->base.type) { case ENTITY_FLOWER: - ser_flower_type(w, e->flower); + ser_flower_type(w, e->base.flower); break; default: break; } break; case ENTITY_MOVE: - ser_vec2(w, e->pos); + ser_vec2(w, e->base.pos); break; case ENTITY_REMOVE: break; @@ -105,19 +126,22 @@ void ser_entity_update(strbuf *w, entity *e, entity_cmd cmd) } } -static vec2 *moved_get_pos(entity *e, entity *moved, vec2 *p_moved) +static maybe_pos moved_get_pos(srv_entity *e, srv_entity *moved, maybe_pos p_moved) { - return (e == moved) ? p_moved : &e->pos; + return (e == moved) ? p_moved : some_pos(entity_pos(e)); } -static bool moved_can_see(entity *a, entity *b, entity *moved, vec2 *p_moved) +static bool moved_can_see(srv_entity *a, srv_entity *b, srv_entity *moved, maybe_pos p_moved) { - vec2 *p_a = moved_get_pos(a, moved, p_moved); - vec2 *p_b = moved_get_pos(b, moved, p_moved); - return p_a && p_b && box2_contains(player_sight(*p_a), *p_b); + maybe_pos p_a = moved_get_pos(a, moved, p_moved); + maybe_pos p_b = moved_get_pos(b, moved, p_moved); + return p_a.present + && p_b.present + && p_a.value.z == p_b.value.z + && box2_contains(player_sight(p_a.value.xy), p_b.value.xy); } -static entity_cmd moved_check_update(entity *observer, entity *target, entity *moved, vec2 *before, vec2 *after) +static entity_cmd moved_check_update(srv_entity *observer, srv_entity *target, srv_entity *moved, maybe_pos before, maybe_pos after) { bool see_before = moved_can_see(observer, target, moved, before); bool see_after = moved_can_see(observer, target, moved, after); @@ -132,18 +156,18 @@ static entity_cmd moved_check_update(entity *observer, entity *target, entity *m return ENTITY_CMD_COUNT; } -void moved_entity(game *g, entity *moved, vec2 *before, vec2 *after) +void moved_entity(game *g, srv_entity *moved, maybe_pos before, maybe_pos after) { for (size_t i = 0; i < g->players.len; i++) { player *p = &g->players.data[i]; if (!p->auth) continue; - entity *observer = entity_from_id(g, p->id); + srv_entity *observer = entity_from_id(g, p->id); if (observer == moved) { SEND_PKT(p->conn, CPKT_ENTITY, for (size_t j = 0; j < g->entities.len; j++) { - entity *target = &g->entities.data[j]; + srv_entity *target = &g->entities.data[j]; entity_cmd cmd = moved_check_update(observer, target, moved, before, after); if (cmd != ENTITY_CMD_COUNT) ser_entity_update(&pkt, target, cmd); @@ -185,18 +209,19 @@ bool silly_noise(vec2 pos, uint32_t grid, double sillyness, int seed) return x % grid == 0 && y % grid == 0; } -entity *spawn_entity(game *g, entity_type type, vec2 pos) +srv_entity *spawn_entity(game *g, entity_type type, pos p) { - entity *e = ARR_APPEND(g->entities); - e->id = g->next_entity++; - e->type = type; - e->pos = pos; + srv_entity *e = ARR_APPEND(g->entities); + e->base.id = g->next_entity++; + e->base.type = type; + e->base.pos = p.xy; + e->z = p.z; return e; } -void remove_entity(game *g, entity *e) +void remove_entity(game *g, srv_entity *e) { - moved_entity(g, e, &e->pos, NULL); + moved_entity(g, e, some_pos(entity_pos(e)), none_pos); ARR_REMOVE(g->entities, e); } @@ -204,7 +229,7 @@ void map_load_node(game *g, map *m, uvec2 v, color col) { uint32_t ucol = color_to_u32(col); - vec2 pos = vec2_add(m->chunk.bounds.pos, CVEC2(v)); + pos p = { vec2_add(m->chunk.bounds.pos, CVEC2(v)), m->z }; node *n = chunk_index(m->chunk, v); *n = (node) { 0 }; @@ -215,30 +240,30 @@ void map_load_node(game *g, map *m, uvec2 v, color col) switch (ucol) { case 0xff9de2: - spawn_entity(g, ENTITY_FLOWER, pos)->flower = rand() % FLOWER_COUNT; + spawn_entity(g, ENTITY_FLOWER, p)->base.flower = rand() % FLOWER_COUNT; [[fallthrough]]; case 0x00880d: n->type = N_GRASS; - n->variant = silly_noise(pos, 3, 1.0, 0); + n->variant = silly_noise(p.xy, 3, 1.0, 0); break; case 0x595959: n->type = N_ROCK; n->z = 1; break; case 0x001b51: - spawn_entity(g, ENTITY_FLOWER, pos)->flower = FLOWER_DANDELION; + spawn_entity(g, ENTITY_FLOWER, p)->base.flower = FLOWER_DANDELION; [[fallthrough]]; case 0x484848: n->type = N_ROCK; n->z = 2; break; case 0x00ffe8: - spawn_entity(g, ENTITY_FLOWER, pos)->flower = FLOWER_DANDELION; + spawn_entity(g, ENTITY_FLOWER, p)->base.flower = FLOWER_DANDELION; [[fallthrough]]; case 0x373737: n->type = N_ROCK; n->z = 3; break; case 0xa09700: n->type = N_PATH; break; case 0x015100: n->type = N_NEEDLE_TREE; break; case 0x00c6ff: n->type = N_WATER; - n->variant = silly_noise(pos, 3, 0.7, 1); + n->variant = silly_noise(p.xy, 3, 0.7, 1); n->z = -1; break; case 0x5c3b12: n->type = N_PLANK; break; case 0x016300: n->type = N_BIG_TREE; break; case 0xfce84e: n->type = N_SAND; - n->variant = silly_noise(pos, 4, 1.0, 1); + n->variant = silly_noise(p.xy, 4, 1.0, 1); break; default: fprintf(stderr, "invalid color in map %.*s at %"PRIu32" %"PRIu32": %06x\n", @@ -348,7 +373,7 @@ void player_remove(player *p, game *g) player_cleanup(p); bool auth = p->auth; - entity_id id = p->id; + entity_id id = p->id; ARR_REMOVE(g->players, p); @@ -401,17 +426,19 @@ bool handle_hi(str pkt, player *p, game *g) p->auth = true; p->name = str_clone(name); - entity *e = ARR_APPEND(g->entities); - e->id = p->id; - e->type = ENTITY_PLAYER; - e->pos.x = e->pos.y = 0; - p->z = 0; + srv_entity *e = ARR_APPEND(g->entities); + e->base.id = p->id; + e->base.type = ENTITY_PLAYER; + e->base.pos.x = e->base.pos.y = 0; + e->z = 0; SEND_PKT(p->conn, CPKT_HI, ser_str(&pkt, g->motd);) send_players(g); - moved_entity(g, e, NULL, &e->pos); - send_nodes(p, g, player_sight(e->pos), p->z); + pos ent_p = entity_pos(e); + + moved_entity(g, e, none_pos, some_pos(ent_p)); + send_nodes(p, g, player_sight(ent_p.xy), ent_p.z); return true; } @@ -425,16 +452,19 @@ bool handle_move(str pkt, player *p, game *g) if (vec2_iszero(v)) return false; - entity *e = entity_from_id(g, p->id); + srv_entity *e = entity_from_id(g, p->id); + + pos old_pos = entity_pos(e); // yolo - vec2 old_pos = e->pos; - e->pos = vec2_add(v, e->pos); + e->base.pos = vec2_add(v, e->base.pos); + + pos new_pos = entity_pos(e); - moved_entity(g, e, &old_pos, &e->pos); + moved_entity(g, e, some_pos(old_pos), some_pos(new_pos)); // TODO: only send newly visible nodes - send_nodes(p, g, player_sight(e->pos), p->z); + send_nodes(p, g, player_sight(new_pos.xy), new_pos.z); return true; } -- cgit v1.2.3