summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLizzy Fleckenstein <lizzy@vlhl.dev>2024-06-19 20:50:41 +0200
committerLizzy Fleckenstein <lizzy@vlhl.dev>2024-06-19 20:50:41 +0200
commit47984162974a8f7d9903d352567005ac569c5a87 (patch)
treeab265785ceaae0585aec80f6d5209e6289cbefd3
parent9720c7efddf2d7595c9a0b021bc5fc6327c22bcf (diff)
client: receive and display player list
Signed-off-by: Lizzy Fleckenstein <lizzy@vlhl.dev>
-rw-r--r--include/array.h2
-rw-r--r--include/net.h2
-rw-r--r--src/client.c79
-rw-r--r--src/net.c8
-rw-r--r--src/ser.c14
-rw-r--r--src/server.c34
-rw-r--r--src/ticker.c2
7 files changed, 104 insertions, 37 deletions
diff --git a/include/array.h b/include/array.h
index 8d52619..5f81798 100644
--- a/include/array.h
+++ b/include/array.h
@@ -11,7 +11,7 @@
#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_REMOVE(A, P) memmove((P), (P)+1, sizeof *(P) * (--(A).len - ((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++])
diff --git a/include/net.h b/include/net.h
index ce2eb02..08c566a 100644
--- a/include/net.h
+++ b/include/net.h
@@ -46,7 +46,7 @@ typedef struct {
ser_pkt_type(&pkt, TYPE); \
__VA_ARGS__ \
if (!peer_send(&(CONN), pkt.data, pkt.len)) \
- fprintf(stderr, "failed to send " #TYPE " to %*s\n", PSTR(*(CONN).name)); \
+ fprintf(stderr, "failed to send " #TYPE " to %.*s\n", PSTR(*(CONN).name)); \
free(pkt.data); }
void invalid_pkt(peer *p, str pkt);
diff --git a/src/client.c b/src/client.c
index d15ba8e..08eae9d 100644
--- a/src/client.c
+++ b/src/client.c
@@ -13,10 +13,17 @@
#include "content.h"
typedef struct {
+ str name;
+ uint64_t id;
+} player;
+
+typedef struct {
peer conn;
struct termios oldtio;
str name;
str pass;
+ uint64_t self_id;
+ array(player) players;
} client;
void gfx_alt_buffer(bool enable)
@@ -27,9 +34,29 @@ void gfx_alt_buffer(bool enable)
printf("\e[?1049l\e[?25h");
}
+void gfx_clear()
+{
+ printf("\e[2J");
+}
+
void gfx_render(client *c, uint64_t dtime)
{
- // TODO: render game
+ gfx_clear();
+ printf("\e[H");
+ for (size_t i = 0; i < c->players.len; i++)
+ printf("%.*s\n", PSTR(c->players.data[i].name));
+ fflush(stdout);
+}
+
+void free_players(client *c)
+{
+ for (size_t i = 0; i < c->players.len; i++)
+ free(c->players.data[i].name.data);
+
+ free(c->players.data);
+
+ c->players.len = 0;
+ c->players.data = NULL;
}
void client_exit(client *c, int ret)
@@ -37,6 +64,8 @@ void client_exit(client *c, int ret)
free(c->name.data);
free(c->pass.data);
+ free_players(c);
+
peer_free(&c->conn);
tcsetattr(STDIN_FILENO, TCSANOW, &c->oldtio);
gfx_alt_buffer(false);
@@ -53,20 +82,56 @@ void handle_input(client *c, char ch)
}
}
-bool handle_pkt(client *c, str pkt)
+bool handle_players(str *w, client *c)
{
- // TODO: handle network traffic
+ free_players(c);
+
+ uint16_t len;
+ if (!deser_u16(w, &len))
+ return false;
+
+ c->players.data = malloc(sizeof *c->players.data * len);
+
+ for (c->players.len = 0; c->players.len < len; c->players.len++) {
+ player *p = &c->players.data[c->players.len];
+ if (!deser_str(w, &p->name))
+ return false;
+ if (!deser_u64(w, &p->id))
+ return false;
+ if (str_cmp(p->name, c->name) == 0)
+ c->self_id = p->id;
+ p->name = str_clone(p->name);
+ }
return true;
}
-int main()
+bool handle_pkt(client *c, str pkt)
{
+ pkt_type type;
+ if (!deser_pkt_type(&pkt, &type))
+ return false;
+
+ switch (type) {
+ case CPKT_PLAYERS: return handle_players(&pkt, c);
+ default: return false;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ if (argc < 4) {
+ fprintf(stderr, "usage: %s server port name [pass]\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
signal_setup();
client c = {0};
- c.name = str_clone(S("kitten"));
- c.pass = str_clone(S(""));
+ const char *server = argv[1];
+ const char *port = argv[2];
+ c.name = str_clone(str_intro(argv[3]));
+ c.pass = str_clone(argc < 5 ? S("") : str_intro(argv[4]));
c.conn.socket = -1;
@@ -78,7 +143,7 @@ int main()
SET_NONBLOCK(STDIN_FILENO); // TODO: handle error
- int socket = socket_create("127.0.0.1", "4560", false);
+ int socket = socket_create(server, port, false);
if (socket < 0)
client_exit(&c, EXIT_FAILURE);
str server_name = S("server");
diff --git a/src/net.c b/src/net.c
index b5edc07..4d41d6f 100644
--- a/src/net.c
+++ b/src/net.c
@@ -9,6 +9,7 @@
#include <poll.h>
#include <errno.h>
#include <netdb.h>
+#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -20,7 +21,7 @@ void invalid_pkt(peer *from, str pkt)
{
// TODO: maybe inform peer about failure? not sure
// FIXME: hexdumping a gazillon bytes to stderr might be an issue
- fprintf(stderr, "invalid pkt from %*s: ", PSTR(*from->name));
+ fprintf(stderr, "invalid pkt from %.*s: ", PSTR(*from->name));
for (size_t i = 0; i < pkt.len; i++)
fprintf(stderr, "%02x%c", (uint8_t) pkt.data[i], i+1 == pkt.len ? '\n' : ' ');
}
@@ -103,13 +104,16 @@ void peer_free(peer *p)
static void next_in(peer *p, bool header, size_t len)
{
+ p->in.promised = len;
p->in.header = header;
p->in.len = 0;
- p->in.buffer = malloc(p->in.promised = len);
}
+
static bool peer_in_ready(peer *p)
{
+ assert(p->in.promised > p->in.len);
+
errno = 0;
ssize_t got = read(p->socket, p->in.buffer + p->in.len, p->in.promised - p->in.len);
if (got <= 0) {
diff --git a/src/ser.c b/src/ser.c
index 7a40da0..b394627 100644
--- a/src/ser.c
+++ b/src/ser.c
@@ -28,25 +28,25 @@ void ser_str(strbuf *w, str x)
void ser_u8(strbuf *w, uint8_t x)
{
- ser_bytes(w, &x, 1);
+ ser_bytes(w, &x, sizeof x);
}
void ser_u16(strbuf *w, uint16_t x)
{
x = htole16(x);
- ser_bytes(w, &x, 2);
+ ser_bytes(w, &x, sizeof x);
}
void ser_u32(strbuf *w, uint32_t x)
{
x = htole32(x);
- ser_bytes(w, &x, 4);
+ ser_bytes(w, &x, sizeof x);
}
void ser_u64(strbuf *w, uint64_t x)
{
x = htole64(x);
- ser_bytes(w, &x, 8);
+ ser_bytes(w, &x, sizeof x);
}
#define SER_SIGN(N) void ser_i##N(strbuf *w, int##N##_t x) { ser_u##N(w, x); };
@@ -98,7 +98,7 @@ bool deser_u8(str *r, uint8_t *buf)
bool deser_u16(str *r, uint16_t *buf)
{
- if (!deser_bytes(r, buf, 2))
+ if (!deser_bytes(r, buf, sizeof *buf))
return false;
*buf = le16toh(*buf);
return true;
@@ -106,7 +106,7 @@ bool deser_u16(str *r, uint16_t *buf)
bool deser_u32(str *r, uint32_t *buf)
{
- if (!deser_bytes(r, buf, 4))
+ if (!deser_bytes(r, buf, sizeof *buf))
return false;
*buf = le32toh(*buf);
return true;
@@ -114,7 +114,7 @@ bool deser_u32(str *r, uint32_t *buf)
bool deser_u64(str *r, uint64_t *buf)
{
- if (!deser_bytes(r, buf, 8))
+ if (!deser_bytes(r, buf, sizeof *buf))
return false;
*buf = le64toh(*buf);
return true;
diff --git a/src/server.c b/src/server.c
index 0addcf2..bcca3a9 100644
--- a/src/server.c
+++ b/src/server.c
@@ -103,7 +103,7 @@ void map_load_node(map *m, uint32_t x, uint32_t y, uint32_t color)
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",
+ fprintf(stderr, "invalid color in map %.*s at %"PRIu32" %"PRIu32": %06x\n",
PSTR(m->name), x, y, color);
n->present = false;
break;
@@ -195,8 +195,8 @@ void ser_node(strbuf *w, node *n)
void send_players(game *g)
{
for (size_t i = 0; i < g->players.len; i++) {
- player *p = &g->players.data[i];
- if (p->auth) SEND_PKT(p->conn, CPKT_PLAYERS,
+ player *to = &g->players.data[i];
+ if (to->auth) SEND_PKT(to->conn, CPKT_PLAYERS,
ser_u16(&pkt, g->players.len);
for (size_t i = 0; i < g->players.len; i++) {
player *p = &g->players.data[i];
@@ -264,7 +264,7 @@ bool handle_hi(str pkt, player *p, game *g)
return false;
if (str_cmp(g->passphrase, pass) != 0) {
- printf("wrong passphrase from %*s\n", PSTR(name)); // TODO: log ip ?
+ printf("wrong passphrase from %.*s\n", PSTR(name)); // TODO: log ip ?
SEND_PKT(p->conn, CPKT_FAIL, ser_fail_reason(&pkt, FAIL_WRONG_PASS);)
return true; // valid pkt, but invalid passphrase
}
@@ -277,7 +277,7 @@ bool handle_hi(str pkt, player *p, game *g)
}
}
- printf("player authenticated as %*s\n", PSTR(name));
+ printf("player authenticated as %.*s\n", PSTR(name));
p->auth = true;
p->name = str_clone(name);
@@ -342,16 +342,8 @@ int main()
for (;;) {
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 %*s disconnected\n", PSTR(p->name));
- player_remove(p, &g);
- continue;
- }
-
- fds[i++] = peer_prepare(&p->conn);
- }
+ for (size_t i = 0; i < g.players.len; i++)
+ fds[i] = peer_prepare(&g.players.data[i].conn);
fds[g.players.len].fd = g.accept_fd;
fds[g.players.len].events = POLLIN;
@@ -370,9 +362,13 @@ int main()
player *p = &g.players.data[i];
str pkt = peer_recv(&p->conn, fds[i]);
- if (p->conn.disco || pkt.len == 0)
- continue;
- if (!handle_pkt(pkt, p, &g))
+ if (p->conn.disco) {
+ printf("player %.*s disconnected\n", PSTR(p->name));
+ player_remove(p, &g);
+ goto cont;
+ }
+
+ if (pkt.len > 0 && !handle_pkt(pkt, p, &g))
invalid_pkt(&p->conn, pkt);
}
@@ -388,5 +384,7 @@ int main()
p->name = S("(unauthenticated)");
peer_init(&p->conn, socket, &p->name);
}
+
+ cont: continue;
}
}
diff --git a/src/ticker.c b/src/ticker.c
index d3c25b5..c0d7df9 100644
--- a/src/ticker.c
+++ b/src/ticker.c
@@ -13,7 +13,7 @@ static struct timespec timestamp_now()
static uint64_t timestamp_diff(struct timespec a, struct timespec b)
{
- return NANOS * ((uint64_t) a.tv_sec - b.tv_sec) + ((int64_t) a.tv_nsec - b.tv_nsec);
+ return ((uint64_t) a.tv_sec - b.tv_sec) * NANOS + a.tv_nsec - b.tv_nsec;
}
void ticker_init(ticker *t, uint64_t f)