Signup almost working server-side.

This commit is contained in:
Scott Duensing 2022-01-15 20:01:29 -06:00
parent 7174d36eb9
commit ba1fe40938
15 changed files with 314 additions and 66 deletions

View file

@ -150,7 +150,8 @@ SOURCES = \
src/welcome.c
LIBS = \
-lSDL2
-lSDL2 \
-lSDL2_image
OTHER_FILES = \
$$DOS_HEADERS \

View file

@ -19,6 +19,7 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#define ENET_IMPLEMENTATION
#include "thirdparty/enet/include/enet.h"
@ -72,6 +73,8 @@ static uint16_t _bufferHead = 0;
static uint16_t _bufferTail = 0;
static uint8_t _connected = 0;
static char _command[COM_BUFFER_SIZE] = { 0 };
static uint32_t _pixelFormat = SDL_PIXELFORMAT_RGBA8888;
static uint8_t _takeScreenshot = 0;
static void comAddToBuffer(char *data, uint16_t len);
@ -79,6 +82,7 @@ static void comBufferShow(void);
static void comModem(uint8_t c);
static void processEvent(void);
static void processNetworkEvent(void);
static void vbeScreenshot(void);
long biostime(int cmd, long newtime) {
@ -453,6 +457,8 @@ static void processEvent(void) {
_keyPressed = 1;
_debounce = 1;
}
// Screenshot?
if (e.key.keysym.scancode == SDL_SCANCODE_F10) _takeScreenshot = 1;
}
break;
}
@ -553,6 +559,11 @@ void vbePresent(void) {
// No network activity. Just sleep.
SDL_Delay(32);
}
if (_takeScreenshot) {
_takeScreenshot = 0;
vbeScreenshot();
}
}
@ -562,6 +573,37 @@ int16_t vbeInfoShow(void) {
}
static void vbeScreenshot(void) {
int32_t x = 0;
char filename[16] = { 0 };
void *pixels = NULL;
SDL_Surface *surface = NULL;
SDL_Surface *save = NULL;
FILE *check = NULL;
while (x <= 999) {
snprintf(filename, 16, "shot%03d.png", x);
check = fopen(filename, "rb");
if (check) {
x++;
fclose(check);
} else {
break;
}
}
if (x > 999) utilDie("Seriously? You have 1000 screenshots in this folder? Remove some.");
surface = SDL_GetWindowSurface(_window);
pixels = (uint8_t *)malloc(surface->w * surface->h * surface->format->BytesPerPixel);
SDL_RenderReadPixels(_renderer, &surface->clip_rect, surface->format->format, pixels, surface->w * surface->format->BytesPerPixel);
save = SDL_CreateRGBSurfaceFrom(pixels, surface->w, surface->h, surface->format->BitsPerPixel, surface->w * surface->format->BytesPerPixel, surface->format->Rmask, surface->format->Gmask, surface->format->Bmask, surface->format->Amask);
IMG_SavePNG(save, filename);
SDL_FreeSurface(save);
SDL_FreeSurface(surface);
free(pixels);
}
int16_t vbeShutdown(void) {
if (_texture) {
SDL_DestroyTexture(_texture);
@ -601,7 +643,7 @@ uint8_t vbeStartup(uint16_t xRes, uint16_t yRes, uint8_t bpp) {
_window = SDL_CreateWindow("GUI Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, xRes, yRes, SDL_WINDOW_ALLOW_HIGHDPI);
_surface = SDL_GetWindowSurface(_window);
_renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED);
_texture = SDL_CreateTexture(_renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, xRes, yRes);
_texture = SDL_CreateTexture(_renderer, _pixelFormat, SDL_TEXTUREACCESS_STREAMING, xRes, yRes);
SDL_RenderSetLogicalSize(_renderer, xRes, yRes);
SDL_SetWindowSize(_window, xRes * _windowScale, yRes * _windowScale);

View file

@ -22,6 +22,7 @@
#include "button.h"
#include "msgbox.h"
#include "runtime.h"
#include "config.h"
#include "comport.h"
#include "network.h"
@ -131,14 +132,14 @@ void taskLogin(void *data) {
T_TITLE, P("User Name:"),
T_X, 42, T_Y, 10,
T_WIDTH, 200,
T_LENGTH, 16,
T_LENGTH, shget(__runtimeData.integers, "maxUser"),
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_LENGTH, shget(__runtimeData.integers, "maxPass"),
T_MASK, '*',
T_TEXTBOX, T_DONE,

View file

@ -78,7 +78,6 @@ void netStartup(void) {
void taskNetwork(void *data) {
int32_t r = 0;
int8_t pingTimeout = 0;
char buffer[1024] = { 0 };
PacketDecodeDataT *packet = NULL;
PacketDecodeDataT decoded = { 0 };
@ -96,10 +95,24 @@ void taskNetwork(void *data) {
r = comRead(__configData.serialCom - 1, buffer, 1024);
// New data or not, process anything in the queue.
if (packetDecode(__packetThreadData, &decoded, buffer, r)) {
// Is this a PONG? If so, ignore it.
if (decoded.packetType == PACKET_TYPE_PONG) {
packetDecodeDataStaticDestroy(&decoded);
continue;
// Is this something we care about?
switch (decoded.packetType) {
case PACKET_TYPE_PING:
// Reply with PONG
encoded.control = PACKET_CONTROL_DAT;
encoded.packetType = PACKET_TYPE_PONG;
encoded.channel = 0;
encoded.encrypt = 0;
packetEncode(__packetThreadData, &encoded, NULL, 0);
packetSend(__packetThreadData, &encoded);
packetDecodeDataStaticDestroy(&decoded);
continue;
break;
default:
// Silences a warning.
break;
}
// Copy the packet out.
@ -126,20 +139,6 @@ void taskNetwork(void *data) {
// Yield to UI.
taskYield();
// Send a ping to the server every 5 seconds, because, ping.
if (__timerSecondTick) {
pingTimeout++;
if (pingTimeout > 5) {
pingTimeout = 0;
encoded.control = PACKET_CONTROL_DAT;
encoded.packetType = PACKET_TYPE_PING;
encoded.channel = 0;
encoded.encrypt = 0;
packetEncode(__packetThreadData, &encoded, NULL, 0); // Must encode each packet - no reusing encoded data.
packetSend(__packetThreadData, &encoded);
}
}
} while (!guiHasStopped() && _netRunning);
// Free any unclaimed packets.

View file

@ -107,7 +107,7 @@ void taskSignUp(void *data) {
T_TITLE, P(" User Name:"),
T_X, 40, T_Y, 64,
T_WIDTH, 300,
T_LENGTH, 16,
T_LENGTH, shget(__runtimeData.integers, "maxUser"),
T_TEXTBOX, T_DONE,
T_TEXTBOX, O(_txtPass1),
@ -115,7 +115,7 @@ void taskSignUp(void *data) {
T_TITLE, P(" Password:"),
T_X, 40, T_Y, 94,
T_WIDTH, 300,
T_LENGTH, 16,
T_LENGTH, shget(__runtimeData.integers, "maxPass"),
T_MASK, '*',
T_TEXTBOX, T_DONE,
@ -124,7 +124,7 @@ void taskSignUp(void *data) {
T_TITLE, P(" Password (again):"),
T_X, 40, T_Y, 124,
T_WIDTH, 300,
T_LENGTH, 16,
T_LENGTH, shget(__runtimeData.integers, "maxPass"),
T_MASK, '*',
T_TEXTBOX, T_DONE,
@ -133,8 +133,7 @@ void taskSignUp(void *data) {
T_TITLE, P(" REAL First Name:"),
T_X, 40, T_Y, 154,
T_WIDTH, 300,
T_LENGTH, 16,
T_MASK, '*',
T_LENGTH, shget(__runtimeData.integers, "maxName"),
T_TEXTBOX, T_DONE,
T_TEXTBOX, O(_txtLast),
@ -142,8 +141,7 @@ void taskSignUp(void *data) {
T_TITLE, P(" REAL Last Name:"),
T_X, 40, T_Y, 184,
T_WIDTH, 300,
T_LENGTH, 16,
T_MASK, '*',
T_LENGTH, shget(__runtimeData.integers, "maxName"),
T_TEXTBOX, T_DONE,
T_TEXTBOX, O(_txtEmail),
@ -151,8 +149,7 @@ void taskSignUp(void *data) {
T_TITLE, P(" VALID E-Mail:"),
T_X, 40, T_Y, 214,
T_WIDTH, 300,
T_LENGTH, 16,
T_MASK, '*',
T_LENGTH, shget(__runtimeData.integers, "maxEmail"),
T_TEXTBOX, T_DONE,
T_BUTTON, O(_btnCancel),

View file

@ -0,0 +1,81 @@
columns:
left:
width: 1/2
fields:
firstname:
label: First Name
width: 1/2
type: text
lastname:
label: Last Name
width: 1/2
type: text
street:
label: Street
type: text
city:
label: City
type: text
width: 1/2
state:
label: State
type: text
width: 1/2
zip:
label: ZIP
type: text
width: 1/4
country:
label: Country
type: text
width: 3/4
right:
width: 1/2
fields:
website:
label: Website
type: url
steam:
label: Steam
type: text
placeholder: username
width: 1/2
epic:
label: Epic Games
type: text
placeholder: username
width: 1/2
xbox:
label: Xbox Live
type: text
placeholder: username
width: 1/2
playstation:
label: PlayStation Network
type: text
placeholder: username
width: 1/2
nintendo:
label: Nintendo
type: text
placeholder: friendcode
width: 1/2
facebook:
label: Facebook
type: text
icon: facebook
placeholder: username
width: 1/2
twitter:
label: Twitter
type: text
icon: twitter
placeholder: @username
width: 1/2
instagram:
label: Instagram
type: text
icon: instagram
placeholder: username
width: 1/2

View file

@ -0,0 +1,8 @@
title: Administrator
description: Kanga World administrator
permissions:
access:
panel: true
extends: sections/userdata

View file

@ -1,4 +1,8 @@
title: User
description: Standard Kanga World user
permissions:
access:
panel: false
panel: false
extends: sections/userdata

View file

@ -30,7 +30,7 @@ return function ($kirby) {
];
// INVALID DATA
if($invalid = invalid($data, $rules, $messages)) {
if ($invalid = invalid($data, $rules, $messages)) {
$alert = $invalid;
$error = true;
@ -43,6 +43,9 @@ return function ($kirby) {
try {
// A lot of this code is duplicated in:
// site/plugins/kangaworld-integration/api/user.php
// CREATE USER
$user = $kirby->users()->create([
'email' => $data['email'],
@ -53,7 +56,6 @@ return function ($kirby) {
// CHECK EMAIL ACTIVATION
if (option('user.email.activation', false) === true) {
$user->update([
'emailActivation' => false,
'emailActivationToken' => $token

View file

@ -1,18 +1,54 @@
<?php
function kpApiUserCreate($name, $email, $password, &$response) {
function kpApiUserCreate($first, $last, $username, $email, $password, &$response) {
try {
kirby()->users()->create([
'name' => $name,
'email' => $email,
'password' => $password,
'language' => 'en',
'role' => 'user'
]);
$response['result'] = 'true';
$response['reason'] = 'User created.';
// Check for duplicate username.
$user = kirby()->users()->filterBy('name', $username);
if ($user->first()) {
$response['result'] = 'false';
$response['reason'] = 'User name already exists.';
} else {
// Save Kirby attributes.
$user = kirby()->users()->create([
'name' => $username,
'email' => $email,
'password' => $password,
'language' => 'en',
'role' => 'user'
]);
// Send activation email.
// This duplicates a lot of code from site/controllers/register.php
$token = Str::random(16);
$link = kirby()->site()->url() . "/user/activate/" . $token;
$email = kirby()->email([
'to' => $email,
'from' => option('user.email.activation.sender'),
'subject' => option('user.email.activation.sender', 'Account Activation Link'),
'template' => 'account-activation',
'data' => [
'link' => $link,
]
]);
// Save our extended attributes.
$user->update([
'firstname' => $first,
'lastname' => $last,
'emailActivation' => false,
'emailActivationToken' => $token
]);
// Save SQL attributes.
// ***TODO***
// Return result.
$response['result'] = 'true';
$response['reason'] = 'User created. Check your E-mail for activation instructions.';
}
} catch(Exception $e) {
$response['reason'] = $e->getMessage();
if (strpos($e->getMessage(), "email")) {
$response['reason'] = 'E-mail already exists.';
} else {
$response['reason'] = $e->getMessage();
}
}
}

View file

@ -40,7 +40,7 @@ return [
break;
case 'USER_CREATE':
kpApiUserCreate(get('name'), get('email'), get('password'), $response);
kpApiUserCreate(get('first'), get('last'), get('user'), get('email'), get('pass'), $response);
break;
case 'USER_GET':

View file

@ -64,15 +64,19 @@ static uint8_t clientDequeuePacket(ClientThreadT *client) {
static void clientProcessPacket(ClientThreadT *client, PacketDecodeDataT *data) {
uint64_t i = 0;
uint64_t x = 0;
uint32_t y = 0;
uint64_t length = 0;
char *buffer = NULL;
PacketEncodeDataT encoded = { 0 };
json_object *response = NULL;
RestStringMapT *strings = NULL;
RestIntegerMapT *integers = NULL;
uint64_t i = 0;
uint64_t x = 0;
uint32_t y = 0;
uint64_t length = 0;
char *buffer = NULL;
PacketEncodeDataT encoded = { 0 };
json_object *response = NULL;
RestStringMapT *strings = NULL;
RestIntegerMapT *integers = NULL;
struct timespec timer = { 0 };
double d = 0;
PacketTypeSignUpT *signup = NULL;
PacketTypeSignUpResultT signupResult = { 0 };
switch (data->packetType) {
case PACKET_TYPE_CLIENT_SHUTDOWN:
@ -83,19 +87,56 @@ static void clientProcessPacket(ClientThreadT *client, PacketDecodeDataT *data)
consoleMessageQueue("%ld: Channel %d %s %s\n", client->threadIndex, data->channel, ((PacketTypeLoginT *)data->data)->user, ((PacketTypeLoginT *)data->data)->pass);
break;
case PACKET_TYPE_PING:
// Build PONG packet.
encoded.control = PACKET_CONTROL_DAT;
encoded.packetType = PACKET_TYPE_PONG;
encoded.channel = 0;
encoded.encrypt = 0;
packetEncode(client->packetThreadData, &encoded, NULL, 0);
// Send it.
packetSend(client->packetThreadData, &encoded);
case PACKET_TYPE_PONG:
// Time round-trip.
clock_gettime(CLOCK_MONOTONIC_RAW, &timer);
d = (timer.tv_sec - client->pingStart.tv_sec) * 1e9;
d = (d + (timer.tv_nsec - client->pingStart.tv_nsec)) * 1e-9;
client->pingStats[client->pingHead] = d;
client->pingHead++;
if (client->pingHead >= PING_STATS_SIZE) client->pingHead = 0;
// ***TODO*** Probably need a mutex here.
if (d > client->pingHigh) client->pingHigh = d;
if (d < client->pingLow) client->pingLow = d;
client->pingAverage = 0;
x = 0;
for (i=0; i<PING_STATS_SIZE; i++) {
if (client->pingStats[i] != 0) {
x++;
client->pingAverage += client->pingStats[i];
}
}
client->pingAverage /= (double)x;
consoleMessageQueue("%ld: Ping: %f Low: %f Average: %f High: %f\n", client->threadIndex, d, client->pingLow, client->pingAverage, client->pingHigh);
break;
case PACKET_TYPE_SIGNUP:
//***TODO***
signup = (PacketTypeSignUpT *)data->data;
response = restRequest("USER_CREATE", "sssss",
"first", signup->first,
"last", signup->last,
"user", signup->user,
"pass", signup->pass,
"email", signup->email
);
if (response) {
signupResult.success = (json_object_get_boolean(json_object_object_get(response, "result")) == TRUE) ? 1 : 0;
buffer = (char *)json_object_get_string(json_object_object_get(response, "reason"));
} else {
// Something bad happened.
signupResult.success = 0;
buffer = "Unknown error. Sorry.";
}
memcpy(signupResult.message, buffer, strlen(buffer));
if (response) restRelease(response);
// Build packet.
encoded.control = PACKET_CONTROL_DAT;
encoded.packetType = PACKET_TYPE_SIGNUP_RESULT;
encoded.channel = 0;
encoded.encrypt = 0;
packetEncode(client->packetThreadData, &encoded, (char *)&signupResult, sizeof(PacketTypeSignUpResultT));
// Send it.
packetSend(client->packetThreadData, &encoded);
break;
case PACKET_TYPE_VERSION_BAD:
@ -230,6 +271,9 @@ void *clientThread(void *data) {
struct timespec sleepTime = { 0, 1000000000/100 }; // 1/100th second.
PacketTypeVersionT version = { 0 };
uint8_t versionSent = 0;
time_t ticks = { 0 };
time_t lastTicks = { 0 };
int8_t pingTimeout = 0;
// Process packets until we're done.
while (client->running) {
@ -250,11 +294,30 @@ void *clientThread(void *data) {
}
}
// Ping the client every 5 seconds.
ticks = time(NULL);
if (ticks != lastTicks) {
lastTicks = ticks;
pingTimeout++;
if (pingTimeout >= 5) {
pingTimeout = 0;
encoded.control = PACKET_CONTROL_DAT;
encoded.packetType = PACKET_TYPE_PING;
encoded.channel = 0;
encoded.encrypt = 0;
packetEncode(client->packetThreadData, &encoded, (char *)&version, sizeof(PacketTypeVersionT));
packetSend(client->packetThreadData, &encoded);
clock_gettime(CLOCK_MONOTONIC_RAW, &client->pingStart);
}
}
// Don't eat all the CPU.
nanosleep(&remaining, &sleepTime);
}
}
// ***TODO*** Write ping stats to database.
// Clean up client data on the way out.
while (arrlen(client->packetQueue) > 0) {
if (client->packetQueue[0]->data) DEL(client->packetQueue[0]->data);

View file

@ -26,6 +26,9 @@
#include "packet.h"
#define PING_STATS_SIZE 100
typedef struct ClientRawPacketS {
char *data;
uint32_t length;
@ -39,6 +42,12 @@ typedef struct ClientThreadS {
PacketThreadDataT *packetThreadData;
ClientRawPacketT **packetQueue;
pthread_mutex_t packetQueueMutex;
struct timespec pingStart;
double pingStats[PING_STATS_SIZE];
uint8_t pingHead;
double pingAverage;
double pingHigh;
double pingLow;
void *peer;
} ClientThreadT;

View file

@ -118,6 +118,11 @@ void *serverThread(void *data) {
client->running = 1;
client->packetQueue = NULL;
client->peer = event.peer;
client->pingHead = 0;
client->pingAverage = 0;
client->pingHigh = 0;
client->pingLow = 9999;
for (i=0; i<PING_STATS_SIZE; i++) client->pingStats[i] = 0;
memset(&client->packetQueueMutex, 1, sizeof(pthread_mutex_t));
pthread_mutex_init(&client->packetQueueMutex, NULL);
// Keep our client in the peer data for later.