[WIP] Initial Blacktek support - groups, vocations, items

items.toml loading through custom parser - no .toml reader that I tested can read it
This commit is contained in:
slawkens
2026-02-15 00:54:18 +01:00
parent 4c3f877091
commit fc9d6b2b91
15 changed files with 418 additions and 180 deletions

View File

@@ -14,7 +14,7 @@ use MyAAC\CsrfToken;
use MyAAC\Hooks; use MyAAC\Hooks;
use MyAAC\Plugins; use MyAAC\Plugins;
use MyAAC\Models\Town; use MyAAC\Models\Town;
use MyAAC\Server\XML\Vocations; use MyAAC\Server\Vocations;
use MyAAC\Settings; use MyAAC\Settings;
defined('MYAAC') or die('Direct access not allowed!'); defined('MYAAC') or die('Direct access not allowed!');

View File

@@ -8,7 +8,7 @@
* @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\Cache\Cache; use MyAAC\Server\Groups;
/** /**
* List of groups. * List of groups.
@@ -47,73 +47,9 @@ class OTS_Groups_List implements IteratorAggregate, Countable
return; return;
} }
if(!isset($file[0])) $groups = new Groups();
{ foreach($groups->getGroups() as $id => $info) {
global $config; $this->groups[$id] = new OTS_Group($info);
$file = $config['data_path'] . 'XML/groups.xml';
}
if(!@file_exists($file)) {
error('Error: Cannot load groups.xml. More info in system/logs/error.log file.');
log_append('error.log', '[OTS_Groups_List.php] Fatal error: Cannot load groups.xml (' . $file . '). It doesnt exist.');
return;
}
$cache = Cache::getInstance();
$data = array();
if($cache->enabled())
{
$tmp = '';
if($cache->fetch('groups', $tmp))
$data = unserialize($tmp);
else
{
$groups = new DOMDocument();
if(!@$groups->load($file)) {
error('Error: Cannot load groups.xml. More info in system/logs/error.log file.');
log_append('error.log', '[OTS_Groups_List.php] Fatal error: Cannot load groups.xml (' . $file . '). Error: ' . print_r(error_get_last(), true));
return;
}
// loads groups
foreach( $groups->getElementsByTagName('group') as $group)
{
$data[$group->getAttribute('id')] = array(
'id' => $group->getAttribute('id'),
'name' => $group->getAttribute('name'),
'access' => $group->getAttribute('access')
);
}
$cache->set('groups', serialize($data), 120);
}
foreach($data as $id => $info)
$this->groups[ $id ] = new OTS_Group($info);
}
else
{
// loads DOM document
$groups = new DOMDocument();
if(!@$groups->load($file)) {
error('Error: Cannot load groups.xml. More info in system/logs/error.log file.');
log_append('error.log', '[OTS_Groups_List.php] Fatal error: Cannot load groups.xml (' . $file . '). Error: ' . print_r(error_get_last(), true));
return;
}
// loads groups
foreach($groups->getElementsByTagName('group') as $group)
{
$data[$group->getAttribute('id')] = array(
'id' => $group->getAttribute('id'),
'name' => $group->getAttribute('name'),
'access' => $group->getAttribute('access')
);
$this->groups[ $group->getAttribute('id') ] = new OTS_Group($data[$group->getAttribute('id')]);
//echo $this->getGroup(1)->getName();
}
} }
} }

View File

@@ -13,7 +13,7 @@ use MyAAC\Cache\Cache;
use MyAAC\Models\Player; use MyAAC\Models\Player;
use MyAAC\Models\PlayerDeath; use MyAAC\Models\PlayerDeath;
use MyAAC\Models\PlayerKillers; use MyAAC\Models\PlayerKillers;
use MyAAC\Server\XML\Vocations; use MyAAC\Server\Vocations;
defined('MYAAC') or die('Direct access not allowed!'); defined('MYAAC') or die('Direct access not allowed!');
$title = 'Highscores'; $title = 'Highscores';

View File

@@ -12,7 +12,7 @@
use MyAAC\Cache\Cache; use MyAAC\Cache\Cache;
use MyAAC\Models\ServerConfig; use MyAAC\Models\ServerConfig;
use MyAAC\Models\ServerRecord; use MyAAC\Models\ServerRecord;
use MyAAC\Server\XML\Vocations; use MyAAC\Server\Vocations;
defined('MYAAC') or die('Direct access not allowed!'); defined('MYAAC') or die('Direct access not allowed!');
$title = 'Who is online?'; $title = 'Who is online?';
@@ -87,13 +87,16 @@ $cached = Cache::remember("online_$order", setting('core.online_cache_ttl') * 60
'name' => getPlayerLink($player['name']), 'name' => getPlayerLink($player['name']),
'player' => $player, 'player' => $player,
'level' => $player['level'], 'level' => $player['level'],
'vocation' => $configVocations[$player['vocation']], 'vocation' => $configVocations[$player['vocation']] ?? 'Unknown',
'skull' => $skull, 'skull' => $skull,
'country_image' => getFlagImage($player['country']), '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'], '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[Vocations::getOriginal($player['vocation'])]++; $originalId = Vocations::getOriginal($player['vocation']);
if ($originalId) {
$vocations[$originalId]++;
}
} }
$record = ''; $record = '';

View File

@@ -40,7 +40,7 @@ class DataLoader
{ {
self::$startTime = microtime(true); self::$startTime = microtime(true);
if(Items::loadFromXML()) { if(Items::load()) {
success(self::$locale['step_database_loaded_items'] . self::getLoadedTime()); success(self::$locale['step_database_loaded_items'] . self::getLoadedTime());
} }
else { else {

View File

@@ -16,57 +16,27 @@ use MyAAC\Models\Spell;
class Items class Items
{ {
private static $error = '';
public static $items; public static $items;
public static function loadFromXML($show = false) public static function load(): bool {
{ $file_path = config('data_path') . 'items/items.toml';
$file_path = config('data_path') . 'items/items.xml'; if (file_exists($file_path)) {
if (!file_exists($file_path)) { $items = new Server\TOML\Items();
self::$error = 'Cannot load file ' . $file_path; }
elseif (file_exists(config('data_path') . 'items/items.xml')) {
$items = new Server\XML\Items();
}
else {
return false; return false;
} }
$xml = new \DOMDocument; $items->load();
$xml->load($file_path);
$items = array();
foreach ($xml->getElementsByTagName('item') as $item) {
if ($item->getAttribute('fromid')) {
for ($id = $item->getAttribute('fromid'); $id <= $item->getAttribute('toid'); $id++) {
$tmp = self::parseNode($id, $item, $show);
$items[$tmp['id']] = $tmp['content'];
}
} else {
$tmp = self::parseNode($item->getAttribute('id'), $item, $show);
$items[$tmp['id']] = $tmp['content'];
}
}
$cache_php = new CachePHP(config('cache_prefix'), CACHE . 'persistent/');
$cache_php->set('items', $items, 5 * 365 * 24 * 60 * 60);
return true; return true;
} }
public static function parseNode($id, $node, $show = false) { public static function init(): void {
$name = $node->getAttribute('name'); if(count(self::$items) > 0) {
$article = $node->getAttribute('article');
$plural = $node->getAttribute('plural');
$attributes = array();
foreach($node->getElementsByTagName('attribute') as $attr) {
$attributes[strtolower($attr->getAttribute('key'))] = $attr->getAttribute('value');
}
return array('id' => $id, 'content' => array('article' => $article, 'name' => $name, 'plural' => $plural, 'attributes' => $attributes));
}
public static function getError() {
return self::$error;
}
public static function load() {
if(self::$items) {
return; return;
} }
@@ -75,7 +45,7 @@ class Items
} }
public static function get($id) { public static function get($id) {
self::load(); self::init();
return self::$items[$id] ?? []; return self::$items[$id] ?? [];
} }
@@ -163,7 +133,7 @@ class Items
$s .= ', Hit% ' . ($item['hitChance'] > 0 ? '+' . $item['hitChance'] : '-' . $item['hitChance']); $s .= ', Hit% ' . ($item['hitChance'] > 0 ? '+' . $item['hitChance'] : '-' . $item['hitChance']);
} }
elseif ($item['weaponType'] != 'ammo') { elseif ($item['weaponType'] != 'ammo') {
} }
} }

View File

@@ -0,0 +1,30 @@
<?php
namespace MyAAC\Server;
use MyAAC\Cache\Cache;
class Groups
{
private static array $groups;
public function __construct() {
self::$groups = Cache::remember('groups', 10 * 60, function () {
$tomlGroups = glob(config('server_path') . 'config/groups.toml');
if (count($tomlGroups) > 0) {
$groups = new TOML\Groups();
}
else {
$groups = new XML\Groups();
}
$groups->load();
return $groups->getGroups();
});
}
public static function getGroups(): array {
return self::$groups;
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace MyAAC\Server\TOML;
use Devium\Toml\Toml;
class Groups
{
private array $groups;
public function load(): void
{
$file = config('server_path') . 'config/groups.toml';
if(!@file_exists($file)) {
error('Error: Cannot load groups.toml. More info in system/logs/error.log file.');
log_append('error.log', '[OTS_Groups_List.php] Fatal error: Cannot load groups.toml (' . $file . '). It doesnt exist.');
return;
}
$toml = file_get_contents($file);
$groups = Toml::decode($toml, asArray: true);
foreach ($groups as $group)
{
$this->groups[$group['id']] = [
'id' => $group['id'],
'name' => $group['name'],
'access' => $group['access'],
];
}
echo '<pre>';
var_dump($this->groups);
echo '</pre>';
}
public function getGroups(): array {
return $this->groups;
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace MyAAC\Server\TOML;
use MyAAC\Cache\PHP as CachePHP;
class Items
{
private string $error;
public function getError(): string
{
return $this->error;
}
public function load(): bool
{
$file_path = config('data_path') . 'items/items.toml';
if (!file_exists($file_path)) {
$this->error = 'Cannot load file ' . $file_path;
return false;
}
//$toml = file_get_contents($file_path);
//$items = \Devium\Toml\Toml::decode($toml, asArray: false);
$itemsParser = new ItemsParser();
$itemsParsed = $itemsParser->parse($file_path);
$items = [];
foreach ($itemsParsed as $item) {
$attributes = array_filter($item, function ($key) {
return !in_array($key, ['id', 'article', 'name', 'plural']);
}, ARRAY_FILTER_USE_KEY);
$items[$item['id']] = ['article' => $item['article'], 'name' => $item['name'], 'plural' => $item['plural'] ?? '', 'attributes' => $attributes];
}
$cache_php = new CachePHP(config('cache_prefix'), CACHE . 'persistent/');
$cache_php->set('items', $items, 5 * 365 * 24 * 60 * 60);
return true;
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace MyAAC\Server\TOML;
class ItemsParser
{
public function parse(string $path): array
{
$ret = [];
$i = 0;
$handle = fopen($path, "r");
if ($handle) {
$parse = '';
while (($line = fgets($handle)) !== false) {
if (str_contains($line, '[[items]]') && $i++ != 0) {
//global $whoopsHandler;
//$whoopsHandler->addDataTable('ini', [$parse]);
$ret[] = parse_ini_string($parse);
$parse = '';
continue;
}
// skip lines like this
// field = {type = "fire", initdamage = 20, ticks = 10000, count = 7, damage = 10}
// as it cannot be parsed by parse_ini_string
if (str_contains($line, 'field =')) {
continue;
}
$parse .= $line;
}
fclose($handle);
}
return $ret;
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace MyAAC\Server\TOML;
use Devium\Toml\Toml;
class Vocations
{
private array $vocations;
private array $vocationsFrom;
public function load(): void
{
$tomlVocations = glob(config('data_path') . 'vocations/*.toml');
if (count($tomlVocations) <= 0) {
throw new \RuntimeException('ERROR: Cannot load any <i>.toml</i> vocation in the data/vocations folder.');
}
foreach ($tomlVocations as $file) {
$toml = file_get_contents($file);
$vocations = Toml::decode($toml, asArray: true);
foreach ($vocations as $vocationArray) {
$id = $vocationArray['id'];
$this->vocations[$id] = $vocationArray['name'];
$this->vocationsFrom[$id] = $vocationArray['promotedfrom'];
}
}
ksort($this->vocations, SORT_NUMERIC);
ksort($this->vocationsFrom, SORT_NUMERIC);
}
public function get(): array {
return $this->vocations;
}
public function getFrom(): array {
return $this->vocationsFrom;
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace MyAAC\Server;
use MyAAC\Cache\Cache;
class Vocations
{
private static array $vocations;
private static array $vocationsFrom;
public function __construct() {
$cached = Cache::remember('vocations', 10 * 60, function () {
$tomlVocations = glob(config('data_path') . 'vocations/*.toml');
if (count($tomlVocations) > 0) {
$vocations = new TOML\Vocations();
}
else {
$vocations = new XML\Vocations();
}
$vocations->load();
$from = $vocations->getFrom();
$amount = 0;
foreach ($from as $vocId => $fromVocation) {
if ($vocId != 0 && $vocId == $fromVocation) {
$amount++;
}
}
return ['vocations' => $vocations->get(), 'vocationsFrom' => $from, 'amount' => $amount];
});
self::$vocations = $cached['vocations'];
self::$vocationsFrom = $cached['vocationsFrom'];
config(['vocations', self::$vocations]);
config(['vocations_amount', $cached['amount']]);
}
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 {
if (!isset(self::$vocationsFrom[$id])) {
return null;
}
while ($tmpId = self::$vocationsFrom[$id]) {
if ($tmpId == $id) {
break;
}
$id = $tmpId;
}
return $id;
}
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

@@ -0,0 +1,40 @@
<?php
namespace MyAAC\Server\XML;
class Groups
{
private array $groups;
public function load(): void
{
$file = config('data_path') . 'XML/groups.xml';
if(!@file_exists($file)) {
error('Error: Cannot load groups.xml. More info in system/logs/error.log file.');
log_append('error.log', '[OTS_Groups_List.php] Fatal error: Cannot load groups.xml (' . $file . '). It doesnt exist.');
return;
}
$groups = new \DOMDocument();
if(!@$groups->load($file)) {
error('Error: Cannot load groups.xml. More info in system/logs/error.log file.');
log_append('error.log', '[OTS_Groups_List.php] Fatal error: Cannot load groups.xml (' . $file . '). Error: ' . print_r(error_get_last(), true));
return;
}
// loads groups
foreach( $groups->getElementsByTagName('group') as $group)
{
$this->groups[$group->getAttribute('id')] = [
'id' => $group->getAttribute('id'),
'name' => $group->getAttribute('name'),
'access' => $group->getAttribute('access')
];
}
}
public function getGroups(): array {
return $this->groups;
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace MyAAC\Server\XML;
use MyAAC\Cache\PHP as CachePHP;
class Items
{
private string $error;
public function getError(): string {
return $this->error;
}
public function load(): bool
{
$file_path = config('data_path') . 'items/items.xml';
if (!file_exists($file_path)) {
$this->error = 'Cannot load file ' . $file_path;
return false;
}
$xml = new \DOMDocument;
$xml->load($file_path);
$items = [];
foreach ($xml->getElementsByTagName('item') as $item) {
if ($item->getAttribute('fromid')) {
for ($id = $item->getAttribute('fromid'); $id <= $item->getAttribute('toid'); $id++) {
$tmp = $this->parseNode($id, $item);
$items[$tmp['id']] = $tmp['content'];
}
} else {
$tmp = $this->parseNode($item->getAttribute('id'), $item);
$items[$tmp['id']] = $tmp['content'];
}
}
$cache_php = new CachePHP(config('cache_prefix'), CACHE . 'persistent/');
$cache_php->set('items', $items, 5 * 365 * 24 * 60 * 60);
return true;
}
public function parseNode($id, $node): array
{
$name = $node->getAttribute('name');
$article = $node->getAttribute('article');
$plural = $node->getAttribute('plural');
$attributes = array();
foreach($node->getElementsByTagName('attribute') as $attr) {
$attributes[strtolower($attr->getAttribute('key'))] = $attr->getAttribute('value');
}
return ['id' => $id, 'content' => ['article' => $article, 'name' => $name, 'plural' => $plural, 'attributes' => $attributes]];
}
}

View File

@@ -2,35 +2,10 @@
namespace MyAAC\Server\XML; namespace MyAAC\Server\XML;
use MyAAC\Cache\Cache;
class Vocations class Vocations
{ {
private static array $vocations; private array $vocations;
private static array $vocationsFrom; private 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 public function load(): void
{ {
@@ -51,51 +26,26 @@ class Vocations
foreach($vocationsXML->getElementsByTagName('vocation') as $vocation) { foreach($vocationsXML->getElementsByTagName('vocation') as $vocation) {
$id = $vocation->getAttribute('id'); $id = $vocation->getAttribute('id');
self::$vocations[$id] = $vocation->getAttribute('name'); $this->vocations[$id] = $vocation->getAttribute('name');
$fromVocation = (int) $vocation->getAttribute('fromvoc'); $fromVocation = (int) $vocation->getAttribute('fromvoc');
self::$vocationsFrom[$id] = $fromVocation; $this->vocationsFrom[$id] = $fromVocation;
} }
} }
public static function get(): array { public function get(): array {
return self::$vocations; return $this->vocations;
} }
public static function getFrom(): array { public function getFrom(): array {
return self::$vocationsFrom; return $this->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 {
while ($tmpId = self::$vocationsFrom[$id]) {
if ($tmpId == $id) {
break;
}
$id = $tmpId;
}
return $id;
} }
public static function getBase($includingRook = true): array { public static function getBase($includingRook = true): array {
$vocations = []; return \MyAAC\Server\Vocations::getBase($includingRook);
foreach (self::$vocationsFrom as $vocId => $fromVoc) { }
if ($vocId == $fromVoc && ($vocId != 0 || $includingRook)) {
$vocations[] = $vocId;
}
}
return $vocations; public static function getOriginal(int $id): ?int {
return \MyAAC\Server\Vocations::getOriginal($id);
} }
} }