summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/array.h19
-rw-r--r--include/content.h45
-rw-r--r--include/peer.h7
-rw-r--r--include/ser.h6
-rw-r--r--include/str.h12
-rw-r--r--meson.build14
-rw-r--r--src/peer.c26
-rw-r--r--src/ser.c45
-rw-r--r--src/server.c318
9 files changed, 391 insertions, 101 deletions
diff --git a/include/array.h b/include/array.h
new file mode 100644
index 0000000..8d52619
--- /dev/null
+++ b/include/array.h
@@ -0,0 +1,19 @@
+// SPDX-FileCopyrightText: 2024 Lizzy Fleckenstein <lizzy@vlhl.dev>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+#ifndef ARRAY_H
+#define ARRAY_H
+
+#include <stddef.h>
+
+#define len(X) (sizeof X / sizeof *X)
+#define array(T) struct { size_t len; T *data; }
+#define arraybuf(T) struct { size_t cap; size_t len; T *data; }
+
+#define ARR_REMOVE(A, P) memmove((P), (P)+1, --(A).len * sizeof *(P) - ((P) - (A).data));
+#define ARR_APPEND(A) (((A).cap == (A).len) \
+ ? (A).data = realloc((A).data, sizeof *(A).data * ((A).cap = (A).cap ? (A).cap * 2 : 1)) \
+ : NULL, &((A).data)[(A).len++])
+
+#endif
diff --git a/include/content.h b/include/content.h
index ac20e7e..ed8b822 100644
--- a/include/content.h
+++ b/include/content.h
@@ -5,6 +5,9 @@
#ifndef CONTENT_H
#define CONTENT_H
+#include <stdint.h>
+#include <stdbool.h>
+
typedef enum {
N_VALLEY_FLOWER,
N_MOUNTAIN_FLOWER,
@@ -14,12 +17,24 @@ typedef enum {
N_WATER,
N_PLANK,
N_PATH,
+ N_GRASS,
+ N_SAND,
} node_type;
typedef struct {
uint8_t r, g, b;
} color;
+static inline uint32_t color_u32(color c)
+{
+ return ((uint32_t) c.b) | (((uint32_t) c.g) << 8) | (((uint32_t) c.r) << 16);
+}
+
+static inline color u32_color(uint32_t u)
+{
+ return (color) { (u >> 16) & 0xFF, (u >> 8) & 0xFF, u & 0xFF };
+}
+
typedef struct {
bool present;
node_type type;
@@ -28,22 +43,36 @@ typedef struct {
} node;
typedef enum {
- MOVE_UP,
+ MOVE_UP = 0,
MOVE_DOWN,
MOVE_LEFT,
MOVE_RIGHT,
} move_dir;
-typedef enum {
- CPKT_HI, // len motd
- CPKT_PLAYERS, // len
+typedef uint8_t fail_reason;
+#define ser_fail_reason ser_u8
+#define deser_fail_reason deser_u8
+
+enum {
+ FAIL_WRONG_PASS = 0,
+ FAIL_ALREADY_ONLINE,
+};
+
+typedef uint16_t pkt_type;
+#define ser_pkt_type ser_u16
+#define deser_pkt_type deser_u16
+
+enum {
+ CPKT_HI = 0, // len motd
+ CPKT_FAIL, // fail_reason
+ CPKT_PLAYERS, // len [len name id]
CPKT_MOVE, // player_id remove x y z
CPKT_NODES, // z x y w h [node]
-} client_pkt;
+};
-typedef enum {
- SPKT_HI, // len name len password
+enum {
+ SPKT_HI = 0, // len name len password
SPKT_MOVE, // move_dir
-} server_pkt;
+};
#endif
diff --git a/include/peer.h b/include/peer.h
index 63de9e5..f6baa4b 100644
--- a/include/peer.h
+++ b/include/peer.h
@@ -8,6 +8,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
+#include <poll.h>
#define PEER_INBUFFER_SIZE 0x100000 // 1MB
#define PEER_OUTBUFFER_SIZE 0x200000 // 2MB
@@ -33,8 +34,8 @@ typedef struct {
void peer_init(peer *p, int socket);
void peer_free(peer *p);
-short peer_prepare(peer *p);
-bool peer_ready(peer *p, short revents);
-bool peer_send(peer *p, uint8_t *data, size_t len);
+struct pollfd peer_prepare(peer *p);
+bool peer_ready(peer *p, struct pollfd revents);
+bool peer_send(peer *p, void *data, size_t len);
#endif
diff --git a/include/ser.h b/include/ser.h
index c1f207d..374e5d9 100644
--- a/include/ser.h
+++ b/include/ser.h
@@ -12,8 +12,9 @@
// ser
-void ser_bytes(strbuf *w, size_t len, uint8_t *x);
+void ser_bytes(strbuf *w, void *x, size_t len);
+void ser_bool(strbuf *w, bool x);
void ser_str(strbuf *w, str x);
void ser_u8(strbuf *w, uint8_t x);
@@ -30,8 +31,9 @@ void ser_i64(strbuf *w, int64_t x);
// deser
-bool deser_bytes(str *r, size_t len, uint8_t *buf);
+bool deser_bytes(str *r, void *buf, size_t len);
+bool deser_bool(str *r, bool *buf);
bool deser_str(str *r, str *buf); // returns slice!
bool deser_u8(str *r, uint8_t *buf);
diff --git a/include/str.h b/include/str.h
index b208515..277a6f3 100644
--- a/include/str.h
+++ b/include/str.h
@@ -7,20 +7,18 @@
#include <stddef.h>
#include <stdbool.h>
+#include "array.h"
// string library taken from cuddlesOS:
// https://github.com/cuddlesOS/cuddles/blob/master/stage3/string.c
-// arr
-#define len(X) (sizeof X / sizeof *X)
-#define array(T) struct { size_t len; T *data; }
-
typedef array(char) str;
-#define S(X) ((str) { len(X)-1, X })
#define NILS ((str) { 0, NULL })
+#define S(X) ((str) { len(X)-1, X })
+#define PSTR(X) (int) (X).len, (X).data
-typedef struct { size_t cap; str buf; } strbuf;
-#define NILSBUF ((strbuf) { 0, NILS })
+typedef arraybuf(char) strbuf;
+#define NILSBUF ((strbuf) { 0, 0, NULL })
// compares two strings by length and ASCII values. return value:
// < 0 if s1 < s2
diff --git a/meson.build b/meson.build
index 469ca20..e1b8dce 100644
--- a/meson.build
+++ b/meson.build
@@ -9,12 +9,16 @@ deps = [
dependency('perlin'),
]
-server = executable('server',
+src = [
+ 'src/str.c',
+ 'src/peer.c',
+ 'src/ser.c',
+]
+
+sillyserver = executable('sillyserver',
sources: [
+ src,
'src/server.c',
- 'src/str.c',
- 'src/peer.c',
- 'src/ser.c',
],
include_directories: 'include/',
dependencies: [
@@ -24,4 +28,4 @@ server = executable('server',
install: true,
)
-run_target('run', command: server)
+run_target('run', command: sillyserver)
diff --git a/src/peer.c b/src/peer.c
index f719622..f984aec 100644
--- a/src/peer.c
+++ b/src/peer.c
@@ -61,7 +61,7 @@ static bool peer_in_ready(peer *p)
if (p->in.len == p->in.promised && p->in.len != 0) {
if (!p->in.header)
return true;
-
+
size_t len = *(pkt_header *) p->in.buffer;
if (len > PEER_INBUFFER_SIZE)
// TODO: figure out what to do if packet too large (disconnect?)
@@ -75,7 +75,7 @@ static bool peer_in_ready(peer *p)
// out
-static void send_raw(peer *p, uint8_t *data, size_t len)
+static void send_raw(peer *p, void *data, size_t len)
{
memcpy(p->out.buffer + p->out.cursor + p->out.avail, data, len);
p->out.avail += len;
@@ -94,7 +94,7 @@ static bool out_space(peer *p, size_t len)
return true;
}
-bool peer_send(peer *p, uint8_t *data, size_t len)
+bool peer_send(peer *p, void *data, size_t len)
{
if (len > PEER_INBUFFER_SIZE)
return false;
@@ -104,7 +104,7 @@ bool peer_send(peer *p, uint8_t *data, size_t len)
if (!out_space(p, sizeof hdr + len))
return false;
- send_raw(p, (uint8_t *) &hdr, sizeof hdr);
+ send_raw(p, &hdr, sizeof hdr);
send_raw(p, data, len);
return true;
@@ -135,21 +135,29 @@ static bool peer_out_ready(peer *p)
// poll
-short peer_prepare(peer *p)
+struct pollfd peer_prepare(peer *p)
{
+ struct pollfd pfd = {0};
+
+ pfd.fd = p->socket;
if (p->in.len == p->in.promised)
next_in(p, true, sizeof(pkt_header));
- return POLLIN | (p->out.avail ? POLLOUT : 0);
+ pfd.events = POLLIN | (p->out.avail ? POLLOUT : 0);
+
+ return pfd;
}
-bool peer_ready(peer *p, short revents)
+bool peer_ready(peer *p, struct pollfd pfd)
{
bool x = false;
- if (revents & POLLIN)
+ if (pfd.revents & (POLLHUP | POLLERR))
+ p->disco = true;
+
+ if (pfd.revents & POLLIN)
x = x || peer_in_ready(p);
- if (revents & POLLOUT)
+ if (pfd.revents & POLLOUT)
x = x || peer_out_ready(p);
return x;
diff --git a/src/ser.c b/src/ser.c
index f06b7b2..7a40da0 100644
--- a/src/ser.c
+++ b/src/ser.c
@@ -7,52 +7,58 @@
#include <endian.h>
#include "ser.h"
-void ser_bytes(strbuf *w, size_t len, uint8_t *x)
+void ser_bytes(strbuf *w, void *x, size_t len)
{
- while (w->buf.len + len > w->cap)
- w->buf.data = realloc(w->buf.data, w->cap = w->cap ? w->cap * 2 : 1);
- memcpy(w->buf.data, x, len);
- w->buf.len += len;
+ while (w->len + len > w->cap)
+ w->data = realloc(w->data, w->cap = w->cap ? w->cap * 2 : 1);
+ memcpy(w->data + w->len, x, len);
+ w->len += len;
+}
+
+void ser_bool(strbuf *w, bool x)
+{
+ ser_u8(w, x);
}
void ser_str(strbuf *w, str x)
{
ser_u16(w, x.len);
- ser_bytes(w, x.len, (uint8_t *) x.data);
+ ser_bytes(w, x.data, x.len);
}
void ser_u8(strbuf *w, uint8_t x)
{
- ser_bytes(w, 1, &x);
+ ser_bytes(w, &x, 1);
}
void ser_u16(strbuf *w, uint16_t x)
{
x = htole16(x);
- ser_bytes(w, 2, (uint8_t *) &x);
+ ser_bytes(w, &x, 2);
}
void ser_u32(strbuf *w, uint32_t x)
{
x = htole32(x);
- ser_bytes(w, 4, (uint8_t *) &x);
+ ser_bytes(w, &x, 4);
}
void ser_u64(strbuf *w, uint64_t x)
{
x = htole64(x);
- ser_bytes(w, 8, (uint8_t *) &x);
+ ser_bytes(w, &x, 8);
}
#define SER_SIGN(N) void ser_i##N(strbuf *w, int##N##_t x) { ser_u##N(w, x); };
+SER_SIGN(8)
SER_SIGN(16)
SER_SIGN(32)
SER_SIGN(64)
#undef SER_SIGN
-bool deser_bytes(str *r, size_t len, uint8_t *buf)
+bool deser_bytes(str *r, void *buf, size_t len)
{
if (len > r->len)
return false;
@@ -62,6 +68,15 @@ bool deser_bytes(str *r, size_t len, uint8_t *buf)
return true;
}
+bool deser_bool(str *r, bool *buf)
+{
+ uint8_t x;
+ if (!deser_u8(r, &x))
+ return false;
+ *buf = x != 0;
+ return true;
+}
+
bool deser_str(str *r, str *buf)
{
uint16_t len;
@@ -78,12 +93,12 @@ bool deser_str(str *r, str *buf)
bool deser_u8(str *r, uint8_t *buf)
{
- return deser_bytes(r, 1, buf);
+ return deser_bytes(r, buf, 1);
}
bool deser_u16(str *r, uint16_t *buf)
{
- if (!deser_bytes(r, 2, (uint8_t *) buf))
+ if (!deser_bytes(r, buf, 2))
return false;
*buf = le16toh(*buf);
return true;
@@ -91,7 +106,7 @@ bool deser_u16(str *r, uint16_t *buf)
bool deser_u32(str *r, uint32_t *buf)
{
- if (!deser_bytes(r, 4, (uint8_t *) buf))
+ if (!deser_bytes(r, buf, 4))
return false;
*buf = le32toh(*buf);
return true;
@@ -99,7 +114,7 @@ bool deser_u32(str *r, uint32_t *buf)
bool deser_u64(str *r, uint64_t *buf)
{
- if (!deser_bytes(r, 8, (uint8_t *) buf))
+ if (!deser_bytes(r, buf, 8))
return false;
*buf = le64toh(*buf);
return true;
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);
+ }
}
-
}