diff --git a/kpmpgsmkii.pro b/kpmpgsmkii.pro index 05bbb6a..b8c4992 100644 --- a/kpmpgsmkii.pro +++ b/kpmpgsmkii.pro @@ -23,3 +23,6 @@ SUBDIRS = \ # font \ # client \ server + +HEADERS += \ + network.h diff --git a/server/server.pro b/server/server.pro index 04d3d1c..f34b7c8 100644 --- a/server/server.pro +++ b/server/server.pro @@ -36,11 +36,13 @@ HEADERS = \ $$SHARED/thirdparty/memwatch/memwatch.h \ $$SHARED/thirdparty/blowfish-api/blowfish.h \ $$SHARED/thirdparty/ini/src/ini.h \ + $$SHARED/thirdparty/enet/include/enet.h \ $$SHARED/array.h \ $$SHARED/log.h \ $$SHARED/memory.h \ $$SHARED/util.h \ src/database.h \ + src/network.h \ src/os.h SOURCES = \ @@ -52,7 +54,8 @@ SOURCES = \ $$SHARED/memory.c \ $$SHARED/util.c \ src/database.c \ - src/main.c + src/main.c \ + src/network.c LIBS = \ -L/usr/lib/x86_64-linux-gnu/ \ diff --git a/server/src/database.c b/server/src/database.c index 8826aae..75c76e8 100644 --- a/server/src/database.c +++ b/server/src/database.c @@ -33,8 +33,11 @@ static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER; uint8_t dbConnect(char *host, uint16_t port, char *database, char *user, char *password) { + uint8_t reconnect = 1; + if (_sql == NULL) { _sql = mysql_init(NULL); + mysql_options(_sql, MYSQL_OPT_RECONNECT, (const void *)&reconnect); if (mysql_real_connect(_sql, host, user, password, database, port, NULL, 0) == NULL) { logWrite("dbConnect: %s\n", mysql_error(_sql)); return FAIL; diff --git a/server/src/main.c b/server/src/main.c index c32aa32..d9d77f0 100644 --- a/server/src/main.c +++ b/server/src/main.c @@ -18,37 +18,38 @@ */ +#include +#include + #include "os.h" #include "util.h" -#include "stddclmr.h" -#include "thirdparty/ini/src/ini.h" -#include "database.h" #include "array.h" +#include "network.h" +#include "database.h" +#include "stddclmr.h" + +#include "thirdparty/ini/src/ini.h" -typedef struct HashValueS { - char *string; - int32_t integer; -} HashValueT; +// "Config" items come from the INI file. "Settings" are from the database. -typedef struct HashMapS { - char *key; - HashValueT value; -} HashMapT; +static char *_configServer = NULL; +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 char *_configServer = NULL; -static uint16_t _configPort = 0; -static char *_configDatabase = NULL; -static char *_configUser = NULL; -static char *_configPassword = NULL; - -static HashMapT *_settings = NULL; - - -static void configRead(char *file); -static void configWrite(char *file); - +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; @@ -102,52 +103,244 @@ 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 }; + + while (_running) { + while (enet_host_service(server, &event, 1) > 0) { + switch (event.type) { + case ENET_EVENT_TYPE_NONE: + break; + + case ENET_EVENT_TYPE_CONNECT: + break; + + case ENET_EVENT_TYPE_RECEIVE: + enet_packet_destroy(event.packet); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + break; + + case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: + break; + } + } + } + + 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; - HashValueT tempValue; + char *configFile = NULL; + uint32_t settingsMaxClients = 0; + uint32_t settingsPortNumber = 0; + uint32_t settingsClientVersion = 0; + ENetHost *server = NULL; + ENetAddress address = { 0 }; + 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; memoryStartup(argv[0]); logOpenByHandle(memoryLogHandleGet()); + logWrite("%s", "Kangaroo Punch MultiPlayer DOS Game Server Mark II\n"); + logWrite("%s", "Copyright (C) 2020-2021 Scott Duensing scott@kangaroopunch.com\n\n"); configFile = utilAppNameWithNewExtensionGet(argv[0], "ini"); configRead(configFile); - // 0 1 2 3 4 5 6 7 8 - // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 - logWrite("%s", "Kangaroo Punch MultiPlayer DOS Game Server Mark II\n"); - logWrite("%s", "Copyright (C) 2020-2021 Scott Duensing scott@kangaroopunch.com\n\n"); - - if (dbConnect(_configServer, _configPort, _configDatabase, _configUser, _configPassword)) { - tempValue.string = NULL; - if (dbSettingsValueGet("maxClients", &tempValue.integer)) shput(_settings, "maxClients", tempValue); - if (dbSettingsValueGet("portNumber", &tempValue.integer)) shput(_settings, "portNumber", tempValue); - dbDisconnect(); - } else { - logWrite("Unable to connect to database.\n"); + if (!dbConnect(_configServer, _configPort, _configDatabase, _configUser, _configPassword)) { + utilDie("Unable to connect to database.\n"); } - // Show hashmap contents. - for (size_t i=0; i"); + // 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(); - // Free hashmap storage. - for (size_t i=0; i. + * + */ + + +#define ENET_IMPLEMENTATION +#include "network.h" diff --git a/server/src/network.h b/server/src/network.h new file mode 100644 index 0000000..7bd89f9 --- /dev/null +++ b/server/src/network.h @@ -0,0 +1,29 @@ +/* + * 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 NETWORK_H +#define NETWORK_H + + +#include "os.h" +#include "thirdparty/enet/include/enet.h" + + +#endif // NETWORK_H diff --git a/server/src/os.h b/server/src/os.h index c86080a..80d9332 100644 --- a/server/src/os.h +++ b/server/src/os.h @@ -29,6 +29,7 @@ #include #include #include +#include // Should be after system headers in this file. #define MEMORY_CHECK_ENABLED diff --git a/shared/array.c b/shared/array.c index 24ab51e..86b309d 100644 --- a/shared/array.c +++ b/shared/array.c @@ -22,3 +22,28 @@ #define STB_DS_IMPLEMENTATION #include "thirdparty/stb_ds.h" + + +void arrayHashMapDump(HashMapT *hashMap) { + // Show hashmap contents. + for (size_t i=0; imalloc != NULL || inits->free != NULL) { - if (inits->malloc == NULL || inits->free == NULL) { + if (inits->emalloc != NULL || inits->efree != NULL) { + if (inits->emalloc == NULL || inits->efree == NULL) { return -1; } - callbacks.malloc = inits->malloc; - callbacks.free = inits->free; + callbacks.emalloc = inits->emalloc; + callbacks.efree = inits->efree; } if (inits->no_memory != NULL) { @@ -1273,7 +1273,7 @@ extern "C" { } void * enet_malloc(size_t size) { - void *memory = callbacks.malloc(size); + void *memory = callbacks.emalloc(size); if (memory == NULL) { callbacks.no_memory(); @@ -1283,7 +1283,7 @@ extern "C" { } void enet_free(void *memory) { - callbacks.free(memory); + callbacks.efree(memory); } // =======================================================================// diff --git a/shared/util.c b/shared/util.c index 3d1a002..30b7449 100644 --- a/shared/util.c +++ b/shared/util.c @@ -49,3 +49,16 @@ char *utilAppNameWithNewExtensionGet(char *appName, char *extension) { return newName; } + + +void utilDie(const char *why, ...) { + va_list args; + char msg[2048]; + + va_start(args, why); + vsprintf(msg, why, args); + va_end(args); + + logWrite("DIE: %s", msg); + exit(1); +} diff --git a/shared/util.h b/shared/util.h index 78ad2e6..2e1be8b 100644 --- a/shared/util.h +++ b/shared/util.h @@ -26,6 +26,7 @@ char *utilAppNameWithNewExtensionGet(char *appName, char *extension); +void utilDie(const char *why, ...); #endif // UTIL_H