// SPDX-FileCopyrightText: 2024 Lizzy Fleckenstein // // SPDX-License-Identifier: AGPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; } } }