diff options
Diffstat (limited to 'src/server.c')
-rw-r--r-- | src/server.c | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/src/server.c b/src/server.c new file mode 100644 index 0000000..73a6475 --- /dev/null +++ b/src/server.c @@ -0,0 +1,230 @@ +// SPDX-FileCopyrightText: 2024 Lizzy Fleckenstein <lizzy@vlhl.dev> +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +#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 <stdint.h> +#include <png.h> +#include <endian.h> +#include <stdbool.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include "content.h" +#include "peer.h" +#include "str.h" + +typedef enum { + MAP_VALLEY, + MAP_ISLAND, + MAP_MOUNTAIN, + MAP_CAVE, + MAP_NUM, +} map_type; + +typedef struct { + const char *filename; + str name; + map_type type; + uint32_t width; + uint32_t height; + int32_t off_x; + int32_t off_y; + int8_t z; // caves: -1 + node *nodes; +} map; + +typedef struct { + str motd; + array(map) maps; +} game; + +typedef struct { + str name; + int32_t x, y; + peer net; +} player; + +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; +} + +node *map_node(game *g, int32_t x, int32_t y, int8_t z) +{ + for (size_t i = 0; i < g->maps.len; i++) { + map *m = &g->maps.data[i]; + if (m->z != z) + continue; + node *n = map_local_node(m, x - m->off_x, y - m->off_y); + if (n != NULL) + return n; + } + return NULL; +} + +/* +void map_load_node(map *m, uint32_t x, uint32_t y, uint32_t color) +{ + +}*/ + +bool map_load(map *m) +{ +#define TRY(expr, ...) if (!(expr)) { \ + fprintf(stderr, __VA_ARGS__); \ + if (file != NULL) fclose(file); \ + png_destroy_read_struct(&png, &info, NULL); \ + return false; } + + FILE *file = NULL; + png_structp png = NULL; + png_infop info = NULL; + + 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"); + + TRY(info = png_create_info_struct(png), "png_create_info_struct failed\n"); + + png_init_io(png, file); + png_read_info(png, info); + + png_uint_32 width = png_get_image_width(png, info); + 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); + + uint32_t colors[100] = {0}; + + png_uint_32 pitch = png_get_rowbytes(png, info); + png_byte row[pitch]; + for (png_uint_32 y = 0; y < height; y++) { + png_read_row(png, row, NULL); + + 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); + for (size_t i = 0; i < 100; i++) { + if (colors[i] == color) { + break; + } else if (colors[i] == 0) { + colors[i] = color; + printf("#%06x\n", color); + break; + } + } + } + } + + fclose(file); + png_destroy_read_struct(&png, &info, NULL); + return true; +#undef TRY +} + +int net_listen(const char *host, const char *port) +{ + struct addrinfo *info = NULL, hints = {0}; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + int err; + if ((err = getaddrinfo(host, port, &hints, &info))) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err)); + return -1; + } + +#define TRY(op, val) if ((val) < 0) { perror(op); freeaddrinfo(info); return -1; } + + int fd; + 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("bind", bind(fd, info->ai_addr, info->ai_addrlen)) + TRY("listen", listen(fd, 3)) + +#undef TRY + + freeaddrinfo(info); + return fd; + +} + +int game_exit(game *g, int ret) +{ + free(g->motd.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); + } + + free(g->maps.data); + + exit(ret); +} + +int main() +{ + game g = {0}; + + g.motd = str_clone(S("Welcome to test server")); + + g.maps.len = 1; + g.maps.data = malloc(g.maps.len * sizeof *g.maps.data); + g.maps.data[0] = (map) { + .filename = "assets/map_valley.png", + .name = str_clone(S("Valley")), + .type = MAP_VALLEY, + .off_x = -50, + .off_y = -50, + .z = 0, + .width = 100, + .height = 100, + .nodes = NULL, + }; + + if (!map_load(&g.maps.data[0])) + game_exit(&g, EXIT_FAILURE); + + int accept_fd = net_listen("0.0.0.0", "4560"); + if (accept_fd < 0) + game_exit(&g, EXIT_FAILURE); + + for (;;) { + int clt_sock = accept(accept_fd, NULL, NULL); + if (clt_sock < 0) { + perror("accept"); + continue; + } + + if (fcntl(clt_sock, F_SETFL, fcntl(clt_sock, F_GETFL, 0) | O_NONBLOCK) < 0) { + perror("fcntl"); + continue; + } + } + +} |