mirror of
https://github.com/slawkens/myaac.git
synced 2026-04-25 20:03:32 +02:00
Compare commits
36 Commits
blacktek-t
...
feature/2f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed88f9f401 | ||
|
|
c2424df7a4 | ||
|
|
4d2ed93b31 | ||
|
|
7471c49793 | ||
|
|
381d5bb884 | ||
|
|
234e17654b | ||
|
|
1da771e3ca | ||
|
|
4d7fe0bd58 | ||
|
|
e2c9c2bbe0 | ||
|
|
04b37b4356 | ||
|
|
bf70595095 | ||
|
|
668f00e746 | ||
|
|
bbc8bef008 | ||
|
|
5e5fd43233 | ||
|
|
867e3e2c38 | ||
|
|
1975fb8ebe | ||
|
|
a44e2d6ebe | ||
|
|
babd822171 | ||
|
|
21e2eed640 | ||
|
|
2e4a8c3d3d | ||
|
|
9f64d7834f | ||
|
|
7d71bc2fee | ||
|
|
fdd0de8602 | ||
|
|
abee4b3962 | ||
|
|
fbdb6890b9 | ||
|
|
041f58ed11 | ||
|
|
03c7dd0002 | ||
|
|
e435062025 | ||
|
|
ecc9bd4042 | ||
|
|
797377e428 | ||
|
|
96b5df9d74 | ||
|
|
b3dfc56c96 | ||
|
|
96d6e04bd2 | ||
|
|
9146eee327 | ||
|
|
3d97fa0719 | ||
|
|
a66cafceab |
@@ -1,18 +1,5 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## [1.8.9 - 06.04.2026]
|
|
||||||
### Added
|
|
||||||
* Settings: Possibility to add custom HTML for the head and body tags like Google Analytics code etc. (https://github.com/slawkens/myaac/commit/108e83806df5686a06826931ed5e243c19cbe130)
|
|
||||||
* Add command: give-admin (https://github.com/slawkens/myaac/commit/9fa9ec746c4b344387a21f21886c2251319806fc)
|
|
||||||
* Usage: php aac give:admin slawkens@gmail.com
|
|
||||||
Parameter: account email, name or id
|
|
||||||
* It's admin for the website, not the GM for the game! For that, go into the admin panel and change the group manually
|
|
||||||
* Add page load time to an Admin Panel footer (https://github.com/slawkens/myaac/commit/4ae2fdd0dfcd56697612395c14aecc2dfd33b1c3)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
* Better character name validation, like in the original game website (#356)
|
|
||||||
* Install: don't suggest deleting of install folder - it's not required (https://github.com/slawkens/myaac/commit/5fcde4708a39255cf68edc8c43f2ac6597e2601d)
|
|
||||||
|
|
||||||
## [1.8.8 - 31.01.2026]
|
## [1.8.8 - 31.01.2026]
|
||||||
### Added
|
### Added
|
||||||
* Change Comment: Add missing hooks - patched from 0.8 (https://github.com/slawkens/myaac/commit/a60a23b84f61d41d1503073b52e01e3120f6d92a)
|
* Change Comment: Add missing hooks - patched from 0.8 (https://github.com/slawkens/myaac/commit/a60a23b84f61d41d1503073b52e01e3120f6d92a)
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
## [2.0-dev - x.x.2025]
|
## [2.0-dev - x.x.2025]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
* Menus: Add an "access" option to Menus (#340)
|
* Add an "access" option to Menus (#340)
|
||||||
* Possibility to hide menus for unauthorized users
|
* Possibility to hide menus for unauthorized users
|
||||||
* Settings: Add Reset button (https://github.com/slawkens/myaac/commit/7104c2258fd724a55239821b46a616dab845b22a, https://github.com/slawkens/myaac/commit/e274b8350451a20c24e652ea05ed1964ebb86b54)
|
* Add the possibility to fetch skills in the getTopPlayers function (#347)
|
||||||
* New Setting: block create account spam by ip (https://github.com/slawkens/myaac/commit/54265f42e987522803288477952d6e5c4daeeb24)
|
|
||||||
* Functions: Add the possibility to fetch skills, balance and frags in the getTopPlayers function (#347)
|
|
||||||
* Plugins: autoload init-priority option (https://github.com/slawkens/myaac/commit/f1aa12840875960849fa0c99a2bbe0ad2949bbec)
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* Better handling of vocations: (#345)
|
* Better handling of vocations: (#345)
|
||||||
@@ -14,7 +11,6 @@
|
|||||||
* Support for Monk vocation
|
* Support for Monk vocation
|
||||||
* Better gallery, loads images from images/gallery folder
|
* Better gallery, loads images from images/gallery folder
|
||||||
* Reworked account action logs to use a single IP column as varchar(45) for both ipv4 and ipv6 (#289)
|
* Reworked account action logs to use a single IP column as varchar(45) for both ipv4 and ipv6 (#289)
|
||||||
* Make myaac_config table columns bigger (https://github.com/slawkens/myaac/commit/2c62a97160a3ffe9976ee5bd1d770a0abc576742)
|
|
||||||
* Admin Panel: save menu collapse state (https://github.com/slawkens/myaac/commit/55da00520df7463a1d1ca41931df1598e9f2ffeb)
|
* Admin Panel: save menu collapse state (https://github.com/slawkens/myaac/commit/55da00520df7463a1d1ca41931df1598e9f2ffeb)
|
||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Official website: https://my-aac.org
|
|||||||
[](https://github.com/slawkens/myaac/actions)
|
[](https://github.com/slawkens/myaac/actions)
|
||||||
[](https://opensource.org/licenses/gpl-license)
|
[](https://opensource.org/licenses/gpl-license)
|
||||||
[](https://github.com/slawkens/myaac/releases)
|
[](https://github.com/slawkens/myaac/releases)
|
||||||
[](https://discord.gg/aVagGPJt3g)
|
[](https://discord.gg/2J39Wus)
|
||||||
[](https://github.com/slawkens/myaac/issues?q=is%3Aissue+is%3Aclosed)
|
[](https://github.com/slawkens/myaac/issues?q=is%3Aissue+is%3Aclosed)
|
||||||
|
|
||||||
| Version | Status | Branch | Requirements |
|
| Version | Status | Branch | Requirements |
|
||||||
@@ -86,6 +86,12 @@ Look: [Contributing](https://docs.my-aac.org/misc/contributing) in our wiki.
|
|||||||
|
|
||||||
If you have a great idea or want to contribute to the project - visit our website at https://www.my-aac.org
|
If you have a great idea or want to contribute to the project - visit our website at https://www.my-aac.org
|
||||||
|
|
||||||
|
## Project supported by JetBrains
|
||||||
|
|
||||||
|
Many thanks to Jetbrains for kindly providing a license for me to work on this and other open-source projects.
|
||||||
|
|
||||||
|
[](https://www.jetbrains.com/?from=https://github.com/slawkens)
|
||||||
|
|
||||||
### License
|
### License
|
||||||
|
|
||||||
This program and all associated files are released under the GNU Public License.
|
This program and all associated files are released under the GNU Public License.
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
use MyAAC\Forum;
|
use MyAAC\Forum;
|
||||||
use MyAAC\Models\Player;
|
use MyAAC\Models\Player;
|
||||||
use MyAAC\Server\Outfits;
|
|
||||||
use MyAAC\Server\XML\Vocations;
|
use MyAAC\Server\XML\Vocations;
|
||||||
|
|
||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
@@ -660,14 +659,14 @@ else if (isset($_REQUEST['search'])) {
|
|||||||
<div class="col-12 col-sm-12 col-lg-6">
|
<div class="col-12 col-sm-12 col-lg-6">
|
||||||
<label for="look_type" class="control-label">Type:</label>
|
<label for="look_type" class="control-label">Type:</label>
|
||||||
<?php
|
<?php
|
||||||
$outfits = Outfits::get();
|
$outfitlist = null;
|
||||||
if ($outfits) { ?>
|
$outfitlist = Outfits_loadfromXML();
|
||||||
|
if ($outfitlist) { ?>
|
||||||
<select name="look_type" id="look_type" class="form-control custom-select">
|
<select name="look_type" id="look_type" class="form-control custom-select">
|
||||||
<?php
|
<?php
|
||||||
foreach ($outfits as $outfit) {
|
foreach ($outfitlist as $_id => $outfit) {
|
||||||
if ($outfit['enabled']) {
|
if ($outfit['enabled'] == 'yes') ;
|
||||||
echo '<option value=' . $outfit['id'] . ($outfit['id'] == $player->getLookType() ? ' selected' : '') . '>' . $outfit['name'] . ' - ' . ($outfit['sex'] == SEX_MALE ? 'Male' : 'Female') . '</option>';
|
echo '<option value=' . $outfit['id'] . ($outfit['id'] == $player->getLookType() ? ' selected' : '') . '>' . $outfit['name'] . ' - ' . ($outfit['type'] == 1 ? 'Male' : 'Female') . '</option>';
|
||||||
}
|
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -46,15 +46,6 @@ if (!is_array($settingsFile)) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($_POST['reset']) && $_POST['reset'] == '1') {
|
|
||||||
$settings = Settings::getInstance();
|
|
||||||
|
|
||||||
$settings->deleteFromDatabase($settingsFile['key']);
|
|
||||||
$settings->clearCache();
|
|
||||||
|
|
||||||
success('Settings for this plugin has been reset.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$settingsKeyName = ($plugin == 'core' ? $plugin : $settingsFile['key']);
|
$settingsKeyName = ($plugin == 'core' ? $plugin : $settingsFile['key']);
|
||||||
|
|
||||||
$title = ($plugin == 'core' ? 'Settings' : 'Plugin Settings - ' . $settingsFile['name']);
|
$title = ($plugin == 'core' ? 'Settings' : 'Plugin Settings - ' . $settingsFile['name']);
|
||||||
@@ -66,5 +57,4 @@ $twig->display('admin.settings.html.twig', [
|
|||||||
'settings' => $settingsFile['settings'],
|
'settings' => $settingsFile['settings'],
|
||||||
'script' => $settingsParsed['script'],
|
'script' => $settingsParsed['script'],
|
||||||
'settingsKeyName' => $settingsKeyName,
|
'settingsKeyName' => $settingsKeyName,
|
||||||
'pluginName' => $plugin,
|
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -172,8 +172,7 @@
|
|||||||
<div class="float-sm-right d-none d-sm-inline">
|
<div class="float-sm-right d-none d-sm-inline">
|
||||||
<span class="p-2 right badge badge-<?php echo((isset($status['online']) and $status['online']) ? 'success' : 'danger'); ?>"><?php echo $config['lua']['serverName'] ?></span>
|
<span class="p-2 right badge badge-<?php echo((isset($status['online']) and $status['online']) ? 'success' : 'danger'); ?>"><?php echo $config['lua']['serverName'] ?></span>
|
||||||
</div>
|
</div>
|
||||||
<?= base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4='); ?>
|
<?php echo base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4='); ?>
|
||||||
<?= 'Load time: ' . round(microtime(true) - START_TIME, 4) . ' seconds.'; ?>
|
|
||||||
</footer>
|
</footer>
|
||||||
<div id="sidebar-overlay"></div>
|
<div id="sidebar-overlay"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ if (version_compare(phpversion(), '8.1', '<')) die('PHP version 8.1 or higher is
|
|||||||
|
|
||||||
const MYAAC = true;
|
const MYAAC = true;
|
||||||
const MYAAC_VERSION = '2.0-dev';
|
const MYAAC_VERSION = '2.0-dev';
|
||||||
const DATABASE_VERSION = 52;
|
const DATABASE_VERSION = 51;
|
||||||
const TABLE_PREFIX = 'myaac_';
|
const TABLE_PREFIX = 'myaac_';
|
||||||
define('START_TIME', microtime(true));
|
define('START_TIME', microtime(true));
|
||||||
define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX'));
|
define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX'));
|
||||||
@@ -104,8 +104,6 @@ const OTSERV_FIRST = OTSERV;
|
|||||||
const OTSERV_LAST = OTSERV_06;
|
const OTSERV_LAST = OTSERV_06;
|
||||||
const TFS_02 = 3;
|
const TFS_02 = 3;
|
||||||
const TFS_03 = 4;
|
const TFS_03 = 4;
|
||||||
const BLACKTEK_2 = 5;
|
|
||||||
const BLACKTEK = 6;
|
|
||||||
const TFS_FIRST = TFS_02;
|
const TFS_FIRST = TFS_02;
|
||||||
const TFS_LAST = TFS_03;
|
const TFS_LAST = TFS_03;
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
"ext-pdo_mysql": "*",
|
"ext-pdo_mysql": "*",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"ext-xml": "*",
|
"ext-xml": "*",
|
||||||
"ext-simplexml": "*",
|
|
||||||
"ext-dom": "*",
|
"ext-dom": "*",
|
||||||
"phpmailer/phpmailer": "^6.1",
|
"phpmailer/phpmailer": "^6.1",
|
||||||
"composer/semver": "^3.2",
|
"composer/semver": "^3.2",
|
||||||
@@ -21,7 +20,7 @@
|
|||||||
"filp/whoops": "^2.15",
|
"filp/whoops": "^2.15",
|
||||||
"maximebf/debugbar": "1.*",
|
"maximebf/debugbar": "1.*",
|
||||||
"guzzlehttp/guzzle": "7.9.3",
|
"guzzlehttp/guzzle": "7.9.3",
|
||||||
"devium/toml": "^1.0"
|
"spomky-labs/otphp": "^11.3"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "^1.10"
|
"phpstan/phpstan": "^1.10"
|
||||||
|
|||||||
842
composer.lock
generated
842
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,4 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use MyAAC\Server\Lua\Loader;
|
|
||||||
|
|
||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
if(!isset($_SESSION['var_server_path'])) {
|
if(!isset($_SESSION['var_server_path'])) {
|
||||||
@@ -14,35 +11,23 @@ $config['server_path'] = $_SESSION['var_server_path'];
|
|||||||
if($config['server_path'][strlen($config['server_path']) - 1] != '/')
|
if($config['server_path'][strlen($config['server_path']) - 1] != '/')
|
||||||
$config['server_path'] .= '/';
|
$config['server_path'] .= '/';
|
||||||
|
|
||||||
$configLuaExists = file_exists($config['server_path'] . 'config.lua');
|
if((!isset($error) || !$error) && !file_exists($config['server_path'] . 'config.lua')) {
|
||||||
$configTomlExists = file_exists($config['server_path'] . 'config/server.toml');
|
|
||||||
if((!isset($error) || !$error)
|
|
||||||
&& !$configLuaExists
|
|
||||||
&& !$configTomlExists) {
|
|
||||||
error($locale['step_database_error_config']);
|
error($locale['step_database_error_config']);
|
||||||
$error = true;
|
$error = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($error) || !$error) {
|
if(!isset($error) || !$error) {
|
||||||
if($configLuaExists) {
|
$config['lua'] = load_config_lua($config['server_path'] . 'config.lua');
|
||||||
$config['lua'] = Loader::load($config['server_path'] . 'config.lua');
|
if(isset($config['lua']['sqlType'])) // tfs 0.3
|
||||||
if (isset($config['lua']['sqlType'])) // tfs 0.3
|
$config['database_type'] = $config['lua']['sqlType'];
|
||||||
$config['database_type'] = $config['lua']['sqlType'];
|
else if(isset($config['lua']['mysqlHost'])) // tfs 0.2/1.0
|
||||||
elseif (isset($config['lua']['mysqlHost'])) // tfs 0.2/1.0
|
$config['database_type'] = 'mysql';
|
||||||
$config['database_type'] = 'mysql';
|
else if(isset($config['lua']['database_type'])) // otserv
|
||||||
elseif (isset($config['lua']['database_type'])) // otserv
|
$config['database_type'] = $config['lua']['database_type'];
|
||||||
$config['database_type'] = $config['lua']['database_type'];
|
else if(isset($config['lua']['sql_type'])) // otserv
|
||||||
elseif (isset($config['lua']['sql_type'])) // otserv
|
$config['database_type'] = $config['lua']['sql_type'];
|
||||||
$config['database_type'] = $config['lua']['sql_type'];
|
else {
|
||||||
else {
|
$config['database_type'] = '';
|
||||||
$config['database_type'] = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elseif ($configTomlExists) {
|
|
||||||
$tomlConfig = new MyAAC\Server\TOML\Config();
|
|
||||||
$tomlConfig->load();
|
|
||||||
$config['server'] = $tomlConfig->get();
|
|
||||||
$config['database_type'] = (isset($config['server']['database']['mysql']) ? 'mysql' : '');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$config['database_type'] = strtolower($config['database_type']);
|
$config['database_type'] = strtolower($config['database_type']);
|
||||||
|
|||||||
@@ -5,9 +5,16 @@ CREATE TABLE IF NOT EXISTS `myaac_account_actions`
|
|||||||
`ip` varchar(45) NOT NULL DEFAULT '',
|
`ip` varchar(45) NOT NULL DEFAULT '',
|
||||||
`date` int NOT NULL DEFAULT 0,
|
`date` int NOT NULL DEFAULT 0,
|
||||||
`action` varchar(255) NOT NULL DEFAULT '',
|
`action` varchar(255) NOT NULL DEFAULT '',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`)
|
||||||
INDEX `myaac_account_actions_account_id` (`account_id`),
|
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
|
||||||
INDEX `myaac_account_actions_ip` (`ip`)
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `myaac_account_email_codes`
|
||||||
|
(
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`account_id` int NOT NULL,
|
||||||
|
`code` varchar(6) NOT NULL,
|
||||||
|
`created_at` int NOT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `myaac_account_emails_verify`
|
CREATE TABLE IF NOT EXISTS `myaac_account_emails_verify`
|
||||||
@@ -45,8 +52,8 @@ CREATE TABLE IF NOT EXISTS `myaac_changelog`
|
|||||||
CREATE TABLE IF NOT EXISTS `myaac_config`
|
CREATE TABLE IF NOT EXISTS `myaac_config`
|
||||||
(
|
(
|
||||||
`id` int NOT NULL AUTO_INCREMENT,
|
`id` int NOT NULL AUTO_INCREMENT,
|
||||||
`name` varchar(255) NOT NULL,
|
`name` varchar(30) NOT NULL,
|
||||||
`value` varchar(10000) NOT NULL,
|
`value` varchar(1000) NOT NULL,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
UNIQUE (`name`)
|
UNIQUE (`name`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use MyAAC\Server\Config;
|
|
||||||
use Twig\Environment as Twig_Environment;
|
use Twig\Environment as Twig_Environment;
|
||||||
use Twig\Loader\FilesystemLoader as Twig_FilesystemLoader;
|
use Twig\Loader\FilesystemLoader as Twig_FilesystemLoader;
|
||||||
|
|
||||||
@@ -31,7 +30,7 @@ if(file_exists(CACHE . 'install.txt')) {
|
|||||||
$install_status = unserialize(file_get_contents(CACHE . 'install.txt'));
|
$install_status = unserialize(file_get_contents(CACHE . 'install.txt'));
|
||||||
|
|
||||||
if(!isset($_REQUEST['step'])) {
|
if(!isset($_REQUEST['step'])) {
|
||||||
$step = $install_status['step'] ?? '';
|
$step = isset($install_status['step']) ? $install_status['step'] : '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +53,7 @@ if($step == 'finish' && (!isset($config['installed']) || !$config['installed']))
|
|||||||
|
|
||||||
// step verify
|
// step verify
|
||||||
$steps = array(1 => 'welcome', 2 => 'license', 3 => 'requirements', 4 => 'config', 5 => 'database', 6 => 'admin', 7 => 'finish');
|
$steps = array(1 => 'welcome', 2 => 'license', 3 => 'requirements', 4 => 'config', 5 => 'database', 6 => 'admin', 7 => 'finish');
|
||||||
if(!in_array($step, $steps)) // check if a step is valid
|
if(!in_array($step, $steps)) // check if step is valid
|
||||||
throw new RuntimeException('ERROR: Unknown step.');
|
throw new RuntimeException('ERROR: Unknown step.');
|
||||||
|
|
||||||
$install_status['step'] = $step;
|
$install_status['step'] = $step;
|
||||||
@@ -62,7 +61,7 @@ $errors = array();
|
|||||||
|
|
||||||
if($step == 'database') {
|
if($step == 'database') {
|
||||||
foreach($_SESSION as $key => $value) {
|
foreach($_SESSION as $key => $value) {
|
||||||
if(!str_contains($key, 'var_')) {
|
if(strpos($key, 'var_') === false) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +83,7 @@ if($step == 'database') {
|
|||||||
$config['server_path'] .= '/';
|
$config['server_path'] .= '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!Config::exists()) {
|
if(!file_exists($config['server_path'] . 'config.lua')) {
|
||||||
$errors[] = $locale['step_database_error_config'];
|
$errors[] = $locale['step_database_error_config'];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -183,7 +182,7 @@ $error = false;
|
|||||||
clearstatcache();
|
clearstatcache();
|
||||||
if(is_writable(CACHE) && (MYAAC_OS != 'WINDOWS' || win_is_writable(CACHE))) {
|
if(is_writable(CACHE) && (MYAAC_OS != 'WINDOWS' || win_is_writable(CACHE))) {
|
||||||
if(!file_exists(BASE . 'install/ip.txt')) {
|
if(!file_exists(BASE . 'install/ip.txt')) {
|
||||||
$content = warning('AAC installation is disabled. To enable it make a file <b>ip.txt</b> in install/ directory and put there your IP.<br/>
|
$content = warning('AAC installation is disabled. To enable it make file <b>ip.txt</b> in install/ directory and put there your IP.<br/>
|
||||||
Your IP is:<br /><b>' . get_browser_real_ip() . '</b>', true);
|
Your IP is:<br /><b>' . get_browser_real_ip() . '</b>', true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -199,7 +198,7 @@ if(is_writable(CACHE) && (MYAAC_OS != 'WINDOWS' || win_is_writable(CACHE))) {
|
|||||||
if(!$allow)
|
if(!$allow)
|
||||||
{
|
{
|
||||||
$content = warning('In file <b>install/ip.txt</b> must be your IP!<br/>
|
$content = warning('In file <b>install/ip.txt</b> must be your IP!<br/>
|
||||||
In the file is:<br /><b>' . nl2br($file_content) . '</b><br/>
|
In file is:<br /><b>' . nl2br($file_content) . '</b><br/>
|
||||||
Your IP is:<br /><b>' . get_browser_real_ip() . '</b>', true);
|
Your IP is:<br /><b>' . get_browser_real_ip() . '</b>', true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -98,6 +98,16 @@ if(!$db->hasColumn('accounts', 'web_flags')) {
|
|||||||
success($locale['step_database_adding_field'] . ' accounts.web_flags...');
|
success($locale['step_database_adding_field'] . ' accounts.web_flags...');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!$db->hasColumn('accounts', '2fa_type')) {
|
||||||
|
if(query("ALTER TABLE `accounts` ADD `2fa_type` tinyint NOT NULL DEFAULT 0 AFTER `web_flags`;"))
|
||||||
|
success($locale['step_database_adding_field'] . ' accounts.2fa_type...');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$db->hasColumn('accounts', '2fa_secret')) {
|
||||||
|
if(query("ALTER TABLE `accounts` ADD `2fa_secret` varchar(16) NOT NULL DEFAULT '' AFTER `2fa_type`;"))
|
||||||
|
success($locale['step_database_adding_field'] . ' accounts.2fa_secret...');
|
||||||
|
}
|
||||||
|
|
||||||
if(!$db->hasColumn('accounts', 'email_verified')) {
|
if(!$db->hasColumn('accounts', 'email_verified')) {
|
||||||
if(query("ALTER TABLE `accounts` ADD `email_verified` TINYINT(1) NOT NULL DEFAULT 0 AFTER `web_flags`;"))
|
if(query("ALTER TABLE `accounts` ADD `email_verified` TINYINT(1) NOT NULL DEFAULT 0 AFTER `web_flags`;"))
|
||||||
success($locale['step_database_adding_field'] . ' accounts.email_verified...');
|
success($locale['step_database_adding_field'] . ' accounts.email_verified...');
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ $up();
|
|||||||
DataLoader::setLocale($locale);
|
DataLoader::setLocale($locale);
|
||||||
DataLoader::load();
|
DataLoader::load();
|
||||||
|
|
||||||
clearCache();
|
|
||||||
|
|
||||||
// add menus entries
|
// add menus entries
|
||||||
require_once SYSTEM . 'migrations/17.php';
|
require_once SYSTEM . 'migrations/17.php';
|
||||||
$up();
|
$up();
|
||||||
@@ -69,10 +67,6 @@ if(file_exists(CACHE . 'install.txt')) {
|
|||||||
unlink(CACHE . 'install.txt');
|
unlink(CACHE . 'install.txt');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(file_exists(BASE . 'install/ip.txt')) {
|
|
||||||
unlink(BASE . 'install/ip.txt');
|
|
||||||
}
|
|
||||||
|
|
||||||
$locale['step_finish_desc'] = str_replace('$ADMIN_PANEL$', generateLink(str_replace('tools/', '',ADMIN_URL), $locale['step_finish_admin_panel'], true), $locale['step_finish_desc']);
|
$locale['step_finish_desc'] = str_replace('$ADMIN_PANEL$', generateLink(str_replace('tools/', '',ADMIN_URL), $locale['step_finish_admin_panel'], true), $locale['step_finish_desc']);
|
||||||
$locale['step_finish_desc'] = str_replace('$HOMEPAGE$', generateLink(str_replace('tools/', '', BASE_URL), $locale['step_finish_homepage'], true), $locale['step_finish_desc']);
|
$locale['step_finish_desc'] = str_replace('$HOMEPAGE$', generateLink(str_replace('tools/', '', BASE_URL), $locale['step_finish_homepage'], true), $locale['step_finish_desc']);
|
||||||
$locale['step_finish_desc'] = str_replace('$LINK$', generateLink('https://my-aac.org', 'https://my-aac.org', true), $locale['step_finish_desc']);
|
$locale['step_finish_desc'] = str_replace('$LINK$', generateLink('https://my-aac.org', 'https://my-aac.org', true), $locale['step_finish_desc']);
|
||||||
|
|||||||
108
login.php
108
login.php
@@ -5,6 +5,7 @@ use MyAAC\Models\PlayerOnline;
|
|||||||
use MyAAC\Models\Account;
|
use MyAAC\Models\Account;
|
||||||
use MyAAC\Models\Player;
|
use MyAAC\Models\Player;
|
||||||
use MyAAC\RateLimit;
|
use MyAAC\RateLimit;
|
||||||
|
use MyAAC\TwoFactorAuth\TwoFactorAuth;
|
||||||
|
|
||||||
require_once 'common.php';
|
require_once 'common.php';
|
||||||
require_once SYSTEM . 'functions.php';
|
require_once SYSTEM . 'functions.php';
|
||||||
@@ -12,7 +13,7 @@ require_once SYSTEM . 'init.php';
|
|||||||
require_once SYSTEM . 'status.php';
|
require_once SYSTEM . 'status.php';
|
||||||
|
|
||||||
# error function
|
# error function
|
||||||
function sendError($message, $code = 3){
|
function sendError($message, $code = 3) {
|
||||||
$ret = [];
|
$ret = [];
|
||||||
$ret['errorCode'] = $code;
|
$ret['errorCode'] = $code;
|
||||||
$ret['errorMessage'] = $message;
|
$ret['errorMessage'] = $message;
|
||||||
@@ -93,9 +94,9 @@ switch ($action) {
|
|||||||
$creatureBoost = $db->query("SELECT * FROM " . $db->tableName('boosted_creature'))->fetchAll();
|
$creatureBoost = $db->query("SELECT * FROM " . $db->tableName('boosted_creature'))->fetchAll();
|
||||||
$bossBoost = $db->query("SELECT * FROM " . $db->tableName('boosted_boss'))->fetchAll();
|
$bossBoost = $db->query("SELECT * FROM " . $db->tableName('boosted_boss'))->fetchAll();
|
||||||
die(json_encode([
|
die(json_encode([
|
||||||
//'boostedcreature' => true,
|
'boostedcreature' => true,
|
||||||
'bossraceid' => intval($bossBoost[0]['raceid']),
|
|
||||||
'creatureraceid' => intval($creatureBoost[0]['raceid']),
|
'creatureraceid' => intval($creatureBoost[0]['raceid']),
|
||||||
|
'bossraceid' => intval($bossBoost[0]['raceid'])
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,17 +109,18 @@ switch ($action) {
|
|||||||
|
|
||||||
case 'login':
|
case 'login':
|
||||||
|
|
||||||
$port = $config['lua']['gameProtocolPort'];
|
$ip = configLua('ip');
|
||||||
|
$port = configLua('gameProtocolPort');
|
||||||
|
|
||||||
// default world info
|
// default world info
|
||||||
$world = [
|
$world = [
|
||||||
'id' => 0,
|
'id' => 0,
|
||||||
'name' => $config['lua']['serverName'],
|
'name' => $config['lua']['serverName'],
|
||||||
'externaladdress' => $config['lua']['ip'],
|
'externaladdress' => $ip,
|
||||||
'externalport' => $port,
|
'externalport' => $port,
|
||||||
'externaladdressprotected' => $config['lua']['ip'],
|
'externaladdressprotected' => $ip,
|
||||||
'externalportprotected' => $port,
|
'externalportprotected' => $port,
|
||||||
'externaladdressunprotected' => $config['lua']['ip'],
|
'externaladdressunprotected' => $ip,
|
||||||
'externalportunprotected' => $port,
|
'externalportunprotected' => $port,
|
||||||
'previewstate' => 0,
|
'previewstate' => 0,
|
||||||
'location' => 'BRA', // BRA, EUR, USA
|
'location' => 'BRA', // BRA, EUR, USA
|
||||||
@@ -133,13 +135,12 @@ switch ($action) {
|
|||||||
|
|
||||||
$inputEmail = $request->email ?? false;
|
$inputEmail = $request->email ?? false;
|
||||||
$inputAccountName = $request->accountname ?? false;
|
$inputAccountName = $request->accountname ?? false;
|
||||||
$inputToken = $request->token ?? false;
|
|
||||||
|
|
||||||
$account = Account::query();
|
$account = Account::query();
|
||||||
if ($inputEmail != false) { // login by email
|
if ($inputEmail) { // login by email
|
||||||
$account->where('email', $inputEmail);
|
$account->where('email', $inputEmail);
|
||||||
}
|
}
|
||||||
else if($inputAccountName != false) { // login by account name
|
else if($inputAccountName) { // login by account name
|
||||||
$account->where('name', $inputAccountName);
|
$account->where('name', $inputAccountName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,13 +152,14 @@ switch ($action) {
|
|||||||
$limiter->load();
|
$limiter->load();
|
||||||
|
|
||||||
$ban_msg = 'A wrong account, password or secret has been entered ' . setting('core.account_login_attempts_limit') . ' times in a row. You are unable to log into your account for the next ' . setting('core.account_login_ban_time') . ' minutes. Please wait.';
|
$ban_msg = 'A wrong account, password or secret has been entered ' . setting('core.account_login_attempts_limit') . ' times in a row. You are unable to log into your account for the next ' . setting('core.account_login_ban_time') . ' minutes. Please wait.';
|
||||||
|
|
||||||
if (!$account) {
|
if (!$account) {
|
||||||
$limiter->increment($ip);
|
$limiter->increment($ip);
|
||||||
if ($limiter->exceeded($ip)) {
|
if ($limiter->exceeded($ip)) {
|
||||||
sendError($ban_msg);
|
sendError($ban_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendError(($inputEmail != false ? 'Email' : 'Account name') . ' or password is not correct.');
|
sendError(($inputEmail ? 'Email' : 'Account name') . ' or password is not correct.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$current_password = encrypt((USE_ACCOUNT_SALT ? $account->salt : '') . $request->password);
|
$current_password = encrypt((USE_ACCOUNT_SALT ? $account->salt : '') . $request->password);
|
||||||
@@ -167,32 +169,30 @@ switch ($action) {
|
|||||||
sendError($ban_msg);
|
sendError($ban_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendError(($inputEmail != false ? 'Email' : 'Account name') . ' or password is not correct.');
|
sendError(($inputEmail ? 'Email' : 'Account name') . ' or password is not correct.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$accountHasSecret = false;
|
$twoFactorAuth = TwoFactorAuth::getInstance($account->id);
|
||||||
if (fieldExist('secret', 'accounts')) {
|
|
||||||
$accountSecret = $account->secret;
|
|
||||||
if ($accountSecret != null && $accountSecret != '') {
|
|
||||||
$accountHasSecret = true;
|
|
||||||
if ($inputToken === false) {
|
|
||||||
$limiter->increment($ip);
|
|
||||||
if ($limiter->exceeded($ip)) {
|
|
||||||
sendError($ban_msg);
|
|
||||||
}
|
|
||||||
sendError('Submit a valid two-factor authentication token.', 6);
|
|
||||||
} else {
|
|
||||||
require_once LIBS . 'rfc6238.php';
|
|
||||||
if (TokenAuth6238::verify($accountSecret, $inputToken) !== true) {
|
|
||||||
$limiter->increment($ip);
|
|
||||||
if ($limiter->exceeded($ip)) {
|
|
||||||
sendError($ban_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendError('Two-factor authentication failed, token is wrong.', 6);
|
$code = '';
|
||||||
}
|
if ($twoFactorAuth->isActive()) {
|
||||||
}
|
if ($twoFactorAuth->getAuthType() === TwoFactorAuth::TYPE_EMAIL) {
|
||||||
|
$code = $request->emailcode ?? false;
|
||||||
}
|
}
|
||||||
|
else if ($twoFactorAuth->getAuthType() === TwoFactorAuth::TYPE_APP) {
|
||||||
|
$code = $request->token ?? false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$error = '';
|
||||||
|
$errorCode = 6;
|
||||||
|
if (!$twoFactorAuth->processClientLogin($code, $error, $errorCode)) {
|
||||||
|
$limiter->increment($ip);
|
||||||
|
if ($limiter->exceeded($ip)) {
|
||||||
|
sendError($ban_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendError($error, $errorCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
$limiter->reset($ip);
|
$limiter->reset($ip);
|
||||||
@@ -220,46 +220,6 @@ switch ($action) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* not needed anymore?
|
|
||||||
if (fieldExist('premdays', 'accounts') && fieldExist('lastday', 'accounts')) {
|
|
||||||
$save = false;
|
|
||||||
$timeNow = time();
|
|
||||||
$premDays = $account->premdays;
|
|
||||||
$lastDay = $account->lastday;
|
|
||||||
$lastLogin = $lastDay;
|
|
||||||
|
|
||||||
if ($premDays != 0 && $premDays != PHP_INT_MAX) {
|
|
||||||
if ($lastDay == 0) {
|
|
||||||
$lastDay = $timeNow;
|
|
||||||
$save = true;
|
|
||||||
} else {
|
|
||||||
$days = (int)(($timeNow - $lastDay) / 86400);
|
|
||||||
if ($days > 0) {
|
|
||||||
if ($days >= $premDays) {
|
|
||||||
$premDays = 0;
|
|
||||||
$lastDay = 0;
|
|
||||||
} else {
|
|
||||||
$premDays -= $days;
|
|
||||||
$reminder = ($timeNow - $lastDay) % 86400;
|
|
||||||
$lastDay = $timeNow - $reminder;
|
|
||||||
}
|
|
||||||
|
|
||||||
$save = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ($lastDay != 0) {
|
|
||||||
$lastDay = 0;
|
|
||||||
$save = true;
|
|
||||||
}
|
|
||||||
if ($save) {
|
|
||||||
$account->premdays = $premDays;
|
|
||||||
$account->lastday = $lastDay;
|
|
||||||
$account->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
$worlds = [$world];
|
$worlds = [$world];
|
||||||
$playdata = compact('worlds', 'characters');
|
$playdata = compact('worlds', 'characters');
|
||||||
|
|
||||||
@@ -268,7 +228,7 @@ switch ($action) {
|
|||||||
if (!fieldExist('istutorial', 'players')) {
|
if (!fieldExist('istutorial', 'players')) {
|
||||||
$sessionKey .= "\n";
|
$sessionKey .= "\n";
|
||||||
}
|
}
|
||||||
$sessionKey .= ($accountHasSecret && strlen($accountSecret) > 5) ? $inputToken : '';
|
$sessionKey .= ($twoFactorAuth->isActive() && strlen($account->{'2fa_secret'}) > 5) ? $account->{'2fa_secret'} : '';
|
||||||
|
|
||||||
// this is workaround to distinguish between TFS 1.x and otservbr
|
// this is workaround to distinguish between TFS 1.x and otservbr
|
||||||
// TFS 1.x requires the number in session key
|
// TFS 1.x requires the number in session key
|
||||||
|
|||||||
12
package-lock.json
generated
12
package-lock.json
generated
@@ -1431,9 +1431,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lodash": {
|
"node_modules/lodash": {
|
||||||
"version": "4.18.1",
|
"version": "4.17.23",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
|
||||||
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
|
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -1743,9 +1743,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.14.2",
|
"version": "6.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
|
||||||
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
|
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ require __DIR__ . '/system/libs/pot/OTS.php';
|
|||||||
$ots = POT::getInstance();
|
$ots = POT::getInstance();
|
||||||
|
|
||||||
require __DIR__ . '/system/libs/pot/InvitesDriver.php';
|
require __DIR__ . '/system/libs/pot/InvitesDriver.php';
|
||||||
require __DIR__ . '/system/libs/rfc6238.php';
|
|
||||||
require __DIR__ . '/common.php';
|
require __DIR__ . '/common.php';
|
||||||
|
|
||||||
const ACTION = '';
|
const ACTION = '';
|
||||||
|
|||||||
@@ -27,10 +27,12 @@ parameters:
|
|||||||
- '#Variable \$player might not be defined#'
|
- '#Variable \$player might not be defined#'
|
||||||
- '#Variable \$guild might not be defined#'
|
- '#Variable \$guild might not be defined#'
|
||||||
- '#Variable \$[a-zA-Z0-9\\_]+ might not be defined#'
|
- '#Variable \$[a-zA-Z0-9\\_]+ might not be defined#'
|
||||||
- '#Class Lua constructor invoked with 0 parameters, 1 required#'
|
|
||||||
# Eloquent models
|
# Eloquent models
|
||||||
- '#Call to an undefined method [a-zA-Z0-9\\_]+::[a-zA-Z0-9\\_]+\(\)#'
|
- '#Call to an undefined method [a-zA-Z0-9\\_]+::[a-zA-Z0-9\\_]+\(\)#'
|
||||||
- '#Call to an undefined static method [a-zA-Z0-9\\_]+::[a-zA-Z0-9\\_]+\(\)#'
|
- '#Call to an undefined static method [a-zA-Z0-9\\_]+::[a-zA-Z0-9\\_]+\(\)#'
|
||||||
|
# system/pages/highscores.php
|
||||||
|
- '#Access to an undefined property Illuminate\\Database\\Eloquent\\Model::\$online_status#'
|
||||||
|
- '#Access to an undefined property Illuminate\\Database\\Eloquent\\Model::\$vocation_name#'
|
||||||
-
|
-
|
||||||
message: '#Variable \$tmp in empty\(\) always exists and is always falsy#'
|
message: '#Variable \$tmp in empty\(\) always exists and is always falsy#'
|
||||||
path: templates\kathrine\javascript.php
|
path: templates\kathrine\javascript.php
|
||||||
|
|||||||
@@ -9,8 +9,6 @@
|
|||||||
*/
|
*/
|
||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
use MyAAC\Server\Lua\Loader;
|
|
||||||
|
|
||||||
class Validator extends \MyAAC\Validator {}
|
class Validator extends \MyAAC\Validator {}
|
||||||
|
|
||||||
function check_name($name, &$errors = '') {
|
function check_name($name, &$errors = '') {
|
||||||
@@ -76,77 +74,3 @@ function fieldExist($field, $table)
|
|||||||
global $db;
|
global $db;
|
||||||
return $db->hasColumn($table, $field);
|
return $db->hasColumn($table, $field);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Outfits_loadfromXML(): ?array
|
|
||||||
{
|
|
||||||
global $config;
|
|
||||||
$file_path = $config['data_path'] . 'XML/outfits.xml';
|
|
||||||
if (!file_exists($file_path)) { return null; }
|
|
||||||
|
|
||||||
$xml = new DOMDocument;
|
|
||||||
$xml->load($file_path);
|
|
||||||
|
|
||||||
$outfits = null;
|
|
||||||
foreach ($xml->getElementsByTagName('outfit') as $outfit) {
|
|
||||||
$outfits[] = Outfit_parseNode($outfit);
|
|
||||||
}
|
|
||||||
return $outfits;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Outfit_parseNode($node): array
|
|
||||||
{
|
|
||||||
$looktype = (int)$node->getAttribute('looktype');
|
|
||||||
$type = (int)$node->getAttribute('type');
|
|
||||||
$lookname = $node->getAttribute('name');
|
|
||||||
$premium = $node->getAttribute('premium');
|
|
||||||
$unlocked = $node->getAttribute('unlocked');
|
|
||||||
$enabled = $node->getAttribute('enabled');
|
|
||||||
return array('id' => $looktype, 'type' => $type, 'name' => $lookname, 'premium' => $premium, 'unlocked' => $unlocked, 'enabled' => $enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Mounts_loadfromXML(): ?array
|
|
||||||
{
|
|
||||||
global $config;
|
|
||||||
$file_path = $config['data_path'] . 'XML/mounts.xml';
|
|
||||||
if (!file_exists($file_path)) { return null; }
|
|
||||||
|
|
||||||
$xml = new DOMDocument;
|
|
||||||
$xml->load($file_path);
|
|
||||||
|
|
||||||
$mounts = null;
|
|
||||||
foreach ($xml->getElementsByTagName('mount') as $mount) {
|
|
||||||
$mounts[] = Mount_parseNode($mount);
|
|
||||||
}
|
|
||||||
return $mounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Mount_parseNode($node): array
|
|
||||||
{
|
|
||||||
$id = (int)$node->getAttribute('id');
|
|
||||||
$clientid = (int)$node->getAttribute('clientid');
|
|
||||||
$name = $node->getAttribute('name');
|
|
||||||
$speed = (int)$node->getAttribute('speed');
|
|
||||||
$premium = $node->getAttribute('premium');
|
|
||||||
$type = $node->getAttribute('type');
|
|
||||||
return array('id' => $id, 'clientid' => $clientid, 'name' => $name, 'speed' => $speed, 'premium' => $premium, 'type' => $type);
|
|
||||||
}
|
|
||||||
|
|
||||||
function load_config_lua(string $file): array
|
|
||||||
{
|
|
||||||
$result = Loader::load($file);
|
|
||||||
if ($result === false) {
|
|
||||||
log_append('error.log', '[load_config_file] Fatal error: Cannot load config.lua (' . $file . ').');
|
|
||||||
throw new \RuntimeException('ERROR: Cannot find ' . $file . ' file.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function configLua($key) {
|
|
||||||
global $config;
|
|
||||||
if (is_array($key)) {
|
|
||||||
return $config['lua'][$key[0]] = $key[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return @$config['lua'][$key];
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -18,19 +18,6 @@ if (!isset($config['database_overwrite'])) {
|
|||||||
|
|
||||||
if(!$config['database_overwrite'] && !isset($config['database_user'][0], $config['database_password'][0], $config['database_name'][0]))
|
if(!$config['database_overwrite'] && !isset($config['database_user'][0], $config['database_password'][0], $config['database_name'][0]))
|
||||||
{
|
{
|
||||||
if (isset($config['server']['database']['mysql'])) { // BlackTek
|
|
||||||
$config['otserv_version'] = BLACKTEK;
|
|
||||||
$config['database_type'] = 'mysql';
|
|
||||||
$config['database_host'] = $config['server']['database']['mysql']['host'];
|
|
||||||
$config['database_port'] = $config['server']['database']['mysql']['port'];
|
|
||||||
$config['database_user'] = $config['server']['database']['mysql']['user'];
|
|
||||||
$config['database_password'] = $config['server']['database']['mysql']['pass'];
|
|
||||||
$config['database_name'] = $config['server']['database']['mysql']['database'];
|
|
||||||
if(!isset($config['database_socket'][0]) && !empty(trim($config['server']['database']['mysql']['socket']))) {
|
|
||||||
$config['database_socket'] = trim($config['server']['database']['mysql']['socket']);
|
|
||||||
}
|
|
||||||
$config['database_encryption'] = 'sha1';
|
|
||||||
}
|
|
||||||
if(isset($config['lua']['sqlType'])) {// tfs 0.3
|
if(isset($config['lua']['sqlType'])) {// tfs 0.3
|
||||||
if(isset($config['lua']['mysqlHost'])) {// tfs 0.2
|
if(isset($config['lua']['mysqlHost'])) {// tfs 0.2
|
||||||
$config['otserv_version'] = TFS_02;
|
$config['otserv_version'] = TFS_02;
|
||||||
@@ -107,6 +94,7 @@ if(!isset($config['database_socket'])) {
|
|||||||
$config['database_socket'] = '';
|
$config['database_socket'] = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$ots->connect(array(
|
$ots->connect(array(
|
||||||
'host' => $config['database_host'],
|
'host' => $config['database_host'],
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ defined('MYAAC') or die('Direct access not allowed!');
|
|||||||
|
|
||||||
use MyAAC\Cache\Cache;
|
use MyAAC\Cache\Cache;
|
||||||
use MyAAC\CsrfToken;
|
use MyAAC\CsrfToken;
|
||||||
|
use MyAAC\Items;
|
||||||
use MyAAC\Models\Config;
|
use MyAAC\Models\Config;
|
||||||
use MyAAC\Models\Guild;
|
use MyAAC\Models\Guild;
|
||||||
use MyAAC\Models\House;
|
use MyAAC\Models\House;
|
||||||
@@ -20,7 +21,6 @@ use MyAAC\Models\PlayerDeath;
|
|||||||
use MyAAC\Models\PlayerKillers;
|
use MyAAC\Models\PlayerKillers;
|
||||||
use MyAAC\News;
|
use MyAAC\News;
|
||||||
use MyAAC\Plugins;
|
use MyAAC\Plugins;
|
||||||
use MyAAC\Server\Items;
|
|
||||||
use MyAAC\Settings;
|
use MyAAC\Settings;
|
||||||
use PHPMailer\PHPMailer\PHPMailer;
|
use PHPMailer\PHPMailer\PHPMailer;
|
||||||
|
|
||||||
@@ -987,6 +987,85 @@ function log_append($file, $str, array $params = [])
|
|||||||
fclose($f);
|
fclose($f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function load_config_lua($filename)
|
||||||
|
{
|
||||||
|
global $config;
|
||||||
|
|
||||||
|
$config_file = $filename;
|
||||||
|
if(!@file_exists($config_file))
|
||||||
|
{
|
||||||
|
log_append('error.log', '[load_config_file] Fatal error: Cannot load config.lua (' . $filename . ').');
|
||||||
|
throw new RuntimeException('ERROR: Cannot find ' . $filename . ' file.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array();
|
||||||
|
$config_string = str_replace(array("\r\n", "\r"), "\n", file_get_contents($filename));
|
||||||
|
$lines = explode("\n", $config_string);
|
||||||
|
if(count($lines) > 0) {
|
||||||
|
foreach($lines as $ln => $line)
|
||||||
|
{
|
||||||
|
$line = trim($line);
|
||||||
|
if(isset($line[0]) && ($line[0] === '{' || $line[0] === '}')) {
|
||||||
|
// arrays are not supported yet
|
||||||
|
// just ignore the error
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tmp_exp = explode('=', $line, 2);
|
||||||
|
if(str_contains($line, 'dofile')) {
|
||||||
|
$delimiter = '"';
|
||||||
|
if(!str_contains($line, $delimiter)) {
|
||||||
|
$delimiter = "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
$tmp = explode($delimiter, $line);
|
||||||
|
$result = array_merge($result, load_config_lua($config['server_path'] . $tmp[1]));
|
||||||
|
}
|
||||||
|
else if(count($tmp_exp) >= 2) {
|
||||||
|
$key = trim($tmp_exp[0]);
|
||||||
|
if(!str_starts_with($key, '--')) {
|
||||||
|
$value = trim($tmp_exp[1]);
|
||||||
|
if(str_contains($value, '--')) {// found some deep comment
|
||||||
|
$value = preg_replace('/--.*$/i', '', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_numeric($value))
|
||||||
|
$result[$key] = (float) $value;
|
||||||
|
elseif(in_array(@$value[0], array("'", '"')) && in_array(@$value[strlen($value) - 1], array("'", '"')))
|
||||||
|
$result[$key] = substr(substr($value, 1), 0, -1);
|
||||||
|
elseif(in_array($value, array('true', 'false')))
|
||||||
|
$result[$key] = $value === 'true';
|
||||||
|
elseif(@$value[0] === '{') {
|
||||||
|
// arrays are not supported yet
|
||||||
|
// just ignore the error
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach($result as $tmp_key => $tmp_value) { // load values defined by other keys, like: dailyFragsToBlackSkull = dailyFragsToRedSkull
|
||||||
|
$value = str_replace($tmp_key, $tmp_value, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$ret = eval("return $value;");
|
||||||
|
}
|
||||||
|
catch (Throwable $e) {
|
||||||
|
throw new RuntimeException('ERROR: Loading config.lua file. Line: ' . ($ln + 1) . ' - Unable to parse value "' . $value . '" - ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if((string) $ret == '' && trim($value) !== '""') {
|
||||||
|
throw new RuntimeException('ERROR: Loading config.lua file. Line ' . ($ln + 1) . ' is not valid [key: ' . $key . ']');
|
||||||
|
}
|
||||||
|
$result[$key] = $ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_merge($result, $config['lua'] ?? []);
|
||||||
|
}
|
||||||
|
|
||||||
function str_replace_first($search,$replace, $subject) {
|
function str_replace_first($search,$replace, $subject) {
|
||||||
$pos = strpos($subject, $search);
|
$pos = strpos($subject, $search);
|
||||||
if ($pos !== false) {
|
if ($pos !== false) {
|
||||||
@@ -1246,6 +1325,15 @@ function config($key) {
|
|||||||
return @$config[$key];
|
return @$config[$key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function configLua($key) {
|
||||||
|
global $config;
|
||||||
|
if (is_array($key)) {
|
||||||
|
return $config['lua'][$key[0]] = $key[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return @$config['lua'][$key];
|
||||||
|
}
|
||||||
|
|
||||||
function setting($key)
|
function setting($key)
|
||||||
{
|
{
|
||||||
$settings = Settings::getInstance();
|
$settings = Settings::getInstance();
|
||||||
@@ -1552,6 +1640,58 @@ function verify_number($number, $name, $max_length)
|
|||||||
echo_error($name . ' cannot be longer than ' . $max_length . ' digits.');
|
echo_error($name . ' cannot be longer than ' . $max_length . ' digits.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Outfits_loadfromXML()
|
||||||
|
{
|
||||||
|
global $config;
|
||||||
|
$file_path = $config['data_path'] . 'XML/outfits.xml';
|
||||||
|
if (!file_exists($file_path)) { return null; }
|
||||||
|
|
||||||
|
$xml = new DOMDocument;
|
||||||
|
$xml->load($file_path);
|
||||||
|
|
||||||
|
$outfits = null;
|
||||||
|
foreach ($xml->getElementsByTagName('outfit') as $outfit) {
|
||||||
|
$outfits[] = Outfit_parseNode($outfit);
|
||||||
|
}
|
||||||
|
return $outfits;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Outfit_parseNode($node) {
|
||||||
|
$looktype = (int)$node->getAttribute('looktype');
|
||||||
|
$type = (int)$node->getAttribute('type');
|
||||||
|
$lookname = $node->getAttribute('name');
|
||||||
|
$premium = $node->getAttribute('premium');
|
||||||
|
$unlocked = $node->getAttribute('unlocked');
|
||||||
|
$enabled = $node->getAttribute('enabled');
|
||||||
|
return array('id' => $looktype, 'type' => $type, 'name' => $lookname, 'premium' => $premium, 'unlocked' => $unlocked, 'enabled' => $enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Mounts_loadfromXML()
|
||||||
|
{
|
||||||
|
global $config;
|
||||||
|
$file_path = $config['data_path'] . 'XML/mounts.xml';
|
||||||
|
if (!file_exists($file_path)) { return null; }
|
||||||
|
|
||||||
|
$xml = new DOMDocument;
|
||||||
|
$xml->load($file_path);
|
||||||
|
|
||||||
|
$mounts = null;
|
||||||
|
foreach ($xml->getElementsByTagName('mount') as $mount) {
|
||||||
|
$mounts[] = Mount_parseNode($mount);
|
||||||
|
}
|
||||||
|
return $mounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Mount_parseNode($node) {
|
||||||
|
$id = (int)$node->getAttribute('id');
|
||||||
|
$clientid = (int)$node->getAttribute('clientid');
|
||||||
|
$name = $node->getAttribute('name');
|
||||||
|
$speed = (int)$node->getAttribute('speed');
|
||||||
|
$premium = $node->getAttribute('premium');
|
||||||
|
$type = $node->getAttribute('type');
|
||||||
|
return array('id' => $id, 'clientid' => $clientid, 'name' => $name, 'speed' => $speed, 'premium' => $premium, 'type' => $type);
|
||||||
|
}
|
||||||
|
|
||||||
function left($str, $length) {
|
function left($str, $length) {
|
||||||
return substr($str, 0, $length);
|
return substr($str, 0, $length);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ use MyAAC\CsrfToken;
|
|||||||
use MyAAC\Hooks;
|
use MyAAC\Hooks;
|
||||||
use MyAAC\Plugins;
|
use MyAAC\Plugins;
|
||||||
use MyAAC\Models\Town;
|
use MyAAC\Models\Town;
|
||||||
use MyAAC\Server\Config;
|
use MyAAC\Server\XML\Vocations;
|
||||||
use MyAAC\Server\Vocations;
|
|
||||||
use MyAAC\Settings;
|
use MyAAC\Settings;
|
||||||
|
|
||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
@@ -91,20 +90,28 @@ foreach($_REQUEST as $var => $value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// load otserv config file
|
// load otserv config file
|
||||||
|
$config_lua_reload = true;
|
||||||
if($cache->enabled()) {
|
if($cache->enabled()) {
|
||||||
$tmp = null;
|
$tmp = null;
|
||||||
if(!$cache->fetch('server_path', $tmp) || $tmp != config('server_path')) {
|
if($cache->fetch('server_path', $tmp) && $tmp == $config['server_path']) {
|
||||||
$cache->delete('config_server');
|
$tmp = null;
|
||||||
|
if($cache->fetch('config_lua', $tmp) && $tmp) {
|
||||||
|
$config['lua'] = unserialize($tmp);
|
||||||
|
$config_lua_reload = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($config['server'])) {
|
if($config_lua_reload) {
|
||||||
$config['server'] = $config['lua'] = Config::get();
|
$config['lua'] = load_config_lua($config['server_path'] . 'config.lua');
|
||||||
|
|
||||||
|
// cache config
|
||||||
if($cache->enabled()) {
|
if($cache->enabled()) {
|
||||||
$cache->set('server_path', config('server_path'), 10 * 60);
|
$cache->set('config_lua', serialize($config['lua']), 2 * 60);
|
||||||
|
$cache->set('server_path', $config['server_path'], 10 * 60);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
unset($tmp);
|
||||||
|
|
||||||
if(isset($config['lua']['servername']))
|
if(isset($config['lua']['servername']))
|
||||||
$config['lua']['serverName'] = $config['lua']['servername'];
|
$config['lua']['serverName'] = $config['lua']['servername'];
|
||||||
|
|||||||
@@ -372,8 +372,8 @@ class POT
|
|||||||
|
|
||||||
global $debugBar;
|
global $debugBar;
|
||||||
if (isset($debugBar)) {
|
if (isset($debugBar)) {
|
||||||
$this->db = new \MyAAC\Debug\TraceablePDOWithBacktrace(new OTS_DB_MySQL($params));
|
$this->db = new DebugBar\DataCollector\PDO\TraceablePDO(new OTS_DB_MySQL($params));
|
||||||
$debugBar->addCollector(new \MyAAC\Debug\PDOCollectorWithBacktrace($this->db));
|
$debugBar->addCollector(new DebugBar\DataCollector\PDO\PDOCollector($this->db));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$this->db = new OTS_DB_MySQL($params);
|
$this->db = new OTS_DB_MySQL($params);
|
||||||
|
|||||||
@@ -736,17 +736,11 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
|
|||||||
*/
|
*/
|
||||||
public function setCustomField($field, $value)
|
public function setCustomField($field, $value)
|
||||||
{
|
{
|
||||||
if( !isset($this->data['id']) )
|
if( !isset($this->data['id']) ) {
|
||||||
{
|
|
||||||
throw new E_OTS_NotLoaded();
|
throw new E_OTS_NotLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
// quotes value for SQL query
|
AccountModel::where('id', $this->data['id'])->update([$field => $value]);
|
||||||
if(!( is_int($value) || is_float($value) ))
|
|
||||||
{
|
|
||||||
$value = $this->db->quote($value);
|
|
||||||
}
|
|
||||||
$this->db->exec('UPDATE ' . $this->db->tableName('accounts') . ' SET ' . $this->db->fieldName($field) . ' = ' . $value . ' WHERE ' . $this->db->fieldName('id') . ' = ' . $this->data['id']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU Lesser General Public License, Version 3
|
* @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU Lesser General Public License, Version 3
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use MyAAC\Server\Groups;
|
use MyAAC\Cache\Cache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of groups.
|
* List of groups.
|
||||||
@@ -47,8 +47,73 @@ class OTS_Groups_List implements IteratorAggregate, Countable
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(Groups::get() as $id => $info) {
|
if(!isset($file[0]))
|
||||||
$this->groups[$id] = new OTS_Group($info);
|
{
|
||||||
|
global $config;
|
||||||
|
$file = $config['data_path'] . 'XML/groups.xml';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!@file_exists($file)) {
|
||||||
|
error('Error: Cannot load groups.xml. More info in system/logs/error.log file.');
|
||||||
|
log_append('error.log', '[OTS_Groups_List.php] Fatal error: Cannot load groups.xml (' . $file . '). It doesnt exist.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cache = Cache::getInstance();
|
||||||
|
|
||||||
|
$data = array();
|
||||||
|
if($cache->enabled())
|
||||||
|
{
|
||||||
|
$tmp = '';
|
||||||
|
if($cache->fetch('groups', $tmp))
|
||||||
|
$data = unserialize($tmp);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$groups = new DOMDocument();
|
||||||
|
if(!@$groups->load($file)) {
|
||||||
|
error('Error: Cannot load groups.xml. More info in system/logs/error.log file.');
|
||||||
|
log_append('error.log', '[OTS_Groups_List.php] Fatal error: Cannot load groups.xml (' . $file . '). Error: ' . print_r(error_get_last(), true));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// loads groups
|
||||||
|
foreach( $groups->getElementsByTagName('group') as $group)
|
||||||
|
{
|
||||||
|
$data[$group->getAttribute('id')] = array(
|
||||||
|
'id' => $group->getAttribute('id'),
|
||||||
|
'name' => $group->getAttribute('name'),
|
||||||
|
'access' => $group->getAttribute('access')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$cache->set('groups', serialize($data), 120);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($data as $id => $info)
|
||||||
|
$this->groups[ $id ] = new OTS_Group($info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// loads DOM document
|
||||||
|
$groups = new DOMDocument();
|
||||||
|
if(!@$groups->load($file)) {
|
||||||
|
error('Error: Cannot load groups.xml. More info in system/logs/error.log file.');
|
||||||
|
log_append('error.log', '[OTS_Groups_List.php] Fatal error: Cannot load groups.xml (' . $file . '). Error: ' . print_r(error_get_last(), true));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// loads groups
|
||||||
|
foreach($groups->getElementsByTagName('group') as $group)
|
||||||
|
{
|
||||||
|
$data[$group->getAttribute('id')] = array(
|
||||||
|
'id' => $group->getAttribute('id'),
|
||||||
|
'name' => $group->getAttribute('name'),
|
||||||
|
'access' => $group->getAttribute('access')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->groups[ $group->getAttribute('id') ] = new OTS_Group($data[$group->getAttribute('id')]);
|
||||||
|
//echo $this->getGroup(1)->getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,284 +0,0 @@
|
|||||||
<?php
|
|
||||||
/** https://github.com/Voronenko/PHPOTP/blob/08cda9cb9c30b7242cf0b3a9100a6244a2874927/code/base32static.php
|
|
||||||
* Encode in Base32 based on RFC 4648.
|
|
||||||
* Requires 20% more space than base64
|
|
||||||
* Great for case-insensitive filesystems like Windows and URL's (except for = char which can be excluded using the pad option for urls)
|
|
||||||
*
|
|
||||||
* @package default
|
|
||||||
* @author Bryan Ruiz
|
|
||||||
**/
|
|
||||||
class Base32Static {
|
|
||||||
|
|
||||||
private static $map = array(
|
|
||||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 7
|
|
||||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 15
|
|
||||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 23
|
|
||||||
'Y', 'Z', '2', '3', '4', '5', '6', '7', // 31
|
|
||||||
'=' // padding character
|
|
||||||
);
|
|
||||||
|
|
||||||
private static $flippedMap = array(
|
|
||||||
'A'=>'0', 'B'=>'1', 'C'=>'2', 'D'=>'3', 'E'=>'4', 'F'=>'5', 'G'=>'6', 'H'=>'7',
|
|
||||||
'I'=>'8', 'J'=>'9', 'K'=>'10', 'L'=>'11', 'M'=>'12', 'N'=>'13', 'O'=>'14', 'P'=>'15',
|
|
||||||
'Q'=>'16', 'R'=>'17', 'S'=>'18', 'T'=>'19', 'U'=>'20', 'V'=>'21', 'W'=>'22', 'X'=>'23',
|
|
||||||
'Y'=>'24', 'Z'=>'25', '2'=>'26', '3'=>'27', '4'=>'28', '5'=>'29', '6'=>'30', '7'=>'31'
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use padding false when encoding for urls
|
|
||||||
*
|
|
||||||
* @return base32 encoded string
|
|
||||||
* @author Bryan Ruiz
|
|
||||||
**/
|
|
||||||
public static function encode($input, $padding = true) {
|
|
||||||
if(empty($input)) return "";
|
|
||||||
|
|
||||||
$input = str_split($input);
|
|
||||||
$binaryString = "";
|
|
||||||
|
|
||||||
for($i = 0; $i < count($input); $i++) {
|
|
||||||
$binaryString .= str_pad(base_convert(ord($input[$i]), 10, 2), 8, '0', STR_PAD_LEFT);
|
|
||||||
}
|
|
||||||
|
|
||||||
$fiveBitBinaryArray = str_split($binaryString, 5);
|
|
||||||
$base32 = "";
|
|
||||||
$i=0;
|
|
||||||
|
|
||||||
while($i < count($fiveBitBinaryArray)) {
|
|
||||||
$base32 .= self::$map[base_convert(str_pad($fiveBitBinaryArray[$i], 5,'0'), 2, 10)];
|
|
||||||
$i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($padding && ($x = strlen($binaryString) % 40) != 0) {
|
|
||||||
if($x == 8) $base32 .= str_repeat(self::$map[32], 6);
|
|
||||||
else if($x == 16) $base32 .= str_repeat(self::$map[32], 4);
|
|
||||||
else if($x == 24) $base32 .= str_repeat(self::$map[32], 3);
|
|
||||||
else if($x == 32) $base32 .= self::$map[32];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $base32;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function decode($input) {
|
|
||||||
if(empty($input)) return;
|
|
||||||
|
|
||||||
$paddingCharCount = substr_count($input, self::$map[32]);
|
|
||||||
$allowedValues = array(6,4,3,1,0);
|
|
||||||
|
|
||||||
if(!in_array($paddingCharCount, $allowedValues)) return false;
|
|
||||||
|
|
||||||
for($i=0; $i<4; $i++){
|
|
||||||
if($paddingCharCount == $allowedValues[$i] &&
|
|
||||||
substr($input, -($allowedValues[$i])) != str_repeat(self::$map[32], $allowedValues[$i])) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$input = str_replace('=','', $input);
|
|
||||||
$input = str_split($input);
|
|
||||||
$binaryString = "";
|
|
||||||
|
|
||||||
for($i=0; $i < count($input); $i = $i+8) {
|
|
||||||
$x = "";
|
|
||||||
|
|
||||||
if(!in_array($input[$i], self::$map)) return false;
|
|
||||||
|
|
||||||
for($j=0; $j < 8; $j++) {
|
|
||||||
$x .= str_pad(base_convert(@self::$flippedMap[@$input[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT);
|
|
||||||
}
|
|
||||||
|
|
||||||
$eightBits = str_split($x, 8);
|
|
||||||
|
|
||||||
for($z = 0; $z < count($eightBits); $z++) {
|
|
||||||
$binaryString .= ( ($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48 ) ? $y:"";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $binaryString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// http://www.faqs.org/rfcs/rfc6238.html
|
|
||||||
// https://github.com/Voronenko/PHPOTP/blob/08cda9cb9c30b7242cf0b3a9100a6244a2874927/code/rfc6238.php
|
|
||||||
// Local changes: http -> https, consistent indentation, 200x200 -> 300x300 QR image size, PHP end tag
|
|
||||||
class TokenAuth6238 {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* verify
|
|
||||||
*
|
|
||||||
* @param string $secretkey Secret clue (base 32).
|
|
||||||
* @return bool True if success, false if failure
|
|
||||||
*/
|
|
||||||
public static function verify($secretkey, $code, $rangein30s = 3) {
|
|
||||||
$key = base32static::decode($secretkey);
|
|
||||||
$unixtimestamp = time()/30;
|
|
||||||
|
|
||||||
for($i=-($rangein30s); $i<=$rangein30s; $i++) {
|
|
||||||
$checktime = (int)($unixtimestamp+$i);
|
|
||||||
$thiskey = self::oath_hotp($key, $checktime);
|
|
||||||
|
|
||||||
if ((int)$code == self::oath_truncate($thiskey,6)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static function getTokenCode($secretkey,$rangein30s = 3) {
|
|
||||||
$result = "";
|
|
||||||
$key = base32static::decode($secretkey);
|
|
||||||
$unixtimestamp = time()/30;
|
|
||||||
|
|
||||||
for($i=-($rangein30s); $i<=$rangein30s; $i++) {
|
|
||||||
$checktime = (int)($unixtimestamp+$i);
|
|
||||||
$thiskey = self::oath_hotp($key, $checktime);
|
|
||||||
$result = $result." # ".self::oath_truncate($thiskey,6);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getTokenCodeDebug($secretkey,$rangein30s = 3) {
|
|
||||||
$result = "";
|
|
||||||
print "<br/>SecretKey: $secretkey <br/>";
|
|
||||||
|
|
||||||
$key = base32static::decode($secretkey);
|
|
||||||
print "Key(base 32 decode): $key <br/>";
|
|
||||||
|
|
||||||
$unixtimestamp = time()/30;
|
|
||||||
print "UnixTimeStamp (time()/30): $unixtimestamp <br/>";
|
|
||||||
|
|
||||||
for($i=-($rangein30s); $i<=$rangein30s; $i++) {
|
|
||||||
$checktime = (int)($unixtimestamp+$i);
|
|
||||||
print "Calculating oath_hotp from (int)(unixtimestamp +- 30sec offset): $checktime basing on secret key<br/>";
|
|
||||||
|
|
||||||
$thiskey = self::oath_hotp($key, $checktime, true);
|
|
||||||
print "======================================================<br/>";
|
|
||||||
print "CheckTime: $checktime oath_hotp:".$thiskey."<br/>";
|
|
||||||
|
|
||||||
$result = $result." # ".self::oath_truncate($thiskey,6,true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getBarCodeUrl($username, $domain, $secretkey, $issuer) {
|
|
||||||
$url = "https://chart.apis.google.com/chart";
|
|
||||||
$url = $url."?chs=300x300&chld=M|0&cht=qr&chl=otpauth://totp/";
|
|
||||||
$url = $url.$username . "@" . $domain . "%3Fsecret%3D" . $secretkey . '%26issuer%3D' . rawurlencode($issuer);
|
|
||||||
return $url;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function generateRandomClue($length = 16) {
|
|
||||||
$b32 = "234567QWERTYUIOPASDFGHJKLZXCVBNM";
|
|
||||||
$s = "";
|
|
||||||
|
|
||||||
for ($i = 0; $i < $length; $i++)
|
|
||||||
$s .= $b32[rand(0,31)];
|
|
||||||
|
|
||||||
return $s;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function hotp_tobytestream($key) {
|
|
||||||
$result = array();
|
|
||||||
$last = strlen($key);
|
|
||||||
for ($i = 0; $i < $last; $i = $i + 2) {
|
|
||||||
$x = $key[$i] + $key[$i + 1];
|
|
||||||
$x = strtoupper($x);
|
|
||||||
$x = hexdec($x);
|
|
||||||
$result = $result.chr($x);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function oath_hotp ($key, $counter, $debug=false) {
|
|
||||||
$result = "";
|
|
||||||
$orgcounter = $counter;
|
|
||||||
$cur_counter = array(0,0,0,0,0,0,0,0);
|
|
||||||
|
|
||||||
if ($debug) {
|
|
||||||
print "Packing counter $counter (".dechex($counter).")into binary string - pay attention to hex representation of key and binary representation<br/>";
|
|
||||||
}
|
|
||||||
|
|
||||||
for($i=7;$i>=0;$i--) { // C for unsigned char, * for repeating to the end of the input data
|
|
||||||
$cur_counter[$i] = pack ('C*', $counter);
|
|
||||||
|
|
||||||
if ($debug) {
|
|
||||||
print $cur_counter[$i]."(".dechex(ord($cur_counter[$i])).")"." from $counter <br/>";
|
|
||||||
}
|
|
||||||
|
|
||||||
$counter = $counter >> 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($debug) {
|
|
||||||
foreach ($cur_counter as $char) {
|
|
||||||
print ord($char) . " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
print "<br/>";
|
|
||||||
}
|
|
||||||
|
|
||||||
$binary = implode($cur_counter);
|
|
||||||
|
|
||||||
// Pad to 8 characters
|
|
||||||
str_pad($binary, 8, chr(0), STR_PAD_LEFT);
|
|
||||||
|
|
||||||
if ($debug) {
|
|
||||||
print "Prior to HMAC calculation pad with zero on the left until 8 characters.<br/>";
|
|
||||||
print "Calculate sha1 HMAC(Hash-based Message Authentication Code http://en.wikipedia.org/wiki/HMAC).<br/>";
|
|
||||||
print "hash_hmac ('sha1', $binary, $key)<br/>";
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = hash_hmac ('sha1', $binary, $key);
|
|
||||||
|
|
||||||
if ($debug) {
|
|
||||||
print "Result: $result <br/>";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function oath_truncate($hash, $length = 6, $debug=false) {
|
|
||||||
$result="";
|
|
||||||
|
|
||||||
// Convert to dec
|
|
||||||
if($debug) {
|
|
||||||
print "converting hex hash into characters<br/>";
|
|
||||||
}
|
|
||||||
|
|
||||||
$hashcharacters = str_split($hash,2);
|
|
||||||
|
|
||||||
if($debug) {
|
|
||||||
print_r($hashcharacters);
|
|
||||||
print "<br/>and convert to decimals:<br/>";
|
|
||||||
}
|
|
||||||
|
|
||||||
for ($j=0; $j<count($hashcharacters); $j++) {
|
|
||||||
$hmac_result[]=hexdec($hashcharacters[$j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if($debug) {
|
|
||||||
print_r($hmac_result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// http://php.net/manual/ru/function.hash-hmac.php
|
|
||||||
// adopted from brent at thebrent dot net 21-May-2009 08:17 comment
|
|
||||||
|
|
||||||
$offset = $hmac_result[19] & 0xf;
|
|
||||||
|
|
||||||
if($debug) {
|
|
||||||
print "Calculating offset as 19th element of hmac:".$hmac_result[19]."<br/>";
|
|
||||||
print "offset:".$offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = (
|
|
||||||
(($hmac_result[$offset+0] & 0x7f) << 24 ) |
|
|
||||||
(($hmac_result[$offset+1] & 0xff) << 16 ) |
|
|
||||||
(($hmac_result[$offset+2] & 0xff) << 8 ) |
|
|
||||||
($hmac_result[$offset+3] & 0xff)
|
|
||||||
) % pow(10,$length);
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,7 +20,7 @@ $locale['not_loaded'] = 'Nicht geladen';
|
|||||||
$locale['loading_spinner'] = 'Bitte warten, installieren...';
|
$locale['loading_spinner'] = 'Bitte warten, installieren...';
|
||||||
$locale['importing_spinner'] = 'Bitte warte, Daten werden importiert...';
|
$locale['importing_spinner'] = 'Bitte warte, Daten werden importiert...';
|
||||||
$locale['please_fill_all'] = 'Bitte füllen Sie alle Felder aus!';
|
$locale['please_fill_all'] = 'Bitte füllen Sie alle Felder aus!';
|
||||||
$locale['already_installed'] = 'MyAAC wurde bereits installiert. Wenn Sie MyAAC neu installieren möchten, löschen Sie die Datei <strong>config.local.php</strong> aus dem Hauptverzeichnis und aktualisieren Sie die Seite.';
|
$locale['already_installed'] = 'MyAAC wurde bereits installiert. Bitte löschen <b>install/</b> Verzeichnis. Wenn Sie MyAAC neu installieren möchten, löschen Sie die Datei <strong>config.local.php</strong> aus dem Hauptverzeichnis und aktualisieren Sie die Seite.';
|
||||||
|
|
||||||
// welcome
|
// welcome
|
||||||
$locale['step_welcome'] = 'Willkommen';
|
$locale['step_welcome'] = 'Willkommen';
|
||||||
@@ -67,7 +67,7 @@ $locale['step_database'] = 'Schema importieren';
|
|||||||
$locale['step_database_title'] = 'MySQL schema importieren';
|
$locale['step_database_title'] = 'MySQL schema importieren';
|
||||||
$locale['step_database_importing'] = 'Ihre Datenbank ist MySQL. Datenbankname ist: "$DATABASE_NAME$". Schema wird jetzt importiert...';
|
$locale['step_database_importing'] = 'Ihre Datenbank ist MySQL. Datenbankname ist: "$DATABASE_NAME$". Schema wird jetzt importiert...';
|
||||||
$locale['step_database_error_path'] = 'Bitte geben Sie den Serverpfad an.';
|
$locale['step_database_error_path'] = 'Bitte geben Sie den Serverpfad an.';
|
||||||
$locale['step_database_error_config'] = 'Datei config.lua oder config/server.toml kann nicht gefunden werden. Ist der Serverpfad korrekt? Gehen Sie zurück und überprüfen Sie noch einmal.';
|
$locale['step_database_error_config'] = 'Datei config.lua kann nicht gefunden werden. Ist der Serverpfad korrekt? Gehen Sie zurück und überprüfen Sie noch einmal.';
|
||||||
$locale['step_database_error_database_empty'] = 'Der Datenbanktyp kann nicht aus config.lua ermittelt werden. Ihr OTS wird von diesem AAC nicht unterstützt.';
|
$locale['step_database_error_database_empty'] = 'Der Datenbanktyp kann nicht aus config.lua ermittelt werden. Ihr OTS wird von diesem AAC nicht unterstützt.';
|
||||||
$locale['step_database_error_only_mysql'] = 'Dieser AAC unterstützt nur MySQL. Aus Ihrer Konfigurationsdatei scheint Ihr OTS die Datenbank $DATABASE_TYPE$ zu verwenden. Bitte ändern Sie Ihre Datenbank in MySQL und folgen Sie dann der Installation erneut.';
|
$locale['step_database_error_only_mysql'] = 'Dieser AAC unterstützt nur MySQL. Aus Ihrer Konfigurationsdatei scheint Ihr OTS die Datenbank $DATABASE_TYPE$ zu verwenden. Bitte ändern Sie Ihre Datenbank in MySQL und folgen Sie dann der Installation erneut.';
|
||||||
$locale['step_database_error_table'] = 'Die Tabelle $TABLE$ existiert nicht. Bitte importieren Sie zuerst Ihr OTS-Datenbankschema.';
|
$locale['step_database_error_table'] = 'Die Tabelle $TABLE$ existiert nicht. Bitte importieren Sie zuerst Ihr OTS-Datenbankschema.';
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ $locale['not_loaded'] = 'Not loaded';
|
|||||||
$locale['loading_spinner'] = 'Please wait, installing...';
|
$locale['loading_spinner'] = 'Please wait, installing...';
|
||||||
$locale['importing_spinner'] = 'Please wait, importing data...';
|
$locale['importing_spinner'] = 'Please wait, importing data...';
|
||||||
$locale['please_fill_all'] = 'Please fill all inputs!';
|
$locale['please_fill_all'] = 'Please fill all inputs!';
|
||||||
$locale['already_installed'] = 'MyAAC has been already installed. If you want to reinstall MyAAC - please delete <strong>config.local.php</strong> file from the main directory and refresh the page.';
|
$locale['already_installed'] = 'MyAAC has been already installed. Please delete <b>install/</b> directory. If you want to reinstall MyAAC - please delete <strong>config.local.php</strong> file from the main directory and refresh the page.';
|
||||||
|
|
||||||
// welcome
|
// welcome
|
||||||
$locale['step_welcome'] = 'Welcome';
|
$locale['step_welcome'] = 'Welcome';
|
||||||
@@ -72,7 +72,7 @@ $locale['step_database_title'] = 'Import MySQL schema';
|
|||||||
$locale['step_database_importing'] = 'Your database is MySQL. Database name is: "$DATABASE_NAME$". Importing schema now...';
|
$locale['step_database_importing'] = 'Your database is MySQL. Database name is: "$DATABASE_NAME$". Importing schema now...';
|
||||||
$locale['step_database_config_saved'] = 'Local configuration has been saved into file: config.local.php';
|
$locale['step_database_config_saved'] = 'Local configuration has been saved into file: config.local.php';
|
||||||
$locale['step_database_error_path'] = 'Please specify server path.';
|
$locale['step_database_error_path'] = 'Please specify server path.';
|
||||||
$locale['step_database_error_config'] = 'Cannot find config.lua or config/server.toml file. Is your server path correct? Go back and check again.';
|
$locale['step_database_error_config'] = 'Cannot find config.lua file. Is your server path correct? Go back and check again.';
|
||||||
$locale['step_database_error_database_empty'] = 'Cannot determine database type from config.lua. Your OTS is unsupported by this AAC.';
|
$locale['step_database_error_database_empty'] = 'Cannot determine database type from config.lua. Your OTS is unsupported by this AAC.';
|
||||||
$locale['step_database_error_only_mysql'] = 'This AAC supports only MySQL. From your config file it seems that your OTS is using: $DATABASE_TYPE$ database. Please change your database to MySQL and then follow the installation again.';
|
$locale['step_database_error_only_mysql'] = 'This AAC supports only MySQL. From your config file it seems that your OTS is using: $DATABASE_TYPE$ database. Please change your database to MySQL and then follow the installation again.';
|
||||||
$locale['step_database_error_table'] = 'Table $TABLE$ doesn\'t exist. Please import your OTS database schema first.';
|
$locale['step_database_error_table'] = 'Table $TABLE$ doesn\'t exist. Please import your OTS database schema first.';
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ $locale['not_loaded'] = 'Nie załadowane';
|
|||||||
$locale['loading_spinner'] = 'Proszę czekać, trwa instalacja...';
|
$locale['loading_spinner'] = 'Proszę czekać, trwa instalacja...';
|
||||||
$locale['importing_spinner'] = 'Proszę czekać, trwa importowanie danych...';
|
$locale['importing_spinner'] = 'Proszę czekać, trwa importowanie danych...';
|
||||||
$locale['please_fill_all'] = 'Proszę wypełnić wszystkie pola!';
|
$locale['please_fill_all'] = 'Proszę wypełnić wszystkie pola!';
|
||||||
$locale['already_installed'] = 'MyAAC został już zainstalowany. Jeśli chcesz zainstalować MyAAC od nowa - proszę usuń plik <strong>config.local.php</strong> z katalogu głównego i odśwież stronę.';
|
$locale['already_installed'] = 'MyAAC został już zainstalowany. Proszę usunąć katalog <b>install/</b>. Jeśli chcesz zainstalować MyAAC od nowa - proszę usuń plik <strong>config.local.php</strong> z katalogu głównego i odśwież stronę.';
|
||||||
|
|
||||||
// welcome
|
// welcome
|
||||||
$locale['step_welcome'] = 'Witamy';
|
$locale['step_welcome'] = 'Witamy';
|
||||||
@@ -71,7 +71,7 @@ $locale['step_database_title'] = 'Baza MySQL';
|
|||||||
$locale['step_database_importing'] = 'Twoja baza to MySQL. Nazwa bazy danych to: "$DATABASE_NAME$". Importowanie schematu...';
|
$locale['step_database_importing'] = 'Twoja baza to MySQL. Nazwa bazy danych to: "$DATABASE_NAME$". Importowanie schematu...';
|
||||||
$locale['step_database_config_saved'] = 'Lokalna konfiguracja została zapisana do pliku: config.local.php';
|
$locale['step_database_config_saved'] = 'Lokalna konfiguracja została zapisana do pliku: config.local.php';
|
||||||
$locale['step_database_error_path'] = 'Proszę podać ścieżkę do serwera.';
|
$locale['step_database_error_path'] = 'Proszę podać ścieżkę do serwera.';
|
||||||
$locale['step_database_error_config'] = 'Nie można znaleźć pliku config.lua lub config/server.toml. Czy ścieżka do katalogu serwera jest poprawna? Wróć się i sprawdź ponownie.';
|
$locale['step_database_error_config'] = 'Nie można znaleźć pliku config.lua. Czy ścieżka do katalogu serwera jest poprawna? Wróć się i sprawdź ponownie.';
|
||||||
$locale['step_database_error_database_empty'] = 'Nie można wykryć typu bazy danych z pliku config.lua. Prawdopodobnie Twój OTS nie jest wspierany przez ten AAC.';
|
$locale['step_database_error_database_empty'] = 'Nie można wykryć typu bazy danych z pliku config.lua. Prawdopodobnie Twój OTS nie jest wspierany przez ten AAC.';
|
||||||
$locale['step_database_error_only_mysql'] = 'Ten AAC wspiera tylko bazy danych MySQL. Z Twojego pliku config wynika, że Twój serwera używa bazy: $DATABASE_TYPE$. Proszę zmienić typ bazy na MySQL i ponownie przystąpić do instalacji.';
|
$locale['step_database_error_only_mysql'] = 'Ten AAC wspiera tylko bazy danych MySQL. Z Twojego pliku config wynika, że Twój serwera używa bazy: $DATABASE_TYPE$. Proszę zmienić typ bazy na MySQL i ponownie przystąpić do instalacji.';
|
||||||
$locale['step_database_error_table'] = 'Tabela $TABLE$ nie istnieje. Proszę najpierw zaimportować schemat bazy danych serwera OTS.';
|
$locale['step_database_error_table'] = 'Tabela $TABLE$ nie istnieje. Proszę najpierw zaimportować schemat bazy danych serwera OTS.';
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ $locale['not_loaded'] = 'Não carregado';
|
|||||||
$locale['loading_spinner'] = 'Por favor aguarde, instalando...';
|
$locale['loading_spinner'] = 'Por favor aguarde, instalando...';
|
||||||
$locale['importing_spinner'] = 'Por favor, aguarde, importando dados...';
|
$locale['importing_spinner'] = 'Por favor, aguarde, importando dados...';
|
||||||
$locale['please_fill_all'] = 'Por favor, preencha todas as entradas!';
|
$locale['please_fill_all'] = 'Por favor, preencha todas as entradas!';
|
||||||
$locale['already_installed'] = 'MyAAC já foi instalado. Se você quiser reinstalar o MyAAC - exclua o arquivo <strong> config.local.php </strong> do diretório principal e atualize a página.';
|
$locale['already_installed'] = 'MyAAC já foi instalado. Por favor, apague o diretório <b> install/ <b/>. Se você quiser reinstalar o MyAAC - exclua o arquivo <strong> config.local.php </strong> do diretório principal e atualize a página.';
|
||||||
|
|
||||||
// welcome
|
// welcome
|
||||||
$locale['step_welcome'] = 'Bem vindo';
|
$locale['step_welcome'] = 'Bem vindo';
|
||||||
@@ -61,7 +61,7 @@ $locale['step_database'] = 'Importar schema';
|
|||||||
$locale['step_database_title'] = 'Importar MySQL schema';
|
$locale['step_database_title'] = 'Importar MySQL schema';
|
||||||
$locale['step_database_importing'] = 'Seu banco de dados é o MySQL. O nome do banco de dados é: "$DATABASE_NAME$". Importando schema agora...';
|
$locale['step_database_importing'] = 'Seu banco de dados é o MySQL. O nome do banco de dados é: "$DATABASE_NAME$". Importando schema agora...';
|
||||||
$locale['step_database_error_path'] = 'Por favor, especifique o caminho da pasta do servidor.';
|
$locale['step_database_error_path'] = 'Por favor, especifique o caminho da pasta do servidor.';
|
||||||
$locale['step_database_error_config'] = 'Não é possível encontrar o arquivo config.lua ou config/server.toml. O caminho da pasta do seu servidor está correto? Volte e verifique novamente.';
|
$locale['step_database_error_config'] = 'Não é possível encontrar o arquivo config.lua. O caminho da pasta do seu servidor está correto? Volte e verifique novamente.';
|
||||||
$locale['step_database_error_database_empty'] = 'Não é possível determinar o tipo de banco de dados a partir do config.lua. Seu OTS não é suportado por este AAC.';
|
$locale['step_database_error_database_empty'] = 'Não é possível determinar o tipo de banco de dados a partir do config.lua. Seu OTS não é suportado por este AAC.';
|
||||||
$locale['step_database_error_only_mysql'] = 'Este AAC suporta apenas o MySQL. A partir do seu arquivo de configuração, parece que o seu OTS está usando: $DATABASE_TYPE$ database. Por favor, mude seu banco de dados para o MySQL e siga a instalação novamente.';
|
$locale['step_database_error_only_mysql'] = 'Este AAC suporta apenas o MySQL. A partir do seu arquivo de configuração, parece que o seu OTS está usando: $DATABASE_TYPE$ database. Por favor, mude seu banco de dados para o MySQL e siga a instalação novamente.';
|
||||||
$locale['step_database_error_table'] = 'A tabela $TABLE$ não existe. Por favor, importe seu schema de banco de dados OTS primeiro.';
|
$locale['step_database_error_table'] = 'A tabela $TABLE$ não existe. Por favor, importe seu schema de banco de dados OTS primeiro.';
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ $locale['loaded'] = 'Laddad';
|
|||||||
$locale['not_loaded'] = 'Inte Laddad';
|
$locale['not_loaded'] = 'Inte Laddad';
|
||||||
|
|
||||||
$locale['please_fill_all'] = 'Vänligen fyll i allt!';
|
$locale['please_fill_all'] = 'Vänligen fyll i allt!';
|
||||||
$locale['already_installed'] = 'MyAAC är redan installerat. Om du vill installera MyAAC igen - ta bort filen <strong>config.local.php</strong> från huvudkatalogen och uppdatera sidan.';
|
$locale['already_installed'] = 'MyAAC är redan installerat. Vänligen ta bort <b>install/<b/> mappen. Om du vill installera MyAAC igen - ta bort filen <strong>config.local.php</strong> från huvudkatalogen och uppdatera sidan.';
|
||||||
|
|
||||||
// welcome
|
// welcome
|
||||||
$locale['step_welcome'] = 'Välkommen';
|
$locale['step_welcome'] = 'Välkommen';
|
||||||
|
|||||||
8
system/migrations/51-account_email_codes.sql
Normal file
8
system/migrations/51-account_email_codes.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
CREATE TABLE `myaac_account_email_codes`
|
||||||
|
(
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`account_id` int NOT NULL,
|
||||||
|
`code` varchar(6) NOT NULL,
|
||||||
|
`created_at` int NOT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
|
||||||
@@ -1,10 +1,36 @@
|
|||||||
<?php
|
<?php
|
||||||
|
// 2fa
|
||||||
|
// add the myaac_account_email_codes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var OTS_DB_MySQL $db
|
||||||
|
*/
|
||||||
|
|
||||||
$up = function () use ($db) {
|
$up = function () use ($db) {
|
||||||
$db->modifyColumn(TABLE_PREFIX . 'config', 'name', "varchar(255) NOT NULL");
|
if (!$db->hasColumn('accounts', '2fa_type')) {
|
||||||
$db->modifyColumn(TABLE_PREFIX . 'config', 'value', "varchar(10000) NOT NULL");
|
$db->addColumn('accounts', '2fa_type', "tinyint NOT NULL DEFAULT 0 AFTER `web_flags`");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$db->hasColumn('accounts', '2fa_secret')) {
|
||||||
|
$db->addColumn('accounts', '2fa_secret', "varchar(16) NOT NULL DEFAULT '' AFTER `2fa_type`");
|
||||||
|
}
|
||||||
|
|
||||||
|
// add myaac_account_email_codes table
|
||||||
|
if (!$db->hasTable(TABLE_PREFIX . 'account_email_codes')) {
|
||||||
|
$db->exec(file_get_contents(__DIR__ . '/51-account_email_codes.sql'));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$down = function () {
|
$down = function () use ($db) {
|
||||||
// nothing to do, to not lose data
|
if ($db->hasColumn('accounts', '2fa_type')) {
|
||||||
|
$db->dropColumn('accounts', '2fa_type');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($db->hasColumn('accounts', '2fa_secret')) {
|
||||||
|
$db->dropColumn('accounts', '2fa_secret');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($db->hasTable(TABLE_PREFIX . 'account_email_codes')) {
|
||||||
|
$db->dropTable(TABLE_PREFIX . 'account_email_codes');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* 2026-04-12
|
|
||||||
* Add indexes to myaac_account_actions table
|
|
||||||
*/
|
|
||||||
$up = function () use ($db) {
|
|
||||||
$db->query("CREATE INDEX `myaac_account_actions_account_id` ON `myaac_account_actions` (`account_id`);");
|
|
||||||
$db->query("CREATE INDEX `myaac_account_actions_ip` ON `myaac_account_actions` (`ip`);");
|
|
||||||
};
|
|
||||||
|
|
||||||
$down = function () {
|
|
||||||
// nothing to do, to not lose data
|
|
||||||
};
|
|
||||||
26
system/pages/account/2fa/app/disable.php
Normal file
26
system/pages/account/2fa/app/disable.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
require __DIR__ . '/../base.php';
|
||||||
|
|
||||||
|
if (!isRequestMethod('post')) {
|
||||||
|
error('This page cannot be accessed directly.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$account_logged->isLoaded()) {
|
||||||
|
error('Account not found!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$twoFactorAuth->isActive($twoFactorAuth::TYPE_APP)) {
|
||||||
|
error("Your account does not have Two Factor App Authentication enabled.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$twoFactorAuth->disable();
|
||||||
|
|
||||||
|
$twig->display('success.html.twig', [
|
||||||
|
'title' => 'Disabled',
|
||||||
|
'description' => 'Two Factor App Authentication has been disabled.'
|
||||||
|
]);
|
||||||
105
system/pages/account/2fa/app/enable.php
Normal file
105
system/pages/account/2fa/app/enable.php
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<?php
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
use MyAAC\TwoFactorAuth\TwoFactorAuth;
|
||||||
|
|
||||||
|
require __DIR__ . '/../base.php';
|
||||||
|
|
||||||
|
if ($twoFactorAuth->isActive()) {
|
||||||
|
$errors[] = 'Two-factor authentication is already enabled on your account.';
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$explodeRecoveryKey = explode('-', $account_logged->getCustomField('key'));
|
||||||
|
$newRecoveryKeyFormat = (count($explodeRecoveryKey) == 4);
|
||||||
|
|
||||||
|
if (ACTION == 'request') {
|
||||||
|
|
||||||
|
if ($newRecoveryKeyFormat) {
|
||||||
|
$key = $_POST['key1'] . '-' . $_POST['key2'] . '-' . $_POST['key3'] . '-' . $_POST['key4'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$key = $_POST['key'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$accountKey = $account_logged->getCustomField('key');
|
||||||
|
if (!empty($key) && $key == $accountKey) {
|
||||||
|
$secret = getSession('2fa_secret');
|
||||||
|
if ($secret === null) {
|
||||||
|
$secret = generateRandom2faSecret();
|
||||||
|
setSession('2fa_secret', $secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
$twoFactorAuth->appDisplayEnable($secret);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (empty($key)) {
|
||||||
|
$errors[] = 'Please enter the recovery key!';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = 'Invalid recovery key!';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ACTION == 'link') {
|
||||||
|
$secret = getSession('2fa_secret');
|
||||||
|
|
||||||
|
if ($secret === null) {
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => ['Secret not set. Go back and try again.']]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$authCode = $_POST['auth-code'] ?? '';
|
||||||
|
if (!empty($authCode)) {
|
||||||
|
$otp = $twoFactorAuth->appInitTOTP($secret);
|
||||||
|
|
||||||
|
if (!$otp->verify($authCode)) {
|
||||||
|
$errors = ['Token is invalid!'];
|
||||||
|
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||||
|
|
||||||
|
$twoFactorAuth->appDisplayEnable($secret, $otp, $errors);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($db->hasColumn('accounts', 'secret')) {
|
||||||
|
$account_logged->setCustomField('secret', $secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
$account_logged->setCustomField('2fa_secret', $secret);
|
||||||
|
$twoFactorAuth->enable(TwoFactorAuth::TYPE_APP);
|
||||||
|
|
||||||
|
$twig->display('success.html.twig',
|
||||||
|
[
|
||||||
|
'title' => 'Authenticator App Connected',
|
||||||
|
'description' => 'You successfully connected your Tibia account to an authenticator app.'
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors = ['You have to enter the code generated by the authenticator!'];
|
||||||
|
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||||
|
$twoFactorAuth->appDisplayEnable($secret, null, $errors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errors)) {
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$twig->display('account/2fa/app/enable.warning.html.twig',
|
||||||
|
[
|
||||||
|
'newRecoveryKeyFormat' => $newRecoveryKeyFormat,
|
||||||
|
'errors' => $errors,
|
||||||
|
]
|
||||||
|
);
|
||||||
41
system/pages/account/2fa/base.php
Normal file
41
system/pages/account/2fa/base.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
use MyAAC\TwoFactorAuth\TwoFactorAuth;
|
||||||
|
|
||||||
|
csrfProtect();
|
||||||
|
|
||||||
|
$title = 'Two Factor Authentication';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var OTS_Account $account_logged
|
||||||
|
*/
|
||||||
|
$code = $_REQUEST['auth-code'] ?? '';
|
||||||
|
|
||||||
|
if (!$account_logged->isLoaded()) {
|
||||||
|
$current_session = getSession('account');
|
||||||
|
if($current_session) {
|
||||||
|
$account_logged = new OTS_Account();
|
||||||
|
$account_logged->load($current_session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$twoFactorAuth = TwoFactorAuth::getInstance($account_logged);
|
||||||
|
$twig->addGlobal('account_logged', $account_logged);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Took from ZnoteAAC
|
||||||
|
* @author Znote
|
||||||
|
*/
|
||||||
|
function generateRandom2faSecret($length = 16): string
|
||||||
|
{
|
||||||
|
$characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
||||||
|
$charactersLength = strlen($characters);
|
||||||
|
$randomString = '';
|
||||||
|
|
||||||
|
for ($i = 0; $i < $length; $i++) {
|
||||||
|
$randomString .= $characters[rand(0, $charactersLength - 1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $randomString;
|
||||||
|
}
|
||||||
34
system/pages/account/2fa/email/disable.php
Normal file
34
system/pages/account/2fa/email/disable.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
require __DIR__ . '/../base.php';
|
||||||
|
|
||||||
|
if ((!setting('core.mail_enabled'))) {
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => ['Account Two-Factor E-Mail Authentication disabled.']]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isRequestMethod('post')) {
|
||||||
|
error('This page cannot be accessed directly.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$account_logged->isLoaded()) {
|
||||||
|
error('Account not found!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$twoFactorAuth->isActive($twoFactorAuth::TYPE_EMAIL)) {
|
||||||
|
error("Your account does not have Two Factor E-Mail Authentication enabled.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$twoFactorAuth->disable();
|
||||||
|
$twoFactorAuth->deleteOldCodes();
|
||||||
|
|
||||||
|
$twig->display('success.html.twig',
|
||||||
|
[
|
||||||
|
'title' => 'Email Code Authentication Disabled',
|
||||||
|
'description' => 'You have successfully <strong>disabled</strong> the <b>Email Code Authentication</b> for your account.'
|
||||||
|
]
|
||||||
|
);
|
||||||
51
system/pages/account/2fa/email/enable.php
Normal file
51
system/pages/account/2fa/email/enable.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use MyAAC\TwoFactorAuth\TwoFactorAuth;
|
||||||
|
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
require __DIR__ . '/../base.php';
|
||||||
|
|
||||||
|
if ((!setting('core.mail_enabled'))) {
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => ['Account Two-Factor E-Mail Authentication disabled.']]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($twoFactorAuth->isActive()) {
|
||||||
|
$errors[] = 'Two-factor authentication is already enabled on your account.';
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$twoFactorAuth->hasRecentEmailCode(15 * 60)) {
|
||||||
|
$twoFactorAuth->resendEmailCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['save'])) {
|
||||||
|
if (!empty($code)) {
|
||||||
|
$twoFactorAuth->setAuthGateway(TwoFactorAuth::TYPE_EMAIL);
|
||||||
|
if ($twoFactorAuth->getAuthGateway()->verifyCode($code)) {
|
||||||
|
$serverName = configLua('serverName');
|
||||||
|
|
||||||
|
$twoFactorAuth->enable(TwoFactorAuth::TYPE_EMAIL);
|
||||||
|
$twoFactorAuth->deleteOldCodes();
|
||||||
|
|
||||||
|
$twig->display('success.html.twig', [
|
||||||
|
'title' => 'Email Code Authentication Activated',
|
||||||
|
'description' => sprintf('You have successfully activated <b>email code authentication</b> for your account. This means an <b>email code</b> will be sent to the email address assigned to your account whenever you try to log in to the %s client or the %s website. In order to log in, you will need to enter the <b>most recent email code</b> you have received.', $serverName, $serverName)
|
||||||
|
]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = 'Invalid email code!';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errors)) {
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$twig->display('account/2fa/email/enable.html.twig', ['wrongCode' => count($errors) > 0]);
|
||||||
32
system/pages/account/2fa/email/resend-code.php
Normal file
32
system/pages/account/2fa/email/resend-code.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
require __DIR__ . '/../base.php';
|
||||||
|
|
||||||
|
if ((!setting('core.mail_enabled'))) {
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => ['Account Two-Factor E-Mail Authentication disabled.']]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$account_logged->isLoaded()) {
|
||||||
|
error('Account not found!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($twoFactorAuth->isActive($twoFactorAuth::TYPE_APP)) {
|
||||||
|
error('You have to disable the app auth first!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($twoFactorAuth->hasRecentEmailCode(30 * 60)) {
|
||||||
|
$errors = ['Sorry, one email per 30 minutes'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$twoFactorAuth->resendEmailCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errors)) {
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$twig->display('account/2fa/email/enable.html.twig');
|
||||||
@@ -17,6 +17,10 @@ if(!$logged)
|
|||||||
if(!empty($errors))
|
if(!empty($errors))
|
||||||
$twig->display('error_box.html.twig', array('errors' => $errors));
|
$twig->display('error_box.html.twig', array('errors' => $errors));
|
||||||
|
|
||||||
|
if (defined('HIDE_LOGIN_BOX') && HIDE_LOGIN_BOX) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$twig->display('account.login.html.twig', array(
|
$twig->display('account.login.html.twig', array(
|
||||||
'redirect' => $_REQUEST['redirect'] ?? null,
|
'redirect' => $_REQUEST['redirect'] ?? null,
|
||||||
'account' => USE_ACCOUNT_NAME ? 'Name' : 'Number',
|
'account' => USE_ACCOUNT_NAME ? 'Name' : 'Number',
|
||||||
@@ -30,3 +34,11 @@ if(!$logged)
|
|||||||
else {
|
else {
|
||||||
$show_form = true;
|
$show_form = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateRecoveryKey(): string
|
||||||
|
{
|
||||||
|
return generateRandomString(5, false, true, true) . '-' .
|
||||||
|
generateRandomString(5, false, true, true) . '-' .
|
||||||
|
generateRandomString(5, false, true, true) . '-' .
|
||||||
|
generateRandomString(5, false, true, true);
|
||||||
|
}
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ if($save)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(setting('core.account_create_character_create')) {
|
if(setting('core.account_create_character_create')) {
|
||||||
$character_name = isset($_POST['name']) ? trim(stripslashes($_POST['name'])) : null;
|
$character_name = isset($_POST['name']) ? stripslashes(ucwords(strtolower($_POST['name']))) : null;
|
||||||
$character_sex = isset($_POST['sex']) ? (int)$_POST['sex'] : null;
|
$character_sex = isset($_POST['sex']) ? (int)$_POST['sex'] : null;
|
||||||
$character_vocation = isset($_POST['vocation']) ? (int)$_POST['vocation'] : null;
|
$character_vocation = isset($_POST['vocation']) ? (int)$_POST['vocation'] : null;
|
||||||
$character_town = isset($_POST['town']) ? (int)$_POST['town'] : null;
|
$character_town = isset($_POST['town']) ? (int)$_POST['town'] : null;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use MyAAC\RateLimit;
|
use MyAAC\RateLimit;
|
||||||
|
use MyAAC\TwoFactorAuth\TwoFactorAuth;
|
||||||
|
|
||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
@@ -52,8 +53,18 @@ if(!empty($login_account) && !empty($login_password))
|
|||||||
$errors[] = 'Your account is not verified. Please verify your email address. If the message is not coming check the SPAM folder in your E-Mail client.<br/>' .
|
$errors[] = 'Your account is not verified. Please verify your email address. If the message is not coming check the SPAM folder in your E-Mail client.<br/>' .
|
||||||
'You can resend the Email here: <a href="' . $link . '">' . $link . '</a>';
|
'You can resend the Email here: <a href="' . $link . '">' . $link . '</a>';
|
||||||
} else {
|
} else {
|
||||||
session_regenerate_id();
|
|
||||||
setSession('account', $account_logged->getId());
|
setSession('account', $account_logged->getId());
|
||||||
|
|
||||||
|
if (!$hooks->trigger(HOOK_ACCOUNT_LOGIN_PRE)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$twoFactorAuth = TwoFactorAuth::getInstance($account_logged);
|
||||||
|
if (!$twoFactorAuth->process($login_account, $login_password, $remember_me, $_POST['auth-code'] ?? '')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
session_regenerate_id();
|
||||||
setSession('password', encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $login_password));
|
setSession('password', encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $login_password));
|
||||||
if($remember_me) {
|
if($remember_me) {
|
||||||
setSession('remember_me', true);
|
setSession('remember_me', true);
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
* @copyright 2019 MyAAC
|
* @copyright 2019 MyAAC
|
||||||
* @link https://my-aac.org
|
* @link https://my-aac.org
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use MyAAC\TwoFactorAuth\TwoFactorAuth;
|
||||||
|
|
||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
$title = 'Account Management';
|
$title = 'Account Management';
|
||||||
@@ -116,6 +119,8 @@ $twig->display('account.management.html.twig', array(
|
|||||||
'account_registered' => $account_registered,
|
'account_registered' => $account_registered,
|
||||||
'account_rlname' => $account_rlname,
|
'account_rlname' => $account_rlname,
|
||||||
'account_location' => $account_location,
|
'account_location' => $account_location,
|
||||||
|
'twoFactorViews' => TwoFactorAuth::getInstance($account_logged)->getAccountManageViews(),
|
||||||
|
|
||||||
'actions' => $actions,
|
'actions' => $actions,
|
||||||
'players' => $account_players
|
'players' => $account_players,
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ else
|
|||||||
if($points >= setting('core.account_generate_new_reckey_price'))
|
if($points >= setting('core.account_generate_new_reckey_price'))
|
||||||
{
|
{
|
||||||
$show_form = false;
|
$show_form = false;
|
||||||
$new_rec_key = generateRandomString(10, false, true, true);
|
$new_rec_key = generateRecoveryKey();
|
||||||
|
|
||||||
$mailBody = $twig->render('mail.account.register.html.twig', array(
|
$mailBody = $twig->render('mail.account.register.html.twig', array(
|
||||||
'recovery_key' => $new_rec_key
|
'recovery_key' => $new_rec_key
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ if(isset($_POST['registeraccountsave']) && $_POST['registeraccountsave'] == "1")
|
|||||||
if($reg_password == $account_logged->getPassword()) {
|
if($reg_password == $account_logged->getPassword()) {
|
||||||
if(empty($old_key)) {
|
if(empty($old_key)) {
|
||||||
$show_form = false;
|
$show_form = false;
|
||||||
$new_rec_key = generateRandomString(10, false, true, true);
|
$new_rec_key = generateRecoveryKey();
|
||||||
|
|
||||||
$account_logged->setCustomField("key", $new_rec_key);
|
$account_logged->setCustomField("key", $new_rec_key);
|
||||||
$account_logged->logAction('Generated recovery key.');
|
$account_logged->logAction('Generated recovery key.');
|
||||||
|
|||||||
@@ -11,20 +11,20 @@
|
|||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
$title = 'Experience Stages';
|
$title = 'Experience Stages';
|
||||||
|
|
||||||
if((!isset($config['lua']['experienceStages']) || !getBoolean($config['lua']['experienceStages']))
|
if(file_exists($config['data_path'] . 'XML/stages.xml')) {
|
||||||
&& (!isset($config['lua']['rateUseStages']) || !getBoolean($config['lua']['rateUseStages']))
|
$stages = new DOMDocument();
|
||||||
) {
|
$stages->load($config['data_path'] . 'XML/stages.xml');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($config['lua']['experienceStages']) || !getBoolean($config['lua']['experienceStages']))
|
||||||
|
{
|
||||||
$enabled = false;
|
$enabled = false;
|
||||||
|
|
||||||
if(file_exists($config['data_path'] . 'XML/stages.xml')) {
|
if(isset($stages)) {
|
||||||
$stages = new DOMDocument();
|
|
||||||
$stages->load($config['data_path'] . 'XML/stages.xml');
|
|
||||||
|
|
||||||
foreach($stages->getElementsByTagName('config') as $node) {
|
foreach($stages->getElementsByTagName('config') as $node) {
|
||||||
/** @var DOMElement $node */
|
/** @var DOMElement $node */
|
||||||
if($node->getAttribute('enabled')) {
|
if($node->getAttribute('enabled'))
|
||||||
$enabled = true;
|
$enabled = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,12 +42,21 @@ if((!isset($config['lua']['experienceStages']) || !getBoolean($config['lua']['ex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$stages = new MyAAC\Server\ExpStages();
|
if(!$stages)
|
||||||
$stagesArray = $stages->get();
|
{
|
||||||
|
echo 'Error: cannot load <b>stages.xml</b>!';
|
||||||
if (empty($stagesArray)) {
|
|
||||||
echo 'Error when loading experience stages.';
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$stagesArray = [];
|
||||||
|
foreach($stages->getElementsByTagName('stage') as $stage)
|
||||||
|
{
|
||||||
|
/** @var DOMElement $stage */
|
||||||
|
$maxLevel = $stage->getAttribute('maxlevel');
|
||||||
|
$stagesArray[] = [
|
||||||
|
'levels' => $stage->getAttribute('minlevel') . (isset($maxLevel[0]) ? '-' . $maxLevel : '+'),
|
||||||
|
'multiplier' => $stage->getAttribute('multiplier')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
$twig->display('experience_stages.html.twig', ['stages' => $stagesArray]);
|
$twig->display('experience_stages.html.twig', ['stages' => $stagesArray]);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use MyAAC\Cache\Cache;
|
|||||||
use MyAAC\Models\Player;
|
use MyAAC\Models\Player;
|
||||||
use MyAAC\Models\PlayerDeath;
|
use MyAAC\Models\PlayerDeath;
|
||||||
use MyAAC\Models\PlayerKillers;
|
use MyAAC\Models\PlayerKillers;
|
||||||
use MyAAC\Server\Vocations;
|
use MyAAC\Server\XML\Vocations;
|
||||||
|
|
||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
$title = 'Highscores';
|
$title = 'Highscores';
|
||||||
@@ -207,14 +207,10 @@ if (empty($highscores)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$highscores = $query->get()->map(function($row) {
|
$highscores = $query->get()->map(function($row) {
|
||||||
/**
|
|
||||||
* @var Player $row
|
|
||||||
*/
|
|
||||||
$tmp = $row->toArray();
|
$tmp = $row->toArray();
|
||||||
$tmp['online'] = $row->online_status;
|
$tmp['online'] = $row->online_status;
|
||||||
$tmp['vocation'] = $row->vocation_name;
|
$tmp['vocation'] = $row->vocation_name;
|
||||||
$tmp['outfit_url'] = $row->outfit_url;
|
$tmp['outfit_url'] = $row->outfit_url; // @phpstan-ignore-line
|
||||||
$tmp['link'] = getPlayerLink($row->name, false);
|
|
||||||
unset($tmp['online_table']);
|
unset($tmp['online_table']);
|
||||||
|
|
||||||
return $tmp;
|
return $tmp;
|
||||||
@@ -248,6 +244,7 @@ foreach($highscores as $id => &$player)
|
|||||||
$player['experience'] = number_format($player['experience']);
|
$player['experience'] = number_format($player['experience']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$player['link'] = getPlayerLink($player['name'], false);
|
||||||
$player['flag'] = getFlagImage($player['country']);
|
$player['flag'] = getFlagImage($player['country']);
|
||||||
$player['outfit'] = '<img style="position:absolute;margin-top:-50px;margin-left:-30px" src="' . $player['outfit_url'] . '" alt="" />';
|
$player['outfit'] = '<img style="position:absolute;margin-top:-50px;margin-left:-30px" src="' . $player['outfit_url'] . '" alt="" />';
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
use MyAAC\Cache\Cache;
|
use MyAAC\Cache\Cache;
|
||||||
use MyAAC\Models\ServerConfig;
|
use MyAAC\Models\ServerConfig;
|
||||||
use MyAAC\Models\ServerRecord;
|
use MyAAC\Models\ServerRecord;
|
||||||
use MyAAC\Server\Vocations;
|
use MyAAC\Server\XML\Vocations;
|
||||||
|
|
||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
$title = 'Who is online?';
|
$title = 'Who is online?';
|
||||||
@@ -87,16 +87,13 @@ $cached = Cache::remember("online_$order", setting('core.online_cache_ttl') * 60
|
|||||||
'name' => getPlayerLink($player['name']),
|
'name' => getPlayerLink($player['name']),
|
||||||
'player' => $player,
|
'player' => $player,
|
||||||
'level' => $player['level'],
|
'level' => $player['level'],
|
||||||
'vocation' => $configVocations[$player['vocation']] ?? 'Unknown',
|
'vocation' => $configVocations[$player['vocation']],
|
||||||
'skull' => $skull,
|
'skull' => $skull,
|
||||||
'country_image' => getFlagImage($player['country']),
|
'country_image' => getFlagImage($player['country']),
|
||||||
'outfit' => setting('core.outfit_images_url') . '?id=' . $player['looktype'] . ($outfit_addons ? '&addons=' . $player['lookaddons'] : '') . '&head=' . $player['lookhead'] . '&body=' . $player['lookbody'] . '&legs=' . $player['looklegs'] . '&feet=' . $player['lookfeet'],
|
'outfit' => setting('core.outfit_images_url') . '?id=' . $player['looktype'] . ($outfit_addons ? '&addons=' . $player['lookaddons'] : '') . '&head=' . $player['lookhead'] . '&body=' . $player['lookbody'] . '&legs=' . $player['looklegs'] . '&feet=' . $player['lookfeet'],
|
||||||
);
|
);
|
||||||
|
|
||||||
$originalId = Vocations::getOriginal($player['vocation']);
|
$vocations[Vocations::getOriginal($player['vocation'])]++;
|
||||||
if ($originalId) {
|
|
||||||
$vocations[$originalId]++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$record = '';
|
$record = '';
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use MyAAC\Cache;
|
use MyAAC\Cache;
|
||||||
use MyAAC\Server\Config;
|
|
||||||
use MyAAC\Settings;
|
use MyAAC\Settings;
|
||||||
|
|
||||||
$templates = Cache::remember('templates', 5 * 60, function () {
|
$templates = Cache::remember('templates', 5 * 60, function () {
|
||||||
@@ -1803,8 +1802,8 @@ Sent by MyAAC,<br/>
|
|||||||
|
|
||||||
// test config.lua existence
|
// test config.lua existence
|
||||||
// if fail - revert the setting and inform the user
|
// if fail - revert the setting and inform the user
|
||||||
if (!Config::exists()) {
|
if (!file_exists($server_path . 'config.lua')) {
|
||||||
error('Server Path is invalid - cannot find config.lua or config/server.toml in the directory. Setting have been reverted.');
|
error('Server Path is invalid - cannot find config.lua in the directory. Setting have been reverted.');
|
||||||
$configToSave['server_path'] = $configOriginal['server_path'];
|
$configToSave['server_path'] = $configOriginal['server_path'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace MyAAC\Commands;
|
namespace MyAAC\Commands;
|
||||||
|
|
||||||
use MyAAC\Server\Config;
|
|
||||||
use POT;
|
use POT;
|
||||||
|
|
||||||
trait Env
|
trait Env
|
||||||
@@ -22,7 +21,7 @@ trait Env
|
|||||||
if($config['server_path'][strlen($config['server_path']) - 1] !== '/')
|
if($config['server_path'][strlen($config['server_path']) - 1] !== '/')
|
||||||
$config['server_path'] .= '/';
|
$config['server_path'] .= '/';
|
||||||
|
|
||||||
$config['server'] = $config['lua'] = Config::get();
|
$config['lua'] = load_config_lua($config['server_path'] . 'config.lua');
|
||||||
|
|
||||||
// POT
|
// POT
|
||||||
require_once SYSTEM . 'libs/pot/OTS.php';
|
require_once SYSTEM . 'libs/pot/OTS.php';
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class GiveAdminCommand extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!$account->isLoaded()) {
|
if (!$account->isLoaded()) {
|
||||||
$io->error('Cannot find account with supplied parameter: ' . $accountParam);
|
$io->error('Cannot find account mit supplied parameter: ' . $accountParam);
|
||||||
return self::FAILURE;
|
return self::FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ namespace MyAAC;
|
|||||||
|
|
||||||
use MyAAC\Cache\Cache;
|
use MyAAC\Cache\Cache;
|
||||||
use MyAAC\Models\Town;
|
use MyAAC\Models\Town;
|
||||||
use MyAAC\Server\Items;
|
|
||||||
|
|
||||||
class DataLoader
|
class DataLoader
|
||||||
{
|
{
|
||||||
@@ -41,7 +40,7 @@ class DataLoader
|
|||||||
{
|
{
|
||||||
self::$startTime = microtime(true);
|
self::$startTime = microtime(true);
|
||||||
|
|
||||||
if(Items::load()) {
|
if(Items::loadFromXML()) {
|
||||||
success(self::$locale['step_database_loaded_items'] . self::getLoadedTime());
|
success(self::$locale['step_database_loaded_items'] . self::getLoadedTime());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Debug;
|
|
||||||
|
|
||||||
use DebugBar\DataCollector\PDO\PDOCollector;
|
|
||||||
use DebugBar\DataCollector\PDO\TraceablePDO;
|
|
||||||
use DebugBar\DataCollector\TimeDataCollector;
|
|
||||||
|
|
||||||
class PDOCollectorWithBacktrace extends PDOCollector
|
|
||||||
{
|
|
||||||
protected function collectPDO(TraceablePDO $pdo, ?TimeDataCollector $timeCollector = null, $connectionName = null): array
|
|
||||||
{
|
|
||||||
$data = parent::collectPDO($pdo, $timeCollector, $connectionName);
|
|
||||||
|
|
||||||
if ($pdo instanceof TraceablePDOWithBacktrace) {
|
|
||||||
$backtraces = $pdo->getBacktraces();
|
|
||||||
foreach ($data['statements'] as $i => &$stmt) {
|
|
||||||
if (isset($backtraces[$i])) {
|
|
||||||
$stmt['backtrace'] = $this->formatBacktrace($backtraces[$i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unset($stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function formatBacktrace(array $backtrace): array
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
foreach ($backtrace as $frame) {
|
|
||||||
if (!isset($frame['file'], $frame['line'])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (str_contains($frame['file'], DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (str_contains($frame['file'], DIRECTORY_SEPARATOR . 'Debug' . DIRECTORY_SEPARATOR)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$function = isset($frame['class'])
|
|
||||||
? $frame['class'] . ($frame['type'] ?? '::') . ($frame['function'] ?? '')
|
|
||||||
: ($frame['function'] ?? '');
|
|
||||||
|
|
||||||
$result[] = ($function ? $function . '() ' : '') . $frame['file'] . ':' . $frame['line'];
|
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Debug;
|
|
||||||
|
|
||||||
use DebugBar\DataCollector\PDO\TraceablePDO;
|
|
||||||
use DebugBar\DataCollector\PDO\TracedStatement;
|
|
||||||
|
|
||||||
class TraceablePDOWithBacktrace extends TraceablePDO
|
|
||||||
{
|
|
||||||
/** @var array[] */
|
|
||||||
protected array $backtraces = [];
|
|
||||||
|
|
||||||
public function addExecutedStatement(TracedStatement $stmt): void
|
|
||||||
{
|
|
||||||
$this->backtraces[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
|
||||||
parent::addExecutedStatement($stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array[]
|
|
||||||
*/
|
|
||||||
public function getBacktraces(): array
|
|
||||||
{
|
|
||||||
return $this->backtraces;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,26 +14,6 @@ class Hooks
|
|||||||
self::$_hooks[$hook->type()][] = $hook;
|
self::$_hooks[$hook->type()][] = $hook;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function unregister($name, $type, $file): void
|
|
||||||
{
|
|
||||||
if (is_string($type)) {
|
|
||||||
$type = constant($type);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isset(self::$_hooks[$type])) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach(self::$_hooks[$type] as $id => $hook) {
|
|
||||||
if($name == $hook->name()
|
|
||||||
&& $type == $hook->type()
|
|
||||||
&& $file == $hook->file()
|
|
||||||
) {
|
|
||||||
unset(self::$_hooks[$type][$id]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function trigger($type, $params = []): bool
|
public function trigger($type, $params = []): bool
|
||||||
{
|
{
|
||||||
$ret = true;
|
$ret = true;
|
||||||
|
|||||||
@@ -1,14 +1,172 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* Items class
|
||||||
* This class is deprecated and will be removed in future versions. Please use the appropriate MyAAC\Server\Items class instead.
|
*
|
||||||
|
* @package MyAAC
|
||||||
|
* @author Gesior <jerzyskalski@wp.pl>
|
||||||
|
* @author Slawkens <slawkens@gmail.com>
|
||||||
|
* @copyright 2019 MyAAC
|
||||||
|
* @link https://my-aac.org
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace MyAAC;
|
namespace MyAAC;
|
||||||
|
|
||||||
class Items extends Server\Items
|
use MyAAC\Cache\PHP as CachePHP;
|
||||||
|
use MyAAC\Models\Spell;
|
||||||
|
|
||||||
|
class Items
|
||||||
{
|
{
|
||||||
public static function load(): bool {
|
private static $error = '';
|
||||||
parent::init();
|
public static $items;
|
||||||
|
|
||||||
|
public static function loadFromXML($show = false)
|
||||||
|
{
|
||||||
|
$file_path = config('data_path') . 'items/items.xml';
|
||||||
|
if (!file_exists($file_path)) {
|
||||||
|
self::$error = 'Cannot load file ' . $file_path;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$xml = new \DOMDocument;
|
||||||
|
$xml->load($file_path);
|
||||||
|
|
||||||
|
$items = array();
|
||||||
|
foreach ($xml->getElementsByTagName('item') as $item) {
|
||||||
|
if ($item->getAttribute('fromid')) {
|
||||||
|
for ($id = $item->getAttribute('fromid'); $id <= $item->getAttribute('toid'); $id++) {
|
||||||
|
$tmp = self::parseNode($id, $item, $show);
|
||||||
|
$items[$tmp['id']] = $tmp['content'];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$tmp = self::parseNode($item->getAttribute('id'), $item, $show);
|
||||||
|
$items[$tmp['id']] = $tmp['content'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$cache_php = new CachePHP(config('cache_prefix'), CACHE . 'persistent/');
|
||||||
|
$cache_php->set('items', $items, 5 * 365 * 24 * 60 * 60);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function parseNode($id, $node, $show = false) {
|
||||||
|
$name = $node->getAttribute('name');
|
||||||
|
$article = $node->getAttribute('article');
|
||||||
|
$plural = $node->getAttribute('plural');
|
||||||
|
|
||||||
|
$attributes = array();
|
||||||
|
foreach($node->getElementsByTagName('attribute') as $attr) {
|
||||||
|
$attributes[strtolower($attr->getAttribute('key'))] = $attr->getAttribute('value');
|
||||||
|
}
|
||||||
|
|
||||||
|
return array('id' => $id, 'content' => array('article' => $article, 'name' => $name, 'plural' => $plural, 'attributes' => $attributes));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getError() {
|
||||||
|
return self::$error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function load() {
|
||||||
|
if(self::$items) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cache_php = new CachePHP(config('cache_prefix'), CACHE . 'persistent/');
|
||||||
|
self::$items = $cache_php->get('items');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get($id) {
|
||||||
|
self::load();
|
||||||
|
return self::$items[$id] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getDescription($id, $count = 1): string
|
||||||
|
{
|
||||||
|
$item = self::get($id);
|
||||||
|
|
||||||
|
$attr = $item['attributes'];
|
||||||
|
$s = '';
|
||||||
|
if(!empty($item['name'])) {
|
||||||
|
if($count > 1) {
|
||||||
|
if($attr['showcount']) {
|
||||||
|
$s .= $count . ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($item['plural'])) {
|
||||||
|
$s .= $item['plural'];
|
||||||
|
}
|
||||||
|
else if((int)$attr['showcount'] == 0) {
|
||||||
|
$s .= $item['name'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$s .= $item['name'] . 's';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(!empty($item['aticle'])) {
|
||||||
|
$s .= $item['article'] . ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
$s .= $item['name'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$s .= 'an item of type ' . $item['id'];
|
||||||
|
|
||||||
|
if(isset($attr['type']) && strtolower($attr['type']) == 'rune') {
|
||||||
|
$spell = Spell::where('item_id', $id)->first();
|
||||||
|
if($spell) {
|
||||||
|
if($spell->level > 0 && $spell->maglevel > 0) {
|
||||||
|
$s .= '. ' . ($count > 1 ? 'They' : 'It') . ' can only be used by ';
|
||||||
|
}
|
||||||
|
|
||||||
|
$configVocations = config('vocations');
|
||||||
|
if(!empty(trim($spell->vocations))) {
|
||||||
|
$vocations = json_decode($spell->vocations);
|
||||||
|
if(count($vocations) > 0) {
|
||||||
|
foreach($vocations as $voc => $show) {
|
||||||
|
$vocations[$configVocations[$voc]] = $show;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$s .= 'players';
|
||||||
|
}
|
||||||
|
|
||||||
|
$s .= ' with';
|
||||||
|
|
||||||
|
if ($spell->level > 0) {
|
||||||
|
$s .= ' level ' . $spell->level;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($spell->maglevel > 0) {
|
||||||
|
if ($spell->level > 0) {
|
||||||
|
$s .= ' and';
|
||||||
|
}
|
||||||
|
|
||||||
|
$s .= ' magic level ' . $spell->maglevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
$s .= ' or higher';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($item['weaponType'])) {
|
||||||
|
if ($item['weaponType'] == 'distance' && isset($item['ammoType'])) {
|
||||||
|
$s .= ' (Range:' . $item['range'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($item['attack']) && $item['attack'] != 0) {
|
||||||
|
$s .= ', Atk ' . ($item['attack'] > 0 ? '+' . $item['attack'] : '-' . $item['attack']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($item['hitChance']) && $item['hitChance'] != -1) {
|
||||||
|
$s .= ', Hit% ' . ($item['hitChance'] > 0 ? '+' . $item['hitChance'] : '-' . $item['hitChance']);
|
||||||
|
}
|
||||||
|
elseif ($item['weaponType'] != 'ammo') {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $s;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,15 +18,6 @@ class Account extends Model {
|
|||||||
|
|
||||||
public $timestamps = false;
|
public $timestamps = false;
|
||||||
|
|
||||||
protected $fillable = [
|
|
||||||
'name', 'number', 'email', 'password',
|
|
||||||
'key', 'created', 'rlname', 'location', 'country',
|
|
||||||
'web_lastlogin', 'web_flags',
|
|
||||||
'email_new', 'email_new_time', 'email_code',
|
|
||||||
'premium_points', 'coins', 'coins_transferable',
|
|
||||||
'premium_ends_at', 'premend', 'lastday', 'premdays',
|
|
||||||
];
|
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'lastday' => 'integer',
|
'lastday' => 'integer',
|
||||||
'premdays' => 'integer',
|
'premdays' => 'integer',
|
||||||
|
|||||||
14
system/src/Models/AccountEMailCode.php
Normal file
14
system/src/Models/AccountEMailCode.php
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyAAC\Models;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class AccountEMailCode extends Model {
|
||||||
|
|
||||||
|
protected $table = TABLE_PREFIX . 'account_email_codes';
|
||||||
|
|
||||||
|
public $timestamps = false;
|
||||||
|
|
||||||
|
protected $fillable = ['account_id', 'code', 'created_at'];
|
||||||
|
|
||||||
|
}
|
||||||
15
system/src/Models/BugTracker.php
Normal file
15
system/src/Models/BugTracker.php
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyAAC\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class BugTracker extends Model {
|
||||||
|
|
||||||
|
protected $table = TABLE_PREFIX . 'bugtracker';
|
||||||
|
|
||||||
|
public $timestamps = false;
|
||||||
|
|
||||||
|
protected $fillable = ['account', 'type', 'status', 'text', 'id', 'subject', 'reply', 'who', 'uid', 'tag'];
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,10 +5,8 @@ use Illuminate\Database\Eloquent\Model;
|
|||||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property string $name
|
|
||||||
* @property int $level
|
* @property int $level
|
||||||
* @property int $vocation
|
* @property int $vocation
|
||||||
* @property int $promotion
|
|
||||||
* @property int $online
|
* @property int $online
|
||||||
* @property int $looktype
|
* @property int $looktype
|
||||||
* @property int $lookhead
|
* @property int $lookhead
|
||||||
@@ -16,8 +14,6 @@ use Illuminate\Database\Eloquent\Relations\HasOne;
|
|||||||
* @property int $looklegs
|
* @property int $looklegs
|
||||||
* @property int $lookfeet
|
* @property int $lookfeet
|
||||||
* @property int $lookaddons
|
* @property int $lookaddons
|
||||||
* @property bool $online_status
|
|
||||||
* @property string $vocation_name
|
|
||||||
* @property string $outfit_url
|
* @property string $outfit_url
|
||||||
* @property hasOne $onlineTable
|
* @property hasOne $onlineTable
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -12,11 +12,13 @@
|
|||||||
namespace MyAAC;
|
namespace MyAAC;
|
||||||
|
|
||||||
use MyAAC\Models\Monster;
|
use MyAAC\Models\Monster;
|
||||||
use MyAAC\Server\Items;
|
|
||||||
|
|
||||||
class Monsters {
|
class Monsters {
|
||||||
private static \OTS_MonstersList $monstersList;
|
/**
|
||||||
private static string $lastError = '';
|
* @var \OTS_MonstersList
|
||||||
|
*/
|
||||||
|
private static $monstersList;
|
||||||
|
private static $lastError = '';
|
||||||
|
|
||||||
public static function loadFromXML($show = false) {
|
public static function loadFromXML($show = false) {
|
||||||
try {
|
try {
|
||||||
@@ -37,7 +39,7 @@ class Monsters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$items = array();
|
$items = array();
|
||||||
Items::init();
|
Items::load();
|
||||||
foreach((array)Items::$items as $id => $item) {
|
foreach((array)Items::$items as $id => $item) {
|
||||||
$items[$item['name']] = $id;
|
$items[$item['name']] = $id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,9 @@ use MyAAC\Cache\Cache;
|
|||||||
use MyAAC\Models\Menu;
|
use MyAAC\Models\Menu;
|
||||||
|
|
||||||
class Plugins {
|
class Plugins {
|
||||||
private static array $warnings = [];
|
private static $warnings = [];
|
||||||
private static string $error = '';
|
private static $error = null;
|
||||||
private static array $plugin_json = [];
|
private static $plugin_json = [];
|
||||||
|
|
||||||
const DEFAULT_PRIORITY = 1000;
|
|
||||||
|
|
||||||
public static function getInits()
|
public static function getInits()
|
||||||
{
|
{
|
||||||
@@ -22,31 +20,13 @@ class Plugins {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$initPriority = self::DEFAULT_PRIORITY;
|
|
||||||
if (isset($plugin['autoload']['init-priority'])) {
|
|
||||||
$initPriority = (int) $plugin['autoload']['init-priority'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$pluginInits = glob(PLUGINS . $plugin['filename'] . '/init.php');
|
$pluginInits = glob(PLUGINS . $plugin['filename'] . '/init.php');
|
||||||
foreach ($pluginInits as $path) {
|
foreach ($pluginInits as $path) {
|
||||||
$inits[] = [
|
$inits[] = $path;
|
||||||
'file' => $path,
|
|
||||||
'priority' => $initPriority
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
usort($inits, function ($a, $b)
|
return $inits;
|
||||||
{
|
|
||||||
return $a['priority'] <=> $b['priority'];
|
|
||||||
});
|
|
||||||
|
|
||||||
$ret = [];
|
|
||||||
foreach ($inits as $init) {
|
|
||||||
$ret[] = $init['file'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ret;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +39,7 @@ class Plugins {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$adminPagesDefaultPriority = self::DEFAULT_PRIORITY;
|
$adminPagesDefaultPriority = 1000;
|
||||||
if (isset($plugin['admin-pages-default-priority'])) {
|
if (isset($plugin['admin-pages-default-priority'])) {
|
||||||
$adminPagesDefaultPriority = $plugin['admin-pages-default-priority'];
|
$adminPagesDefaultPriority = $plugin['admin-pages-default-priority'];
|
||||||
}
|
}
|
||||||
@@ -137,7 +117,7 @@ class Plugins {
|
|||||||
|
|
||||||
$routes = [];
|
$routes = [];
|
||||||
foreach(self::getAllPluginsJson() as $plugin) {
|
foreach(self::getAllPluginsJson() as $plugin) {
|
||||||
$routesDefaultPriority = self::DEFAULT_PRIORITY;
|
$routesDefaultPriority = 1000;
|
||||||
if (isset($plugin['routes-default-priority'])) {
|
if (isset($plugin['routes-default-priority'])) {
|
||||||
$routesDefaultPriority = $plugin['routes-default-priority'];
|
$routesDefaultPriority = $plugin['routes-default-priority'];
|
||||||
}
|
}
|
||||||
@@ -185,7 +165,7 @@ class Plugins {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$pagesDefaultPriority = self::DEFAULT_PRIORITY;
|
$pagesDefaultPriority = 1000;
|
||||||
if (isset($plugin['pages-default-priority'])) {
|
if (isset($plugin['pages-default-priority'])) {
|
||||||
$pagesDefaultPriority = $plugin['pages-default-priority'];
|
$pagesDefaultPriority = $plugin['pages-default-priority'];
|
||||||
}
|
}
|
||||||
@@ -338,7 +318,7 @@ class Plugins {
|
|||||||
foreach(self::getAllPluginsJson() as $plugin) {
|
foreach(self::getAllPluginsJson() as $plugin) {
|
||||||
if (isset($plugin['hooks'])) {
|
if (isset($plugin['hooks'])) {
|
||||||
foreach ($plugin['hooks'] as $_name => $info) {
|
foreach ($plugin['hooks'] as $_name => $info) {
|
||||||
$priority = self::DEFAULT_PRIORITY;
|
$priority = 1000;
|
||||||
|
|
||||||
if (str_contains($info['type'], 'HOOK_')) {
|
if (str_contains($info['type'], 'HOOK_')) {
|
||||||
$info['type'] = str_replace('HOOK_', '', $info['type']);
|
$info['type'] = str_replace('HOOK_', '', $info['type']);
|
||||||
@@ -452,7 +432,7 @@ class Plugins {
|
|||||||
return $plugins;
|
return $plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getPluginSettings($filename): mixed
|
public static function getPluginSettings($filename)
|
||||||
{
|
{
|
||||||
$plugin_json = self::getPluginJson($filename);
|
$plugin_json = self::getPluginJson($filename);
|
||||||
if (!$plugin_json) {
|
if (!$plugin_json) {
|
||||||
@@ -888,15 +868,6 @@ class Plugins {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
global $hooks;
|
|
||||||
foreach($plugin_info['hooks'] ?? [] as $name => $info) {
|
|
||||||
if (str_contains($info['type'], 'HOOK_')) {
|
|
||||||
$info['type'] = str_replace('HOOK_', '', $info['type']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$hooks->unregister($name, 'HOOK_' . $info['type'], $info['file']);
|
|
||||||
}
|
|
||||||
|
|
||||||
clearCache();
|
clearCache();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -921,15 +892,15 @@ class Plugins {
|
|||||||
return Semver::satisfies($plugin_info['version'], $version);
|
return Semver::satisfies($plugin_info['version'], $version);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getWarnings(): array {
|
public static function getWarnings() {
|
||||||
return self::$warnings;
|
return self::$warnings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function clearWarnings(): void {
|
public static function clearWarnings() {
|
||||||
self::$warnings = [];
|
self::$warnings = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getError(): string {
|
public static function getError() {
|
||||||
return self::$error;
|
return self::$error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -940,7 +911,7 @@ class Plugins {
|
|||||||
* @param string $templateName
|
* @param string $templateName
|
||||||
* @param array $menus
|
* @param array $menus
|
||||||
*/
|
*/
|
||||||
public static function installMenus($templateName, $menus, $clearOld = false): void
|
public static function installMenus($templateName, $menus, $clearOld = false)
|
||||||
{
|
{
|
||||||
global $db;
|
global $db;
|
||||||
|
|
||||||
@@ -991,7 +962,7 @@ class Plugins {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function getAutoLoadOption(array $plugin, string $optionName, bool $default = true): bool
|
private static function getAutoLoadOption(array $plugin, string $optionName, bool $default = true)
|
||||||
{
|
{
|
||||||
if (isset($plugin['autoload'])) {
|
if (isset($plugin['autoload'])) {
|
||||||
$autoload = $plugin['autoload'];
|
$autoload = $plugin['autoload'];
|
||||||
@@ -1000,7 +971,7 @@ class Plugins {
|
|||||||
return getBoolean($autoload[$optionName]);
|
return getBoolean($autoload[$optionName]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elseif (is_bool($autoload)) {
|
else if (is_bool($autoload)) {
|
||||||
return $autoload;
|
return $autoload;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server;
|
|
||||||
|
|
||||||
use MyAAC\Cache\Cache;
|
|
||||||
|
|
||||||
class Config
|
|
||||||
{
|
|
||||||
public static function get()
|
|
||||||
{
|
|
||||||
return Cache::remember('config_server', 10 * 60, function () {
|
|
||||||
if (file_exists(config('server_path') . Lua\Config::FILE)) {
|
|
||||||
$config = new Lua\Config();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$config = new TOML\Config();
|
|
||||||
}
|
|
||||||
|
|
||||||
$config->load();
|
|
||||||
|
|
||||||
return $config->get();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function exists(): bool {
|
|
||||||
return file_exists(config('server_path') . Lua\Config::FILE) || file_exists(config('server_path') . 'config/server.toml');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server;
|
|
||||||
|
|
||||||
use MyAAC\Cache\Cache;
|
|
||||||
|
|
||||||
class ExpStages
|
|
||||||
{
|
|
||||||
private static array $stages = [];
|
|
||||||
|
|
||||||
public static function get() {
|
|
||||||
if (count(self::$stages) == 0) {
|
|
||||||
self::$stages = Cache::remember('exp_stages', 10 * 60, function () {
|
|
||||||
if (file_exists(config('server_path') . TOML\ExpStages::FILE)) {
|
|
||||||
$expStages = new TOML\ExpStages();
|
|
||||||
}
|
|
||||||
elseif (file_exists(config('data_path') . XML\ExpStages::FILE)) {
|
|
||||||
$expStages = new XML\ExpStages();
|
|
||||||
}
|
|
||||||
elseif (file_exists(config('data_path') . Lua\ExpStages::FILE)) {
|
|
||||||
$expStages = new Lua\ExpStages();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$expStages->load();
|
|
||||||
|
|
||||||
return $expStages->get();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::$stages;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server;
|
|
||||||
|
|
||||||
use MyAAC\Cache\Cache;
|
|
||||||
|
|
||||||
class Groups
|
|
||||||
{
|
|
||||||
private static array $groups = [];
|
|
||||||
|
|
||||||
public static function get() {
|
|
||||||
if (count(self::$groups) == 0) {
|
|
||||||
self::$groups = Cache::remember('groups', 10 * 60, function () {
|
|
||||||
if (file_exists(config('server_path') . TOML\Groups::FILE)) {
|
|
||||||
$groups = new TOML\Groups();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$groups = new XML\Groups();
|
|
||||||
}
|
|
||||||
|
|
||||||
$groups->load();
|
|
||||||
|
|
||||||
return $groups->get();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::$groups;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Items class
|
|
||||||
*
|
|
||||||
* @package MyAAC
|
|
||||||
* @author Gesior <jerzyskalski@wp.pl>
|
|
||||||
* @author Slawkens <slawkens@gmail.com>
|
|
||||||
* @copyright 2019 MyAAC
|
|
||||||
* @link https://my-aac.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace MyAAC\Server;
|
|
||||||
|
|
||||||
use MyAAC\Cache\PHP as CachePHP;
|
|
||||||
use MyAAC\Models\Spell;
|
|
||||||
|
|
||||||
class Items
|
|
||||||
{
|
|
||||||
public static array $items = [];
|
|
||||||
|
|
||||||
private static string $error = '';
|
|
||||||
|
|
||||||
const FILE_ITEMS_TOML = 'items/items.toml';
|
|
||||||
const FILE_ITEMS_XML = 'items/items.xml';
|
|
||||||
|
|
||||||
public static function getError(): string {
|
|
||||||
return self::$error;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function load(): bool {
|
|
||||||
if (file_exists(config('data_path') . self::FILE_ITEMS_TOML)) {
|
|
||||||
$items = new TOML\Items();
|
|
||||||
}
|
|
||||||
elseif (file_exists(config('data_path') . self::FILE_ITEMS_XML)) {
|
|
||||||
$items = new XML\Items();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
self::$error = 'Cannot load items.xml or items.toml file. Files not found.';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$items->load()) {
|
|
||||||
self::$error = $items->getError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function init(): void {
|
|
||||||
if(count(self::$items) > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$cache_php = new CachePHP(config('cache_prefix'), CACHE . 'persistent/');
|
|
||||||
self::$items = (array)$cache_php->get('items');
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function get($id) {
|
|
||||||
self::init();
|
|
||||||
return self::$items[$id] ?? [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getDescription($id, $count = 1): string
|
|
||||||
{
|
|
||||||
$item = self::get($id);
|
|
||||||
|
|
||||||
$attr = $item['attributes'];
|
|
||||||
$s = '';
|
|
||||||
if(!empty($item['name'])) {
|
|
||||||
if($count > 1) {
|
|
||||||
if($attr['showcount']) {
|
|
||||||
$s .= $count . ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!empty($item['plural'])) {
|
|
||||||
$s .= $item['plural'];
|
|
||||||
}
|
|
||||||
else if((int)$attr['showcount'] == 0) {
|
|
||||||
$s .= $item['name'];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$s .= $item['name'] . 's';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(!empty($item['aticle'])) {
|
|
||||||
$s .= $item['article'] . ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
$s .= $item['name'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$s .= 'an item of type ' . $item['id'];
|
|
||||||
|
|
||||||
if(isset($attr['type']) && strtolower($attr['type']) == 'rune') {
|
|
||||||
$spell = Spell::where('item_id', $id)->first();
|
|
||||||
if($spell) {
|
|
||||||
if($spell->level > 0 && $spell->maglevel > 0) {
|
|
||||||
$s .= '. ' . ($count > 1 ? 'They' : 'It') . ' can only be used by ';
|
|
||||||
}
|
|
||||||
|
|
||||||
$configVocations = config('vocations');
|
|
||||||
if(!empty(trim($spell->vocations))) {
|
|
||||||
$vocations = json_decode($spell->vocations);
|
|
||||||
if(count($vocations) > 0) {
|
|
||||||
foreach($vocations as $voc => $show) {
|
|
||||||
$vocations[$configVocations[$voc]] = $show;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$s .= 'players';
|
|
||||||
}
|
|
||||||
|
|
||||||
$s .= ' with';
|
|
||||||
|
|
||||||
if ($spell->level > 0) {
|
|
||||||
$s .= ' level ' . $spell->level;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($spell->maglevel > 0) {
|
|
||||||
if ($spell->level > 0) {
|
|
||||||
$s .= ' and';
|
|
||||||
}
|
|
||||||
|
|
||||||
$s .= ' magic level ' . $spell->maglevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
$s .= ' or higher';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($item['weaponType'])) {
|
|
||||||
if ($item['weaponType'] == 'distance' && isset($item['ammoType'])) {
|
|
||||||
$s .= ' (Range:' . $item['range'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($item['attack']) && $item['attack'] != 0) {
|
|
||||||
$s .= ', Atk ' . ($item['attack'] > 0 ? '+' . $item['attack'] : '-' . $item['attack']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($item['hitChance']) && $item['hitChance'] != -1) {
|
|
||||||
$s .= ', Hit% ' . ($item['hitChance'] > 0 ? '+' . $item['hitChance'] : '-' . $item['hitChance']);
|
|
||||||
}
|
|
||||||
elseif ($item['weaponType'] != 'ammo') {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server\Lua;
|
|
||||||
|
|
||||||
class Config
|
|
||||||
{
|
|
||||||
const FILE = 'config.lua';
|
|
||||||
|
|
||||||
private array $config = [];
|
|
||||||
|
|
||||||
public function load(): void
|
|
||||||
{
|
|
||||||
$file = config('server_path') . self::FILE;
|
|
||||||
|
|
||||||
$this->config = Loader::load($file);
|
|
||||||
if($this->config === false) {
|
|
||||||
log_append('error.log', '[Config] Fatal error: Cannot load config.lua (' . $file . ').');
|
|
||||||
throw new \RuntimeException('ERROR: Cannot find ' . $file . ' file.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get(): array {
|
|
||||||
return $this->config;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server\Lua;
|
|
||||||
|
|
||||||
class ExpStages
|
|
||||||
{
|
|
||||||
private array $stages = [];
|
|
||||||
|
|
||||||
const FILE = 'stages.lua';
|
|
||||||
|
|
||||||
public function load(): void
|
|
||||||
{
|
|
||||||
$file = config('data_path') . self::FILE;
|
|
||||||
|
|
||||||
if(!@file_exists($file)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!extension_loaded('lua')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$lua = new \Lua();
|
|
||||||
try {
|
|
||||||
$stagesContent = file_get_contents($file);
|
|
||||||
$stagesContent .= 'return experienceStages';
|
|
||||||
$stages = $lua->eval($stagesContent);
|
|
||||||
}
|
|
||||||
catch (\Exception $e) {
|
|
||||||
error('Error: Cannot load stages.lua. More info in system/logs/error.log file.');
|
|
||||||
log_append('error.log', "[" . __CLASS__ . "] Fatal error: Cannot load stages.lua - $file. Error: " . $e->getMessage());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($stages as $stage) {
|
|
||||||
$this->stages[] = [
|
|
||||||
'levels' => $stage['minlevel'] . (isset($stage['maxlevel']) ? '-' . $stage['maxlevel'] : '+'),
|
|
||||||
'multiplier' => $stage['multiplier']
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get(): array {
|
|
||||||
return $this->stages;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server\Lua;
|
|
||||||
|
|
||||||
class Loader
|
|
||||||
{
|
|
||||||
public static function load($file): bool|array
|
|
||||||
{
|
|
||||||
if(!@file_exists($file)){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = [];
|
|
||||||
$config_string = str_replace(array("\r\n", "\r"), "\n", file_get_contents($file));
|
|
||||||
$lines = explode("\n", $config_string);
|
|
||||||
if(count($lines) > 0) {
|
|
||||||
foreach($lines as $ln => $line) {
|
|
||||||
$line = trim($line);
|
|
||||||
if(isset($line[0]) && ($line[0] === '{' || $line[0] === '}')) {
|
|
||||||
// arrays are not supported yet
|
|
||||||
// just ignore the error
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$tmp_exp = explode('=', $line, 2);
|
|
||||||
if(str_contains($line, 'dofile')) {
|
|
||||||
$delimiter = '"';
|
|
||||||
if(!str_contains($line, $delimiter)) {
|
|
||||||
$delimiter = "'";
|
|
||||||
}
|
|
||||||
|
|
||||||
$tmp = explode($delimiter, $line);
|
|
||||||
$result = array_merge($result, self::load(config('server_path') . $tmp[1]));
|
|
||||||
}
|
|
||||||
else if(count($tmp_exp) >= 2) {
|
|
||||||
$key = trim($tmp_exp[0]);
|
|
||||||
if(!str_starts_with($key, '--')) {
|
|
||||||
$value = trim($tmp_exp[1]);
|
|
||||||
if(str_contains($value, '--')) {// found some deep comment
|
|
||||||
$value = preg_replace('/--.*$/i', '', $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(is_numeric($value))
|
|
||||||
$result[$key] = (float) $value;
|
|
||||||
elseif(in_array(@$value[0], array("'", '"')) && in_array(@$value[strlen($value) - 1], array("'", '"')))
|
|
||||||
$result[$key] = substr(substr($value, 1), 0, -1);
|
|
||||||
elseif(in_array($value, array('true', 'false')))
|
|
||||||
$result[$key] = $value === 'true';
|
|
||||||
elseif(@$value[0] === '{') {
|
|
||||||
// arrays are not supported yet
|
|
||||||
// just ignore the error
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach($result as $tmp_key => $tmp_value) { // load values defined by other keys, like: dailyFragsToBlackSkull = dailyFragsToRedSkull
|
|
||||||
$value = str_replace($tmp_key, $tmp_value, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$ret = eval("return $value;");
|
|
||||||
}
|
|
||||||
catch (\Throwable $e) {
|
|
||||||
throw new \RuntimeException('ERROR: Loading config.lua file. Line: ' . ($ln + 1) . ' - Unable to parse value "' . $value . '" - ' . $e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
if((string) $ret == '' && trim($value) !== '""') {
|
|
||||||
throw new \RuntimeException('ERROR: Loading config.lua file. Line ' . ($ln + 1) . ' is not valid [key: ' . $key . ']');
|
|
||||||
}
|
|
||||||
$result[$key] = $ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server;
|
|
||||||
|
|
||||||
use MyAAC\Cache\Cache;
|
|
||||||
|
|
||||||
class Mounts
|
|
||||||
{
|
|
||||||
private static array $mounts = [];
|
|
||||||
|
|
||||||
public static function get()
|
|
||||||
{
|
|
||||||
if (count(self::$mounts) == 0) {
|
|
||||||
self::$mounts = Cache::remember('mounts', 10 * 60, function () {
|
|
||||||
if (file_exists(config('server_path') . TOML\Mounts::FILE)) {
|
|
||||||
$mounts = new TOML\Mounts();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$mounts = new XML\Mounts();
|
|
||||||
}
|
|
||||||
|
|
||||||
$mounts->load();
|
|
||||||
|
|
||||||
return $mounts->get();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::$mounts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server;
|
|
||||||
|
|
||||||
use MyAAC\Cache\Cache;
|
|
||||||
|
|
||||||
class Outfits
|
|
||||||
{
|
|
||||||
private static array $outfits = [];
|
|
||||||
|
|
||||||
public static function get()
|
|
||||||
{
|
|
||||||
if (count(self::$outfits) == 0) {
|
|
||||||
self::$outfits = Cache::remember('outfits', 10 * 60, function () {
|
|
||||||
if (file_exists(config('server_path') . TOML\Outfits::FILE)) {
|
|
||||||
$outfits = new TOML\Outfits();
|
|
||||||
} else {
|
|
||||||
$outfits = new XML\Outfits();
|
|
||||||
}
|
|
||||||
|
|
||||||
$outfits->load();
|
|
||||||
|
|
||||||
return $outfits->get();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::$outfits;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server\TOML;
|
|
||||||
|
|
||||||
use Devium\Toml\Toml;
|
|
||||||
use RuntimeException;
|
|
||||||
|
|
||||||
class Config
|
|
||||||
{
|
|
||||||
private array $config = [];
|
|
||||||
|
|
||||||
public function load(): void
|
|
||||||
{
|
|
||||||
$path = config('server_path') . 'config/';
|
|
||||||
$files = glob($path . '*.toml');
|
|
||||||
|
|
||||||
// filter files we don't need
|
|
||||||
$ignore = ['account_manager', 'groups', 'mounts', 'object_pools', 'outfits', 'scripts'];
|
|
||||||
$files = array_filter($files, function ($file) use ($ignore) {
|
|
||||||
foreach ($ignore as $item) {
|
|
||||||
if (str_contains($file, $item)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach ($files as $file) {
|
|
||||||
$key = basename($file, '.toml');
|
|
||||||
|
|
||||||
$toml = file_get_contents($file);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$this->config[$key] = Toml::decode($toml, asArray: true);
|
|
||||||
}
|
|
||||||
catch (\Exception $e) {
|
|
||||||
throw new RuntimeException("Error: Cannot load config/$key.toml. More info in system/logs/error.log file.");
|
|
||||||
log_append('error.log', "[" . __CLASS__ . "] Fatal error: Cannot load config/$key.toml - $file. Error: " . $e->getMessage());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->init();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function init(): void
|
|
||||||
{
|
|
||||||
$this->config['serverName'] = $this->config['server']['identity']['name'] ?? 'Unknown';
|
|
||||||
$this->config['freePremium'] = $this->config['server']['accounts']['free_premium'] ?? false;
|
|
||||||
$this->config['ip'] = $this->config['server']['network']['ip'] ?? '127.0.0.1';
|
|
||||||
$this->config['worldType'] = $this->config['server']['world']['type'] ?? 'unknown';
|
|
||||||
$this->config['experienceStages'] = $this->config['stages']['config']['enabled'] ?? false;
|
|
||||||
$this->config['houseRentPeriod'] = $this->config['server']['houses']['rent_period'] ?? 'never';
|
|
||||||
$this->config['pzLocked'] = $this->config['combat']['skull']['pz_locked'] ?? 60 * 1000;
|
|
||||||
$this->config['url'] = $this->config['server']['identity']['url'] ?? 'http://localhost';
|
|
||||||
$this->config['protectionLevel'] = $this->config['server']['pvp']['protection_level'] ?? 0;
|
|
||||||
$this->config['rateExp'] = $this->config['rates']['rates']['experience'] ?? 1;
|
|
||||||
$this->config['rateMagic'] = $this->config['rates']['rates']['magic'] ?? 1;
|
|
||||||
$this->config['rateSkill'] = $this->config['rates']['rates']['skill'] ?? 1;
|
|
||||||
$this->config['rateLoot'] = $this->config['rates']['rates']['loot'] ?? 1;
|
|
||||||
$this->config['rateSpawn'] = $this->config['rates']['rates']['spawn'] ?? 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get(): array {
|
|
||||||
return $this->config;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server\TOML;
|
|
||||||
|
|
||||||
use Devium\Toml\Toml;
|
|
||||||
|
|
||||||
class ExpStages
|
|
||||||
{
|
|
||||||
private array $stages = [];
|
|
||||||
|
|
||||||
const FILE = 'config/stages.toml';
|
|
||||||
|
|
||||||
public function load(): void
|
|
||||||
{
|
|
||||||
$file = config('server_path') . self::FILE;
|
|
||||||
|
|
||||||
if(!@file_exists($file)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$toml = file_get_contents($file);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$stages = Toml::decode($toml, asArray: true);
|
|
||||||
}
|
|
||||||
catch (\Exception $e) {
|
|
||||||
error('Error: Cannot load stages.toml. More info in system/logs/error.log file.');
|
|
||||||
log_append('error.log', "[" . __CLASS__ . "] Fatal error: Cannot load stages.toml - $file. Error: " . $e->getMessage());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($stages['stage'] as $stage) {
|
|
||||||
$this->stages[] = [
|
|
||||||
'levels' => $stage['minlevel'] . (isset($stage['maxlevel']) ? '-' . $stage['maxlevel'] : '+'),
|
|
||||||
'multiplier' => $stage['multiplier']
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get(): array {
|
|
||||||
return $this->stages;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server\TOML;
|
|
||||||
|
|
||||||
use Devium\Toml\Toml;
|
|
||||||
|
|
||||||
class Groups
|
|
||||||
{
|
|
||||||
private array $groups = [];
|
|
||||||
|
|
||||||
const FILE = 'config/groups.toml';
|
|
||||||
|
|
||||||
public function load(): void
|
|
||||||
{
|
|
||||||
$file = config('server_path') . self::FILE;
|
|
||||||
|
|
||||||
if(!@file_exists($file)) {
|
|
||||||
error('Error: Cannot load groups.toml. More info in system/logs/error.log file.');
|
|
||||||
log_append('error.log', "[" . __CLASS__ . "] Fatal error: Cannot load groups.toml - $file. It doesn't exist.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$toml = file_get_contents($file);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$groups = Toml::decode($toml, asArray: true);
|
|
||||||
}
|
|
||||||
catch (\Exception $e) {
|
|
||||||
error('Error: Cannot load groups.toml. More info in system/logs/error.log file.');
|
|
||||||
log_append('error.log', "[" . __CLASS__ . "] Fatal error: Cannot load groups.toml - $file. Error: " . $e->getMessage());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($groups as $group)
|
|
||||||
{
|
|
||||||
$this->groups[$group['id']] = [
|
|
||||||
'id' => $group['id'],
|
|
||||||
'name' => $group['name'],
|
|
||||||
'access' => $group['access'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get(): array {
|
|
||||||
return $this->groups;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server\TOML;
|
|
||||||
|
|
||||||
use MyAAC\Cache\PHP as CachePHP;
|
|
||||||
|
|
||||||
class Items
|
|
||||||
{
|
|
||||||
private string $error = '';
|
|
||||||
|
|
||||||
public function getError(): string {
|
|
||||||
return $this->error;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function load(): bool
|
|
||||||
{
|
|
||||||
$file = config('data_path') . 'items/items.toml';
|
|
||||||
if (!file_exists($file)) {
|
|
||||||
$this->error = 'Cannot load file ' . $file;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//$toml = file_get_contents($file);
|
|
||||||
//$items = \Devium\Toml\Toml::decode($toml, asArray: false);
|
|
||||||
|
|
||||||
$itemsParser = new ItemsParser();
|
|
||||||
$itemsParsed = $itemsParser->parse($file);
|
|
||||||
|
|
||||||
$items = [];
|
|
||||||
foreach ($itemsParsed as $item) {
|
|
||||||
$attributes = array_filter($item, function ($key) {
|
|
||||||
return !in_array($key, ['id', 'article', 'name', 'plural']);
|
|
||||||
}, ARRAY_FILTER_USE_KEY);
|
|
||||||
|
|
||||||
$id = $item['id'] ?? null;
|
|
||||||
if ($id === null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$items[$id] = [
|
|
||||||
'article' => $item['article'] ?? '',
|
|
||||||
'name' => $item['name'] ?? '',
|
|
||||||
'plural' => $item['plural'] ?? '',
|
|
||||||
'attributes' => $attributes,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$cache_php = new CachePHP(config('cache_prefix'), CACHE . 'persistent/');
|
|
||||||
$cache_php->set('items', $items, 5 * 365 * 24 * 60 * 60);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server\TOML;
|
|
||||||
|
|
||||||
class ItemsParser
|
|
||||||
{
|
|
||||||
public function parse(string $path): array
|
|
||||||
{
|
|
||||||
$ret = [];
|
|
||||||
|
|
||||||
$i = 0;
|
|
||||||
$handle = fopen($path, 'r');
|
|
||||||
|
|
||||||
if ($handle === false) {
|
|
||||||
throw new \RuntimeException('Failed to open items file: ' . $path);
|
|
||||||
}
|
|
||||||
|
|
||||||
$parse = '';
|
|
||||||
|
|
||||||
while (($line = fgets($handle)) !== false) {
|
|
||||||
if (str_contains($line, '[[items]]') && $i++ != 0) {
|
|
||||||
//global $whoopsHandler;
|
|
||||||
//$whoopsHandler->addDataTable('ini', [$parse]);
|
|
||||||
$ret[] = parse_ini_string($parse);
|
|
||||||
$parse = '';
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip lines like this
|
|
||||||
// field = {type = "fire", initdamage = 20, ticks = 10000, count = 7, damage = 10}
|
|
||||||
// as it cannot be parsed by parse_ini_string
|
|
||||||
if (str_starts_with(ltrim($line), 'field =')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$parse .= $line;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($parse !== '') {
|
|
||||||
$ret[] = parse_ini_string($parse);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose($handle);
|
|
||||||
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server\TOML;
|
|
||||||
|
|
||||||
use Devium\Toml\Toml;
|
|
||||||
|
|
||||||
class Mounts
|
|
||||||
{
|
|
||||||
private array $mounts = [];
|
|
||||||
|
|
||||||
const FILE = 'config/mounts.toml';
|
|
||||||
|
|
||||||
public function load(): void
|
|
||||||
{
|
|
||||||
$file = config('server_path') . self::FILE;
|
|
||||||
|
|
||||||
if(!@file_exists($file)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$toml = file_get_contents($file);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$mounts = Toml::decode($toml, asArray: true);
|
|
||||||
}
|
|
||||||
catch (\Exception $e) {
|
|
||||||
error('Error: Cannot load mounts.toml. More info in system/logs/error.log file.');
|
|
||||||
log_append('error.log', "[" . __CLASS__ . "] Fatal error: Cannot load mounts.toml - $file. Error: " . $e->getMessage());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($mounts as $name => $mount)
|
|
||||||
{
|
|
||||||
$this->mounts[] = [
|
|
||||||
'id' => $mount['id'],
|
|
||||||
'client_id' => $mount['clientid'] ?? false,
|
|
||||||
'name' => $name,
|
|
||||||
'speed' => $mount['speed'] ?? 0,
|
|
||||||
'premium' => $mount['premium'] ?? false,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get(): array {
|
|
||||||
return $this->mounts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server\TOML;
|
|
||||||
|
|
||||||
use Devium\Toml\Toml;
|
|
||||||
|
|
||||||
class Outfits
|
|
||||||
{
|
|
||||||
private array $outfits = [];
|
|
||||||
|
|
||||||
const FILE = 'config/outfits.toml';
|
|
||||||
|
|
||||||
public function load(): void
|
|
||||||
{
|
|
||||||
$file = config('server_path') . self::FILE;
|
|
||||||
|
|
||||||
if(!@file_exists($file)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$toml = file_get_contents($file);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$outfits = Toml::decode($toml, asArray: true);
|
|
||||||
}
|
|
||||||
catch (\Exception $e) {
|
|
||||||
error('Error: Cannot load outfits.toml. More info in system/logs/error.log file.');
|
|
||||||
log_append('error.log', "[" . __CLASS__ . "] Fatal error: Cannot load outfits.toml - $file. Error: " . $e->getMessage());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($outfits as $outfit)
|
|
||||||
{
|
|
||||||
$this->outfits[] = [
|
|
||||||
'id' => $outfit['id'],
|
|
||||||
'sex' => ($outfit['sex'] == 'male' ? SEX_MALE : SEX_FEMALE),
|
|
||||||
'name' => $outfit['name'],
|
|
||||||
'premium' => $outfit['premium'] ?? false,
|
|
||||||
'locked' => $outfit['locked'] ?? false,
|
|
||||||
'enabled' => $outfit['enabled'] ?? true,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get(): array {
|
|
||||||
return $this->outfits;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server\TOML;
|
|
||||||
|
|
||||||
use Devium\Toml\Toml;
|
|
||||||
|
|
||||||
class Vocations
|
|
||||||
{
|
|
||||||
private array $vocations = [];
|
|
||||||
private array $vocationsFrom = [];
|
|
||||||
|
|
||||||
public function load(): void
|
|
||||||
{
|
|
||||||
$tomlVocations = glob(config('data_path') . 'vocations/*.toml');
|
|
||||||
if (count($tomlVocations) <= 0) {
|
|
||||||
throw new \RuntimeException('ERROR: Cannot load any .toml vocation from the data/vocations folder.');
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($tomlVocations as $file) {
|
|
||||||
$toml = file_get_contents($file);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$vocations = Toml::decode($toml, asArray: true);
|
|
||||||
}
|
|
||||||
catch (\Exception $e) {
|
|
||||||
$basename = basename($file);
|
|
||||||
error("Error: Cannot load vocations/$basename. More info in system/logs/error.log file.");
|
|
||||||
log_append('error.log', "[" . __CLASS__ . "] Fatal error: Cannot load mounts.toml - $file. Error: " . $e->getMessage());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($vocations as $vocationArray) {
|
|
||||||
$id = $vocationArray['id'];
|
|
||||||
|
|
||||||
$this->vocations[$id] = $vocationArray['name'];
|
|
||||||
$this->vocationsFrom[$id] = $vocationArray['promotedfrom'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ksort($this->vocations, SORT_NUMERIC);
|
|
||||||
ksort($this->vocationsFrom, SORT_NUMERIC);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get(): array {
|
|
||||||
return $this->vocations;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFrom(): array {
|
|
||||||
return $this->vocationsFrom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server;
|
|
||||||
|
|
||||||
use MyAAC\Cache\Cache;
|
|
||||||
|
|
||||||
class Vocations
|
|
||||||
{
|
|
||||||
private static array $vocations = [];
|
|
||||||
private static array $vocationsFrom = [];
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
$cached = Cache::remember('vocations', 10 * 60, function () {
|
|
||||||
$tomlVocations = glob(config('data_path') . 'vocations/*.toml');
|
|
||||||
if (count($tomlVocations) > 0) {
|
|
||||||
$vocations = new TOML\Vocations();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$vocations = new XML\Vocations();
|
|
||||||
}
|
|
||||||
|
|
||||||
$vocations->load();
|
|
||||||
$from = $vocations->getFrom();
|
|
||||||
|
|
||||||
$amount = 0;
|
|
||||||
foreach ($from as $vocId => $fromVocation) {
|
|
||||||
if ($vocId != 0 && $vocId == $fromVocation) {
|
|
||||||
$amount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ['vocations' => $vocations->get(), 'vocationsFrom' => $from, 'amount' => $amount];
|
|
||||||
});
|
|
||||||
|
|
||||||
self::$vocations = $cached['vocations'];
|
|
||||||
self::$vocationsFrom = $cached['vocationsFrom'];
|
|
||||||
|
|
||||||
config(['vocations', self::$vocations]);
|
|
||||||
config(['vocations_amount', $cached['amount']]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function get(): array {
|
|
||||||
return self::$vocations;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getFrom(): array {
|
|
||||||
return self::$vocationsFrom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getPromoted(int $id): ?int {
|
|
||||||
foreach (self::$vocationsFrom as $vocId => $fromVocation) {
|
|
||||||
if ($id == $fromVocation && $vocId != $id) {
|
|
||||||
return $vocId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getOriginal(int $id): ?int {
|
|
||||||
if (!isset(self::$vocationsFrom[$id])) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ($tmpId = self::$vocationsFrom[$id]) {
|
|
||||||
if ($tmpId == $id) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = $tmpId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getBase($includingRook = true): array {
|
|
||||||
$vocations = [];
|
|
||||||
foreach (self::$vocationsFrom as $vocId => $fromVoc) {
|
|
||||||
if ($vocId == $fromVoc && ($vocId != 0 || $includingRook)) {
|
|
||||||
$vocations[] = $vocId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $vocations;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server\XML;
|
|
||||||
|
|
||||||
class ExpStages
|
|
||||||
{
|
|
||||||
private array $stages = [];
|
|
||||||
|
|
||||||
const FILE = 'XML/stages.xml';
|
|
||||||
|
|
||||||
public function load(): void
|
|
||||||
{
|
|
||||||
$file = config('data_path') . self::FILE;
|
|
||||||
|
|
||||||
if(!@file_exists($file)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$xml = new \DOMDocument();
|
|
||||||
if(!$xml->load($file)) {
|
|
||||||
error('Error: Cannot load stages.xml. More info in system/logs/error.log file.');
|
|
||||||
log_append('error.log', "[" . __CLASS__ . "] Fatal error: Cannot load stages.xml - $file. Error: " . print_r(error_get_last(), true));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($xml->getElementsByTagName('stage') as $stage)
|
|
||||||
{
|
|
||||||
/** @var \DOMElement $stage */
|
|
||||||
$maxLevel = $stage->getAttribute('maxlevel');
|
|
||||||
$this->stages[] = [
|
|
||||||
'levels' => $stage->getAttribute('minlevel') . (isset($maxLevel[0]) ? '-' . $maxLevel : '+'),
|
|
||||||
'multiplier' => $stage->getAttribute('multiplier')
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get(): array {
|
|
||||||
return $this->stages;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server\XML;
|
|
||||||
|
|
||||||
class Groups
|
|
||||||
{
|
|
||||||
private array $groups = [];
|
|
||||||
|
|
||||||
const FILE = 'XML/groups.xml';
|
|
||||||
|
|
||||||
public function load(): void
|
|
||||||
{
|
|
||||||
$file = config('data_path') . self::FILE;
|
|
||||||
|
|
||||||
if(!@file_exists($file)) {
|
|
||||||
error('Error: Cannot load groups.xml. More info in system/logs/error.log file.');
|
|
||||||
log_append('error.log', "[" . __CLASS__ . "] Fatal error: Cannot load groups.xml - $file. It doesn't exist.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$groups = new \DOMDocument();
|
|
||||||
if(!@$groups->load($file)) {
|
|
||||||
error('Error: Cannot load groups.xml. More info in system/logs/error.log file.');
|
|
||||||
log_append('error.log', "[" . __CLASS__ . "] Fatal error: Cannot load groups.xml - $file. Error: " . print_r(error_get_last(), true));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// loads groups
|
|
||||||
foreach( $groups->getElementsByTagName('group') as $group)
|
|
||||||
{
|
|
||||||
$this->groups[$group->getAttribute('id')] = [
|
|
||||||
'id' => $group->getAttribute('id'),
|
|
||||||
'name' => $group->getAttribute('name'),
|
|
||||||
'access' => $group->getAttribute('access')
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get(): array {
|
|
||||||
return $this->groups;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server\XML;
|
|
||||||
|
|
||||||
use MyAAC\Cache\PHP as CachePHP;
|
|
||||||
|
|
||||||
class Items
|
|
||||||
{
|
|
||||||
private string $error = '';
|
|
||||||
|
|
||||||
const FILE = 'items/items.xml';
|
|
||||||
|
|
||||||
public function getError(): string {
|
|
||||||
return $this->error;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function load(): bool
|
|
||||||
{
|
|
||||||
$file = config('data_path') . self::FILE;
|
|
||||||
if (!file_exists($file)) {
|
|
||||||
$this->error = 'Cannot load file ' . $file;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$items = [];
|
|
||||||
|
|
||||||
try {
|
|
||||||
$xml = new \SimpleXMLElement(file_get_contents($file));
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->error = 'Error: Cannot load items.xml. More info in system/logs/error.log file.';
|
|
||||||
log_append('error.log', "[" . __CLASS__ . "] Fatal error: Cannot load items.xml - $file. Error: " . $e->getMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($xml->xpath('item') as $item) {
|
|
||||||
if ($item->attributes()->fromid) {
|
|
||||||
for ($id = (int)$item->attributes()->fromid; $id <= (int)$item->attributes()->toid; $id++) {
|
|
||||||
$tmp = $this->parseNode($id, $item);
|
|
||||||
$items[$tmp['id']] = $tmp['content'];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$tmp = $this->parseNode($item->attributes()->id, $item);
|
|
||||||
$items[$tmp['id']] = $tmp['content'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$cache_php = new CachePHP(config('cache_prefix'), CACHE . 'persistent/');
|
|
||||||
$cache_php->set('items', $items, 5 * 365 * 24 * 60 * 60);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function parseNode($id, $node): array
|
|
||||||
{
|
|
||||||
$name = $node->attributes()->name;
|
|
||||||
$article = $node->attributes()->article;
|
|
||||||
$plural = $node->attributes()->plural;
|
|
||||||
|
|
||||||
$attributes = [];
|
|
||||||
foreach($node->xpath('attribute') as $attr) {
|
|
||||||
$attributes[strtolower($attr->attributes()->key)] = (string)$attr->attributes()->value;
|
|
||||||
|
|
||||||
if ($attr->xpath('attribute')) {
|
|
||||||
foreach($attr->xpath('attribute') as $attr2) {
|
|
||||||
$attributes[strtolower($attr2->attributes()->key)] = (string)$attr2->attributes()->value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'id' => (int)$id,
|
|
||||||
'content' => [
|
|
||||||
'article' => (string)$article,
|
|
||||||
'name' => (string)$name,
|
|
||||||
'plural' => (string)$plural,
|
|
||||||
'attributes' => $attributes
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server\XML;
|
|
||||||
|
|
||||||
class Mounts
|
|
||||||
{
|
|
||||||
private array $mounts = [];
|
|
||||||
|
|
||||||
const FILE = 'XML/mounts.xml';
|
|
||||||
|
|
||||||
public function load(): void
|
|
||||||
{
|
|
||||||
$file = config('data_path') . self::FILE;
|
|
||||||
|
|
||||||
if(!@file_exists($file)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$xml = new \DOMDocument();
|
|
||||||
if(!$xml->load($file)) {
|
|
||||||
error('Error: Cannot load mounts.xml. More info in system/logs/error.log file.');
|
|
||||||
log_append('error.log', "[" . __CLASS__ . "] Fatal error: Cannot load mounts.xml - $file. Error: " . print_r(error_get_last(), true));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($xml->getElementsByTagName('mount') as $mount) {
|
|
||||||
$this->mounts[] = $this->parseMountNode($mount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function parseMountNode($node): array
|
|
||||||
{
|
|
||||||
$id = (int)$node->getAttribute('id');
|
|
||||||
$client_id = (int)$node->getAttribute('clientid');
|
|
||||||
$name = $node->getAttribute('name');
|
|
||||||
$speed = (int)$node->getAttribute('speed');
|
|
||||||
$premium = getBoolean($node->getAttribute('premium'));
|
|
||||||
$type = $node->getAttribute('type');
|
|
||||||
|
|
||||||
return [
|
|
||||||
'id' => $id,
|
|
||||||
'client_id' => $client_id,
|
|
||||||
'name' => $name,
|
|
||||||
'speed' => $speed,
|
|
||||||
'premium' => $premium,
|
|
||||||
'type' => $type
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get(): array {
|
|
||||||
return $this->mounts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Server\XML;
|
|
||||||
|
|
||||||
class Outfits
|
|
||||||
{
|
|
||||||
private array $outfits = [];
|
|
||||||
|
|
||||||
const FILE = 'XML/outfits.xml';
|
|
||||||
|
|
||||||
public function load(): void
|
|
||||||
{
|
|
||||||
$file = config('data_path') . self::FILE;
|
|
||||||
|
|
||||||
if(!@file_exists($file)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$xml = new \DOMDocument();
|
|
||||||
if(!$xml->load($file)) {
|
|
||||||
error('Error: Cannot load outfits.xml. More info in system/logs/error.log file.');
|
|
||||||
log_append('error.log', "[" . __CLASS__ . "] Fatal error: Cannot load outfits.xml - $file. Error: " . print_r(error_get_last(), true));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($xml->getElementsByTagName('outfit') as $outfit) {
|
|
||||||
$this->outfits[] = $this->parseOutfitNode($outfit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function parseOutfitNode($node): array
|
|
||||||
{
|
|
||||||
$looktype = (int)$node->getAttribute('looktype');
|
|
||||||
$type = (int)$node->getAttribute('type');
|
|
||||||
$name = $node->getAttribute('name');
|
|
||||||
$premium = getBoolean($node->getAttribute('premium'));
|
|
||||||
$locked = !getBoolean($node->getAttribute('unlocked'));
|
|
||||||
$enabled = getBoolean($node->getAttribute('enabled'));
|
|
||||||
|
|
||||||
return [
|
|
||||||
'id' => $looktype,
|
|
||||||
'sex' => ($type === 1 ? SEX_MALE : SEX_FEMALE),
|
|
||||||
'name' => $name,
|
|
||||||
'premium' => $premium,
|
|
||||||
'locked' => $locked,
|
|
||||||
'enabled' => $enabled,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get(): array {
|
|
||||||
return $this->outfits;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,42 +2,100 @@
|
|||||||
|
|
||||||
namespace MyAAC\Server\XML;
|
namespace MyAAC\Server\XML;
|
||||||
|
|
||||||
|
use MyAAC\Cache\Cache;
|
||||||
|
|
||||||
class Vocations
|
class Vocations
|
||||||
{
|
{
|
||||||
private array $vocations = [];
|
private static array $vocations;
|
||||||
private array $vocationsFrom = [];
|
private static array $vocationsFrom;
|
||||||
|
|
||||||
const FILE = 'vocations.xml';
|
public function __construct()
|
||||||
|
{
|
||||||
|
$cached = Cache::remember('vocations', 10 * 60, function () {
|
||||||
|
$this->load();
|
||||||
|
$from = $this->getFrom();
|
||||||
|
|
||||||
|
$amount = 0;
|
||||||
|
foreach ($from as $vocId => $fromVocation) {
|
||||||
|
if ($vocId != 0 && $vocId == $fromVocation) {
|
||||||
|
$amount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['vocations' => $this->get(), 'vocationsFrom' => $from, 'amount' => $amount];
|
||||||
|
});
|
||||||
|
|
||||||
|
self::$vocations = $cached['vocations'];
|
||||||
|
self::$vocationsFrom = $cached['vocationsFrom'];
|
||||||
|
|
||||||
|
config(['vocations', self::$vocations]);
|
||||||
|
config(['vocations_amount', $cached['amount']]);
|
||||||
|
}
|
||||||
|
|
||||||
public function load(): void
|
public function load(): void
|
||||||
{
|
{
|
||||||
$file = config('data_path') . 'XML/' . self::FILE;
|
if(!class_exists('DOMDocument')) {
|
||||||
|
throw new \RuntimeException('Please install PHP xml extension. MyAAC will not work without it.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$vocationsXML = new \DOMDocument();
|
||||||
|
$file = config('data_path') . 'XML/vocations.xml';
|
||||||
if(!@file_exists($file)) {
|
if(!@file_exists($file)) {
|
||||||
$file = config('data_path') . self::FILE;
|
$file = config('data_path') . 'vocations.xml';
|
||||||
}
|
}
|
||||||
|
|
||||||
$xml = new \DOMDocument();
|
if(!$vocationsXML->load($file)) {
|
||||||
if(!$xml->load($file)) {
|
throw new \RuntimeException('ERROR: Cannot load <i>vocations.xml</i> - the file is malformed. Check the file with xml syntax validator.');
|
||||||
error('Error: Cannot load vocations.xml. More info in system/logs/error.log file.');
|
|
||||||
log_append('error.log', "[" . __CLASS__ . "] Fatal error: Cannot load vocations.xml - $file. Error: " . print_r(error_get_last(), true));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($xml->getElementsByTagName('vocation') as $vocation) {
|
foreach($vocationsXML->getElementsByTagName('vocation') as $vocation) {
|
||||||
$id = $vocation->getAttribute('id');
|
$id = $vocation->getAttribute('id');
|
||||||
|
|
||||||
$this->vocations[$id] = $vocation->getAttribute('name');
|
self::$vocations[$id] = $vocation->getAttribute('name');
|
||||||
|
|
||||||
$fromVocation = (int) $vocation->getAttribute('fromvoc');
|
$fromVocation = (int) $vocation->getAttribute('fromvoc');
|
||||||
$this->vocationsFrom[$id] = $fromVocation;
|
self::$vocationsFrom[$id] = $fromVocation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get(): array {
|
public static function get(): array {
|
||||||
return $this->vocations;
|
return self::$vocations;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFrom(): array {
|
public static function getFrom(): array {
|
||||||
return $this->vocationsFrom;
|
return self::$vocationsFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getPromoted(int $id): ?int {
|
||||||
|
foreach (self::$vocationsFrom as $vocId => $fromVocation) {
|
||||||
|
if ($id == $fromVocation && $vocId != $id) {
|
||||||
|
return $vocId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getOriginal(int $id): ?int {
|
||||||
|
while ($tmpId = self::$vocationsFrom[$id]) {
|
||||||
|
if ($tmpId == $id) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $tmpId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getBase($includingRook = true): array {
|
||||||
|
$vocations = [];
|
||||||
|
foreach (self::$vocationsFrom as $vocId => $fromVoc) {
|
||||||
|
if ($vocId == $fromVoc && ($vocId != 0 || $includingRook)) {
|
||||||
|
$vocations[] = $vocId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $vocations;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -367,7 +367,6 @@ class Settings implements \ArrayAccess
|
|||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
<button name="save" type="submit" class="btn btn-primary">Save</button>
|
<button name="save" type="submit" class="btn btn-primary">Save</button>
|
||||||
<button form="reset-settings-form" name="reset" type="submit" class="btn btn-warning position-absolute" style="right: 0; bottom: 0" onclick="return confirm('Are you sure? This will clear all settings for this plugin!')">Reset</button>
|
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|||||||
19
system/src/TwoFactorAuth/Gateway/AppAuthGateway.php
Normal file
19
system/src/TwoFactorAuth/Gateway/AppAuthGateway.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyAAC\TwoFactorAuth\Gateway;
|
||||||
|
|
||||||
|
use MyAAC\TwoFactorAuth\Interface\AuthGatewayInterface;
|
||||||
|
use OTPHP\TOTP;
|
||||||
|
|
||||||
|
class AppAuthGateway extends BaseAuthGateway implements AuthGatewayInterface
|
||||||
|
{
|
||||||
|
public function verifyCode(string $code): bool
|
||||||
|
{
|
||||||
|
$otp = TOTP::createFromSecret($this->account->getCustomField('secret'));
|
||||||
|
|
||||||
|
$otp->setLabel($this->account->getEmail());
|
||||||
|
$otp->setIssuer(configLua('serverName'));
|
||||||
|
|
||||||
|
return $otp->verify($code);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
system/src/TwoFactorAuth/Gateway/BaseAuthGateway.php
Normal file
12
system/src/TwoFactorAuth/Gateway/BaseAuthGateway.php
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyAAC\TwoFactorAuth\Gateway;
|
||||||
|
|
||||||
|
class BaseAuthGateway
|
||||||
|
{
|
||||||
|
protected \OTS_Account $account;
|
||||||
|
|
||||||
|
public function __construct(\OTS_Account $account) {
|
||||||
|
$this->account = $account;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
system/src/TwoFactorAuth/Gateway/EmailAuthGateway.php
Normal file
16
system/src/TwoFactorAuth/Gateway/EmailAuthGateway.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyAAC\TwoFactorAuth\Gateway;
|
||||||
|
|
||||||
|
use MyAAC\Models\AccountEMailCode;
|
||||||
|
use MyAAC\TwoFactorAuth\Interface\AuthGatewayInterface;
|
||||||
|
use MyAAC\TwoFactorAuth\TwoFactorAuth;
|
||||||
|
|
||||||
|
class EmailAuthGateway extends BaseAuthGateway implements AuthGatewayInterface
|
||||||
|
{
|
||||||
|
public function verifyCode(string $code): bool
|
||||||
|
{
|
||||||
|
return AccountEMailCode::where('account_id', '=', $this->account->getId())->where('code', $code)->where('created_at', '>', time() - TwoFactorAuth::EMAIL_CODE_VALID_UNTIL)->first() !== null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyAAC\TwoFactorAuth\Interface;
|
||||||
|
|
||||||
|
interface AuthGatewayInterface
|
||||||
|
{
|
||||||
|
public function __construct(\OTS_Account $account);
|
||||||
|
public function verifyCode(string $code): bool;
|
||||||
|
}
|
||||||
270
system/src/TwoFactorAuth/TwoFactorAuth.php
Normal file
270
system/src/TwoFactorAuth/TwoFactorAuth.php
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyAAC\TwoFactorAuth;
|
||||||
|
|
||||||
|
use MyAAC\Models\AccountEMailCode;
|
||||||
|
use MyAAC\TwoFactorAuth\Gateway\AppAuthGateway;
|
||||||
|
use MyAAC\TwoFactorAuth\Gateway\EmailAuthGateway;
|
||||||
|
use OTPHP\TOTP;
|
||||||
|
|
||||||
|
class TwoFactorAuth
|
||||||
|
{
|
||||||
|
const TYPE_NONE = 0;
|
||||||
|
const TYPE_EMAIL = 1;
|
||||||
|
const TYPE_APP = 2;
|
||||||
|
// maybe later
|
||||||
|
//const TYPE_SMS = 3;
|
||||||
|
|
||||||
|
const EMAIL_CODE_VALID_UNTIL = 24 * 60 * 60;
|
||||||
|
|
||||||
|
private static self $instance;
|
||||||
|
|
||||||
|
private \OTS_Account $account;
|
||||||
|
private int $authType;
|
||||||
|
private EmailAuthGateway|AppAuthGateway $authGateway;
|
||||||
|
|
||||||
|
public function __construct(\OTS_Account|int $account) {
|
||||||
|
if (is_int($account)) {
|
||||||
|
$this->account = new \OTS_Account();
|
||||||
|
$this->account->load($account);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->account = $account;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->authType = (int)$this->account->getCustomField('2fa_type');
|
||||||
|
$this->setAuthGateway($this->authType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getInstance($account = null): self
|
||||||
|
{
|
||||||
|
if (!isset(self::$instance)) {
|
||||||
|
self::$instance = new self($account);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process($login_account, $login_password, $remember_me, $code): bool
|
||||||
|
{
|
||||||
|
global $twig;
|
||||||
|
|
||||||
|
if (!$this->isActive()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$view = 'app';
|
||||||
|
if ($this->authType == self::TYPE_EMAIL) {
|
||||||
|
$view = 'email';#
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($code)) {
|
||||||
|
if ($this->authType == self::TYPE_EMAIL) {
|
||||||
|
if (!$this->hasRecentEmailCode(15 * 60)) {
|
||||||
|
$this->resendEmailCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
define('HIDE_LOGIN_BOX', true);
|
||||||
|
$twig->display("account/2fa/$view/login.html.twig", [
|
||||||
|
'account_login' => $login_account,
|
||||||
|
'password_login' => $login_password,
|
||||||
|
'remember_me' => $remember_me,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->getAuthGateway()->verifyCode($code)) {
|
||||||
|
if ($this->authType === self::TYPE_EMAIL) {
|
||||||
|
$this->deleteOldCodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setting('core.mail_enabled')) {
|
||||||
|
$mailBody = $twig->render('mail.account.2fa.email-code.wrong-attempt.html.twig');
|
||||||
|
|
||||||
|
if (!_mail($this->account->getEMail(), configLua('serverName') . ' - Failed Two-Factor Authentication Attempt', $mailBody)) {
|
||||||
|
error('An error occurred while sending email. For Admin: More info can be found in system/logs/mailer-error.log');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
define('HIDE_LOGIN_BOX', true);
|
||||||
|
|
||||||
|
if ($this->authType == self::TYPE_APP) {
|
||||||
|
$errors[] = 'The token is invalid!';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = 'Invalid E-Mail code!';
|
||||||
|
}
|
||||||
|
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||||
|
|
||||||
|
$twig->display("account/2fa/$view/login.html.twig",
|
||||||
|
[
|
||||||
|
'account_login' => $login_account,
|
||||||
|
'password_login' => $login_password,
|
||||||
|
'remember_me' => $remember_me,
|
||||||
|
|
||||||
|
'wrongCode' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processClientLogin($code, string &$error, &$errorCode): bool
|
||||||
|
{
|
||||||
|
if (!$this->isActive()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->authType == self::TYPE_EMAIL) {
|
||||||
|
$errorCode = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($code === false) {
|
||||||
|
$error = 'Submit a valid two-factor authentication token.';
|
||||||
|
|
||||||
|
if ($this->authType == self::TYPE_EMAIL) {
|
||||||
|
if (!$this->hasRecentEmailCode(15 * 60)) {
|
||||||
|
$this->resendEmailCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->getAuthGateway()->verifyCode($code)) {
|
||||||
|
$error = 'Two-factor authentication failed, token is wrong.';
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->authType === self::TYPE_EMAIL) {
|
||||||
|
$this->deleteOldCodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAuthGateway(int $authType): void
|
||||||
|
{
|
||||||
|
if ($authType === self::TYPE_EMAIL) {
|
||||||
|
$this->authGateway = new EmailAuthGateway($this->account);
|
||||||
|
}
|
||||||
|
else if ($authType === self::TYPE_APP) {
|
||||||
|
$this->authGateway = new AppAuthGateway($this->account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAccountManageViews(): array
|
||||||
|
{
|
||||||
|
if ($this->authType == self::TYPE_EMAIL) {
|
||||||
|
$twoFactorView = 'account/2fa/main.protected.html.twig';
|
||||||
|
$twoFactorView2 = 'account/2fa/email/manage.connected.html.twig';
|
||||||
|
}
|
||||||
|
elseif ($this->authType == self::TYPE_APP) {
|
||||||
|
$twoFactorView = 'account/2fa/app/manage.connected.html.twig';
|
||||||
|
$twoFactorView2 = 'account/2fa/main.protected.html.twig';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$twoFactorView = 'account/2fa/app/manage.enable.html.twig';
|
||||||
|
$twoFactorView2 = 'account/2fa/email/manage.enable.html.twig';
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$twoFactorView, $twoFactorView2];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enable(int $type): void {
|
||||||
|
$this->account->setCustomField('2fa_type', $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function disable(): void
|
||||||
|
{
|
||||||
|
global $db;
|
||||||
|
|
||||||
|
$this->account->setCustomField('2fa_type', self::TYPE_NONE);
|
||||||
|
|
||||||
|
if ($db->hasColumn('accounts', 'secret')) {
|
||||||
|
$this->account->setCustomField('secret', null);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->account->setCustomField('2fa_secret', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isActive(?int $authType = null): bool {
|
||||||
|
if ($authType !== null) {
|
||||||
|
return $this->authType === $authType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->authType != self::TYPE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAuthType(): int {
|
||||||
|
return $this->authType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAuthGateway(): AppAuthGateway|EmailAuthGateway {
|
||||||
|
return $this->authGateway;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasRecentEmailCode($since = self::EMAIL_CODE_VALID_UNTIL): bool {
|
||||||
|
return AccountEMailCode::where('account_id', '=', $this->account->getId())->where('created_at', '>', time() - $since)->first() !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteOldCodes(): void {
|
||||||
|
AccountEMailCode::where('account_id', '=', $this->account->getId())->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function appInitTOTP(string $secret): TOTP
|
||||||
|
{
|
||||||
|
$otp = TOTP::createFromSecret($secret);
|
||||||
|
|
||||||
|
$otp->setLabel($this->account->getEmail());
|
||||||
|
$otp->setIssuer(configLua('serverName'));
|
||||||
|
|
||||||
|
return $otp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function appDisplayEnable(string $secret, ?TOTP $otp = null, array $errors = []): void
|
||||||
|
{
|
||||||
|
global $twig;
|
||||||
|
|
||||||
|
if ($otp === null) {
|
||||||
|
$otp = $this->appInitTOTP($secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
$grCodeUri = $otp->getQrCodeUri(
|
||||||
|
'https://api.qrserver.com/v1/create-qr-code/?data=[DATA]&size=200x200&ecc=M',
|
||||||
|
'[DATA]'
|
||||||
|
);
|
||||||
|
|
||||||
|
$twig->display('account/2fa/app/enable.html.twig', [
|
||||||
|
'grCodeUri' => $grCodeUri,
|
||||||
|
'secret' => $secret,
|
||||||
|
'errors' => $errors,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resendEmailCode(): void
|
||||||
|
{
|
||||||
|
global $twig;
|
||||||
|
|
||||||
|
$newCode = generateRandomString(6, true, false, true);
|
||||||
|
AccountEMailCode::create([
|
||||||
|
'account_id' => $this->account->getId(),
|
||||||
|
'code' => $newCode,
|
||||||
|
'created_at' => time(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$mailBody = $twig->render('mail.account.2fa.email-code.html.twig', [
|
||||||
|
'code' => $newCode,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!_mail($this->account->getEMail(), configLua('serverName') . ' - Requested Authentication Email Code', $mailBody)) {
|
||||||
|
error('An error occurred while sending email. For Admin: More info can be found in system/logs/mailer-error.log');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -183,7 +183,7 @@ class Validator
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// installer doesn't know settings yet
|
// installer doesn't know config.php yet
|
||||||
// that's why we need to ignore the nulls
|
// that's why we need to ignore the nulls
|
||||||
if(defined('MYAAC_INSTALL')) {
|
if(defined('MYAAC_INSTALL')) {
|
||||||
$minLength = 4;
|
$minLength = 4;
|
||||||
@@ -207,15 +207,21 @@ class Validator
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(strspn($name, "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM ") != $length)
|
if(strspn($name, "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM- [ ] '") != $length)
|
||||||
{
|
{
|
||||||
self::$lastError = "This name contains invalid letters. Please use only A-Z, a-z and space!";
|
self::$lastError = "Invalid name format. Use only A-Z, spaces and '.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(preg_match('/ {2,}/', $name))
|
if(preg_match('/ {2,}/', $name))
|
||||||
{
|
{
|
||||||
self::$lastError = 'Invalid character name format. Use only A-Z, a-z and no double spaces.';
|
self::$lastError = 'Invalid character name format. Use only A-Z and no double spaces.';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!preg_match("/[A-z ']/", $name))
|
||||||
|
{
|
||||||
|
self::$lastError = "Invalid name format. Use only A-Z, spaces and '.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,23 +230,17 @@ class Validator
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate new character name.
|
* Validate new character name.
|
||||||
* Name length must be 3-25 chars
|
* Name lenght must be 3-25 chars
|
||||||
*
|
*
|
||||||
* @param string $name Name to check
|
* @param string $name Name to check
|
||||||
* @return bool Is name valid?
|
* @return bool Is name valid?
|
||||||
*/
|
*/
|
||||||
public static function newCharacterName($name)
|
public static function newCharacterName($name)
|
||||||
{
|
{
|
||||||
global $db;
|
global $db, $config;
|
||||||
|
|
||||||
$name = trim($name);
|
|
||||||
$name_lower = strtolower($name);
|
$name_lower = strtolower($name);
|
||||||
|
|
||||||
if(strlen($name) < 1) {
|
|
||||||
self::$lastError = 'Please enter a name.';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$first_words_blocked = array_merge(["'", '-'], setting('core.create_character_name_blocked_prefix'));
|
$first_words_blocked = array_merge(["'", '-'], setting('core.create_character_name_blocked_prefix'));
|
||||||
foreach($first_words_blocked as $word) {
|
foreach($first_words_blocked as $word) {
|
||||||
if($word == substr($name_lower, 0, strlen($word))) {
|
if($word == substr($name_lower, 0, strlen($word))) {
|
||||||
@@ -249,6 +249,11 @@ class Validator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(str_ends_with($name_lower, "'") || str_ends_with($name_lower, "-")) {
|
||||||
|
self::$lastError = 'Your name contains illegal characters.';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(substr($name_lower, 1, 1) == ' ') {
|
if(substr($name_lower, 1, 1) == ' ') {
|
||||||
self::$lastError = 'Your name contains illegal space.';
|
self::$lastError = 'Your name contains illegal space.';
|
||||||
return false;
|
return false;
|
||||||
@@ -260,36 +265,11 @@ class Validator
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(preg_match('/ {2,}/', $name)) {
|
if(preg_match('/ {2,}/', $name)) {
|
||||||
self::$lastError = 'Invalid character name format. Use only A-Z and no double spaces.';
|
self::$lastError = 'Invalid character name format. Use only A-Z and numbers 0-9 and no double spaces.';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (substr($name[0], 0, 1) !== strtoupper(substr($name[0], 0, 1))) {
|
if(strtolower($config['lua']['serverName']) == $name_lower) {
|
||||||
self::$lastError = 'The first letter of a name has to be a capital letter.';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (explode(' ', $name) as $word) {
|
|
||||||
$wordCut = substr($word, 1, strlen($word));
|
|
||||||
$hasUpperCase = preg_match('/[A-Z]/', $wordCut);
|
|
||||||
if ($hasUpperCase) {
|
|
||||||
self::$lastError = 'In names capital letters are only allowed at the beginning of a word.';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strlen($word) == 1) {
|
|
||||||
self::$lastError = 'This name contains a word with only one letter. Please use more than one letter for each word.';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$hasVowel = preg_match('/[aeiouAEIOU]/', $word);
|
|
||||||
if (!$hasVowel) {
|
|
||||||
self::$lastError = 'This name contains a word without vowels. Please choose another name.';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(strtolower(configLua('serverName')) == $name_lower) {
|
|
||||||
self::$lastError = 'Your name cannot be same as server name.';
|
self::$lastError = 'Your name cannot be same as server name.';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
const SEX_FEMALE = 0;
|
|
||||||
const SEX_MALE = 1;
|
|
||||||
|
|
||||||
const SKILL_FRAGS = -1;
|
const SKILL_FRAGS = -1;
|
||||||
const SKILL_BALANCE = -2;
|
const SKILL_BALANCE = -2;
|
||||||
|
|
||||||
@@ -72,6 +69,7 @@ define('HOOK_ACCOUNT_LOGIN_AFTER_PASSWORD', ++$i);
|
|||||||
define('HOOK_ACCOUNT_LOGIN_AFTER_REMEMBER_ME', ++$i);
|
define('HOOK_ACCOUNT_LOGIN_AFTER_REMEMBER_ME', ++$i);
|
||||||
define('HOOK_ACCOUNT_LOGIN_AFTER_PAGE', ++$i);
|
define('HOOK_ACCOUNT_LOGIN_AFTER_PAGE', ++$i);
|
||||||
define('HOOK_ACCOUNT_LOGIN_POST', ++$i);
|
define('HOOK_ACCOUNT_LOGIN_POST', ++$i);
|
||||||
|
define('HOOK_ACCOUNT_LOGIN_PRE', ++$i);
|
||||||
define('HOOK_ACCOUNT_LOST_CHECK_CODE_FINISH_AFTER_PASSWORD', ++$i);
|
define('HOOK_ACCOUNT_LOST_CHECK_CODE_FINISH_AFTER_PASSWORD', ++$i);
|
||||||
define('HOOK_ACCOUNT_LOST_CHECK_CODE_FINISH_AFTER_PASSWORD_REPEAT', ++$i);
|
define('HOOK_ACCOUNT_LOST_CHECK_CODE_FINISH_AFTER_PASSWORD_REPEAT', ++$i);
|
||||||
define('HOOK_ACCOUNT_LOST_EMAIL_SET_NEW_PASSWORD_POST', ++$i);
|
define('HOOK_ACCOUNT_LOST_EMAIL_SET_NEW_PASSWORD_POST', ++$i);
|
||||||
|
|||||||
@@ -147,6 +147,9 @@
|
|||||||
{% include('buttons.base.html.twig') %}
|
{% include('buttons.base.html.twig') %}
|
||||||
</form>
|
</form>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
{{ include('account/2fa/main.html.twig') }}
|
||||||
|
|
||||||
{{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_ACCOUNT_LOGS') }}
|
{{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_ACCOUNT_LOGS') }}
|
||||||
<a name="Account+Logs" ></a>
|
<a name="Account+Logs" ></a>
|
||||||
<h2>Account Logs</h2>
|
<h2>Account Logs</h2>
|
||||||
|
|||||||
76
system/templates/account/2fa/app/enable.html.twig
Normal file
76
system/templates/account/2fa/app/enable.html.twig
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
|
||||||
|
<table style="width:100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="TableContentContainer ">
|
||||||
|
<table class="TableContent" width="100%" style="border:1px solid #faf0d7;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<ol>
|
||||||
|
<li>Open an authenticator app of your choice (e.g. <a
|
||||||
|
target="_blank"
|
||||||
|
href="https://support.google.com/accounts/answer/1066447"
|
||||||
|
rel="noopener noreferrer">Google Authenticator</a>, <a
|
||||||
|
target="_blank" href="https://www.authy.com/users"
|
||||||
|
rel="noopener noreferrer">Authy</a>). In the app you
|
||||||
|
will be asked either to enter a key manually:<br><b>{{ secret }}</b><br>or
|
||||||
|
to scan the barcode below:<br>
|
||||||
|
<img alt="QR code" style="margin-top: 15px; margin-bottom: 15px;"
|
||||||
|
src="{{ grCodeUri }}">
|
||||||
|
</li>
|
||||||
|
<li><label for="totp">Enter the verification code you have received from the used
|
||||||
|
authenticator app:</label><br>
|
||||||
|
<div style="margin-top: 15px; margin-bottom: 15px;">
|
||||||
|
|
||||||
|
<input form="form" id="auth-code" name="auth-code" maxlength="6" autocomplete="off">
|
||||||
|
{% if errors|length > 0 %}
|
||||||
|
<br/>
|
||||||
|
<div class="FormFieldError">{{ errors[0] }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>Click on "Continue" to connect the authenticator app to your
|
||||||
|
Tibia account.
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<table style="width: 100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr align="center" valign="top">
|
||||||
|
<td>
|
||||||
|
<form id="form" method="post" action="{{ getLink('account/2fa/app/enable') }}">
|
||||||
|
|
||||||
|
<input type="hidden" name="action" value="link">
|
||||||
|
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
{% set button_color = 'green' %}
|
||||||
|
{% set button_name = 'Continue' %}
|
||||||
|
{{ include('buttons.base.html.twig') }}
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<form action="{{ getLink('account/manage') }}" method="post" style="padding:0;margin:0;">
|
||||||
|
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
{% set button_color = 'blue' %}
|
||||||
|
{% set button_name = 'Cancel' %}
|
||||||
|
{{ include('buttons.base.html.twig') }}
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
101
system/templates/account/2fa/app/enable.warning.html.twig
Normal file
101
system/templates/account/2fa/app/enable.warning.html.twig
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
{% set title = 'Warning' %}
|
||||||
|
{% set background = config('darkborder') %}
|
||||||
|
|
||||||
|
{% set content %}
|
||||||
|
<table style="width:100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="red"><b>Please read this warning carefully as it contains important security information! If you skip this message, you might lose your {{ config.lua.serverName }} account!</b></span><br><br>
|
||||||
|
<p>Before you connect your account with an authenticator app, you will be asked to
|
||||||
|
enter your recovery key. If you do not have a valid recovery key, you need to
|
||||||
|
order a new one before you can connect your account with an authenticator.</p>
|
||||||
|
<p>Why?<br>The recovery key is the only way to unlink the authenticator app from
|
||||||
|
your {{ config.lua.serverName }} account in various cases, among others, if:</p>
|
||||||
|
<ul style="list-style-type:square">
|
||||||
|
<li>you lose your device (mobile phone, tablet, etc.) with the authenticator
|
||||||
|
app
|
||||||
|
</li>
|
||||||
|
<li>the device with the authenticator app does not work anymore</li>
|
||||||
|
<li>the device with the authenticator app gets stolen</li>
|
||||||
|
<li>you delete the authenticator app from your device and reinstall it</li>
|
||||||
|
<li>your device is reset for some reason</li>
|
||||||
|
</ul>
|
||||||
|
<p></p>
|
||||||
|
<p>Please note that the authenticator app data is not saved on your device's account
|
||||||
|
(e.g. Google or iTunes sync) even if you have app data backup&synchronisation
|
||||||
|
activated in the settings of your device!</p>
|
||||||
|
<p>In all these scenarios, the recovery key is the only way to get access to your
|
||||||
|
{{ config.lua.serverName }} account. Note that not even customer support will be able to help you in
|
||||||
|
these cases if you do not have a valid recovery key.<br>For this reason, make
|
||||||
|
sure to store your recovery key always in a safe place!</p><br>Do you have a
|
||||||
|
valid recovery key and would like to request the email with the confirmation key to
|
||||||
|
start connecting your {{ config.lua.serverName }} account to an authenticator app?<br><br><b>Enter your
|
||||||
|
recovery key:</b><br/>
|
||||||
|
|
||||||
|
<div style="margin-top: 15px; margin-bottom: 15px;">
|
||||||
|
|
||||||
|
{% if newRecoveryKeyFormat %}
|
||||||
|
|
||||||
|
<input form="form" class="UpperCaseInput" name="key1" value="" size="5" maxlength="5" autocomplete="off"> -
|
||||||
|
<input form="form" class="UpperCaseInput" name="key2" value="" size="5" maxlength="5" autocomplete="off"> - <input form="form" class="UpperCaseInput" name="key3" value="" size="5" maxlength="5" autocomplete="off"> -
|
||||||
|
<input form="form" class="UpperCaseInput" name="key4" value="" size="5" maxlength="5" autocomplete="off">
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<input form="form" class="UpperCaseInput" name="key" value="" autocomplete="off">
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if errors|length > 0 %}
|
||||||
|
<br/>
|
||||||
|
<div class="FormFieldError">{{ errors[0] }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endset %}
|
||||||
|
{% include 'tables.headline.html.twig' %}
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<table style="width:100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr align="center">
|
||||||
|
<td>
|
||||||
|
<form id="form" action="{{ getLink('account/2fa/app/enable') }}" method="post" style="padding:0;margin:0;">
|
||||||
|
|
||||||
|
<input type="hidden" name="action" value="request" />
|
||||||
|
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
{% set button_name = 'Request' %}
|
||||||
|
{{ include('buttons.base.html.twig') }}
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<form action="{{ getLink('account/register') }}" method="post" style="padding:0;margin:0;">
|
||||||
|
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
{% set button_name = 'Order Recovery Key' %}
|
||||||
|
{{ include('buttons.base.html.twig') }}
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<form action="{{ getLink('account/manage') }}" method="post" style="padding:0;margin:0;">
|
||||||
|
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
{% set button_name = 'Cancel Request' %}
|
||||||
|
{{ include('buttons.base.html.twig') }}
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.UpperCaseInput {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
64
system/templates/account/2fa/app/login.html.twig
Normal file
64
system/templates/account/2fa/app/login.html.twig
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
{% set title = 'Enter Authenticator App Token' %}
|
||||||
|
{% set background = config('darkborder') %}
|
||||||
|
|
||||||
|
{% set content %}
|
||||||
|
<table style="width:100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="TableContentContainer ">
|
||||||
|
<table class="TableContent" width="100%" style="border:1px solid #faf0d7;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Enter the verification code generated by the app:<br>
|
||||||
|
<div style="margin-top: 15px; margin-bottom: 15px;">
|
||||||
|
<div class="LabelV200" style="float:left;">Authenticator App Token:</div>
|
||||||
|
<input form="form" id="auth-code" name="auth-code" maxlength="6" autocomplete="off" required autofocus></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endset %}
|
||||||
|
{% include 'tables.headline.html.twig' %}
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<table style="width: 100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr align="center" valign="top">
|
||||||
|
<td>
|
||||||
|
<form id="form" action="{{ getLink('account/manage') }}" method="post">
|
||||||
|
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
<input type="hidden" name="account_login" value="{{ account_login ?? '' }}" />
|
||||||
|
<input type="hidden" name="password_login" value="{{ password_login ?? '' }}" />
|
||||||
|
{% if remember_me %}
|
||||||
|
<input type="hidden" name="remember_me" value="true" />
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<input type="hidden" name="step" value="verify">
|
||||||
|
|
||||||
|
{% set button_color = 'green' %}
|
||||||
|
{% set button_name = 'Continue' %}
|
||||||
|
{{ include('buttons.base.html.twig') }}
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<form action="{{ getLink('account/manage') }}" method="post"
|
||||||
|
style="padding:0;margin:0;">
|
||||||
|
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
{% set button_color = 'blue' %}
|
||||||
|
{% set button_name = 'Cancel' %}
|
||||||
|
{{ include('buttons.base.html.twig') }}
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
25
system/templates/account/2fa/app/manage.connected.html.twig
Normal file
25
system/templates/account/2fa/app/manage.connected.html.twig
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="TableContentContainer ">
|
||||||
|
<table class="TableContent" width="100%" style="border:1px solid #faf0d7;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div style="float: right; width: 135px;">
|
||||||
|
<form action="{{ getLink('account/2fa/app/disable') }}" method="post" style="padding:0;margin:0;">
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
{% set button_name = 'Unlink' %}
|
||||||
|
{{ include('buttons.base.html.twig') }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<b>Your Tibia account is <span style="color: green">connected</span> to an authenticator app.</b>
|
||||||
|
<p>If you do not want to use an authenticator app any longer, you can "Unlink" the authenticator
|
||||||
|
App. Note, however, an authenticator app is an important security feature which helps to
|
||||||
|
prevent any unauthorized access to your Tibia account.</p></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user