2
0
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:
slawkens 2024-11-07 16:34:10 +01:00
commit a5075d77dc
19 changed files with 115 additions and 71 deletions

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