aboutsummaryrefslogtreecommitdiff
path: root/src/packet
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2022-09-27 17:03:06 +0200
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2022-11-01 16:26:16 +0100
commit980858186149651df5543b6fc99a4f7db0cdd089 (patch)
treed347200b0a562d84df505097651ad0642f207fdd /src/packet
parent39f50e601d395bbd2d78d0147ac530b756da2fff (diff)
downloadjancity-980858186149651df5543b6fc99a4f7db0cdd089.tar.gz
WIP
Diffstat (limited to 'src/packet')
-rw-r--r--src/packet/CMakeLists.txt3
-rw-r--r--src/packet/inc/packet.h91
-rw-r--r--src/packet/src/packet.c251
3 files changed, 345 insertions, 0 deletions
diff --git a/src/packet/CMakeLists.txt b/src/packet/CMakeLists.txt
new file mode 100644
index 0000000..597dff5
--- /dev/null
+++ b/src/packet/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_library(packet "src/packet.c")
+target_include_directories(packet PUBLIC "inc")
+target_link_libraries(packet PUBLIC game net PRIVATE util)
diff --git a/src/packet/inc/packet.h b/src/packet/inc/packet.h
new file mode 100644
index 0000000..6f555a6
--- /dev/null
+++ b/src/packet/inc/packet.h
@@ -0,0 +1,91 @@
+#ifndef PACKET_H
+#define PACKET_H
+
+#include <game.h>
+#include <net.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+union packet
+{
+ struct packet_common
+ {
+ enum packet_type
+ {
+ PACKET_TYPE_NAME,
+ PACKET_TYPE_CHAT,
+ PACKET_TYPE_READY,
+
+ MAX_PACKET_TYPES
+ } type;
+ } common;
+
+ struct packet_name
+ {
+ struct packet_common common;
+ const char *name;
+ } name;
+
+ struct packet_chat
+ {
+ struct packet_common common;
+ const char *msg;
+ } chat;
+
+ struct packet_ready
+ {
+ struct packet_common common;
+ bool ready;
+ } ready;
+};
+
+enum {PACKET_MAX_MESSAGE_LEN = 64};
+
+struct packet_ctx
+{
+ struct net_host *host;
+ void (*cb)(net_peer, const union packet *, void *);
+ void *arg;
+};
+
+struct packet_input
+{
+ union packet p;
+ size_t body_i;
+
+ enum
+ {
+ PACKET_STATE_HEADER,
+ PACKET_STATE_BODY
+ } state;
+
+ union packet_data
+ {
+ struct packet_ctx_name
+ {
+ size_t len;
+ char name[GAME_PLAYER_NAME_LEN];
+ } name;
+
+ struct packet_ctx_chat
+ {
+ size_t len;
+ char msg[PACKET_MAX_MESSAGE_LEN];
+ } chat;
+ } data;
+};
+
+int packet_send(const struct packet_ctx *h, net_peer peer, const union packet *pkt);
+int packet_feed(const struct packet_ctx *h, net_peer p,
+ struct packet_input *in, const void *buf, size_t n);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PACKET_H */
diff --git a/src/packet/src/packet.c b/src/packet/src/packet.c
new file mode 100644
index 0000000..3ace4af
--- /dev/null
+++ b/src/packet/src/packet.c
@@ -0,0 +1,251 @@
+#include <packet.h>
+#include <net.h>
+#include <util.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+typedef uint8_t packet_len, packet_ready;
+
+int packet_send(const struct packet_ctx *const h, const net_peer peer,
+ const union packet *const pkt)
+{
+ const uint8_t header = pkt->common.type;
+
+ if (net_write(h->host, peer, &header, sizeof header))
+ {
+ fprintf(stderr, "%s: failed to write header\n", __func__);
+ return -1;
+ }
+
+ const void *buf = NULL;
+ size_t n = 0;
+ bool send_len = false;
+ packet_ready r;
+
+ switch (pkt->common.type)
+ {
+ case PACKET_TYPE_NAME:
+ buf = pkt->name.name;
+ n = strlen(buf);
+ send_len = true;
+ break;
+
+ case PACKET_TYPE_CHAT:
+ buf = pkt->chat.msg;
+ n = strlen(buf);
+ send_len = true;
+ break;
+
+ case PACKET_TYPE_READY:
+ r = pkt->ready.ready;
+ buf = &r;
+ n = sizeof r;
+ break;
+
+ default:
+ fprintf(stderr, "%s: unknown packet type %d\n", __func__,
+ pkt->common.type);
+ return -1;
+ }
+
+ if (send_len)
+ {
+ const packet_len len = n;
+
+ if (net_write(h->host, peer, &len, sizeof len))
+ {
+ fprintf(stderr, "%s: failed to write body length\n", __func__);
+ return -1;
+ }
+ }
+
+ if (net_write(h->host, peer, buf, n))
+ {
+ fprintf(stderr, "%s: failed to write body\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int read_header(struct packet_input *const in,
+ const void **const buf, size_t *const n)
+{
+ const uint8_t header = *(const uint8_t *)*buf;
+
+ if (header >= MAX_PACKET_TYPES)
+ {
+ fprintf(stderr, "%s: invalid packet type %" PRIu8 "\n",
+ __func__, header);
+ return -1;
+ }
+
+ in->p.common.type = header;
+ *buf = (const uint8_t *)*buf + sizeof header;
+ *n -= sizeof header;
+ return 0;
+}
+
+static int read_string_len(size_t *const len, const size_t maxlen,
+ const void **const buf, size_t *const n)
+{
+ *len = *(const packet_len *)*buf;
+
+ if (*len >= maxlen)
+ {
+ fprintf(stderr, "%s: maximum length exceeded (%zu/%zu)\n",
+ __func__, *len, maxlen);
+ return -1;
+ }
+ else if (!*len)
+ {
+ fprintf(stderr, "%s: invalid zero length\n", __func__);
+ return -1;
+ }
+
+ *buf = (const uint8_t *)*buf + sizeof (packet_len);
+ *n -= sizeof (packet_len);
+ return 0;
+}
+
+static int read_string_body(struct packet_input *const in, char *const dst,
+ const size_t len, const void **const buf, size_t *const n)
+{
+ const size_t rem = len > *n ? *n : len;
+
+ for (const char *ch = *buf;
+ ch - (const char *)*buf < rem;
+ ch++)
+ {
+ if (!isprint((unsigned char)*ch))
+ {
+ fprintf(stderr, "%s: unexpected character %#hhx\n", __func__, *ch);
+ return -1;
+ }
+
+ dst[in->body_i++] = *ch;
+ }
+
+ *buf = (const uint8_t *)*buf + rem;
+ *n -= rem;
+
+ if (in->body_i == len)
+ {
+ dst[in->body_i] = '\0';
+ return 0;
+ }
+
+ return 1;
+}
+
+static int read_string(struct packet_input *const in, char *const dst,
+ size_t *const len, const size_t maxlen, const void **const buf,
+ size_t *const n)
+{
+ if (!*len && read_string_len(len, maxlen, buf, n))
+ return -1;
+ else if (!*n)
+ return 1;
+
+ return read_string_body(in, dst, *len, buf, n);
+}
+
+static int read_chat(struct packet_input *const in, const void **const buf,
+ size_t *const n)
+{
+ struct packet_ctx_chat *const c = &in->data.chat;
+ const int ret = read_string(in, c->msg, &c->len, sizeof c->msg - 1, buf, n);
+
+ if (!ret)
+ in->p.chat.msg = c->msg;
+
+ return ret;
+}
+
+static int read_name(struct packet_input *const in, const void **const buf,
+ size_t *const n)
+{
+ struct packet_ctx_name *const c = &in->data.name;
+ const int ret = read_string(in, c->name, &c->len,
+ sizeof c->name - 1, buf, n);
+
+ if (!ret)
+ in->p.name.name = c->name;
+
+ return ret;
+}
+
+static int read_ready(struct packet_input *const in, const void **const buf,
+ size_t *const n)
+{
+ const packet_ready pr = *(const packet_ready *)*buf;
+
+ if (pr > 1)
+ {
+ fprintf(stderr, "%s: invalid ready flag %#hhx\n", __func__, pr);
+ return -1;
+ }
+
+ in->p.ready.ready = pr;
+ *buf = (const uint8_t *)*buf + sizeof pr;
+ *n -= sizeof pr;
+ return 0;
+}
+
+static int read_body(struct packet_input *const in, const void **const buf,
+ size_t *const n)
+{
+ static int (*const f[])(struct packet_input *, const void **, size_t *) =
+ {
+ [PACKET_TYPE_CHAT] = read_chat,
+ [PACKET_TYPE_NAME] = read_name,
+ [PACKET_TYPE_READY] = read_ready
+ };
+
+ return f[in->p.common.type](in, buf, n);
+}
+
+static void reset(struct packet_input *const in)
+{
+ *in = (const struct packet_input){0};
+}
+
+int packet_feed(const struct packet_ctx *const h, const net_peer p,
+ struct packet_input *const in, const void *buf, size_t n)
+{
+ int res;
+
+ switch (in->state)
+ {
+ case PACKET_STATE_HEADER:
+ if ((res = read_header(in, &buf, &n)) < 0)
+ goto failure;
+ else if (!res)
+ in->state = PACKET_STATE_BODY;
+
+ if (!n)
+ break;
+
+ /* Fall through. */
+ case PACKET_STATE_BODY:
+ if ((res = read_body(in, &buf, &n)) < 0)
+ goto failure;
+ else if (!res)
+ {
+ h->cb(p, &in->p, h->arg);
+ reset(in);
+ }
+
+ break;
+ }
+
+ return 0;
+
+failure:
+ reset(in);
+ return -1;
+}