From 10f5da9595cbefceff4fdfb65d4e8ae9f74e9b5d Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Mon, 6 Dec 2021 19:44:34 -0600 Subject: [PATCH] Packet resending implemented. Not very tested. --- client/client.pro | 9 - client/src/main.c | 151 ++++++++++----- client/src/system/comport.c | 8 + client/src/system/comport.h | 6 +- client/src/test.c | 370 ------------------------------------ client/src/test.h | 37 ---- client/src/welcome.c | 10 - server/src/client.c | 32 +++- server/src/client.h | 1 + server/src/server.c | 55 ++++-- server/src/server.h | 2 + shared/packet.c | 120 ++++++++++-- shared/packet.h | 83 +++++--- shared/util.c | 26 ++- shared/util.h | 3 +- 15 files changed, 361 insertions(+), 552 deletions(-) delete mode 100644 client/src/test.c delete mode 100644 client/src/test.h diff --git a/client/client.pro b/client/client.pro index af2791f..31a9bd7 100644 --- a/client/client.pro +++ b/client/client.pro @@ -16,8 +16,6 @@ # along with this program. If not, see . # -#CONFIG *= testing - # NOTE: You'll occasionally find a file with a capital "C" extension. # These are C files that we want ignored by the DOS compiler. @@ -115,7 +113,6 @@ SOURCES = \ src/system/surface.c \ src/system/taglist.c \ $$SHARED/util.c \ - src/test.c \ $$SHARED/array.c \ $$SHARED/log.c \ src/system/timer.c \ @@ -151,10 +148,4 @@ OTHER_FILES = \ ../test.conf \ postBuild.sh -testing { - DEFINES *= TESTING - HEADERS += src/test.h - SOURCES += src/test.c -} - QMAKE_POST_LINK = $$PWD/postBuild.sh "$$PWD" "$$DESTDIR" diff --git a/client/src/main.c b/client/src/main.c index 143c1d4..766af19 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -49,25 +49,118 @@ #include "welcome.h" -#ifdef TESTING -#include "test.h" -#endif - PacketThreadDataT *__packetThreadData = NULL; // Exported in os.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"); + 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, "\rOK\r"); + if (r <= 0) { + comClose(__configData.serialCom - 1); + logWrite("Modem does not support ENET! Please check settings.\n"); + 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"); + return; + } + + // Connected! Show icon and negotiate session. + logWrite("Connected\n"); + + 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 (1) { + 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); + } + } + } + taskYield(); + } + } + + // 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; ColorT alpha; -#ifdef TESTING - int8_t debugState = 0; - int8_t key = 0; -#endif (void)data; @@ -78,34 +171,16 @@ static void taskGuiEventLoop(void *data) { timerUpdate(); mouse = mouseRead(); if (keyHit()) { -#ifdef TESTING - lastKey = keyASCIIGet(); //***DEBUG*** -#endif guiKeyboardProcess(keyASCIIGet(), keyExtendedGet(), keyScanCodeGet(), keyShiftGet(), keyControlGet(), keyAltGet()); //logWrite("Key '%d' Extended '%d' Scancode '%d' Shift '%d' Control '%d' Alt '%d'\n", keyASCIIGet(), keyExtended(), keyScanCode(), keyShift(), keyControl(), keyAlt()); } guiMouseProcess(mouse); guiComposite(); imageRenderWithAlpha(pointer, mouse->x, mouse->y, alpha); - -#ifdef TESTING - if (key == '=') guiDebugWidgetTreeDump(guiRootGet(), 0); - if (key == '+') { - debugState++; - if (debugState > 2) debugState = 0; - } - if (debugState > 0) widgetDebugDraw(guiRootGet(), debugState - 1); - //if (timerHalfSecondOn) guiDrawRectangle(0, 0, vbeSurfaceWidthGet() - 1, vbeSurfaceHeightGet() - 1, vbeMakeColor(255, 255, 255)); -#endif - vbeVBlankWait(); vbePresent(); taskYield(); -#ifdef TESTING - } while (key != 27); // Exit on ESC. -#else } while (!guiHasStopped()); -#endif imageUnload(&pointer); } @@ -152,31 +227,15 @@ int main(int argc, char *argv[]) { guiStartup(); taskStartup(); - __packetThreadData = packetThreadDataCreate(); + __packetThreadData = packetThreadDataCreate(NULL); + packetSenderRegister(comPacketSender); -#ifdef TESTING - taskCreate(comPortScanTest, NULL); -#else - taskCreate(taskWelcome, NULL); + taskCreate(taskComDebugLoop, NULL); + //taskCreate(taskWelcome, NULL); taskCreate(taskGuiEventLoop, NULL); -#endif taskRun(); -/* - char *testPacket = "Hello Server!"; - PacketEncodeDataT encoded; - encoded.control = PACKET_CONTROL_DAT; - encoded.packetType = PACKET_TYPE_TEST; - encoded.channel = 1; - encoded.encrypt = 0; - packetEncode(__packetThreadData, &encoded, testPacket, strlen(testPacket)); - logWrite("Encoded %d bytes (raw length %d)\n", encoded.length, strlen(testPacket)); - PacketDecodeDataT decoded; - packetDecode(__packetThreadData, &decoded, encoded.dataPointer, encoded.length); - logWrite("Decoded %d bytes (raw length %d)\n", decoded.length, encoded.length); -*/ - packetThreadDataDestroy(&__packetThreadData); taskShutdown(); diff --git a/client/src/system/comport.c b/client/src/system/comport.c index e76c238..d5dfe2b 100644 --- a/client/src/system/comport.c +++ b/client/src/system/comport.c @@ -21,6 +21,14 @@ #include "comport.h" #include "timer.h" #include "task.h" +#include "config.h" + + +void comPacketSender(char *data, uint32_t length, void *userData) { + (void)userData; + + comWrite(__configData.serialCom - 1, data, length); +} int comReceiveBufferFlush(int com) { diff --git a/client/src/system/comport.h b/client/src/system/comport.h index ad25130..d2b3d00 100644 --- a/client/src/system/comport.h +++ b/client/src/system/comport.h @@ -23,6 +23,7 @@ #include "os.h" +#include "packet.h" #ifdef __linux__ @@ -52,8 +53,9 @@ int comWrite(int com, const char *data, int len); #endif -int comReceiveBufferFlush(int com); -int comWaitWithTimeout(int com, char *buffer, int len, int quarterSeconds, char *expecting); +void comPacketSender(char *data, uint32_t length, void *userData); +int comReceiveBufferFlush(int com); +int comWaitWithTimeout(int com, char *buffer, int len, int quarterSeconds, char *expecting); #endif // COMPORT_H diff --git a/client/src/test.c b/client/src/test.c deleted file mode 100644 index 4280913..0000000 --- a/client/src/test.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - * 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 "test.h" - -#include "task.h" -#include "gui.h" -#include "widget.h" -#include "desktop.h" -#include "window.h" -#include "button.h" -#include "label.h" -#include "checkbox.h" -#include "radio.h" -#include "picture.h" -#include "frame.h" -#include "textbox.h" -#include "updown.h" -#include "listbox.h" -#include "terminal.h" - -#include "taglist.h" -#include "comport.h" - -#include - - -uint8_t _lastKey = 0; - -static TerminalT *_t1 = NULL; - - -static void buttonClick(WidgetT *widget); -//static void test(void *data); -static void testTerminal(void *data); - - -static void buttonClick(WidgetT *widget) { - logWrite("'%s' was clicked.\n", ((ButtonT *)widget)->title); -} - - -void comPortScanTest(void *data) { - int rc; - int len; - char buffer[1024]; - - (void)data; - - for (int x=0; x<4; x++) { - rc = comOpen(x, 57600L, 8, 'n', 1, SER_HANDSHAKING_RTSCTS); - if (rc == SER_SUCCESS) { - logWrite("COM%d exists!\n", x + 1); - snprintf(buffer, 1024, "%s%c", "AT+SOCK1", 13); - comWrite(x, buffer, strlen(buffer)); - sleep(1); - len = comRead(x, buffer, 1024); - buffer[len] = 0; - if (strstr(buffer, "OK")) { - logWrite("ENET SoftModem found!\n"); - } else { - logWrite("Result: [%s]\n", buffer); - } - comClose(x); - } else { - logWrite("No COM%d.\n", x + 1); - } - } - - guiStop(); -} - - -/* -static void test(void *data) { - DesktopT *desktop = (DesktopT *)guiRootGet(); - WindowT *w1 = NULL; - WindowT *w2 = NULL; - WindowT *w3 = NULL; - WindowT *w4 = NULL; - ButtonT *b1 = NULL; - LabelT *l1 = NULL; - CheckboxT *c1 = NULL; - RadioT *r1a = NULL; - RadioT *r2a = NULL; - RadioT *r3a = NULL; - RadioT *r1b = NULL; - RadioT *r2b = NULL; - RadioT *r3b = NULL; - PictureT *p1 = NULL; - FrameT *f1 = NULL; - TextboxT *tb1 = NULL; - TextboxT *tb2 = NULL; - UpdownT *u1 = NULL; - ListboxT *lb1 = NULL; - - (void)data; - - // Windows - w1 = windowNew(300, 25, 300, 200, "Window 1"); - guiAttach(W(desktop), W(w1)); - w2 = windowNew(150, 150, 300, 200, "Window 2"); - guiAttach(W(desktop), W(w2)); - w3 = windowNew(300, 300, 300, 200, "Window 3"); - guiAttach(W(desktop), W(w3)); - w4 = windowNew(10, 10, 7 + 8 + (80 * 8), 26 + 8 + (24 * 14), "Terminal"); - guiAttach(W(desktop), W(w4)); - - // Window 1 - p1 = pictureNew(0, 0, "data/kanga.png"); - guiAttach(W(w1), W(p1)); - lb1 = listboxNew(155, 10, 120, 140, "List Box"); - listboxItemAdd(lb1, "One"); - listboxItemAdd(lb1, "Two"); - listboxItemAdd(lb1, "Three"); - listboxItemAdd(lb1, "Four"); - listboxItemAdd(lb1, "Five"); - listboxItemAdd(lb1, "Six"); - listboxItemAdd(lb1, "Seven"); - listboxItemAdd(lb1, "Eight"); - listboxItemAdd(lb1, "Nine"); - listboxItemAdd(lb1, "Ten"); - listboxStepSet(lb1, 3); - guiAttach(W(w1), W(lb1)); - - // Window 2 - r1a = radioNew(10, 10, "Radio 1a", 1); - guiAttach(W(w2), W(r1a)); - r2a = radioNew(20 + widgetWidthGet(W(r1a)), 10, "Radio 2a", 1); - guiAttach(W(w2), W(r2a)); - r3a = radioNew(30 + widgetWidthGet(W(r1a)) + widgetWidthGet(W(r2a)), 10, "Radio 3a", 1); - guiAttach(W(w2), W(r3a)); - r1b = radioNew(10, 35, "Radio 1b", 2); - guiAttach(W(w2), W(r1b)); - r2b = radioNew(20 + widgetWidthGet(W(r1b)), 35, "Radio 2b", 2); - guiAttach(W(w2), W(r2b)); - r3b = radioNew(30 + widgetWidthGet(W(r1b)) + widgetWidthGet(W(r2b)), 35, "Radio 3b", 2); - guiAttach(W(w2), W(r3b)); - radioSelectedSet(r1a); - radioSelectedSet(r2b); - tb1 = textboxNew(10, 60, 265, "Test Textbox"); - textboxValueSet(tb1, "Really long text string to edit!"); - guiAttach(W(w2), W(tb1)); - tb2 = textboxNew(10, 85, 265, "Test Textbox"); - textboxValueSet(tb2, "Short string."); - guiAttach(W(w2), W(tb2)); - u1 = updownNew(10, 110, 0, 1024, 5, "UpDown"); - guiAttach(W(w2), W(u1)); - - // Window 3 - f1 = frameNew(10, 5, 175, 125, "Test Frame"); - guiAttach(W(w3), W(f1)); - b1 = buttonNew(0, 0, "Test Button", buttonClick); - guiAttach(W(f1), W(b1)); - l1 = labelNew(10, 40, "Test Label"); - guiAttach(W(f1), W(l1)); - c1 = checkboxNew(10, 65, "Test Checkbox"); - guiAttach(W(f1), W(c1)); - - // Window 4 - Terminal - t1 = terminalNew(0, 0, 80, 24); - guiAttach(W(w4), W(t1)); - - taskCreate(testTerminal, "terminalTest"); -} -*/ - - -void taskTestTagList(void *data) { - WindowT *w1 = NULL; - WindowT *w2 = NULL; - WindowT *w3 = NULL; - WindowT *w4 = NULL; - ButtonT *b1 = NULL; - LabelT *l1 = NULL; - CheckboxT *c1 = NULL; - RadioT *r1a = NULL; - RadioT *r2a = NULL; - RadioT *r3a = NULL; - RadioT *r1b = NULL; - RadioT *r2b = NULL; - RadioT *r3b = NULL; - PictureT *p1 = NULL; - FrameT *f1 = NULL; - TextboxT *tb1 = NULL; - TextboxT *tb2 = NULL; - UpdownT *u1 = NULL; - ListboxT *lb1 = NULL; - - (void)data; - - TagItemT ui[] = { - T_START, - - T_WINDOW, O(w1), - T_TITLE, P("Window 1"), - T_X, 300, T_Y, 25, T_WIDTH, 300, T_HEIGHT, 200, - T_PICTURE, O(p1), - T_X, 0, T_Y, 0, - T_FILENAME, P("data/kanga.png"), - T_PICTURE, T_DONE, - T_LISTBOX, O(lb1), - T_TITLE, P("Listbox"), - T_X, 155, T_Y, 10, T_WIDTH, 120, T_HEIGHT, 140, - T_ITEM, P("One"), - T_ITEM, P("Two"), - T_ITEM, P("Three"), - T_ITEM, P("Four"), - T_ITEM, P("Five"), - T_ITEM, P("Six"), - T_ITEM, P("Seven"), - T_ITEM, P("Eight"), - T_ITEM, P("Nine"), - T_ITEM, P("Ten"), - T_STEP, 3, - T_LISTBOX, T_DONE, - T_WINDOW, T_DONE, - - T_WINDOW, O(w2), - T_TITLE, P("Window 2"), - T_X, 150, T_Y, 150, T_WIDTH, 300, T_HEIGHT, 200, - T_RADIOBUTTON, O(r1a), - T_TITLE, P("Radio 1a"), - T_X, 10, T_Y, 10, - T_GROUP, 1, - T_SELECTED, 1, - T_RADIOBUTTON, T_DONE, - T_RADIOBUTTON, O(r2a), - T_TITLE, P("Radio 2a"), - T_X, 20 + 80, T_Y, 10, - T_GROUP, 1, - T_RADIOBUTTON, T_DONE, - T_RADIOBUTTON, O(r3a), - T_TITLE, P("Radio 3a"), - T_X, 30 + 80 * 2, T_Y, 10, - T_GROUP, 1, - T_RADIOBUTTON, T_DONE, - T_RADIOBUTTON, O(r1b), - T_TITLE, P("Radio 1b"), - T_X, 10, T_Y, 35, - T_GROUP, 2, - T_RADIOBUTTON, T_DONE, - T_RADIOBUTTON, O(r2b), - T_TITLE, P("Radio 2b"), - T_X, 20 + 80, T_Y, 35, - T_GROUP, 2, - T_SELECTED, 1, - T_RADIOBUTTON, T_DONE, - T_RADIOBUTTON, O(r3b), - T_TITLE, P("Radio 3b"), - T_X, 30 + 80 * 2, T_Y, 35, - T_GROUP, 2, - T_RADIOBUTTON, T_DONE, - T_TEXTBOX, O(tb1), - T_TITLE, P("Test Textbox"), - T_X, 10, T_Y, 60, T_WIDTH, 265, - T_VALUE, P("Really long text string to edit!"), - T_TEXTBOX, T_DONE, - T_TEXTBOX, O(tb2), - T_TITLE, P("Test Textbox"), - T_X, 10, T_Y, 85, T_WIDTH, 265, - T_VALUE, P("Short String."), - T_TEXTBOX, T_DONE, - T_UPDOWN, O(u1), - T_TITLE, P("UpDown"), - T_X, 10, T_Y, 120, - T_MINIMUM, 0, T_MAXIMUM, 1024, T_STEP, 5, - T_UPDOWN, T_DONE, - T_WINDOW, T_DONE, - - T_WINDOW, O(w3), - T_TITLE, P("Window 3"), - T_X, 300, T_Y, 300, T_WIDTH, 300, T_HEIGHT, 200, - T_FRAME, O(f1), - T_TITLE, P("Test Frame"), - T_X, 10, T_Y, 5, T_WIDTH, 175, T_HEIGHT, 125, - T_BUTTON, O(b1), - T_TITLE, P("Test Button"), - T_X, 0, T_Y, 0, - T_CLICK, P(buttonClick), - T_BUTTON, T_DONE, - T_LABEL, O(l1), - T_TITLE, P("Test Label"), - T_X, 10, T_Y, 40, - T_LABEL, T_DONE, - T_CHECKBOX, O(c1), - T_TITLE, P("Test Checkbox"), - T_X, 10, T_Y, 65, - T_CHECKBOX, T_DONE, - T_FRAME, T_DONE, - T_WINDOW, T_DONE, - - T_WINDOW, O(w4), - T_TITLE, P("Terminal"), - T_X, 10, T_Y, 10, T_WIDTH, 7 + 8 + (80 * 8), T_HEIGHT, 26 + 8 + (24 * 14), - T_TERMINAL, O(_t1), - T_X, 0, T_Y, 0, T_WIDTH, 80, T_HEIGHT, 24, - T_TERMINAL, T_DONE, - T_WINDOW, T_DONE, - - T_END - }; - - tagListRun(ui); - - taskCreate(testTerminal, NULL); -} - - -static void testTerminal(void *data) { - FILE *in = NULL; - char *buffer = NULL; - uint16_t length = 0; - - (void)data; - - // Load ANSI file for terminal test. - in = fopen("data/kanga.ans", "rt"); - fseek(in, 0, SEEK_END); - length = ftell(in); - fseek(in, 0, SEEK_SET); - buffer = (char *)malloc(length + 1); - fread(buffer, 1, length, in); - fclose(in); - buffer[length] = 0; - - terminalStringPrint(_t1, buffer); - - free(buffer); -} - - -void widgetDebugDraw(WidgetT *widget, uint8_t debugToggle) { - size_t len = arrlenu(widget->children); - size_t i; - RectT r; - - if (debugToggle) { - // Clipping region (blue) - guiWidgetBoundsDrawableOnScreenGet(widget, &r); - surfaceRectangleDraw(r.x, r.y, r.x + r.w, r.y + r.h, vbeColorMake(0, 0, 255)); - } else { - // Widget border (red) - guiWidgetPositionOnScreenGet(widget, &r); - surfaceRectangleDraw(r.x, r.y, r.x + widget->pos.w, r.y + widget->pos.h, vbeColorMake(255, 0, 0)); - } - - if (len > 0) { - for (i=0; ichildren[i], debugToggle); - } - } -} - diff --git a/client/src/test.h b/client/src/test.h deleted file mode 100644 index 288e245..0000000 --- a/client/src/test.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 TEST_H -#define TEST_H - - -#include "os.h" -#include "terminal.h" - - -extern uint8_t _lastKey; - - -void comPortScanTest(void *data); -void taskTestTagList(void *data); -void widgetDebugDraw(WidgetT *widget, uint8_t debugToggle); - - -#endif // TEST_H diff --git a/client/src/welcome.c b/client/src/welcome.c index ec5a922..6e16f37 100644 --- a/client/src/welcome.c +++ b/client/src/welcome.c @@ -132,16 +132,6 @@ static void taskConnectClick(void *data) { // Connected! Show icon and negotiate session. widgetVisibleSet(W(_picConnect), 1); taskYield(); - - char *testPacket = "Hello Server!"; - PacketEncodeDataT encoded; - encoded.control = PACKET_CONTROL_DAT; - encoded.packetType = PACKET_TYPE_TEST; - encoded.channel = 1; - encoded.encrypt = 0; - packetEncode(__packetThreadData, &encoded, testPacket, strlen(testPacket)); - comWrite(__configData.serialCom - 1, encoded.dataPointer, encoded.length); - logWrite("Sent %d bytes (raw length %d)\n", encoded.length, strlen(testPacket)); } diff --git a/server/src/client.c b/server/src/client.c index 546f2df..7397169 100644 --- a/server/src/client.c +++ b/server/src/client.c @@ -23,6 +23,7 @@ #include "network.h" #include "console.h" #include "packet.h" +#include "server.h" static uint8_t clientDequeuePacket(ClientThreadT *client); @@ -62,13 +63,29 @@ static uint8_t clientDequeuePacket(ClientThreadT *client) { static void clientProcessPacket(ClientThreadT *client, PacketDecodeDataT *data) { - char temp[PACKET_MAX + 1] = { 0 }; - uint16_t x = 0; + PacketEncodeDataT encoded = { 0 }; - for (x=0; xlength; x++) temp[x] = data->data[x]; - temp[x] = 0; + switch (data->packetType) { + case PACKET_TYPE_PING: + // Build PONG packet. + encoded.control = PACKET_CONTROL_DAT; + encoded.packetType = PACKET_TYPE_PONG; + encoded.channel = 1; + encoded.encrypt = 0; + packetEncode(client->packetThreadData, &encoded, NULL, 0); + // Send it. + packetSend(client->packetThreadData, &encoded); + logWrite("Got PING, sent PONG\n\r"); + break; - consoleMessageQueue("%ld: Channel %d Packet %d [%s]\n", client->threadIndex, data->channel, data->packetType, temp); + case PACKET_TYPE_CLIENT_SHUTDOWN: + serverDisconnectClient(client); + break; + + default: + consoleMessageQueue("%ld: Channel %d Unknown Packet %d\n", client->threadIndex, data->channel, data->packetType); + break; + } } @@ -94,14 +111,9 @@ void *clientThread(void *data) { ENetPeer *peer = (ENetPeer *)data; ClientThreadT *client = (ClientThreadT *)peer->data; - ENetPacket *packet = NULL; struct timespec remaining = { 0, 0 }; struct timespec sleepTime = { 0, 1000000000/100 }; // 1/100th second. - // Send service banner. - packet = enet_packet_create("KPMPGSMKII\r", 11, ENET_PACKET_FLAG_RELIABLE); - enet_peer_send(peer, 0, packet); - // Process packets until we're done. while (client->running) { if (!clientDequeuePacket(client)) { diff --git a/server/src/client.h b/server/src/client.h index e1b6d49..c9cf1a4 100644 --- a/server/src/client.h +++ b/server/src/client.h @@ -39,6 +39,7 @@ typedef struct ClientThreadS { PacketThreadDataT *packetThreadData; ClientRawPacketT **packetQueue; pthread_mutex_t packetQueueMutex; + void *peer; } ClientThreadT; diff --git a/server/src/server.c b/server/src/server.c index aba44aa..afa6cf9 100644 --- a/server/src/server.c +++ b/server/src/server.c @@ -30,6 +30,35 @@ pthread_t _serverThreadHandle = { 0 }; void *_serverThreadStatus = NULL; +static void serverPacketSender(char *data, uint32_t length, void *userData); + + +void serverDisconnectClient(ClientThreadT *client) { + ENetPeer *peer = (ENetPeer *)client->peer; + void *status = NULL; + char buffer[2048] = { 0 }; + + // Stop client processing. + client->running = 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); + // Hang up. + enet_peer_reset(peer); +} + + +static void serverPacketSender(char *data, uint32_t length, void *userData) { + ENetPeer *peer = (ENetPeer *)userData; + ENetPacket *packet = NULL; + + // Send packet. + packet = enet_packet_create(data, length, ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(peer, 0, packet); +} + + void serverShutdown(void) { pthread_join(_serverThreadHandle, &_serverThreadStatus); enet_host_destroy(_server); @@ -40,6 +69,9 @@ void serverStartup(uint32_t port, uint32_t maxClients) { ENetAddress address = { 0 }; pthread_attr_t serverThreadAttributes = { 0 }; + // Tell the packet code how to send packets. + packetSenderRegister(serverPacketSender); + // Set up listening socket. address.host = ENET_HOST_ANY; address.port = port; @@ -61,10 +93,10 @@ void serverStartup(uint32_t port, uint32_t maxClients) { void *serverThread(void *data) { ENetHost *server = (ENetHost *)data; ENetEvent event = { 0 }; + ENetPacket *packet = NULL; ClientThreadT *client = NULL; uint64_t nextIndex = 0; size_t i = 0; - void *status = NULL; char buffer[2048] = { 0 }; while (_running) { @@ -77,7 +109,7 @@ void *serverThread(void *data) { // Create new client. NEW(ClientThreadT, client); if (!client) utilDie("Unable to allocate new client.\n"); - client->packetThreadData = packetThreadDataCreate(); + client->packetThreadData = packetThreadDataCreate(event.peer); if (!client->packetThreadData) { utilDie("Unable to allocate packetThreadData for new client.\n"); break; // This break silences an invalid clang warning. @@ -85,6 +117,7 @@ void *serverThread(void *data) { client->threadIndex = nextIndex++; client->running = 1; client->packetQueue = NULL; + client->peer = event.peer; memset(&client->packetQueueMutex, 1, sizeof(pthread_mutex_t)); pthread_mutex_init(&client->packetQueueMutex, NULL); // Keep our client in the peer data for later. @@ -96,6 +129,9 @@ void *serverThread(void *data) { // Tell the console. enet_address_get_host_ip(&event.peer->address, buffer, 2047); consoleMessageQueue("%ld: [%s] connected.\n", client->threadIndex, buffer); + // Send banner to client. + packet = enet_packet_create("KPMPGSMKII\r", 11, ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(event.peer, 0, packet); break; case ENET_EVENT_TYPE_RECEIVE: @@ -106,14 +142,7 @@ void *serverThread(void *data) { case ENET_EVENT_TYPE_DISCONNECT: case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: - // Get our client. - client = (ClientThreadT *)event.peer->data; - // Stop client processing. - client->running = 0; - pthread_join(client->threadHandle, &status); - // Tell the console. - enet_address_get_host_ip(&event.peer->address, buffer, 2047); - consoleMessageQueue("%ld: [%s] disconnected.\n", client->threadIndex, buffer); + serverDisconnectClient((ClientThreadT *)event.peer->data); break; } } @@ -123,11 +152,7 @@ void *serverThread(void *data) { for (i=0; ipeerCount; i++) { client = (ClientThreadT *)server->peers[i].data; if (client) { - // Stop client processing. - client->running = 0; - pthread_join(client->threadHandle, &status); - // Hang up. - enet_peer_reset(&server->peers[i]); + serverDisconnectClient(client); } } diff --git a/server/src/server.h b/server/src/server.h index a229126..0fb5ab5 100644 --- a/server/src/server.h +++ b/server/src/server.h @@ -23,8 +23,10 @@ #include "os.h" +#include "client.h" +void serverDisconnectClient(ClientThreadT *client); void serverShutdown(void); void serverStartup(uint32_t port, uint32_t maxClients); void *serverThread(void *data); diff --git a/shared/packet.c b/shared/packet.c index 507cbcd..2b35a19 100644 --- a/shared/packet.c +++ b/shared/packet.c @@ -21,6 +21,9 @@ #include "packet.h" +static packetSender _packetSender = NULL; + + static uint8_t packetCRC(char *data, uint16_t length, uint8_t startAt); @@ -28,7 +31,7 @@ static uint8_t packetCRC(char *data, uint16_t length, uint8_t startAt) { uint16_t x = 0; for (x=0; xdecodeBuffer[data->length - 1] != packetCRC(threadData->decodeBuffer, data->length - 1, 0)) continue; + // Get sequence value. + sequence = ((uint8_t)threadData->decodeBuffer[0]) & 0x1f; + + // Is this a NAK? + if ((((uint8_t)threadData->decodeBuffer[0]) & 0xc0) >> 6 == PACKET_CONTROL_NAK) { + // Rewind until we find the packet we need in history. + x = threadData->historyPosition - 1; + if (x < 0) x = PACKET_SEQUENCE_MAX - 1; + while (threadData->history[x].sequence != sequence && x != threadData->historyPosition) { + x--; + if (x < 0) x = PACKET_SEQUENCE_MAX - 1; + } + // Did we find it? + if (x == threadData->historyPosition) { + // No! BAD! + logWrite("Unable to locate missing packet in history!\n\r"); + } else { + // Yes. Replay missing packets. + while (x != threadData->historyPosition) { + logWrite("Resending %d!\n\r", threadData->history[x].sequence); + _packetSender(threadData->history[x].data, threadData->history[x].length, threadData->senderData); + x++; + if (x >= PACKET_SEQUENCE_MAX) x = 0; + } + } + continue; + } + + // Is this the sequence number we're expecting? + if (sequence == threadData->lastRemoteSequence) { + // Yes! + threadData->lastRemoteSequence++; + } else { + // No! NAK it! + logWrite("Packet out of sequence! Got %d wanted %d!\n\r", sequence, threadData->lastRemoteSequence); + nak.control = PACKET_CONTROL_NAK; // Negative acknowledge. + nak.packetType = PACKET_TYPE_NONE; // Not destined for the app. + nak.channel = 0; // Channel doesn't matter for NAK. + nak.encrypt = 0; // Encryption doesn't matter for NAK. + nak.sequence = threadData->lastRemoteSequence; // The last good packet we saw. + packetEncode(threadData, &nak, NULL, 0); + packetSend(threadData, &nak); + continue; + } + // Fill decoded data fields. data->packetType = threadData->decodeBuffer[1]; data->channel = threadData->decodeBuffer[2]; @@ -88,10 +138,15 @@ uint8_t packetDecode(PacketThreadDataT *threadData, PacketDecodeDataT *data, cha } - // Copy packet data to new buffer. - data->data = (char *)malloc(data->length - 4); // 4 for 3 byte header and 1 byte CRC. - if (!data) continue; - memcpy(data->data, &threadData->decodeBuffer[3], data->length - 4); // Skip header and CRC. + // Copy packet data to new buffer, if any. + if (data->length - 4 > 0) { + data->data = (char *)malloc(data->length - 4); // 4 for 3 byte header and 1 byte CRC. + if (!data) continue; + memcpy(data->data, &threadData->decodeBuffer[3], data->length - 4); // Skip header and CRC. + } else { + // No payload. + data->data = NULL; + } // Fix length to remove header and checksum. data->length -= 4; break; @@ -138,11 +193,14 @@ uint8_t packetEncode(PacketThreadDataT *threadData, PacketEncodeDataT *data, cha if (length > PACKET_MAX) return 0; data->dataPointer = NULL; - data->length = 0; - data->sequence = threadData->sequence++; + data->length = 0; + + if (data->control == PACKET_CONTROL_DAT) { + data->sequence = threadData->sequence++; + } // Make needed header bytes. - control = (data->control << 6) + (data->encrypt << 5) + (data->sequence & 0x1f); + control = (((uint8_t)data->control) << 6) + (data->encrypt << 5) + (data->sequence & 0x1f); // Calculate CRC over header bytes and payload. crc = packetCRC((char *)&control, 1, crc); @@ -185,15 +243,47 @@ uint8_t packetEncode(PacketThreadDataT *threadData, PacketEncodeDataT *data, cha } -PacketThreadDataT *packetThreadDataCreate(void) { +void packetSend(PacketThreadDataT *threadData, PacketEncodeDataT *data) { + // Valid control type? + if (data->control != PACKET_CONTROL_BAD && data->control <= PACKET_CONTROL_COUNT) { + _packetSender(data->dataPointer, data->length, threadData->senderData); + } else { + logWrite("Invalid PACKET_CONTROL!\n\r"); + } + + // Add to history? + if (data->control == PACKET_CONTROL_DAT) { + threadData->history[threadData->historyPosition].sequence = data->sequence; + threadData->history[threadData->historyPosition].length = data->length; + memcpy(threadData->history[threadData->historyPosition].data, data->dataPointer, data->length); + threadData->historyPosition++; + if (threadData->historyPosition >= PACKET_SEQUENCE_MAX) { + threadData->historyPosition = 0; + } + } + + // Mark invalid so caller has to change it. + data->control = PACKET_CONTROL_BAD; +} + + +void packetSenderRegister(packetSender sender) { + _packetSender = sender; +} + + +PacketThreadDataT *packetThreadDataCreate(void *senderData) { PacketThreadDataT *data = NULL; data = (PacketThreadDataT *)malloc(sizeof(PacketThreadDataT)); if (data) { - data->sequence = 0; - data->decodeQueueHead = 0; - data->decodeQueueTail = 0; - data->newPacket = 1; + data->sequence = 0; + data->lastRemoteSequence = 0; + data->historyPosition = 0; + data->decodeQueueHead = 0; + data->decodeQueueTail = 0; + data->newPacket = 1; + data->senderData = senderData; } return data; diff --git a/shared/packet.h b/shared/packet.h index 53789a8..4c9ae7b 100644 --- a/shared/packet.h +++ b/shared/packet.h @@ -39,60 +39,87 @@ */ -#define PACKET_MAX 1024 // Maximum number of bytes per packet -#define PACKET_SEQUENCE_MAX 32 // Five bits of data +#define PACKET_MAX 1024 // Maximum number of bytes per packet +#define PACKET_SEQUENCE_MAX 16 // Five bits of data, 32 max. #define PACKET_INPUT_QUEUE_SIZE 4096 #define PACKET_BUFFER_SIZE (PACKET_MAX * 2 + 2 + 8) // Worst case, every byte is a PACKET_FRAME. Add two for ending frame. Add 8 for worst case header and CRC. -#define PACKET_FRAME 0x7e +#define PACKET_FRAME 0x7e -#define PACKET_CONTROL_DAT 0 // Regular data packet. -#define PACKET_CONTROL_RTX 1 // Retransmission packet. -#define PACKET_CONTROL_NAK 2 // Negative acknowledge. -#define PACKET_TYPE_NONE 0 // No packet. -#define PACKET_TYPE_TEST 1 +// This enum must be 4 entries or less. +typedef enum PacketControlE { + PACKET_CONTROL_DAT = 0, // Regular data packet. + PACKET_CONTROL_RTX, // Retransmission packet. + PACKET_CONTROL_NAK, // Negative acknowledge. + PACKET_CONTROL_BAD, + PACKET_CONTROL_COUNT +} PacketControlT; + +// This enum is treated as BYTES in the code. Do not go over 255 entries. +typedef enum PacketTypeE { + PACKET_TYPE_NONE = 0, // No packet. + PACKET_TYPE_PING, + PACKET_TYPE_PONG, + PACKET_TYPE_SERVER_SHUTDOWN, + PACKET_TYPE_CLIENT_SHUTDOWN, + PACKET_TYPE_COUNT +} PacketTypeT; typedef struct PacketDecodeDataS { // Output - uint8_t packetType; // One of PACKET_TYPE_* - char *data; // Buffer received. MUST FREE. - uint16_t length; // Length of buffer. - uint8_t channel; // Which data channel it arrived on. + PacketTypeT packetType; // One of PACKET_TYPE_* + char *data; // Buffer received. MUST FREE. + uint16_t length; // Length of buffer. + uint8_t channel; // Which data channel it arrived on. } PacketDecodeDataT; typedef struct PacketEncodeDataS { // Input - uint8_t control; // One of PACKET_CONTROL_* - uint8_t packetType; // One of PACKET_TYPE_* - uint8_t channel; // Which data channel to use. - uint8_t encrypt; // Do we want the packet encrypted? + PacketControlT control; // One of PACKET_CONTROL_* + PacketTypeT packetType; // One of PACKET_TYPE_* + uint8_t channel; // Which data channel to use. + uint8_t encrypt; // Do we want the packet encrypted? // Input & Output - uint16_t sequence; // For NAK packets, input the sequence number we missed. Other packets returns the sequence of this packet. + uint8_t sequence; // For NAK packets, input the sequence number we missed. Other packets returns the sequence of this packet. // Output - char *dataPointer; // Buffer ready to send. DO NOT FREE. - uint16_t length; // Length of buffer. + char *dataPointer; // Buffer ready to send. DO NOT FREE. + uint16_t length; // Length of buffer. } PacketEncodeDataT; +typedef struct PacketHistoryDataS { + char data[PACKET_BUFFER_SIZE]; + uint16_t length; + uint8_t sequence; +} PacketHistoryDataT; + typedef struct PacketThreadDataS { // Internal state per thread for packet processing. - uint8_t sequence; - char decodeBuffer[PACKET_MAX]; - char encodeBuffer[PACKET_BUFFER_SIZE]; - char decodeQueue[PACKET_INPUT_QUEUE_SIZE]; - uint16_t decodeQueueHead; - uint16_t decodeQueueTail; - uint8_t inEscape; - uint8_t newPacket; + uint8_t sequence; + uint8_t lastRemoteSequence; + PacketHistoryDataT history[PACKET_SEQUENCE_MAX]; + uint8_t historyPosition; + char decodeBuffer[PACKET_MAX]; + char encodeBuffer[PACKET_BUFFER_SIZE]; + char decodeQueue[PACKET_INPUT_QUEUE_SIZE]; + uint16_t decodeQueueHead; + uint16_t decodeQueueTail; + uint8_t inEscape; + uint8_t newPacket; + void *senderData; } PacketThreadDataT; +typedef void (*packetSender)(char *data, uint32_t length, void *userData); + uint8_t packetDecode(PacketThreadDataT *threadData, PacketDecodeDataT *data, char *input, uint16_t length); void packetDecodeDataDestroy(PacketDecodeDataT **packet); uint8_t packetEncode(PacketThreadDataT *threadData, PacketEncodeDataT *data, char *input, uint16_t length); -PacketThreadDataT *packetThreadDataCreate(void); +void packetSend(PacketThreadDataT *threadData, PacketEncodeDataT *data); +void packetSenderRegister(packetSender sender); +PacketThreadDataT *packetThreadDataCreate(void *senderData); void packetThreadDataDestroy(PacketThreadDataT **data); diff --git a/shared/util.c b/shared/util.c index f7852c4..d313d1d 100644 --- a/shared/util.c +++ b/shared/util.c @@ -51,16 +51,12 @@ char *utilAppNameWithNewExtensionGet(char *appName, char *extension) { } -void utilDie(const char *why, ...) { - va_list args; - char msg[2048]; +void utilBitsPrint(uint8_t byte) { + int i = 0; - va_start(args, why); - vsprintf(msg, why, args); - va_end(args); - - logWrite("DIE: %s", msg); - exit(1); + for (i = 7; 0 <= i; i--) { + printf("%c", (byte & (1 << i)) ? '1' : '0'); + } } @@ -93,3 +89,15 @@ char *utilCreateStringVArgs(char *format, va_list args) { return buffer; } + +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 25e7dbe..e302373 100644 --- a/shared/util.h +++ b/shared/util.h @@ -26,9 +26,10 @@ char *utilAppNameWithNewExtensionGet(char *appName, char *extension); -void utilDie(const char *why, ...); +void utilBitsPrint(uint8_t byte); char *utilCreateString(char *format, ...); char *utilCreateStringVArgs(char *format, va_list args); +void utilDie(const char *why, ...); #endif // UTIL_H