diff --git a/.gitignore b/.gitignore
index f81878b..d93ba10 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@ bin/
obj/
retired/
test/
+doors/
*/out/
diff --git a/LICENSE b/LICENSE
index 0e77ed4..284f6bb 100644
--- a/LICENSE
+++ b/LICENSE
@@ -19,6 +19,7 @@ along with this program. If not, see .
Licenses Used By:
+
Client
======
@@ -42,16 +43,16 @@ MemWatch
http://www.linkdata.se/sourcecode/memwatch/
GPL2
-minicoro
---------
-https://github.com/edubart/minicoro
-Public Domain or MIT No Attribution
-
SDL2
----
https://www.libsdl.org/
BSD 3-Clause
+SDL2_image
+----------
+https://www.libsdl.org/
+BSD 3-Clause
+
SHA256
------
https://github.com/ilvn/SHA256
@@ -78,6 +79,7 @@ https://github.com/bonybrown/tiny-AES128-C
Unlicense
+
Font Converter
==============
@@ -92,6 +94,7 @@ https://github.com/nothings/stb
Public Domain
+
Server
======
diff --git a/client/assets/menu.xcf b/client/assets/menu.xcf
index 74ce554..88d575d 100644
--- a/client/assets/menu.xcf
+++ b/client/assets/menu.xcf
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a96e8aa41d87c8a7aed6e5ba1236bd8285742fdfdca256a1333bd8b29ce9583f
-size 21301
+oid sha256:58e69ccb53b34298d69737cb3160f5bb7fafaa87747c1474b3331a6a8e1fe862
+size 25417
diff --git a/client/client.pro b/client/client.pro
index 1c9bfe4..06d1b94 100644
--- a/client/client.pro
+++ b/client/client.pro
@@ -71,6 +71,7 @@ HEADERS = \
$$SHARED/packets.h \
src/config.h \
$$SHARED/util.h \
+ src/file.h \
src/gui/msgbox.h \
src/gui/timer.h \
src/hangup.h \
@@ -123,6 +124,7 @@ SOURCES = \
$$SHARED/thirdparty/tiny-AES-c/aes.c \
$$SHARED/thirdparty/tiny-AES128-C/pkcs7_padding.c \
$$SHARED/memory.c \
+ src/file.c \
src/gui/msgbox.c \
src/gui/timer.c \
src/hangup.c \
diff --git a/client/src/file.c b/client/src/file.c
new file mode 100644
index 0000000..c7edd04
--- /dev/null
+++ b/client/src/file.c
@@ -0,0 +1,188 @@
+/*
+ * 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 "network.h"
+#include "packet.h"
+
+#include "taglist.h"
+#include "window.h"
+#include "label.h"
+
+#include "file.h"
+
+
+static uint8_t _channel = 0;
+static uint8_t _dialogVisible = 0;
+static int16_t _index = -1;
+static char **_fileList = NULL;
+static FILE *_handle = NULL;
+static fileCallback _done = NULL;
+static WindowT *_winFile = NULL;
+static LabelT *_lblFile = NULL;
+
+
+static void fileCheckNext(void);
+static void fileShowDialog(void);
+static void fileShowError(char *message);
+static void packetHandler(PacketDecodeDataT *packet);
+
+
+void fileCacheCheck(fileCallback callback, char *vpaths[]) {
+ _done = callback;
+ _fileList = vpaths;
+ _index = -1;
+ _dialogVisible = 0;
+ _channel = netChannelGet(packetHandler);
+
+ fileCheckNext();
+}
+
+
+static void fileCheckNext(void) {
+ PacketEncodeDataT encoded = { 0 };
+ char *packetData = NULL;
+ uint16_t length = 0;
+ uint32_t temp = 0;
+ char *shaPointer = NULL;
+ static char *badSHA = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
+
+ _index++;
+
+ if (_fileList[_index] == NULL) {
+ // End of list!
+ if (_dialogVisible) guiDelete(D(_winFile));
+ netChannelRelease(_channel);
+ _done();
+ return;
+ }
+
+ // Check next file.
+ temp = FILE_REQUEST_CHECK;
+ shaPointer = cacheSha256Get(_fileList[_index]);
+ if (!shaPointer) shaPointer = badSHA;
+ packetData = packetContentPack(&length, "iss", temp, shaPointer, _fileList[_index]);
+ logWrite("Checking [%d] [%s] [%s]\n", temp, shaPointer, _fileList[_index]);
+ encoded.control = PACKET_CONTROL_DAT;
+ encoded.packetType = PACKET_TYPE_FILE_REQUEST;
+ encoded.channel = _channel;
+ encoded.encrypt = 0;
+ packetEncode(__packetThreadData, &encoded, packetData, length);
+ packetSend(__packetThreadData, &encoded);
+ DEL(packetData);
+}
+
+
+static void fileShowDialog(void) {
+ TagItemT uiFile[] = {
+ T_START,
+ T_WINDOW, O(_winFile),
+ T_TITLE, P("Updating Cache"),
+ T_WIDTH, 200, T_HEIGHT, 100,
+ T_LABEL, O(_lblFile),
+ T_X, 41, T_Y, 25,
+ T_TITLE, P("Downloading..."),
+ T_LABEL, T_DONE,
+ T_WINDOW, T_DONE,
+ T_END
+ };
+
+ tagListRun(uiFile);
+ _dialogVisible = 1;
+}
+
+
+static void fileShowError(char *message) {
+ // ***TODO*** Handle error. This is fatal.
+ if (_dialogVisible) guiDelete(D(_winFile));
+ netChannelRelease(_channel);
+}
+
+
+static void packetHandler(PacketDecodeDataT *packet) {
+ FileResponseT response = 0;
+ uint32_t fileLength = 0;
+ PacketEncodeDataT encoded = { 0 };
+ char *packetData = NULL;
+ uint16_t length = 0;
+ uint32_t temp = 0;
+
+ logWrite("Got packet %d\n", packet->packetType);
+
+ if (packet->packetType == PACKET_TYPE_FILE_RESPONSE) {
+ // Extract the response type. We get more data later.
+ packetContentUnpack(packet->data, "i", &response);
+ logWrite("Got response %d [%d bytes]\n", response, sizeof(response));
+ switch (response) {
+ case FILE_RESPONSE_UNKNOWN:
+ logWrite("Unknown file transfer response.\n");
+ fileShowError("Unknown file transfer response.");
+ break;
+
+ case FILE_RESPONSE_OKAY:
+ // This file is already up-to-date, move on to next.
+ logWrite("Got FILE_RESPONSE_OKAY\n");
+ fileCheckNext();
+ break;
+
+ case FILE_RESPONSE_SEND:
+ // Get file size.
+ logWrite("Got FILE_RESPONSE_SEND\n");
+ packetContentUnpack(packet->data, "ii", &response, &fileLength);
+ // Receive new file data.
+ if (!_dialogVisible) fileShowDialog();
+ // Are we starting a new file?
+ if (_handle == NULL) {
+ logWrite("Opening [%s]\n", _fileList[_index]);
+ _handle = cacheFOpen(_fileList[_index], "wb");
+ if (!_handle) {
+ fileShowError("Unable to write to cache.");
+ break;
+ }
+ }
+ // Write data to file, skipping first two integers stored in packet.
+ logWrite("Writing %d bytes to [%s]\n", packet->length - 8, _fileList[_index]);
+ fwrite(&packet->data[8], packet->length - 8, 1, _handle);
+ // Is this file complete?
+ if (ftell(_handle) >= fileLength) {
+ logWrite("Closing [%s]\n", _fileList[_index]);
+ // Close this file.
+ cacheFClose(_handle);
+ _handle = NULL;
+ // Next file!
+ fileCheckNext();
+ } else {
+ logWrite("Sending FILE_REQUEST_NEXT\n");
+ // Tell the server we got this bit of data, send the next.
+ temp = FILE_REQUEST_NEXT;
+ packetData = packetContentPack(&length, "i", temp);
+ encoded.control = PACKET_CONTROL_DAT;
+ encoded.packetType = PACKET_TYPE_FILE_REQUEST;
+ encoded.channel = _channel;
+ encoded.encrypt = 0;
+ packetEncode(__packetThreadData, &encoded, packetData, length);
+ packetSend(__packetThreadData, &encoded);
+ DEL(packetData);
+ }
+ break;
+ }
+ }
+
+ packetDecodeDataDestroy(&packet);
+}
diff --git a/client/src/file.h b/client/src/file.h
new file mode 100644
index 0000000..27c66ab
--- /dev/null
+++ b/client/src/file.h
@@ -0,0 +1,34 @@
+/*
+ * 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 FILE_H
+#define FILE_H
+
+
+#include "os.h"
+
+
+typedef void (*fileCallback)(void);
+
+
+void fileCacheCheck(fileCallback callback, char *vpaths[]);
+
+
+#endif // FILE_H
diff --git a/client/src/main.c b/client/src/main.c
index fde7705..071d145 100644
--- a/client/src/main.c
+++ b/client/src/main.c
@@ -292,8 +292,6 @@ static void tableSave(void) {
}
-void menuShow(void);
-
int main(int argc, char *argv[]) {
if (startup(argc, argv)) return 1;
@@ -301,8 +299,7 @@ int main(int argc, char *argv[]) {
// Perform "first run" setup tasks or start the client?
if (hasValidSettings()) {
// We have what we need, start the client.
- //welcomeShow();
- menuShow();
+ welcomeShow();
} else {
// Run the setup.
settingsShow(checkSettings);
diff --git a/client/src/menu.c b/client/src/menu.c
index 40d0b3c..e972c9b 100644
--- a/client/src/menu.c
+++ b/client/src/menu.c
@@ -30,11 +30,13 @@
#include "menu.h"
#include "hangup.h"
#include "welcome.h"
+#include "file.h"
static void btnLogoffClick(WidgetT *widget);
static void btnOptionsClick(WidgetT *widget);
static void btnMsgBoxLogoff(MsgBoxButtonT button);
+static void menuFilesReady(void);
static void picDoorClick(WidgetT *widget);
static void setButtons(uint8_t enabled);
@@ -67,8 +69,6 @@ static void btnMsgBoxLogoff(MsgBoxButtonT button) {
}
-
-
static void btnOptionsClick(WidgetT *widget) {
(void)widget;
// ***TODO***
@@ -76,7 +76,10 @@ static void btnOptionsClick(WidgetT *widget) {
}
-void menuShow(void) {
+static void menuFilesReady(void) {
+ char *dos = strdup(cacheFilenameGet("menu:dosgames.png"));
+ char *door = strdup(cacheFilenameGet("menu:doorgames.png"));
+ char *fiction = strdup(cacheFilenameGet("menu:interactivefiction.png"));
TagItemT uiMenu[] = {
T_START,
@@ -86,18 +89,18 @@ void menuShow(void) {
T_WIDTH, 350, T_HEIGHT, 275,
T_PICTURE, O(_picDOS),
- T_FILENAME, P("dos.png"),
+ T_FILENAME, P(dos),
T_X, 18, T_Y, 18,
T_PICTURE, T_DONE,
T_PICTURE, O(_picDoor),
- T_FILENAME, P("door.png"),
+ T_FILENAME, P(door),
T_X, 18, T_Y, 77,
T_CLICK, P(picDoorClick),
T_PICTURE, T_DONE,
T_PICTURE, O(_picIF),
- T_FILENAME, P("if.png"),
+ T_FILENAME, P(fiction),
T_X, 18, T_Y, 136,
T_PICTURE, T_DONE,
@@ -118,6 +121,21 @@ void menuShow(void) {
};
tagListRun(uiMenu);
+
+ DEL(fiction);
+ DEL(door);
+ DEL(dos);
+}
+
+
+void menuShow(void) {
+ char *fileList[] = {
+ "menu:dosgames.png",
+ "menu:doorgames.png",
+ "menu:interactivefiction.png",
+ NULL
+ };
+ fileCacheCheck(menuFilesReady, fileList);
}
diff --git a/client/src/system/cache.h b/client/src/system/cache.h
index 5410eb8..f6685bb 100644
--- a/client/src/system/cache.h
+++ b/client/src/system/cache.h
@@ -25,7 +25,7 @@
#include "os.h"
-#define CACHE_VIRTUAL_PATH_MAX 512
+#define CACHE_VIRTUAL_PATH_MAX 512 // Should match setting in the server's client/file.h
typedef struct CachePreMakeListS {
diff --git a/kpmpgsmkii.pro b/kpmpgsmkii.pro
index 01169bb..f82183f 100644
--- a/kpmpgsmkii.pro
+++ b/kpmpgsmkii.pro
@@ -21,8 +21,8 @@ TEMPLATE = subdirs
CONFIG *= ORDERED
SUBDIRS = \
- client
-# server
+ client \
+ server
# precache
# font
# primes
diff --git a/server/server.pro b/server/server.pro
index c86a751..3fb3a35 100644
--- a/server/server.pro
+++ b/server/server.pro
@@ -61,7 +61,8 @@ HEADERS = \
src/os.h \
src/rest.h \
src/database.h \
- src/server.h
+ src/server.h \
+ src/settings.h
SOURCES = \
$$SHARED/thirdparty/memwatch/memwatch.c \
@@ -85,7 +86,8 @@ SOURCES = \
src/network.c \
src/rest.c \
src/database.c \
- src/server.c
+ src/server.c \
+ src/settings.c
LIBS = \
-L/usr/lib/x86_64-linux-gnu/ \
diff --git a/server/src/client.h b/server/src/client.h
index df1dbe9..6849667 100644
--- a/server/src/client.h
+++ b/server/src/client.h
@@ -52,6 +52,8 @@ typedef struct ClientThreadS {
void *peer;
// User State Stuff.
uint8_t authenticated;
+ FILE *handle; // ***TODO*** Needs to support more than one file transfer at a time.
+ uint32_t fileSize; // Length of current file being transferred.
} ClientThreadT;
diff --git a/server/src/client/file.c b/server/src/client/file.c
index 49890ac..1ded8f9 100644
--- a/server/src/client/file.c
+++ b/server/src/client/file.c
@@ -19,14 +19,13 @@
#include "database.h"
+#include "settings.h"
#include "file.h"
-static void clientApiFileRequestCacheCheck(ClientThreadT *client, PacketDecodeDataT *data);
-static void clientApiFileRequestClose(ClientThreadT *client, PacketDecodeDataT *data);
-static void clientApiFileRequestOpen(ClientThreadT *client, PacketDecodeDataT *data);
-static void clientApiFileRequestRead(ClientThreadT *client, PacketDecodeDataT *data);
+static void clientApiFileRequestCheck(ClientThreadT *client, PacketDecodeDataT *data);
+static void clientApiFileRequestNext(ClientThreadT *client, PacketDecodeDataT *data);
void clientApiFileRequest(ClientThreadT *client, PacketDecodeDataT *data) {
@@ -36,30 +35,23 @@ void clientApiFileRequest(ClientThreadT *client, PacketDecodeDataT *data) {
uint16_t length = 0;
// Must be logged in to do file operations.
- if (!client->authenticated) {
+ if (client->authenticated) {
// Extract the request type. We get more data later.
packetContentUnpack(data->data, "i", &request);
switch (request) {
- case FILE_REQUEST_CACHE_CHECK:
- clientApiFileRequestCacheCheck(client, data);
+ case FILE_REQUEST_CHECK:
+ clientApiFileRequestCheck(client, data);
break;
- case FILE_REQUEST_OPEN:
- clientApiFileRequestOpen(client, data);
- break;
-
- case FILE_REQUEST_READ:
- clientApiFileRequestRead(client, data);
- break;
-
- case FILE_REQUEST_CLOSE:
- clientApiFileRequestClose(client, data);
+ case FILE_REQUEST_NEXT:
+ clientApiFileRequestNext(client, data);
break;
default:
- // No idea what they want. First value is 0 for fail, 1 for success.
- packetData = packetContentPack(&length, "i", 0);
+ logWrite("Got FILE_REQUEST_UNKNOWN [%d] %d\n\r", request, data->length);
+ // No idea what they want.
+ packetData = packetContentPack(&length, "i", FILE_RESPONSE_UNKNOWN);
// Build packet.
encoded.control = PACKET_CONTROL_DAT;
encoded.packetType = PACKET_TYPE_FILE_RESPONSE;
@@ -76,51 +68,99 @@ void clientApiFileRequest(ClientThreadT *client, PacketDecodeDataT *data) {
}
-static void clientApiFileRequestCacheCheck(ClientThreadT *client, PacketDecodeDataT *data) {
- FileRequestsT request = 0;
- char *path = NULL;
- char *sha256 = NULL;
- PacketEncodeDataT encoded = { 0 };
- char *packetData = NULL;
- uint16_t length = 0;
+static void clientApiFileRequestCheck(ClientThreadT *client, PacketDecodeDataT *data) {
+ FileRequestsT request = 0;
+ char *path = NULL;
+ char *sha256 = NULL;
+ PacketEncodeDataT encoded = { 0 };
+ char *packetData = NULL;
+ uint16_t length = 0;
+ char buffer[1024] = { 0 };
+ char buffer2[1024] = { 0 };
+ uint32_t temp = 0;
+
+ logWrite("Got FILE_REQUEST_CHECK\n\r");
// Extract the request.
- packetContentUnpack(data->data, "i", &request, &path, &sha256);
-
- // Look up the entry and compare SHA.
+ packetContentUnpack(data->data, "iss", &request, &sha256, &path);
+ logWrite("[%s] [%s]\n\r", sha256, path);
+ // Look up the entry and compare SHA256.
+ dbFileSha256Get(path, buffer, FILE_VIRTUAL_PATH_MAX);
+ if (strcasecmp(sha256, buffer) == 0) {
+ logWrite("File is current. Sending FILE_RESPONSE_OKAY.\n\r");
+ // File is already current on client.
+ temp = FILE_RESPONSE_OKAY;
+ packetData = packetContentPack(&length, "i", temp);
+ encoded.control = PACKET_CONTROL_DAT;
+ encoded.packetType = PACKET_TYPE_FILE_RESPONSE;
+ encoded.channel = data->channel;
+ encoded.encrypt = 0;
+ packetEncode(client->packetThreadData, &encoded, packetData, length);
+ packetSend(client->packetThreadData, &encoded);
+ DEL(packetData);
+ } else {
+ logWrite("File needs updated.\n\r");
+ // Get real path.
+ dbFileRealPathGet(path, buffer, FILE_VIRTUAL_PATH_MAX);
+ snprintf(buffer2, 1024, "%s%s", __settingsFile, buffer);
+ // Open file & get file size.
+ client->handle = fopen(buffer2, "rb");
+ if (!client->handle) {
+ // ***TODO*** Handle error
+ }
+ fseek(client->handle, 0, SEEK_END);
+ client->fileSize = ftell(client->handle);
+ fseek(client->handle, 0, SEEK_SET);
+ // Start sending new file to client.
+ packetData = (char *)malloc(PACKET_MAX);
+ if (!packetData) {
+ // ***TODO*** Handle error
+ }
+ logWrite("Size is %d\n\r", client->fileSize);
+ clientApiFileRequestNext(client, data);
+ }
}
-static void clientApiFileRequestClose(ClientThreadT *client, PacketDecodeDataT *data) {
- FileRequestsT request = 0;
+static void clientApiFileRequestNext(ClientThreadT *client, PacketDecodeDataT *data) {
+ uint32_t temp = 0;
PacketEncodeDataT encoded = { 0 };
- char *packetData = NULL;
- uint16_t length = 0;
-
-}
-
-
-static void clientApiFileRequestOpen(ClientThreadT *client, PacketDecodeDataT *data) {
- FileRequestsT request = 0;
- char *path = NULL;
- char *sha256 = NULL;
- PacketEncodeDataT encoded = { 0 };
- char *packetData = NULL;
- uint16_t length = 0;
-
- // Extract the request.
- packetContentUnpack(data->data, "i", &request, &path);
-
- // Look up the entry and fetch SHA.
-
-}
-
-
-static void clientApiFileRequestRead(ClientThreadT *client, PacketDecodeDataT *data) {
- FileRequestsT request = 0;
- PacketEncodeDataT encoded = { 0 };
- char *packetData = NULL;
uint16_t length = 0;
+ uint16_t dataSize = 0;
+ char packetData[PACKET_MAX];
+ // Do we have an open file?
+ if (client->handle) {
+ logWrite("Sending FILE_RESPONSE_SEND.\n\r");
+ // Add response type.
+ temp = FILE_RESPONSE_SEND;
+ memcpy(packetData, &temp, sizeof(int32_t));
+
+ // Add file length.
+ memcpy(&packetData[4], &client->fileSize, sizeof(int32_t));
+
+ // Add file data.
+ dataSize = client->fileSize - ftell(client->handle);
+ if (dataSize > PACKET_MAX - 8) {
+ // File is larger than a packet size.
+ dataSize = PACKET_MAX - 8; // 8 for two integers of response data.
+ fread(&packetData[8], dataSize, 1, client->handle);
+ } else {
+ // File remains will fit in this packet.
+ fread(&packetData[8], dataSize, 1, client->handle);
+ fclose(client->handle);
+ client->handle = NULL;
+ }
+ logWrite("Sending %d bytes\n\r", dataSize);
+
+ // Build packet.
+ encoded.control = PACKET_CONTROL_DAT;
+ encoded.packetType = PACKET_TYPE_FILE_RESPONSE;
+ encoded.channel = data->channel;
+ encoded.encrypt = 0;
+ packetEncode(client->packetThreadData, &encoded, packetData, length);
+ // Send it.
+ packetSend(client->packetThreadData, &encoded);
+ }
}
diff --git a/server/src/client/file.h b/server/src/client/file.h
index 1fe1d76..fd73c1a 100644
--- a/server/src/client/file.h
+++ b/server/src/client/file.h
@@ -26,6 +26,9 @@
#include "client.h"
+#define FILE_VIRTUAL_PATH_MAX 512 // Should match setting in the client's cache.h
+
+
void clientApiFileRequest(ClientThreadT *client, PacketDecodeDataT *data);
diff --git a/server/src/database.c b/server/src/database.c
index d50e807..bc9bd79 100644
--- a/server/src/database.c
+++ b/server/src/database.c
@@ -65,6 +65,102 @@ uint8_t dbDisconnect(void) {
}
+uint8_t dbFileRealPathGet(char *vpath, char *value, uint32_t max) {
+ char statement[STATEMENT_MAX];
+ char *p = statement;
+ MYSQL_RES *result = NULL;
+ MYSQL_ROW row;
+ int count;
+
+ pthread_mutex_lock(&_mutex);
+
+ if (!_sql) {
+ pthread_mutex_unlock(&_mutex);
+ return FAIL;
+ }
+
+ p += sprintf(p, "SELECT realPath FROM files WHERE virtualPath='");
+ p += mysql_real_escape_string(_sql, p, vpath, strlen(vpath));
+ p += sprintf(p, "'");
+ if (mysql_real_query(_sql, statement, p - statement) != 0) {
+ logWrite("dbFileRealPathGet: %s\n", mysql_error(_sql));
+ pthread_mutex_unlock(&_mutex);
+ return FAIL;
+ }
+
+ result = mysql_store_result(_sql);
+ count = mysql_num_rows(result);
+ if (count != 1) {
+ logWrite("dbFileRealPathGet: Wrong number of rows returned: %d.\n", count);
+ mysql_free_result(result);
+ pthread_mutex_unlock(&_mutex);
+ return FAIL;
+ }
+
+ if ((row = mysql_fetch_row(result)) == NULL) {
+ logWrite("dbFileRealPathGet: %s\n", mysql_error(_sql));
+ pthread_mutex_unlock(&_mutex);
+ return FAIL;
+ }
+
+ strncpy(value, row[0], max);
+
+ mysql_free_result(result);
+
+ pthread_mutex_unlock(&_mutex);
+
+ return SUCCESS;
+}
+
+
+uint8_t dbFileSha256Get(char *vpath, char *value, uint32_t max) {
+ char statement[STATEMENT_MAX];
+ char *p = statement;
+ MYSQL_RES *result = NULL;
+ MYSQL_ROW row;
+ int count;
+
+ pthread_mutex_lock(&_mutex);
+
+ if (!_sql) {
+ pthread_mutex_unlock(&_mutex);
+ return FAIL;
+ }
+
+ p += sprintf(p, "SELECT sha256 FROM files WHERE virtualPath='");
+ p += mysql_real_escape_string(_sql, p, vpath, strlen(vpath));
+ p += sprintf(p, "'");
+ if (mysql_real_query(_sql, statement, p - statement) != 0) {
+ logWrite("dbFileSha256Get: %s\n", mysql_error(_sql));
+ pthread_mutex_unlock(&_mutex);
+ return FAIL;
+ }
+
+ result = mysql_store_result(_sql);
+ count = mysql_num_rows(result);
+ if (count != 1) {
+ logWrite("dbFileSha256Get: Wrong number of rows returned: %d.\n", count);
+ mysql_free_result(result);
+ pthread_mutex_unlock(&_mutex);
+ return FAIL;
+ }
+
+ if ((row = mysql_fetch_row(result)) == NULL) {
+ logWrite("dbFileSha256Get: %s\n", mysql_error(_sql));
+ pthread_mutex_unlock(&_mutex);
+ return FAIL;
+ }
+
+ strncpy(value, row[0], max);
+
+ mysql_free_result(result);
+
+ pthread_mutex_unlock(&_mutex);
+
+ return SUCCESS;
+}
+
+
uint8_t dbSettingsStringGet(char *host, char *key, char *value, uint32_t max) {
char statement[STATEMENT_MAX];
char *p = statement;
diff --git a/server/src/database.h b/server/src/database.h
index 80a74a7..ebb45fb 100644
--- a/server/src/database.h
+++ b/server/src/database.h
@@ -36,6 +36,8 @@ typedef struct DBTableS {
uint8_t dbConnect(char *host, uint16_t port, char *database, char *user, char *password);
uint8_t dbDisconnect(void);
+uint8_t dbFileRealPathGet(char *vpath, char *value, uint32_t max);
+uint8_t dbFileSha256Get(char *vpath, char *value, uint32_t max);
uint8_t dbSettingsStringGet(char *host, char *key, char *value, uint32_t max);
uint8_t dbSettingsValueGet(char *host, char *key, int32_t *value);
uint8_t dbTableGet(char *which, DBTableT ***table);
diff --git a/server/src/main.c b/server/src/main.c
index 508d6cf..b398f2a 100644
--- a/server/src/main.c
+++ b/server/src/main.c
@@ -24,6 +24,7 @@
#include "server.h"
#include "rest.h"
#include "database.h"
+#include "settings.h"
#include "thirdparty/ini/src/ini.h"
@@ -102,13 +103,6 @@ int main(int argc, char *argv[]) {
char *configFile = NULL;
char hostname[256] = { 0 };
- char settingsFile[DB_CONFIG_ITEM_SIZE] = { 0 };
- char settingsRest[DB_CONFIG_ITEM_SIZE] = { 0 };
- char settingsUser[DB_CONFIG_ITEM_SIZE] = { 0 };
- char settingsPass[DB_CONFIG_ITEM_SIZE] = { 0 };
- int64_t settingsMaxClients = 0;
- int64_t settingsPortNumber = 0;
- int64_t settingsClientVersion = 0;
(void)argc;
@@ -130,22 +124,22 @@ int main(int argc, char *argv[]) {
}
// Fetch settings needed to start server.
- if (!dbSettingsValueGet(hostname, "maxClients", (int32_t *)&settingsMaxClients)) utilDie("Unable to load maxClients.\n");
- if (!dbSettingsValueGet(hostname, "portNumber", (int32_t *)&settingsPortNumber)) utilDie("Unable to load portNumber.\n");
- if (!dbSettingsValueGet(hostname, "clientVersion", (int32_t *)&settingsClientVersion)) utilDie("Unable to load clientVersion.\n");
- if (!dbSettingsStringGet(hostname, "fileLocation", settingsFile, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load file location.\n");
- if (!dbSettingsStringGet(hostname, "restEndpoint", settingsRest, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load REST URL.\n");
- if (!dbSettingsStringGet(hostname, "restUser", settingsUser, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load REST user.\n");
- if (!dbSettingsStringGet(hostname, "restPassword", settingsPass, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load REST password.\n");
+ if (!dbSettingsValueGet(hostname, "maxClients", (int32_t *)&__settingsMaxClients)) utilDie("Unable to load maxClients.\n");
+ if (!dbSettingsValueGet(hostname, "portNumber", (int32_t *)&__settingsPortNumber)) utilDie("Unable to load portNumber.\n");
+ if (!dbSettingsValueGet(hostname, "clientVersion", (int32_t *)&__settingsClientVersion)) utilDie("Unable to load clientVersion.\n");
+ if (!dbSettingsStringGet(hostname, "fileLocation", __settingsFile, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load file location.\n");
+ if (!dbSettingsStringGet(hostname, "restEndpoint", __settingsRest, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load REST URL.\n");
+ if (!dbSettingsStringGet(hostname, "restUser", __settingsUser, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load REST user.\n");
+ if (!dbSettingsStringGet(hostname, "restPassword", __settingsPass, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load REST password.\n");
// Start up REST.
- if (!restStartup(settingsRest, settingsUser, settingsPass)) {
+ if (!restStartup(__settingsRest, __settingsUser, __settingsPass)) {
logWrite("Unable to locate REST endpoint. Web site integration disabled.\n");
__restAvailable = 0;
}
clientStartup();
- serverStartup(settingsPortNumber, settingsMaxClients);
+ serverStartup(__settingsPortNumber, __settingsMaxClients);
logWrite("Server online.\n");
// Run Console.
diff --git a/server/src/settings.c b/server/src/settings.c
new file mode 100644
index 0000000..60cec6a
--- /dev/null
+++ b/server/src/settings.c
@@ -0,0 +1,30 @@
+/*
+ * 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 "settings.h"
+
+
+char __settingsFile[DB_CONFIG_ITEM_SIZE] = { 0 };
+char __settingsRest[DB_CONFIG_ITEM_SIZE] = { 0 };
+char __settingsUser[DB_CONFIG_ITEM_SIZE] = { 0 };
+char __settingsPass[DB_CONFIG_ITEM_SIZE] = { 0 };
+int64_t __settingsMaxClients = 0;
+int64_t __settingsPortNumber = 0;
+int64_t __settingsClientVersion = 0;
diff --git a/server/src/settings.h b/server/src/settings.h
new file mode 100644
index 0000000..d60fa9c
--- /dev/null
+++ b/server/src/settings.h
@@ -0,0 +1,37 @@
+/*
+ * 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 SETTINGS_H
+#define SETTINGS_H
+
+#include "os.h"
+#include "database.h"
+
+
+extern char __settingsFile[DB_CONFIG_ITEM_SIZE];
+extern char __settingsRest[DB_CONFIG_ITEM_SIZE];
+extern char __settingsUser[DB_CONFIG_ITEM_SIZE];
+extern char __settingsPass[DB_CONFIG_ITEM_SIZE];
+extern int64_t __settingsMaxClients;
+extern int64_t __settingsPortNumber;
+extern int64_t __settingsClientVersion;
+
+
+#endif // SETTINGS_H
diff --git a/shared/packets.h b/shared/packets.h
index 650156c..83c69e6 100644
--- a/shared/packets.h
+++ b/shared/packets.h
@@ -60,13 +60,25 @@ typedef enum PacketTypeE {
PACKET_TYPE_COUNT
} PacketTypeT;
+/*
+ * Client -> PACKET_TYPE_FILE_REQUEST FILE_REQUEST_CHECK SHA256 VPATH
+ * Server -> PACKET_TYPE_FILE_RESPONSE FILE_RESPONSE_OKAY
+ * - or -
+ * Server -> PACKET_TYPE_FILE_RESPONSE FILE_RESPONSE_SEND (4 bytes length) (data...)
+ * Client -> PACKET_TYPE_FILE_REQUEST FILE_REQUEST_NEXT
+ * (Repeat until received data matches the length.)
+ */
typedef enum FileRequestsE {
FILE_REQUEST_UNKNOWN = 0,
- FILE_REQUEST_CACHE_CHECK,
- FILE_REQUEST_OPEN,
- FILE_REQUEST_READ,
- FILE_REQUEST_CLOSE
+ FILE_REQUEST_CHECK,
+ FILE_REQUEST_NEXT
} FileRequestsT;
+typedef enum FileResponseE {
+ FILE_RESPONSE_UNKNOWN = 0,
+ FILE_RESPONSE_OKAY,
+ FILE_RESPONSE_SEND
+} FileResponseT;
+
#endif // PACKETS_H