diff options
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | LICENSE | 13 | ||||
-rwxr-xr-x | client.c | 325 | ||||
-rwxr-xr-x | help.c | 187 | ||||
-rwxr-xr-x | server.c | 272 | ||||
-rw-r--r-- | torus.h | 78 |
6 files changed, 880 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..39fd7f6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +client +help +server +torus.dat +torus.sock diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0904697 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright © 2017, Curtis McEnroe <curtis@cmcenroe.me> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/client.c b/client.c new file mode 100755 index 0000000..775d296 --- /dev/null +++ b/client.c @@ -0,0 +1,325 @@ +#if 0 +exec cc -Wall -Wextra -pedantic $@ -lcurses -o client $0 +#endif + +#include <ctype.h> +#include <curses.h> +#include <err.h> +#include <errno.h> +#include <poll.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sysexits.h> +#include <unistd.h> + +#undef COLOR_BLACK +#undef COLOR_RED +#undef COLOR_GREEN +#undef COLOR_YELLOW +#undef COLOR_BLUE +#undef COLOR_MAGENTA +#undef COLOR_CYAN +#undef COLOR_WHITE +#include "torus.h" + +#define ESC (0x1B) +#define DEL (0x7F) + +#define CH_COLOR(ch) (ch & A_BOLD ? COLOR_BRIGHT | ch >> 8 & 0xFF : ch >> 8 & 0xFF) + +static int client; + +static void clientMessage(const struct ClientMessage *msg) { + ssize_t len = send(client, msg, sizeof(*msg), 0); + if (len < 0) err(EX_IOERR, "send"); +} + +static void clientMove(int8_t dx, int8_t dy) { + struct ClientMessage msg = { .type = CLIENT_MOVE }; + msg.data.m.dx = dx; + msg.data.m.dy = dy; + clientMessage(&msg); +} + +static void clientColor(uint8_t color) { + struct ClientMessage msg = { .type = CLIENT_COLOR }; + msg.data.c = color; + clientMessage(&msg); +} + +static void clientPut(char cell) { + struct ClientMessage msg = { .type = CLIENT_PUT }; + msg.data.p = cell; + clientMessage(&msg); +} + +static enum { + MODE_NORMAL, + MODE_INSERT, + MODE_REPLACE, + MODE_DRAW, +} mode; +static struct { + int8_t dx; + int8_t dy; + uint8_t len; +} insert; +static char drawChar; + +static void insertMode(int8_t dx, int8_t dy) { + mode = MODE_INSERT; + insert.dx = dx; + insert.dy = dy; + insert.len = 0; +} + +static void swapCell(int8_t dx, int8_t dy) { + int sy, sx; + getyx(stdscr, sy, sx); + + move(sy + dy, sx + dx); + char swapCell = inch() & 0x7F; + uint8_t swapColor = CH_COLOR(inch()); + + move(sy, sx); + char cell = inch() & 0x7F; + uint8_t color = CH_COLOR(inch()); + + clientColor(swapColor); + clientPut(swapCell); + clientMove(dx, dy); + clientColor(color); + clientPut(cell); +} + +static void readInput(void) { + int c = getch(); + + if (mode == MODE_INSERT) { + if (c == ESC) { + mode = MODE_NORMAL; + clientMove(-insert.dx, -insert.dy); + } else if (!insert.dx && !insert.dy) { + switch (c) { + case 'h': insertMode(-1, 0); break; + case 'j': insertMode( 0, 1); break; + case 'k': insertMode( 0, -1); break; + case 'l': insertMode( 1, 0); break; + case 'y': insertMode(-1, -1); break; + case 'u': insertMode( 1, -1); break; + case 'b': insertMode(-1, 1); break; + case 'n': insertMode( 1, 1); break; + } + } else if (c == '\b' || c == DEL) { + clientMove(-insert.dx, -insert.dy); + clientPut(' '); + insert.len--; + } else if (c == '\n') { + clientMove(insert.dy, insert.dx); + for (uint8_t i = 0; i < insert.len; ++i) { + clientMove(-insert.dx, -insert.dy); + } + insert.len = 0; + } else if (isprint(c)) { + clientPut(c); + clientMove(insert.dx, insert.dy); + insert.len++; + } + return; + } + + if (mode == MODE_REPLACE) { + if (isprint(c)) clientPut(c); + mode = MODE_NORMAL; + return; + } + + if (mode == MODE_DRAW && !drawChar) { + if (c == ESC) mode = MODE_NORMAL; + if (isprint(c)) { + drawChar = c; + clientPut(c); + } + return; + } + + switch (c) { + case ESC: mode = MODE_NORMAL; break; + + case 'q': endwin(); exit(EX_OK); + + case 'a': clientMove(1, 0); // fallthrough + case 'i': insertMode(1, 0); break; + case 'I': insertMode(0, 0); break; + case 'r': mode = MODE_REPLACE; break; + case 'R': mode = MODE_DRAW; drawChar = 0; break; + case 'x': clientPut(' '); break; + case '~': clientPut(inch() & 0x7F); clientMove(1, 0); break; + + case 'h': clientMove(-1, 0); break; + case 'j': clientMove( 0, 1); break; + case 'k': clientMove( 0, -1); break; + case 'l': clientMove( 1, 0); break; + case 'y': clientMove(-1, -1); break; + case 'u': clientMove( 1, -1); break; + case 'b': clientMove(-1, 1); break; + case 'n': clientMove( 1, 1); break; + + case 'H': swapCell(-1, 0); break; + case 'J': swapCell( 0, 1); break; + case 'K': swapCell( 0, -1); break; + case 'L': swapCell( 1, 0); break; + case 'Y': swapCell(-1, -1); break; + case 'U': swapCell( 1, -1); break; + case 'B': swapCell(-1, 1); break; + case 'N': swapCell( 1, 1); break; + + case '1': clientColor(COLOR_RED); break; + case '2': clientColor(COLOR_GREEN); break; + case '3': clientColor(COLOR_YELLOW); break; + case '4': clientColor(COLOR_BLUE); break; + case '5': clientColor(COLOR_MAGENTA); break; + case '6': clientColor(COLOR_CYAN); break; + case '7': clientColor(COLOR_WHITE); break; + + case '!': clientColor(COLOR_BRIGHT | COLOR_RED); break; + case '@': clientColor(COLOR_BRIGHT | COLOR_GREEN); break; + case '#': clientColor(COLOR_BRIGHT | COLOR_YELLOW); break; + case '$': clientColor(COLOR_BRIGHT | COLOR_BLUE); break; + case '%': clientColor(COLOR_BRIGHT | COLOR_MAGENTA); break; + case '^': clientColor(COLOR_BRIGHT | COLOR_CYAN); break; + case '&': clientColor(COLOR_BRIGHT | COLOR_WHITE); break; + + case KEY_LEFT: clientMove(-1, 0); break; + case KEY_DOWN: clientMove( 0, 1); break; + case KEY_UP: clientMove( 0, -1); break; + case KEY_RIGHT: clientMove( 1, 0); break; + } + + if (mode == MODE_DRAW && drawChar) clientPut(drawChar); +} + +static void serverPut(uint8_t x, uint8_t y, uint8_t color, char cell) { + int attrs = COLOR_PAIR(color & ~COLOR_BRIGHT); + if (color & COLOR_BRIGHT) attrs |= A_BOLD; + mvaddch(y, x, attrs | cell); +} + +static void serverTile(void) { + struct Tile tile; + ssize_t len = recv(client, &tile, sizeof(tile), 0); + if (len < 0) err(EX_IOERR, "recv"); + if (len < (ssize_t)sizeof(tile)) { + errx(EX_PROTOCOL, "This tile isn't big enough..."); + } + + for (int y = 0; y < CELL_ROWS; ++y) { + for (int x = 0; x < CELL_COLS; ++x) { + serverPut(x, y, tile.colors[y][x], tile.cells[y][x]); + } + } +} + +static void readMessage(void) { + struct ServerMessage msg; + ssize_t len = recv(client, &msg, sizeof(msg), 0); + if (len < 0) err(EX_IOERR, "recv"); + if (len < (ssize_t)sizeof(msg)) errx(EX_PROTOCOL, "A message was cut short."); + + int sy, sx; + getyx(stdscr, sy, sx); + + switch (msg.type) { + case SERVER_TILE: + serverTile(); + break; + + case SERVER_MOVE: + move(msg.data.m.cellY, msg.data.m.cellX); + refresh(); + return; + + case SERVER_PUT: + serverPut( + msg.data.p.cellX, + msg.data.p.cellY, + msg.data.p.color, + msg.data.p.cell + ); + break; + + default: + errx(EX_PROTOCOL, "I don't know what %d means!", msg.type); + } + + move(sy, sx); + refresh(); +} + +static void drawBorder(void) { + if (LINES < CELL_ROWS || COLS < CELL_COLS) { + endwin(); + fprintf(stderr, "Sorry, your terminal is too small!\n"); + fprintf(stderr, "It needs to be at least 80x25 characters.\n"); + exit(EX_CONFIG); + } + if (LINES > CELL_ROWS) { + mvhline(CELL_ROWS, 0, 0, CELL_COLS); + } + if (COLS > CELL_COLS) { + mvvline(0, CELL_COLS, 0, CELL_ROWS); + } + if (LINES > CELL_ROWS && COLS > CELL_COLS) { + mvaddch(CELL_ROWS, CELL_COLS, ACS_LRCORNER); + } +} + +static void initColors(void) { + if (!has_colors()) { + endwin(); + fprintf(stderr, "Sorry, your terminal doesn't support colors!\n"); + fprintf(stderr, "I only need 16, I promise.\n"); + exit(EX_CONFIG); + } + start_color(); + for (int fg = COLOR_RED; fg < COLOR_BRIGHT; ++fg) { + init_pair(fg, fg, COLOR_BLACK); + } +} + +int main() { + client = socket(PF_LOCAL, SOCK_STREAM, 0); + if (client < 0) err(EX_OSERR, "socket"); + + struct sockaddr_un addr = { + .sun_family = AF_LOCAL, + .sun_path = "torus.sock", + }; + int error = connect(client, (struct sockaddr *)&addr, sizeof(addr)); + if (error) err(EX_IOERR, "torus.sock"); + + initscr(); + cbreak(); + noecho(); + keypad(stdscr, true); + set_escdelay(100); + + initColors(); + drawBorder(); + + struct pollfd fds[2] = { + { .fd = STDIN_FILENO, .events = POLLIN }, + { .fd = client, .events = POLLIN }, + }; + for (;;) { + int nfds = poll(fds, 2, -1); + if (nfds < 0 && errno == EINTR) continue; + if (nfds < 0) err(EX_IOERR, "poll"); + + if (fds[0].revents) readInput(); + if (fds[1].revents) readMessage(); + } +} diff --git a/help.c b/help.c new file mode 100755 index 0000000..8360143 --- /dev/null +++ b/help.c @@ -0,0 +1,187 @@ +#if 0 +exec cc -Wall -Wextra -pedantic $@ -o help $0 +#endif + +#include <err.h> +#include <stdint.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sysexits.h> +#include <unistd.h> + +#include "torus.h" + +static int client; + +static void clientMessage(const struct ClientMessage *msg) { + ssize_t len = send(client, msg, sizeof(*msg), 0); + if (len < 0) err(EX_IOERR, "send"); +} + +static void clientMove(int8_t dx, int8_t dy) { + struct ClientMessage msg = { .type = CLIENT_MOVE }; + msg.data.m.dx = dx; + msg.data.m.dy = dy; + clientMessage(&msg); +} + +static void clientColor(uint8_t color) { + struct ClientMessage msg = { .type = CLIENT_COLOR }; + msg.data.c = color; + clientMessage(&msg); +} + +static void clientPut(char cell) { + struct ClientMessage msg = { .type = CLIENT_PUT }; + msg.data.p = cell; + clientMessage(&msg); +} + +#define DELAY (100000) + +static void clear(uint8_t width, uint8_t height) { + uint8_t x = 0; + for (uint8_t y = 0; y < height; ++y) { + if (x) { + for (; x > 0; --x) { + clientPut(' '); + clientMove(-1, 0); + usleep(DELAY / 10); + } + clientPut(' '); + x = 0; + } else { + for (; x < width; ++x) { + clientPut(' '); + clientMove(1, 0); + usleep(DELAY / 10); + } + clientPut(' '); + x = width; + } + clientMove(0, 1); + } + clientMove(-x, -height); +} + +static void white(void) { + clientColor(COLOR_WHITE); +} + +static void brite(void) { + clientColor(COLOR_BRIGHT | COLOR_WHITE); +} + +static int8_t lineLen; + +static void string(const char *str) { + for (; *str; ++str) { + clientPut(*str); + clientMove(1, 0); + lineLen++; + usleep(DELAY); + } +} + +static void enter(void) { + clientMove(-lineLen, 1); + lineLen = 0; + usleep(DELAY); +} + +static void mvPut(int8_t dx, int8_t dy, char cell) { + clientMove(dx, dy); + clientPut(cell); + usleep(DELAY); +} + +int main() { + client = socket(PF_LOCAL, SOCK_STREAM, 0); + if (client < 0) err(EX_OSERR, "socket"); + + struct sockaddr_un addr = { + .sun_family = AF_LOCAL, + .sun_path = "torus.sock", + }; + int error = connect(client, (struct sockaddr *)&addr, sizeof(addr)); + if (error) err(EX_IOERR, "torus.sock"); + + pid_t pid = fork(); + if (pid < 0) err(EX_OSERR, "fork"); + + if (!pid) { + for (;;) { + char buf[4096]; + ssize_t len = recv(client, buf, sizeof(buf), 0); + if (len < 0) err(EX_IOERR, "recv"); + if (!len) return EX_OK; + } + } + + clientMove(-CELL_INIT_X, -CELL_INIT_Y); + + for (;;) { + clear(28, 11); + clientMove(2, 1); + + white(); string("Welcome to "); + brite(); string("ascii.town"); + white(); string("!"); + enter(); + + white(); mvPut( 2, 4, 'o'); + white(); mvPut( 1, 0, '-'); + brite(); mvPut( 1, 0, 'l'); + white(); mvPut(-1, 1, '\\'); + brite(); mvPut( 1, 1, 'n'); + white(); mvPut(-2, -1, '|'); + brite(); mvPut( 0, 1, 'j'); + white(); mvPut(-1, -1, '/'); + brite(); mvPut(-1, 1, 'b'); + white(); mvPut( 1, -2, '-'); + brite(); mvPut(-1, 0, 'h'); + white(); mvPut( 1, -1, '\\'); + brite(); mvPut(-1, -1, 'y'); + white(); mvPut( 2, 1, '|'); + brite(); mvPut( 0, -1, 'k'); + white(); mvPut( 1, 1, '/'); + brite(); mvPut( 1, -1, 'u'); + + clientMove(5, 0); + brite(); string("q"); + white(); string(" quit"); + enter(); + brite(); string("i"); + white(); string(" insert"); + enter(); + brite(); string("r"); + white(); string(" replace"); + enter(); + brite(); string("R"); + white(); string(" draw"); + enter(); + brite(); string("~"); + white(); string(" color"); + enter(); + + clientMove(13, -6); + clientColor(COLOR_RED); mvPut(0, 0, '1'); + clientColor(COLOR_GREEN); mvPut(0, 1, '2'); + clientColor(COLOR_YELLOW); mvPut(0, 1, '3'); + clientColor(COLOR_BLUE); mvPut(0, 1, '4'); + clientColor(COLOR_MAGENTA); mvPut(0, 1, '5'); + clientColor(COLOR_CYAN); mvPut(0, 1, '6'); + clientColor(COLOR_WHITE); mvPut(0, 1, '7'); + clientColor(COLOR_BRIGHT | COLOR_WHITE); mvPut(2, 0, '&'); + clientColor(COLOR_BRIGHT | COLOR_CYAN); mvPut(0, -1, '^'); + clientColor(COLOR_BRIGHT | COLOR_MAGENTA); mvPut(0, -1, '%'); + clientColor(COLOR_BRIGHT | COLOR_BLUE); mvPut(0, -1, '$'); + clientColor(COLOR_BRIGHT | COLOR_YELLOW); mvPut(0, -1, '#'); + clientColor(COLOR_BRIGHT | COLOR_GREEN); mvPut(0, -1, '@'); + clientColor(COLOR_BRIGHT | COLOR_RED); mvPut(0, -1, '!'); + + clientMove(-26, -3); + + sleep(30); + } +} diff --git a/server.c b/server.c new file mode 100755 index 0000000..7b16f33 --- /dev/null +++ b/server.c @@ -0,0 +1,272 @@ +#if 0 +exec cc -Wall -Wextra -pedantic $@ -o server $0 +#endif + +#include <sys/types.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/event.h> +#include <sys/mman.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/un.h> +#include <sysexits.h> +#include <unistd.h> + +#include "torus.h" + +static struct Tile *tiles; + +static void tilesMap(void) { + int fd = open("torus.dat", O_CREAT | O_RDWR, 0644); + if (fd < 0) err(EX_IOERR, "torus.dat"); + + int error = ftruncate(fd, TILES_SIZE); + if (error) err(EX_IOERR, "ftruncate"); + + tiles = mmap(NULL, TILES_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (tiles == MAP_FAILED) err(EX_OSERR, "mmap"); +} + +static struct Tile *tileGet(uint32_t tileX, uint32_t tileY) { + struct Tile *tile = &tiles[tileY * TILE_ROWS + tileX]; + if (!tile->present) { + memset(tile->cells, ' ', CELLS_SIZE); + memset(tile->colors, COLOR_WHITE, CELLS_SIZE); + tile->present = true; + } + return tile; +} + +static struct Client { + int fd; + + uint32_t tileX; + uint32_t tileY; + uint8_t cellX; + uint8_t cellY; + uint8_t color; + + struct Client *prev; + struct Client *next; +} *clientHead; + +static struct Client *clientAdd(int fd) { + struct Client *client = malloc(sizeof(*client)); + if (!client) err(EX_OSERR, "malloc"); + + client->fd = fd; + client->tileX = TILE_INIT_X; + client->tileY = TILE_INIT_Y; + client->cellX = CELL_INIT_X; + client->cellY = CELL_INIT_Y; + client->color = COLOR_WHITE; + + client->prev = NULL; + if (clientHead) { + clientHead->prev = client; + client->next = clientHead; + } else { + client->next = NULL; + } + clientHead = client; + + return client; +} + +static void clientRemove(struct Client *client) { + if (client->prev) client->prev->next = client->next; + if (client->next) client->next->prev = client->prev; + if (clientHead == client) clientHead = client->next; + close(client->fd); + free(client); +} + +static bool clientSend(struct Client *client, const struct ServerMessage *msg) { + ssize_t len = send(client->fd, msg, sizeof(*msg), 0); + if (len < 0) { + clientRemove(client); + return false; + } + + if (msg->type == SERVER_TILE) { + struct Tile *tile = tileGet(client->tileX, client->tileY); + len = send(client->fd, tile, sizeof(*tile), 0); + if (len < 0) { + clientRemove(client); + return false; + } + } + + return true; +} + +static bool clientCast(struct Client *origin, const struct ServerMessage *msg) { + uint32_t tileX = origin->tileX; + uint32_t tileY = origin->tileY; + + bool success = clientSend(origin, msg); + + for (struct Client *client = clientHead; client; client = client->next) { + if (client == origin) continue; + if (client->tileX != tileX) continue; + if (client->tileY != tileY) continue; + + struct Client *next = client->next; + if (!clientSend(client, msg)) { + client = next; + if (!client) break; + } + } + + return success; +} + +static bool clientMove(struct Client *client, int8_t dx, uint8_t dy) { + struct Client old = *client; + + client->cellX += dx; + client->cellY += dy; + + // TODO: Handle moves greater than 1 in either direction. + if (client->cellX == CELL_COLS) { client->tileX++; client->cellX = 0; } + if (client->cellX == UINT8_MAX) { client->tileX--; client->cellX = CELL_COLS - 1; } + if (client->cellY == CELL_ROWS) { client->tileY++; client->cellY = 0; } + if (client->cellY == UINT8_MAX) { client->tileY--; client->cellY = CELL_ROWS - 1; } + + if (client->tileX == TILE_COLS) client->tileX = 0; + if (client->tileX == UINT32_MAX) client->tileX = TILE_COLS - 1; + if (client->tileY == TILE_ROWS) client->tileY = 0; + if (client->tileY == UINT32_MAX) client->tileY = TILE_ROWS - 1; + + struct ServerMessage msg = { .type = SERVER_MOVE }; + msg.data.m.cellX = client->cellX; + msg.data.m.cellY = client->cellY; + if (!clientSend(client, &msg)) return false; + + if (client->tileX != old.tileX || client->tileY != old.tileY) { + msg.type = SERVER_TILE; + return clientSend(client, &msg); + } + + return true; +} + +static bool clientPut(struct Client *client, char cell) { + struct Tile *tile = tileGet(client->tileX, client->tileY); + tile->colors[client->cellY][client->cellX] = client->color; + tile->cells[client->cellY][client->cellX] = cell; + + struct ServerMessage msg = { .type = SERVER_PUT }; + msg.data.p.cellX = client->cellX; + msg.data.p.cellY = client->cellY; + msg.data.p.color = client->color; + msg.data.p.cell = cell; + return clientCast(client, &msg); +} + +int main() { + int error; + + signal(SIGPIPE, SIG_IGN); + + tilesMap(); + + int server = socket(PF_LOCAL, SOCK_STREAM, 0); + if (server < 0) err(EX_OSERR, "socket"); + + error = unlink("torus.sock"); + if (error && errno != ENOENT) err(EX_IOERR, "torus.sock"); + + struct sockaddr_un addr = { + .sun_family = AF_LOCAL, + .sun_path = "torus.sock", + }; + error = bind(server, (struct sockaddr *)&addr, sizeof(addr)); + if (error) err(EX_IOERR, "torus.sock"); + + error = listen(server, 0); + if (error) err(EX_OSERR, "listen"); + + int kq = kqueue(); + if (kq < 0) err(EX_OSERR, "kqueue"); + + struct kevent event = { + .ident = server, + .filter = EVFILT_READ, + .flags = EV_ADD, + .fflags = 0, + .data = 0, + .udata = NULL, + }; + int nevents = kevent(kq, &event, 1, NULL, 0, NULL); + if (nevents < 0) err(EX_OSERR, "kevent"); + + for (;;) { + nevents = kevent(kq, NULL, 0, &event, 1, NULL); + if (nevents < 0) err(EX_IOERR, "kevent"); + if (!nevents) continue; + + if (!event.udata) { + int fd = accept(server, NULL, NULL); + if (fd < 0) err(EX_IOERR, "accept"); + fcntl(fd, F_SETFL, O_NONBLOCK); + + struct Client *client = clientAdd(fd); + + struct kevent event = { + .ident = fd, + .filter = EVFILT_READ, + .flags = EV_ADD, + .fflags = 0, + .data = 0, + .udata = client, + }; + nevents = kevent(kq, &event, 1, NULL, 0, NULL); + if (nevents < 0) err(EX_OSERR, "kevent"); + + struct ServerMessage msg = { .type = SERVER_TILE }; + if (!clientSend(client, &msg)) continue; + clientMove(client, 0, 0); + + continue; + } + + struct Client *client = event.udata; + if (event.flags & EV_EOF) { + clientRemove(client); + continue; + } + + struct ClientMessage msg; + ssize_t len = recv(client->fd, &msg, sizeof(msg), 0); + if (len != sizeof(msg)) { + clientRemove(client); + continue; + } + + switch (msg.type) { + case CLIENT_MOVE: + clientMove(client, msg.data.m.dx, msg.data.m.dy); + break; + + case CLIENT_COLOR: + client->color = msg.data.c; + break; + + case CLIENT_PUT: + clientPut(client, msg.data.p); + break; + + default: + clientRemove(client); + } + } +} diff --git a/torus.h b/torus.h new file mode 100644 index 0000000..23b4708 --- /dev/null +++ b/torus.h @@ -0,0 +1,78 @@ +#include <stdbool.h> +#include <stdint.h> +#include <assert.h> + +#define ALIGNED(x) __attribute__((aligned(x))) + +enum { + COLOR_BLACK, + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW, + COLOR_BLUE, + COLOR_MAGENTA, + COLOR_CYAN, + COLOR_WHITE, + COLOR_BRIGHT, +}; + +#define CELL_ROWS (25) +#define CELL_COLS (80) +#define CELLS_SIZE (sizeof(char[CELL_ROWS][CELL_COLS])) + +#define CELL_INIT_X (CELL_COLS / 2) +#define CELL_INIT_Y (CELL_ROWS / 2) + +struct Tile { + bool present; + char cells[CELL_ROWS][CELL_COLS] ALIGNED(16); + uint8_t colors[CELL_ROWS][CELL_COLS] ALIGNED(16); +} ALIGNED(4096); +static_assert(sizeof(struct Tile) == 4096, "struct Tile is page-sized"); + +#define TILE_ROWS (512) +#define TILE_COLS (512) +#define TILES_SIZE (sizeof(struct Tile[TILE_ROWS][TILE_COLS])) + +#define TILE_INIT_X (0) +#define TILE_INIT_Y (0) + +enum ServerMessageType { + SERVER_TILE, + SERVER_MOVE, + SERVER_PUT, +}; + +struct ServerMessage { + enum ServerMessageType type; + union { + struct { + uint8_t cellX; + uint8_t cellY; + } m; + struct { + uint8_t cellX; + uint8_t cellY; + uint8_t color; + char cell; + } p; + } data; +}; + +enum ClientMessageType { + CLIENT_MOVE, + CLIENT_COLOR, + CLIENT_PUT, +}; + +struct ClientMessage { + enum ClientMessageType type; + union { + struct { + int8_t dx; + int8_t dy; + } m; + uint8_t c; + char p; + } data; +}; |