Compare commits

...

48 Commits

Author SHA1 Message Date
slawkens
9aaa630976 [WIP] Rewrite OTS_Player class 2025-11-13 21:52:15 +01:00
slawkens
9a99018dce Merge branch 'main' into develop 2025-11-13 20:08:38 +01:00
slawkens
e440c0d6a6 Update .gitignore 2025-11-13 19:14:59 +01:00
slawkens
780d4ccef7 Server Status: Write to status-error.log if there is connection error 2025-11-06 22:06:05 +01:00
slawkens
0a6d44bf21 Fix $status['uptimeReadable'], was totally wrong 2025-11-06 13:47:09 +01:00
slawkens
4d17001a0b Add some popular network images (Facebook, Instagram, WhatsApp) 2025-11-06 12:48:33 +01:00
slawkens
946364f59d New Setting: Account Countries Most Popular 2025-11-06 12:01:33 +01:00
slawkens
5861efdbe9 Settings: escapeHtml in values (support for html code) 2025-11-06 11:48:42 +01:00
slawkens
175e97828b Don't display hidden news for admin - it's confusing 2025-11-05 22:21:42 +01:00
slawkens
9ce55db44c Display error message after failed settings save - just in case 2025-11-05 22:02:11 +01:00
slawkens
88532b0ebb Better message than "Access denied" 2025-11-05 19:32:31 +01:00
slawkens
1c7af30997 Revert "Just testing something, excuse me"
This reverts commit 7ca05e47ff.
2025-11-04 22:17:00 +01:00
slawkens
7ca05e47ff Just testing something, excuse me 2025-11-04 22:01:55 +01:00
slawkens
baec6c9ebf plugin:activate/deactivate alias 2025-11-04 08:17:41 +01:00
slawkens
6367054487 Add plugin:remove + plugin:delete as alias for plugin:uninstall 2025-11-03 21:06:27 +01:00
slawkens
ae7a47464f Update menus.php 2025-11-03 20:37:14 +01:00
slawkens
d201e75b11 Revert "Try to fix "VirtualProtect() failed [87] The parameter is incorrect" in php logs"
This reverts commit 4924696943.
2025-11-02 13:38:15 +01:00
slawkens
4924696943 Try to fix "VirtualProtect() failed [87] The parameter is incorrect" in php logs 2025-11-02 13:23:30 +01:00
slawkens
25a3db68e6 Use $db->hasTableAndColumns + move $skulls to correct place 2025-11-02 13:10:09 +01:00
slawkens
730a0f2912 Ensure some cache folders & index.html exists 2025-11-02 12:21:29 +01:00
slawkens
fd729242ff Fix typo -> satisfied 2025-11-02 12:05:47 +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
34 changed files with 336 additions and 342 deletions

1
.gitignore vendored
View File

@@ -24,6 +24,7 @@ releases
tmp tmp
config.local.php config.local.php
config2.local.php
# all custom templates # all custom templates
templates/* templates/*

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

View File

@@ -249,10 +249,10 @@ else if (isset($_REQUEST['search'])) {
$player->setLookHead($look_head); $player->setLookHead($look_head);
$player->setLookLegs($look_legs); $player->setLookLegs($look_legs);
$player->setLookType($look_type); $player->setLookType($look_type);
if ($hasLookAddons) if ($hasLookAddons) {
$player->setLookAddons($look_addons); $player->setLookAddons($look_addons);
if ($db->hasColumn('players', 'offlinetraining_time')) }
$player->setCustomField('offlinetraining_time', $offlinetraining);
$player->setPosX($pos_x); $player->setPosX($pos_x);
$player->setPosY($pos_y); $player->setPosY($pos_y);
$player->setPosZ($pos_z); $player->setPosZ($pos_z);
@@ -275,23 +275,11 @@ else if (isset($_REQUEST['search'])) {
if ($hasBlessingsColumn) if ($hasBlessingsColumn)
$player->setBlessings($blessings); $player->setBlessings($blessings);
if ($hasBlessingColumn) {
for ($i = 1; $i <= $bless_count; $i++) {
$a = 'blessing' . $i;
$player->setCustomField('blessings' . $i, ${'blessing' . $i} ? '1' : '0');
}
}
$player->setBalance($balance); $player->setBalance($balance);
if ($db->hasColumn('players', 'stamina')) if ($db->hasColumn('players', 'stamina'))
$player->setStamina($stamina); $player->setStamina($stamina);
if ($db->hasColumn('players', 'deletion'))
$player->setCustomField('deletion', $deleted ? '1' : '0'); $player->setDeleted($deleted ? '1' : '0');
else
$player->setCustomField('deleted', $deleted ? '1' : '0');
$player->setCustomField('hide', $hide ? '1' : '0');
$player->setCustomField('created', $created);
if (isset($comment))
$player->setCustomField('comment', $comment);
foreach ($_POST['skills'] as $skill => $value) { foreach ($_POST['skills'] as $skill => $value) {
$player->setSkill($skill, $value); $player->setSkill($skill, $value);
@@ -300,6 +288,24 @@ else if (isset($_REQUEST['search'])) {
$player->setSkillTries($skill, $value); $player->setSkillTries($skill, $value);
} }
$player->save(); $player->save();
if ($db->hasColumn('players', 'offlinetraining_time')) {
$player->setCustomField('offlinetraining_time', $offlinetraining);
}
if ($hasBlessingColumn) {
for ($i = 1; $i <= $bless_count; $i++) {
$a = 'blessing' . $i;
$player->setCustomField('blessings' . $i, ${'blessing' . $i} ? '1' : '0');
}
}
$player->setCustomField('hide', $hide ? '1' : '0');
$player->setCustomField('created', $created);
if (isset($comment)) {
$player->setCustomField('comment', $comment);
}
echo_success('Player saved at: ' . date('G:i')); echo_success('Player saved at: ' . date('G:i'));
$player->load($id); $player->load($id);
} }

View File

@@ -60,7 +60,7 @@ usort($menus, function ($a, $b) {
foreach ($menus as $i => $menu) { foreach ($menus as $i => $menu) {
if (isset($menu['link']) && is_array($menu['link'])) { if (isset($menu['link']) && is_array($menu['link'])) {
usort($menus[$i]['link'], function ($a, $b) { usort($menu['link'], function ($a, $b) {
return $a['order'] - $b['order']; return $a['order'] - $b['order'];
}); });
} }

View File

@@ -12,7 +12,7 @@ require SYSTEM . 'login.php';
if(!admin()) { if(!admin()) {
http_response_code(500); http_response_code(500);
die('Access denied.'); die('You are not logged in. Probably session expired. Please login again.');
} }
csrfProtect(); csrfProtect();
@@ -40,3 +40,6 @@ if (count($errors) > 0) {
if ($success) { if ($success) {
echo 'Saved at ' . date('H:i'); echo 'Saved at ' . date('H:i');
} }
else {
echo 'Something unexpected happened - it was impossible to save the settings, please try again later. If problem persists - contact MyAAC developers.';
}

View File

@@ -26,8 +26,8 @@
if (version_compare(phpversion(), '8.1', '<')) die('PHP version 8.1 or higher is required.'); if (version_compare(phpversion(), '8.1', '<')) die('PHP version 8.1 or higher is required.');
const MYAAC = true; const MYAAC = true;
const MYAAC_VERSION = '1.8.5-dev'; const MYAAC_VERSION = '2.0-dev';
const DATABASE_VERSION = 46; const DATABASE_VERSION = 47;
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'));

BIN
images/facebook_16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 B

BIN
images/instagram_16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

BIN
images/whatsapp_16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

View File

@@ -1,11 +1,11 @@
CREATE TABLE IF NOT EXISTS `myaac_account_actions` CREATE TABLE IF NOT EXISTS `myaac_account_actions`
( (
`id` int NOT NULL AUTO_INCREMENT,
`account_id` int NOT NULL, `account_id` int NOT NULL,
`ip` int unsigned NOT NULL DEFAULT 0, `ip` varchar(45) NOT NULL DEFAULT '',
`ipv6` binary(16) NOT NULL DEFAULT 0,
`date` int NOT NULL DEFAULT 0, `date` int NOT NULL DEFAULT 0,
`action` varchar(255) NOT NULL DEFAULT '', `action` varchar(255) NOT NULL DEFAULT '',
KEY (`account_id`) 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`

View File

@@ -37,8 +37,46 @@ if ($db->hasTable('players')) {
$query = $db->query('SELECT `id` FROM `players` WHERE `name` = ' . $db->quote($p['name'])); $query = $db->query('SELECT `id` FROM `players` WHERE `name` = ' . $db->quote($p['name']));
if ($query->rowCount() == 0) { if ($query->rowCount() == 0) {
if (!query("INSERT INTO `players` (`id`, `name`, `group_id`, `account_id`, `level`, `vocation`, `health`, `healthmax`, `experience`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `maglevel`, `mana`, `manamax`, `manaspent`, `soul`, `town_id`, `posx`, `posy`, `posz`, `conditions`, `cap`, `sex`, `lastlogin`, `lastip`, `save`, `lastlogout`, `balance`, `$deleted`, `created`, `hide`, `comment`) VALUES (null, " . $db->quote($p['name']) . ", 1, " . getSession('account') . ", " . $p['level'] . ", " . $p['vocation_id'] . ", " . $p['health'] . ", " . $p['healthmax'] . ", " . $p['experience'] . ", 118, 114, 38, 57, " . $p['looktype'] . ", 0, " . $p['mana'] . ", " . $p['manamax'] . ", 0, " . $p['soul'] . ", 1, 1000, 1000, 7, '', " . $p['cap'] . ", 1, " . $time . ", 2130706433, 1, " . $time . ", 0, 0, " . $time . ", 1, '');")) $player = new OTS_Player();
$success = false;
$player->setData([
'name' => $p['name'],
'group_id' => 1,
'account_id' => getSession('account'),
'level' => $p['level'],
'vocation' => $p['vocation_id'],
'health' => $p['health'],
'healthmax' => $p['healthmax'],
'experience' => $p['experience'],
'lookbody' => 118,
'lookfeet' => 114,
'lookhead' => 38,
'looklegs' => 57,
'looktype' => $p['looktype'],
'maglevel' => 0,
'mana' => $p['mana'],
'manamax' => $p['manamax'],
'manaspent' => 0,
'soul' => $p['soul'],
'town_id' => 1,
'posx' => 1000,
'posy' => 1000,
'posz' => 7,
'conditions' => '',
'cap' => $p['cap'],
'sex' => 1,
'lastlogin' => $time,
'lastip' => 2130706433,
'save' => 1,
'lastlogout' => $time,
'balance' => 0,
$deleted => 0,
'created' => $time,
'hide' => 1,
'comment' => '',
]);
$player->save();
} }
} }

View File

@@ -1177,7 +1177,8 @@ function getTopPlayers($limit = 5, $skill = 'level') {
}); });
} }
function deleteDirectory($dir, $ignore = array(), $contentOnly = false) { function deleteDirectory($dir, $ignore = array(), $contentOnly = false): bool
{
if(!file_exists($dir)) { if(!file_exists($dir)) {
return true; return true;
} }
@@ -1203,6 +1204,21 @@ function deleteDirectory($dir, $ignore = array(), $contentOnly = false) {
return rmdir($dir); return rmdir($dir);
} }
function ensureFolderExists($dir): void
{
if (!file_exists($dir)) {
mkdir($dir, 0777, true);
}
}
function ensureIndexExists($dir): void
{
$dir = rtrim($dir, '/');
if (!file_exists($file = $dir . '/index.html')) {
touch($file);
}
}
function config($key) { function config($key) {
global $config; global $config;
if (is_array($key)) { if (is_array($key)) {
@@ -1712,6 +1728,43 @@ function isCanary(): bool
return isset($vipSystemEnabled); return isset($vipSystemEnabled);
} }
function getStatusUptimeReadable(int $uptime): string
{
$fullMinute = 60;
$fullHour = (60 * $fullMinute);
$fullDay = (24 * $fullHour);
$fullMonth = (30 * $fullDay);
$fullYear = (365 * $fullDay);
// years
$years = floor($uptime / $fullYear);
$y = ($years > 1 ? "$years years, " : ($years == 1 ? 'year, ' : ''));
$uptime -= $years * $fullYear;
// months
$months = floor($uptime / $fullMonth);
$m = ($months > 1 ? "$months months, " : ($months == 1 ? 'month, ' : ''));
$uptime -= $months * $fullMonth;
// days
$days = floor($uptime / $fullDay);
$d = ($days > 1 ? "$days days, " : ($days == 1 ? 'day, ' : ''));
$uptime -= $days * $fullDay;
// hours
$hours = floor($uptime / $fullHour);
$uptime -= $hours * $fullHour;
// minutes
$min = floor($uptime / $fullMinute);
return "{$y}{$m}{$d}{$hours}h {$min}m";
}
// validator functions // validator functions
require_once SYSTEM . 'compat/base.php'; require_once SYSTEM . 'compat/base.php';

View File

@@ -18,6 +18,9 @@ use MyAAC\Settings;
defined('MYAAC') or die('Direct access not allowed!'); defined('MYAAC') or die('Direct access not allowed!');
ensureIndexExists(CACHE);
ensureIndexExists(CACHE . 'twig/');
global $config; global $config;
if(!isset($config['installed']) || !$config['installed']) { if(!isset($config['installed']) || !$config['installed']) {
throw new RuntimeException('MyAAC has not been installed yet or there was error during installation. Please install again.'); throw new RuntimeException('MyAAC has not been installed yet or there was error during installation. Please install again.');

View File

@@ -12,6 +12,8 @@
* @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\Models\AccountAction;
/** /**
* OTServ account abstraction. * OTServ account abstraction.
* *
@@ -1007,26 +1009,16 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
public function logAction($action) public function logAction($action)
{ {
$ip = get_browser_real_ip(); AccountAction::create([
if(!str_contains($ip, ":")) { 'account_id' => $this->getId(),
$ipv6 = '0'; 'ip' => get_browser_real_ip(),
} 'date' => time(),
else { 'action' => $action,
$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).')');
} }
public function getActionsLog($limit1, $limit2) public function getActionsLog($limit) {
{ return AccountAction::where('account_id', $this->data['id'])->orderByDesc('date')->limit($limit)->get()->toArray();
$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;
} }
/** /**
* Returns players iterator. * Returns players iterator.

View File

@@ -277,6 +277,7 @@ class OTS_DB_MySQL extends OTS_Base_DB
'field' => $result['Field'], 'field' => $result['Field'],
'type' => $result['Type'], 'type' => $result['Type'],
'null' => strtolower($result['Null']), 'null' => strtolower($result['Null']),
'key' => strtolower($result['Key'] ?? ''),
'default' => $result['Default'], 'default' => $result['Default'],
'extra' => $result['Extra'], 'extra' => $result['Extra'],
]; ];

View File

@@ -1,20 +1,6 @@
<?php <?php
$__load = array();
/* use MyAAC\Models\Player as PlayerModel;
'loss_experience' => NULL,
'loss_items' => NULL,
'guild_info' => NULL,
'skull_type' => NULL,
'skull_time' => NULL,
'blessings' => NULL,
'direction' => NULL,
'stamina' => NULL,
'world_id' => NULL,
'online' => NULL,
'deletion' => NULL,
'promotion' => NULL,
'marriage' => NULL
);*/
/**#@+ /**#@+
* @version 0.0.1 * @version 0.0.1
@@ -109,6 +95,10 @@ class OTS_Player extends OTS_Row_DAO
POT::SKILL_FISH => array('value' => 0, 'tries' => 0) POT::SKILL_FISH => array('value' => 0, 'tries' => 0)
); );
private array $columns = ['name', 'account_id', 'group_id', 'sex', 'vocation', 'experience', 'level', 'maglevel', 'health', 'healthmax', 'mana', 'manamax', 'manaspent', 'soul', 'lookbody', 'lookfeet', 'lookhead', 'looklegs', 'looktype', 'posx', 'posy', 'posz', 'lastlogin', 'lastlogout', 'lastip', 'town_id', 'balance', 'created', 'comment', 'hide'];
private array $optionalColumns = ['cap', 'skull', 'skull_type', 'skull_time', 'loss_experience', 'loss_mana', 'loss_skills', 'loss_items', 'loss_containers', 'guildnick', 'rank_id', 'promotion', 'direction', 'blessings', 'stamina', 'lookaddons', 'save', 'conditions', 'world_id', 'online', 'deletion', 'deleted', 'marriage'];
private static array $playersOnline; private static array $playersOnline;
/** /**
* Magic PHP5 method. * Magic PHP5 method.
@@ -133,90 +123,14 @@ class OTS_Player extends OTS_Row_DAO
*/ */
public function load($id, $fields = null, $load_skills = true) public function load($id, $fields = null, $load_skills = true)
{ {
global $__load; $columns = $this->columns;
foreach ($this->optionalColumns as $column) {
if(!isset($__load['loss_experience'])) if ($this->db->hasColumn('players', $column)) {
{ $columns[] = $column;
$loss = '';
if($this->db->hasColumn('players', 'loss_experience')) {
$loss = ', `loss_experience`, `loss_mana`, `loss_skills`';
} }
$__load['loss_experience'] = $loss;
}
if(!isset($__load['loss_items']))
{
$loss_items = '';
if($this->db->hasColumn('players', 'loss_items')) {
$loss_items = ', `loss_items`, `loss_containers`';
}
$__load['loss_items'] = $loss_items;
}
if(!isset($__load['guild_info']))
{
$guild_info = '';
if(!$this->db->hasTable('guild_members') && $this->db->hasColumn('players', 'guildnick')) {
$guild_info = ', `guildnick`, `rank_id`';
}
$__load['guild_info'] = $guild_info;
}
if(!isset($__load['skull_type']))
{
$skull_type = 'skull';
if($this->db->hasColumn('players', 'skull_type')) {
$skull_type = 'skull_type';
}
$__load['skull_type'] = $skull_type;
}
if(!isset($__load['skull_time']))
{
$skull_time = 'skulltime';
if($this->db->hasColumn('players', 'skull_time')) {
$skull_time = 'skull_time';
}
$__load['skull_time'] = $skull_time;
}
if(!isset($__load['blessings'])) {
$__load['blessings'] = $this->db->hasColumn('players', 'blessings');
}
if(!isset($__load['direction'])) {
$__load['direction'] = $this->db->hasColumn('players', 'direction');
}
if(!isset($__load['stamina'])) {
$__load['stamina'] = $this->db->hasColumn('players', 'stamina');
}
if(!isset($__load['world_id'])) {
$__load['world_id'] = $this->db->hasColumn('players', 'world_id');
}
if(!isset($__load['online'])) {
$__load['online'] = $this->db->hasColumn('players', 'online');
}
if(!isset($__load['deletion'])) {
$__load['deletion'] = $this->db->hasColumn('players', 'deletion');
}
if(!isset($__load['promotion'])) {
$__load['promotion'] = $this->db->hasColumn('players', 'promotion');
}
if(!isset($__load['marriage'])) {
$__load['marriage'] = $this->db->hasColumn('players', 'marriage');
} }
if(isset($fields)) { // load only what we wish if(isset($fields)) { // load only what we wish
if(in_array('promotion', $fields)) {
if(!$this->db->hasColumn('players', 'promotion')) {
unset($fields[array_search('promotion', $fields)]);
}
}
if(in_array('deleted', $fields)) { if(in_array('deleted', $fields)) {
if($this->db->hasColumn('players', 'deletion')) { if($this->db->hasColumn('players', 'deletion')) {
unset($fields[array_search('deleted', $fields)]); unset($fields[array_search('deleted', $fields)]);
@@ -224,21 +138,20 @@ class OTS_Player extends OTS_Row_DAO
} }
} }
if(in_array('online', $fields)) { $columns = [];
if(!$this->db->hasColumn('players', 'online')) { foreach ($fields as $field) {
unset($fields[array_search('online', $fields)]); if ($this->db->hasColumn('players', $field)) {
$columns[] = $field;
} }
} }
$this->data = $this->db->query('SELECT ' . implode(', ', $fields) . ' FROM `players` WHERE `id` = ' . (int)$id)->fetch();
}
else {
// SELECT query on database
$this->data = $this->db->query('SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`' . ($this->db->hasColumn('players', 'lookaddons') ? ', `lookaddons`' : '') . ', `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `save`, `conditions`, `' . $__load['skull_time'] . '` as `skulltime`, `' . $__load['skull_type'] . '` as `skull`' . $__load['guild_info'] . ', `town_id`' . $__load['loss_experience'] . $__load['loss_items'] . ', `balance`' . ($__load['blessings'] ? ', `blessings`' : '') . ($__load['direction'] ? ', `direction`' : '') . ($__load['stamina'] ? ', `stamina`' : '') . ($__load['world_id'] ? ', `world_id`' : '') . ($__load['online'] ? ', `online`' : '') . ', `' . ($__load['deletion'] ? 'deletion' : 'deleted') . '`' . ($__load['promotion'] ? ', `promotion`' : '') . ($__load['marriage'] ? ', `marriage`' : '') . ', `comment`, `created`, `hide` FROM `players` WHERE `id` = ' . (int)$id)->fetch();
} }
array_unshift($columns, 'id');
$this->data = PlayerModel::where('id', $id)->first($columns)->toArray();
// loads skills // loads skills
if( $this->isLoaded() && $load_skills) if( $this->isLoaded() && $load_skills) {
{
if($this->db->hasColumn('players', 'skill_fist')) { if($this->db->hasColumn('players', 'skill_fist')) {
$skill_ids = array( $skill_ids = array(
@@ -318,153 +231,57 @@ class OTS_Player extends OTS_Row_DAO
*/ */
public function save() public function save()
{ {
$skull_type = 'skull'; $defaultValues = [
if($this->db->hasColumn('players', 'skull_type')) { 'loss_experience' => 100,
$skull_type = 'skull_type'; 'loss_mana' => 100,
'loss_skills' => 100,
'loss_items' => 100,
'loss_containers' => 100,
'guildnick' => '',
'rank_id' => 0,
'promotion' => 0,
'direction' => 0,
'blessings' => 0,
'conditions' => '',
'town_id' => 1,
'deletion' => 0,
'deleted' => 0,
'stamina' => 0,
'marriage' => 0,
];
foreach ($defaultValues as $key => $value) {
if (!isset($this->data[$key])) {
$this->data[$key] = $value;
}
} }
$skull_time = 'skulltime'; $columns = $this->columns;
if($this->db->hasColumn('players', 'skull_time')) { foreach ($this->optionalColumns as $column) {
$skull_time = 'skull_time'; if ($this->db->hasColumn('players', $column)) {
$columns[] = $column;
}
} }
if(!isset($this->data['loss_experience'])) $values = [];
$this->data['loss_experience'] = 100; foreach ($columns as $column) {
$value = $this->data[$column];
if ($column == 'created') {
$value = time();
}
if(!isset($this->data['loss_mana'])) $values[$column] = $value;
$this->data['loss_mana'] = 100; }
if(!isset($this->data['loss_skills']))
$this->data['loss_skills'] = 100;
if(!isset($this->data['loss_items']))
$this->data['loss_items'] = 10;
if(!isset($this->data['loss_containers']))
$this->data['loss_containers'] = 100;
if(!isset($this->data['guildnick']))
$this->data['guildnick'] = '';
if(!isset($this->data['rank_id']))
$this->data['rank_id'] = 0;
if(!isset($this->data['promotion']))
$this->data['promotion'] = 0;
if(!isset($this->data['direction']))
$this->data['direction'] = 0;
if(!isset($this->data['conditions']))
$this->data['conditions'] = '';
if(!isset($this->data['town_id']))
$this->data['town_id'] = 1;
// updates existing player // updates existing player
if( isset($this->data['id']) ) if( isset($this->data['id']) ) {
{ PlayerModel::where('id', $this->data['id'])->update($values);
$loss = '';
if($this->db->hasColumn('players', 'loss_experience')) {
$loss = ', `loss_experience` = ' . $this->data['loss_experience'] . ', `loss_mana` = ' . $this->data['loss_mana'] . ', `loss_skills` = ' . $this->data['loss_skills'];
}
$loss_items = '';
if($this->db->hasColumn('players', 'loss_items')) {
$loss_items = ', `loss_items` = ' . $this->data['loss_items'] . ', `loss_containers` = ' . $this->data['loss_containers'];
}
$guild_info = '';
if(!$this->db->hasTable('guild_members') && $this->db->hasColumn('players', 'guildnick')) {
$guild_info = ', `guildnick` = ' . $this->db->quote($this->data['guildnick']) . ', ' . $this->db->fieldName('rank_id') . ' = ' . $this->data['rank_id'];
}
$direction = '';
if($this->db->hasColumn('players', 'direction')) {
$direction = ', `direction` = ' . $this->db->quote($this->data['direction']);
}
$blessings = '';
if($this->db->hasColumn('players', 'blessings')) {
$blessings = ', `blessings` = ' . $this->db->quote($this->data['blessings']);
}
$stamina = '';
if($this->db->hasColumn('players', 'stamina')) {
$stamina = ', `stamina` = ' . $this->db->quote($this->data['stamina']);
}
$lookaddons = '';
if($this->db->hasColumn('players', 'lookaddons')) {
$lookaddons = ', `lookaddons` = ' . $this->db->quote($this->data['lookaddons']);
}
// UPDATE query on database
$this->db->query('UPDATE ' . $this->db->tableName('players') . ' SET ' . $this->db->fieldName('name') . ' = ' . $this->db->quote($this->data['name']) . ', ' . $this->db->fieldName('account_id') . ' = ' . $this->data['account_id'] . ', ' . $this->db->fieldName('group_id') . ' = ' . $this->data['group_id'] . ', ' . $this->db->fieldName('sex') . ' = ' . $this->data['sex'] . ', ' . $this->db->fieldName('vocation') . ' = ' . $this->data['vocation'] . ', ' . $this->db->fieldName('experience') . ' = ' . $this->data['experience'] . ', ' . $this->db->fieldName('level') . ' = ' . $this->data['level'] . ', ' . $this->db->fieldName('maglevel') . ' = ' . $this->data['maglevel'] . ', ' . $this->db->fieldName('health') . ' = ' . $this->data['health'] . ', ' . $this->db->fieldName('healthmax') . ' = ' . $this->data['healthmax'] . ', ' . $this->db->fieldName('mana') . ' = ' . $this->data['mana'] . ', ' . $this->db->fieldName('manamax') . ' = ' . $this->data['manamax'] . ', ' . $this->db->fieldName('manaspent') . ' = ' . $this->data['manaspent'] . ', ' . $this->db->fieldName('soul') . ' = ' . $this->data['soul'] . ', ' . $this->db->fieldName('lookbody') . ' = ' . $this->data['lookbody'] . ', ' . $this->db->fieldName('lookfeet') . ' = ' . $this->data['lookfeet'] . ', ' . $this->db->fieldName('lookhead') . ' = ' . $this->data['lookhead'] . ', ' . $this->db->fieldName('looklegs') . ' = ' . $this->data['looklegs'] . ', ' . $this->db->fieldName('looktype') . ' = ' . $this->data['looktype'] . $lookaddons . ', ' . $this->db->fieldName('posx') . ' = ' . $this->data['posx'] . ', ' . $this->db->fieldName('posy') . ' = ' . $this->data['posy'] . ', ' . $this->db->fieldName('posz') . ' = ' . $this->data['posz'] . ', ' . $this->db->fieldName('cap') . ' = ' . $this->data['cap'] . ', ' . $this->db->fieldName('lastlogin') . ' = ' . $this->data['lastlogin'] . ', ' . $this->db->fieldName('lastlogout') . ' = ' . $this->data['lastlogout'] . ', ' . $this->db->fieldName('lastip') . ' = ' . $this->db->quote($this->data['lastip']) . ', ' . $this->db->fieldName('save') . ' = ' . (int) $this->data['save'] . ', ' . $this->db->fieldName('conditions') . ' = ' . $this->db->quote($this->data['conditions']) . ', `' . $skull_time . '` = ' . $this->data['skulltime'] . ', `' . $skull_type . '` = ' . (int) $this->data['skull'] . $guild_info . ', ' . $this->db->fieldName('town_id') . ' = ' . $this->data['town_id'] . $loss . $loss_items . ', ' . $this->db->fieldName('balance') . ' = ' . $this->data['balance'] . $blessings . $stamina . $direction . ' WHERE ' . $this->db->fieldName('id') . ' = ' . $this->data['id']);
} }
// creates new player // creates new player
else else {
{ $player = PlayerModel::create($values);
$loss = '';
$loss_data = '';
if($this->db->hasColumn('players', 'loss_experience')) {
$loss = ', `loss_experience`, `loss_mana`, `loss_skills`';
$loss_data = ', ' . $this->data['loss_experience'] . ', ' . $this->data['loss_mana'] . ', ' . $this->data['loss_skills'];
}
$loss_items = '';
$loss_items_data = '';
if($this->db->hasColumn('players', 'loss_items')) {
$loss_items = ', `loss_items`, `loss_containers`';
$loss_items_data = ', ' . $this->data['loss_items'] . ', ' . $this->data['loss_containers'];
}
$guild_info = '';
$guild_info_data = '';
if(!$this->db->hasTable('guild_members') && $this->db->hasColumn('players', 'guildnick')) {
$guild_info = ', `guildnick`, `rank_id`';
$guild_info_data = ', ' . $this->db->quote($this->data['guildnick']) . ', ' . $this->data['rank_id'];
}
$promotion = '';
$promotion_data = '';
if($this->db->hasColumn('players', 'promotion')) {
$promotion = ', `promotion`';
$promotion_data = ', ' . $this->data['promotion'];
}
$direction = '';
$direction_data = '';
if($this->db->hasColumn('players', 'direction')) {
$direction = ', `direction`';
$direction_data = ', ' . $this->data['direction'];
}
$blessings = '';
$blessings_data = '';
if($this->db->hasColumn('players', 'blessings')) {
$blessings = ', `blessings`';
$blessings_data = ', ' . $this->data['blessings'];
}
$stamina = '';
$stamina_data = '';
if($this->db->hasColumn('players', 'stamina')) {
$stamina = ', `stamina`';
$stamina_data = ', ' . $this->data['stamina'];
}
$lookaddons = '';
$lookaddons_data = '';
if($this->db->hasColumn('players', 'lookaddons')) {
$lookaddons = ', `lookaddons`';
$lookaddons_data = ', ' . $this->data['lookaddons'];
}
// INSERT query on database
$this->db->query('INSERT INTO `players` (`name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`' . $lookaddons . ', `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `save`, `conditions`, `' . $skull_time . '`, `' . $skull_type . '`' . $guild_info . ', `town_id`' . $loss . $loss_items . ', `balance`' . $blessings . $stamina . $direction . ', `created`' . $promotion . ', `comment`) VALUES (' . $this->db->quote($this->data['name']) . ', ' . $this->data['account_id'] . ', ' . $this->data['group_id'] . ', ' . $this->data['sex'] . ', ' . $this->data['vocation'] . ', ' . $this->data['experience'] . ', ' . $this->data['level'] . ', ' . $this->data['maglevel'] . ', ' . $this->data['health'] . ', ' . $this->data['healthmax'] . ', ' . $this->data['mana'] . ', ' . $this->data['manamax'] . ', ' . $this->data['manaspent'] . ', ' . $this->data['soul'] . ', ' . $this->data['lookbody'] . ', ' . $this->data['lookfeet'] . ', ' . $this->data['lookhead'] . ', ' . $this->data['looklegs'] . ', ' . $this->data['looktype'] . $lookaddons_data . ', ' . $this->data['posx'] . ', ' . $this->data['posy'] . ', ' . $this->data['posz'] . ', ' . $this->data['cap'] . ', ' . $this->data['lastlogin'] . ', ' . $this->data['lastlogout'] . ', ' . $this->data['lastip'] . ', ' . (int) $this->data['save'] . ', ' . $this->db->quote($this->data['conditions']) . ', ' . $this->data['skulltime'] . ', ' . (int) $this->data['skull'] . $guild_info_data . ', ' . $this->data['town_id'] . $loss_data . $loss_items_data . ', ' . $this->data['balance'] . $blessings_data . $stamina_data . $direction_data . ', ' . time() . $promotion_data . ', "")');
// ID of new group // ID of new group
$this->data['id'] = $this->db->lastInsertId(); $this->data['id'] = $player->id;
} }
// updates skills - doesn't matter if we have just created character - trigger inserts new skills // updates skills - doesn't matter if we have just created character - trigger inserts new skills
@@ -490,7 +307,7 @@ class OTS_Player extends OTS_Row_DAO
$set .= ','; $set .= ',';
} }
$skills = $this->db->query('UPDATE `players` SET ' . $set . ' WHERE `id` = ' . $this->data['id']); $this->db->query('UPDATE `players` SET ' . $set . ' WHERE `id` = ' . $this->data['id']);
} }
else if($this->db->hasTable('player_skills')) { else if($this->db->hasTable('player_skills')) {
foreach($this->skills as $id => $skill) foreach($this->skills as $id => $skill)
@@ -748,21 +565,25 @@ class OTS_Player extends OTS_Row_DAO
public function isDeleted() public function isDeleted()
{ {
$field = 'deleted'; $column = 'deleted';
if($this->db->hasColumn('players', 'deletion')) if($this->db->hasColumn('players', 'deletion'))
$field = 'deletion'; $column = 'deletion';
if( !isset($this->data[$field]) ) if( !isset($this->data[$column]) )
{ {
throw new E_OTS_NotLoaded(); throw new E_OTS_NotLoaded();
} }
return $this->data[$field] > 0; return $this->data[$column] > 0;
} }
public function setDeleted($deleted) public function setDeleted($deleted)
{ {
$this->data['deleted'] = (int) $deleted; $column = 'deleted';
if($this->db->hasColumn('players', 'deletion'))
$column = 'deletion';
$this->data[$column] = (int) $deleted;
} }
public function isOnline() public function isOnline()
@@ -1792,12 +1613,12 @@ class OTS_Player extends OTS_Row_DAO
*/ */
public function getSkullTime() public function getSkullTime()
{ {
if( !isset($this->data['skulltime']) ) $column = 'skulltime';
{ if($this->db->hasColumn('players', 'skull_time')) {
throw new E_OTS_NotLoaded(); $column = 'skull_time';
} }
return $this->data['skulltime']; return $this->data[$column] ?? 0;
} }
/** /**
@@ -1811,7 +1632,12 @@ class OTS_Player extends OTS_Row_DAO
*/ */
public function setSkullTime($skulltime) public function setSkullTime($skulltime)
{ {
$this->data['skulltime'] = (int) $skulltime; $column = 'skulltime';
if($this->db->hasColumn('players', 'skull_time')) {
$column = 'skull_time';
}
$this->data[$column] = (int) $skulltime;
} }
/** /**
@@ -3250,6 +3076,10 @@ class OTS_Player extends OTS_Row_DAO
return 0; return 0;
} }
public function setData(array $data): void{
$this->data = $data;
}
/** /**
* Magic PHP5 method. * Magic PHP5 method.
* *

View File

@@ -97,6 +97,8 @@ class OTS_ServerInfo
return new OTS_Buffer($data); return new OTS_Buffer($data);
} }
log_append('status-error.log', "Cannot connect to {$this->server}:{$this->port} - Error code: $error, message: $message");
return false; return false;
} }

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

@@ -367,7 +367,7 @@ if(!empty($errors))
if (setting('core.account_country')) { if (setting('core.account_country')) {
$countries = array(); $countries = array();
foreach (array('pl', 'se', 'br', 'us', 'gb') as $c) foreach (setting('core.account_countries_most_popular') ?? [] as $c)
$countries[$c] = $config['countries'][$c]; $countries[$c] = $config['countries'][$c];
$countries['--'] = '----------'; $countries['--'] = '----------';

View File

@@ -96,12 +96,8 @@ if($email_new_time > 1)
} }
} }
$actions = array(); $actions = $account_logged->getActionsLog(1000);
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']));
}
$players = array();
/** @var OTS_Players_List $account_players */ /** @var OTS_Players_List $account_players */
$account_players = $account_logged->getPlayersList(); $account_players = $account_logged->getPlayersList();
$account_players->orderBy('id'); $account_players->orderBy('id');

View File

@@ -202,36 +202,38 @@ if($player->isLoaded() && !$player->isDeleted())
unset($storage); unset($storage);
} }
if($db->hasTable('player_items') && $db->hasColumn('player_items', 'pid') && $db->hasColumn('player_items', 'sid') && $db->hasColumn('player_items', 'itemtype')) { if ($db->hasTableAndColumns('player_items', ['pid', 'sid', 'itemtype'])) {
$eq_sql = $db->query('SELECT `pid`, `itemtype` FROM player_items WHERE player_id = '.$player->getId().' AND (`pid` >= 1 and `pid` <= 10)'); $eq_sql = $db->query('SELECT `pid`, `itemtype` FROM player_items WHERE player_id = '.$player->getId().' AND (`pid` >= 1 and `pid` <= 10)');
$equipment = array(); $equipment = [];
foreach($eq_sql as $eq) foreach($eq_sql as $eq) {
$equipment[$eq['pid']] = $eq['itemtype']; $equipment[$eq['pid']] = $eq['itemtype'];
}
$empty_slots = array("", "no_helmet", "no_necklace", "no_backpack", "no_armor", "no_handleft", "no_handright", "no_legs", "no_boots", "no_ring", "no_ammo"); $empty_slots = ["", "no_helmet", "no_necklace", "no_backpack", "no_armor", "no_handleft", "no_handright", "no_legs", "no_boots", "no_ring", "no_ammo"];
for($i = 0; $i <= 10; $i++)
{ for($i = 0; $i <= 10; $i++) {
if(!isset($equipment[$i]) || $equipment[$i] == 0) if(!isset($equipment[$i]) || $equipment[$i] == 0)
$equipment[$i] = $empty_slots[$i]; $equipment[$i] = $empty_slots[$i];
} }
for($i = 1; $i < 11; $i++) for($i = 1; $i < 11; $i++) {
{ if(Validator::number($equipment[$i])) {
if(Validator::number($equipment[$i]))
$equipment[$i] = getItemImage($equipment[$i]); $equipment[$i] = getItemImage($equipment[$i]);
else }
else {
$equipment[$i] = '<img src="images/items/' . $equipment[$i] . '.gif" width="32" height="32" border="0" alt=" ' . $equipment[$i] . '" />'; $equipment[$i] = '<img src="images/items/' . $equipment[$i] . '.gif" width="32" height="32" border="0" alt=" ' . $equipment[$i] . '" />';
}
} }
$skulls = array(
1 => 'yellow_skull',
2 => 'green_skull',
3 => 'white_skull',
4 => 'red_skull',
5 => 'black_skull'
);
} }
$skulls = [
1 => 'yellow_skull',
2 => 'green_skull',
3 => 'white_skull',
4 => 'red_skull',
5 => 'black_skull',
];
$dead_add_content = ''; $dead_add_content = '';
$deaths = array(); $deaths = array();
if($db->hasTable('killers')) { if($db->hasTable('killers')) {

View File

@@ -122,7 +122,7 @@ if(!$news_cached)
); );
} }
$tickers_db = $db->query('SELECT * FROM `' . TABLE_PREFIX . 'news` WHERE `type` = ' . TICKER .($canEdit ? '' : ' AND `hide` != 1') .' ORDER BY `date` DESC LIMIT ' . setting('core.news_ticker_limit')); $tickers_db = $db->query('SELECT * FROM `' . TABLE_PREFIX . 'news` WHERE `type` = ' . TICKER . ' AND `hide` != 1 ORDER BY `date` DESC LIMIT ' . setting('core.news_ticker_limit'));
$tickers_content = ''; $tickers_content = '';
if($tickers_db->rowCount() > 0) if($tickers_db->rowCount() > 0)
{ {
@@ -142,7 +142,8 @@ if(!$news_cached)
if($cache->enabled() && !$canEdit) if($cache->enabled() && !$canEdit)
$cache->set('news_' . $template_name . '_' . TICKER, $tickers_content, 60 * 60); $cache->set('news_' . $template_name . '_' . TICKER, $tickers_content, 60 * 60);
$featured_article_db =$db->query('SELECT `id`, `title`, `article_text`, `article_image`, `hide` FROM `' . TABLE_PREFIX . 'news` WHERE `type` = ' . ARTICLE . ($canEdit ? '' : ' AND `hide` != 1') .' ORDER BY `date` DESC LIMIT 1'); $featured_article_db =$db->query('SELECT `id`, `title`, `article_text`, `article_image`, `hide` FROM `' . TABLE_PREFIX . 'news` WHERE `type` = ' . ARTICLE . ' AND `hide` != 1 ORDER BY `date` DESC LIMIT 1');
$article = ''; $article = '';
if($featured_article_db->rowCount() > 0) { if($featured_article_db->rowCount() > 0) {
$article = $featured_article_db->fetch(); $article = $featured_article_db->fetch();
@@ -175,7 +176,7 @@ else {
if(!$news_cached) if(!$news_cached)
{ {
ob_start(); ob_start();
$newses = $db->query('SELECT * FROM ' . $db->tableName(TABLE_PREFIX . 'news') . ' WHERE type = ' . NEWS . ($canEdit ? '' : ' AND hide != 1') . ' ORDER BY date' . ' DESC LIMIT ' . setting('core.news_limit')); $newses = $db->query('SELECT * FROM ' . $db->tableName(TABLE_PREFIX . 'news') . ' WHERE type = ' . NEWS . ' AND hide != 1 ORDER BY date' . ' DESC LIMIT ' . setting('core.news_limit'));
if($newses->rowCount() > 0) if($newses->rowCount() > 0)
{ {
foreach($newses as $news) foreach($newses as $news)

View File

@@ -737,6 +737,18 @@ Sent by MyAAC,<br/>
'desc' => 'should country of user be automatically recognized by his IP? This makes an external API call to http://ipinfo.io', 'desc' => 'should country of user be automatically recognized by his IP? This makes an external API call to http://ipinfo.io',
'default' => true, 'default' => true,
], ],
'account_countries_most_popular' => [
'name' => 'Account Countries Most Popular',
'type' => 'text',
'desc' => 'Those countries will be display at the top of the list on the create account page. The short codes of countries can be found in file <i>system/countries.conf.php</i>',
'default' => 'pl,se,br,us,gb',
'callbacks' => [
'get' => function ($value) {
$tmp = array_map('trim', explode(',', $value));
return array_filter($tmp, function ($v) {return !empty($v); });
},
],
],
'characters_per_account' => [ 'characters_per_account' => [
'name' => 'Characters per Account', 'name' => 'Characters per Account',
'type' => 'number', 'type' => 'number',

View File

@@ -27,6 +27,9 @@ class PHP
{ {
$var = var_export($var, true); $var = var_export($var, true);
ensureFolderExists($this->dir);
ensureIndexExists($this->dir);
// Write to temp file first to ensure atomicity // Write to temp file first to ensure atomicity
$tmp = $this->dir . "tmp_$key." . uniqid('', true) . '.tmp'; $tmp = $this->dir . "tmp_$key." . uniqid('', true) . '.tmp';
file_put_contents($tmp, '<?php $var = ' . $var . ';', LOCK_EX); file_put_contents($tmp, '<?php $var = ' . $var . ';', LOCK_EX);

View File

@@ -13,6 +13,7 @@ class PluginDisableCommand extends Command
protected function configure(): void protected function configure(): void
{ {
$this->setName('plugin:disable') $this->setName('plugin:disable')
->setAliases(['plugin:deactivate'])
->setDescription('This command disables plugin') ->setDescription('This command disables plugin')
->addArgument('plugin-name', InputArgument::REQUIRED, 'Plugin that you want to disable'); ->addArgument('plugin-name', InputArgument::REQUIRED, 'Plugin that you want to disable');
} }

View File

@@ -13,6 +13,7 @@ class PluginEnableCommand extends Command
protected function configure(): void protected function configure(): void
{ {
$this->setName('plugin:enable') $this->setName('plugin:enable')
->setAliases(['plugin:activate'])
->setDescription('This command enables plugin') ->setDescription('This command enables plugin')
->addArgument('plugin-name', InputArgument::REQUIRED, 'Plugin that you want to enable'); ->addArgument('plugin-name', InputArgument::REQUIRED, 'Plugin that you want to enable');
} }

View File

@@ -13,6 +13,7 @@ class PluginUninstallCommand extends Command
protected function configure(): void protected function configure(): void
{ {
$this->setName('plugin:uninstall') $this->setName('plugin:uninstall')
->setAliases(['plugin:remove', 'plugin:delete'])
->setDescription('This command uninstalls plugin') ->setDescription('This command uninstalls plugin')
->addArgument('plugin-name', InputArgument::REQUIRED, 'Plugin that you want to uninstall'); ->addArgument('plugin-name', InputArgument::REQUIRED, 'Plugin that you want to uninstall');
} }

View File

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

View File

@@ -23,6 +23,8 @@ class Player extends Model {
public $timestamps = false; public $timestamps = false;
protected $guarded = [];
protected $casts = [ protected $casts = [
'worldid' => 'integer', 'worldid' => 'integer',
'sex' => 'integer', 'sex' => 'integer',

View File

@@ -513,6 +513,9 @@ class Plugins {
return false; return false;
} }
ensureFolderExists($cachePlugins = CACHE . 'plugins');
ensureIndexExists($cachePlugins);
self::$error = 'There was a problem with extracting zip archive.'; self::$error = 'There was a problem with extracting zip archive.';
$file_name = $plugin_temp_dir . $json_file; $file_name = $plugin_temp_dir . $json_file;
if(!file_exists($file_name)) { if(!file_exists($file_name)) {
@@ -549,21 +552,21 @@ class Plugins {
if(isset($plugin_json['require'])) { if(isset($plugin_json['require'])) {
$require = $plugin_json['require']; $require = $plugin_json['require'];
$myaac_satified = true; $myaac_satisfied = true;
if(isset($require['myaac_'])) { if(isset($require['myaac_'])) {
$require_myaac = $require['myaac_']; $require_myaac = $require['myaac_'];
if(!Semver::satisfies(MYAAC_VERSION, $require_myaac)) { if(!Semver::satisfies(MYAAC_VERSION, $require_myaac)) {
$myaac_satified = false; $myaac_satisfied = false;
} }
} }
else if(isset($require['myaac'])) { else if(isset($require['myaac'])) {
$require_myaac = $require['myaac']; $require_myaac = $require['myaac'];
if(version_compare(MYAAC_VERSION, $require_myaac, '<')) { if(version_compare(MYAAC_VERSION, $require_myaac, '<')) {
$myaac_satified = false; $myaac_satisfied = false;
} }
} }
if(!$myaac_satified) { if(!$myaac_satisfied) {
self::$error = "Your AAC version doesn't meet the requirement of this plugin. Required version is: " . $require_myaac . ", and you're using version " . MYAAC_VERSION . "."; self::$error = "Your AAC version doesn't meet the requirement of this plugin. Required version is: " . $require_myaac . ", and you're using version " . MYAAC_VERSION . ".";
return false; return false;
} }

View File

@@ -248,7 +248,7 @@ class Settings implements \ArrayAccess
echo '<div class="input-group" id="show-hide-' . $key . '">'; echo '<div class="input-group" id="show-hide-' . $key . '">';
} }
echo '<input class="form-control" type="' . $setting['type'] . '" name="settings[' . $key . ']" value="' . ($settingsDb[$key] ?? ($setting['default'] ?? '')) . '" id="' . $key . '"' . $min . $max . $step . '/>'; echo '<input class="form-control" type="' . $setting['type'] . '" name="settings[' . $key . ']" value="' . escapeHtml($settingsDb[$key] ?? ($setting['default'] ?? '')) . '" id="' . $key . '"' . $min . $max . $step . '/>';
if ($setting['type'] === 'password') { if ($setting['type'] === 'password') {
echo '<div class="input-group-append input-group-text"><a href=""><i class="fas fa-eye-slash" ></i></a></div></div>'; echo '<div class="input-group-append input-group-text"><a href=""><i class="fas fa-eye-slash" ></i></a></div></div>';
@@ -266,7 +266,7 @@ class Settings implements \ArrayAccess
if ($rows < 2) { if ($rows < 2) {
$rows = 2; // always min 2 rows for textarea $rows = 2; // always min 2 rows for textarea
} }
echo '<textarea class="form-control" rows="' . $rows . '" name="settings[' . $key . ']" id="' . $key . '">' . $value . '</textarea>'; echo '<textarea class="form-control" rows="' . $rows . '" name="settings[' . $key . ']" id="' . $key . '">' . escapeHtml($value) . '</textarea>';
} }
else if ($setting['type'] === 'options') { else if ($setting['type'] === 'options') {

View File

@@ -145,13 +145,7 @@ function updateStatus() {
} }
$uptime = $status['uptime'] = $serverStatus->getUptime(); $uptime = $status['uptime'] = $serverStatus->getUptime();
$m = date('m', $uptime); $status['uptimeReadable'] = getStatusUptimeReadable($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['monsters'] = $serverStatus->getMonstersCount();
$status['motd'] = $serverStatus->getMOTD(); $status['motd'] = $serverStatus->getMOTD();

View File

@@ -53,6 +53,9 @@
exit; exit;
} }
ensureFolderExists(SIGNATURES_CACHE);
ensureIndexExists(SIGNATURES_CACHE);
$cached = SIGNATURES_CACHE.$player->getId() . '.png'; $cached = SIGNATURES_CACHE.$player->getId() . '.png';
if(file_exists($cached) && (time() < (filemtime($cached) + (60 * setting('core.signature_cache_time'))))) if(file_exists($cached) && (time() < (filemtime($cached) + (60 * setting('core.signature_cache_time')))))
{ {