summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client.c125
-rw-r--r--src/net.c (renamed from src/peer.c)91
-rw-r--r--src/server.c75
-rw-r--r--src/ticker.c43
4 files changed, 260 insertions, 74 deletions
diff --git a/src/client.c b/src/client.c
new file mode 100644
index 0000000..0dabbbf
--- /dev/null
+++ b/src/client.c
@@ -0,0 +1,125 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <unistd.h>
+#include <poll.h>
+#include <errno.h>
+#include <ctype.h>
+#include "str.h"
+#include "ser.h"
+#include "net.h"
+#include "ticker.h"
+
+typedef struct {
+ peer conn;
+ struct termios oldtio;
+ str name;
+ str pass;
+} client;
+
+void gfx_alt_buffer(bool enable)
+{
+ if (enable)
+ printf("\e[?1049h\e[?25l");
+ else
+ printf("\e[?1049l\e[?25h");
+}
+
+void gfx_render(client *c, uint64_t dtime)
+{
+ // TODO: render game
+}
+
+void client_exit(client *c, int ret)
+{
+ free(c->name.data);
+ free(c->pass.data);
+
+ peer_free(&c->conn);
+ tcsetattr(STDIN_FILENO, TCSANOW, &c->oldtio);
+ gfx_alt_buffer(false);
+
+ exit(ret);
+}
+
+void handle_input(client *c, char ch)
+{
+ ch = tolower(ch);
+ switch (ch) {
+ case 'q': client_exit(c, EXIT_SUCCESS); break;
+ default: break;
+ }
+}
+
+bool handle_pkt(client *c, str pkt)
+{
+ // TODO: handle network traffic
+ return true;
+}
+
+int main()
+{
+ client c = {0};
+
+ c.name = str_clone(S("kitten"));
+ c.pass = str_clone(S(""));
+
+ c.conn.socket = -1;
+
+ tcgetattr(STDIN_FILENO, &c.oldtio); // TODO: handle error
+
+ struct termios newtio = c.oldtio;
+ newtio.c_lflag &= ~(ICANON | ECHO);
+ tcsetattr(STDIN_FILENO, TCSANOW, &newtio); // TODO: handle error
+
+ SET_NONBLOCK(STDIN_FILENO); // TODO: handle error
+
+ int socket = socket_create("127.0.0.1", "4560", false);
+ if (socket < 0)
+ client_exit(&c, EXIT_FAILURE);
+ peer_init(&c.conn, socket);
+
+ gfx_alt_buffer(true);
+
+ ticker t;
+ ticker_init(&t, NANOS/60);
+
+ for (;;) {
+ struct pollfd fds[2];
+
+ fds[0].fd = STDIN_FILENO;
+ fds[0].events = POLLIN;
+
+ fds[1] = peer_prepare(&c.conn);
+
+ if (poll(fds, 2, ticker_timeout(&t)) < 0) {
+ switch (errno) {
+ case EINTR: continue;
+ default: perror("poll"); continue;
+ }
+ }
+
+ if (fds[0].revents) {
+ // this is cursed
+ // maybe dont do this idk
+ char ch;
+ errno = 0;
+ while (read(STDIN_FILENO, &ch, 1) == 1)
+ handle_input(&c, ch);
+ switch (errno) {
+ case 0: case EWOULDBLOCK: case EINTR: break;
+ default: perror("read"); break;
+ }
+ }
+
+ str pkt = peer_recv(&c.conn, fds[1]);
+ if (c.conn.disco)
+ client_exit(&c, EXIT_SUCCESS);
+ if (pkt.len > 0 && !handle_pkt(&c, pkt))
+ invalid_pkt(S("server"), pkt);
+
+ uint64_t dtime;
+ if (ticker_tick(&t, &dtime))
+ gfx_render(&c, dtime);
+ }
+}
diff --git a/src/peer.c b/src/net.c
index f984aec..18970c1 100644
--- a/src/peer.c
+++ b/src/net.c
@@ -8,7 +8,73 @@
#include <unistd.h>
#include <poll.h>
#include <errno.h>
-#include "peer.h"
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "net.h"
+
+void invalid_pkt(str 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));
+ for (size_t i = 0; i < pkt.len; i++)
+ fprintf(stderr, "%02x%c", (uint8_t) pkt.data[i], i+1 == pkt.len ? '\n' : ' ');
+}
+
+int socket_create(const char *host, const char *port, bool server)
+{
+ struct addrinfo *info = NULL, hints = {0};
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ int err;
+ if ((err = getaddrinfo(host, port, &hints, &info))) {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err));
+ return -1;
+ }
+
+#define TRY(op, val) if ((val) < 0) { perror(op); close(fd); freeaddrinfo(info); return -1; }
+
+ int fd = -1;
+ 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))
+
+ if (server) {
+ TRY("bind", bind(fd, info->ai_addr, info->ai_addrlen))
+ TRY("listen", listen(fd, 3))
+ } else {
+ TRY("connect", connect(fd, info->ai_addr, info->ai_addrlen))
+ TRY("fcntl", SET_NONBLOCK(fd))
+ }
+
+#undef TRY
+
+ freeaddrinfo(info);
+ return fd;
+}
+
+int socket_accept(int accept_fd)
+{
+ int socket = accept(accept_fd, NULL, NULL); // TODO: save address
+ if (socket < 0) {
+ perror("accept");
+ return -1;
+ }
+
+ if (SET_NONBLOCK(socket) < 0) {
+ perror("fcntl");
+ close(socket);
+ return -1;
+ }
+
+ return socket;
+}
void peer_init(peer *p, int socket)
{
@@ -43,10 +109,11 @@ static void next_in(peer *p, bool header, size_t len)
static bool peer_in_ready(peer *p)
{
+ errno = 0;
ssize_t got = read(p->socket, p->in.buffer + p->in.len, p->in.promised - p->in.len);
- if (got < 0) {
+ if (got <= 0) {
switch (errno) {
- case ECONNRESET:
+ case 0: case ECONNRESET:
p->disco = true;
return true;
case EINTR:
@@ -63,7 +130,7 @@ static bool peer_in_ready(peer *p)
return true;
size_t len = *(pkt_header *) p->in.buffer;
- if (len > PEER_INBUFFER_SIZE)
+ if (len > PEER_INBUFFER_SIZE || len == 0)
// TODO: figure out what to do if packet too large (disconnect?)
next_in(p, true, sizeof(pkt_header));
else
@@ -148,17 +215,17 @@ struct pollfd peer_prepare(peer *p)
return pfd;
}
-bool peer_ready(peer *p, struct pollfd pfd)
+str peer_recv(peer *p, struct pollfd pfd)
{
- bool x = false;
-
if (pfd.revents & (POLLHUP | POLLERR))
p->disco = true;
- if (pfd.revents & POLLIN)
- x = x || peer_in_ready(p);
- if (pfd.revents & POLLOUT)
- x = x || peer_out_ready(p);
+ if (!p->disco && pfd.revents & POLLOUT)
+ peer_out_ready(p);
+
+ if (!p->disco && pfd.revents & POLLIN)
+ if (peer_in_ready(p))
+ return (str) { p->in.len, (char *) p->in.buffer };
- return x;
+ return NILS;
}
diff --git a/src/server.c b/src/server.c
index fa48b25..621acae 100644
--- a/src/server.c
+++ b/src/server.c
@@ -11,18 +11,12 @@
#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 "net.h"
#include "str.h"
#include "ser.h"
@@ -56,6 +50,7 @@ typedef struct {
} player;
typedef struct {
+ int accept_fd;
str motd;
str passphrase;
array(map) maps;
@@ -179,36 +174,6 @@ bool map_load(map *m)
#undef TRY
}
-int net_listen(const char *host, const char *port)
-{
- struct addrinfo *info = NULL, hints = {0};
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
-
- int err;
- if ((err = getaddrinfo(host, port, &hints, &info))) {
- fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err));
- return -1;
- }
-
-#define TRY(op, val) if ((val) < 0) { perror(op); freeaddrinfo(info); return -1; }
-
- int fd;
- 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("bind", bind(fd, info->ai_addr, info->ai_addrlen))
- TRY("listen", listen(fd, 3))
-
-#undef TRY
-
- freeaddrinfo(info);
- return fd;
-
-}
-
void ser_color(strbuf *w, color c)
{
ser_u8(w, c.r);
@@ -336,7 +301,7 @@ bool handle_hi(str pkt, player *p, game *g)
return true;
}
-bool process_pkt(str pkt, player *p, game *g)
+bool handle_pkt(str pkt, player *p, game *g)
{
pkt_type type;
if (!deser_pkt_type(&pkt, &type))
@@ -355,6 +320,7 @@ int main()
{
game g = {0};
+ g.accept_fd = -1;
g.entity_id = 1;
g.motd = str_clone(S("Welcome to test server"));
g.passphrase = str_clone(S(""));
@@ -376,8 +342,7 @@ int main()
if (!map_load(&g.maps.data[0]))
game_exit(&g, EXIT_FAILURE);
- int accept_fd = net_listen("0.0.0.0", "4560");
- if (accept_fd < 0)
+ if ((g.accept_fd = socket_create("0.0.0.0", "4560", true)) < 0)
game_exit(&g, EXIT_FAILURE);
for (;;) {
@@ -394,7 +359,7 @@ int main()
fds[i++] = peer_prepare(&p->conn);
}
- fds[g.players.len].fd = accept_fd;
+ fds[g.players.len].fd = g.accept_fd;
fds[g.players.len].events = POLLIN;
if (poll(fds, g.players.len + 1, -1) < 0) {
@@ -406,32 +371,18 @@ int main()
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)
+
+ str pkt = peer_recv(&p->conn, fds[i]);
+ if (p->conn.disco || pkt.len == 0)
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 (!handle_pkt(pkt, p, &g))
+ invalid_pkt(p->name, pkt);
}
if (fds[g.players.len].revents) {
- int socket = accept(accept_fd, NULL, NULL); // TODO: save ip
- if (socket < 0) {
- perror("accept");
+ int socket = socket_accept(g.accept_fd);
+ if (socket < 0)
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);
diff --git a/src/ticker.c b/src/ticker.c
new file mode 100644
index 0000000..d3c25b5
--- /dev/null
+++ b/src/ticker.c
@@ -0,0 +1,43 @@
+// SPDX-FileCopyrightText: 2024 Lizzy Fleckenstein <lizzy@vlhl.dev>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+#include "ticker.h"
+
+static struct timespec timestamp_now()
+{
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts); // TODO: handle error
+ return ts;
+}
+
+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);
+}
+
+void ticker_init(ticker *t, uint64_t f)
+{
+ t->freq_nanos = f;
+ t->timestamp = timestamp_now();
+}
+
+bool ticker_tick(ticker *t, uint64_t *dtime)
+{
+ struct timespec ts = timestamp_now();
+ *dtime = timestamp_diff(ts, t->timestamp);
+ if (*dtime < t->freq_nanos)
+ return false;
+ t->timestamp = ts;
+ return true;
+}
+
+// millis
+int ticker_timeout(ticker *t)
+{
+ uint64_t nanos = timestamp_diff(timestamp_now(), t->timestamp);
+ if (t->freq_nanos > nanos)
+ return (t->freq_nanos - nanos) / 1000000; // nanos to millis
+ else
+ return 0;
+}