Packet resending implemented. Not very tested.

This commit is contained in:
Scott Duensing 2021-12-06 19:44:34 -06:00
parent a07a61b5dd
commit 10f5da9595
15 changed files with 361 additions and 552 deletions

View file

@ -16,8 +16,6 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
#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"

View file

@ -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();

View file

@ -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) {

View file

@ -23,6 +23,7 @@
#include "os.h"
#include "packet.h"
#ifdef __linux__
@ -52,6 +53,7 @@ int comWrite(int com, const char *data, int len);
#endif
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);

View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
#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 <unistd.h>
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; i<len; i++) {
widgetDebugDraw(widget->children[i], debugToggle);
}
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
#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

View file

@ -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));
}

View file

@ -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; x<data->length; 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)) {

View file

@ -39,6 +39,7 @@ typedef struct ClientThreadS {
PacketThreadDataT *packetThreadData;
ClientRawPacketT **packetQueue;
pthread_mutex_t packetQueueMutex;
void *peer;
} ClientThreadT;

View file

@ -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; i<server->peerCount; 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);
}
}

View file

@ -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);

View file

@ -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; x<length; x++) {
startAt += data[x];
startAt ^= data[x]; // Good ole' XOR.
}
return startAt;
@ -36,8 +39,10 @@ static uint8_t packetCRC(char *data, uint16_t length, uint8_t startAt) {
uint8_t packetDecode(PacketThreadDataT *threadData, PacketDecodeDataT *data, char *input, uint16_t length) {
uint8_t sequence = 0;
uint16_t x = 0;
char c = 0;
PacketEncodeDataT nak = { 0 };
// input and inputLength are incoming raw data or NULL and 0 to continue processing already received data.
// Returns 1 on packet ready, 0 on still waiting.
@ -79,6 +84,51 @@ uint8_t packetDecode(PacketThreadDataT *threadData, PacketDecodeDataT *data, cha
// Check CRC.
if ((uint8_t)threadData->decodeBuffer[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.
// 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;
@ -139,10 +194,13 @@ uint8_t packetEncode(PacketThreadDataT *threadData, PacketEncodeDataT *data, cha
data->dataPointer = NULL;
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->lastRemoteSequence = 0;
data->historyPosition = 0;
data->decodeQueueHead = 0;
data->decodeQueueTail = 0;
data->newPacket = 1;
data->senderData = senderData;
}
return data;

View file

@ -40,24 +40,37 @@
#define PACKET_MAX 1024 // Maximum number of bytes per packet
#define PACKET_SEQUENCE_MAX 32 // Five bits of data
#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_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_*
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.
@ -65,20 +78,29 @@ typedef struct PacketDecodeDataS {
typedef struct PacketEncodeDataS {
// Input
uint8_t control; // One of PACKET_CONTROL_*
uint8_t packetType; // One of PACKET_TYPE_*
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.
} 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;
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];
@ -86,13 +108,18 @@ typedef struct PacketThreadDataS {
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);

View file

@ -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);
}

View file

@ -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