Compare commits

...

38 Commits

Author SHA1 Message Date
slawkens
8facc68050 New images for vocations (+ added Monk) 2025-12-18 23:02:39 +01:00
slawkens
2fac0ab491 Restore vocations.xml loading
For better handling of vocations
Monk is supported now
2025-12-18 22:04:03 +01:00
slawkens
4fffaf6aff Merge branch 'main' into develop 2025-12-18 14:33:45 +01:00
slawkens
c44c9f9cf4 Add type hints and return types to cache classes 2025-12-18 14:33:07 +01:00
slawkens
ccfd6f1a87 Add PHP to cache engine list in settings 2025-12-18 14:23:25 +01:00
slawkens
96b8e00f49 Refactor PHP cache to store expiration and improve typing
Cache entries now store both the value and expiration timestamp in the file, allowing for more reliable expiration checks. Method signatures have been updated with type hints.
2025-12-18 14:22:42 +01:00
slawkens
11cb1cf97e Save db cache only if it has changed 2025-12-18 11:53:06 +01:00
slawkens
c5d3d3a25f Merge branch 'main' into develop 2025-12-14 10:21:33 +01:00
slawkens
e1197515f3 Merge branch 'main' into develop 2025-11-23 10:13:00 +01:00
slawkens
eebfc600cb Detect "deletion" column in guilds show 2025-11-18 00:15:32 +01:00
slawkens
9a99018dce Merge branch 'main' into develop 2025-11-13 20:08:38 +01:00
slawkens
6479546c22 Update CHANGELOG-2.x.md 2025-10-31 16:23:41 +01:00
slawkens
effb23f367 Create CHANGELOG-2.x.md 2025-10-31 15:32:49 +01:00
slawkens
08657c1599 Fix migration 47.php (convert IPs) 2025-10-31 15:25:55 +01:00
slawkens
1379c93439 Create 47.php 2025-10-31 07:00:11 +01:00
slawkens
19b1cfdd34 Merge branch 'main' into develop 2025-10-31 06:56:34 +01:00
slawkens
e719725841 Merge branch 'main' into develop 2025-05-09 13:45:54 +02:00
slawkens
bb3e90110d Merge branch 'main' into develop 2025-05-09 13:14:12 +02:00
slawkens
2f0758e351 Update schema.sql 2025-04-26 06:17:58 +02:00
slawkens
6667c8c364 Merge branch 'main' into develop 2025-04-26 06:17:38 +02:00
slawkens
c13a540878 Merge branch 'main' into develop 2025-04-18 13:58:42 +02:00
slawkens
869ec035d9 Merge branch 'main' into develop 2025-04-04 21:09:12 +02:00
slawkens
9d696d31d8 Merge branch 'main' into develop 2025-04-04 20:08:24 +02:00
slawkens
8cc4caf587 Merge branch 'main' into develop 2025-04-01 07:43:57 +02:00
slawkens
e1d1c7d5db Merge branch 'main' into develop 2025-03-31 22:21:16 +02:00
slawkens
320733c2c1 Merge branch 'main' into develop 2025-03-31 19:51:21 +02:00
slawkens
c1809a98d1 Merge branch 'main' into develop 2025-03-30 07:11:15 +02:00
slawkens
46ed541015 Merge branch 'main' into develop 2025-03-16 20:54:40 +01:00
slawkens
29207361b7 Merge branch 'main' into develop 2025-03-16 12:39:32 +01:00
slawkens
25013ae91b Merge branch 'main' into develop 2025-03-15 23:09:14 +01:00
slawkens
5d630ba9dd Fix the second "Save" button -> addition to previous commit 2025-03-15 22:49:43 +01:00
slawkens
feadf1314d Fix: add possibility to remove all menu items 2025-03-15 22:49:37 +01:00
slawkens
08b8a716d4 Fix the second "Save" button -> addition to previous commit 2025-03-10 13:04:57 +01:00
slawkens
cc26b5c744 Fix: add possibility to remove all menu items 2025-03-10 10:48:19 +01:00
Slawomir Boczek
cb6e9a6a88 Feature/twig hooks filters (#258)
* feat: Hooks filters

* Cleanup
2025-03-09 21:39:37 +01:00
slawkens
4adb0758c5 Set version to 2.0-dev 2025-03-09 21:26:24 +01:00
Slawomir Boczek
7312383f73 Account actions rework on ip (Use single column for IP - VARCHAR(45)) (#289)
* Account actions rework on ip (Use single column for IP - VARCHAR(45))

* No foreach needed here
2025-03-09 21:18:12 +01:00
slawkens
3c1210fefa Nothing important, just better code style 2025-03-03 20:07:54 +01:00
32 changed files with 293 additions and 180 deletions

4
CHANGELOG-2.x.md Normal file
View File

@@ -0,0 +1,4 @@
## [2.0-dev - x.x.2025]
### Changed
* Reworked account action logs to use single IP column as varchar(45) for both ipv4 and ipv6 (https://github.com/slawkens/myaac/pull/289)

View File

@@ -9,6 +9,7 @@
*/
use MyAAC\Models\Account as AccountModel;
use MyAAC\Models\AccountAction;
use MyAAC\Models\Player;
defined('MYAAC') or die('Direct access not allowed!');
@@ -481,9 +482,8 @@ else if (isset($_REQUEST['search'])) {
</thead>
<tbody>
<?php
$accountActions = \MyAAC\Models\AccountAction::where('account_id', $account->getId())->orderByDesc('date')->get();
$accountActions = AccountAction::where('account_id', $account->getId())->orderByDesc('date')->get();
foreach ($accountActions as $i => $log):
$log->ip = ($log->ip != 0 ? long2ip($log->ip) : inet_ntop($log->ipv6));
?>
<tr>
<td><?php echo $i + 1; ?></td>

View File

@@ -26,8 +26,8 @@
if (version_compare(phpversion(), '8.1', '<')) die('PHP version 8.1 or higher is required.');
const MYAAC = true;
const MYAAC_VERSION = '1.8.7-dev';
const DATABASE_VERSION = 46;
const MYAAC_VERSION = '2.0-dev';
const DATABASE_VERSION = 47;
const TABLE_PREFIX = 'myaac_';
define('START_TIME', microtime(true));
define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX'));

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 33 KiB

BIN
images/monk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -1,11 +1,11 @@
CREATE TABLE IF NOT EXISTS `myaac_account_actions`
(
`id` int NOT NULL AUTO_INCREMENT,
`account_id` int NOT NULL,
`ip` int unsigned NOT NULL DEFAULT 0,
`ipv6` binary(16) NOT NULL DEFAULT 0,
`ip` varchar(45) NOT NULL DEFAULT '',
`date` int NOT NULL DEFAULT 0,
`action` varchar(255) NOT NULL DEFAULT '',
KEY (`account_id`)
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
CREATE TABLE IF NOT EXISTS `myaac_account_emails_verify`

View File

@@ -5,8 +5,6 @@ $deprecatedConfig = [
'genders',
'template',
'template_allow_change',
'vocations_amount',
'vocations',
'client',
'session_prefix',
'friendly_urls',

View File

@@ -14,6 +14,7 @@ use MyAAC\CsrfToken;
use MyAAC\Hooks;
use MyAAC\Plugins;
use MyAAC\Models\Town;
use MyAAC\Server\XML\Vocations;
use MyAAC\Settings;
defined('MYAAC') or die('Direct access not allowed!');
@@ -214,3 +215,5 @@ if (count($towns) <= 0) {
config(['towns', $towns]);
unset($towns);
new Vocations();

View File

@@ -12,6 +12,8 @@
* @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU Lesser General Public License, Version 3
*/
use MyAAC\Models\AccountAction;
/**
* OTServ account abstraction.
*
@@ -1007,26 +1009,16 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
public function logAction($action)
{
$ip = get_browser_real_ip();
if(!str_contains($ip, ":")) {
$ipv6 = '0';
}
else {
$ipv6 = $ip;
$ip = '';
}
return $this->db->exec('INSERT INTO `' . TABLE_PREFIX . 'account_actions` (`account_id`, `ip`, `ipv6`, `date`, `action`) VALUES (' . $this->db->quote($this->getId()).', ' . ($ip == '' ? '0' : $this->db->quote(ip2long($ip))) . ', (' . ($ipv6 == '0' ? $this->db->quote('') : $this->db->quote(inet_pton($ipv6))) . '), UNIX_TIMESTAMP(NOW()), ' . $this->db->quote($action).')');
AccountAction::create([
'account_id' => $this->getId(),
'ip' => get_browser_real_ip(),
'date' => time(),
'action' => $action,
]);
}
public function getActionsLog($limit1, $limit2)
{
$actions = array();
foreach($this->db->query('SELECT `ip`, `ipv6`, `date`, `action` FROM `' . TABLE_PREFIX . 'account_actions` WHERE `account_id` = ' . $this->data['id'] . ' ORDER by `date` DESC LIMIT ' . $limit1 . ', ' . $limit2 . '')->fetchAll() as $a)
$actions[] = array('ip' => $a['ip'], 'ipv6' => $a['ipv6'], 'date' => $a['date'], 'action' => $a['action']);
return $actions;
public function getActionsLog($limit) {
return AccountAction::where('account_id', $this->data['id'])->orderByDesc('date')->limit($limit)->get()->toArray();
}
/**
* Returns players iterator.

View File

@@ -26,6 +26,7 @@ use MyAAC\Cache\Cache;
*/
class OTS_DB_MySQL extends OTS_Base_DB
{
private bool $hasCacheChanged = false;
private array $has_table_cache = [];
private array $has_column_cache = [];
private array $get_column_info_cache = [];
@@ -164,7 +165,7 @@ class OTS_DB_MySQL extends OTS_Base_DB
$cache->delete('database_columns_info');
$cache->delete('database_checksum');
}
else {
else if ($this->hasCacheChanged) {
$cache->set('database_tables', serialize($this->has_table_cache), 3600);
$cache->set('database_columns', serialize($this->has_column_cache), 3600);
$cache->set('database_columns_info', serialize($this->get_column_info_cache), 3600);
@@ -228,6 +229,8 @@ class OTS_DB_MySQL extends OTS_Base_DB
private function hasTableInternal($name): bool
{
$this->hasCacheChanged = true;
return ($this->has_table_cache[$name] = $this->query('SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = ' . $this->quote(config('database_name')) . ' AND `TABLE_NAME` = ' . $this->quote($name) . ' LIMIT 1;')->rowCount() > 0);
}
@@ -241,6 +244,8 @@ class OTS_DB_MySQL extends OTS_Base_DB
}
private function hasColumnInternal($table, $column): bool {
$this->hasCacheChanged = true;
return $this->hasTable($table) && ($this->has_column_cache[$table . '.' . $column] = count($this->query('SHOW COLUMNS FROM `' . $table . "` LIKE " . $this->quote($column))->fetchAll()) > 0);
}
@@ -272,11 +277,14 @@ class OTS_DB_MySQL extends OTS_Base_DB
return false;
}
$this->hasCacheChanged = true;
$formatResult = function ($result) {
return [
'field' => $result['Field'],
'type' => $result['Type'],
'null' => strtolower($result['Null']),
'key' => strtolower($result['Key'] ?? ''),
'default' => $result['Default'],
'extra' => $result['Extra'],
];

View File

@@ -852,13 +852,7 @@ class OTS_Player extends OTS_Row_DAO
throw new E_OTS_NotLoaded();
}
if(isset($this->data['promotion'])) {
global $config;
if((int)$this->data['promotion'] > 0)
return ($this->data['vocation'] + ($this->data['promotion'] * $config['vocations_amount']));
}
return $this->data['vocation'];
return \OTS_Toolbox::getVocationFromPromotion($this->data['vocation'], $this->data['promotion'] ?? 0);
}

View File

@@ -13,6 +13,8 @@
* @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU Lesser General Public License, Version 3
*/
use MyAAC\Server\XML\Vocations;
/**
* Toolbox for common operations.
*
@@ -110,14 +112,21 @@ class OTS_Toolbox
$list->setFilter($filter);
return $list;
}
public static function getVocationName($id, $promotion = 0): string
public static function getVocationFromPromotion($id, $promotion = 0): int
{
if($promotion > 0) {
$id = ($id + ($promotion * config('vocations_amount')));
for ($i = 0; $i < $promotion; $i++) {
if ($_id = Vocations::getPromoted($id)) {
$id = $_id;
}
}
}
return config('vocations')[$id] ?? 'Unknown';
return $id;
}
public static function getVocationName($id, $promotion = 0): string {
return config('vocations')[self::getVocationFromPromotion($id, $promotion)] ?? 'Unknown';
}
}

42
system/migrations/47.php Normal file
View File

@@ -0,0 +1,42 @@
<?php
/**
* @var OTS_DB_MySQL $db
*/
// 2025-02-27
// remove ipv6, change to ip (for both ipv4 + ipv6) as VARCHAR(45)
$up = function () use ($db) {
$accountActionsInfo = $db->getColumnInfo(TABLE_PREFIX . 'account_actions', 'account_id');
if ($accountActionsInfo && is_array($accountActionsInfo) && $accountActionsInfo['key'] == 'pri') {
$db->query("ALTER TABLE `myaac_account_actions` DROP KEY `account_id`;");
}
if (!$db->hasColumn(TABLE_PREFIX . 'account_actions', 'id')) {
$db->addColumn(TABLE_PREFIX . 'account_actions', 'id', 'INT NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`id`)');
}
$db->modifyColumn(TABLE_PREFIX . 'account_actions', 'ip', "VARCHAR(45) NOT NULL DEFAULT ''");
$db->query("UPDATE `" . TABLE_PREFIX . "account_actions` SET `ip` = INET_NTOA(`ip`) WHERE `ip` != '0';");
$db->query("UPDATE `" . TABLE_PREFIX . "account_actions` SET `ip` = INET6_NTOA(`ipv6`) WHERE `ip` = '0';");
if ($db->hasColumn(TABLE_PREFIX . 'account_actions', 'ipv6')) {
$db->dropColumn(TABLE_PREFIX . 'account_actions', 'ipv6');
}
};
$down = function () use ($db) {
if ($db->hasColumn(TABLE_PREFIX . 'account_actions', 'id')) {
$db->query("ALTER TABLE `" . TABLE_PREFIX . "account_actions` DROP `id`;");
}
$db->query("ALTER TABLE `" . TABLE_PREFIX . "account_actions` ADD KEY (`account_id`);");
if (!$db->hasColumn(TABLE_PREFIX . 'account_actions', 'ipv6')) {
$db->addColumn(TABLE_PREFIX . 'account_actions', 'ipv6', "BINARY(16) NOT NULL DEFAULT 0x00000000000000000000000000000000 AFTER ip");
}
$db->query("UPDATE `" . TABLE_PREFIX . "account_actions` SET `ipv6` = INET6_ATON(ip) WHERE NOT IS_IPV4(`ip`);");
$db->query("UPDATE `" . TABLE_PREFIX . "account_actions` SET `ip` = INET_ATON(`ip`) WHERE IS_IPV4(`ip`);");
$db->query("UPDATE `" . TABLE_PREFIX . "account_actions` SET `ip` = 0 WHERE `ipv6` != 0x00000000000000000000000000000000;");
$db->modifyColumn(TABLE_PREFIX . 'account_actions', 'ip', "INT(11) UNSIGNED NOT NULL DEFAULT 0;");
};

View File

@@ -96,12 +96,8 @@ if($email_new_time > 1)
}
}
$actions = array();
foreach($account_logged->getActionsLog(0, 1000) as $action) {
$actions[] = array('action' => $action['action'], 'date' => $action['date'], 'ip' => $action['ip'] != 0 ? long2ip($action['ip']) : inet_ntop($action['ipv6']));
}
$actions = $account_logged->getActionsLog(1000);
$players = array();
/** @var OTS_Players_List $account_players */
$account_players = $account_logged->getPlayersList();
$account_players->orderBy('id');

View File

@@ -452,10 +452,8 @@ WHERE killers.death_id = '".$death['id']."' ORDER BY killers.final_hit DESC, kil
if($query->rowCount() > 0) {
echo 'Did you mean:<ul>';
foreach($query as $player) {
if(isset($player['promotion'])) {
if((int)$player['promotion'] > 0)
$player['vocation'] += ($player['promotion'] * $config['vocations_amount']);
}
$player['vocation'] = OTS_Toolbox::getVocationFromPromotion($player['vocation'], $player['promotion'] ?? 0);
echo '<li>' . getPlayerLink($player['name']) . ' (<small><strong>level ' . $player['level'] . ', ' . $config['vocations'][$player['vocation']] . '</strong></small>)</li>';
}
echo '</ul>';

View File

@@ -13,6 +13,7 @@ use MyAAC\Cache\Cache;
use MyAAC\Models\Player;
use MyAAC\Models\PlayerDeath;
use MyAAC\Models\PlayerKillers;
use MyAAC\Server\XML\Vocations;
defined('MYAAC') or die('Direct access not allowed!');
$title = 'Highscores';
@@ -35,24 +36,20 @@ if(!is_numeric($page) || $page < 1 || $page > PHP_INT_MAX) {
$query = Player::query();
$configVocations = config('vocations');
$configVocationsAmount = config('vocations_amount');
$vocationId = null;
if($vocation !== 'all') {
foreach($configVocations as $id => $name) {
if(strtolower($name) == $vocation) {
$vocationId = $id;
$add_vocs = [$id];
$filterVocations = [$id];
if ($id !== 0) {
$i = $id + $configVocationsAmount;
while (isset($configVocations[$i])) {
$add_vocs[] = $i;
$i += $configVocationsAmount;
}
while($tmpVoc = Vocations::getPromoted($id)) {
$id = $tmpVoc;
$filterVocations[] = $tmpVoc;
}
$query->whereIn('players.vocation', $add_vocs);
$query->whereIn('players.vocation', $filterVocations);
break;
}
}
@@ -325,4 +322,5 @@ $twig->display('highscores.html.twig', [
'page' => $page,
'baseLink' => $baseLink,
'updatedAt' => $updatedAt,
'baseVocations' => Vocations::getBase(true),
]);

View File

@@ -12,6 +12,7 @@
use MyAAC\Cache\Cache;
use MyAAC\Models\ServerConfig;
use MyAAC\Models\ServerRecord;
use MyAAC\Server\XML\Vocations;
defined('MYAAC') or die('Direct access not allowed!');
$title = 'Who is online?';
@@ -56,15 +57,14 @@ $cached = Cache::remember("online_$order", setting('core.online_cache_ttl') * 60
$vocations = array_map(function ($name) {
return 0;
}, setting('core.vocations'));
}, config('vocations'));
if($db->hasTable('players_online')) // tfs 1.0
$playersOnline = $db->query('SELECT `accounts`.`country`, `players`.`name`, `players`.`level`, `players`.`vocation`' . $outfit . ', `' . $skull_time . '` as `skulltime`, `' . $skull_type . '` as `skull` FROM `accounts`, `players`, `players_online` WHERE `players`.`id` = `players_online`.`player_id` AND `accounts`.`id` = `players`.`account_id` ORDER BY ' . $orderSql);
else
$playersOnline = $db->query('SELECT `accounts`.`country`, `players`.`name`, `players`.`level`, `players`.`vocation`' . $outfit . ', ' . $promotion . ' `' . $skull_time . '` as `skulltime`, `' . $skull_type . '` as `skull` FROM `accounts`, `players` WHERE `players`.`online` > 0 AND `accounts`.`id` = `players`.`account_id` ORDER BY ' . $orderSql);
$settingVocations = setting('core.vocations');
$settingVocationsAmount = setting('core.vocations_amount');
$configVocations = config('vocations');
$players = [];
foreach($playersOnline as $player) {
@@ -81,22 +81,19 @@ $cached = Cache::remember("online_$order", setting('core.online_cache_ttl') * 60
}
}
if(isset($player['promotion'])) {
if((int)$player['promotion'] > 0)
$player['vocation'] += ($player['promotion'] * $settingVocationsAmount);
}
$player['vocation'] = OTS_Toolbox::getVocationFromPromotion($player['vocation'], $player['promotion'] ?? 0);
$players[] = array(
'name' => getPlayerLink($player['name']),
'player' => $player,
'level' => $player['level'],
'vocation' => $settingVocations[$player['vocation']],
'vocation' => $configVocations[$player['vocation']],
'skull' => $skull,
'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'],
);
$vocations[($player['vocation'] > $settingVocationsAmount ? $player['vocation'] - $settingVocationsAmount : $player['vocation'])]++;
$vocations[Vocations::getOriginal($player['vocation'])]++;
}
$record = '';
@@ -142,6 +139,7 @@ $twig->display('online.html.twig', array(
'vocations' => $cached['vocations'],
'vocs' => $cached['vocations'], // deprecated, to be removed
'order' => $order,
'baseVocations' => Vocations::getBase(false),
));
// search bar

View File

@@ -219,7 +219,14 @@ return [
'cache_engine' => [
'name' => 'Cache Engine',
'type' => 'options',
'options' => ['auto' => 'Auto', 'file' => 'Files', 'apc' => 'APC', 'apcu' => 'APCu', 'disable' => 'Disable'],
'options' => [
'auto' => 'Auto',
'file' => 'Files',
'apc' => 'APC',
'apcu' => 'APCu',
'php' => 'PHP',
'disable' => 'Disable',
],
'desc' => 'Auto is most reasonable. It will detect the best cache engine',
'default' => 'auto',
'is_config' => true,
@@ -312,23 +319,6 @@ return [
},
],
],
'vocations_amount' => [
'name' => 'Vocations Amount',
'type' => 'number',
'desc' => 'How much basic vocations your server got (without promotion)',
'default' => 4,
],
'vocations' => [
'name' => 'Vocation Names',
'type' => 'textarea',
'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 ($value) {
return array_map('trim', explode(',', $value));
},
],
],
[
'type' => 'category',
'title' => 'Database',

View File

@@ -13,8 +13,8 @@ namespace MyAAC\Cache;
class APC
{
private $prefix;
private $enabled;
private string $prefix;
private bool $enabled;
public function __construct($prefix = '')
{
@@ -22,14 +22,14 @@ class APC
$this->enabled = function_exists('apc_fetch');
}
public function set($key, $var, $ttl = 0)
public function set($key, $var, $ttl = 0): void
{
$key = $this->prefix . $key;
apc_delete($key);
apc_store($key, $var, $ttl);
}
public function get($key)
public function get($key): string
{
$tmp = '';
if ($this->fetch($this->prefix . $key, $tmp)) {
@@ -39,18 +39,15 @@ class APC
return '';
}
public function fetch($key, &$var)
{
public function fetch($key, &$var): bool {
return ($var = apc_fetch($this->prefix . $key)) !== false;
}
public function delete($key)
{
public function delete($key): void {
apc_delete($this->prefix . $key);
}
public function enabled()
{
public function enabled(): bool {
return $this->enabled;
}
}

View File

@@ -13,8 +13,8 @@ namespace MyAAC\Cache;
class APCu
{
private $prefix;
private $enabled;
private string $prefix;
private bool $enabled;
public function __construct($prefix = '')
{
@@ -22,14 +22,14 @@ class APCu
$this->enabled = function_exists('apcu_fetch');
}
public function set($key, $var, $ttl = 0)
public function set($key, $var, $ttl = 0): void
{
$key = $this->prefix . $key;
apcu_delete($key);
apcu_store($key, $var, $ttl);
}
public function get($key)
public function get($key): string
{
$tmp = '';
if ($this->fetch($this->prefix . $key, $tmp)) {
@@ -39,18 +39,15 @@ class APCu
return '';
}
public function fetch($key, &$var)
{
public function fetch($key, &$var): bool {
return ($var = apcu_fetch($this->prefix . $key)) !== false;
}
public function delete($key)
{
public function delete($key): void {
apcu_delete($this->prefix . $key);
}
public function enabled()
{
public function enabled(): bool {
return $this->enabled;
}
}

View File

@@ -83,7 +83,7 @@ class Cache
/**
* @return string
*/
public static function detect()
public static function detect(): string
{
if (function_exists('apc_fetch'))
return 'apc';
@@ -98,8 +98,7 @@ class Cache
/**
* @return bool
*/
public function enabled()
{
public function enabled(): bool {
return false;
}

View File

@@ -12,18 +12,22 @@ namespace MyAAC\Cache;
class File
{
private $prefix;
private $dir;
private $enabled;
private string $prefix;
private string $dir;
private bool $enabled;
public function __construct($prefix = '', $dir = '')
{
$this->prefix = $prefix;
$this->dir = $dir;
ensureFolderExists($this->dir);
ensureIndexExists($this->dir);
$this->enabled = (file_exists($this->dir) && is_dir($this->dir) && is_writable($this->dir));
}
public function set($key, $var, $ttl = 0)
public function set($key, $var, $ttl = 0): void
{
$file = $this->_name($key);
file_put_contents($file, $var);
@@ -35,7 +39,7 @@ class File
touch($file, time() + $ttl);
}
public function get($key)
public function get($key): string
{
$tmp = '';
if ($this->fetch($key, $tmp)) {
@@ -45,7 +49,7 @@ class File
return '';
}
public function fetch($key, &$var)
public function fetch($key, &$var): bool
{
$file = $this->_name($key);
if (!file_exists($file) || filemtime($file) < time()) {
@@ -56,7 +60,7 @@ class File
return true;
}
public function delete($key)
public function delete($key): void
{
$file = $this->_name($key);
if (file_exists($file)) {
@@ -64,13 +68,11 @@ class File
}
}
public function enabled()
{
public function enabled(): bool {
return $this->enabled;
}
private function _name($key)
{
private function _name($key): string {
return sprintf('%s%s%s', $this->dir, $this->prefix, sha1($key));
}
}

View File

@@ -12,39 +12,40 @@ namespace MyAAC\Cache;
class PHP
{
private $prefix;
private $dir;
private $enabled;
private string $prefix;
private string $dir;
private bool $enabled;
public function __construct($prefix = '', $dir = '')
{
$this->prefix = $prefix;
$this->dir = $dir;
$this->enabled = (file_exists($this->dir) && is_dir($this->dir) && is_writable($this->dir));
}
public function set($key, $var, $ttl = 0)
{
$var = var_export($var, true);
ensureFolderExists($this->dir);
ensureIndexExists($this->dir);
// Write to temp file first to ensure atomicity
$tmp = $this->dir . "tmp_$key." . uniqid('', true) . '.tmp';
file_put_contents($tmp, '<?php $var = ' . $var . ';', LOCK_EX);
$this->enabled = (file_exists($this->dir) && is_dir($this->dir) && is_writable($this->dir));
}
$file = $this->_name($key);
rename($tmp, $file);
public function set($key, $var, $ttl = 0): void
{
$var = var_export($var, true);
if ($ttl === 0) {
$ttl = 365 * 24 * 60 * 60; // 365 days
}
touch($file, time() + $ttl);
$expires = time() + $ttl;
// Write to temp file first to ensure atomicity
$tmp = $this->dir . "tmp_$key." . uniqid('', true) . '.tmp';
file_put_contents($tmp, "<?php return ['expires' => $expires, 'var' => $var];", LOCK_EX);
$file = $this->_name($key);
rename($tmp, $file);
}
public function get($key)
public function get($key): string
{
$tmp = '';
if ($this->fetch($key, $tmp)) {
@@ -54,19 +55,23 @@ class PHP
return '';
}
public function fetch($key, &$var)
public function fetch($key, &$var): bool
{
$file = $this->_name($key);
if (!file_exists($file) || filemtime($file) < time()) {
if (!file_exists($file)) {
return false;
}
@include $file;
$var = isset($var) ? $var : null;
$content = include $file;
if (!isset($content) || $content['expires'] < time()) {
return false;
}
$var = $content['var'];
return true;
}
public function delete($key)
public function delete($key): void
{
$file = $this->_name($key);
if (file_exists($file)) {
@@ -74,13 +79,11 @@ class PHP
}
}
public function enabled()
{
public function enabled(): bool {
return $this->enabled;
}
private function _name($key)
{
private function _name($key): string {
return sprintf('%s%s%s', $this->dir, $this->prefix, sha1($key) . '.php');
}
}

View File

@@ -13,8 +13,8 @@ namespace MyAAC\Cache;
class XCache
{
private $prefix;
private $enabled;
private string $prefix;
private bool $enabled;
public function __construct($prefix = '')
{
@@ -22,14 +22,14 @@ class XCache
$this->enabled = function_exists('xcache_get') && ini_get('xcache.var_size');
}
public function set($key, $var, $ttl = 0)
public function set($key, $var, $ttl = 0): void
{
$key = $this->prefix . $key;
xcache_unset($key);
xcache_set($key, $var, $ttl);
}
public function get($key)
public function get($key): string
{
$tmp = '';
if ($this->fetch($this->prefix . $key, $tmp)) {
@@ -39,7 +39,7 @@ class XCache
return '';
}
public function fetch($key, &$var)
public function fetch($key, &$var): bool
{
$key = $this->prefix . $key;
if (!xcache_isset($key)) {
@@ -50,13 +50,11 @@ class XCache
return true;
}
public function delete($key)
{
public function delete($key): void {
xcache_unset($this->prefix . $key);
}
public function enabled()
{
public function enabled(): bool {
return $this->enabled;
}
}

View File

@@ -9,6 +9,6 @@ class AccountAction extends Model {
public $timestamps = false;
protected $fillable = ['account_id', 'ip', 'ipv6', 'date', 'action'];
protected $fillable = ['account_id', 'ip', 'date', 'action'];
}

View File

@@ -46,14 +46,8 @@ class Player extends Model {
});
}
public function getVocationNameAttribute()
{
$vocation = $this->vocation;
if (isset($this->promotion) && $this->promotion > 0) {
$vocation += ($this->promotion * setting('core.vocations_amount'));
}
return config('vocations')[$vocation] ?? 'Unknown';
public function getVocationNameAttribute() {
return \OTS_Toolbox::getVocationName($this->vocation, $this->promotion ?? 0);
}
public function getIsDeletedAttribute()

View File

@@ -0,0 +1,93 @@
<?php
namespace MyAAC\Server\XML;
use MyAAC\Cache\Cache;
class Vocations
{
private static array $vocations;
private static array $vocationsFrom;
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
{
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)) {
$file = config('data_path') . 'vocations.xml';
}
if(!$vocationsXML->load($file)) {
throw new \RuntimeException('ERROR: Cannot load <i>vocations.xml</i> - the file is malformed. Check the file with xml syntax validator.');
}
foreach($vocationsXML->getElementsByTagName('vocation') as $vocation) {
$id = $vocation->getAttribute('id');
self::$vocations[$id] = $vocation->getAttribute('name');
$fromVocation = (int) $vocation->getAttribute('fromvoc');
self::$vocationsFrom[$id] = $fromVocation;
}
}
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 {
return self::$vocationsFrom[$id] ?? null;
}
public static function getBase($includingRook = true): array {
$vocations = [];
foreach (self::$vocationsFrom as $vocId => $fromVoc) {
if ($vocId == $fromVoc && ($vocId != 0 || $includingRook)) {
$vocations[] = $vocId;
}
}
return $vocations;
}
}

View File

@@ -18,7 +18,7 @@
<label for="vocationFilter">Choose a vocation</label>
<select onchange="location = this.value;" id="vocationFilter">
<option value="{{ getLink('highscores') }}/{{ list|urlencode }}" class="size_xs">[ALL]</option>
{% for i in 0..config.vocations_amount %}
{% for i in baseVocations %}
<option value="{{ getLink('highscores') }}/{{ list|urlencode }}/{{ config.vocations[i]|lower|urlencode }}" class="size_xs" {% if vocationId is not null and vocationId == i %}selected{% endif %}>{{ config.vocations[i]}}</option>
{% endfor %}
</select>
@@ -120,7 +120,7 @@
<tr bgcolor="{{ config.lightborder }}">
<td>
<a href="{{ getLink('highscores') }}/{{ list|urlencode }}" class="size_xs">[ALL]</a><br/>
{% for i in 0..config.vocations_amount %}
{% for i in baseVocations %}
<a href="{{ getLink('highscores') }}/{{ list|urlencode }}/{{ config.vocations[i]|lower|urlencode }}" class="size_xs">{{ config.vocations[i]}}</a><br/>
{% endfor %}
</td>

View File

@@ -10,22 +10,19 @@
{% if setting('core.online_vocations_images') %}
<table width="200" cellspacing="1" cellpadding="0" border="0" align="center">
<tr bgcolor="{{ config.darkborder }}">
<td><img src="images/sorcerer.png" /></td>
<td><img src="images/druid.png" /></td>
<td><img src="images/paladin.png" /></td>
<td><img src="images/knight.png" /></td>
{% for vocationId in baseVocations %}
<td><img src="images/{{ config('vocations')[vocationId]|lower }}.png" width="150" height="200"/></td>
{% endfor %}
</tr>
<tr bgcolor="{{ config.vdarkborder }}">
<td class="white" style="text-align: center;"><strong>Sorcerers</strong></td>
<td class="white" style="text-align: center;"><strong>Druids</strong></td>
<td class="white" style="text-align: center;"><strong>Paladins</strong></td>
<td class="white" style="text-align: center;"><strong>Knights</strong></td>
{% for vocationId in baseVocations %}
<td class="white" style="text-align: center;"><strong>{{ config('vocations')[vocationId] }}s</strong></td>
{% endfor %}
</tr>
<tr bgcolor="{{ config.lightborder }}">
<td style="text-align: center;">{{ vocs[1] }}</td>
<td style="text-align: center;">{{ vocs[2] }}</td>
<td style="text-align: center;">{{ vocs[3] }}</td>
<td style="text-align: center;">{{ vocs[4] }}</td>
{% for vocationId in baseVocations %}
<td style="text-align: center;">{{ vocs[vocationId] }}</td>
{% endfor %}
</tr>
</table>
<div style="text-align: center;">&nbsp;</div>
@@ -35,11 +32,14 @@
<td class="white" colspan="2"><b>Vocation statistics</b></td>
</tr>
{% for i in 1..config.vocations_amount %}
<tr bgcolor="{{ getStyle(i) }}">
<td width="25%">{{ config.vocations[i] }}</td>
<td width="75%">{{ vocs[i] }}</td>
</tr>
{% set i = 0 %}
{% for vocationId in baseVocations %}
<tr bgcolor="{{ getStyle(i) }}">
<td width="25%">{{ config.vocations[vocationId] }}</td>
<td width="75%">{{ vocs[vocationId] }}</td>
</tr>
{% set i = i + 1 %}
{% endfor %}
</table>
<br/>