diff --git a/client/client.pro b/client/client.pro
index fee1e68..aba09f4 100644
--- a/client/client.pro
+++ b/client/client.pro
@@ -72,6 +72,7 @@ HEADERS = \
src/login.h \
src/network.h \
src/runtime.h \
+ src/signup.h \
src/thirdparty/minicoro/minicoro.h \
src/system/comport.h \
src/settings.h \
@@ -119,7 +120,9 @@ SOURCES = \
src/login.c \
src/network.c \
src/settings.c \
+ src/signup.c \
src/system/comport.c \
+ src/system/os.c \
src/system/surface.c \
src/system/taglist.c \
$$SHARED/util.c \
diff --git a/client/data/ddialing.png b/client/data/ddialing.png
index c9aef6a..3497667 100644
--- a/client/data/ddialing.png
+++ b/client/data/ddialing.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:05b73b4d78f35c17dc9a770b48be09c15737122d40a18ca163d48e98bac06ab5
-size 20525
+oid sha256:88f41ed691c97f8289f600b84f0a58bbe840199e4e9fc0134c88eedd57aa5b2a
+size 12587
diff --git a/client/src/gui/textbox.c b/client/src/gui/textbox.c
index 788f9b7..c1806e1 100644
--- a/client/src/gui/textbox.c
+++ b/client/src/gui/textbox.c
@@ -58,6 +58,7 @@ WidgetT *textboxInit(WidgetT *widget, char *title) {
t->value = (char *)malloc(t->maxLength);
t->caret = 0;
t->offset = 0;
+ t->password = 0;
if (!t->value) return NULL;
t->value[0] = 0;
@@ -76,6 +77,7 @@ static void textboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extende
TextboxT *t = (TextboxT *)widget;
uint16_t x;
+ uint16_t y;
char *temp;
(void)scancode;
@@ -167,20 +169,25 @@ static void textboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extende
default: // Other keys
if (ascii >= 32 && ascii <= 126) {
+ // Remember length so we can zero terminate this after the edit.
+ y = strlen(t->value);
// Insert character, if room.
- if (strlen(t->value) < (size_t)t->maxLength - 1) {
+ if (y < (size_t)t->maxLength - 1) {
// Move existing characters over, if needed.
- if (t->caret + t->offset < strlen(t->value)) {
- for (x=strlen(t->value) + 1; x>t->caret + t->offset; x--) {
+ if (t->caret + t->offset < y) {
+ for (x=y + 1; x>t->caret + t->offset; x--) {
t->value[x] = t->value[x-1];
}
}
// Place typed character at caret.
t->value[t->caret + t->offset] = ascii;
+ y++;
+ // Fix zero termination.
+ t->value[y] = 0;
// Is the caret on the right edge of the box?
if (t->caret == t->visible - 1) {
// Can we move the string offset?
- if (t->offset < strlen(t->value)) {
+ if (t->offset < y) {
t->offset++;
}
} else {
@@ -267,6 +274,7 @@ static void textboxPaint(WidgetT *widget, uint8_t enabled, RectT pos) {
uint16_t textY;
char cursor[2] = { 0xb1, 0 };
+
if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY) || guiFocusGet() == widget) {
labelWidth = (strlen(t->title) * fontWidthGet(_guiFont)) + _guiMetric[METRIC_TEXTBOX_PADDING];
valueWidth = (t->visible * fontWidthGet(_guiFont)) + (_guiMetric[METRIC_TEXTBOX_HORIZONTAL_PADDING] * 2);
@@ -288,6 +296,7 @@ static void textboxPaint(WidgetT *widget, uint8_t enabled, RectT pos) {
// Draw value.
draw = strdup(&t->value[t->offset]);
if (strlen(draw) > t->visible) draw[t->visible] = 0;
+ if (t->password != 0) memset(draw, t->password, strlen(draw));
fontRender(_guiFont, draw, _guiColor[COLOR_TEXTBOX_TEXT], _guiColor[COLOR_TEXTBOX_BACKGROUND], textX, textY);
free(draw);
@@ -302,6 +311,11 @@ static void textboxPaint(WidgetT *widget, uint8_t enabled, RectT pos) {
}
+void textboxPasswordCharacterSet(TextboxT *textbox, char c) {
+ textbox->password = c;
+}
+
+
void textboxTitleSet(TextboxT *textbox, char *title) {
if (textbox->title) free(textbox->title);
textbox->title = strdup(title);
diff --git a/client/src/gui/textbox.h b/client/src/gui/textbox.h
index 1703e46..cac029a 100644
--- a/client/src/gui/textbox.h
+++ b/client/src/gui/textbox.h
@@ -30,6 +30,7 @@ typedef struct TextboxS {
WidgetT base; // Must be first in every widget
char *title;
char *value;
+ char password; // Character to use to mask passwords
uint16_t maxLength; // Maximum length of value + 1
uint16_t caret; // Cursor position in control (caret + offset = cursor positition in value)
uint16_t offset; // First character position to display in control
@@ -40,6 +41,7 @@ typedef struct TextboxS {
WidgetT *textboxInit(WidgetT *widget, char *title);
void textboxLengthMaxSet(TextboxT *textbox, uint16_t length);
TextboxT *textboxNew(uint16_t x, uint16_t y, uint16_t w, char *title);
+void textboxPasswordCharacterSet(TextboxT *textbox, char c);
void textboxTitleSet(TextboxT *textbox, char *title);
char *textboxValueGet(TextboxT *textbox);
void textboxValueSet(TextboxT *textbox, char *value);
diff --git a/client/src/gui/widget.c b/client/src/gui/widget.c
index 4e899a4..ec5f8ed 100644
--- a/client/src/gui/widget.c
+++ b/client/src/gui/widget.c
@@ -28,6 +28,7 @@ uint8_t widgetEnableGet(WidgetT *widget) {
void widgetEnableSet(WidgetT *widget, uint8_t enabled) {
+ // ***TODO*** Should also set children.
// We did "DISABLED" for the flag so that "flags = 0" (the default) would enable widgets.
if (enabled) {
GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DISABLED);
diff --git a/client/src/login.c b/client/src/login.c
index c32e106..7baa984 100644
--- a/client/src/login.c
+++ b/client/src/login.c
@@ -18,13 +18,103 @@
*/
-#include "login.h"
+#include "textbox.h"
+#include "button.h"
+#include "msgbox.h"
+#include "config.h"
+#include "comport.h"
+#include "network.h"
#include "taglist.h"
+#include "timer.h"
#include "task.h"
+#include "login.h"
+#include "signup.h"
+#include "welcome.h"
+
static WindowT *_winLogin = NULL;
+static TextboxT *_txtUser = NULL;
+static TextboxT *_txtPass = NULL;
+static ButtonT *_btnCancel = NULL;
+static ButtonT *_btnSignUp = NULL;
+static ButtonT *_btnLogin = NULL;
+
+
+static void btnCancelClick(WidgetT *widget);
+static void btnSignUpClick(WidgetT *widget);
+static void btnLoginClick(WidgetT *widget);
+static void btnMsgBoxCancel(MsgBoxButtonT button);
+static void setButtons(uint8_t enabled);
+static void taskDisconnect(void *data);
+
+
+static void btnCancelClick(WidgetT *widget) {
+ (void)widget;
+
+ // ***TODO*** This should disable the entire dialog instead of just the buttons. Need to finish the Enable GUI code first.
+
+ setButtons(0);
+ msgBoxTwo("Cancel?", MSGBOX_ICON_QUESTION, "Cancel login?\n \nThis will disconnect you from the server.", "Okay", btnMsgBoxCancel, "Cancel", btnMsgBoxCancel);
+}
+
+
+static void btnSignUpClick(WidgetT *widget) {
+ (void)widget;
+
+ guiDelete(D(_winLogin));
+ taskCreate(taskSignUp, NULL);
+}
+
+
+static void btnLoginClick(WidgetT *widget) {
+ (void)widget;
+
+}
+
+
+static void btnMsgBoxCancel(MsgBoxButtonT button) {
+
+ if (button == MSGBOX_BUTTON_ONE) {
+ guiDelete(D(_winLogin));
+ taskCreate(taskDisconnect, taskWelcome);
+ } else {
+ setButtons(1);
+ }
+}
+
+
+static void setButtons(uint8_t enabled) {
+ widgetEnableSet(W(_btnCancel), enabled);
+ widgetEnableSet(W(_btnSignUp), enabled);
+ widgetEnableSet(W(_btnLogin), enabled);
+}
+
+
+static void taskDisconnect(void *nextTask) {
+ PacketEncodeDataT encoded = { 0 };
+ int16_t timeout = 2;
+
+ // Tell server we're disconnecting.
+ encoded.packetType = PACKET_TYPE_CLIENT_SHUTDOWN;
+ encoded.control = PACKET_CONTROL_DAT;
+ encoded.channel = 0;
+ encoded.encrypt = 0;
+ packetEncode(__packetThreadData, &encoded, NULL, 0);
+ packetSend(__packetThreadData, &encoded);
+
+ // Snooze a bit for the packet to send.
+ while (timeout > 0) {
+ taskYield();
+ if (__timerQuarterSecondTick) timeout--;
+ }
+
+ // Shut down packet processing & COM port.
+ netShutdown();
+ comClose(__configData.serialCom - 1);
+ if (nextTask) taskCreate(taskWelcome, nextTask);
+}
void taskLogin(void *data) {
@@ -35,7 +125,39 @@ void taskLogin(void *data) {
T_START,
T_WINDOW, O(_winLogin),
T_TITLE, P("Login"),
- T_WIDTH, 500, T_HEIGHT, 225,
+ T_WIDTH, 300, T_HEIGHT, 155,
+
+ T_TEXTBOX, O(_txtUser),
+ T_TITLE, P("User Name:"),
+ T_X, 42, T_Y, 10,
+ T_WIDTH, 200,
+ T_LENGTH, 16,
+ T_TEXTBOX, T_DONE,
+
+ T_TEXTBOX, O(_txtPass),
+ T_TITLE, P(" Password:"),
+ T_X, 42, T_Y, 40,
+ T_WIDTH, 200,
+ T_LENGTH, 16,
+ T_MASK, '*',
+ T_TEXTBOX, T_DONE,
+
+ T_BUTTON, O(_btnCancel),
+ T_TITLE, P("Cancel"),
+ T_X, 25, T_Y, 85,
+ T_CLICK, P(btnCancelClick),
+ T_BUTTON, T_DONE,
+ T_BUTTON, O(_btnSignUp),
+ T_TITLE, P("Sign Up"),
+ T_X, 110, T_Y, 85,
+ T_CLICK, P(btnSignUpClick),
+ T_BUTTON, T_DONE,
+ T_BUTTON, O(_btnLogin),
+ T_TITLE, P("Login"),
+ T_X, 199, T_Y, 85,
+ T_CLICK, P(btnLoginClick),
+ T_BUTTON, T_DONE,
+
T_WINDOW, T_DONE,
T_END
};
diff --git a/client/src/main.c b/client/src/main.c
index 3f1b409..3b742d7 100644
--- a/client/src/main.c
+++ b/client/src/main.c
@@ -47,138 +47,19 @@
#include "gui.h"
#include "config.h"
#include "runtime.h"
+#include "comport.h"
#include "welcome.h"
+#include "signup.h"
PacketThreadDataT *__packetThreadData = NULL; // Exported in os.h
RuntimeDataT __runtimeData; // Exported in runtime.h
-static void taskComDebugLoop(void *data);
static void taskGuiEventLoop(void *data);
-#include "comport.h"
-static void taskComDebugLoop(void *data) {
- int32_t r;
- int32_t x;
- char buffer[1024];
-
- (void)data;
-
- // Open COM port.
- logWrite("Opening COM\n");
- r = comOpen(__configData.serialCom - 1, 57600L, 8, 'n', 1, SER_HANDSHAKING_RTSCTS);
- if (r != SER_SUCCESS) {
- logWrite("Unable to open COM port! Please check settings.\n");
- guiStop();
- return;
- }
- // Send a CR to clear anything in the modem.
- logWrite("Clearing modem buffer\n");
- snprintf(buffer, 1023, "%c", 13);
- comWrite(__configData.serialCom - 1, buffer, strlen(buffer));
- // Wait roughly a second for anything.
- comWaitWithTimeout(__configData.serialCom - 1, buffer, 1023, 4, "weExpectNothing");
- // Send actual init
- logWrite("Init modem\n");
- snprintf(buffer, 1023, "%s%c", "AT+SOCK1", 13);
- comWrite(__configData.serialCom - 1, buffer, strlen(buffer));
- // Wait roughly a second for "OK".
- r = comWaitWithTimeout(__configData.serialCom - 1, buffer, 1023, 4, "OK");
- if (r <= 0) {
- comClose(__configData.serialCom - 1);
- logWrite("Modem does not support ENET! Please check settings.\n");
- guiStop();
- return;
- }
- logWrite("Modem OK\n");
- // Flush COM port.
- timerQuarterSecondsWait(4);
- comReceiveBufferFlush(__configData.serialCom - 1);
-
- // Show dialing, dial service.
- logWrite("Dialing\n");
- snprintf(buffer, 1023, "ATDT%s:%d%c", __configData.serverHost, __configData.serverPort, 13);
- comWrite(__configData.serialCom - 1, buffer, strlen(buffer));
- // Wait 7 seconds for welcome banner.
- r = comWaitWithTimeout(__configData.serialCom - 1, buffer, 1023, 4 * 7, "KPMPGSMKII\r");
- if (r <= 0) {
- comClose(__configData.serialCom - 1);
- logWrite("Unable to connect to server! Please check settings or try later.\n");
- guiStop();
- return;
- }
-
- // Connected! Show icon and negotiate session.
- logWrite("Connected\n");
-
- packetEncryptionSetup(__packetThreadData);
-
- PacketEncodeDataT encoded = { 0 };
- PacketDecodeDataT decoded = { 0 };
-
- // Send it 10 times, waiting for a PONG between each.
- for (x=0; x<10; x++) {
- // Send PING.
- logWrite("Sending PING %d\n", x);
- encoded.control = PACKET_CONTROL_DAT;
- encoded.packetType = PACKET_TYPE_PING;
- encoded.channel = 1;
- encoded.encrypt = 0;
- packetEncode(__packetThreadData, &encoded, NULL, 0); // Must encode each packet - no reusing encoded data.
- packetSend(__packetThreadData, &encoded);
- // Wait for PONG.
- while (!guiHasStopped()) {
- r = comRead(__configData.serialCom - 1, buffer, 1);
- if (r == 1) {
- if (packetDecode(__packetThreadData, &decoded, buffer, 1)) {
- if (decoded.packetType == PACKET_TYPE_PONG) {
- logWrite("Received PONG\n");
- break;
- } else {
- logWrite("Unexpected packet type received %d\n", decoded.packetType);
- }
- }
- } else {
- taskYield();
- }
- }
- }
-
- if (guiHasStopped()) return;
-
- // Send LOGIN.
- logWrite("Sending LOGIN\n");
- PacketTypeLoginT loginData;
- strcpy(loginData.user, "Encryption");
- strcpy(loginData.pass, "Works!");
- encoded.control = PACKET_CONTROL_DAT;
- encoded.packetType = PACKET_TYPE_LOGIN;
- encoded.channel = 1;
- encoded.encrypt = 1;
- packetEncode(__packetThreadData, &encoded, (char *)&loginData, sizeof(PacketTypeLoginT)); // Must encode each packet - no reusing encoded data.
- packetSend(__packetThreadData, &encoded);
-
- // Send CLIENT_SHUTDOWN.
- logWrite("Sending CLIENT_SHUTDOWN\n");
- encoded.control = PACKET_CONTROL_DAT;
- encoded.packetType = PACKET_TYPE_CLIENT_SHUTDOWN;
- encoded.channel = 1;
- encoded.encrypt = 0;
- packetEncode(__packetThreadData, &encoded, NULL, 0);
- packetSend(__packetThreadData, &encoded);
-
- logWrite("Sleeping\n");
- timerQuarterSecondsWait(8);
-
- logWrite("COM closed\n");
- comClose(__configData.serialCom - 1);
- guiStop();
-}
-
-
static void taskGuiEventLoop(void *data) {
MouseT *mouse = NULL;
ImageT *pointer = NULL;
@@ -211,6 +92,10 @@ static void taskGuiEventLoop(void *data) {
int main(int argc, char *argv[]) {
+ FILE *cache = NULL;
+ char *line = NULL;
+ char *p = NULL;
+
#ifndef __linux__
// On DOS, display the contets of the log now that we're back in text mode.
char *logName = NULL;
@@ -260,31 +145,74 @@ int main(int argc, char *argv[]) {
sh_new_strdup(__runtimeData.integers);
sh_new_strdup(__runtimeData.strings);
- // ***TODO*** Load tables from disk cache
+ // ***TODO*** Default initial tables
- //taskCreate(taskComDebugLoop, NULL);
+ mkdir("cache", 0777);
+ line = (char *)malloc(4096);
+ if (line) {
+ // Load string cache.
+ cache = fopen("cache/string.dat", "rt");
+ if (cache) {
+ while (fscanf(cache, "%s\n", line) != EOF) {
+ p = strstr(line, "=");
+ if (p) {
+ *p = 0;
+ p++;
+ shput(__runtimeData.strings, line, strdup(p));
+ }
+ }
+ fclose(cache);
+ }
+ // Load integer cache.
+ cache = fopen("cache/integer.dat", "rt");
+ if (cache) {
+ while (fscanf(cache, "%s\n", line) != EOF) {
+ p = strstr(line, "=");
+ if (p) {
+ *p = 0;
+ p++;
+ shput(__runtimeData.integers, line, atol(p));
+ }
+ }
+ fclose(cache);
+ }
+ free(line);
+ line = NULL;
+ }
+
+// taskCreate(taskSignUp, NULL);
taskCreate(taskWelcome, NULL);
taskCreate(taskGuiEventLoop, NULL);
taskRun();
- // ***TODO*** Write tables from disk cache
-
- // Free integer table.
- if (__runtimeData.integers) {
- while (shlen(__runtimeData.integers) > 0) {
- shdel(__runtimeData.integers, __runtimeData.integers[0].key);
+ // Save & free integer table.
+ cache = fopen("cache/integer.dat", "wt");
+ if (cache) {
+ if (__runtimeData.integers) {
+ while (shlen(__runtimeData.integers) > 0) {
+ //logWrite("[%s]=[%d]\n", __runtimeData.integers[0].key, __runtimeData.integers[0].value);
+ fprintf(cache, "%s=%d\n", __runtimeData.integers[0].key, __runtimeData.integers[0].value);
+ shdel(__runtimeData.integers, __runtimeData.integers[0].key);
+ }
+ shfree(__runtimeData.integers);
}
- shfree(__runtimeData.integers);
+ fclose(cache);
}
- // Free string table.
- if (__runtimeData.strings) {
- while (shlen(__runtimeData.strings) > 0) {
- DEL(__runtimeData.strings[0].value);
- shdel(__runtimeData.strings, __runtimeData.strings[0].key);
+ // Save & free string table.
+ cache = fopen("cache/string.dat", "wt");
+ if (cache) {
+ if (__runtimeData.strings) {
+ while (shlen(__runtimeData.strings) > 0) {
+ //logWrite("[%s]=[%s]\n", __runtimeData.strings[0].key, __runtimeData.strings[0].value);
+ fprintf(cache, "%s=%s\n", __runtimeData.strings[0].key, __runtimeData.strings[0].value);
+ DEL(__runtimeData.strings[0].value);
+ shdel(__runtimeData.strings, __runtimeData.strings[0].key);
+ }
+ shfree(__runtimeData.strings);
}
- shfree(__runtimeData.strings);
+ fclose(cache);
}
packetThreadDataDestroy(&__packetThreadData);
diff --git a/client/src/network.c b/client/src/network.c
index 9ecc4bc..4ad68ca 100644
--- a/client/src/network.c
+++ b/client/src/network.c
@@ -66,11 +66,16 @@ PacketDecodeDataT *netGetPacket(uint8_t channel) {
}
-void netStop(void) {
+void netShutdown(void) {
_netRunning = 0;
}
+void netStartup(void) {
+ taskCreate(taskNetwork, NULL);
+}
+
+
void taskNetwork(void *data) {
int32_t r = 0;
int8_t pingTimeout = 0;
diff --git a/client/src/network.h b/client/src/network.h
index e10fbdf..333632d 100644
--- a/client/src/network.h
+++ b/client/src/network.h
@@ -27,7 +27,8 @@
uint8_t netGetChannelFree(void);
PacketDecodeDataT *netGetPacket(uint8_t channel);
-void netStop(void);
+void netShutdown(void);
+void netStartup(void);
void taskNetwork(void *data);
diff --git a/client/src/signup.c b/client/src/signup.c
new file mode 100644
index 0000000..95112ff
--- /dev/null
+++ b/client/src/signup.c
@@ -0,0 +1,384 @@
+/*
+ * 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 "textbox.h"
+#include "button.h"
+#include "label.h"
+#include "msgbox.h"
+
+#include "network.h"
+#include "runtime.h"
+#include "taglist.h"
+#include "timer.h"
+#include "task.h"
+
+#include "signup.h"
+#include "login.h"
+
+
+static WindowT *_winSignUp = NULL;
+static LabelT *_lblRequired = NULL;
+static TextboxT *_txtEmail = NULL;
+static TextboxT *_txtFirst = NULL;
+static TextboxT *_txtLast = NULL;
+static TextboxT *_txtUser = NULL;
+static TextboxT *_txtPass1 = NULL;
+static TextboxT *_txtPass2 = NULL;
+static ButtonT *_btnCancel = NULL;
+static ButtonT *_btnSignUp = NULL;
+
+
+static void btnCancelClick(WidgetT *widget);
+static void btnMsgBoxContinue(MsgBoxButtonT button);
+static void btnMsgBoxFinish(MsgBoxButtonT button);
+static void setButtons(uint8_t enabled);
+static void taskSignUpClick(void *data);
+static uint8_t validateEmail(char *email);
+static uint8_t validateEmailLetter(char c);
+static uint8_t validateName(char *username);
+static uint8_t validatePassword(char *password);
+static uint8_t validateUser(char *username);
+
+
+static void btnCancelClick(WidgetT *widget) {
+ (void)widget;
+
+ guiDelete(D(_winSignUp));
+ taskCreate(taskLogin, NULL);
+}
+
+
+static void btnMsgBoxContinue(MsgBoxButtonT button) {
+ (void)button;
+
+ setButtons(1);
+}
+
+
+static void btnMsgBoxFinish(MsgBoxButtonT button) {
+ (void)button;
+
+ guiDelete(D(_winSignUp));
+ taskCreate(taskLogin, NULL);
+}
+
+
+static void setButtons(uint8_t enabled) {
+ widgetEnableSet(W(_btnCancel), enabled);
+ widgetEnableSet(W(_btnSignUp), enabled);
+}
+
+
+void taskSignUp(void *data) {
+
+ (void)data;
+
+ TagItemT uiSignUp[] = {
+ T_START,
+ T_WINDOW, O(_winSignUp),
+ T_TITLE, P("Create New Account"),
+ T_WIDTH, 400, T_HEIGHT, 340,
+
+ T_LABEL, O(_lblRequired),
+ T_TITLE, P("All fields are required!"),
+ T_X, 100, T_Y, 25,
+ T_COLOR_FOREGROUND, vbeColorMake(255, 255, 0),
+ T_LABEL, T_DONE,
+
+ T_TEXTBOX, O(_txtUser),
+ // 123456789012345678
+ T_TITLE, P(" User Name:"),
+ T_X, 40, T_Y, 64,
+ T_WIDTH, 300,
+ T_LENGTH, 16,
+ T_TEXTBOX, T_DONE,
+
+ T_TEXTBOX, O(_txtPass1),
+ // 123456789012345678
+ T_TITLE, P(" Password:"),
+ T_X, 40, T_Y, 94,
+ T_WIDTH, 300,
+ T_LENGTH, 16,
+ T_MASK, '*',
+ T_TEXTBOX, T_DONE,
+
+ T_TEXTBOX, O(_txtPass2),
+ // 123456789012345678
+ T_TITLE, P(" Password (again):"),
+ T_X, 40, T_Y, 124,
+ T_WIDTH, 300,
+ T_LENGTH, 16,
+ T_MASK, '*',
+ T_TEXTBOX, T_DONE,
+
+ T_TEXTBOX, O(_txtFirst),
+ // 123456789012345678
+ T_TITLE, P(" REAL First Name:"),
+ T_X, 40, T_Y, 154,
+ T_WIDTH, 300,
+ T_LENGTH, 16,
+ T_MASK, '*',
+ T_TEXTBOX, T_DONE,
+
+ T_TEXTBOX, O(_txtLast),
+ // 123456789012345678
+ T_TITLE, P(" REAL Last Name:"),
+ T_X, 40, T_Y, 184,
+ T_WIDTH, 300,
+ T_LENGTH, 16,
+ T_MASK, '*',
+ T_TEXTBOX, T_DONE,
+
+ T_TEXTBOX, O(_txtEmail),
+ // 123456789012345678
+ T_TITLE, P(" VALID E-Mail:"),
+ T_X, 40, T_Y, 214,
+ T_WIDTH, 300,
+ T_LENGTH, 16,
+ T_MASK, '*',
+ T_TEXTBOX, T_DONE,
+
+ T_BUTTON, O(_btnCancel),
+ T_TITLE, P("Cancel"),
+ T_X, 25, T_Y, 270,
+ T_CLICK, P(btnCancelClick),
+ T_BUTTON, T_DONE,
+ T_BUTTON, O(_btnSignUp),
+ T_TITLE, P("Sign Up"),
+ T_X, 291, T_Y, 270,
+ T_CLICK, P(taskProxy),
+ T_USER_DATA, P(taskSignUpClick),
+ T_BUTTON, T_DONE,
+
+ T_WINDOW, T_DONE,
+ T_END
+ };
+
+ tagListRun(uiSignUp);
+}
+
+
+static void taskSignUpClick(void *data) {
+ PacketEncodeDataT encoded = { 0 };
+ PacketDecodeDataT *decoded = NULL;
+ PacketTypeSignUpT signup = { 0 };
+ PacketTypeSignUpResultT *result = { 0 };
+ int16_t timeout = 5;
+
+ (void)data;
+
+ // ***TODO*** This should disable the entire dialog instead of just the buttons. Need to finish the Enable GUI code first.
+
+ setButtons(0);
+
+ // Get the data.
+ memcpy(signup.email, textboxValueGet(_txtEmail), strlen(textboxValueGet(_txtEmail)));
+ memcpy(signup.first, textboxValueGet(_txtFirst), strlen(textboxValueGet(_txtFirst)));
+ memcpy(signup.last, textboxValueGet(_txtLast), strlen(textboxValueGet(_txtLast)));
+ memcpy(signup.pass, textboxValueGet(_txtPass1), strlen(textboxValueGet(_txtPass1)));
+ memcpy(signup.user, textboxValueGet(_txtUser), strlen(textboxValueGet(_txtUser)));
+
+ // Validate it. ***TODO*** These messages could be a lot better.
+ if (!validateEmail(signup.email)) {
+ msgBoxOne("Invalid E-Mail", MSGBOX_ICON_ERROR, "Please enter a valid E-mail address.", "Okay", btnMsgBoxContinue);
+ return;
+ }
+ if (!validateName(signup.first)) {
+ msgBoxOne("Invalid First Name", MSGBOX_ICON_ERROR, "Please enter a valid first name.", "Okay", btnMsgBoxContinue);
+ return;
+ }
+ if (!validateName(signup.last)) {
+ msgBoxOne("Invalid Last Name", MSGBOX_ICON_ERROR, "Please enter a valid last name.", "Okay", btnMsgBoxContinue);
+ return;
+ }
+ if (strcmp(textboxValueGet(_txtPass1), textboxValueGet(_txtPass2)) != 0) {
+ msgBoxOne("Invalid Password", MSGBOX_ICON_ERROR, "Passwords must match.", "Okay", btnMsgBoxContinue);
+ return;
+ }
+ if (!validatePassword(signup.pass)) {
+ msgBoxOne("Invalid Password", MSGBOX_ICON_ERROR, "Please enter a valid password.", "Okay", btnMsgBoxContinue);
+ return;
+ }
+ if (!validateUser(signup.user)) {
+ msgBoxOne("Invalid User Name", MSGBOX_ICON_ERROR, "Please enter a valid user name.", "Okay", btnMsgBoxContinue);
+ return;
+ }
+
+ // Send signup request.
+ encoded.packetType = PACKET_TYPE_SIGNUP;
+ encoded.control = PACKET_CONTROL_DAT;
+ encoded.channel = 0;
+ encoded.encrypt = 0;
+ packetEncode(__packetThreadData, &encoded, (char *)&signup, sizeof(PacketTypeSignUpT));
+ packetSend(__packetThreadData, &encoded);
+
+ // Wait for response.
+ do {
+ decoded = netGetPacket(0);
+ if (decoded) {
+ switch (decoded->packetType) {
+ case PACKET_TYPE_SIGNUP_RESULT:
+ result = (PacketTypeSignUpResultT *)decoded->data;
+ if (result->success) {
+ msgBoxOne("Success!", MSGBOX_ICON_INFORMATION, result->message, "Okay", btnMsgBoxFinish);
+ } else {
+ msgBoxOne("Uh Oh!", MSGBOX_ICON_ERROR, result->message, "Okay", btnMsgBoxContinue);
+ }
+ timeout = 0;
+ break;
+
+ default:
+ logWrite("Unexpected packet received: %d\n", decoded->packetType);
+ break;
+ }
+ packetDecodeDataDestroy(&decoded);
+ }
+ taskYield();
+ if (__timerQuarterSecondTick) timeout--;
+ } while (!guiHasStopped() && timeout > 0);
+
+ setButtons(1);
+}
+
+
+static uint8_t validateEmail(char *email) {
+
+ uint16_t x;
+ uint16_t step = 0;
+
+ // This does a really terrible job of checking if a string is an email address.
+ // It only has be sorta close. The server will make sure.
+
+ for (x=0; x= '0') && (c <= '9')) return 1;
+ if ((c >= 'a') && (c <= 'z')) return 1;
+ if ((c >= 'A') && (c <= 'Z')) return 1;
+ return 0;
+}
+
+
+static uint8_t validateName(char *username) {
+ uint16_t x;
+ char *allowed;
+ char needle[2];
+
+ if ((int32_t)strlen(username) < shget(__runtimeData.integers, "minName")) return 0;
+ if ((int32_t)strlen(username) > shget(__runtimeData.integers, "maxName")) return 0;
+
+ allowed = shget(__runtimeData.strings, "nameAllowed");
+ needle[1] = 0;
+ for (x=0; x shget(__runtimeData.integers, "maxPass")) return 0;
+
+ passLower = shget(__runtimeData.strings, "passLower");
+ passUpper = shget(__runtimeData.strings, "passUpper");
+ passSpecial = shget(__runtimeData.strings, "passSpecial");
+ passNumeric = shget(__runtimeData.strings, "passNumeric");
+ needle[1] = 0;
+ for (x=0; x shget(__runtimeData.integers, "maxUser")) return 0;
+
+ allowed = shget(__runtimeData.strings, "userAllowed");
+ needle[1] = 0;
+ for (x=0; x.
+ *
+ */
+
+
+#ifndef SIGNUP_H
+#define SIGNUP_H
+
+
+#include "os.h"
+
+
+void taskSignUp(void *data);
+
+
+#endif // SIGNUP_H
diff --git a/client/src/system/os.c b/client/src/system/os.c
new file mode 100644
index 0000000..1c32230
--- /dev/null
+++ b/client/src/system/os.c
@@ -0,0 +1,23 @@
+/*
+ * 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 "os.h"
+
+
diff --git a/client/src/system/os.h b/client/src/system/os.h
index 578871d..d659b03 100644
--- a/client/src/system/os.h
+++ b/client/src/system/os.h
@@ -34,6 +34,7 @@
#include
#include
#include
+#include
#ifdef __linux__
diff --git a/client/src/system/taglist.c b/client/src/system/taglist.c
index 0b0a754..f310628 100644
--- a/client/src/system/taglist.c
+++ b/client/src/system/taglist.c
@@ -106,6 +106,7 @@ static void tagListWidgetAttributeHandle(void) {
uint8_t hasIndex;
uint8_t enabled;
uint8_t visible;
+ char mask;
// Process generated lists in reverse.
while (arrlen(_widgetList) > 0) {
@@ -140,6 +141,7 @@ static void tagListWidgetAttributeHandle(void) {
hasIndex = 0;
enabled = 1;
visible = 1;
+ mask = 0;
// Parse provided attributes.
for (i=0; itagList); i+=2) {
@@ -196,6 +198,10 @@ static void tagListWidgetAttributeHandle(void) {
length = v;
break;
+ case T_MASK:
+ mask = v;
+ break;
+
case T_MAXIMUM:
maximum = v;
break;
@@ -320,8 +326,9 @@ static void tagListWidgetAttributeHandle(void) {
case T_TEXTBOX:
widget = W(textboxNew(pos.x, pos.y, pos.w, title));
- textboxValueSet((TextboxT *)widget, valueString);
+ if (hasValue) textboxValueSet((TextboxT *)widget, valueString);
if (length > 0) textboxLengthMaxSet((TextboxT *)widget, length);
+ textboxPasswordCharacterSet((TextboxT *)widget, mask);
break;
case T_UPDOWN:
diff --git a/client/src/system/taglist.h b/client/src/system/taglist.h
index 061fa2d..b216ff6 100644
--- a/client/src/system/taglist.h
+++ b/client/src/system/taglist.h
@@ -71,6 +71,7 @@ enum TagItemsE {
T_INDEX,
T_ITEM,
T_LENGTH,
+ T_MASK,
T_MAXIMUM,
T_MINIMUM,
T_SELECTED,
diff --git a/client/src/welcome.c b/client/src/welcome.c
index 3a18990..a3ca60c 100644
--- a/client/src/welcome.c
+++ b/client/src/welcome.c
@@ -137,7 +137,7 @@ static void taskConnectClick(void *data) {
}
// Start packet handler and negotiate encryption.
- taskCreate(taskNetwork, NULL);
+ netStartup();
packetEncryptionSetup(__packetThreadData);
timeout = 5;
do {
@@ -161,20 +161,18 @@ static void taskConnectClick(void *data) {
switch (decoded->packetType) {
case PACKET_TYPE_NUMBER:
// Store in number table.
- logWrite("Added integer: [%s] = [%d]\n", &decoded->data[4], (int32_t)decoded->data[0]);
shput(__runtimeData.integers, &decoded->data[4], (int32_t)decoded->data[0]);
// Reset timeout.
timeout = 10;
break;
case PACKET_TYPE_PROCEED:
- logWrite("Received PACKET_TYPE_PROCEED\n");
waiting = 0;
break;
case PACKET_TYPE_STRING:
// Store in string table.
- logWrite("Added string: [%s] = [%s]\n", decoded->data, &decoded->data[strlen(decoded->data) + 1]);
+ //logWrite("Storing [%s]=[%s]\n", decoded->data, &decoded->data[strlen(decoded->data) + 1]);
shput(__runtimeData.strings, decoded->data, strdup(&decoded->data[strlen(decoded->data) + 1]));
// Reset timeout.
timeout = 10;
@@ -219,7 +217,7 @@ static void taskConnectClick(void *data) {
// Connected! Show icon.
widgetVisibleSet(W(_picConnect), 1);
timeout = 6; // Roughly 1.5 seconds.
- while (timeout > 0) {
+ while (timeout > 0 && !guiHasStopped()) {
taskYield();
if (__timerQuarterSecondTick) timeout--;
}
@@ -244,7 +242,7 @@ static void btnSettingsClick(WidgetT *widget) {
}
-void setButtons(uint8_t enabled) {
+static void setButtons(uint8_t enabled) {
widgetEnableSet(W(_btnConnect), (__configData.serialCom > 0 && strlen(__configData.serverHost) > 2) ? enabled : 0);
widgetEnableSet(W(_btnSettings), enabled);
widgetEnableSet(W(_btnQuit), enabled);
diff --git a/server/src/client.c b/server/src/client.c
index 72b51d4..0f68dd7 100644
--- a/server/src/client.c
+++ b/server/src/client.c
@@ -92,7 +92,10 @@ static void clientProcessPacket(ClientThreadT *client, PacketDecodeDataT *data)
packetEncode(client->packetThreadData, &encoded, NULL, 0);
// Send it.
packetSend(client->packetThreadData, &encoded);
- logWrite("Got PING, sent PONG\n\r");
+ break;
+
+ case PACKET_TYPE_SIGNUP:
+ //***TODO***
break;
case PACKET_TYPE_VERSION_BAD:
@@ -100,21 +103,19 @@ static void clientProcessPacket(ClientThreadT *client, PacketDecodeDataT *data)
break;
case PACKET_TYPE_VERSION_OKAY:
- logWrite("Got VERSION_OK.\n\r");
// Fetch string table from REST.
response = restRequest("CONFIG_GET_STRINGS", NULL);
if (!response) {
- logWrite("Unable to fetch strings!\n\r");
+ consoleMessageQueue("%ld: Unable to fetch strings!\n", client->threadIndex);
break;
}
strings = restHelperConfigStringMapGet(response);
if (!strings) {
- logWrite("Unable to map strings!\n\r");
+ consoleMessageQueue("%ld: Unable to map strings!\n", client->threadIndex);
break;
}
restRelease(response);
// Send string table to client.
- logWrite("Sending strings.\n\r");
for (i=0; i<(unsigned)shlen(strings); i++) {
// Strings are encoded in a single buffer as: KEY\0DATA\0
x = strlen(strings[i].key);
@@ -122,11 +123,12 @@ static void clientProcessPacket(ClientThreadT *client, PacketDecodeDataT *data)
length = x + y + 2;
buffer = (char *)malloc(length);
if (!buffer) {
- logWrite("Unable to allocate buffer for string packet!\n\r");
+ consoleMessageQueue("%ld: Unable to allocate buffer for string packet!\n", client->threadIndex);
break;
}
memcpy(buffer, strings[i].key, x + 1);
memcpy(&buffer[x + 1], strings[i].value, y + 1);
+ //consoleMessageQueue("[%s]=[%s]\n", strings[i].key, strings[i].value);
// Build packet.
encoded.control = PACKET_CONTROL_DAT;
encoded.packetType = PACKET_TYPE_STRING;
@@ -136,23 +138,21 @@ static void clientProcessPacket(ClientThreadT *client, PacketDecodeDataT *data)
// Send it.
packetSend(client->packetThreadData, &encoded);
DEL(buffer);
- //logWrite("[%s] = [%s]\r\n", strings[i].key, strings[i].value);
}
restHelperConfigStringMapRelease(strings);
// Fetch number table from REST.
response = restRequest("CONFIG_GET_NUMBERS", NULL);
if (!response) {
- logWrite("Unable to fetch numbers!\n\r");
+ consoleMessageQueue("%ld: Unable to fetch numbers!\n", client->threadIndex);
break;
}
integers = restHelperConfigIntegerMapGet(response);
if (!integers) {
- logWrite("Unable to map numbers!\n\r");
+ consoleMessageQueue("%ld: Unable to map numbers!\n", client->threadIndex);
break;
}
restRelease(response);
// Send number table to client.
- logWrite("Sending numbers.\n\r");
for (i=0; i<(unsigned)shlen(integers); i++) {
// Integers are encoded in a single buffer as: 1234DATA\0
// Integers are 64 bit until sent to the client when they are truncated to 32.
@@ -161,7 +161,7 @@ static void clientProcessPacket(ClientThreadT *client, PacketDecodeDataT *data)
length = x + 5;
buffer = (char *)malloc(length);
if (!buffer) {
- logWrite("Unable to allocate buffer for number packet!\n\r");
+ consoleMessageQueue("%ld: Unable to allocate buffer for number packet!\n\r", client->threadIndex);
break;
}
memcpy(buffer, &y, 4);
@@ -186,7 +186,6 @@ static void clientProcessPacket(ClientThreadT *client, PacketDecodeDataT *data)
packetEncode(client->packetThreadData, &encoded, NULL, 0);
// Send it.
packetSend(client->packetThreadData, &encoded);
- logWrite("Sending proceed.\n\r");
break;
default:
diff --git a/server/src/rest.c b/server/src/rest.c
index 0ebe7e1..9858a32 100644
--- a/server/src/rest.c
+++ b/server/src/rest.c
@@ -25,6 +25,7 @@
#include "os.h"
#include "rest.h"
#include "array.h"
+#include "console.h"
typedef struct RestResponseS {
@@ -147,6 +148,7 @@ RestStringMapT *restHelperConfigStringMapGet(json_object *object) {
for (i=0; irunning = 0;
- pthread_join(client->threadHandle, &status);
// Tell the console.
enet_address_get_host_ip(&peer->address, buffer, 2047);
consoleMessageQueue("%ld: [%s] disconnected.\n", client->threadIndex, buffer);
+ // Stop client processing.
+ client->running = 0;
+ pthread_join(client->threadHandle, &status);
// Hang up.
enet_peer_reset(peer);
}
diff --git a/shared/packet.c b/shared/packet.c
index 175e5fc..6803e4f 100644
--- a/shared/packet.c
+++ b/shared/packet.c
@@ -74,6 +74,23 @@ static void *packetAesContextCreate(uint8_t *key, uint8_t *iv) {
static uint8_t packetCRC(char *data, uint16_t length, uint8_t startAt) {
uint16_t x = 0;
+ /*
+ * ***TODO***
+ *
+ * Something in the data is throwing this off. CRCs fail when there are
+ * "special characters" in the data, such as these string table fields:
+ *
+ * Storing [userAllowed]=[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890`!@#$%^&*()_-+=[]{}\/?<>.,:;"']
+ * CRC failed! 192 != 60
+ *
+ * Storing [passSpecial]=[`!@#$%^&*()_-+=[]\{}|:;"',.<>/?]
+ * CRC failed! 8 != 244
+ *
+ * Like the rest of the reliability code, this is only needed if we have
+ * RS-232 issues to the softmodem. Documenting for later if needed.
+ *
+ */
+
for (x=0; xnewPacket = 1;
- // Check CRC.
- if ((uint8_t)threadData->decodeBuffer[threadData->length - 1] != packetCRC(threadData->decodeBuffer, threadData->length - 1, 0)) continue;
-
/*
+ // Check CRC.
+ x = packetCRC(threadData->decodeBuffer, threadData->length - 1, 0);
+ if ((uint8_t)threadData->decodeBuffer[threadData->length - 1] != (uint8_t)x) {
+ //logWrite("CRC failed! %d != %d\n", (uint8_t)threadData->decodeBuffer[threadData->length - 1], (uint8_t)x);
+ continue;
+ }
+
// Get sequence value.
sequence = ((uint8_t)threadData->decodeBuffer[0]) & 0x1f;
diff --git a/shared/packets.h b/shared/packets.h
index af71fdd..147545c 100644
--- a/shared/packets.h
+++ b/shared/packets.h
@@ -27,8 +27,11 @@
#define PACKET_PROTOCOL_VERSION 1
-#define PACKET_MAX_USER 16
-#define PACKET_MAX_PASS 16
+#define PACKET_MAX_USER 16 // These need to match the configuration in the database.
+#define PACKET_MAX_PASS 16 // Or better, use the database values.
+#define PACKET_MAX_NAME 32
+#define PACKET_MAX_EMAIL 32
+#define PACKET_MAX_MESSAGE 256
// This enum is treated as BYTES in the code. Do not go over 255 entries.
@@ -47,6 +50,8 @@ typedef enum PacketTypeE {
PACKET_TYPE_NUMBER,
PACKET_TYPE_PROCEED,
PACKET_TYPE_LOGIN,
+ PACKET_TYPE_SIGNUP,
+ PACKET_TYPE_SIGNUP_RESULT,
PACKET_TYPE_COUNT
} PacketTypeT;
@@ -62,6 +67,19 @@ typedef struct PacketTypeLoginS {
char pass[PACKET_MAX_PASS];
} PacketTypeLoginT;
+typedef struct PacketTypeSignUpS {
+ char user[PACKET_MAX_USER];
+ char pass[PACKET_MAX_PASS];
+ char first[PACKET_MAX_NAME];
+ char last[PACKET_MAX_NAME];
+ char email[PACKET_MAX_EMAIL];
+} PacketTypeSignUpT;
+
+typedef struct PacketTypeSignUpResultS {
+ uint8_t success;
+ char message[PACKET_MAX_MESSAGE];
+} PacketTypeSignUpResultT;
+
#pragma pack(pop)