summaryrefslogtreecommitdiff
path: root/src/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/server.c')
-rw-r--r--src/server.c230
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;
+ }
+ }
+
+}