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