From efaf9f886ff9be83117d5affaf2ef4f94b106a95 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Mon, 22 Nov 2021 21:20:26 -0600 Subject: [PATCH] Main server loop written. Actual client data next! --- server/server.pro | 8 +- server/src/client.c | 32 +++++++ server/src/client.h | 41 ++++++++ server/src/console.c | 222 +++++++++++++++++++++++++++++++++++++++++++ server/src/console.h | 32 +++++++ server/src/main.c | 216 +++++++++++------------------------------ 6 files changed, 391 insertions(+), 160 deletions(-) create mode 100644 server/src/client.c create mode 100644 server/src/client.h create mode 100644 server/src/console.c create mode 100644 server/src/console.h diff --git a/server/server.pro b/server/server.pro index f34b7c8..1e5cc4a 100644 --- a/server/server.pro +++ b/server/server.pro @@ -19,7 +19,9 @@ TEMPLATE = app CONFIG -= qt -CONFIG += c11 +CONFIG += \ + console \ + c11 DESTDIR = $$OUT_PWD/bin SHARED = $$PWD/../shared @@ -41,6 +43,8 @@ HEADERS = \ $$SHARED/log.h \ $$SHARED/memory.h \ $$SHARED/util.h \ + src/client.h \ + src/console.h \ src/database.h \ src/network.h \ src/os.h @@ -53,6 +57,8 @@ SOURCES = \ $$SHARED/log.c \ $$SHARED/memory.c \ $$SHARED/util.c \ + src/client.c \ + src/console.c \ src/database.c \ src/main.c \ src/network.c diff --git a/server/src/client.c b/server/src/client.c new file mode 100644 index 0000000..1c63b26 --- /dev/null +++ b/server/src/client.c @@ -0,0 +1,32 @@ +/* + * Kangaroo Punch MultiPlayer Game Server Mark II + * Copyright (C) 2020-2021 Scott Duensing + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#include + +#include "client.h" +#include "network.h" + + +void *clientThread(void *data) { + + ClientThreadT *client = (ClientThreadT *)data; + + pthread_exit(NULL); +} diff --git a/server/src/client.h b/server/src/client.h new file mode 100644 index 0000000..8d32b99 --- /dev/null +++ b/server/src/client.h @@ -0,0 +1,41 @@ +/* + * Kangaroo Punch MultiPlayer Game Server Mark II + * Copyright (C) 2020-2021 Scott Duensing + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#ifndef CLIENT_H +#define CLIENT_H + + +#include "os.h" +#include "network.h" + + +typedef struct ClientThreadS { + uint64_t threadIndex; + pthread_t threadHandle; + pthread_attr_t threadAttributes; + ENetPeer *peer; + uint8_t running; +} ClientThreadT; + + +void *clientThread(void *data); + + +#endif // CLIENT_H diff --git a/server/src/console.c b/server/src/console.c new file mode 100644 index 0000000..3e7a4f8 --- /dev/null +++ b/server/src/console.c @@ -0,0 +1,222 @@ +/* + * Kangaroo Punch MultiPlayer Game Server Mark II + * Copyright (C) 2020-2021 Scott Duensing + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#include +#include +#include + +#include "console.h" +#include "array.h" +#include "util.h" + + +static uint8_t _running = 1; +static struct termios _termios = { 0 }; +static char **_consoleMessageQueue = NULL; +static pthread_mutex_t _messageQueueMutex = PTHREAD_MUTEX_INITIALIZER; + +static uint8_t getch(void); +static uint8_t kbhit(void); +static void sendToConsole(const char *message, ...); +static void terminalModeConioSet(void); +static void terminalModeOriginalSet(void); + + +void consoleMessageQueue(const char *message, ...) { + va_list args = { 0 }; + char buffer[2048] = { 0 }; + char *newMessage = NULL; + + va_start(args, message); + vsprintf(buffer, message, args); + va_end(args); + + newMessage = strdup(buffer); + + pthread_mutex_lock(&_messageQueueMutex); + arrput(_consoleMessageQueue, newMessage); + pthread_mutex_unlock(&_messageQueueMutex); +} + + +void consoleRun(void) { + uint8_t commandEntering = 0; + char command[256] = { 0 }; + uint16_t commandIndex = 0; + char c = 0; + uint8_t commandOk = 0; + struct timespec remaining = { 0, 0 }; + struct timespec sleepTime = { 0, 1000000000/4 }; + + if (pthread_mutex_init(&_messageQueueMutex, NULL)) utilDie("Unable to create console message queue mutex.\n"); + + terminalModeConioSet(); + while (_running) { + + if (kbhit()) { + + // Is this a new command? + if (!commandEntering) { + // Show prompt. + printf(">"); + // Command is being entered. + commandEntering = 1; + } + + // Read key. + c = getch(); + + switch (c) { + // Backspace + case 8: + case 127: + break; + + // ESC + case 27: + // Abort command entry. + printf("\n\r"); + commandIndex = 0; + command[0] = 0; + commandEntering = 0; + break; + + // ENTER + case 13: + printf("\n\r"); + logWriteToFileOnly(">%s\n", command); + commandOk = 0; + if (!strcasecmp(command, "HELP") || !strcasecmp(command, "?")) { + sendToConsole("HELP or ? - This message.\n"); + sendToConsole("SHUTDOWN - Stop the server.\n"); + commandOk = 1; + } + if (!strcasecmp(command, "SHUTDOWN")) { + _running = 0; + commandOk = 1; + } + // Did we grok it? + if (!commandOk) { + sendToConsole("Unknown command! Type HELP (or ?) for help.\n\r"); + } + commandIndex = 0; + command[0] = 0; + commandEntering = 0; + break; + + default: + // Add to command. + command[commandIndex++] = c; + command[commandIndex] = 0; + printf("%c", c); + // Overflow? + if (commandIndex == 255) { + printf("\n\r"); + commandIndex = 0; + command[0] = 0; + commandEntering = 0; + } + break; + } + + fflush(stdout); + } + + // Only do this if we're not typing on the console. + if (!commandEntering) { + // Don't eat all the CPU. + nanosleep(&remaining, &sleepTime); + + // Messages to display? + pthread_mutex_lock(&_messageQueueMutex); + if (arrlenu(_consoleMessageQueue) > 0) { + sendToConsole("%s", _consoleMessageQueue[0]); + arrdel(_consoleMessageQueue, 0); + } + pthread_mutex_unlock(&_messageQueueMutex); + } + } + terminalModeOriginalSet(); + + pthread_mutex_destroy(&_messageQueueMutex); +} + + +static uint8_t getch(void) { + int r = 0; + uint8_t c = 0; + + if ((r = read(0, &c, sizeof(c))) < 0) { + return r; + } else { + return c; + } +} + + +static uint8_t kbhit(void) { + struct timeval tv = { 0L, 0L }; + fd_set fds = { 0 }; + + FD_ZERO(&fds); + FD_SET(0, &fds); + + return select(1, &fds, NULL, NULL, &tv) > 0; +} + + +static void sendToConsole(const char *message, ...) { + va_list args = { 0 }; + char buffer[2048] = { 0 }; + + va_start(args, message); + + vsprintf(buffer, message, args); + printf("%s", buffer); + if (buffer[strlen(buffer) - 1] == '\n') { + // Console needs a CR with its LF. + printf("\r"); + } + fflush(stdout); + + logWriteToFileOnly("%s", buffer); + + va_end(args); +} + + +static void terminalModeConioSet(void) { + struct termios new_termios = { 0 }; + + // https://stackoverflow.com/questions/448944/c-non-blocking-keyboard-input + + // Take two copies - one for now, one for later. + tcgetattr(0, &_termios); + memcpy(&new_termios, &_termios, sizeof(new_termios)); + cfmakeraw(&new_termios); + tcsetattr(0, TCSANOW, &new_termios); +} + + +static void terminalModeOriginalSet(void) { + tcsetattr(0, TCSANOW, &_termios); +} + + diff --git a/server/src/console.h b/server/src/console.h new file mode 100644 index 0000000..5bb4579 --- /dev/null +++ b/server/src/console.h @@ -0,0 +1,32 @@ +/* + * Kangaroo Punch MultiPlayer Game Server Mark II + * Copyright (C) 2020-2021 Scott Duensing + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#ifndef CONSOLE_H +#define CONSOLE_H + + +#include "os.h" + + +void consoleMessageQueue(const char *message, ...); +void consoleRun(void); + + +#endif // CONSOLE_H diff --git a/server/src/main.c b/server/src/main.c index d9d77f0..97d1d8b 100644 --- a/server/src/main.c +++ b/server/src/main.c @@ -18,12 +18,11 @@ */ -#include -#include - #include "os.h" #include "util.h" #include "array.h" +#include "client.h" +#include "console.h" #include "network.h" #include "database.h" #include "stddclmr.h" @@ -38,18 +37,14 @@ static uint16_t _configPort = 0; static char *_configDatabase = NULL; static char *_configUser = NULL; static char *_configPassword = NULL; + static uint8_t _running = 1; -static struct termios _termios = { 0 }; static void configRead(char *file); static void configWrite(char *file); -static uint8_t getch(void); -static uint8_t kbhit(void); -static void sendToConsole(const char *message, ...); static void *serverThread(void *data); -static void terminalModeConioSet(void); -static void terminalModeOriginalSet(void); + static void configRead(char *file) { ini_t *ini = NULL; @@ -103,52 +98,15 @@ static void configWrite(char *file) { } -static uint8_t getch(void) { - int r = 0; - uint8_t c = 0; - - if ((r = read(0, &c, sizeof(c))) < 0) { - return r; - } else { - return c; - } -} - - -static uint8_t kbhit(void) { - struct timeval tv = { 0L, 0L }; - fd_set fds = { 0 }; - - FD_ZERO(&fds); - FD_SET(0, &fds); - - return select(1, &fds, NULL, NULL, &tv) > 0; -} - - -static void sendToConsole(const char *message, ...) { - va_list args; - char buffer[2048]; - - va_start(args, message); - - vsprintf(buffer, message, args); - printf("%s", buffer); - if (buffer[strlen(buffer) - 1] == '\n') { - // Console needs a CR with its LF. - printf("\r"); - } - fflush(stdout); - - logWriteToFileOnly("%s", buffer); - - va_end(args); -} - - static void *serverThread(void *data) { - ENetHost *server = (ENetHost *)data; - ENetEvent event = { 0 }; + ENetHost *server = (ENetHost *)data; + ENetEvent event = { 0 }; + ClientThreadT **clientList = NULL; + ClientThreadT *client = NULL; + uint64_t nextIndex = 0; + size_t i = 0; + void *status = NULL; + char buffer[2048] = { 0 }; while (_running) { while (enet_host_service(server, &event, 1) > 0) { @@ -157,6 +115,23 @@ static void *serverThread(void *data) { break; case ENET_EVENT_TYPE_CONNECT: + // Create new client. + NEW(ClientThreadT, client); + if (!client) utilDie("Unable to allocate new client.\n"); + client->threadIndex = nextIndex++; + client->peer = event.peer; + client->running = 1; + // Keep our index in the peer data for later. + event.peer->data = (void *)client->threadIndex; + // Make new thread for this client. + if (pthread_attr_init(&client->threadAttributes) != 0) utilDie("Unable to create client thread attributes.\n"); + pthread_attr_setdetachstate(&client->threadAttributes, PTHREAD_CREATE_JOINABLE); + if (pthread_create(&client->threadHandle, &client->threadAttributes, clientThread, (void *)client) != 0) utilDie("Unable to start client thread.\n"); + // Add to our client list. + arrput(clientList, client); + // Tell the console. + enet_address_get_host_ip(&event.peer->address, buffer, 2047); + consoleMessageQueue("%ld: %s connected.\n", client->threadIndex, buffer); break; case ENET_EVENT_TYPE_RECEIVE: @@ -164,36 +139,40 @@ static void *serverThread(void *data) { break; case ENET_EVENT_TYPE_DISCONNECT: - break; - case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: + // Find our client. + for (i=0; idata == clientList[i]->threadIndex) { + // Stop client processing. + clientList[i]->running = 0; + pthread_join(clientList[i]->threadHandle, &status); + // Tell the console. + enet_address_get_host_ip(&event.peer->address, buffer, 2047); + consoleMessageQueue("%ld: %s disconnected.\n", clientList[i]->threadIndex, buffer); + // Take it out of the client list. + arrdel(clientList, i); + break; + } + } break; } } } + // Anyone still running, be rude. Nuke 'em. + for (i=0; ipeer); + // Stop client processing. + clientList[i]->running = 0; + pthread_join(clientList[i]->threadHandle, &status); + } + arrfree(clientList); + pthread_exit(NULL); } -static void terminalModeConioSet(void) { - struct termios new_termios = { 0 }; - - // https://stackoverflow.com/questions/448944/c-non-blocking-keyboard-input - - // Take two copies - one for now, one for later. - tcgetattr(0, &_termios); - memcpy(&new_termios, &_termios, sizeof(new_termios)); - cfmakeraw(&new_termios); - tcsetattr(0, TCSANOW, &new_termios); -} - - -static void terminalModeOriginalSet(void) { - tcsetattr(0, TCSANOW, &_termios); -} - - int main(int argc, char *argv[]) { char *configFile = NULL; @@ -205,13 +184,6 @@ int main(int argc, char *argv[]) { pthread_t serverThreadHandle = { 0 }; pthread_attr_t serverThreadAttributes = { 0 }; void *serverThreadStatus = NULL; - uint8_t commandEntering = 0; - char command[256] = { 0 }; - uint16_t commandIndex = 0; - char c = 0; - uint8_t commandOk = 0; - struct timespec remaining = { 0, 0 }; - struct timespec sleepTime = { 0, 1000000000/4 }; (void)argc; @@ -249,85 +221,11 @@ int main(int argc, char *argv[]) { logWrite("Server online.\n"); - // Console. - terminalModeConioSet(); - while (_running) { + // Run Console. + consoleRun(); - if (kbhit()) { - - // Is this a new command? - if (!commandEntering) { - // Show prompt. - printf(">"); - // Command is being entered. - commandEntering = 1; - } - - // Read key. - c = getch(); - - switch (c) { - // Backspace - case 8: - case 127: - break; - - // ESC - case 27: - // Abort command entry. - printf("\n\r"); - commandIndex = 0; - command[0] = 0; - commandEntering = 0; - break; - - // ENTER - case 13: - printf("\n\r"); - logWriteToFileOnly(">%s\n", command); - commandOk = 0; - if (!strcasecmp(command, "HELP") || !strcasecmp(command, "?")) { - sendToConsole("HELP or ? - This message.\n"); - sendToConsole("SHUTDOWN - Stop the server.\n"); - commandOk = 1; - } - if (!strcasecmp(command, "SHUTDOWN")) { - _running = 0; - commandOk = 1; - } - // Did we grok it? - if (!commandOk) { - sendToConsole("Unknown command! Type HELP (or ?) for help.\n\r"); - } - commandIndex = 0; - command[0] = 0; - commandEntering = 0; - break; - - default: - // Add to command. - command[commandIndex++] = c; - command[commandIndex] = 0; - printf("%c", c); - // Overflow? - if (commandIndex == 255) { - printf("\n\r"); - commandIndex = 0; - command[0] = 0; - commandEntering = 0; - } - break; - } - - fflush(stdout); - } - - // Only do this if we're not typing on the console. - if (!commandEntering) { - nanosleep(&remaining, &sleepTime); - } - } - terminalModeOriginalSet(); + // Console closed. Stop server. + _running = 0; // Wait for all running threads to shut down. logWrite("Shutting down.\n");