diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | README | 20 | ||||
-rw-r--r-- | client.c | 663 | ||||
-rw-r--r-- | help.c | 244 | ||||
-rw-r--r-- | index.html | 4 | ||||
-rw-r--r-- | merge.c | 146 | ||||
-rw-r--r-- | meta.c | 36 | ||||
-rw-r--r-- | server.c | 564 | ||||
-rw-r--r-- | sshd_config | 18 | ||||
-rw-r--r-- | torus.h | 138 |
10 files changed, 916 insertions, 919 deletions
diff --git a/Makefile b/Makefile index 6d39351..49377bc 100644 --- a/Makefile +++ b/Makefile @@ -45,5 +45,3 @@ tags: *.h *.c clean: rm -f tags $(BINS) termcap termcap.db chroot.tar - -.PHONY: all clean diff --git a/README b/README index c4da87e..eda30ad 100644 --- a/README +++ b/README @@ -2,14 +2,14 @@ Welcome to ascii.town! This software targets FreeBSD and Darwin. - torus.h Shared structures and message protocol. - server.c Maps torus.dat and listens on torus.sock. - client.c Curses client. - help.c Automated client which redraws a helpful banner. - meta.c Exports metadata from data file to CSV. - merge.c Interactively merges two data files. - snapshot.sh Snapshots data file, appropriate for cron. + torus.h Shared structures and message protocol. + server.c Maps torus.dat and listens on torus.sock. + client.c Curses client. + help.c Automated client which redraws a helpful banner. + meta.c Exports metadata from data file to CSV. + merge.c Interactively merges two data files. + snapshot.sh Snapshots data file, appropriate for cron. - sshd_config Anonymous login to chrooted client. - termcap.diff Patch for bright colors in xterm and rxvt. - index.html Friendly directions. + sshd_config Anonymous login to chrooted client. + termcap.diff Patch for bright colors in xterm and rxvt. + index.html Friendly directions. diff --git a/client.c b/client.c index 48dc03b..ee120e5 100644 --- a/client.c +++ b/client.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, Curtis McEnroe <curtis@cmcenroe.me> +/* Copyright (C) 2017 Curtis McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -30,398 +30,397 @@ #include "torus.h" enum { - ESC = 0x1B, - DEL = 0x7F, + ESC = 0x1B, + DEL = 0x7F, }; static int client; static void clientMessage(struct ClientMessage msg) { - ssize_t size = send(client, &msg, sizeof(msg), 0); - if (size < 0) err(EX_IOERR, "send"); + ssize_t size = send(client, &msg, sizeof(msg), 0); + if (size < 0) err(EX_IOERR, "send"); } static void clientMove(int8_t dx, int8_t dy) { - struct ClientMessage msg = { - .type = CLIENT_MOVE, - .move = { .dx = dx, .dy = dy }, - }; - clientMessage(msg); + struct ClientMessage msg = { + .type = CLIENT_MOVE, + .move = { .dx = dx, .dy = dy }, + }; + clientMessage(msg); } static void clientPut(uint8_t color, char cell) { - struct ClientMessage msg = { - .type = CLIENT_PUT, - .put = { .color = color, .cell = cell }, - }; - clientMessage(msg); + struct ClientMessage msg = { + .type = CLIENT_PUT, + .put = { .color = color, .cell = cell }, + }; + clientMessage(msg); } static void clientSpawn(uint8_t spawn) { - struct ClientMessage msg = { - .type = CLIENT_SPAWN, - .spawn = spawn, - }; - clientMessage(msg); + struct ClientMessage msg = { + .type = CLIENT_SPAWN, + .spawn = spawn, + }; + clientMessage(msg); } static chtype colorAttrs(uint8_t color) { - uint8_t bright = color & COLOR_BRIGHT; - uint8_t fg = color & 0x07; - uint8_t bg = color & 0x70; - return COLOR_PAIR(bg >> 1 | fg) | (bright ? A_BOLD : 0); + uint8_t bright = color & COLOR_BRIGHT; + uint8_t fg = color & 0x07; + uint8_t bg = color & 0x70; + return COLOR_PAIR(bg >> 1 | fg) | (bright ? A_BOLD : 0); } static uint8_t attrsColor(chtype attrs) { - chtype bright = attrs & A_BOLD; - short fg = PAIR_NUMBER(attrs) & 007; - short bg = PAIR_NUMBER(attrs) & 070; - return bg << 1 | fg | (bright ? COLOR_BRIGHT : 0); + chtype bright = attrs & A_BOLD; + short fg = PAIR_NUMBER(attrs) & 007; + short bg = PAIR_NUMBER(attrs) & 070; + return bg << 1 | fg | (bright ? COLOR_BRIGHT : 0); } static struct { - int8_t speed; - uint8_t color; - enum { - MODE_NORMAL, - MODE_INSERT, - MODE_REPLACE, - MODE_PUT, - MODE_DRAW, - } mode; - int8_t dx; - int8_t dy; - uint8_t len; - char draw; + int8_t speed; + uint8_t color; + enum { + MODE_NORMAL, + MODE_INSERT, + MODE_REPLACE, + MODE_PUT, + MODE_DRAW, + } mode; + int8_t dx; + int8_t dy; + uint8_t len; + char draw; } input = { - .speed = 1, - .color = COLOR_WHITE, - .dx = 1, + .speed = 1, + .color = COLOR_WHITE, + .dx = 1, }; static void colorFg(uint8_t fg) { - input.color = (input.color & 0xF8) | fg; + input.color = (input.color & 0xF8) | fg; } static void colorBg(uint8_t bg) { - input.color = (input.color & 0x0F) | (bg << 4); + input.color = (input.color & 0x0F) | (bg << 4); } static void colorInvert(void) { - input.color = - (input.color & 0x08) | - ((input.color & 0x07) << 4) | - ((input.color & 0x70) >> 4); + input.color = + (input.color & 0x08) | + ((input.color & 0x07) << 4) | + ((input.color & 0x70) >> 4); } static void insertMode(int8_t dx, int8_t dy) { - input.mode = MODE_INSERT; - input.dx = dx; - input.dy = dy; - input.len = 0; + input.mode = MODE_INSERT; + input.dx = dx; + input.dy = dy; + input.len = 0; } static void swapCell(int8_t dx, int8_t dy) { - uint8_t aColor = attrsColor(inch()); - char aCell = inch() & A_CHARTEXT; - - int sy, sx; - getyx(stdscr, sy, sx); - move(sy + dy, sx + dx); - uint8_t bColor = attrsColor(inch()); - char bCell = inch() & A_CHARTEXT; - move(sy, sx); - - clientPut(bColor, bCell); - clientMove(dx, dy); - clientPut(aColor, aCell); + uint8_t aColor = attrsColor(inch()); + char aCell = inch() & A_CHARTEXT; + + int sy, sx; + getyx(stdscr, sy, sx); + move(sy + dy, sx + dx); + uint8_t bColor = attrsColor(inch()); + char bCell = inch() & A_CHARTEXT; + move(sy, sx); + + clientPut(bColor, bCell); + clientMove(dx, dy); + clientPut(aColor, aCell); } static void readInput(void) { - int c = getch(); - - if (input.mode == MODE_INSERT) { - if (c == ESC) { - input.mode = MODE_NORMAL; - clientMove(-input.dx, -input.dy); - } else if (!input.dx && !input.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(-input.dx, -input.dy); - clientPut(input.color, ' '); - input.len--; - } else if (c == '\n') { - clientMove(input.dy, input.dx); - clientMove(-input.dx * input.len, -input.dy * input.len); - input.len = 0; - } else if (isprint(c)) { - clientPut(input.color, c); - clientMove(input.dx, input.dy); - input.len++; - } - return; - } - - if (input.mode == MODE_REPLACE) { - if (isprint(c)) clientPut(attrsColor(inch()), c); - input.mode = MODE_NORMAL; - return; - } - - if (input.mode == MODE_PUT) { - if (isprint(c)) clientPut(input.color, c); - input.mode = MODE_NORMAL; - return; - } - - if (input.mode == MODE_DRAW && !input.draw) { - if (c == ESC) input.mode = MODE_NORMAL; - if (isprint(c)) { - input.draw = c; - clientPut(input.color, c); - } - return; - } - - switch (c) { - case ESC: input.mode = MODE_NORMAL; break; - - case 'q': endwin(); exit(EX_OK); - case 'Q': { - if ((input.color & 0x07) < SPAWNS_LEN) { - clientSpawn(input.color & 0x07); - } else { - clientSpawn(0); - } - } break; - - case 'a': clientMove(1, 0); // fallthrough - case 'i': insertMode(1, 0); break; - case 'I': insertMode(0, 0); break; - case 'r': input.mode = MODE_REPLACE; break; - case 'p': input.mode = MODE_PUT; break; - case 'R': input.mode = MODE_DRAW; input.draw = 0; break; - case 'x': clientPut(attrsColor(inch()), ' '); break; - - case '~': { - clientPut(input.color, inch() & A_CHARTEXT); - clientMove(input.dx, input.dy); - } break; - - case '[': if (input.speed > 1) input.speed--; break; - case ']': if (input.speed < 4) input.speed++; break; - - case 'h': clientMove(-input.speed, 0); break; - case 'j': clientMove( 0, input.speed); break; - case 'k': clientMove( 0, -input.speed); break; - case 'l': clientMove( input.speed, 0); break; - case 'y': clientMove(-input.speed, -input.speed); break; - case 'u': clientMove( input.speed, -input.speed); break; - case 'b': clientMove(-input.speed, input.speed); break; - case 'n': clientMove( input.speed, input.speed); 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 '`': input.color = attrsColor(inch()); break; - - case '0': colorFg(COLOR_BLACK); break; - case '1': colorFg(COLOR_RED); break; - case '2': colorFg(COLOR_GREEN); break; - case '3': colorFg(COLOR_YELLOW); break; - case '4': colorFg(COLOR_BLUE); break; - case '5': colorFg(COLOR_MAGENTA); break; - case '6': colorFg(COLOR_CYAN); break; - case '7': colorFg(COLOR_WHITE); break; - - case ')': colorBg(COLOR_BLACK); break; - case '!': colorBg(COLOR_RED); break; - case '@': colorBg(COLOR_GREEN); break; - case '#': colorBg(COLOR_YELLOW); break; - case '$': colorBg(COLOR_BLUE); break; - case '%': colorBg(COLOR_MAGENTA); break; - case '^': colorBg(COLOR_CYAN); break; - case '&': colorBg(COLOR_WHITE); break; - - case '*': - case '8': input.color ^= COLOR_BRIGHT; break; - - case '(': - case '9': colorInvert(); 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 (input.mode == MODE_DRAW && input.draw) clientPut(input.color, input.draw); + int c = getch(); + + if (input.mode == MODE_INSERT) { + if (c == ESC) { + input.mode = MODE_NORMAL; + clientMove(-input.dx, -input.dy); + } else if (!input.dx && !input.dy) { + switch (c) { + break; 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); + } + } else if (c == '\b' || c == DEL) { + clientMove(-input.dx, -input.dy); + clientPut(input.color, ' '); + input.len--; + } else if (c == '\n') { + clientMove(input.dy, input.dx); + clientMove(-input.dx * input.len, -input.dy * input.len); + input.len = 0; + } else if (isprint(c)) { + clientPut(input.color, c); + clientMove(input.dx, input.dy); + input.len++; + } + return; + } + + if (input.mode == MODE_REPLACE) { + if (isprint(c)) clientPut(attrsColor(inch()), c); + input.mode = MODE_NORMAL; + return; + } + + if (input.mode == MODE_PUT) { + if (isprint(c)) clientPut(input.color, c); + input.mode = MODE_NORMAL; + return; + } + + if (input.mode == MODE_DRAW && !input.draw) { + if (c == ESC) input.mode = MODE_NORMAL; + if (isprint(c)) { + input.draw = c; + clientPut(input.color, c); + } + return; + } + + switch (c) { + break; case ESC: input.mode = MODE_NORMAL; + + break; case 'q': endwin(); exit(EX_OK); + break; case 'Q': { + if ((input.color & 0x07) < SPAWNS_LEN) { + clientSpawn(input.color & 0x07); + } else { + clientSpawn(0); + } + } + + break; case 'i': insertMode(1, 0); + break; case 'a': clientMove(1, 0); insertMode(1, 0); + break; case 'I': insertMode(0, 0); + break; case 'r': input.mode = MODE_REPLACE; + break; case 'p': input.mode = MODE_PUT; + break; case 'R': input.mode = MODE_DRAW; input.draw = 0; + break; case 'x': clientPut(attrsColor(inch()), ' '); + + break; case '~': { + clientPut(input.color, inch() & A_CHARTEXT); + clientMove(input.dx, input.dy); + } + + break; case '[': if (input.speed > 1) input.speed--; + break; case ']': if (input.speed < 4) input.speed++; + + break; case 'h': clientMove(-input.speed, 0); + break; case 'j': clientMove( 0, input.speed); + break; case 'k': clientMove( 0, -input.speed); + break; case 'l': clientMove( input.speed, 0); + break; case 'y': clientMove(-input.speed, -input.speed); + break; case 'u': clientMove( input.speed, -input.speed); + break; case 'b': clientMove(-input.speed, input.speed); + break; case 'n': clientMove( input.speed, input.speed); + + 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 '`': input.color = attrsColor(inch()); + + break; case '0': colorFg(COLOR_BLACK); + break; case '1': colorFg(COLOR_RED); + break; case '2': colorFg(COLOR_GREEN); + break; case '3': colorFg(COLOR_YELLOW); + break; case '4': colorFg(COLOR_BLUE); + break; case '5': colorFg(COLOR_MAGENTA); + break; case '6': colorFg(COLOR_CYAN); + break; case '7': colorFg(COLOR_WHITE); + + break; case ')': colorBg(COLOR_BLACK); + break; case '!': colorBg(COLOR_RED); + break; case '@': colorBg(COLOR_GREEN); + break; case '#': colorBg(COLOR_YELLOW); + break; case '$': colorBg(COLOR_BLUE); + break; case '%': colorBg(COLOR_MAGENTA); + break; case '^': colorBg(COLOR_CYAN); + break; case '&': colorBg(COLOR_WHITE); + + break; case '*': case '8': input.color ^= COLOR_BRIGHT; + + break; case '(': case '9': colorInvert(); + + 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); + } + + if (input.mode == MODE_DRAW && input.draw) clientPut(input.color, input.draw); } static void serverPut(uint8_t x, uint8_t y, uint8_t color, char cell) { - mvaddch(y, x, colorAttrs(color) | cell); + mvaddch(y, x, colorAttrs(color) | cell); } static void serverTile(void) { - struct Tile tile; - ssize_t size = recv(client, &tile, sizeof(tile), 0); - if (size < 0) err(EX_IOERR, "recv"); - if ((size_t)size < 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]); - } - } + struct Tile tile; + ssize_t size = recv(client, &tile, sizeof(tile), 0); + if (size < 0) err(EX_IOERR, "recv"); + if ((size_t)size < 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 serverCursor(uint8_t oldX, uint8_t oldY, uint8_t newX, uint8_t newY) { - if (oldX != CURSOR_NONE) { - move(oldY, oldX); - addch(inch() & ~A_REVERSE); - } - if (newX != CURSOR_NONE) { - move(newY, newX); - addch(inch() | A_REVERSE); - } + if (oldX != CURSOR_NONE) { + move(oldY, oldX); + addch(inch() & ~A_REVERSE); + } + if (newX != CURSOR_NONE) { + move(newY, newX); + addch(inch() | A_REVERSE); + } } static void readMessage(void) { - struct ServerMessage msg; - ssize_t size = recv(client, &msg, sizeof(msg), 0); - if (size < 0) err(EX_IOERR, "recv"); - if ((size_t)size < 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.move.cellY, msg.move.cellX); - refresh(); - } return; - - case SERVER_PUT: { - serverPut( - msg.put.cellX, - msg.put.cellY, - msg.put.color, - msg.put.cell - ); - } break; - - case SERVER_CURSOR: { - serverCursor( - msg.cursor.oldCellX, - msg.cursor.oldCellY, - msg.cursor.newCellX, - msg.cursor.newCellY - ); - } break; - - default: errx(EX_PROTOCOL, "I don't know what %d means!", msg.type); - } - - move(sy, sx); - refresh(); + struct ServerMessage msg; + ssize_t size = recv(client, &msg, sizeof(msg), 0); + if (size < 0) err(EX_IOERR, "recv"); + if ((size_t)size < sizeof(msg)) errx(EX_PROTOCOL, "A message was cut short."); + + int sy, sx; + getyx(stdscr, sy, sx); + + switch (msg.type) { + break; case SERVER_TILE: { + serverTile(); + } + + break; case SERVER_MOVE: { + move(msg.move.cellY, msg.move.cellX); + refresh(); + return; + } + + break; case SERVER_PUT: { + serverPut( + msg.put.cellX, + msg.put.cellY, + msg.put.color, + msg.put.cell + ); + } + + break; case SERVER_CURSOR: { + serverCursor( + msg.cursor.oldCellX, + msg.cursor.oldCellY, + msg.cursor.newCellX, + msg.cursor.newCellY + ); + } + + 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); - } + 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" - "If you think it does, check TERM.\n" - ); - exit(EX_CONFIG); - } - start_color(); - if (COLOR_PAIRS < 64) { - endwin(); - fprintf( - stderr, - "Sorry, your terminal doesn't support enough color pairs!\n" - "If you think it does, check TERM.\n" - ); - exit(EX_CONFIG); - } - for (int bg = COLOR_BLACK; bg < COLOR_BRIGHT; ++bg) { - for (int fg = COLOR_BLACK; fg < COLOR_BRIGHT; ++fg) { - init_pair(PAIR_NUMBER(colorAttrs(bg << 4 | fg)), fg, bg); - } - } + if (!has_colors()) { + endwin(); + fprintf( + stderr, + "Sorry, your terminal doesn't support colors!\n" + "If you think it does, check TERM.\n" + ); + exit(EX_CONFIG); + } + start_color(); + if (COLOR_PAIRS < 64) { + endwin(); + fprintf( + stderr, + "Sorry, your terminal doesn't support enough color pairs!\n" + "If you think it does, check TERM.\n" + ); + exit(EX_CONFIG); + } + for (int bg = COLOR_BLACK; bg < COLOR_BRIGHT; ++bg) { + for (int fg = COLOR_BLACK; fg < COLOR_BRIGHT; ++fg) { + init_pair(PAIR_NUMBER(colorAttrs(bg << 4 | fg)), fg, bg); + } + } } 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_NOINPUT, "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(); - } + 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_NOINPUT, "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 index da73527..61a0488 100644 --- a/help.c +++ b/help.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, Curtis McEnroe <curtis@cmcenroe.me> +/* Copyright (C) 2017 Curtis McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -26,37 +26,37 @@ static int client; static void clientMessage(struct ClientMessage msg) { - ssize_t size = send(client, &msg, sizeof(msg), 0); - if (size < 0) err(EX_IOERR, "send"); + ssize_t size = send(client, &msg, sizeof(msg), 0); + if (size < 0) err(EX_IOERR, "send"); } static void clientMove(int8_t dx, int8_t dy) { - struct ClientMessage msg = { - .type = CLIENT_MOVE, - .move = { .dx = dx, .dy = dy }, - }; - clientMessage(msg); + struct ClientMessage msg = { + .type = CLIENT_MOVE, + .move = { .dx = dx, .dy = dy }, + }; + clientMessage(msg); } static void clientPut(uint8_t color, char cell) { - struct ClientMessage msg = { - .type = CLIENT_PUT, - .put = { .color = color, .cell = cell }, - }; - clientMessage(msg); + struct ClientMessage msg = { + .type = CLIENT_PUT, + .put = { .color = color, .cell = cell }, + }; + clientMessage(msg); } static const useconds_t DELAY = 50000; enum { - R = COLOR_RED, - G = COLOR_GREEN, - Y = COLOR_YELLOW, - B = COLOR_BLUE, - M = COLOR_MAGENTA, - C = COLOR_CYAN, - W = COLOR_WHITE, - I = COLOR_BRIGHT | COLOR_WHITE, + R = COLOR_RED, + G = COLOR_GREEN, + Y = COLOR_YELLOW, + B = COLOR_BLUE, + M = COLOR_MAGENTA, + C = COLOR_CYAN, + W = COLOR_WHITE, + I = COLOR_BRIGHT | COLOR_WHITE, }; static void h(void) { clientMove(-1, 0); usleep(DELAY); } @@ -69,116 +69,116 @@ static void b(void) { clientMove(-1, 1); usleep(DELAY); } static void n(void) { clientMove( 1, 1); usleep(DELAY); } static void p(uint8_t color, char cell) { - clientPut(color, cell); - usleep(DELAY); + clientPut(color, cell); + usleep(DELAY); } static uint8_t len; static void s(uint8_t color, const char *str) { - for (; *str; ++len, ++str) { - clientPut(color, *str); - clientMove(1, 0); - usleep(DELAY); - } + for (; *str; ++len, ++str) { + clientPut(color, *str); + clientMove(1, 0); + usleep(DELAY); + } } static void r(void) { - clientMove(-len, 1); - usleep(DELAY); - len = 0; + clientMove(-len, 1); + usleep(DELAY); + len = 0; } 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_NOINPUT, "torus.sock"); - - pid_t pid = fork(); - if (pid < 0) err(EX_OSERR, "fork"); - - if (!pid) { - for (;;) { - char buf[4096]; - ssize_t size = recv(client, buf, sizeof(buf), 0); - if (size < 0) err(EX_IOERR, "recv"); - if (!size) return EX_OK; - } - } - - clientMove(-CELL_INIT_X, -CELL_INIT_Y); - clientMove(28, 0); - - for (;;) { - for (int i = 0; i < 10; ++i) { - clientMove(0, 1); - usleep(DELAY / 5); - } - for (int i = 0; i < 11; ++i) { - for (int j = 0; j < 28; ++j) { - clientPut(W, ' '); - if (i % 2) clientMove(1, 0); - else clientMove(-1, 0); - usleep(DELAY / 5); - } - clientPut(W, ' '); - if (i != 10) clientMove(0, -1); - usleep(DELAY / 5); - } - - j(); l(); l(); - s(W, "Welcome to "); s(I, "ascii.town"); s(W, "!"); r(); - r(); r(); - - n(); n(); s(W, "o-"); s(I, "l"); - h(); b(); p(W, '\\'); n(); p(I, 'n'); - y(); h(); p(W, '|'); j(); p(I, 'j'); - y(); p(W, '/'); b(); p(I, 'b'); - k(); u(); p(W, '-'); h(); p(I, 'h'); - u(); p(W, '\\'); y(); p(I, 'y'); - n(); l(); p(W, '|'); k(); p(I, 'k'); - n(); p(W, '/'); u(); p(I, 'u'); - - u(); s(W, " "); len = 0; - - s(I, "q "); s(W, "quit"); r(); - s(I, "i "); s(W, "insert"); r(); - s(I, "r "); s(W, "replace"); r(); - s(I, "R "); s(W, "draw"); r(); - s(I, "~ "); s(W, "color"); r(); - s(I, "` "); s(W, "pipette"); r(); - s(I, "* "); s(W, "bright"); - - s(W, " "); len = 0; - - clientPut(W, '7'); k(); - clientPut(C, '6'); k(); - clientPut(M, '5'); k(); - clientPut(B, '4'); k(); - clientPut(Y, '3'); k(); - clientPut(G, '2'); k(); - clientPut(R, '1'); k(); - - l(); n(); - - clientPut(R << 4, '!'); j(); - clientPut(G << 4, '@'); j(); - clientPut(Y << 4, '#'); j(); - clientPut(B << 4, '$'); j(); - clientPut(M << 4, '%'); j(); - clientPut(C << 4, '^'); j(); - clientPut(W << 4, '&'); j(); - - h(); k(); k(); k(); k(); k(); k(); k(); k(); k(); h(); - - sleep(30); - - u(); l(); l(); l(); - } + 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_NOINPUT, "torus.sock"); + + pid_t pid = fork(); + if (pid < 0) err(EX_OSERR, "fork"); + + if (!pid) { + for (;;) { + char buf[4096]; + ssize_t size = recv(client, buf, sizeof(buf), 0); + if (size < 0) err(EX_IOERR, "recv"); + if (!size) return EX_OK; + } + } + + clientMove(-CELL_INIT_X, -CELL_INIT_Y); + clientMove(28, 0); + + for (;;) { + for (int i = 0; i < 10; ++i) { + clientMove(0, 1); + usleep(DELAY / 5); + } + for (int i = 0; i < 11; ++i) { + for (int j = 0; j < 28; ++j) { + clientPut(W, ' '); + if (i % 2) clientMove(1, 0); + else clientMove(-1, 0); + usleep(DELAY / 5); + } + clientPut(W, ' '); + if (i != 10) clientMove(0, -1); + usleep(DELAY / 5); + } + + j(); l(); l(); + s(W, "Welcome to "); s(I, "ascii.town"); s(W, "!"); r(); + r(); r(); + + n(); n(); s(W, "o-"); s(I, "l"); + h(); b(); p(W, '\\'); n(); p(I, 'n'); + y(); h(); p(W, '|'); j(); p(I, 'j'); + y(); p(W, '/'); b(); p(I, 'b'); + k(); u(); p(W, '-'); h(); p(I, 'h'); + u(); p(W, '\\'); y(); p(I, 'y'); + n(); l(); p(W, '|'); k(); p(I, 'k'); + n(); p(W, '/'); u(); p(I, 'u'); + + u(); s(W, " "); len = 0; + + s(I, "q "); s(W, "quit"); r(); + s(I, "i "); s(W, "insert"); r(); + s(I, "r "); s(W, "replace"); r(); + s(I, "R "); s(W, "draw"); r(); + s(I, "~ "); s(W, "color"); r(); + s(I, "` "); s(W, "pipette"); r(); + s(I, "* "); s(W, "bright"); + + s(W, " "); len = 0; + + clientPut(W, '7'); k(); + clientPut(C, '6'); k(); + clientPut(M, '5'); k(); + clientPut(B, '4'); k(); + clientPut(Y, '3'); k(); + clientPut(G, '2'); k(); + clientPut(R, '1'); k(); + + l(); n(); + + clientPut(R << 4, '!'); j(); + clientPut(G << 4, '@'); j(); + clientPut(Y << 4, '#'); j(); + clientPut(B << 4, '$'); j(); + clientPut(M << 4, '%'); j(); + clientPut(C << 4, '^'); j(); + clientPut(W << 4, '&'); j(); + + h(); k(); k(); k(); k(); k(); k(); k(); k(); k(); h(); + + sleep(30); + + u(); l(); l(); l(); + } } diff --git a/index.html b/index.html index ad0c46a..3874b1a 100644 --- a/index.html +++ b/index.html @@ -4,11 +4,11 @@ Hey there, friend. This should get you there. <p> <code> - ssh torus@ascii.town + ssh torus@ascii.town </code> <p> You can also take a detour and play NetHack. <p> <code> - ssh nethack@ascii.town + ssh nethack@ascii.town </code> diff --git a/merge.c b/merge.c index 8ca5267..afe1012 100644 --- a/merge.c +++ b/merge.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, Curtis McEnroe <curtis@cmcenroe.me> +/* Copyright (C) 2017 Curtis McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -23,79 +23,79 @@ #include "torus.h" static void drawTile(int offsetY, const struct Tile *tile) { - for (uint8_t y = 0; y < CELL_ROWS; ++y) { - for (uint8_t x = 0; x < CELL_COLS; ++x) { - uint8_t color = tile->colors[y][x]; - char cell = tile->cells[y][x]; - - int attrs = COLOR_PAIR(color & ~COLOR_BRIGHT); - if (color & COLOR_BRIGHT) attrs |= A_BOLD; - mvaddch(offsetY + y, x, attrs | cell); - } - } + for (uint8_t y = 0; y < CELL_ROWS; ++y) { + for (uint8_t x = 0; x < CELL_COLS; ++x) { + uint8_t color = tile->colors[y][x]; + char cell = tile->cells[y][x]; + + int attrs = COLOR_PAIR(color & ~COLOR_BRIGHT); + if (color & COLOR_BRIGHT) attrs |= A_BOLD; + mvaddch(offsetY + y, x, attrs | cell); + } + } } int main(int argc, char *argv[]) { - if (argc != 4) return EX_USAGE; - - FILE *fileA = fopen(argv[1], "r"); - if (!fileA) err(EX_NOINPUT, "%s", argv[1]); - - FILE *fileB = fopen(argv[2], "r"); - if (!fileB) err(EX_NOINPUT, "%s", argv[2]); - - FILE *fileC = fopen(argv[3], "w"); - if (!fileC) err(EX_CANTCREAT, "%s", argv[3]); - - initscr(); - cbreak(); - noecho(); - keypad(stdscr, true); - set_escdelay(100); - - start_color(); - for (int bg = COLOR_BLACK; bg < COLOR_BRIGHT; ++bg) { - for (int fg = COLOR_BLACK; fg < COLOR_BRIGHT; ++fg) { - init_pair(bg << 4 | fg, fg, bg); - } - } - - mvhline(CELL_ROWS, 0, 0, CELL_COLS); - mvhline(CELL_ROWS * 2 + 1, 0, 0, CELL_COLS); - mvvline(0, CELL_COLS, 0, CELL_ROWS * 2 + 1); - mvaddch(CELL_ROWS, CELL_COLS, ACS_RTEE); - mvaddch(CELL_ROWS * 2 + 1, CELL_COLS, ACS_LRCORNER); - - struct Tile tileA, tileB; - for (;;) { - size_t countA = fread(&tileA, sizeof(tileA), 1, fileA); - if (ferror(fileA)) err(EX_IOERR, "%s", argv[1]); - - size_t countB = fread(&tileB, sizeof(tileB), 1, fileB); - if (ferror(fileB)) err(EX_IOERR, "%s", argv[2]); - - if (!countA && !countB) break; - if (!countA || !countB) errx(EX_DATAERR, "different size inputs"); - - const struct Tile *tileC = (tileA.accessTime > tileB.accessTime) - ? &tileA - : &tileB; - - if (tileA.modifyTime != tileB.modifyTime) { - drawTile(0, &tileA); - drawTile(CELL_ROWS + 1, &tileB); - move(CELL_ROWS * 2 + 2, 0); - refresh(); - - int c; - do { c = getch(); } while (c != 'a' && c != 'b'); - tileC = (c == 'a') ? &tileA : &tileB; - } - - fwrite(tileC, sizeof(*tileC), 1, fileC); - if (ferror(fileC)) err(EX_IOERR, "%s", argv[3]); - } - - endwin(); - return EX_OK; + if (argc != 4) return EX_USAGE; + + FILE *fileA = fopen(argv[1], "r"); + if (!fileA) err(EX_NOINPUT, "%s", argv[1]); + + FILE *fileB = fopen(argv[2], "r"); + if (!fileB) err(EX_NOINPUT, "%s", argv[2]); + + FILE *fileC = fopen(argv[3], "w"); + if (!fileC) err(EX_CANTCREAT, "%s", argv[3]); + + initscr(); + cbreak(); + noecho(); + keypad(stdscr, true); + set_escdelay(100); + + start_color(); + for (int bg = COLOR_BLACK; bg < COLOR_BRIGHT; ++bg) { + for (int fg = COLOR_BLACK; fg < COLOR_BRIGHT; ++fg) { + init_pair(bg << 4 | fg, fg, bg); + } + } + + mvhline(CELL_ROWS, 0, 0, CELL_COLS); + mvhline(CELL_ROWS * 2 + 1, 0, 0, CELL_COLS); + mvvline(0, CELL_COLS, 0, CELL_ROWS * 2 + 1); + mvaddch(CELL_ROWS, CELL_COLS, ACS_RTEE); + mvaddch(CELL_ROWS * 2 + 1, CELL_COLS, ACS_LRCORNER); + + struct Tile tileA, tileB; + for (;;) { + size_t countA = fread(&tileA, sizeof(tileA), 1, fileA); + if (ferror(fileA)) err(EX_IOERR, "%s", argv[1]); + + size_t countB = fread(&tileB, sizeof(tileB), 1, fileB); + if (ferror(fileB)) err(EX_IOERR, "%s", argv[2]); + + if (!countA && !countB) break; + if (!countA || !countB) errx(EX_DATAERR, "different size inputs"); + + const struct Tile *tileC = (tileA.accessTime > tileB.accessTime) + ? &tileA + : &tileB; + + if (tileA.modifyTime != tileB.modifyTime) { + drawTile(0, &tileA); + drawTile(CELL_ROWS + 1, &tileB); + move(CELL_ROWS * 2 + 2, 0); + refresh(); + + int c; + do { c = getch(); } while (c != 'a' && c != 'b'); + tileC = (c == 'a') ? &tileA : &tileB; + } + + fwrite(tileC, sizeof(*tileC), 1, fileC); + if (ferror(fileC)) err(EX_IOERR, "%s", argv[3]); + } + + endwin(); + return EX_OK; } diff --git a/meta.c b/meta.c index 3f4dcc8..ce71e82 100644 --- a/meta.c +++ b/meta.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, Curtis McEnroe <curtis@cmcenroe.me> +/* Copyright (C) 2017 Curtis McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -21,22 +21,22 @@ #include "torus.h" int main() { - printf("tileX,tileY,createTime,modifyCount,modifyTime,accessCount,accessTime\n"); - for (int i = 0;; ++i) { - struct Tile tile; - size_t count = fread(&tile, sizeof(tile), 1, stdin); - if (ferror(stdin)) err(EX_IOERR, "(stdin)"); - if (!count) return EX_OK; + printf("tileX,tileY,createTime,modifyCount,modifyTime,accessCount,accessTime\n"); + for (int i = 0;; ++i) { + struct Tile tile; + size_t count = fread(&tile, sizeof(tile), 1, stdin); + if (ferror(stdin)) err(EX_IOERR, "(stdin)"); + if (!count) return EX_OK; - printf( - "%d,%d,%ld,%u,%ld,%u,%ld\n", - i % TILE_COLS, - i / TILE_COLS, - tile.createTime, - tile.modifyCount, - tile.modifyTime, - tile.accessCount, - tile.accessTime - ); - } + printf( + "%d,%d,%ld,%u,%ld,%u,%ld\n", + i % TILE_COLS, + i / TILE_COLS, + tile.createTime, + tile.modifyCount, + tile.modifyTime, + tile.accessCount, + tile.accessTime + ); + } } diff --git a/server.c b/server.c index 67aa76c..a5b8855 100644 --- a/server.c +++ b/server.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, Curtis McEnroe <curtis@cmcenroe.me> +/* Copyright (C) 2017 Curtis McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -38,337 +38,337 @@ static struct Tile *tiles; static void tilesMap(void) { - int fd = open("torus.dat", O_CREAT | O_RDWR, 0644); - if (fd < 0) err(EX_CANTCREAT, "torus.dat"); + int fd = open("torus.dat", O_CREAT | O_RDWR, 0644); + if (fd < 0) err(EX_CANTCREAT, "torus.dat"); - int error = ftruncate(fd, TILES_SIZE); - if (error) err(EX_IOERR, "ftruncate"); + 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"); + tiles = mmap(NULL, TILES_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (tiles == MAP_FAILED) err(EX_OSERR, "mmap"); - error = madvise(tiles, TILES_SIZE, MADV_RANDOM); - if (error) err(EX_OSERR, "madvise"); + error = madvise(tiles, TILES_SIZE, MADV_RANDOM); + if (error) err(EX_OSERR, "madvise"); #ifdef MADV_NOCORE - error = madvise(tiles, TILES_SIZE, MADV_NOCORE); - if (error) err(EX_OSERR, "madvise"); + error = madvise(tiles, TILES_SIZE, MADV_NOCORE); + if (error) err(EX_OSERR, "madvise"); #endif } static struct Tile *tileGet(uint32_t tileX, uint32_t tileY) { - struct Tile *tile = &tiles[tileY * TILE_ROWS + tileX]; - if (!tile->createTime) { - memset(tile->cells, ' ', CELLS_SIZE); - memset(tile->colors, COLOR_WHITE, CELLS_SIZE); - tile->createTime = time(NULL); - } - return tile; + struct Tile *tile = &tiles[tileY * TILE_ROWS + tileX]; + if (!tile->createTime) { + memset(tile->cells, ' ', CELLS_SIZE); + memset(tile->colors, COLOR_WHITE, CELLS_SIZE); + tile->createTime = time(NULL); + } + return tile; } static struct Tile *tileAccess(uint32_t tileX, uint32_t tileY) { - struct Tile *tile = tileGet(tileX, tileY); - tile->accessTime = time(NULL); - tile->accessCount++; - return tile; + struct Tile *tile = tileGet(tileX, tileY); + tile->accessTime = time(NULL); + tile->accessCount++; + return tile; } static struct Tile *tileModify(uint32_t tileX, uint32_t tileY) { - struct Tile *tile = tileGet(tileX, tileY); - tile->modifyTime = time(NULL); - tile->modifyCount++; - return tile; + struct Tile *tile = tileGet(tileX, tileY); + tile->modifyTime = time(NULL); + tile->modifyCount++; + return tile; } static struct Client { - int fd; + int fd; - uint32_t tileX; - uint32_t tileY; - uint8_t cellX; - uint8_t cellY; + uint32_t tileX; + uint32_t tileY; + uint8_t cellX; + uint8_t cellY; - struct Client *prev; - struct Client *next; + 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_VOID_X; - client->tileY = TILE_VOID_Y; - client->cellX = CELL_INIT_X; - client->cellY = CELL_INIT_Y; - - client->prev = NULL; - if (clientHead) { - clientHead->prev = client; - client->next = clientHead; - } else { - client->next = NULL; - } - clientHead = client; - - return client; + struct Client *client = malloc(sizeof(*client)); + if (!client) err(EX_OSERR, "malloc"); + + client->fd = fd; + client->tileX = TILE_VOID_X; + client->tileY = TILE_VOID_Y; + client->cellX = CELL_INIT_X; + client->cellY = CELL_INIT_Y; + + client->prev = NULL; + if (clientHead) { + clientHead->prev = client; + client->next = clientHead; + } else { + client->next = NULL; + } + clientHead = client; + + return client; } static bool clientSend(const struct Client *client, struct ServerMessage msg) { - ssize_t size = send(client->fd, &msg, sizeof(msg), 0); - if (size < 0) return false; + ssize_t size = send(client->fd, &msg, sizeof(msg), 0); + if (size < 0) return false; - if (msg.type == SERVER_TILE) { - struct Tile *tile = tileAccess(client->tileX, client->tileY); - size = send(client->fd, tile, sizeof(*tile), 0); - if (size < 0) return false; - } + if (msg.type == SERVER_TILE) { + struct Tile *tile = tileAccess(client->tileX, client->tileY); + size = send(client->fd, tile, sizeof(*tile), 0); + if (size < 0) return false; + } - return true; + return true; } static void clientCast(const struct Client *origin, struct ServerMessage msg) { - for (struct Client *client = clientHead; client; client = client->next) { - if (client == origin) continue; - if (client->tileX != origin->tileX) continue; - if (client->tileY != origin->tileY) continue; - clientSend(client, msg); - } + for (struct Client *client = clientHead; client; client = client->next) { + if (client == origin) continue; + if (client->tileX != origin->tileX) continue; + if (client->tileY != origin->tileY) continue; + clientSend(client, msg); + } } 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; - - struct ServerMessage msg = { - .type = SERVER_CURSOR, - .cursor = { - .oldCellX = client->cellX, .oldCellY = client->cellY, - .newCellX = CURSOR_NONE, .newCellY = CURSOR_NONE, - }, - }; - clientCast(client, msg); - - close(client->fd); - free(client); + if (client->prev) client->prev->next = client->next; + if (client->next) client->next->prev = client->prev; + if (clientHead == client) clientHead = client->next; + + struct ServerMessage msg = { + .type = SERVER_CURSOR, + .cursor = { + .oldCellX = client->cellX, .oldCellY = client->cellY, + .newCellX = CURSOR_NONE, .newCellY = CURSOR_NONE, + }, + }; + clientCast(client, msg); + + close(client->fd); + free(client); } static bool clientCursors(const struct Client *client) { - struct ServerMessage msg = { - .type = SERVER_CURSOR, - .cursor = { .oldCellX = CURSOR_NONE, .oldCellY = CURSOR_NONE }, - }; - - for (struct Client *friend = clientHead; friend; friend = friend->next) { - if (friend == client) continue; - if (friend->tileX != client->tileX) continue; - if (friend->tileY != client->tileY) continue; - - msg.cursor.newCellX = friend->cellX; - msg.cursor.newCellY = friend->cellY; - if (!clientSend(client, msg)) return false; - } - return true; + struct ServerMessage msg = { + .type = SERVER_CURSOR, + .cursor = { .oldCellX = CURSOR_NONE, .oldCellY = CURSOR_NONE }, + }; + + for (struct Client *friend = clientHead; friend; friend = friend->next) { + if (friend == client) continue; + if (friend->tileX != client->tileX) continue; + if (friend->tileY != client->tileY) continue; + + msg.cursor.newCellX = friend->cellX; + msg.cursor.newCellY = friend->cellY; + if (!clientSend(client, msg)) return false; + } + return true; } static bool clientUpdate(const struct Client *client, const struct Client *old) { - struct ServerMessage msg = { - .type = SERVER_MOVE, - .move = { .cellX = client->cellX, .cellY = client->cellY }, - }; - if (!clientSend(client, msg)) return false; - - if (client->tileX != old->tileX || client->tileY != old->tileY) { - msg.type = SERVER_TILE; - if (!clientSend(client, msg)) return false; - - if (!clientCursors(client)) return false; - - msg = (struct ServerMessage) { - .type = SERVER_CURSOR, - .cursor = { - .oldCellX = old->cellX, .oldCellY = old->cellY, - .newCellX = CURSOR_NONE, .newCellY = CURSOR_NONE, - }, - }; - clientCast(old, msg); - - msg = (struct ServerMessage) { - .type = SERVER_CURSOR, - .cursor = { - .oldCellX = CURSOR_NONE, .oldCellY = CURSOR_NONE, - .newCellX = client->cellX, .newCellY = client->cellY, - }, - }; - clientCast(client, msg); - - } else { - msg = (struct ServerMessage) { - .type = SERVER_CURSOR, - .cursor = { - .oldCellX = old->cellX, .oldCellY = old->cellY, - .newCellX = client->cellX, .newCellY = client->cellY, - }, - }; - clientCast(client, msg); - } - - return true; + struct ServerMessage msg = { + .type = SERVER_MOVE, + .move = { .cellX = client->cellX, .cellY = client->cellY }, + }; + if (!clientSend(client, msg)) return false; + + if (client->tileX != old->tileX || client->tileY != old->tileY) { + msg.type = SERVER_TILE; + if (!clientSend(client, msg)) return false; + + if (!clientCursors(client)) return false; + + msg = (struct ServerMessage) { + .type = SERVER_CURSOR, + .cursor = { + .oldCellX = old->cellX, .oldCellY = old->cellY, + .newCellX = CURSOR_NONE, .newCellY = CURSOR_NONE, + }, + }; + clientCast(old, msg); + + msg = (struct ServerMessage) { + .type = SERVER_CURSOR, + .cursor = { + .oldCellX = CURSOR_NONE, .oldCellY = CURSOR_NONE, + .newCellX = client->cellX, .newCellY = client->cellY, + }, + }; + clientCast(client, msg); + + } else { + msg = (struct ServerMessage) { + .type = SERVER_CURSOR, + .cursor = { + .oldCellX = old->cellX, .oldCellY = old->cellY, + .newCellX = client->cellX, .newCellY = client->cellY, + }, + }; + clientCast(client, msg); + } + + return true; } static bool clientSpawn(struct Client *client, uint8_t spawn) { - if (spawn >= SPAWNS_LEN) return false; - struct Client old = *client; - client->tileX = SPAWNS[spawn].tileX; - client->tileY = SPAWNS[spawn].tileY; - client->cellX = CELL_INIT_X; - client->cellY = CELL_INIT_Y; - return clientUpdate(client, &old); + if (spawn >= SPAWNS_LEN) return false; + struct Client old = *client; + client->tileX = SPAWNS[spawn].tileX; + client->tileY = SPAWNS[spawn].tileY; + client->cellX = CELL_INIT_X; + client->cellY = CELL_INIT_Y; + return clientUpdate(client, &old); } static bool clientMove(struct Client *client, int8_t dx, int8_t dy) { - struct Client old = *client; - - if (dx > CELL_COLS - client->cellX) dx = CELL_COLS - client->cellX; - if (dx < -client->cellX - 1) dx = -client->cellX - 1; - if (dy > CELL_ROWS - client->cellY) dy = CELL_ROWS - client->cellY; - if (dy < -client->cellY - 1) dy = -client->cellY - 1; - - client->cellX += dx; - client->cellY += dy; - - 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; - - assert(client->cellX < CELL_COLS); - assert(client->cellY < CELL_ROWS); - assert(client->tileX < TILE_COLS); - assert(client->tileY < TILE_ROWS); - - return clientUpdate(client, &old); + struct Client old = *client; + + if (dx > CELL_COLS - client->cellX) dx = CELL_COLS - client->cellX; + if (dx < -client->cellX - 1) dx = -client->cellX - 1; + if (dy > CELL_ROWS - client->cellY) dy = CELL_ROWS - client->cellY; + if (dy < -client->cellY - 1) dy = -client->cellY - 1; + + client->cellX += dx; + client->cellY += dy; + + 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; + + assert(client->cellX < CELL_COLS); + assert(client->cellY < CELL_ROWS); + assert(client->tileX < TILE_COLS); + assert(client->tileY < TILE_ROWS); + + return clientUpdate(client, &old); } static bool clientPut(const struct Client *client, uint8_t color, char cell) { - struct Tile *tile = tileModify(client->tileX, client->tileY); - tile->colors[client->cellY][client->cellX] = color; - tile->cells[client->cellY][client->cellX] = cell; - - struct ServerMessage msg = { - .type = SERVER_PUT, - .put = { - .cellX = client->cellX, - .cellY = client->cellY, - .color = color, - .cell = cell, - }, - }; - bool success = clientSend(client, msg); - clientCast(client, msg); - return success; + struct Tile *tile = tileModify(client->tileX, client->tileY); + tile->colors[client->cellY][client->cellX] = color; + tile->cells[client->cellY][client->cellX] = cell; + + struct ServerMessage msg = { + .type = SERVER_PUT, + .put = { + .cellX = client->cellX, + .cellY = client->cellY, + .color = color, + .cell = cell, + }, + }; + bool success = clientSend(client, msg); + clientCast(client, msg); + return success; } int main() { - int error; - - 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_CANTCREAT, "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, - }; - 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 (!event.udata) { - int fd = accept(server, NULL, NULL); - if (fd < 0) err(EX_IOERR, "accept"); - fcntl(fd, F_SETFL, O_NONBLOCK); - - int on = 1; - error = setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on)); - if (error) err(EX_IOERR, "setsockopt"); - - struct Client *client = clientAdd(fd); - - struct kevent event = { - .ident = fd, - .filter = EVFILT_READ, - .flags = EV_ADD, - .udata = client, - }; - nevents = kevent(kq, &event, 1, NULL, 0, NULL); - if (nevents < 0) err(EX_IOERR, "kevent"); - - if (!clientSpawn(client, 0)) clientRemove(client); - - continue; - } - - struct Client *client = event.udata; - if (event.flags & EV_EOF) { - clientRemove(client); - continue; - } - - struct ClientMessage msg; - ssize_t size = recv(client->fd, &msg, sizeof(msg), 0); - if (size != sizeof(msg)) { - clientRemove(client); - continue; - } - - bool success = false; - if (msg.type == CLIENT_MOVE) { - success = clientMove(client, msg.move.dx, msg.move.dy); - } else if (msg.type == CLIENT_PUT) { - success = clientPut(client, msg.put.color, msg.put.cell); - } else if (msg.type == CLIENT_SPAWN) { - success = clientSpawn(client, msg.spawn); - } - if (!success) clientRemove(client); - } + int error; + + 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_CANTCREAT, "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, + }; + 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 (!event.udata) { + int fd = accept(server, NULL, NULL); + if (fd < 0) err(EX_IOERR, "accept"); + fcntl(fd, F_SETFL, O_NONBLOCK); + + int on = 1; + error = setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on)); + if (error) err(EX_IOERR, "setsockopt"); + + struct Client *client = clientAdd(fd); + + struct kevent event = { + .ident = fd, + .filter = EVFILT_READ, + .flags = EV_ADD, + .udata = client, + }; + nevents = kevent(kq, &event, 1, NULL, 0, NULL); + if (nevents < 0) err(EX_IOERR, "kevent"); + + if (!clientSpawn(client, 0)) clientRemove(client); + + continue; + } + + struct Client *client = event.udata; + if (event.flags & EV_EOF) { + clientRemove(client); + continue; + } + + struct ClientMessage msg; + ssize_t size = recv(client->fd, &msg, sizeof(msg), 0); + if (size != sizeof(msg)) { + clientRemove(client); + continue; + } + + bool success = false; + if (msg.type == CLIENT_MOVE) { + success = clientMove(client, msg.move.dx, msg.move.dy); + } else if (msg.type == CLIENT_PUT) { + success = clientPut(client, msg.put.color, msg.put.cell); + } else if (msg.type == CLIENT_SPAWN) { + success = clientSpawn(client, msg.spawn); + } + if (!success) clientRemove(client); + } } diff --git a/sshd_config b/sshd_config index 9e48107..2d4366a 100644 --- a/sshd_config +++ b/sshd_config @@ -1,13 +1,13 @@ UsePAM no Match User torus - PasswordAuthentication yes - PermitEmptyPasswords yes - ChrootDirectory /home/torus - ForceCommand client + PasswordAuthentication yes + PermitEmptyPasswords yes + ChrootDirectory /home/torus + ForceCommand client - AllowAgentForwarding no - AllowTcpForwarding no - AllowStreamLocalForwarding no - MaxSessions 1 - X11Forwarding no + AllowAgentForwarding no + AllowTcpForwarding no + AllowStreamLocalForwarding no + MaxSessions 1 + X11Forwarding no diff --git a/torus.h b/torus.h index 92cf934..d3c0d81 100644 --- a/torus.h +++ b/torus.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, Curtis McEnroe <curtis@cmcenroe.me> +/* Copyright (C) 2017 Curtis McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -34,20 +34,20 @@ #undef COLOR_WHITE enum { - COLOR_BLACK, - COLOR_RED, - COLOR_GREEN, - COLOR_YELLOW, - COLOR_BLUE, - COLOR_MAGENTA, - COLOR_CYAN, - COLOR_WHITE, - COLOR_BRIGHT, + COLOR_BLACK, + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW, + COLOR_BLUE, + COLOR_MAGENTA, + COLOR_CYAN, + COLOR_WHITE, + COLOR_BRIGHT, }; enum { - CELL_ROWS = 25, - CELL_COLS = 80, + CELL_ROWS = 25, + CELL_COLS = 80, }; static const size_t CELLS_SIZE = sizeof(char[CELL_ROWS][CELL_COLS]); @@ -55,21 +55,21 @@ static const uint8_t CELL_INIT_X = CELL_COLS / 2; static const uint8_t CELL_INIT_Y = CELL_ROWS / 2; struct ALIGNED(4096) Tile { - time_t createTime; - time_t modifyTime; - char ALIGNED(16) cells[CELL_ROWS][CELL_COLS]; - uint8_t ALIGNED(16) colors[CELL_ROWS][CELL_COLS]; - uint32_t modifyCount; - uint32_t accessCount; - time_t accessTime; + time_t createTime; + time_t modifyTime; + char ALIGNED(16) cells[CELL_ROWS][CELL_COLS]; + uint8_t ALIGNED(16) colors[CELL_ROWS][CELL_COLS]; + uint32_t modifyCount; + uint32_t accessCount; + time_t accessTime; }; -static_assert(4096 == sizeof(struct Tile), "struct File is page-sized"); +static_assert(4096 == sizeof(struct Tile), "struct Tile is page-sized"); static_assert(16 == offsetof(struct Tile, cells), "stable cells offset"); static_assert(2016 == offsetof(struct Tile, colors), "stable colors offset"); enum { - TILE_ROWS = 512, - TILE_COLS = 512, + TILE_ROWS = 512, + TILE_COLS = 512, }; static const size_t TILES_SIZE = sizeof(struct Tile[TILE_ROWS][TILE_COLS]); @@ -77,61 +77,61 @@ static const uint32_t TILE_VOID_X = UINT32_MAX; static const uint32_t TILE_VOID_Y = UINT32_MAX; static const struct { - uint32_t tileX; - uint32_t tileY; + uint32_t tileX; + uint32_t tileY; } SPAWNS[] = { - { 0, 0 }, - { TILE_COLS * 3 / 4, TILE_ROWS * 3 / 4 }, // NW - { TILE_COLS * 1 / 4, TILE_ROWS * 3 / 4 }, // NE - { TILE_COLS * 1 / 4, TILE_ROWS * 1 / 4 }, // SE - { TILE_COLS * 3 / 4, TILE_ROWS * 1 / 4 }, // SW + { 0, 0 }, + { TILE_COLS * 3 / 4, TILE_ROWS * 3 / 4 }, // NW + { TILE_COLS * 1 / 4, TILE_ROWS * 3 / 4 }, // NE + { TILE_COLS * 1 / 4, TILE_ROWS * 1 / 4 }, // SE + { TILE_COLS * 3 / 4, TILE_ROWS * 1 / 4 }, // SW }; static const size_t SPAWNS_LEN = sizeof(SPAWNS) / sizeof(SPAWNS[0]); struct ServerMessage { - enum PACKED { - SERVER_TILE, - SERVER_MOVE, - SERVER_PUT, - SERVER_CURSOR, - } type; - union { - struct { - uint8_t cellX; - uint8_t cellY; - } move; - struct { - uint8_t cellX; - uint8_t cellY; - uint8_t color; - char cell; - } put; - struct { - uint8_t oldCellX; - uint8_t oldCellY; - uint8_t newCellX; - uint8_t newCellY; - } cursor; - }; + enum PACKED { + SERVER_TILE, + SERVER_MOVE, + SERVER_PUT, + SERVER_CURSOR, + } type; + union { + struct { + uint8_t cellX; + uint8_t cellY; + } move; + struct { + uint8_t cellX; + uint8_t cellY; + uint8_t color; + char cell; + } put; + struct { + uint8_t oldCellX; + uint8_t oldCellY; + uint8_t newCellX; + uint8_t newCellY; + } cursor; + }; }; static const uint8_t CURSOR_NONE = UINT8_MAX; struct ClientMessage { - enum PACKED { - CLIENT_MOVE, - CLIENT_PUT, - CLIENT_SPAWN, - } type; - union { - struct { - int8_t dx; - int8_t dy; - } move; - struct { - uint8_t color; - char cell; - } put; - uint8_t spawn; - }; + enum PACKED { + CLIENT_MOVE, + CLIENT_PUT, + CLIENT_SPAWN, + } type; + union { + struct { + int8_t dx; + int8_t dy; + } move; + struct { + uint8_t color; + char cell; + } put; + uint8_t spawn; + }; }; |