mirror of
https://github.com/slawkens/myaac.git
synced 2025-05-17 03:19:21 +02:00
Merge branch 'develop' into feature/migrations-up-down
This commit is contained in:
commit
a5075d77dc
24
CHANGELOG.md
24
CHANGELOG.md
@ -1,6 +1,28 @@
|
||||
# Changelog
|
||||
|
||||
## [1.0-RC -23.07.2024]
|
||||
## [1.0-RC.2 - 25.10.2024]
|
||||
|
||||
Still waiting for your reports about bugs found in this release. We are very close to stable release.
|
||||
|
||||
### Added
|
||||
* feat: rate limit settings for blocking accounts login attempts (@gpedro, #266)
|
||||
* search by email in accounts editor (https://github.com/slawkens/myaac/commit/c2ec46824621468f2a1cb4046805c485ed13fea5)
|
||||
* New hooks in account manage + create (https://github.com/slawkens/myaac/commit/93641fc68ac9a5f1479329e2bd41380c19534d5d)
|
||||
|
||||
### Changed
|
||||
* chore: drop raw queries + accounts - search by email + accounts - required min size for search by account number (@gpedro, #266)
|
||||
* Use https for outfit & item images (https://github.com/slawkens/myaac/commit/71c00aa5e01fbdfd88802912e200dd1025976231)
|
||||
* Do not require players & guilds tables on install (https://github.com/slawkens/myaac/commit/779aa152fa940261c9b161533946f44e288597a2)
|
||||
* Do not create player if there is no players table in db (https://github.com/slawkens/myaac/commit/201f95caa8b70e88fa651eac8c3c3aa7cd765bd0)
|
||||
|
||||
### Fixed
|
||||
* Highscore frags fixed for TFS 0.3 (@Scrollog, #263)
|
||||
* Missing groups variable #262. thanks, @Scrollog for reporting (https://github.com/slawkens/myaac/commit/8d8bdb6dac6df21672ac77288fff2f2f8d6eb665)
|
||||
* Verified email for login.php (@gpedro, #265)
|
||||
* Warning if core.account_country is disabled (https://github.com/slawkens/myaac/commit/ab73d60c61e14a1cacdb6cfbf7f89f4bf3be0833)
|
||||
|
||||
|
||||
## [1.0-RC.1 - 23.07.2024]
|
||||
|
||||
Changes since 1.0-beta:
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
if (version_compare(phpversion(), '8.1', '<')) die('PHP version 8.1 or higher is required.');
|
||||
|
||||
const MYAAC = true;
|
||||
const MYAAC_VERSION = '1.0-RC';
|
||||
const MYAAC_VERSION = '1.0-RC.2';
|
||||
const DATABASE_VERSION = 40;
|
||||
const TABLE_PREFIX = 'myaac_';
|
||||
define('START_TIME', microtime(true));
|
||||
|
@ -105,4 +105,8 @@ $config['clients'] = [
|
||||
1316,
|
||||
1320,
|
||||
1321,
|
||||
1322,
|
||||
1330,
|
||||
1332,
|
||||
1340,
|
||||
];
|
||||
|
@ -40,8 +40,13 @@ else
|
||||
|
||||
if(empty($errors))
|
||||
{
|
||||
if(!admin() && !Validator::newCharacterName($name))
|
||||
if(!Validator::characterName($name)) {
|
||||
$errors[] = Validator::getLastError();
|
||||
}
|
||||
|
||||
if(!admin() && !Validator::newCharacterName($name)) {
|
||||
$errors[] = Validator::getLastError();
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($errors)) {
|
||||
|
@ -148,6 +148,10 @@ if($save)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* two hooks for compatibility
|
||||
*/
|
||||
$hooks->trigger(HOOK_ACCOUNT_CREATE_AFTER_SUBMIT, $params);
|
||||
if (!$hooks->trigger(HOOK_ACCOUNT_CREATE_POST, $params)) {
|
||||
return;
|
||||
}
|
||||
@ -187,6 +191,8 @@ if($save)
|
||||
$new_account->setEMail($email);
|
||||
$new_account->save();
|
||||
|
||||
$hooks->trigger(HOOK_ACCOUNT_CREATE_AFTER_SAVED, ['account' => $new_account]);
|
||||
|
||||
if(USE_ACCOUNT_SALT)
|
||||
$new_account->setCustomField('salt', $salt);
|
||||
|
||||
|
@ -42,7 +42,7 @@ if(!empty($login_account) && !empty($login_password))
|
||||
}
|
||||
}
|
||||
|
||||
if($account_logged->isLoaded() && encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $login_password) == $account_logged->getPassword() && ($limiter->enabled && !$limiter->exceeded($ip))
|
||||
if($account_logged->isLoaded() && encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $login_password) == $account_logged->getPassword() && (!$limiter->enabled || !$limiter->exceeded($ip))
|
||||
)
|
||||
{
|
||||
if (setting('core.account_mail_verify') && (int)$account_logged->getCustomField('email_verified') !== 1) {
|
||||
@ -82,10 +82,10 @@ if(!empty($login_account) && !empty($login_password))
|
||||
$limiter->increment($ip);
|
||||
if ($limiter->exceeded($ip)) {
|
||||
$errorMessage = 'A wrong password has been entered ' . $limiter->max_attempts . ' times in a row. You are unable to log into your account for the next ' . $limiter->ttl . ' minutes. Please wait.';
|
||||
}
|
||||
}
|
||||
|
||||
$errors[] = $errorMessage;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -35,11 +35,12 @@ $settingHighscoresVocationBox = setting('core.highscores_vocation_box');
|
||||
$configVocations = config('vocations');
|
||||
$configVocationsAmount = config('vocations_amount');
|
||||
|
||||
if($settingHighscoresVocationBox && $vocation !== 'all')
|
||||
{
|
||||
$vocationId = null;
|
||||
if($settingHighscoresVocationBox && $vocation !== 'all') {
|
||||
foreach($configVocations as $id => $name) {
|
||||
if(strtolower($name) == $vocation) {
|
||||
$add_vocs = array($id);
|
||||
$vocationId = $id;
|
||||
$add_vocs = [$id];
|
||||
|
||||
$i = $id + $configVocationsAmount;
|
||||
while(isset($configVocations[$i])) {
|
||||
@ -175,7 +176,7 @@ if (empty($highscores)) {
|
||||
$query
|
||||
->join('player_skills', 'player_skills.player_id', '=', 'players.id')
|
||||
->where('skillid', $skill)
|
||||
->addSelect('player_skills.skillid as value');
|
||||
->addSelect('player_skills.value as value');
|
||||
}
|
||||
} else if ($skill == SKILL_FRAGS) // frags
|
||||
{
|
||||
@ -287,6 +288,7 @@ $twig->display('highscores.html.twig', [
|
||||
'skillName' => ($skill == SKILL_FRAGS ? 'Frags' : ($skill == SKILL_BALANCE ? 'Balance' : getSkillName($skill))),
|
||||
'levelName' => ($skill != SKILL_FRAGS && $skill != SKILL_BALANCE ? 'Level' : ($skill == SKILL_BALANCE ? 'Balance' : 'Frags')),
|
||||
'vocation' => $vocation !== 'all' ? $vocation : null,
|
||||
'vocationId' => $vocationId,
|
||||
'types' => $types,
|
||||
'linkPreviousPage' => $linkPreviousPage,
|
||||
'linkNextPage' => $linkNextPage,
|
||||
|
@ -62,7 +62,9 @@ if ($monsterModel && isset($monsterModel->name)) {
|
||||
$elements = json_decode($monster['elements'], true);
|
||||
$immunities = json_decode($monster['immunities'], true);
|
||||
$loot = json_decode($monster['loot'], true);
|
||||
usort($loot, 'sort_by_chance');
|
||||
if (!empty($loot)) {
|
||||
usort($loot, 'sort_by_chance');
|
||||
}
|
||||
|
||||
foreach ($loot as &$item) {
|
||||
$item['name'] = getItemNameById($item['id']);
|
||||
|
@ -100,7 +100,7 @@ foreach($playersOnline as $player) {
|
||||
}
|
||||
|
||||
$record = '';
|
||||
if($players > 0) {
|
||||
if(count($players_data) > 0) {
|
||||
if( setting('core.online_record')) {
|
||||
$result = null;
|
||||
$timestamp = false;
|
||||
@ -114,7 +114,7 @@ if($players > 0) {
|
||||
}
|
||||
}
|
||||
|
||||
if($record) {
|
||||
if($result) {
|
||||
$record = 'The maximum on this game world was ' . $result['record'] . ' players' . ($timestamp ? ' on ' . date("M d Y, H:i:s", $result['timestamp']) . '.' : '.');
|
||||
}
|
||||
}
|
||||
|
@ -23,32 +23,8 @@ class CreateCharacter
|
||||
*/
|
||||
public function checkName($name, &$errors)
|
||||
{
|
||||
$minLength = setting('core.create_character_name_min_length');
|
||||
$maxLength = setting('core.create_character_name_max_length');
|
||||
|
||||
if(empty($name)) {
|
||||
$errors['name'] = 'Please enter a name for your character!';
|
||||
return false;
|
||||
}
|
||||
|
||||
if(strlen($name) > $maxLength) {
|
||||
$errors['name'] = 'Name is too long. Max. length <b>' . $maxLength . '</b> letters.';
|
||||
return false;
|
||||
}
|
||||
|
||||
if(strlen($name) < $minLength) {
|
||||
$errors['name'] = 'Name is too short. Min. length <b>' . $minLength . '</b> letters.';
|
||||
return false;
|
||||
}
|
||||
|
||||
$name_length = strlen($name);
|
||||
if(strspn($name, "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM- '") != $name_length) {
|
||||
$errors['name'] = 'This name contains invalid letters, words or format. Please use only a-Z, - , \' and space.';
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!preg_match("/[A-z ']/", $name)) {
|
||||
$errors['name'] = 'Your name contains illegal characters.';
|
||||
if (!\Validator::characterName($name)) {
|
||||
$errors['name'] = \Validator::getLastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ class RateLimit
|
||||
public int $max_attempts;
|
||||
public int $ttl;
|
||||
public $enabled = false;
|
||||
protected array $data;
|
||||
protected array $data = [];
|
||||
|
||||
public function __construct(string $key, int $max_attempts, int $ttl)
|
||||
{
|
||||
@ -76,7 +76,7 @@ class RateLimit
|
||||
public function save(): void
|
||||
{
|
||||
global $cache;
|
||||
if (!$this->enabled) {
|
||||
if (!$this->enabled || !$cache->enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@ class RateLimit
|
||||
}
|
||||
|
||||
$data = [];
|
||||
if ($this->enabled && $cache->enabled()) {
|
||||
if ($cache->enabled()) {
|
||||
$tmp = '';
|
||||
if ($cache->fetch($this->key, $tmp)) {
|
||||
$data = unserialize($tmp);
|
||||
@ -110,8 +110,6 @@ class RateLimit
|
||||
|
||||
$this->save();
|
||||
}
|
||||
} else {
|
||||
$data = [];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,8 +178,7 @@ class Validator
|
||||
*/
|
||||
public static function characterName($name)
|
||||
{
|
||||
if(!isset($name[0]))
|
||||
{
|
||||
if(empty($name)) {
|
||||
self::$lastError = 'Please enter character name.';
|
||||
return false;
|
||||
}
|
||||
@ -250,7 +249,7 @@ class Validator
|
||||
}
|
||||
}
|
||||
|
||||
if(substr($name_lower, -1) == "'" || substr($name_lower, -1) == "-") {
|
||||
if(str_ends_with($name_lower, "'") || str_ends_with($name_lower, "-")) {
|
||||
self::$lastError = 'Your name contains illegal characters.';
|
||||
return false;
|
||||
}
|
||||
@ -285,7 +284,7 @@ class Validator
|
||||
|
||||
$words_blocked = array_merge(['--', "''","' ", " '", '- ', ' -', "-'", "'-"], setting('core.create_character_name_blocked_words'));
|
||||
foreach($words_blocked as $word) {
|
||||
if(!(strpos($name_lower, $word) === false)) {
|
||||
if(str_contains($name_lower, $word)) {
|
||||
self::$lastError = 'Your name contains illegal words.';
|
||||
return false;
|
||||
}
|
||||
@ -335,7 +334,7 @@ class Validator
|
||||
NPCs::load();
|
||||
if(NPCs::$npcs) {
|
||||
foreach (NPCs::$npcs as $npc) {
|
||||
if(strpos($name_lower, $npc) !== false) {
|
||||
if(str_contains($name_lower, $npc)) {
|
||||
self::$lastError = 'Your name cannot contains NPC name.';
|
||||
return false;
|
||||
}
|
||||
|
@ -45,6 +45,12 @@ define('HOOK_ACCOUNT_CREATE_AFTER_TOWNS', ++$i);
|
||||
define('HOOK_ACCOUNT_CREATE_BEFORE_SUBMIT_BUTTON', ++$i);
|
||||
define('HOOK_ACCOUNT_CREATE_AFTER_FORM', ++$i);
|
||||
define('HOOK_ACCOUNT_CREATE_POST', ++$i);
|
||||
define('HOOK_ACCOUNT_CREATE_AFTER_SUBMIT', ++$i);
|
||||
define('HOOK_ACCOUNT_CREATE_AFTER_SAVED', ++$i);
|
||||
define('HOOK_ACCOUNT_MANAGE_BEFORE_GENERAL_INFORMATION', ++$i);
|
||||
define('HOOK_ACCOUNT_MANAGE_BEFORE_PUBLIC_INFORMATION', ++$i);
|
||||
define('HOOK_ACCOUNT_MANAGE_BEFORE_ACCOUNT_LOGS', ++$i);
|
||||
define('HOOK_ACCOUNT_MANAGE_BEFORE_CHARACTERS', ++$i);
|
||||
define('HOOK_ACCOUNT_LOGIN_BEFORE_PAGE', ++$i);
|
||||
define('HOOK_ACCOUNT_LOGIN_BEFORE_ACCOUNT', ++$i);
|
||||
define('HOOK_ACCOUNT_LOGIN_AFTER_ACCOUNT', ++$i);
|
||||
|
@ -142,10 +142,14 @@ function updateStatus() {
|
||||
}
|
||||
}
|
||||
|
||||
$status['uptime'] = $serverStatus->getUptime();
|
||||
$h = floor($status['uptime'] / 3600);
|
||||
$m = floor(($status['uptime'] - $h * 3600) / 60);
|
||||
$status['uptimeReadable'] = $h . 'h ' . $m . 'm';
|
||||
$uptime = $status['uptime'] = $serverStatus->getUptime();
|
||||
$m = date('m', $uptime);
|
||||
$m = $m > 1 ? "$m months, " : ($m == 1 ? 'month, ' : '');
|
||||
$d = date('d', $uptime);
|
||||
$d = $d > 1 ? "$d days, " : ($d == 1 ? 'day, ' : '');
|
||||
$h = date('H', $uptime);
|
||||
$min = date('i', $uptime);
|
||||
$status['uptimeReadable'] = "{$m}{$d}{$h}h {$min}m";
|
||||
|
||||
$status['monsters'] = $serverStatus->getMonstersCount();
|
||||
$status['motd'] = $serverStatus->getMOTD();
|
||||
|
@ -88,6 +88,7 @@
|
||||
</div>
|
||||
<br/><br/>
|
||||
{% endif %}
|
||||
{{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_GENERAL_INFORMATION') }}
|
||||
<a name="General+Information"></a>
|
||||
<h2>General Information</h2>
|
||||
<table width="100%">
|
||||
@ -127,6 +128,7 @@
|
||||
{% endautoescape %}
|
||||
</table>
|
||||
<br/>
|
||||
{{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_PUBLIC_INFORMATION') }}
|
||||
<a name="Public+Information"></a>
|
||||
<h2>Public Information</h2>
|
||||
<table width="100%">
|
||||
@ -145,6 +147,7 @@
|
||||
{% include('buttons.base.html.twig') %}
|
||||
</form>
|
||||
<br/>
|
||||
{{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_ACCOUNT_LOGS') }}
|
||||
<a name="Account+Logs" ></a>
|
||||
<h2>Action Log</h2>
|
||||
<table>
|
||||
@ -164,6 +167,7 @@
|
||||
{% endautoescape %}
|
||||
</table>
|
||||
<br/>
|
||||
{{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_CHARACTERS') }}
|
||||
<a name="Characters" ></a>
|
||||
<h2>Character list: {{ players|length }} characters.</h2>
|
||||
<table>
|
||||
|
@ -7,21 +7,21 @@
|
||||
<tr>
|
||||
<td>Filters</td>
|
||||
<td>
|
||||
<label for="vocationFilter">Choose a Skill</label>
|
||||
<select onchange="location = this.value;" aria-label="skillFilter" id="skillFilter">
|
||||
<label for="skillFilter">Choose a Skill</label>
|
||||
<select onchange="location = this.value;" id="skillFilter">
|
||||
{% set i = 0 %}
|
||||
{% for link, name in types %}
|
||||
<option value="{{ getLink('highscores') }}/{{ link }}{% if vocation is not null %}{{ vocation }}{% endif %}" class="size_xs">{{ name }}</option>
|
||||
<option value="{{ getLink('highscores') }}/{{ link }}/{% if vocation is not null %}{{ vocation }}{% endif %}" class="size_xs" {% if list is not null and list == link %}selected{% endif %}>{{ name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<label for="vocationFilter">Choose a vocation</label>
|
||||
<select onchange="location = this.value;" aria-label="vocationFilter" id="vocationFilter">
|
||||
<select onchange="location = this.value;" id="vocationFilter">
|
||||
<option value="{{ getLink('highscores') }}/{{ list }}" class="size_xs">[ALL]</option>
|
||||
{% set i = 0 %}
|
||||
{% for i in 1..config.vocations_amount %}
|
||||
<option value="{{ getLink('highscores') }}/{{ list }}/{{ config.vocations[i]|lower }}" class="size_xs">{{ config.vocations[i]}}</option>
|
||||
<option value="{{ getLink('highscores') }}/{{ list }}/{{ config.vocations[i]|lower }}" class="size_xs" {% if vocationId is not null and vocationId == i %}selected{% endif %}>{{ config.vocations[i]}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
|
@ -23,6 +23,9 @@
|
||||
Currently {{ players|length }} players are online.<br/>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if setting('core.online_record') %}
|
||||
{{ record }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -111,6 +111,7 @@
|
||||
</div>
|
||||
<br/><br/>
|
||||
{% endif %}
|
||||
{{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_GENERAL_INFORMATION') }}
|
||||
<a name="General+Information" ></a>
|
||||
<div class="TopButtonContainer">
|
||||
<div class="TopButton">
|
||||
@ -221,6 +222,7 @@
|
||||
{% endset %}
|
||||
{% include 'tables.headline.html.twig' %}
|
||||
<br/>
|
||||
{{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_PUBLIC_INFORMATION') }}
|
||||
<a name="Public+Information"></a>
|
||||
<div class="TopButtonContainer">
|
||||
<div class="TopButton">
|
||||
@ -280,6 +282,7 @@
|
||||
{% endset %}
|
||||
{% include 'tables.headline.html.twig' %}
|
||||
<br/>
|
||||
{{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_ACCOUNT_LOGS') }}
|
||||
<a name="Account+Logs" ></a>
|
||||
<div class="TopButtonContainer">
|
||||
<div class="TopButton">
|
||||
@ -333,6 +336,7 @@
|
||||
{% endset %}
|
||||
{% include 'tables.headline.html.twig' %}
|
||||
<br/>
|
||||
{{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_CHARACTERS') }}
|
||||
<a name="Characters" ></a>
|
||||
<div class="TopButtonContainer">
|
||||
<div class="TopButton" >
|
||||
|
@ -23,32 +23,36 @@ if(isset($_GET['account']))
|
||||
{
|
||||
$account = $_GET['account'];
|
||||
if(USE_ACCOUNT_NAME) {
|
||||
if(!Validator::accountName($account))
|
||||
if(!Validator::accountName($account)) {
|
||||
error_(Validator::getLastError());
|
||||
}
|
||||
}
|
||||
else if(!Validator::accountId($account))
|
||||
else if(!Validator::accountId($account)) {
|
||||
error_(Validator::getLastError());
|
||||
}
|
||||
|
||||
$_account = new OTS_Account();
|
||||
if(USE_ACCOUNT_NAME || USE_ACCOUNT_NUMBER)
|
||||
if(USE_ACCOUNT_NAME || USE_ACCOUNT_NUMBER) {
|
||||
$_account->find($account);
|
||||
else
|
||||
} else {
|
||||
$_account->load($account);
|
||||
}
|
||||
|
||||
$accountNameOrNumber = (USE_ACCOUNT_NAME ? ' name' : 'number');
|
||||
if($_account->isLoaded())
|
||||
if($_account->isLoaded()) {
|
||||
error_("Account with this $accountNameOrNumber already exist.");
|
||||
}
|
||||
|
||||
success_("Good account $accountNameOrNumber ($account).");
|
||||
}
|
||||
else if(isset($_GET['email']))
|
||||
{
|
||||
$email = $_GET['email'];
|
||||
if(!Validator::email($email))
|
||||
if(!Validator::email($email)) {
|
||||
error_(Validator::getLastError());
|
||||
}
|
||||
|
||||
if(setting('core.account_mail_unique'))
|
||||
{
|
||||
if(setting('core.account_mail_unique')) {
|
||||
if(Account::where('email', '=', $email)->exists())
|
||||
error_('Account with this e-mail already exist.');
|
||||
}
|
||||
@ -62,11 +66,13 @@ else if(isset($_GET['name']))
|
||||
$name = strtolower(stripslashes($name));
|
||||
}
|
||||
|
||||
if(!Validator::characterName($name))
|
||||
if(!Validator::characterName($name)) {
|
||||
error_(Validator::getLastError());
|
||||
}
|
||||
|
||||
if(!admin() && !Validator::newCharacterName($name))
|
||||
if(!admin() && !Validator::newCharacterName($name)) {
|
||||
error_(Validator::getLastError());
|
||||
}
|
||||
|
||||
$createCharacter = new CreateCharacter();
|
||||
if (!$createCharacter->checkName($name, $errors)) {
|
||||
@ -83,16 +89,19 @@ else if(isset($_GET['password']) && isset($_GET['password_confirm'])) {
|
||||
error_('Please enter the password for your new account.');
|
||||
}
|
||||
|
||||
if(!Validator::password($password))
|
||||
if(!Validator::password($password)) {
|
||||
error_(Validator::getLastError());
|
||||
}
|
||||
|
||||
if($password != $password_confirm)
|
||||
if($password != $password_confirm) {
|
||||
error_('Passwords are not the same.');
|
||||
}
|
||||
|
||||
success_(1);
|
||||
}
|
||||
else
|
||||
else {
|
||||
error_('Error: no input specified.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Output message & exit.
|
||||
|
Loading…
x
Reference in New Issue
Block a user