diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index 4dd8e9dd..b606e723 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -1,9 +1,9 @@ name: Cypress on: pull_request: - branches: [main] + branches: [develop] push: - branches: [main] + branches: [develop] jobs: cypress: diff --git a/.github/workflows/phplint.yml b/.github/workflows/phplint.yml index a1b9c4ac..aa95caed 100644 --- a/.github/workflows/phplint.yml +++ b/.github/workflows/phplint.yml @@ -1,9 +1,9 @@ name: PHP Linting on: pull_request: - branches: [main] + branches: [develop] push: - branches: [main] + branches: [develop] jobs: phplint: diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 8a1e73a3..6df6979a 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -2,9 +2,9 @@ name: "PHPStan" on: pull_request: - branches: [main] + branches: [develop] push: - branches: [main] + branches: [develop] jobs: tests: diff --git a/CHANGELOG-2.x.md b/CHANGELOG-2.x.md index 096b1ee2..63cbdde7 100644 --- a/CHANGELOG-2.x.md +++ b/CHANGELOG-2.x.md @@ -1,9 +1,12 @@ ## [2.0-dev - x.x.2025] +### Added +* Add an "access" option to Menus (#340) + * Possibility to hide menus for unauthorized users +* Add the possibility to fetch skills in the getTopPlayers function + ### Changed * Better handling of vocations: (#345) * Load from vocations.xml (No need to manually set) * Support for Monk vocation -* Add an access option to Menus (#340) - * Possibility to hide menus for unauthorized users * Reworked account action logs to use a single IP column as varchar(45) for both ipv4 and ipv6 (#289) diff --git a/admin/pages/accounts.php b/admin/pages/accounts.php index 8a20aa62..ee8ee33b 100644 --- a/admin/pages/accounts.php +++ b/admin/pages/accounts.php @@ -631,6 +631,7 @@ else if (isset($_REQUEST['search'])) { +
@@ -641,6 +642,7 @@ else if (isset($_REQUEST['search'])) {
+
diff --git a/admin/pages/players.php b/admin/pages/players.php index bd3ab713..17e8f7d1 100644 --- a/admin/pages/players.php +++ b/admin/pages/players.php @@ -10,6 +10,7 @@ use MyAAC\Forum; use MyAAC\Models\Player; +use MyAAC\Server\XML\Vocations; defined('MYAAC') or die('Direct access not allowed!'); @@ -237,7 +238,30 @@ else if (isset($_REQUEST['search'])) { $player->setGroup($groups->getGroup($group)); $player->setLevel($level); $player->setExperience($experience); + + if ($db->hasColumn('players', 'promotion')) { + $promotion = 0; + + $vocationOriginal = Vocations::getOriginal($vocation); + if ($vocation != $vocationOriginal) { + $tmpId = $vocationOriginal; + while($promoted = Vocations::getPromoted($tmpId)) { + $promotion++; + + $tmpId = $promoted; + if ($promoted == $vocation) { + break; + } + } + + $vocation = $vocationOriginal; + } + + $player->setPromotion($promotion); + } + $player->setVocation($vocation); + $player->setHealth($health); $player->setHealthMax($health_max); $player->setMagLevel($magic_level); diff --git a/system/functions.php b/system/functions.php index f11fa382..a1ca6b72 100644 --- a/system/functions.php +++ b/system/functions.php @@ -17,6 +17,8 @@ use MyAAC\Models\Guild; use MyAAC\Models\House; use MyAAC\Models\Pages; use MyAAC\Models\Player; +use MyAAC\Models\PlayerDeath; +use MyAAC\Models\PlayerKillers; use MyAAC\News; use MyAAC\Plugins; use MyAAC\Settings; @@ -878,11 +880,12 @@ function getWorldName($id) * * @param string $to Recipient email address. * @param string $subject Subject of the message. - * @param string $body Message body in html format. + * @param string $body Message body in HTML format. * @param string $altBody Alternative message body, plain text. * @return bool PHPMailer status returned (success/failure). + * @throws \PHPMailer\PHPMailer\Exception */ -function _mail($to, $subject, $body, $altBody = '', $add_html_tags = true) +function _mail(string $to, string $subject, string $body, string $altBody = ''): bool { global $mailer, $config; @@ -900,12 +903,6 @@ function _mail($to, $subject, $body, $altBody = '', $add_html_tags = true) $mailer->clearAllRecipients(); } - $signature_html = setting('core.mail_signature_html'); - if($add_html_tags && isset($body[0])) - $tmp_body = '' . $body . '

' . $signature_html . ''; - else - $tmp_body = $body . '

' . $signature_html; - $mailOption = setting('core.mail_option'); if($mailOption == MAIL_SMTP) { @@ -932,6 +929,9 @@ function _mail($to, $subject, $body, $altBody = '', $add_html_tags = true) $mailer->isMail(); } + $signature_html = setting('core.mail_signature_html'); + $tmp_body = $body . '

' . $signature_html; + $mailer->isHTML(isset($body[0]) > 0); $mailer->From = setting('core.mail_address'); $mailer->Sender = setting('core.mail_address'); @@ -1135,11 +1135,44 @@ function csrfProtect(): void } } -function getTopPlayers($limit = 5, $skill = 'level') { +function getSkillIdByName(string $name): int|null +{ + $skills = [ + 'level' => POT::SKILL_LEVEL, + 'experience' => POT::SKILL_LEVEL, + + 'magic' => POT::SKILL_MAGIC, + 'maglevel' => POT::SKILL_MAGIC, + + 'balance' => SKILL_BALANCE, + 'frags' => SKILL_FRAGS, + + 'club' => POT::SKILL_CLUB, + 'sword' => POT::SKILL_SWORD, + 'axe' => POT::SKILL_AXE, + 'dist' => POT::SKILL_DIST, + 'distance' => POT::SKILL_DIST, + 'shield' => POT::SKILL_SHIELD, + 'shielding' => POT::SKILL_SHIELD, + 'fish' => POT::SKILL_FISH, + 'fishing' => POT::SKILL_FISH, + ]; + + return $skills[$name] ?? null; +} + +function getTopPlayers($limit = 5, $skill = POT::SKILL_LEVEL) +{ global $db; - if ($skill === 'level') { - $skill = 'experience'; + $skillOriginal = $skill; + + if (is_string($skill)) { + $skill = getSkillIdByName($skill); + } + + if (!is_numeric($skill)) { + throw new RuntimeException("getTopPlayers: Invalid skill: $skillOriginal"); } return Cache::remember("top_{$limit}_{$skill}", 2 * 60, function () use ($db, $limit, $skill) { @@ -1160,15 +1193,64 @@ function getTopPlayers($limit = 5, $skill = 'level') { $columns[] = 'lookmount'; } - return Player::query() + $query = Player::query() ->select($columns) ->withOnlineStatus() ->notDeleted() ->where('group_id', '<', setting('core.highscores_groups_hidden')) ->whereNotIn('id', setting('core.highscores_ids_hidden')) ->where('account_id', '!=', 1) - ->orderByDesc($skill) - ->limit($limit) + ->orderByDesc('value'); + + if ($limit > 0) { + $query->limit($limit); + } + + if ($skill >= POT::SKILL_FIRST && $skill <= POT::SKILL_LAST) { // skills + if ($db->hasColumn('players', 'skill_fist')) {// tfs 1.0 + $skill_ids = array( + POT::SKILL_FIST => 'skill_fist', + POT::SKILL_CLUB => 'skill_club', + POT::SKILL_SWORD => 'skill_sword', + POT::SKILL_AXE => 'skill_axe', + POT::SKILL_DIST => 'skill_dist', + POT::SKILL_SHIELD => 'skill_shielding', + POT::SKILL_FISH => 'skill_fishing', + ); + + $query + ->addSelect($skill_ids[$skill] . ' as value') + ->orderByDesc($skill_ids[$skill] . '_tries'); + } else { + $query + ->join('player_skills', 'player_skills.player_id', '=', 'players.id') + ->where('skillid', $skill) + ->addSelect('player_skills.value as value'); + } + } else if ($skill == SKILL_FRAGS) // frags + { + if ($db->hasTable('player_killers')) { + $query->addSelect(['value' => PlayerKillers::whereColumn('player_killers.player_id', 'players.id')->selectRaw('COUNT(*)')]); + } else { + $query->addSelect(['value' => PlayerDeath::unjustified()->whereColumn('player_deaths.killed_by', 'players.name')->selectRaw('COUNT(*)')]); + } + } else if ($skill == SKILL_BALANCE) // balance + { + $query + ->addSelect('players.balance as value'); + } else { + if ($skill == POT::SKILL_MAGIC) { + $query + ->addSelect('players.maglevel as value', 'players.maglevel') + ->orderByDesc('manaspent'); + } else { // level + $query + ->addSelect('players.level as value', 'players.experience') + ->orderByDesc('experience'); + } + } + + return $query ->get() ->map(function ($e, $i) { $row = $e->toArray(); diff --git a/system/pages/#examples/top-5.php b/system/pages/#examples/top-5.php new file mode 100644 index 00000000..f091f2b5 --- /dev/null +++ b/system/pages/#examples/top-5.php @@ -0,0 +1,29 @@ + + + 'The character information has been changed.' )); $show_form = false; + + $hooks->trigger(HOOK_ACCOUNT_CHARACTERS_CHANGE_COMMENT_AFTER_SUCCESS, ['player' => $player]); } } } else { @@ -70,9 +72,11 @@ if($show_form) { } if(isset($player) && $player) { - $twig->display('account.characters.change-comment.html.twig', array( - 'player' => $player->toArray() - )); + $_player = $player->toArray(); + $_player['id'] = $player->id; // Hack, as it's somehow missing in the toArray() function + + $twig->display('account.characters.change-comment.html.twig', [ + 'player' => $_player, + ]); } } -?> diff --git a/system/pages/forum/show_thread.php b/system/pages/forum/show_thread.php index ceeb2602..441cf0da 100644 --- a/system/pages/forum/show_thread.php +++ b/system/pages/forum/show_thread.php @@ -70,7 +70,7 @@ foreach($posts as &$post) { } $post['group'] = $groupName; - $post['player_link'] = getPlayerLink($player->getName()); + $post['player_link'] = '' . $player->getName() . ''; $post['vocation'] = $player->getVocationName(); diff --git a/system/settings.php b/system/settings.php index e1b0fa1a..8a49a218 100644 --- a/system/settings.php +++ b/system/settings.php @@ -319,6 +319,34 @@ return [ }, ], ], + /** + * @deprecated + * To be removed in v3.0 + */ + 'vocations_amount' => [ + 'hidden' => true, + 'type' => 'number', + //'name' => 'Vocations Amount', + //'desc' => 'How many basic vocations your server got (without promotion)', + 'default' => 4, + 'callbacks' => [ + 'get' => function () { + return config('vocations_amount'); + }, + ], + ], + 'vocations' => [ + 'hidden' => true, + 'type' => 'textarea', + //'name' => 'Vocation Names', + //'desc' => 'Separated by comma. Must be in the same order as in vocations.xml, starting with id: 0.', + 'default' => 'None, Sorcerer, Druid, Paladin, Knight, Master Sorcerer, Elder Druid,Royal Paladin, Elite Knight', + 'callbacks' => [ + 'get' => function () { + return config('vocations'); + }, + ], + ], [ 'type' => 'category', 'title' => 'Database', diff --git a/system/src/Cache/PHP.php b/system/src/Cache/PHP.php index 9b21552b..5c455b7e 100644 --- a/system/src/Cache/PHP.php +++ b/system/src/Cache/PHP.php @@ -45,7 +45,7 @@ class PHP rename($tmp, $file); } - public function get($key): string + public function get($key) { $tmp = ''; if ($this->fetch($key, $tmp)) { diff --git a/system/src/Server/XML/Vocations.php b/system/src/Server/XML/Vocations.php index 6bd8f357..ff90ece8 100644 --- a/system/src/Server/XML/Vocations.php +++ b/system/src/Server/XML/Vocations.php @@ -77,7 +77,15 @@ class Vocations } public static function getOriginal(int $id): ?int { - return self::$vocationsFrom[$id] ?? null; + while ($tmpId = self::$vocationsFrom[$id]) { + if ($tmpId == $id) { + break; + } + + $id = $tmpId; + } + + return $id; } public static function getBase($includingRook = true): array { diff --git a/system/src/global.php b/system/src/global.php index a140bdde..a5701f00 100644 --- a/system/src/global.php +++ b/system/src/global.php @@ -28,6 +28,10 @@ define('HOOK_CHARACTERS_AFTER_CHARACTERS', ++$i); define('HOOK_LOGIN', ++$i); define('HOOK_LOGIN_ATTEMPT', ++$i); define('HOOK_LOGOUT', ++$i); +define('HOOK_ACCOUNT_CHARACTERS_CHANGE_COMMENT_AFTER_SUCCESS', ++$i); +define('HOOK_ACCOUNT_CHARACTERS_CHANGE_COMMENT_AFTER_NAME', ++$i); +define('HOOK_ACCOUNT_CHARACTERS_CHANGE_COMMENT_AFTER_HIDE_ACCOUNT', ++$i); +define('HOOK_ACCOUNT_CHARACTERS_CHANGE_COMMENT_AFTER_COMMENT', ++$i); define('HOOK_ACCOUNT_CHANGE_PASSWORD_AFTER_OLD_PASSWORD', ++$i); define('HOOK_ACCOUNT_CHANGE_PASSWORD_AFTER_NEW_PASSWORD', ++$i); define('HOOK_ACCOUNT_CHANGE_PASSWORD_POST', ++$i); diff --git a/system/templates/account.characters.change-comment.html.twig b/system/templates/account.characters.change-comment.html.twig index ab8fb384..5d3a6912 100644 --- a/system/templates/account.characters.change-comment.html.twig +++ b/system/templates/account.characters.change-comment.html.twig @@ -33,6 +33,7 @@ If you do not want to specify a certain field, just leave it blank.

Name: {{ player.name }} + {{ hook('HOOK_ACCOUNT_CHARACTERS_CHANGE_COMMENT_AFTER_NAME') }} Hide Account: @@ -42,6 +43,7 @@ If you do not want to specify a certain field, just leave it blank.

{% if player.group_id > 1 %} (you will be also hidden on the Team page!){% endif %} + {{ hook('HOOK_ACCOUNT_CHARACTERS_CHANGE_COMMENT_AFTER_HIDE_ACCOUNT') }}
@@ -65,6 +67,7 @@ If you do not want to specify a certain field, just leave it blank.

Comment:
[max. length: 2000 chars, 50 lines (ENTERs)] + {{ hook('HOOK_ACCOUNT_CHARACTERS_CHANGE_COMMENT_AFTER_COMMENT') }} diff --git a/system/templates/account.management.html.twig b/system/templates/account.management.html.twig index 7df72d9e..546c44ff 100644 --- a/system/templates/account.management.html.twig +++ b/system/templates/account.management.html.twig @@ -114,7 +114,7 @@ Last Login: - {{ "now"|date("j F Y, G:i:s") }} + {{ account_logged.getCustomField('web_lastlogin')|date("j F Y, G:i:s") }} {% autoescape false %} diff --git a/system/templates/online.html.twig b/system/templates/online.html.twig index 2ce64287..07af9151 100644 --- a/system/templates/online.html.twig +++ b/system/templates/online.html.twig @@ -26,7 +26,7 @@ {% for vocationId in baseVocations %} - + {% endfor %} diff --git a/templates/tibiacom/account.management.html.twig b/templates/tibiacom/account.management.html.twig index 649eba36..d5fc70b4 100644 --- a/templates/tibiacom/account.management.html.twig +++ b/templates/tibiacom/account.management.html.twig @@ -151,7 +151,7 @@ Last Login: - {{ "now"|date("j F Y, G:i:s") }} + {{ account_logged.getCustomField('web_lastlogin')|date("j F Y, G:i:s") }} {% autoescape false %}