Removed dependency on Kirby from the game server. Started file API.
This commit is contained in:
parent
26938e5aaf
commit
409c3b4cfb
49 changed files with 831 additions and 1336 deletions
|
@ -58,7 +58,7 @@ ALL_LDLIBS := -lc
|
|||
|
||||
|
||||
# Source, Binaries, Dependencies
|
||||
SRC := $(shell find $(SRCDIR) -type f -name '*.c' | grep -v '/linux/' | grep -v '/server/' | grep -v '/primes/' | grep -v '/font/' | grep -v '/retired/' | grep -v '/test/')
|
||||
SRC := $(shell find $(SRCDIR) -type f -name '*.c' | grep -v '/linux/' | grep -v '/server/' | grep -v '/primes/' | grep -v '/font/' | grep -v '/precache/' | grep -v '/retired/' | grep -v '/test/')
|
||||
OBJ := $(patsubst $(SRCDIR)/%,$(OBJDIR)/%,$(SRC:.c=.o))
|
||||
DEP := $(OBJ:.o=.d)
|
||||
BIN := $(BINDIR)/$(TARGET)
|
||||
|
|
5
build.sh
5
build.sh
|
@ -23,11 +23,10 @@
|
|||
# See: https://github.com/andrewwutw/build-djgpp
|
||||
|
||||
mkdir -p \
|
||||
bin/data \
|
||||
obj/client/src/system \
|
||||
obj/client/src/dos \
|
||||
obj/client/src/gui \
|
||||
obj/client/src/embedded \
|
||||
obj/client/src/thirdparty/SHA256 \
|
||||
obj/client/src/thirdparty/serial \
|
||||
obj/shared/thirdparty/memwatch \
|
||||
obj/shared/thirdparty/blowfish-api \
|
||||
|
@ -41,4 +40,4 @@ make -f Makefile.djgpp
|
|||
|
||||
rm bin/client
|
||||
|
||||
cp client/data/* bin/data/.
|
||||
cp precache/out/* bin/.
|
||||
|
|
|
@ -60,7 +60,7 @@ void linuxOsStartup(void);
|
|||
#else
|
||||
|
||||
#define BITS32
|
||||
#define PATH_SLASH '\\'
|
||||
#define OS_PATH_SLASH '\\'
|
||||
|
||||
// DOS includes.
|
||||
#include <dos.h>
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
|
||||
function kpApiConfigGetConfig(&$response) {
|
||||
$response['payload'] = collection('kwconfig');
|
||||
$response['result'] = 'true';
|
||||
$response['reason'] = 'Configuration entries returned.';
|
||||
}
|
||||
|
||||
|
||||
function kpApiConfigGetNumbers(&$response) {
|
||||
$response['payload'] = collection('kwnumbers');
|
||||
$response['result'] = 'true';
|
||||
$response['reason'] = 'Number entries returned.';
|
||||
}
|
||||
|
||||
|
||||
function kpApiConfigGetStrings(&$response) {
|
||||
$response['payload'] = collection('kwstrings');
|
||||
$response['result'] = 'true';
|
||||
$response['reason'] = 'String entries returned.';
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
|
||||
function kpApiTest(&$response) {
|
||||
|
||||
$response['payload'] = collection('kwstrings');
|
||||
$response['result'] = 'true';
|
||||
$response['reason'] = 'Dataset returned.';
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -37,6 +37,7 @@ function kpApiUserCreate($first, $last, $username, $email, $password, &$response
|
|||
]);
|
||||
// Save SQL attributes.
|
||||
// ***TODO***
|
||||
// ***TODO*** Intercept password change and user enable/disable to update the DB.
|
||||
|
||||
// Return result.
|
||||
$response['result'] = 'true';
|
||||
|
@ -51,26 +52,4 @@ function kpApiUserCreate($first, $last, $username, $email, $password, &$response
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function kpApiUserLogin($username, $pass, &$response) {
|
||||
try {
|
||||
// Find user by name instead of email.
|
||||
$user = kirby()->users()->filterBy('name', $username)->first();
|
||||
if ($user) {
|
||||
// ***TODO*** Must be activated!
|
||||
// Attempt to sign them in.
|
||||
kirby()->auth()->login($user->email(), $pass);
|
||||
// They don't need an actual Kirby session, so log them off.
|
||||
kirby()->auth()->logout();
|
||||
// Report it.
|
||||
$response['result'] = 'true';
|
||||
$response['reason'] = 'User logged in.';
|
||||
} else {
|
||||
$response['reason'] = 'User name or password incorrect.';
|
||||
}
|
||||
} catch(Exception $e) {
|
||||
$response['reason'] = 'User name or password incorrect.';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace KangarooPunch;
|
||||
|
||||
|
||||
use \PDO;
|
||||
|
||||
|
||||
class KPunch {
|
||||
|
||||
public static function databaseGet() {
|
||||
|
||||
$db = null;
|
||||
|
||||
try {
|
||||
$host = option('kangaroopunch.kangaworld-integration.sql.host');
|
||||
$port = option('kangaroopunch.kangaworld-integration.sql.port');
|
||||
$data = option('kangaroopunch.kangaworld-integration.sql.data');
|
||||
$user = option('kangaroopunch.kangaworld-integration.sql.user');
|
||||
$pass = option('kangaroopunch.kangaworld-integration.sql.pass');
|
||||
$db = new PDO("mysql:host=$host;port=$port;dbname=$data", $user, $pass);
|
||||
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
} catch(PDOException $ex) {
|
||||
$db = null;
|
||||
}
|
||||
|
||||
return $db;
|
||||
}
|
||||
|
||||
|
||||
public static function databaseQueryToJSON($db, $query, $options = null) {
|
||||
|
||||
$result = null;
|
||||
|
||||
if ($db) {
|
||||
$statement = $db->prepare($query);
|
||||
$statement->execute($options);
|
||||
$resultset = array();
|
||||
while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
|
||||
$resultset[] = $row;
|
||||
}
|
||||
$result = json_encode($resultset);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,90 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace KangarooPunch;
|
||||
|
||||
|
||||
use KangarooPunch\KPunch;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
use Kirby\Exception\NotFoundException;
|
||||
use Kirby\Toolkit\V;
|
||||
|
||||
|
||||
class KwConfig {
|
||||
|
||||
public static function create(array $input): bool {
|
||||
validate($input);
|
||||
$db = KPunch::databaseGet();
|
||||
KPunch::databaseQueryToJSON($db,
|
||||
'INSERT INTO config (name, data, description) VALUES (?, ?, ?)',
|
||||
array(
|
||||
$input['name'],
|
||||
$input['data'],
|
||||
$input['description']
|
||||
)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static function delete(string $id): bool {
|
||||
$db = KPunch::databaseGet();
|
||||
KPunch::databaseQueryToJSON($db, 'DELETE FROM config WHERE id = ?', array($id));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static function find(string $id): array {
|
||||
$db = KPunch::databaseGet();
|
||||
$result = KPunch::databaseQueryToJSON($db, 'SELECT * FROM config WHERE id = ?', array($id));
|
||||
if ($result == null) {
|
||||
throw new NotFoundException('The entry could not be found');
|
||||
}
|
||||
return json_decode($result);
|
||||
}
|
||||
|
||||
|
||||
public static function list(): array {
|
||||
$db = KPunch::databaseGet();
|
||||
return json_decode(KPunch::databaseQueryToJSON($db, 'SELECT * FROM config'));
|
||||
}
|
||||
|
||||
|
||||
public static function update(array $input): bool {
|
||||
validate($input);
|
||||
$db = KPunch::databaseGet();
|
||||
KPunch::databaseQueryToJSON($db,
|
||||
'UPDATE config SET name = ?, data = ?, description = ? WHERE id = ?',
|
||||
array(
|
||||
$input['name'],
|
||||
$input['data'],
|
||||
$input['description'],
|
||||
$input['id']
|
||||
)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private static function validate($input) {
|
||||
if (V::minlength($input['name'], 1) === false) {
|
||||
throw new InvalidArgumentException('The name must not be empty');
|
||||
}
|
||||
if (V::maxlength($input['name'], 32) === false) {
|
||||
throw new InvalidArgumentException('The name must not be longer than 32 characters');
|
||||
}
|
||||
|
||||
if (V::minlength($input['data'], 1) === false) {
|
||||
throw new InvalidArgumentException('The data must not be empty');
|
||||
}
|
||||
if (V::maxlength($input['data'], 1024) === false) {
|
||||
throw new InvalidArgumentException('The data must not be longer than 1024 characters');
|
||||
}
|
||||
|
||||
if (V::maxlength($input['description'], 255) === false) {
|
||||
throw new InvalidArgumentException('The description must not be longer than 255 characters');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,83 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace KangarooPunch;
|
||||
|
||||
|
||||
use KangarooPunch\KPunch;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
use Kirby\Exception\NotFoundException;
|
||||
use Kirby\Toolkit\V;
|
||||
|
||||
|
||||
class KwNumbers {
|
||||
|
||||
public static function create(array $input): bool {
|
||||
validate($input);
|
||||
$db = KPunch::databaseGet();
|
||||
KPunch::databaseQueryToJSON($db,
|
||||
'INSERT INTO numbers (name, data, description) VALUES (?, ?, ?)',
|
||||
array(
|
||||
$input['name'],
|
||||
$input['data'],
|
||||
$input['description']
|
||||
)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static function delete(string $id): bool {
|
||||
$db = KPunch::databaseGet();
|
||||
KPunch::databaseQueryToJSON($db, 'DELETE FROM numbers WHERE id = ?', array($id));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static function find(string $id): array {
|
||||
$db = KPunch::databaseGet();
|
||||
$result = KPunch::databaseQueryToJSON($db, 'SELECT * FROM numbers WHERE id = ?', array($id));
|
||||
if ($result == null) {
|
||||
throw new NotFoundException('The entry could not be found');
|
||||
}
|
||||
return json_decode($result);
|
||||
}
|
||||
|
||||
|
||||
public static function list(): array {
|
||||
$db = KPunch::databaseGet();
|
||||
return json_decode(KPunch::databaseQueryToJSON($db, 'SELECT * FROM numbers'));
|
||||
}
|
||||
|
||||
|
||||
public static function update(array $input): bool {
|
||||
validate($input);
|
||||
$db = KPunch::databaseGet();
|
||||
KPunch::databaseQueryToJSON($db,
|
||||
'UPDATE numbers SET name = ?, data = ?, description = ? WHERE id = ?',
|
||||
array(
|
||||
$input['name'],
|
||||
$input['data'],
|
||||
$input['description'],
|
||||
$input['id']
|
||||
)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private static function validate($input) {
|
||||
if (V::minlength($input['name'], 1) === false) {
|
||||
throw new InvalidArgumentException('The name must not be empty');
|
||||
}
|
||||
if (V::maxlength($input['name'], 32) === false) {
|
||||
throw new InvalidArgumentException('The name must not be longer than 32 characters');
|
||||
}
|
||||
|
||||
if (V::maxlength($input['description'], 255) === false) {
|
||||
throw new InvalidArgumentException('The description must not be longer than 255 characters');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,90 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace KangarooPunch;
|
||||
|
||||
|
||||
use KangarooPunch\KPunch;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
use Kirby\Exception\NotFoundException;
|
||||
use Kirby\Toolkit\V;
|
||||
|
||||
|
||||
class KwStrings {
|
||||
|
||||
public static function create(array $input): bool {
|
||||
validate($input);
|
||||
$db = KPunch::databaseGet();
|
||||
KPunch::databaseQueryToJSON($db,
|
||||
'INSERT INTO strings (name, data, description) VALUES (?, ?, ?)',
|
||||
array(
|
||||
$input['name'],
|
||||
$input['data'],
|
||||
$input['description']
|
||||
)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static function delete(string $id): bool {
|
||||
$db = KPunch::databaseGet();
|
||||
KPunch::databaseQueryToJSON($db, 'DELETE FROM strings WHERE id = ?', array($id));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static function find(string $id): array {
|
||||
$db = KPunch::databaseGet();
|
||||
$result = KPunch::databaseQueryToJSON($db, 'SELECT * FROM strings WHERE id = ?', array($id));
|
||||
if ($result == null) {
|
||||
throw new NotFoundException('The entry could not be found');
|
||||
}
|
||||
return json_decode($result);
|
||||
}
|
||||
|
||||
|
||||
public static function list(): array {
|
||||
$db = KPunch::databaseGet();
|
||||
return json_decode(KPunch::databaseQueryToJSON($db, 'SELECT * FROM strings'));
|
||||
}
|
||||
|
||||
|
||||
public static function update(array $input): bool {
|
||||
validate($input);
|
||||
$db = KPunch::databaseGet();
|
||||
KPunch::databaseQueryToJSON($db,
|
||||
'UPDATE strings SET name = ?, data = ?, description = ? WHERE id = ?',
|
||||
array(
|
||||
$input['name'],
|
||||
$input['data'],
|
||||
$input['description'],
|
||||
$input['id']
|
||||
)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private static function validate($input) {
|
||||
if (V::minlength($input['name'], 1) === false) {
|
||||
throw new InvalidArgumentException('The name must not be empty');
|
||||
}
|
||||
if (V::maxlength($input['name'], 32) === false) {
|
||||
throw new InvalidArgumentException('The name must not be longer than 32 characters');
|
||||
}
|
||||
|
||||
if (V::minlength($input['data'], 1) === false) {
|
||||
throw new InvalidArgumentException('The data must not be empty');
|
||||
}
|
||||
if (V::maxlength($input['data'], 1024) === false) {
|
||||
throw new InvalidArgumentException('The data must not be longer than 1024 characters');
|
||||
}
|
||||
|
||||
if (V::maxlength($input['description'], 255) === false) {
|
||||
throw new InvalidArgumentException('The description must not be longer than 255 characters');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,22 +0,0 @@
|
|||
<?php
|
||||
|
||||
use KangarooPunch\KwConfig;
|
||||
|
||||
return [
|
||||
'pattern' => 'kwconfig/create',
|
||||
'load' => function () {
|
||||
|
||||
return [
|
||||
'component' => 'k-form-dialog',
|
||||
'props' => [
|
||||
'fields' => require __DIR__ . '/fields.php',
|
||||
'submitButton' => t('create'),
|
||||
]
|
||||
];
|
||||
},
|
||||
'submit' => function () {
|
||||
return KwConfig::create(get());
|
||||
}
|
||||
];
|
||||
|
||||
?>
|
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
|
||||
use KangarooPunch\KwConfig;
|
||||
|
||||
return [
|
||||
'pattern' => 'kwconfig/(:any)/delete',
|
||||
'load' => function (string $id) {
|
||||
return [
|
||||
'component' => 'k-remove-dialog',
|
||||
'props' => [
|
||||
'text' => 'Do you really want to delete this entry?'
|
||||
]
|
||||
];
|
||||
},
|
||||
'submit' => function (string $id) {
|
||||
return KwConfig::delete($id);
|
||||
}
|
||||
];
|
||||
|
||||
?>
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
use KangarooPunch\KwConfig;
|
||||
use Kirby\Toolkit\A;
|
||||
|
||||
return [
|
||||
'name' => [
|
||||
'label' => 'Name',
|
||||
'type' => 'text'
|
||||
],
|
||||
'type' => [
|
||||
'label' => 'Type',
|
||||
'type' => 'select',
|
||||
'empty' => false,
|
||||
'width' => '1/2',
|
||||
'options' => A::map(KwConfig::types(), function ($type) {
|
||||
return ['value' => $type, 'text' => $type];
|
||||
})
|
||||
],
|
||||
'description' => [
|
||||
'label' => 'Description',
|
||||
'type' => 'textarea',
|
||||
'buttons' => false
|
||||
],
|
||||
'data' => [
|
||||
'label' => 'Value',
|
||||
'type' => 'text'
|
||||
]
|
||||
];
|
||||
|
||||
?>
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
|
||||
use KangarooPunch\KwConfig;
|
||||
|
||||
return [
|
||||
'pattern' => 'kwconfig/(:any)/update',
|
||||
'load' => function (string $id) {
|
||||
$kwconfig = KwConfig::find($id);
|
||||
|
||||
return [
|
||||
'component' => 'k-form-dialog',
|
||||
'props' => [
|
||||
'fields' => require __DIR__ . '/fields.php',
|
||||
'value' => $kwconfig
|
||||
]
|
||||
];
|
||||
},
|
||||
'submit' => function (string $id) {
|
||||
return KwConfig::update(get());
|
||||
}
|
||||
];
|
||||
|
||||
?>
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'pattern' => 'kwconfig/(:any)',
|
||||
'action' => function (string $id) {
|
||||
return [
|
||||
[
|
||||
'text' => 'Edit',
|
||||
'icon' => 'edit',
|
||||
'dialog' => 'kwconfig/' . $id . '/update'
|
||||
],
|
||||
[
|
||||
'text' => 'Delete',
|
||||
'icon' => 'trash',
|
||||
'dialog' => 'kwconfig/' . $id . '/delete'
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
?>
|
|
@ -1,7 +1,5 @@
|
|||
<?php
|
||||
|
||||
require __DIR__ . '/../api/config.php';
|
||||
require __DIR__ . '/../api/test.php';
|
||||
require __DIR__ . '/../api/user.php';
|
||||
|
||||
|
||||
|
@ -28,30 +26,10 @@ return [
|
|||
$response['reason'] = 'API Available.';
|
||||
break;
|
||||
|
||||
case 'API_TEST':
|
||||
kpApiTest($response);
|
||||
break;
|
||||
|
||||
case 'CONFIG_GET_CONFIG':
|
||||
kpApiConfigGetConfig($response);
|
||||
break;
|
||||
|
||||
case 'CONFIG_GET_NUMBERS':
|
||||
kpApiConfigGetNumbers($response);
|
||||
break;
|
||||
|
||||
case 'CONFIG_GET_STRINGS':
|
||||
kpApiConfigGetStrings($response);
|
||||
break;
|
||||
|
||||
case 'USER_CREATE':
|
||||
kpApiUserCreate(get('first'), get('last'), get('user'), get('email'), get('pass'), $response);
|
||||
break;
|
||||
|
||||
case 'USER_LOGIN':
|
||||
kpApiUserLogin(get('user'), get('pass'), $response);
|
||||
break;
|
||||
|
||||
default:
|
||||
$response['reason'] = 'Invalid command.';
|
||||
break;
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'kwconfig' => [
|
||||
'label' => 'Kanga World Configuration',
|
||||
'icon' => 'globe',
|
||||
'menu' => true,
|
||||
// update and delete dialogs
|
||||
'dialogs' => [
|
||||
require __DIR__ . '/../dialogs/create.php',
|
||||
require __DIR__ . '/../dialogs/update.php',
|
||||
require __DIR__ . '/../dialogs/delete.php'
|
||||
],
|
||||
// dropdown with edit and delete buttons
|
||||
'dropdowns' => [
|
||||
require __DIR__ . '/../dropdowns/kwconfig.php'
|
||||
],
|
||||
// search for settings
|
||||
'searches' => [
|
||||
'kwconfig' => require __DIR__ . '/../searches/kwconfig.php'
|
||||
],
|
||||
// view route
|
||||
'views' => [
|
||||
require __DIR__ . '/../views/kwconfig.php',
|
||||
require __DIR__ . '/../views/kwentry.php'
|
||||
],
|
||||
]
|
||||
|
||||
];
|
||||
|
||||
?>
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
use KangarooPunch\KwConfig;
|
||||
use KangarooPunch\KwNumbers;
|
||||
use KangarooPunch\KwStrings;
|
||||
|
||||
return [
|
||||
'kwconfig' => function($site) {
|
||||
return new Collection(KwConfig::list());
|
||||
},
|
||||
|
||||
'kwnumbers' => function($site) {
|
||||
return new Collection(KwNumbers::list());
|
||||
},
|
||||
|
||||
'kwstrings' => function($site) {
|
||||
return new Collection(KwStrings::list());
|
||||
}
|
||||
];
|
||||
|
||||
?>
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'sql' => [
|
||||
'host' => 'mysql',
|
||||
'port' => 3306,
|
||||
'data' => 'dosThing',
|
||||
'user' => 'dosThing',
|
||||
'pass' => 'password'
|
||||
]
|
||||
];
|
||||
|
||||
?>
|
|
@ -1,43 +0,0 @@
|
|||
|
||||
.k-kwconfig {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
border-spacing: 1px;
|
||||
}
|
||||
.k-kwconfig td,
|
||||
.k-kwconfig th {
|
||||
text-align: left;
|
||||
font-size: var(--text-sm);
|
||||
padding: var(--spacing-2);
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
background: var(--color-white);
|
||||
}
|
||||
.k-kwconfig th {
|
||||
font-weight: var(--font-bold);
|
||||
}
|
||||
.k-kwconfig th button {
|
||||
font: inherit;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
.k-kwconfig-name {
|
||||
/* width: 8rem; */
|
||||
}
|
||||
.k-kwconfig-type {
|
||||
/* width: 8rem; */
|
||||
}
|
||||
.k-kwconfig-description {
|
||||
width: 50%;
|
||||
}
|
||||
.k-kwconfig-data {
|
||||
/* width: 10rem; */
|
||||
font-variant-numeric: tabular-nums;
|
||||
text-align: right !important;
|
||||
}
|
||||
.k-kwconfig-options {
|
||||
padding: 0 !important;
|
||||
width: 3rem;
|
||||
overflow: visible !important;
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
(function() {
|
||||
"use strict";
|
||||
var render$1 = function() {
|
||||
var _vm = this;
|
||||
var _h = _vm.$createElement;
|
||||
var _c = _vm._self._c || _h;
|
||||
return _c("k-inside", [_c("k-view", [_c("k-header", [_vm._v(" Kanga World Configuration "), _c("k-button-group", { attrs: { "slot": "right" }, slot: "right" }, [_c("k-button", { attrs: { "text": "New Entry", "icon": "add" }, on: { "click": function($event) {
|
||||
return _vm.$dialog("kwconfig/create");
|
||||
} } })], 1)], 1), _c("table", { staticClass: "k-kwconfig" }, [_c("tr", [_c("th", { staticClass: "k-kwconfig-name" }, [_c("button", { on: { "click": function($event) {
|
||||
return _vm.sortBy("name");
|
||||
} } }, [_vm._v(" Name "), _vm.sort === "name" ? _c("span", { domProps: { "innerHTML": _vm._s(_vm.sortArrow) } }) : _vm._e()])]), _c("th", { staticClass: "k-kwconfig-type" }, [_c("button", { on: { "click": function($event) {
|
||||
return _vm.sortBy("type");
|
||||
} } }, [_vm._v(" Type "), _vm.sort === "type" ? _c("span", { domProps: { "innerHTML": _vm._s(_vm.sortArrow) } }) : _vm._e()])]), _c("th", { staticClass: "k-kwconfig-description" }, [_c("button", { on: { "click": function($event) {
|
||||
return _vm.sortBy("description");
|
||||
} } }, [_vm._v(" Description "), _vm.sort === "description" ? _c("span", { domProps: { "innerHTML": _vm._s(_vm.sortArrow) } }) : _vm._e()])]), _c("th", { staticClass: "k-kwconfig-data" }, [_c("button", { on: { "click": function($event) {
|
||||
return _vm.sortBy("data");
|
||||
} } }, [_vm._v(" Value "), _vm.sort === "data" ? _c("span", { domProps: { "innerHTML": _vm._s(_vm.sortArrow) } }) : _vm._e()])]), _c("th", { staticClass: "k-kwconfig-options" })]), _vm._l(_vm.kwconfig, function(kwconfig, name) {
|
||||
return _c("tr", { key: name }, [_c("td", { staticClass: "k-kwconfig-name" }, [_vm._v(_vm._s(kwconfig.name))]), _c("td", { staticClass: "k-kwconfig-type" }, [_vm._v(_vm._s(kwconfig.type))]), _c("td", { staticClass: "k-kwconfig-description" }, [_vm._v(_vm._s(kwconfig.description))]), _c("td", { staticClass: "k-kwconfig-data" }, [_vm._v(_vm._s(kwconfig.data))]), _c("td", { staticClass: "k-kwconfig-options" }, [_c("k-options-dropdown", { attrs: { "options": "kwconfig/" + name } })], 1)]);
|
||||
})], 2)], 1)], 1);
|
||||
};
|
||||
var staticRenderFns$1 = [];
|
||||
render$1._withStripped = true;
|
||||
var KwConfig_vue_vue_type_style_index_0_lang = "";
|
||||
function normalizeComponent(scriptExports, render2, staticRenderFns2, functionalTemplate, injectStyles, scopeId, moduleIdentifier, shadowMode) {
|
||||
var options = typeof scriptExports === "function" ? scriptExports.options : scriptExports;
|
||||
if (render2) {
|
||||
options.render = render2;
|
||||
options.staticRenderFns = staticRenderFns2;
|
||||
options._compiled = true;
|
||||
}
|
||||
if (functionalTemplate) {
|
||||
options.functional = true;
|
||||
}
|
||||
if (scopeId) {
|
||||
options._scopeId = "data-v-" + scopeId;
|
||||
}
|
||||
var hook;
|
||||
if (moduleIdentifier) {
|
||||
hook = function(context) {
|
||||
context = context || this.$vnode && this.$vnode.ssrContext || this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext;
|
||||
if (!context && typeof __VUE_SSR_CONTEXT__ !== "undefined") {
|
||||
context = __VUE_SSR_CONTEXT__;
|
||||
}
|
||||
if (injectStyles) {
|
||||
injectStyles.call(this, context);
|
||||
}
|
||||
if (context && context._registeredComponents) {
|
||||
context._registeredComponents.add(moduleIdentifier);
|
||||
}
|
||||
};
|
||||
options._ssrRegister = hook;
|
||||
} else if (injectStyles) {
|
||||
hook = shadowMode ? function() {
|
||||
injectStyles.call(this, (options.functional ? this.parent : this).$root.$options.shadowRoot);
|
||||
} : injectStyles;
|
||||
}
|
||||
if (hook) {
|
||||
if (options.functional) {
|
||||
options._injectStyles = hook;
|
||||
var originalRender = options.render;
|
||||
options.render = function renderWithStyleInjection(h, context) {
|
||||
hook.call(context);
|
||||
return originalRender(h, context);
|
||||
};
|
||||
} else {
|
||||
var existing = options.beforeCreate;
|
||||
options.beforeCreate = existing ? [].concat(existing, hook) : [hook];
|
||||
}
|
||||
}
|
||||
return {
|
||||
exports: scriptExports,
|
||||
options
|
||||
};
|
||||
}
|
||||
const __vue2_script$1 = {
|
||||
props: {
|
||||
dir: String,
|
||||
sort: String,
|
||||
kwconfig: Object
|
||||
},
|
||||
computed: {
|
||||
sortArrow() {
|
||||
return this.dir === "asc" ? "↓" : "↑";
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
sortBy(sort) {
|
||||
let dir = "asc";
|
||||
if (sort === this.sort)
|
||||
dir = this.dir === "asc" ? "desc" : "asc";
|
||||
this.$reload({
|
||||
query: {
|
||||
dir,
|
||||
sort
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
const __cssModules$1 = {};
|
||||
var __component__$1 = /* @__PURE__ */ normalizeComponent(__vue2_script$1, render$1, staticRenderFns$1, false, __vue2_injectStyles$1, null, null, null);
|
||||
function __vue2_injectStyles$1(context) {
|
||||
for (let o in __cssModules$1) {
|
||||
this[o] = __cssModules$1[o];
|
||||
}
|
||||
}
|
||||
__component__$1.options.__file = "src/components/KwConfig.vue";
|
||||
var KwConfig = /* @__PURE__ */ function() {
|
||||
return __component__$1.exports;
|
||||
}();
|
||||
var render = function() {
|
||||
var _vm = this;
|
||||
var _h = _vm.$createElement;
|
||||
var _c = _vm._self._c || _h;
|
||||
return _c("k-inside", [_c("k-view", [_c("k-header", [_vm._v(" " + _vm._s(_vm.kwentry.name) + " "), _c("k-button-group", { attrs: { "slot": "left" }, slot: "left" }, [_c("k-button", { attrs: { "text": "Edit", "icon": "edit" }, on: { "click": function($event) {
|
||||
return _vm.$dialog("kwconfig/" + _vm.kwentry.name + "/update");
|
||||
} } }), _c("k-button", { attrs: { "text": "Delete", "icon": "trash" }, on: { "click": function($event) {
|
||||
return _vm.$dialog("kwconfig/" + _vm.kwentry.name + "/delete");
|
||||
} } })], 1)], 1), _c("table", { staticClass: "k-kwconfig" }, [_c("tr", [_c("th", { staticClass: "k-kwconfig-type" }, [_vm._v("Type")]), _c("th", { staticClass: "k-kwconfig-description" }, [_vm._v("Description")]), _c("th", { staticClass: "k-kwconfig-data" }, [_vm._v("Value")])]), _c("tr", [_c("td", { staticClass: "k-kwconfig-type" }, [_vm._v(_vm._s(_vm.kwentry.type))]), _c("td", { staticClass: "k-kwconfig-description" }, [_vm._v(_vm._s(_vm.kwentry.description))]), _c("td", { staticClass: "k-kwconfig-data" }, [_vm._v(_vm._s(_vm.kwentry.data))])])])], 1)], 1);
|
||||
};
|
||||
var staticRenderFns = [];
|
||||
render._withStripped = true;
|
||||
const __vue2_script = {
|
||||
props: {
|
||||
kwentry: Object
|
||||
}
|
||||
};
|
||||
const __cssModules = {};
|
||||
var __component__ = /* @__PURE__ */ normalizeComponent(__vue2_script, render, staticRenderFns, false, __vue2_injectStyles, null, null, null);
|
||||
function __vue2_injectStyles(context) {
|
||||
for (let o in __cssModules) {
|
||||
this[o] = __cssModules[o];
|
||||
}
|
||||
}
|
||||
__component__.options.__file = "src/components/KwEntry.vue";
|
||||
var KwEntry = /* @__PURE__ */ function() {
|
||||
return __component__.exports;
|
||||
}();
|
||||
panel.plugin("kangaroopunch/kangaworld-integration", {
|
||||
components: {
|
||||
"k-kwconfig-view": KwConfig,
|
||||
"k-kwentry-view": KwEntry
|
||||
}
|
||||
});
|
||||
})();
|
|
@ -1,19 +1,8 @@
|
|||
<?php
|
||||
|
||||
load([
|
||||
'KangarooPunch\KwConfig' => 'KwConfig.php',
|
||||
'KangarooPunch\KwNumbers' => 'KwNumbers.php',
|
||||
'KangarooPunch\KwStrings' => 'KwStrings.php',
|
||||
'KangarooPunch\KPunch' => 'KPunch.php'
|
||||
], __DIR__ . '/classes');
|
||||
|
||||
|
||||
Kirby::plugin('kangaroopunch/kangaworld-integration', [
|
||||
|
||||
'api' => require __DIR__ . '/features/api.php',
|
||||
// 'areas' => require __DIR__ . '/features/areas.php',
|
||||
'collections' => require __DIR__ . '/features/collections.php',
|
||||
'options' => require __DIR__ . '/features/options.php'
|
||||
'api' => require __DIR__ . '/features/api.php'
|
||||
|
||||
]);
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"scripts": {
|
||||
"dev": "npx -y kirbyup src/index.js --watch",
|
||||
"build": "npx -y kirbyup src/index.js"
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
use KangarooPunch\KwConfig;
|
||||
|
||||
return [
|
||||
'label' => 'Configuration',
|
||||
'icon' => 'globe',
|
||||
'query' => function(string $query) {
|
||||
|
||||
$kwconfig = KwConfig::list();
|
||||
$results = [];
|
||||
|
||||
foreach ($kwconfig as $kwentry) {
|
||||
if ((Str::contains($kwentry['name'], $query, true) === true) ||
|
||||
(Str::contains($kwentry['description'], $query, true) === true) ||
|
||||
(Str::contains($kwentry['data'], $query, true) === true)) {
|
||||
$results[] = [
|
||||
'text' => $kwentry['name'],
|
||||
'link' => '/kwconfig/' . esc($kwentry['name'], 'url'),
|
||||
'image' => [
|
||||
'icon' => 'globe',
|
||||
'back' => 'purple-400'
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
];
|
||||
|
||||
?>
|
|
@ -1,126 +0,0 @@
|
|||
<template>
|
||||
<k-inside>
|
||||
<k-view>
|
||||
<k-header>
|
||||
Kanga World Configuration
|
||||
<k-button-group slot="right">
|
||||
<k-button
|
||||
text="New Entry"
|
||||
icon="add"
|
||||
@click="$dialog('kwconfig/create')"
|
||||
/>
|
||||
</k-button-group>
|
||||
</k-header>
|
||||
<table class="k-kwconfig">
|
||||
<tr>
|
||||
<th class="k-kwconfig-name">
|
||||
<button @click="sortBy('name')">
|
||||
Name
|
||||
<span v-if="sort === 'name'" v-html="sortArrow"/>
|
||||
</button>
|
||||
</th>
|
||||
<th class="k-kwconfig-type">
|
||||
<button @click="sortBy('type')">
|
||||
Type
|
||||
<span v-if="sort === 'type'" v-html="sortArrow"/>
|
||||
</button>
|
||||
</th>
|
||||
<th class="k-kwconfig-description">
|
||||
<button @click="sortBy('description')">
|
||||
Description
|
||||
<span v-if="sort === 'description'" v-html="sortArrow"/>
|
||||
</button>
|
||||
</th>
|
||||
<th class="k-kwconfig-data">
|
||||
<button @click="sortBy('data')">
|
||||
Value
|
||||
<span v-if="sort === 'data'" v-html="sortArrow"/>
|
||||
</button>
|
||||
</th>
|
||||
<th class="k-kwconfig-options"></th>
|
||||
</tr>
|
||||
<tr v-for="(kwconfig, name) in kwconfig" :key="name">
|
||||
<td class="k-kwconfig-name">{{ kwconfig.name }}</td>
|
||||
<td class="k-kwconfig-type">{{ kwconfig.type }}</td>
|
||||
<td class="k-kwconfig-description">{{ kwconfig.description }}</td>
|
||||
<td class="k-kwconfig-data">{{ kwconfig.data }}</td>
|
||||
<td class="k-kwconfig-options">
|
||||
<k-options-dropdown :options="'kwconfig/' + name" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</k-view>
|
||||
</k-inside>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
dir: String,
|
||||
sort: String,
|
||||
kwconfig: Object
|
||||
},
|
||||
computed: {
|
||||
sortArrow() {
|
||||
return this.dir === "asc" ? "↓" : "↑";
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
sortBy(sort) {
|
||||
let dir = "asc";
|
||||
if (sort === this.sort) dir = this.dir === "asc" ? "desc" : "asc";
|
||||
this.$reload({
|
||||
query: {
|
||||
dir: dir,
|
||||
sort: sort
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.k-kwconfig {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
border-spacing: 1px;
|
||||
}
|
||||
.k-kwconfig td,
|
||||
.k-kwconfig th {
|
||||
text-align: left;
|
||||
font-size: var(--text-sm);
|
||||
padding: var(--spacing-2);
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
background: var(--color-white);
|
||||
}
|
||||
.k-kwconfig th {
|
||||
font-weight: var(--font-bold);
|
||||
}
|
||||
.k-kwconfig th button {
|
||||
font: inherit;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
.k-kwconfig-name {
|
||||
/* width: 8rem; */
|
||||
}
|
||||
.k-kwconfig-type {
|
||||
/* width: 8rem; */
|
||||
}
|
||||
.k-kwconfig-description {
|
||||
width: 50%;
|
||||
}
|
||||
.k-kwconfig-data {
|
||||
/* width: 10rem; */
|
||||
font-variant-numeric: tabular-nums;
|
||||
text-align: right !important;
|
||||
}
|
||||
.k-kwconfig-options {
|
||||
padding: 0 !important;
|
||||
width: 3rem;
|
||||
overflow: visible !important;
|
||||
}
|
||||
</style>
|
|
@ -1,41 +0,0 @@
|
|||
<template>
|
||||
<k-inside>
|
||||
<k-view>
|
||||
<k-header>
|
||||
{{ kwentry.name }}
|
||||
<k-button-group slot="left">
|
||||
<k-button
|
||||
text="Edit"
|
||||
icon="edit"
|
||||
@click="$dialog('kwconfig/' + kwentry.name + '/update')"
|
||||
/>
|
||||
<k-button
|
||||
text="Delete"
|
||||
icon="trash"
|
||||
@click="$dialog('kwconfig/' + kwentry.name + '/delete')"
|
||||
/>
|
||||
</k-button-group>
|
||||
</k-header>
|
||||
<table class="k-kwconfig">
|
||||
<tr>
|
||||
<th class="k-kwconfig-type">Type</th>
|
||||
<th class="k-kwconfig-description">Description</th>
|
||||
<th class="k-kwconfig-data">Value</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="k-kwconfig-type">{{ kwentry.type }}</td>
|
||||
<td class="k-kwconfig-description">{{ kwentry.description }}</td>
|
||||
<td class="k-kwconfig-data">{{ kwentry.data }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</k-view>
|
||||
</k-inside>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
kwentry: Object
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -1,9 +0,0 @@
|
|||
import KwConfig from "./components/KwConfig.vue";
|
||||
import KwEntry from "./components/KwEntry.vue";
|
||||
|
||||
panel.plugin("kangaroopunch/kangaworld-integration", {
|
||||
components: {
|
||||
"k-kwconfig-view": KwConfig,
|
||||
"k-kwentry-view": KwEntry
|
||||
}
|
||||
});
|
|
@ -1,25 +0,0 @@
|
|||
<?php
|
||||
|
||||
use KangarooPunch\KwConfig;
|
||||
|
||||
return [
|
||||
'pattern' => 'kwconfig',
|
||||
'action' => function () {
|
||||
$sort = get('sort', 'name');
|
||||
$dir = get('dir', 'asc');
|
||||
$kwconfig = KwConfig::list();
|
||||
$kwconfig = A::sort($kwconfig, $sort, $dir);
|
||||
|
||||
return [
|
||||
'component' => 'k-kwconfig-view',
|
||||
'props' => [
|
||||
'dir' => $dir,
|
||||
'sort' => $sort,
|
||||
'kwconfig' => $kwconfig
|
||||
],
|
||||
'search' => 'kwconfig'
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
?>
|
|
@ -1,25 +0,0 @@
|
|||
<?php
|
||||
|
||||
use KangarooPunch\KwConfig;
|
||||
|
||||
return [
|
||||
'pattern' => 'kwconfig/(:any)',
|
||||
'action' => function ($id) {
|
||||
$kwentry = KwConfig::find($id);
|
||||
|
||||
return [
|
||||
'component' => 'k-kwentry-view',
|
||||
'breadcrumb' => [
|
||||
[
|
||||
'label' => $kwentry['name'],
|
||||
'link' => 'kwconfig/' . $id
|
||||
]
|
||||
],
|
||||
'props' => [
|
||||
'kwentry' => $kwentry
|
||||
]
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
?>
|
|
@ -21,8 +21,8 @@ TEMPLATE = subdirs
|
|||
CONFIG *= ORDERED
|
||||
|
||||
SUBDIRS = \
|
||||
client
|
||||
# server
|
||||
# client \
|
||||
server
|
||||
# precache
|
||||
# font
|
||||
# primes
|
||||
|
|
|
@ -50,6 +50,7 @@ HEADERS = \
|
|||
$$SHARED/thirdparty/tiny-AES-c/aes.h \
|
||||
$$SHARED/thirdparty/tiny-AES128-C/pkcs7_padding.h \
|
||||
src/client.h \
|
||||
src/client/file.h \
|
||||
src/client/login.h \
|
||||
src/client/pong.h \
|
||||
src/client/shutdown.h \
|
||||
|
@ -59,6 +60,7 @@ HEADERS = \
|
|||
src/network.h \
|
||||
src/os.h \
|
||||
src/rest.h \
|
||||
src/database.h \
|
||||
src/server.h
|
||||
|
||||
SOURCES = \
|
||||
|
@ -72,6 +74,7 @@ SOURCES = \
|
|||
$$SHARED/thirdparty/tiny-AES-c/aes.c \
|
||||
$$SHARED/thirdparty/tiny-AES128-C/pkcs7_padding.c \
|
||||
src/client.c \
|
||||
src/client/file.c \
|
||||
src/client/login.c \
|
||||
src/client/pong.c \
|
||||
src/client/shutdown.c \
|
||||
|
@ -81,12 +84,14 @@ SOURCES = \
|
|||
src/main.c \
|
||||
src/network.c \
|
||||
src/rest.c \
|
||||
src/database.c \
|
||||
src/server.c
|
||||
|
||||
LIBS = \
|
||||
-L/usr/lib/x86_64-linux-gnu/ \
|
||||
-ldl \
|
||||
-lm \
|
||||
-lmariadb \
|
||||
-lpthread \
|
||||
-lgnutls \
|
||||
-lcrypt \
|
||||
|
|
|
@ -24,13 +24,13 @@
|
|||
#include "console.h"
|
||||
#include "packet.h"
|
||||
#include "server.h"
|
||||
#include "rest.h"
|
||||
|
||||
#include "client/login.h"
|
||||
#include "client/signup.h"
|
||||
#include "client/pong.h"
|
||||
#include "client/version.h"
|
||||
#include "client/shutdown.h"
|
||||
#include "client/file.h"
|
||||
|
||||
|
||||
typedef void (*clientApi)(ClientThreadT *client, PacketDecodeDataT *data);
|
||||
|
@ -105,6 +105,7 @@ void clientStartup(void) {
|
|||
memset(_clientApiMethod, 0, sizeof(_clientApiMethod));
|
||||
|
||||
_clientApiMethod[PACKET_TYPE_CLIENT_SHUTDOWN] = clientApiClientShutdown;
|
||||
_clientApiMethod[PACKET_TYPE_FILE_REQUEST] = clientApiFileRequest;
|
||||
_clientApiMethod[PACKET_TYPE_LOGIN] = clientApiLogin;
|
||||
_clientApiMethod[PACKET_TYPE_PONG] = clientApiPong;
|
||||
_clientApiMethod[PACKET_TYPE_SIGNUP] = clientApiSignup;
|
||||
|
|
|
@ -35,6 +35,7 @@ typedef struct ClientRawPacketS {
|
|||
} ClientRawPacketT;
|
||||
|
||||
typedef struct ClientThreadS {
|
||||
// System Stuff.
|
||||
uint64_t threadIndex;
|
||||
pthread_t threadHandle;
|
||||
pthread_attr_t threadAttributes;
|
||||
|
@ -49,6 +50,8 @@ typedef struct ClientThreadS {
|
|||
double pingHigh;
|
||||
double pingLow;
|
||||
void *peer;
|
||||
// User State Stuff.
|
||||
uint8_t authenticated;
|
||||
} ClientThreadT;
|
||||
|
||||
|
||||
|
|
126
server/src/client/file.c
Normal file
126
server/src/client/file.c
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* 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 "database.h"
|
||||
|
||||
#include "file.h"
|
||||
|
||||
|
||||
static void clientApiFileRequestCacheCheck(ClientThreadT *client, PacketDecodeDataT *data);
|
||||
static void clientApiFileRequestClose(ClientThreadT *client, PacketDecodeDataT *data);
|
||||
static void clientApiFileRequestOpen(ClientThreadT *client, PacketDecodeDataT *data);
|
||||
static void clientApiFileRequestRead(ClientThreadT *client, PacketDecodeDataT *data);
|
||||
|
||||
|
||||
void clientApiFileRequest(ClientThreadT *client, PacketDecodeDataT *data) {
|
||||
FileRequestsT request = 0;
|
||||
PacketEncodeDataT encoded = { 0 };
|
||||
char *packetData = NULL;
|
||||
uint16_t length = 0;
|
||||
|
||||
// Must be logged in to do file operations.
|
||||
if (!client->authenticated) {
|
||||
// Extract the request type. We get more data later.
|
||||
packetContentUnpack(data->data, "i", &request);
|
||||
|
||||
switch (request) {
|
||||
case FILE_REQUEST_CACHE_CHECK:
|
||||
clientApiFileRequestCacheCheck(client, data);
|
||||
break;
|
||||
|
||||
case FILE_REQUEST_OPEN:
|
||||
clientApiFileRequestOpen(client, data);
|
||||
break;
|
||||
|
||||
case FILE_REQUEST_READ:
|
||||
clientApiFileRequestRead(client, data);
|
||||
break;
|
||||
|
||||
case FILE_REQUEST_CLOSE:
|
||||
clientApiFileRequestClose(client, data);
|
||||
break;
|
||||
|
||||
default:
|
||||
// No idea what they want. First value is 0 for fail, 1 for success.
|
||||
packetData = packetContentPack(&length, "i", 0);
|
||||
// Build packet.
|
||||
encoded.control = PACKET_CONTROL_DAT;
|
||||
encoded.packetType = PACKET_TYPE_FILE_RESPONSE;
|
||||
encoded.channel = data->channel;
|
||||
encoded.encrypt = 0;
|
||||
packetEncode(client->packetThreadData, &encoded, packetData, length);
|
||||
// Send it.
|
||||
packetSend(client->packetThreadData, &encoded);
|
||||
DEL(packetData);
|
||||
break;
|
||||
}
|
||||
|
||||
} // authenticated
|
||||
}
|
||||
|
||||
|
||||
static void clientApiFileRequestCacheCheck(ClientThreadT *client, PacketDecodeDataT *data) {
|
||||
FileRequestsT request = 0;
|
||||
char *path = NULL;
|
||||
char *sha256 = NULL;
|
||||
PacketEncodeDataT encoded = { 0 };
|
||||
char *packetData = NULL;
|
||||
uint16_t length = 0;
|
||||
|
||||
// Extract the request.
|
||||
packetContentUnpack(data->data, "i", &request, &path, &sha256);
|
||||
|
||||
// Look up the entry and compare SHA.
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void clientApiFileRequestClose(ClientThreadT *client, PacketDecodeDataT *data) {
|
||||
FileRequestsT request = 0;
|
||||
PacketEncodeDataT encoded = { 0 };
|
||||
char *packetData = NULL;
|
||||
uint16_t length = 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void clientApiFileRequestOpen(ClientThreadT *client, PacketDecodeDataT *data) {
|
||||
FileRequestsT request = 0;
|
||||
char *path = NULL;
|
||||
char *sha256 = NULL;
|
||||
PacketEncodeDataT encoded = { 0 };
|
||||
char *packetData = NULL;
|
||||
uint16_t length = 0;
|
||||
|
||||
// Extract the request.
|
||||
packetContentUnpack(data->data, "i", &request, &path);
|
||||
|
||||
// Look up the entry and fetch SHA.
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void clientApiFileRequestRead(ClientThreadT *client, PacketDecodeDataT *data) {
|
||||
FileRequestsT request = 0;
|
||||
PacketEncodeDataT encoded = { 0 };
|
||||
char *packetData = NULL;
|
||||
uint16_t length = 0;
|
||||
|
||||
}
|
32
server/src/client/file.h
Normal file
32
server/src/client/file.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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 FILE_H
|
||||
#define FILE_H
|
||||
|
||||
|
||||
#include "os.h"
|
||||
#include "client.h"
|
||||
|
||||
|
||||
void clientApiFileRequest(ClientThreadT *client, PacketDecodeDataT *data);
|
||||
|
||||
|
||||
#endif // FILE_H
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
|
||||
#include "rest.h"
|
||||
#include "database.h"
|
||||
|
||||
#include "login.h"
|
||||
|
||||
|
@ -28,31 +28,26 @@ void clientApiLogin(ClientThreadT *client, PacketDecodeDataT *data) {
|
|||
char *user = NULL;
|
||||
char *pass = NULL;
|
||||
PacketEncodeDataT encoded = { 0 };
|
||||
json_object *response = NULL;
|
||||
uint8_t result = 0;
|
||||
char *string = NULL;
|
||||
char *buffer = NULL;
|
||||
uint16_t length = 0;
|
||||
|
||||
// This only makes sense if they're not logged in.
|
||||
if (!client->authenticated) {
|
||||
packetContentUnpack(data->data, "ss", &user, &pass);
|
||||
response = restRequest("USER_LOGIN", "ss",
|
||||
"user", user,
|
||||
"pass", pass
|
||||
);
|
||||
result = dbUserLogin(user, pass);
|
||||
DEL(user);
|
||||
DEL(pass);
|
||||
if (response) {
|
||||
string = (char *)json_object_get_string(json_object_object_get(response, "result"));
|
||||
result = (string[0] == 't' || string[0] == 'T') ? 1 : 0;
|
||||
buffer = (char *)json_object_get_string(json_object_object_get(response, "reason"));
|
||||
}
|
||||
if (result) {
|
||||
buffer = "Logged in.";
|
||||
client->authenticated = 1;
|
||||
} else {
|
||||
// Something bad happened.
|
||||
result = 0;
|
||||
buffer = "Unknown error. Sorry.";
|
||||
}
|
||||
logWrite("Login: %d %s\r\n", result, buffer);
|
||||
packetData = packetContentPack(&length, "is", result, buffer);
|
||||
if (response) restRelease(response);
|
||||
// Build packet.
|
||||
encoded.control = PACKET_CONTROL_DAT;
|
||||
encoded.packetType = PACKET_TYPE_LOGIN_RESULT;
|
||||
|
|
|
@ -38,7 +38,6 @@ void clientApiPong(ClientThreadT *client, PacketDecodeDataT *data) {
|
|||
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;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
|
||||
#include "rest.h"
|
||||
#include "database.h"
|
||||
|
||||
#include "signup.h"
|
||||
|
||||
|
@ -37,7 +38,29 @@ void clientApiSignup(ClientThreadT *client, PacketDecodeDataT *data) {
|
|||
PacketEncodeDataT encoded = { 0 };
|
||||
uint16_t length = 0;
|
||||
|
||||
// This only makes sense if they're not logged in.
|
||||
if (!client->authenticated) {
|
||||
// Get info for the user.
|
||||
packetContentUnpack(data->data, "sssss", &email, &first, &last, &pass, &user);
|
||||
|
||||
// See if they already exist in the database.
|
||||
if (dbUserNameExists(user)) {
|
||||
// User name already exists.
|
||||
result = 0;
|
||||
buffer = "User name already exists.";
|
||||
} else {
|
||||
if (dbUserEmailExists(email)) {
|
||||
// Email already exists.
|
||||
result = 0;
|
||||
buffer = "E-mail already exists.";
|
||||
} else {
|
||||
// Create user in DB.
|
||||
if (dbUserCreate(first, last, user, pass, email) == FAIL) {
|
||||
result = 0;
|
||||
buffer = "Unknown error. Sorry.";
|
||||
} else {
|
||||
// If we found a REST endpoint, create the user there as well.
|
||||
if (__restAvailable) {
|
||||
response = restRequest("USER_CREATE", "sssss",
|
||||
"first", first,
|
||||
"last", last,
|
||||
|
@ -46,6 +69,7 @@ void clientApiSignup(ClientThreadT *client, PacketDecodeDataT *data) {
|
|||
"email", email
|
||||
);
|
||||
if (response) {
|
||||
// Success with REST.
|
||||
string = (char *)json_object_get_string(json_object_object_get(response, "result"));
|
||||
result = (string[0] == 't' || string[0] == 'T') ? 1 : 0;
|
||||
buffer = (char *)json_object_get_string(json_object_object_get(response, "reason"));
|
||||
|
@ -54,6 +78,22 @@ void clientApiSignup(ClientThreadT *client, PacketDecodeDataT *data) {
|
|||
result = 0;
|
||||
buffer = "Unknown error. Sorry.";
|
||||
}
|
||||
} else {
|
||||
// Success with DB only!
|
||||
result = 1;
|
||||
buffer = "Check your E-mail for activation instructions.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEL(first);
|
||||
DEL(last);
|
||||
DEL(user);
|
||||
DEL(pass);
|
||||
DEL(email);
|
||||
}
|
||||
|
||||
packetData = packetContentPack(&length, "is", result, buffer);
|
||||
if (response) restRelease(response);
|
||||
// Build packet.
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
|
||||
#include "rest.h"
|
||||
#include "database.h"
|
||||
#include "console.h"
|
||||
#include "array.h"
|
||||
|
||||
|
@ -31,44 +31,32 @@ void clientApiVersionBad(ClientThreadT *client, PacketDecodeDataT *data) {
|
|||
|
||||
|
||||
void clientApiVersionOkay(ClientThreadT *client, PacketDecodeDataT *data) {
|
||||
json_object *response = NULL;
|
||||
RestStringMapT *strings = NULL;
|
||||
RestIntegerMapT *integers = NULL;
|
||||
uint64_t i = 0;
|
||||
uint64_t x = 0;
|
||||
uint32_t y = 0;
|
||||
uint16_t length = 0;
|
||||
char *buffer = NULL;
|
||||
PacketEncodeDataT encoded = { 0 };
|
||||
DBTableT *record = NULL;
|
||||
DBTableT **table = NULL;
|
||||
|
||||
(void)data;
|
||||
|
||||
// Fetch string table from REST.
|
||||
response = restRequest("CONFIG_GET_STRINGS", NULL);
|
||||
if (!response) {
|
||||
consoleMessageQueue("%ld: Unable to fetch strings!\n", client->threadIndex);
|
||||
return;
|
||||
}
|
||||
strings = restHelperConfigStringMapGet(response);
|
||||
if (!strings) {
|
||||
consoleMessageQueue("%ld: Unable to map strings!\n", client->threadIndex);
|
||||
return;
|
||||
}
|
||||
restRelease(response);
|
||||
|
||||
// Send string table to client.
|
||||
for (i=0; i<(unsigned)shlen(strings); i++) {
|
||||
// Fetch string table from DB.
|
||||
if (dbTableGet("strings", table) == SUCCESS) {
|
||||
while (arrlen(table)) {
|
||||
record = table[0];
|
||||
arrdel(table, 0);
|
||||
// Strings are encoded in a single buffer as: KEY\0DATA\0
|
||||
x = strlen(strings[i].key);
|
||||
y = strlen(strings[i].value);
|
||||
x = strlen(record->name);
|
||||
y = strlen(record->data);
|
||||
length = x + y + 2;
|
||||
buffer = (char *)malloc(length);
|
||||
if (!buffer) {
|
||||
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);
|
||||
memcpy(buffer, record->name, x + 1);
|
||||
memcpy(&buffer[x + 1], record->data, y + 1);
|
||||
// Build packet.
|
||||
encoded.control = PACKET_CONTROL_DAT;
|
||||
encoded.packetType = PACKET_TYPE_STRING;
|
||||
|
@ -78,37 +66,34 @@ void clientApiVersionOkay(ClientThreadT *client, PacketDecodeDataT *data) {
|
|||
// Send it.
|
||||
packetSend(client->packetThreadData, &encoded);
|
||||
DEL(buffer);
|
||||
consoleMessageQueue("PACKET_TYPE_STRING [%s] sent.\n", strings[i].key);
|
||||
DEL(record->name);
|
||||
DEL(record->data);
|
||||
DEL(record);
|
||||
consoleMessageQueue("PACKET_TYPE_STRING [%s] sent.\n", record->name);
|
||||
}
|
||||
restHelperConfigStringMapRelease(strings);
|
||||
|
||||
// Fetch number table from REST.
|
||||
response = restRequest("CONFIG_GET_NUMBERS", NULL);
|
||||
if (!response) {
|
||||
consoleMessageQueue("%ld: Unable to fetch numbers!\n", client->threadIndex);
|
||||
arrfree(table);
|
||||
} else {
|
||||
consoleMessageQueue("%ld: Unable to fetch strings!\n", client->threadIndex);
|
||||
return;
|
||||
}
|
||||
integers = restHelperConfigIntegerMapGet(response);
|
||||
if (!integers) {
|
||||
consoleMessageQueue("%ld: Unable to map numbers!\n", client->threadIndex);
|
||||
return;
|
||||
}
|
||||
restRelease(response);
|
||||
|
||||
// Send number table to client.
|
||||
for (i=0; i<(unsigned)shlen(integers); i++) {
|
||||
// Fetch number table from DB.
|
||||
if (dbTableGet("numbers", table) == SUCCESS) {
|
||||
while (arrlen(table)) {
|
||||
record = table[0];
|
||||
arrdel(table, 0);
|
||||
// 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.
|
||||
x = strlen(integers[i].key);
|
||||
y = integers[i].value; // 64->32
|
||||
x = strlen(record->name);
|
||||
y = atol(record->data);
|
||||
length = x + 5;
|
||||
buffer = (char *)malloc(length);
|
||||
if (!buffer) {
|
||||
consoleMessageQueue("%ld: Unable to allocate buffer for number packet!\n\r", client->threadIndex);
|
||||
consoleMessageQueue("%ld: Unable to allocate buffer for number packet!\n", client->threadIndex);
|
||||
break;
|
||||
}
|
||||
memcpy(buffer, &y, 4);
|
||||
memcpy(&buffer[4], integers[i].key, x + 1);
|
||||
memcpy(&buffer[4], record->name, x + 1);
|
||||
// Build packet.
|
||||
encoded.control = PACKET_CONTROL_DAT;
|
||||
encoded.packetType = PACKET_TYPE_NUMBER;
|
||||
|
@ -118,9 +103,16 @@ void clientApiVersionOkay(ClientThreadT *client, PacketDecodeDataT *data) {
|
|||
// Send it.
|
||||
packetSend(client->packetThreadData, &encoded);
|
||||
DEL(buffer);
|
||||
consoleMessageQueue("PACKET_TYPE_NUMBER [%s] sent.\n", integers[i].key);
|
||||
DEL(record->name);
|
||||
DEL(record->data);
|
||||
DEL(record);
|
||||
consoleMessageQueue("PACKET_TYPE_NUMBER [%s] sent.\n", record->name);
|
||||
}
|
||||
arrfree(table);
|
||||
} else {
|
||||
consoleMessageQueue("%ld: Unable to fetch numbers!\n", client->threadIndex);
|
||||
return;
|
||||
}
|
||||
restHelperConfigIntegerMapRelease(integers);
|
||||
|
||||
// Build PROCEED packet.
|
||||
encoded.control = PACKET_CONTROL_DAT;
|
||||
|
|
|
@ -69,7 +69,7 @@ void consoleRun(void) {
|
|||
|
||||
terminalModeConioSet();
|
||||
|
||||
while (_running) {
|
||||
while (__running) {
|
||||
|
||||
if (kbhit()) {
|
||||
|
||||
|
@ -120,7 +120,7 @@ void consoleRun(void) {
|
|||
commandOk = 1;
|
||||
}
|
||||
if (!strcasecmp(command, "SHUTDOWN")) {
|
||||
_running = 0;
|
||||
__running = 0;
|
||||
commandOk = 1;
|
||||
}
|
||||
// Did we grok it?
|
||||
|
|
342
server/src/database.c
Normal file
342
server/src/database.c
Normal file
|
@ -0,0 +1,342 @@
|
|||
/*
|
||||
* 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 <mysql.h>
|
||||
#include <pthread.h>
|
||||
#include <crypt.h>
|
||||
|
||||
#include "array.h"
|
||||
#include "database.h"
|
||||
|
||||
|
||||
#define STATEMENT_MAX 2048
|
||||
|
||||
|
||||
static MYSQL *_sql = NULL;
|
||||
static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
|
||||
uint8_t dbConnect(char *host, uint16_t port, char *database, char *user, char *password) {
|
||||
uint8_t reconnect = 1;
|
||||
|
||||
if (_sql == NULL) {
|
||||
_sql = mysql_init(NULL);
|
||||
mysql_options(_sql, MYSQL_OPT_RECONNECT, (const void *)&reconnect);
|
||||
if (mysql_real_connect(_sql, host, user, password, database, port, NULL, 0) == NULL) {
|
||||
logWrite("dbConnect: %s\n", mysql_error(_sql));
|
||||
return FAIL;
|
||||
}
|
||||
if (pthread_mutex_init(&_mutex, NULL)) {
|
||||
logWrite("dbConnect: SQL mutex creation failed.\n");
|
||||
return FAIL;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
|
||||
uint8_t dbDisconnect(void) {
|
||||
if (_sql) {
|
||||
mysql_close(_sql);
|
||||
_sql = NULL;
|
||||
pthread_mutex_destroy(&_mutex);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
|
||||
uint8_t dbSettingsStringGet(char *key, char *value, uint32_t max) {
|
||||
char statement[STATEMENT_MAX];
|
||||
char *p = statement;
|
||||
MYSQL_RES *result = NULL;
|
||||
MYSQL_ROW row;
|
||||
int count;
|
||||
|
||||
pthread_mutex_lock(&_mutex);
|
||||
|
||||
if (!_sql) {
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
p += sprintf(p, "SELECT data FROM config WHERE name='");
|
||||
p += mysql_real_escape_string(_sql, p, key, strlen(key));
|
||||
p += sprintf(p, "'");
|
||||
if (mysql_real_query(_sql, statement, p - statement) != 0) {
|
||||
logWrite("dbConfigValueGet: %s\n", mysql_error(_sql));
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
result = mysql_store_result(_sql);
|
||||
count = mysql_num_rows(result);
|
||||
if (count != 1) {
|
||||
logWrite("dbConfigValueGet: Too many rows returned: %d.\n", count);
|
||||
mysql_free_result(result);
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if ((row = mysql_fetch_row(result)) == NULL) {
|
||||
logWrite("dbConfigValueGet: %s\n", mysql_error(_sql));
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
strncpy(value, row[0], max);
|
||||
|
||||
mysql_free_result(result);
|
||||
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint8_t dbSettingsValueGet(char *key, int32_t *value) {
|
||||
char temp[DB_CONFIG_ITEM_SIZE] = { 0 };
|
||||
|
||||
if (dbSettingsStringGet(key, temp, DB_CONFIG_ITEM_SIZE) == FAIL) return FAIL;
|
||||
|
||||
*value = atoi(temp);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint8_t dbTableGet(char *which, DBTableT **table) {
|
||||
char statement[STATEMENT_MAX];
|
||||
char *p = statement;
|
||||
MYSQL_RES *result = NULL;
|
||||
MYSQL_ROW row;
|
||||
int count;
|
||||
DBTableT *record = NULL;
|
||||
|
||||
pthread_mutex_lock(&_mutex);
|
||||
|
||||
if (!_sql) {
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
p += sprintf(p, "SELECT name, data FROM ");
|
||||
p += mysql_real_escape_string(_sql, p, which, strlen(which));
|
||||
if (mysql_real_query(_sql, statement, p - statement) != 0) {
|
||||
logWrite("dbTableGet: %s\n", mysql_error(_sql));
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
result = mysql_store_result(_sql);
|
||||
for (count = 0; count < (int)mysql_num_rows(result); count++) {
|
||||
if ((row = mysql_fetch_row(result)) == NULL) {
|
||||
logWrite("dbTableGet: %s\n", mysql_error(_sql));
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
NEW(DBTableT, record);
|
||||
record->name = strdup(row[0]);
|
||||
record->data = strdup(row[1]);
|
||||
arrput(table, record);
|
||||
}
|
||||
|
||||
mysql_free_result(result);
|
||||
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint8_t dbUserCreate(char *first, char *last, char *user, char *pass, char *email) {
|
||||
char statement[STATEMENT_MAX];
|
||||
char *p = statement;
|
||||
char *hash = NULL;
|
||||
char *salt = NULL;
|
||||
|
||||
pthread_mutex_lock(&_mutex);
|
||||
|
||||
if (!_sql) {
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// Not thread safe. Protected by SQL _mutex.
|
||||
salt = crypt_gensalt(0, 0, 0, 0);
|
||||
hash = crypt(pass, salt);
|
||||
|
||||
// We don't pass 'enabled' into the database and instead rely on the default value.
|
||||
p += sprintf(p, "INSERT INTO users (first, last, user, pass, email) VALUES ('");
|
||||
p += mysql_real_escape_string(_sql, p, first, strlen(first));
|
||||
p += sprintf(p, "', '");
|
||||
p += mysql_real_escape_string(_sql, p, last, strlen(last));
|
||||
p += sprintf(p, "', '");
|
||||
p += mysql_real_escape_string(_sql, p, user, strlen(user));
|
||||
p += sprintf(p, "', '");
|
||||
p += mysql_real_escape_string(_sql, p, hash, strlen(hash));
|
||||
p += sprintf(p, "', '");
|
||||
p += mysql_real_escape_string(_sql, p, email, strlen(email));
|
||||
p += sprintf(p, "')");
|
||||
if (mysql_real_query(_sql, statement, p - statement) != 0) {
|
||||
logWrite("dbUserCreate: %s\n", mysql_error(_sql));
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint8_t dbUserEmailExists(char *email) {
|
||||
char statement[STATEMENT_MAX];
|
||||
char *p = statement;
|
||||
MYSQL_RES *result = NULL;
|
||||
int count;
|
||||
|
||||
pthread_mutex_lock(&_mutex);
|
||||
|
||||
if (!_sql) {
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
p += sprintf(p, "SELECT id FROM users WHERE email='");
|
||||
p += mysql_real_escape_string(_sql, p, email, strlen(email));
|
||||
p += sprintf(p, "'");
|
||||
if (mysql_real_query(_sql, statement, p - statement) != 0) {
|
||||
logWrite("dbUserEmailExists: %s\n", mysql_error(_sql));
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
result = mysql_store_result(_sql);
|
||||
count = mysql_num_rows(result);
|
||||
if (count != 1) {
|
||||
logWrite("dbUserEmailExists: Too many rows returned: %d.\n", count);
|
||||
mysql_free_result(result);
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
mysql_free_result(result);
|
||||
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint8_t dbUserLogin(char *user, char *password) {
|
||||
char statement[STATEMENT_MAX];
|
||||
char *p = statement;
|
||||
MYSQL_RES *result = NULL;
|
||||
MYSQL_ROW row;
|
||||
int count;
|
||||
uint8_t status = FAIL;
|
||||
|
||||
pthread_mutex_lock(&_mutex);
|
||||
|
||||
if (!_sql) {
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
p += sprintf(p, "SELECT pass FROM users WHERE user='");
|
||||
p += mysql_real_escape_string(_sql, p, user, strlen(user));
|
||||
p += sprintf(p, "' AND enabled = 1");
|
||||
if (mysql_real_query(_sql, statement, p - statement) != 0) {
|
||||
logWrite("dbUserLogin: %s\n", mysql_error(_sql));
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
result = mysql_store_result(_sql);
|
||||
count = mysql_num_rows(result);
|
||||
if (count != 1) {
|
||||
logWrite("dbUserLogin: Too many rows returned: %d.\n", count);
|
||||
mysql_free_result(result);
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if ((row = mysql_fetch_row(result)) == NULL) {
|
||||
logWrite("dbUserLogin: %s\n", mysql_error(_sql));
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// Not thread safe. Protected by SQL _mutex.
|
||||
p = crypt(password, row[0]);
|
||||
if (strcmp(p, password) == 0) status = SUCCESS;
|
||||
|
||||
mysql_free_result(result);
|
||||
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
uint8_t dbUserNameExists(char *user) {
|
||||
char statement[STATEMENT_MAX];
|
||||
char *p = statement;
|
||||
MYSQL_RES *result = NULL;
|
||||
int count;
|
||||
|
||||
pthread_mutex_lock(&_mutex);
|
||||
|
||||
if (!_sql) {
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
p += sprintf(p, "SELECT id FROM users WHERE user='");
|
||||
p += mysql_real_escape_string(_sql, p, user, strlen(user));
|
||||
p += sprintf(p, "'");
|
||||
if (mysql_real_query(_sql, statement, p - statement) != 0) {
|
||||
logWrite("dbUserNameExists: %s\n", mysql_error(_sql));
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
result = mysql_store_result(_sql);
|
||||
count = mysql_num_rows(result);
|
||||
if (count != 1) {
|
||||
logWrite("dbUserNameExists: Too many rows returned: %d.\n", count);
|
||||
mysql_free_result(result);
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
mysql_free_result(result);
|
||||
|
||||
pthread_mutex_unlock(&_mutex);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
48
server/src/database.h
Normal file
48
server/src/database.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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 DATABASE_H
|
||||
#define DATABASE_H
|
||||
|
||||
|
||||
#include "os.h"
|
||||
|
||||
|
||||
#define DB_CONFIG_ITEM_SIZE 1024
|
||||
|
||||
|
||||
typedef struct DBTableS {
|
||||
char *name;
|
||||
char *data;
|
||||
} DBTableT;
|
||||
|
||||
|
||||
uint8_t dbConnect(char *host, uint16_t port, char *database, char *user, char *password);
|
||||
uint8_t dbDisconnect(void);
|
||||
uint8_t dbSettingsStringGet(char *key, char *value, uint32_t max);
|
||||
uint8_t dbSettingsValueGet(char *key, int32_t *value);
|
||||
uint8_t dbTableGet(char *which, DBTableT **table);
|
||||
uint8_t dbUserCreate(char *first, char *last, char *user, char *pass, char *email);
|
||||
uint8_t dbUserEmailExists(char *email);
|
||||
uint8_t dbUserLogin(char *user, char *password);
|
||||
uint8_t dbUserNameExists(char *user);
|
||||
|
||||
|
||||
#endif // DATABASE_H
|
|
@ -23,18 +23,23 @@
|
|||
#include "stddclmr.h"
|
||||
#include "server.h"
|
||||
#include "rest.h"
|
||||
#include "database.h"
|
||||
|
||||
#include "thirdparty/ini/src/ini.h"
|
||||
|
||||
|
||||
uint8_t _running = 1; // Exported in os.h
|
||||
// Exported in os.h
|
||||
uint8_t __running = 1;
|
||||
uint8_t __restAvailable = 1;
|
||||
|
||||
|
||||
// "Config" items come from the INI file. "Settings" are from the database.
|
||||
|
||||
static char *_configServer = NULL;
|
||||
static uint16_t _configPort = 0;
|
||||
static char *_configDatabase = NULL;
|
||||
static char *_configUser = NULL;
|
||||
static char *_configPassword = NULL;
|
||||
static char *_configREST = NULL;
|
||||
|
||||
|
||||
static void configRead(char *file);
|
||||
|
@ -44,18 +49,24 @@ static void configWrite(char *file);
|
|||
static void configRead(char *file) {
|
||||
ini_t *ini = NULL;
|
||||
|
||||
// Numeric defaults.
|
||||
_configPort = 16550;
|
||||
|
||||
ini = ini_load(file);
|
||||
if (ini) {
|
||||
ini_sget(ini, "SERVER", "PORT", "%d", &_configPort);
|
||||
_configServer = strdup(ini_get(ini, "SERVER", "HOST"));
|
||||
_configDatabase = strdup(ini_get(ini, "SERVER", "DATA"));
|
||||
_configUser = strdup(ini_get(ini, "SERVER", "USER"));
|
||||
_configPassword = strdup(ini_get(ini, "SERVER", "PASS"));
|
||||
_configREST = strdup(ini_get(ini, "SERVER", "REST"));
|
||||
ini_free(ini);
|
||||
}
|
||||
|
||||
// String defaults.
|
||||
if (!_configServer) strdup("kanga.world");
|
||||
if (!_configDatabase) strdup("kpmpgsmkii");
|
||||
if (!_configUser) strdup("");
|
||||
if (!_configPassword) strdup("");
|
||||
if (!_configREST) strdup("http://localhost:8000/api/kp/kangaworld/v1");
|
||||
}
|
||||
|
||||
|
||||
|
@ -66,17 +77,22 @@ static void configWrite(char *file) {
|
|||
if (out) {
|
||||
fprintf(out,
|
||||
"[SERVER]\n"
|
||||
"REST=%s\n"
|
||||
"HOST=%s\n"
|
||||
"PORT=%d\n"
|
||||
"DATA=%s\n"
|
||||
"USER=%s\n"
|
||||
"PASS=%s\n",
|
||||
_configREST,
|
||||
_configServer,
|
||||
_configPort,
|
||||
_configDatabase,
|
||||
_configUser,
|
||||
_configPassword
|
||||
);
|
||||
fclose(out);
|
||||
}
|
||||
|
||||
DEL(_configREST);
|
||||
DEL(_configServer);
|
||||
DEL(_configDatabase);
|
||||
DEL(_configUser);
|
||||
DEL(_configPassword);
|
||||
}
|
||||
|
@ -85,10 +101,14 @@ static void configWrite(char *file) {
|
|||
int main(int argc, char *argv[]) {
|
||||
|
||||
char *configFile = NULL;
|
||||
char hostname[256] = { 0 };
|
||||
char settingsFile[DB_CONFIG_ITEM_SIZE] = { 0 };
|
||||
char settingsRest[DB_CONFIG_ITEM_SIZE] = { 0 };
|
||||
char settingsUser[DB_CONFIG_ITEM_SIZE] = { 0 };
|
||||
char settingsPass[DB_CONFIG_ITEM_SIZE] = { 0 };
|
||||
int64_t settingsMaxClients = 0;
|
||||
int64_t settingsPortNumber = 0;
|
||||
int64_t settingsClientVersion = 0;
|
||||
json_object *response = NULL;
|
||||
|
||||
(void)argc;
|
||||
|
||||
|
@ -96,18 +116,39 @@ int main(int argc, char *argv[]) {
|
|||
logOpenByHandle(memoryLogHandleGet());
|
||||
logWrite("%s", "Kangaroo Punch MultiPlayer Game Server Mark II\n");
|
||||
logWrite("%s", "Copyright (C) 2020-2021 Scott Duensing scott@kangaroopunch.com\n\n");
|
||||
|
||||
// Load our config.
|
||||
configFile = utilAppNameWithNewExtensionGet(argv[0], "ini");
|
||||
configRead(configFile);
|
||||
|
||||
if (!restStartup(_configREST, _configUser, _configPassword)) {
|
||||
utilDie("Unable to start REST.\n");
|
||||
// Find our hostname.
|
||||
gethostname(hostname, 256);
|
||||
|
||||
// Get settings entries for this host.
|
||||
snprintf(settingsFile, DB_CONFIG_ITEM_SIZE, "%s_file", hostname);
|
||||
snprintf(settingsRest, DB_CONFIG_ITEM_SIZE, "%s_rest", hostname);
|
||||
snprintf(settingsUser, DB_CONFIG_ITEM_SIZE, "%s_user", hostname);
|
||||
snprintf(settingsPass, DB_CONFIG_ITEM_SIZE, "%s_pass", hostname);
|
||||
|
||||
// Connect to database.
|
||||
if (!dbConnect(_configServer, _configPort, _configDatabase, _configUser, _configPassword)) {
|
||||
utilDie("Unable to connect to database.\n");
|
||||
}
|
||||
|
||||
response = restRequest("CONFIG_GET_CONFIG", NULL);
|
||||
settingsMaxClients = restHelperConfigIntegerGet(response, "maxClients", 2);
|
||||
settingsPortNumber = restHelperConfigIntegerGet(response, "portNumber", 16550);
|
||||
settingsClientVersion = restHelperConfigIntegerGet(response, "clientVersion", 1);
|
||||
restRelease(response);
|
||||
// Fetch settings needed to start server.
|
||||
if (!dbSettingsValueGet("maxClients", (int32_t *)&settingsMaxClients)) utilDie("Unable to load maxClients.\n");
|
||||
if (!dbSettingsValueGet("portNumber", (int32_t *)&settingsPortNumber)) utilDie("Unable to load portNumber.\n");
|
||||
if (!dbSettingsValueGet("clientVersion", (int32_t *)&settingsClientVersion)) utilDie("Unable to load clientVersion.\n");
|
||||
if (!dbSettingsStringGet(settingsFile, settingsFile, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load file location.\n");
|
||||
if (!dbSettingsStringGet(settingsRest, settingsRest, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load REST URL.\n");
|
||||
if (!dbSettingsStringGet(settingsUser, settingsUser, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load REST user.\n");
|
||||
if (!dbSettingsStringGet(settingsPass, settingsPass, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load REST password.\n");
|
||||
|
||||
// Start up REST.
|
||||
if (!restStartup(settingsRest, settingsUser, settingsPass)) {
|
||||
logWrite("Unable to locate REST API. Web site integration disabled.\n");
|
||||
__restAvailable = 0;
|
||||
}
|
||||
|
||||
clientStartup();
|
||||
serverStartup(settingsPortNumber, settingsMaxClients);
|
||||
|
@ -117,7 +158,7 @@ int main(int argc, char *argv[]) {
|
|||
consoleRun();
|
||||
|
||||
// Console closed. Stop server.
|
||||
_running = 0;
|
||||
__running = 0;
|
||||
|
||||
// Wait for all running threads to shut down.
|
||||
logWrite("Shutting down.\n");
|
||||
|
|
|
@ -48,7 +48,9 @@
|
|||
#define FAIL 0
|
||||
|
||||
|
||||
extern uint8_t _running; // Declared in main.c
|
||||
// Declared in main.c
|
||||
extern uint8_t __running;
|
||||
extern uint8_t __restAvailable;
|
||||
|
||||
|
||||
#endif // OS_H
|
||||
|
|
|
@ -46,126 +46,6 @@ static unsigned long restThreadtIdGet(void);
|
|||
static json_object *restUrlPost(json_object *request);
|
||||
|
||||
|
||||
int64_t restHelperConfigIntegerGet(json_object *object, char *name, int64_t defaultValue) {
|
||||
uint64_t i = 0;
|
||||
json_object *config = NULL;
|
||||
json_object *data = NULL;
|
||||
json_object *item = NULL;
|
||||
int64_t result = defaultValue;
|
||||
|
||||
config = json_object_object_get(object, "payload");
|
||||
if (config) {
|
||||
data = json_object_object_get(config, "data");
|
||||
if (data) {
|
||||
for (i=0; i<json_object_array_length(data); i++) {
|
||||
item = json_object_array_get_idx(data, i);
|
||||
if (strcmp(name, json_object_get_string(json_object_object_get(item, "name"))) == 0) {
|
||||
// Because of the way Kirby is sending us REST data, everything is a string. We have to convert it to an integer.
|
||||
result = atol(json_object_get_string(json_object_object_get(item, "data")));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
RestIntegerMapT *restHelperConfigIntegerMapGet(json_object *object) {
|
||||
uint64_t i = 0;
|
||||
json_object *config = NULL;
|
||||
json_object *data = NULL;
|
||||
json_object *item = NULL;
|
||||
RestIntegerMapT *result = NULL;
|
||||
|
||||
sh_new_strdup(result);
|
||||
|
||||
config = json_object_object_get(object, "payload");
|
||||
if (config) {
|
||||
data = json_object_object_get(config, "data");
|
||||
if (data) {
|
||||
for (i=0; i<json_object_array_length(data); i++) {
|
||||
item = json_object_array_get_idx(data, i);
|
||||
shput(result, json_object_get_string(json_object_object_get(item, "name")), atol(json_object_get_string(json_object_object_get(item, "data"))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void restHelperConfigIntegerMapRelease(RestIntegerMapT *map) {
|
||||
while (shlen(map) > 0) {
|
||||
shdel(map, map[0].key);
|
||||
}
|
||||
shfree(map);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
char *restHelperConfigStringGet(json_object *object, char *name, char *defaultValue) {
|
||||
uint64_t i = 0;
|
||||
json_object *config = NULL;
|
||||
json_object *data = NULL;
|
||||
json_object *item = NULL;
|
||||
char *result = strdup(defaultValue);
|
||||
|
||||
config = json_object_object_get(object, "payload");
|
||||
if (config) {
|
||||
data = json_object_object_get(config, "data");
|
||||
if (data) {
|
||||
for (i=0; i<json_object_array_length(data); i++) {
|
||||
item = json_object_array_get_idx(data, i);
|
||||
if (strcmp(name, json_object_get_string(json_object_object_get(item, "name"))) == 0) {
|
||||
DEL(result);
|
||||
result = strdup(json_object_get_string(json_object_object_get(item, "data")));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
RestStringMapT *restHelperConfigStringMapGet(json_object *object) {
|
||||
uint64_t i = 0;
|
||||
json_object *config = NULL;
|
||||
json_object *data = NULL;
|
||||
json_object *item = NULL;
|
||||
RestStringMapT *result = NULL;
|
||||
|
||||
sh_new_strdup(result);
|
||||
|
||||
config = json_object_object_get(object, "payload");
|
||||
if (config) {
|
||||
data = json_object_object_get(config, "data");
|
||||
if (data) {
|
||||
for (i=0; i<json_object_array_length(data); i++) {
|
||||
item = json_object_array_get_idx(data, i);
|
||||
shput(result, json_object_get_string(json_object_object_get(item, "name")), strdup(json_object_get_string(json_object_object_get(item, "data"))));
|
||||
//consoleMessageQueue("[%s]=[%s]\n", json_object_get_string(json_object_object_get(item, "name")), json_object_get_string(json_object_object_get(item, "data")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void restHelperConfigStringMapRelease(RestStringMapT *map) {
|
||||
while (shlen(map) > 0) {
|
||||
DEL(map[0].value);
|
||||
shdel(map, map[0].key);
|
||||
}
|
||||
shfree(map);
|
||||
}
|
||||
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function" // It's used, but the compiler isn't picking it up because it's via a callback.
|
||||
static void restMutexLocker(int mode, int n, const char *file, int line) {
|
||||
|
@ -275,8 +155,9 @@ void restShutdown(void) {
|
|||
|
||||
|
||||
uint8_t restStartup(char *url, char *user, char *password) {
|
||||
uint64_t i;
|
||||
json_object *response;
|
||||
uint64_t i = 0;
|
||||
json_object *response = NULL;
|
||||
uint8_t result = SUCCESS;
|
||||
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
|
@ -302,10 +183,10 @@ uint8_t restStartup(char *url, char *user, char *password) {
|
|||
|
||||
// Is the remote server there?
|
||||
response = restRequest("API_AVAILABLE", NULL);
|
||||
if (!response) return FAIL;
|
||||
if (!response) result = FAIL;
|
||||
restRelease(response);
|
||||
|
||||
return SUCCESS;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -25,23 +25,6 @@
|
|||
#include <json-c/json.h>
|
||||
|
||||
|
||||
typedef struct RestStringMapS {
|
||||
char *key;
|
||||
char *value;
|
||||
} RestStringMapT;
|
||||
|
||||
typedef struct RestIntegerMapS {
|
||||
char *key;
|
||||
int64_t value;
|
||||
} RestIntegerMapT;
|
||||
|
||||
|
||||
int64_t restHelperConfigIntegerGet(json_object *object, char *name, int64_t defaultValue);
|
||||
RestIntegerMapT *restHelperConfigIntegerMapGet(json_object *object);
|
||||
void restHelperConfigIntegerMapRelease(RestIntegerMapT *map);
|
||||
//char *restHelperConfigStringGet(json_object *object, char *name, char *defaultValue);
|
||||
RestStringMapT *restHelperConfigStringMapGet(json_object *object);
|
||||
void restHelperConfigStringMapRelease(RestStringMapT *map);
|
||||
void restRelease(json_object *object);
|
||||
json_object *restRequest(char *command, char *format, ...);
|
||||
void restShutdown(void);
|
||||
|
|
|
@ -25,9 +25,15 @@
|
|||
#include "packet.h"
|
||||
|
||||
|
||||
ENetHost *_server = NULL;
|
||||
pthread_t _serverThreadHandle = { 0 };
|
||||
void *_serverThreadStatus = NULL;
|
||||
#define CLIENT_OVERFLOW 8
|
||||
|
||||
|
||||
static ENetHost *_server = NULL;
|
||||
static pthread_t _serverThreadHandle = { 0 };
|
||||
static void *_serverThreadStatus = NULL;
|
||||
static uint32_t _serverMaxClients = 0;
|
||||
static uint32_t _serverClients = 0;
|
||||
static pthread_mutex_t _serverMutex = { 0 };
|
||||
|
||||
|
||||
static void serverPacketSender(char *data, uint32_t length, void *userData);
|
||||
|
@ -46,6 +52,10 @@ void serverDisconnectClient(ClientThreadT *client) {
|
|||
pthread_join(client->threadHandle, &status);
|
||||
// Hang up.
|
||||
enet_peer_reset(peer);
|
||||
// Adjust client count.
|
||||
pthread_mutex_lock(&_serverMutex);
|
||||
_serverClients--;
|
||||
pthread_mutex_unlock(&_serverMutex);
|
||||
}
|
||||
|
||||
|
||||
|
@ -61,6 +71,7 @@ static void serverPacketSender(char *data, uint32_t length, void *userData) {
|
|||
|
||||
void serverShutdown(void) {
|
||||
pthread_join(_serverThreadHandle, &_serverThreadStatus);
|
||||
pthread_mutex_destroy(&_serverMutex);
|
||||
enet_host_destroy(_server);
|
||||
}
|
||||
|
||||
|
@ -69,6 +80,8 @@ void serverStartup(uint32_t port, uint32_t maxClients) {
|
|||
ENetAddress address = { 0 };
|
||||
pthread_attr_t serverThreadAttributes = { 0 };
|
||||
|
||||
_serverMaxClients = maxClients;
|
||||
|
||||
// Tell the packet code how to send packets.
|
||||
packetSenderRegister(serverPacketSender);
|
||||
|
||||
|
@ -76,7 +89,7 @@ void serverStartup(uint32_t port, uint32_t maxClients) {
|
|||
address.host = ENET_HOST_ANY;
|
||||
address.port = port;
|
||||
_server = enet_host_create(&address, /* the address to bind the server host to */
|
||||
maxClients, /* allow up to XX clients and/or outgoing connections */
|
||||
maxClients + CLIENT_OVERFLOW, /* allow up to XX clients and/or outgoing connections (some extra so we can send "server full" messages) */
|
||||
1, /* allow up to 2 channels to be used, 0 and 1 */
|
||||
0, /* assume any amount of incoming bandwidth */
|
||||
0 /* assume any amount of outgoing bandwidth */
|
||||
|
@ -84,6 +97,8 @@ void serverStartup(uint32_t port, uint32_t maxClients) {
|
|||
if (_server == NULL) utilDie("Unable to open server listening port.\n");
|
||||
|
||||
// Start server thread.
|
||||
memset(&_serverMutex, 0, sizeof(pthread_mutex_t));
|
||||
pthread_mutex_init(&_serverMutex, NULL);
|
||||
if (pthread_attr_init(&serverThreadAttributes) != 0) utilDie("Unable to create server thread attributes.\n");
|
||||
pthread_attr_setdetachstate(&serverThreadAttributes, PTHREAD_CREATE_JOINABLE);
|
||||
if (pthread_create(&_serverThreadHandle, &serverThreadAttributes, serverThread, (void *)_server) != 0) utilDie("Unable to start server thread.\n");
|
||||
|
@ -99,21 +114,35 @@ void *serverThread(void *data) {
|
|||
size_t i = 0;
|
||||
char buffer[2048] = { 0 };
|
||||
|
||||
while (_running) {
|
||||
while (__running) {
|
||||
while (enet_host_service(server, &event, 1) > 0) {
|
||||
switch (event.type) {
|
||||
case ENET_EVENT_TYPE_NONE:
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_CONNECT:
|
||||
// Is the server full?
|
||||
if (_serverClients >= _serverMaxClients) {
|
||||
// Send full banner to client.
|
||||
packet = enet_packet_create("KPMPGSMKII\rFULL\r", 16, ENET_PACKET_FLAG_RELIABLE);
|
||||
enet_peer_send(event.peer, 0, packet);
|
||||
break;
|
||||
}
|
||||
// Create new client.
|
||||
pthread_mutex_lock(&_serverMutex);
|
||||
_serverClients++;
|
||||
pthread_mutex_unlock(&_serverMutex);
|
||||
NEW(ClientThreadT, client);
|
||||
if (!client) utilDie("Unable to allocate new client.\n");
|
||||
// System Stuff.
|
||||
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.
|
||||
}
|
||||
for (i=0; i<PING_STATS_SIZE; i++) client->pingStats[i] = 0;
|
||||
memset(&client->packetQueueMutex, 0, sizeof(pthread_mutex_t));
|
||||
pthread_mutex_init(&client->packetQueueMutex, NULL);
|
||||
client->threadIndex = nextIndex++;
|
||||
client->running = 1;
|
||||
client->packetQueue = NULL;
|
||||
|
@ -122,9 +151,8 @@ void *serverThread(void *data) {
|
|||
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);
|
||||
// User State Stuff.
|
||||
client->authenticated = 0;
|
||||
// Keep our client in the peer data for later.
|
||||
event.peer->data = (void *)client;
|
||||
// Make new thread for this client.
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
*/
|
||||
|
||||
|
||||
// ***TODO*** On DOS, open and close the log on every write so the file gets updated and we still have data on a crash.
|
||||
|
||||
|
||||
#include "log.h"
|
||||
|
||||
|
||||
|
@ -27,6 +30,7 @@ static char *_logBuffer = NULL;
|
|||
static uint16_t _logBufferSize = 0;
|
||||
static logCallback _logCallback = NULL;
|
||||
|
||||
|
||||
void logCallbackSet(logCallback callback) {
|
||||
_logCallback = callback;
|
||||
}
|
||||
|
|
|
@ -41,11 +41,13 @@ typedef enum PacketTypeE {
|
|||
|
||||
// Packets received by only the server:
|
||||
PACKET_TYPE_CLIENT_SHUTDOWN,
|
||||
PACKET_TYPE_FILE_REQUEST,
|
||||
PACKET_TYPE_LOGIN,
|
||||
PACKET_TYPE_PONG,
|
||||
PACKET_TYPE_SIGNUP,
|
||||
|
||||
// Packets received by only the client:
|
||||
PACKET_TYPE_FILE_RESPONSE,
|
||||
PACKET_TYPE_LOGIN_RESULT,
|
||||
PACKET_TYPE_NUMBER,
|
||||
PACKET_TYPE_PING,
|
||||
|
@ -58,5 +60,13 @@ typedef enum PacketTypeE {
|
|||
PACKET_TYPE_COUNT
|
||||
} PacketTypeT;
|
||||
|
||||
typedef enum FileRequestsE {
|
||||
FILE_REQUEST_UNKNOWN = 0,
|
||||
FILE_REQUEST_CACHE_CHECK,
|
||||
FILE_REQUEST_OPEN,
|
||||
FILE_REQUEST_READ,
|
||||
FILE_REQUEST_CLOSE
|
||||
} FileRequestsT;
|
||||
|
||||
|
||||
#endif // PACKETS_H
|
||||
|
|
Loading…
Add table
Reference in a new issue