Main server loop written. Actual client data next!
This commit is contained in:
parent
f85322c9d7
commit
efaf9f886f
6 changed files with 391 additions and 160 deletions
|
@ -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
|
||||
|
|
32
server/src/client.c
Normal file
32
server/src/client.c
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "client.h"
|
||||
#include "network.h"
|
||||
|
||||
|
||||
void *clientThread(void *data) {
|
||||
|
||||
ClientThreadT *client = (ClientThreadT *)data;
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
41
server/src/client.h
Normal file
41
server/src/client.h
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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
|
222
server/src/console.c
Normal file
222
server/src/console.c
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <termios.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#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("<CANCEL>\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("<OVERFLOW>\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);
|
||||
}
|
||||
|
||||
|
32
server/src/console.h
Normal file
32
server/src/console.h
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CONSOLE_H
|
||||
#define CONSOLE_H
|
||||
|
||||
|
||||
#include "os.h"
|
||||
|
||||
|
||||
void consoleMessageQueue(const char *message, ...);
|
||||
void consoleRun(void);
|
||||
|
||||
|
||||
#endif // CONSOLE_H
|
|
@ -18,12 +18,11 @@
|
|||
*/
|
||||
|
||||
|
||||
#include <termios.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#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; i<arrlenu(clientList); i++) {
|
||||
if ((uint64_t)event.peer->data == 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; i<arrlenu(clientList); i++) {
|
||||
// Hang up.
|
||||
enet_peer_reset(clientList[i]->peer);
|
||||
// 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("<CANCEL>\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("<OVERFLOW>\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");
|
||||
|
|
Loading…
Add table
Reference in a new issue