From ead2881be92d33076c2104dbd75bad3561f26088 Mon Sep 17 00:00:00 2001 From: Lizzy Fleckenstein Date: Wed, 19 Jun 2024 15:29:19 +0200 Subject: server: implement auth and sending nodes --- include/array.h | 19 ++++ include/content.h | 45 ++++++-- include/peer.h | 7 +- include/ser.h | 6 +- include/str.h | 12 +-- meson.build | 14 ++- src/peer.c | 26 +++-- src/ser.c | 45 +++++--- src/server.c | 318 +++++++++++++++++++++++++++++++++++++++++++++--------- 9 files changed, 391 insertions(+), 101 deletions(-) create mode 100644 include/array.h 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 +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +#ifndef ARRAY_H +#define ARRAY_H + +#include + +#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 +#include + 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 #include #include +#include #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 #include +#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 #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 #include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include #include #include +#include #include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#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); + } } - } -- cgit v1.2.3