AmendHub

jcs

/

subtext

/

amendments

/

43

chat: Only output to a user if their output buffer can handle it

We can't directly output to another user's session from
chat_printf_line because it may call uthread_yield, which won't do
anything since it's interrupting the sender's thread. If the user's
output buffer can't handle the data, just kick them out of chat for
being too lagged.

jcs made amendment 43 about 1 year ago
--- chat.c Wed Dec 29 08:57:17 2021 +++ chat.c Sun Jan 2 10:39:22 2022 @@ -25,7 +25,7 @@ #include "session.h" #include "util.h" -#define MAX_CHAT_INPUT 100 +#define CHAT_MAX_INPUT 100 static char chat_tbuf[256]; @@ -35,14 +35,14 @@ void chat_broadcast(struct session *s, char *str) { short n; - + for (n = 0; n < nsessions; n++) { if (!sessions[n]->chatting) continue; if (strcmp((char *)s->chatting_with_node, (char *)sessions[n]->chatting_with_node) != 0) continue; - + chat_printf_line(sessions[n], 1, "%s", str); } } @@ -51,27 +51,32 @@ size_t chat_printf_line(struct session *s, short around_bar, char *format, ...) { static char chat_pbuf[256]; + static char chat_final_pbuf[512]; char *pbuf_line; va_list ap; - size_t len, llen, max_llen; struct tm *tm; - short i; - + size_t final_len = 0, plen, llen, max_llen; + + if (around_bar) + final_len = strlcpy(chat_final_pbuf, + ansi(s, ANSI_SAVE_CURSOR, ANSI_END), + sizeof(chat_final_pbuf)); + + /* prepend time */ tm = localtime((time_t *)&Time); - len = strftime(chat_pbuf, sizeof(chat_pbuf), "[%H:%M] ", tm); + plen = strftime(chat_pbuf, sizeof(chat_pbuf), "[%H:%M] ", tm); + /* append orignal line */ va_start(ap, format); - len += vsnprintf(chat_pbuf + len, sizeof(chat_pbuf) - len, format, ap); + plen += vsnprintf(chat_pbuf + plen, sizeof(chat_pbuf) - plen, format, + ap); va_end(ap); pbuf_line = chat_pbuf; max_llen = s->terminal_columns; - if (around_bar) - session_output_string(s, ansi(s, ANSI_SAVE_CURSOR, ANSI_END)); - - while (len > 0) { - if (len > max_llen) { + while (plen > 0) { + if (plen > max_llen) { /* chop at last word boundary */ llen = max_llen; while (llen >= 0 && pbuf_line[llen] != ' ') @@ -80,40 +85,68 @@ chat_printf_line(struct session *s, short around_bar, /* no words to break on, hard break */ llen = max_llen; } else { - llen = len; + llen = plen; } - + if (around_bar) - session_output_string(s, - ansi(s, ANSI_CURSOR_LINE_COL, 1, 1, ANSI_DELETE_LINES_N, 1, + final_len = strlcat(chat_final_pbuf, + ansi(s, ANSI_CURSOR_LINE_COL, 1, 1, + ANSI_DELETE_LINES_N, 1, ANSI_CURSOR_LINE_COL, s->terminal_lines - 2, 1, - ANSI_INSERT_LINES_N, 1, ANSI_END)); + ANSI_INSERT_LINES_N, 1, + ANSI_END), + sizeof(chat_final_pbuf)); if (pbuf_line != chat_pbuf) { if (!around_bar) - session_output(s, "\r\n", 2); - session_output(s, " ", 3); + final_len = strlcat(chat_final_pbuf, "\r\n", + sizeof(chat_final_pbuf)); + final_len = strlcat(chat_final_pbuf, " ", + sizeof(chat_final_pbuf)); } - session_output(s, pbuf_line, llen); + /* manually copy since pbuf_line+llen is not null terminated */ + if (final_len + llen + 1 > sizeof(chat_final_pbuf)) + break; + memcpy(chat_final_pbuf + final_len, pbuf_line, llen); + chat_final_pbuf[final_len + llen] = '\0'; + pbuf_line += llen; - len -= llen; + plen -= llen; } if (around_bar) - session_output_string(s, - ansi(s, ANSI_RESTORE_SAVED_CURSOR, ANSI_END)); + final_len = strlcat(chat_final_pbuf, + ansi(s, ANSI_RESTORE_SAVED_CURSOR, ANSI_END), + sizeof(chat_final_pbuf)); else - session_output(s, "\r\n", 2); + final_len = strlcat(chat_final_pbuf, "\r\n", + sizeof(chat_final_pbuf)); - return 1; /* XXX */ + if (final_len > sizeof(chat_final_pbuf)) + panic("chat_final_pbuf overflow! %ld", final_len); + + /* + * If this session's output buffer is too full to take this, kick them + * out of chat + */ + if (s->obuflen + final_len > sizeof(s->obuf)) { + s->chatting = 0; + s->abort_input = 1; + return 0; + } + + memcpy(s->obuf + s->obuflen, chat_final_pbuf, final_len); + s->obuflen += final_len; + + return final_len; } void chat_start(struct session *s, char *with_node) { char *input; - short len, alen; + short len, alen, lagged = 0; s->chatting = 1; if (with_node[0]) @@ -129,12 +162,14 @@ chat_start(struct session *s, char *with_node) ansi(s, ANSI_BOLD, ANSI_END), ansi(s, ANSI_RESET, ANSI_END)); chat_printf_line(s, 0, "Type %s/help%s for available commands", ansi(s, ANSI_BOLD, ANSI_END), ansi(s, ANSI_RESET, ANSI_END)); - + session_flush(s); + snprintf(chat_tbuf, sizeof(chat_tbuf), " [ %s ] [ Chatting with %s ]", s->user ? s->user->username : "guest", s->chatting_with_node[0] ? "..." : "everyone"); session_printf(s, "%s\r\n", session_bar(s, chat_tbuf, NULL)); + session_flush(s); /* TODO: print other users in chat */ @@ -143,13 +178,15 @@ chat_start(struct session *s, char *with_node) s->user ? s->user->username : "guest", s->user && s->user->is_sysop ? " (sysop)" : ""); chat_broadcast(s, chat_tbuf); + session_flush(s); for (;;) { - input = session_field_input(s, MAX_CHAT_INPUT - 1, + input = session_field_input(s, CHAT_MAX_INPUT - 1, s->terminal_columns - 1, 0); if (!input) break; session_printf(s, "\r%s", ansi(s, ANSI_ERASE_LINE, ANSI_END)); + session_flush(s); if (input[0] == '/') { if (strcmp(input, "/quit") == 0 || @@ -162,35 +199,62 @@ chat_start(struct session *s, char *with_node) } else { chat_printf_line(s, 1, "*** Unknown command: %s", input + 1); + session_flush(s); } } else { snprintf(chat_tbuf, sizeof(chat_tbuf), "<%s> %s", s->user ? s->user->username : "guest", input); chat_broadcast(s, chat_tbuf); + session_flush(s); } free(input); } - + snprintf(chat_tbuf, sizeof(chat_tbuf), "*** %s%s has left chat", s->user ? s->user->username : "guest", s->user && s->user->is_sysop ? " (sysop)" : ""); + + if (s->ending) + strlcat(chat_tbuf, " (disconnected)", sizeof(chat_tbuf)); + else if (s->abort_input) + strlcat(chat_tbuf, " (too lagged)", sizeof(chat_tbuf)); chat_broadcast(s, chat_tbuf); + session_flush(s); + if (s->abort_input && !s->chatting) + lagged = 1; + s->chatting = 0; memset(s->chatting_with_node, 0, sizeof(s->chatting_with_node)); + + /* clear chat bar */ + session_output_string(s, ansi(s, ANSI_CURSOR_LINE_COL, + s->terminal_lines - 1, 1, ANSI_ERASE_LINE, ANSI_END)); + if (lagged) { + /* + * We were kicked out for being too lagged, so we didn't see + * that last broadcast; show it once we've flushed our output on + * our own time. + */ + session_printf(s, "%s\r\n", chat_tbuf); + session_flush(s); + } + session_output(s, "\r\n", 2); + session_flush(s); } void chat_help(struct session *s) { chat_printf_line(s, 1, "*** Available chat commands:"); - chat_printf_line(s, 1, "*** %s/quit% : Leave chat", + chat_printf_line(s, 1, "*** %s/quit%s : Leave chat", ansi(s, ANSI_BOLD, ANSI_END), ansi(s, ANSI_RESET, ANSI_END)); - chat_printf_line(s, 1, "*** %s/msg user message% : " + chat_printf_line(s, 1, "*** %s/msg user message%s : " "Send \"message\" only to user \"user\"", ansi(s, ANSI_BOLD, ANSI_END), ansi(s, ANSI_RESET, ANSI_END)); + session_flush(s); } --- chat.h Thu Dec 23 14:12:24 2021 +++ chat.h Sat Jan 1 18:34:46 2022 @@ -18,6 +18,7 @@ #define __CHAT_H__ #include <stddef.h> +#include "db.h" #include "session.h" void chat_broadcast(struct session *s, char *str); --- console.c Wed Dec 29 20:45:19 2021 +++ console.c Sun Jan 2 10:15:25 2022 @@ -261,7 +261,6 @@ short console_input(struct session *session) { /* nothing to do here, input is fed from main loop */ - uthread_yield(); return 0; } @@ -451,7 +450,7 @@ console_shift_chars(struct console *console, short sta if (offset < 0) mover.top -= FONT_HEIGHT; mover.left = console->win->portRect.left + CONSOLE_PADDING; - mover.bottom = mover.top + (FONT_HEIGHT * (count / console->ncolumns)); + mover.bottom = mover.top + (FONT_HEIGHT * ((count / console->ncolumns) + 1)); mover.right = mover.left + (FONT_WIDTH * console->ncolumns); ScrollRect(&mover, 0, (offset / console->ncolumns) * FONT_HEIGHT, rgn); DisposeRgn(rgn); --- main.c Tue Dec 28 22:03:18 2021 +++ main.c Sat Jan 1 20:20:23 2022 @@ -84,7 +84,7 @@ main(void) db = db_open(NULL); if (!db) - ExitToShell(); + panic("Failed to open database"); if (db->config.telnet_port) telnet_init();