diff options
Diffstat (limited to 'src/server.c')
-rw-r--r-- | src/server.c | 318 |
1 files changed, 266 insertions, 52 deletions
diff --git a/src/server.c b/src/server.c index 73a6475..fa48b25 100644 --- a/src/server.c +++ b/src/server.c @@ -4,22 +4,27 @@ #include <stdio.h> #include <stdlib.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netdb.h> -#include <sys/select.h> -#include <arpa/inet.h> -#include <netinet/in.h> +#include <stddef.h> #include <stdint.h> -#include <png.h> -#include <endian.h> #include <stdbool.h> #include <string.h> +#include <endian.h> #include <unistd.h> +#include <inttypes.h> #include <fcntl.h> +#include <errno.h> +#include <poll.h> +#include <netdb.h> +#include <png.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include "array.h" #include "content.h" #include "peer.h" #include "str.h" +#include "ser.h" typedef enum { MAP_VALLEY, @@ -42,26 +47,27 @@ typedef struct { } map; typedef struct { - str motd; - array(map) maps; -} game; - -typedef struct { + bool auth; str name; + uint64_t id; int32_t x, y; - peer net; + int8_t z; + peer conn; } player; +typedef struct { + str motd; + str passphrase; + array(map) maps; + arraybuf(player) players; + uint64_t entity_id; +} game; + node *map_local_node(map *m, int32_t x, int32_t y) { if (x < 0 || y < 0 || (uint32_t) x >= m->width || (uint32_t) y >= m->height) return NULL; - - node *n = &m->nodes[x*m->width+y]; - if (!n->present) - return NULL; - - return n; + return &m->nodes[x*m->height+y]; } node *map_node(game *g, int32_t x, int32_t y, int8_t z) @@ -71,17 +77,42 @@ node *map_node(game *g, int32_t x, int32_t y, int8_t z) if (m->z != z) continue; node *n = map_local_node(m, x - m->off_x, y - m->off_y); - if (n != NULL) - return n; + if (n == NULL || !n->present) + continue; + return n; } return NULL; } -/* void map_load_node(map *m, uint32_t x, uint32_t y, uint32_t color) { - -}*/ + node *n = map_local_node(m, x, y); + n->present = color != 0xffffff; + + if (!n->present) + return; + + n->z = 0; + n->col = u32_color(color); + switch (color) { + case 0x00880d: n->type = N_GRASS; break; // TODO: color + case 0x595959: n->type = N_ROCK; n->z = 1; break; + case 0x484848: n->type = N_ROCK; n->z = 2; break; + case 0x373737: n->type = N_ROCK; n->z = 3; break; + case 0x00ffe8: n->type = N_MOUNTAIN_FLOWER; break; + case 0xa09700: n->type = N_PATH; break; + case 0x015100: n->type = N_NEEDLE_TREE; break; + case 0x00c6ff: n->type = N_WATER; n->z = -1; break; + case 0x5c3b12: n->type = N_PLANK; break; + case 0xff9de2: n->type = N_VALLEY_FLOWER; break; // TODO: color + case 0x016300: n->type = N_BIG_TREE; break; + default: + fprintf(stderr, "invalid color in map %*s at %"PRIu32" %"PRIu32": %06x\n", + PSTR(m->name), x, y, color); + n->present = false; + break; + } +} bool map_load(map *m) { @@ -95,12 +126,12 @@ bool map_load(map *m) png_structp png = NULL; png_infop info = NULL; - TRY((file = fopen(m->filename, "r")) != NULL, "failed to open %s\n", m->filename); + TRY((file = fopen(m->filename, "r")) != NULL, "failed to open %s\n", m->filename) TRY(png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL), - "png_create_read_struct failed\n"); + "png_create_read_struct failed\n") - TRY(info = png_create_info_struct(png), "png_create_info_struct failed\n"); + TRY(info = png_create_info_struct(png), "png_create_info_struct failed\n") png_init_io(png, file); png_read_info(png, info); @@ -109,11 +140,13 @@ bool map_load(map *m) png_uint_32 height = png_get_image_height(png, info); png_byte color_type = png_get_color_type(png, info); - TRY(width == m->width, "%s: width mismatch\n", m->filename); - TRY(height == m->height, "%s: height mismatch\n", m->filename); - TRY(color_type == PNG_COLOR_TYPE_RGB, "%s: color type is not RGB\n", m->filename); + TRY(width == m->width, "%s: width mismatch\n", m->filename) + TRY(height == m->height, "%s: height mismatch\n", m->filename) + TRY(color_type == PNG_COLOR_TYPE_RGB, "%s: color type is not RGB\n", m->filename) + + m->nodes = malloc(m->width * m->height * sizeof *m->nodes); - uint32_t colors[100] = {0}; + // uint32_t colors[100] = {0}; png_uint_32 pitch = png_get_rowbytes(png, info); png_byte row[pitch]; @@ -123,7 +156,9 @@ bool map_load(map *m) for (png_uint_32 x = 0; x < width; x++) { png_bytep p = &row[x*3]; - uint32_t color = ((uint32_t) p[2]) | (((uint32_t) p[1]) << 8) | (((uint32_t) p[0]) << 16); + uint32_t col = color_u32((color) { p[0], p[1], p[2] }); + + /* for (size_t i = 0; i < 100; i++) { if (colors[i] == color) { break; @@ -132,14 +167,16 @@ bool map_load(map *m) printf("#%06x\n", color); break; } - } + }*/ + + map_load_node(m, x, y, col); } } fclose(file); png_destroy_read_struct(&png, &info, NULL); return true; -#undef TRY +#undef TRY } int net_listen(const char *host, const char *port) @@ -160,7 +197,7 @@ int net_listen(const char *host, const char *port) TRY("socket", fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol)) int flag = 1; - TRY("setsockopt", setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &flag, sizeof flag)); + TRY("setsockopt", setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &flag, sizeof flag)) TRY("bind", bind(fd, info->ai_addr, info->ai_addrlen)) TRY("listen", listen(fd, 3)) @@ -172,26 +209,155 @@ int net_listen(const char *host, const char *port) } -int game_exit(game *g, int ret) +void ser_color(strbuf *w, color c) { - free(g->motd.data); + ser_u8(w, c.r); + ser_u8(w, c.g); + ser_u8(w, c.b); +} + +void ser_node(strbuf *w, node *n) +{ + bool present = n != NULL && n->present; + ser_bool(w, present); + if (!present) + return; + ser_i8(w, n->z); + ser_color(w, n->col); +} + +bool player_auth(player *p) +{ + return p->auth; +} + +#define SEND_PKT(TYPE, PEERS, NPEERS, FILTER, ...) \ + { strbuf pkt = NILSBUF; \ + ser_pkt_type(&pkt, TYPE); \ + __VA_ARGS__ \ + for (size_t i = 0; i < NPEERS; i++) { \ + if (FILTER(&(PEERS)[i]) && !peer_send(&(PEERS)[i].conn, pkt.data, pkt.len)) \ + fprintf(stderr, "failed to send " #TYPE " to %*s\n", PSTR((PEERS)[i].name));} \ + free(pkt.data); } - if (g->maps.data) - for (size_t i = 0; i < g->maps.len; i++) { - free(g->maps.data[i].name.data); - free(g->maps.data[i].nodes); +void send_players(game *g) +{ + SEND_PKT(CPKT_PLAYERS, g->players.data, g->players.len, player_auth, + ser_u16(&pkt, g->players.len); + for (size_t i = 0; i < g->players.len; i++) { + player *p = &g->players.data[i]; + if (!p->auth) continue; + ser_str(&pkt, p->name); + ser_u64(&pkt, p->id); } - + ) +} + +// send_nudes when +void send_nodes(player *p, game *g, int8_t z, int32_t x, int32_t y, uint32_t w, uint32_t h) +{ + SEND_PKT(CPKT_NODES, p, 1,, + ser_i8(&pkt, z); + ser_i32(&pkt, x); + ser_i32(&pkt, y); + ser_u32(&pkt, w); + ser_u32(&pkt, h); + for (uint32_t xi = 0; xi < w; xi++) + for (uint32_t yi = 0; yi < w; yi++) + ser_node(&pkt, map_node(g, x+xi, y+yi, z)); + ) +} + +void player_free(player *p) +{ + peer_free(&p->conn); + if (p->auth) + free(p->name.data); +} + +void player_remove(player *p, game *g) +{ + player_free(p); + + bool auth = p->auth; + ARR_REMOVE(g->players, p); + if (auth) send_players(g); +} + +void game_exit(game *g, int ret) +{ + free(g->motd.data); + free(g->passphrase.data); + + for (size_t i = 0; i < g->maps.len; i++) { + free(g->maps.data[i].name.data); + free(g->maps.data[i].nodes); + } free(g->maps.data); + for (size_t i = 0; i < g->players.len; i++) + player_free(&g->players.data[i]); + free(g->players.data); + exit(ret); } +bool handle_hi(str pkt, player *p, game *g) +{ + str name, pass; + if (! (deser_str(&pkt, &name) && deser_str(&pkt, &pass))) + return false; + + if (str_cmp(g->passphrase, pass) != 0) { + fprintf(stderr, "wrong passphrase from %*s\n", PSTR(name)); // TODO: log ip ? + SEND_PKT(CPKT_FAIL, p, 1,, ser_fail_reason(&pkt, FAIL_WRONG_PASS);) + return true; // valid pkt, but invalid passphrase + } + + for (size_t i = 0; i < g->players.len; i++) { + player *p2 = &g->players.data[i]; + if (p2->auth && str_cmp(p2->name, name) == 0) { + SEND_PKT(CPKT_FAIL, p, 1,, ser_fail_reason(&pkt, FAIL_ALREADY_ONLINE);) + return true; + } + } + + p->auth = true; + p->name = str_clone(name); + p->x = p->y = 0; + p->z = 0; + + SEND_PKT(CPKT_HI, p, 1,, ser_str(&pkt, g->motd);) + send_players(g); + + uint32_t range = 10; + send_nodes(p, g, p->z, p->x-range, p->y-range, range*2, range*2); + + return true; +} + +bool process_pkt(str pkt, player *p, game *g) +{ + pkt_type type; + if (!deser_pkt_type(&pkt, &type)) + return false; + + if ((type == SPKT_HI) == p->auth) + return false; + + switch (type) { + case SPKT_HI: return handle_hi(pkt, p, g); + default: return false; + } +} + int main() { game g = {0}; + g.entity_id = 1; g.motd = str_clone(S("Welcome to test server")); + g.passphrase = str_clone(S("")); g.maps.len = 1; g.maps.data = malloc(g.maps.len * sizeof *g.maps.data); @@ -215,16 +381,64 @@ int main() game_exit(&g, EXIT_FAILURE); for (;;) { - int clt_sock = accept(accept_fd, NULL, NULL); - if (clt_sock < 0) { - perror("accept"); - continue; + struct pollfd fds[g.players.len + 1]; + + for (size_t i = 0; i < g.players.len;) { + player *p = &g.players.data[i]; + if (p->conn.disco) { + printf("player disco\n"); + player_remove(p, &g); + continue; + } + + fds[i++] = peer_prepare(&p->conn); } - if (fcntl(clt_sock, F_SETFL, fcntl(clt_sock, F_GETFL, 0) | O_NONBLOCK) < 0) { - perror("fcntl"); - continue; - } + fds[g.players.len].fd = accept_fd; + fds[g.players.len].events = POLLIN; + + if (poll(fds, g.players.len + 1, -1) < 0) { + switch (errno) { + case EINTR: continue; + default: perror("poll"); continue; + } + } + + for (size_t i = 0; i < g.players.len; i++) { + player *p = &g.players.data[i]; + if (!peer_ready(&p->conn, fds[i])) + continue; + if (p->conn.disco) + continue; + str pkt = { p->conn.in.len, (char *) p->conn.in.buffer }; + if (!process_pkt(pkt, p, &g)) { + // TODO: maybe inform client about failure? not sure + // FIXME: hexdumping a gazillon bytes to stderr might be an issue + fprintf(stderr, "invalid pkt from %*s: ", PSTR(p->name)); + for (size_t i = 0; i < pkt.len; i++) + fprintf(stderr, "%02x%c", (uint8_t) pkt.data[i], i+1 == pkt.len ? '\n' : ' '); + } + } + + if (fds[g.players.len].revents) { + int socket = accept(accept_fd, NULL, NULL); // TODO: save ip + if (socket < 0) { + perror("accept"); + continue; + } + + if (fcntl(socket, F_SETFL, fcntl(socket, F_GETFL, 0) | O_NONBLOCK) < 0) { + close(socket); + perror("fcntl"); + continue; + } + + printf("new player\n"); + player *p = ARR_APPEND(g.players); + p->auth = false; + p->id = g.entity_id++; + p->name = S("unauthenticated"); + peer_init(&p->conn, socket); + } } - } |