diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2022-09-27 17:03:06 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2022-11-01 16:26:16 +0100 |
| commit | 980858186149651df5543b6fc99a4f7db0cdd089 (patch) | |
| tree | d347200b0a562d84df505097651ad0642f207fdd /src/packet | |
| parent | 39f50e601d395bbd2d78d0147ac530b756da2fff (diff) | |
| download | jancity-980858186149651df5543b6fc99a4f7db0cdd089.tar.gz | |
WIP
Diffstat (limited to 'src/packet')
| -rw-r--r-- | src/packet/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/packet/inc/packet.h | 91 | ||||
| -rw-r--r-- | src/packet/src/packet.c | 251 |
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; +} |
