From 3804498ad980f737b5f569eca45298698d77289c Mon Sep 17 00:00:00 2001
From: Catalin Novgorodschi <1140613+Shadowss@users.noreply.github.com>
Date: Thu, 14 May 2026 15:05:32 +0300
Subject: [PATCH] Incremental Refactor Automation & Database (cache)
Incremental Refactor Automation (starvation split in more methods) & Database (cache & checkAllianceEmbassiesStatus refactor), Fix a bug in Alliance.php (now you cannot kick alliance leader)
---
GameEngine/Alliance.php | 231 ++++++-------
GameEngine/Artifacts.php | 2 +-
GameEngine/Automation.php | 402 +++++++++++++---------
GameEngine/Battle.php | 2 +-
GameEngine/Building.php | 2 +-
GameEngine/Chat.php | 2 +-
GameEngine/Database.php | 677 +++++++++++++++-----------------------
GameEngine/Form.php | 2 +-
GameEngine/Generator.php | 2 +-
GameEngine/Logging.php | 2 +-
GameEngine/Market.php | 2 +-
GameEngine/Profile.php | 2 +-
version.php | 8 +-
13 files changed, 626 insertions(+), 710 deletions(-)
diff --git a/GameEngine/Alliance.php b/GameEngine/Alliance.php
index 2f92457e..6150a784 100755
--- a/GameEngine/Alliance.php
+++ b/GameEngine/Alliance.php
@@ -7,7 +7,7 @@
## Version: 30.04.2026 ##
## Filename Alliance.php ##
## Developed by: Dzoki ##
-## Refactor by: Shadow ##
+## Refactored by: Shadow (cata7007@gmail.com) ##
## License: TravianZ Project ##
## Copyright: TravianZ (c) 2010-2026. All rights reserved. ##
## URLs: https://travianz.org ##
@@ -708,67 +708,50 @@ class Alliance {
/*****************************************
Function to kick a user from alliance
+ REFACTORIZAT 14.05.2026 - blochează kick la fondator
*****************************************/
-
private function kickAlliUser($post) {
- global $database, $session, $form;
+ global $database, $session, $form;
$targetUID = (int)($post['a_user'] ?? 0);
+ $UserData = $database->getUserArray($targetUID, 1);
+ $allyId = (int)$session->alliance;
- // Obținem datele utilizatorului (folosit pentru username și verificări)
- $UserData = $database->getUserArray($targetUID, 1);
-
- // ==================== VERIFICĂRI DE PERMISIUNI ȘI VALIDĂRI ====================
+ // ==================== VERIFICĂRI ====================
if ($this->userPermArray['opt2'] == 0) {
$form->addError("perm", NO_PERMISSION);
- } elseif ($database->getUserField($targetUID, "alliance", 0) != $session->alliance) {
- $form->addError("perm", USER_NOT_IN_YOUR_ALLY);
- } elseif ($UserData['id'] == $session->uid) {
- $form->addError("perm", CANT_EDIT_YOUR_PERMISSIONS); // mesajul original era CANT_EDIT_YOUR_PERMISSIONS
- } else {
- // ==================== EXECUTARE KICK ====================
- $kickedUsername = $database->getUserField($targetUID, "username", 0);
- $database->updateUserField($targetUID, 'alliance', 0, 1);
- $database->deleteAlliPermissions($targetUID);
-
- // !!! Comportament original - șterge alianța la fiecare kick !!!
- $database->deleteAlliance($session->alliance);
-
- // Log notice în alianță
- $notice = '' .
- addslashes($kickedUsername) .
- ' has been expelled from the alliance by ' .
- addslashes($session->username) .
- '.';
- $database->insertAlliNotice($session->alliance, $notice);
-
- // ==================== PROMOVARE NOU LIDER (dacă a fost eliminat liderul) ====================
- if ($database->isAllianceOwner($UserData['id']) == $session->alliance) {
- $newOwner = $database->getAllMember2($session->alliance);
- $newLeader = $newOwner['id'];
-
- // Dăm permisiuni complete noului lider
- $database->updateAlliPermissions(
- $newFounderID,
- (int)$session->alliance,
- 'Alliance Founder',
- 1, 1, 1, 1, 1, 1, 1, 1
- );
-
- // Actualizăm liderul alianței (SQL mai sigur)
- $database->query(
- "UPDATE " . TB_PREFIX . "alidata
- SET leader = " . (int)$newLeader . "
- WHERE id = " . (int)$session->alliance
- );
-
- Automation::updateMax($newLeader);
- }
-
- // Mesaj de succes (comportament original)
- $form->addError("perm", $kickedUsername . ALLY_USER_KICKED);
+ return;
}
+ if ($database->getUserField($targetUID, "alliance", 0) != $allyId) {
+ $form->addError("perm", USER_NOT_IN_YOUR_ALLY);
+ return;
+ }
+ if ($UserData['id'] == $session->uid) {
+ $form->addError("perm", CANT_EDIT_YOUR_PERMISSIONS);
+ return;
+ }
+ // === NOU: nu poți da kick fondatorului ===
+ if ($database->isAllianceOwner($targetUID) == $allyId) {
+ $form->addError("perm", "You cannot expel the alliance founder.");
+ return;
+ }
+
+ // ==================== EXECUTARE KICK ====================
+ $kickedUsername = $UserData['username'];
+
+ $database->evictUserFromAlliance($targetUID);
+ $database->deleteAlliPermissions($targetUID);
+
+ $notice = '' .
+ addslashes($kickedUsername) .
+ ' has been expelled from the alliance by ' . addslashes($session->username) . '.';
+ $database->insertAlliNotice($allyId, $notice);
+
+ // păstrăm comportamentul original
+ $database->deleteAlliance($allyId);
+
+ $form->addError("perm", $kickedUsername . ALLY_USER_KICKED);
}
/*****************************************
Function to set forum link
@@ -816,94 +799,86 @@ class Alliance {
/*****************************************
Function to quit from alliance
+ REFACTORIZAT 14.05.2026 - folosește Database v7
*****************************************/
-
private function quitally($post) {
- global $database, $session, $form;
+ global $database, $session, $form;
// ==================== VERIFICARE PAROLĂ ====================
if (!isset($post['pw']) || $post['pw'] === '') {
$form->addError("pw", PW_EMPTY);
- } elseif (!password_verify($post['pw'], $session->userinfo['password'])) {
+ return;
+ }
+ if (!password_verify($post['pw'], $session->userinfo['password'])) {
$form->addError("pw", LOGIN_PW_ERROR);
- } else {
- // Parola este corectă → continuăm cu părăsirea alianței
+ return;
+ }
- $isFounder = $session->alliance && $database->isAllianceOwner($session->uid) == $session->alliance;
- $memberCount = $database->countAllianceMembers($session->alliance);
+ // Parola corectă → continuăm
+ $allyId = (int)$session->alliance;
+ $uid = (int)$session->uid;
+ $isFounder = $allyId && $database->isAllianceOwner($uid) == $allyId;
+ $memberCount = $database->countAllianceMembers($allyId);
- // ==================== CAZ SPECIAL: LIDERUL PĂRĂSEȘTE ALIANȚA ====================
- if ($isFounder && $memberCount > 1) {
- // Trebuie să alegem un nou fondator
- if (!isset($post['new_founder'])) {
- $form->addError("founder", 'Founder was not selected.');
- return;
+ // ==================== CAZ SPECIAL: LIDERUL PLEACĂ ====================
+ if ($isFounder && $memberCount > 1) {
+ if (empty($post['new_founder'])) {
+ $form->addError("founder", 'Founder was not selected.');
+ return;
+ }
+ $newFounderID = (int)$post['new_founder'];
+
+ // Validăm că noul fondator e în alianță și nu ești tu
+ $members = $database->getAllMember($allyId);
+ $valid = false;
+ foreach ($members as $m) {
+ if ($m['id'] == $newFounderID && $newFounderID != $uid) {
+ $valid = true;
+ $newFounderName = $m['username'];
+ break;
}
- $newFounderID = (int)$post['new_founder'];
-
- // Validăm că noul fondator face parte din alianță
- $members = $database->getAllMember($session->alliance);
- $validMemberFound = false;
- foreach ($members as $member) {
- if ($member['id'] == $newFounderID) {
- $validMemberFound = true;
- break;
- }
- }
- if (!$validMemberFound || $newFounderID == $session->uid) {
- $form->addError("founder", 'Invalid founder.');
- return;
- }
-
- // Dăm permisiuni complete noului lider
- $database->updateAlliPermissions(
- $newFounderID,
- (int)$session->alliance,
- 'Alliance Founder',
- 1, 1, 1, 1, 1, 1, 1, 1
- );
-
- // Actualizăm liderul alianței
- $_SESSION['alliance_user'] = 0;
- $database->query(
- "UPDATE " . TB_PREFIX . "alidata
- SET leader = " . $newFounderID . "
- WHERE id = " . (int)$session->alliance
- );
-
- Automation::updateMax($newFounderID);
-
- // Trimitem mesaj în joc noului lider
- $messageBody = "Hi!\n\n" .
- "This is to inform you that the former leader of your alliance - " .
- "uid . "\">" .
- $database->escape($session->username) .
- ", has decided to quit and elected you as his replacement. " .
- "You now gain full access, administration and responsibilities to your alliance.\n\n" .
- "Good luck!\n\nYours sincerely,\nServer Robot :)";
- $database->sendMessage(
- $newFounderID,
- 4,
- 'You are now leader of your alliance',
- $messageBody,
- 0, 0, 0, 0, 0,
- true
- );
+ }
+ if (!$valid) {
+ $form->addError("founder", 'Invalid founder.');
+ return;
}
- // ==================== PĂRĂSIRE ALIANȚĂ (pentru toți membrii) ====================
- $database->updateUserField($session->uid, 'alliance', 0, 1);
- $database->deleteAlliPermissions($session->uid);
+ // Mesaj specific pentru quit voluntar
+ $messageBody = "Hi!\n\n" .
+ "This is to inform you that the former leader of your alliance - " .
+ "" .
+ $database->escape($session->username) .
+ ", has decided to quit and elected you as his replacement. " .
+ "You now gain full access, administration and responsibilities to your alliance.\n\n" .
+ "Good luck!\n\nYours sincerely,\nServer Robot :)";
- // Log notice + ștergere alianță (comportament exact ca în original)
- $database->deleteAlliance($session->alliance);
- $notice = '' .
- addslashes($session->username) .
- ' has quit the alliance.';
- $database->insertAlliNotice($session->alliance, $notice);
- header("Location: spieler.php?uid=" . $session->uid);
- exit;
+ // Folosim metoda centralizată cu mesaj custom
+ $database->promoteNewAllianceLeader(
+ $allyId,
+ $newFounderID,
+ $uid,
+ $newFounderName,
+ ['username' => $session->username, 'id' => $uid],
+ $messageBody
+ );
+
+ $_SESSION['alliance_user'] = 0;
}
+
+ // ==================== PĂRĂSIRE ALIANȚĂ ====================
+ $database->updateUserField($uid, 'alliance', 0, 1);
+ $database->deleteAlliPermissions($uid);
+
+ // șterge alianța dacă e goală (comportament original)
+ $database->deleteAlliance($allyId);
+
+ $notice = '' .
+ addslashes($session->username) .
+ ' has quit the alliance.';
+ $database->insertAlliNotice($allyId, $notice);
+
+ header("Location: spieler.php?uid=" . $uid);
+ exit;
}
/*****************************************
diff --git a/GameEngine/Artifacts.php b/GameEngine/Artifacts.php
index 4167334a..19ebdf2b 100644
--- a/GameEngine/Artifacts.php
+++ b/GameEngine/Artifacts.php
@@ -7,7 +7,7 @@
## Version: 13.05.2026 ##
## Filename Artifacts.php ##
## Developed by: Dzoki ##
-## Refactor by: Shadow ##
+## Refactored by: Shadow (cata7007@gmail.com) ##
## License: TravianZ Project ##
## Copyright: TravianZ (c) 2010-2026. All rights reserved. ##
## URLs: https://travianz.org ##
diff --git a/GameEngine/Automation.php b/GameEngine/Automation.php
index cfee998d..e2a5e8bc 100644
--- a/GameEngine/Automation.php
+++ b/GameEngine/Automation.php
@@ -61,6 +61,12 @@ class Automation {
private $artifacts;
+ /**
+ * Cache pentru utilizatori pentru a reduce query-urile duplicate
+ * @var array
+ */
+ private $userCache = [];
+
public function __construct() {
//Classes initialization
@@ -110,6 +116,19 @@ class Automation {
$this->artefactOfTheFool();
}
+ /**
+ * Returneaza datele utilizatorului cu cache local
+ */
+ private function getCachedUser($uid, $mode = 1) {
+ global $database;
+ $uid = (int)$uid;
+ if (!isset($this->userCache[$uid])) {
+ $this->userCache[$uid] = $database->getUserArray($uid, $mode);
+ }
+ return $this->userCache[$uid];
+ }
+
+
public function procResType($ref, $mode = 0) {
//Capital or only 1 village left = cannot be destroyed
return addslashes(empty($build = Building::procResType($ref)) && !$mode ? "Village can't be" : $build);
@@ -872,6 +891,9 @@ class Automation {
}
private function sendunitsComplete() {
+ // PROCESARE ATACURI COMPLETE - functie critica, pastrata 100% compatibila
+ // Aceasta functie gestioneaza toate atacurile care ajung la destinatie
+ // Include: batalii, capcane, evaziune erou, distrugere cladiri, cuceriri
global $bid19, $bid23, $bid34, $u99, $database, $battle, $technology, $units;
$time = time();
@@ -919,16 +941,16 @@ class Automation {
$DefenderWref = $data['to'];
$NatarCapital = false;
- $Attacker['id'] = $database->getUserArray($database->getVillageField($data['from'],"owner"), 1)["id"];
+ $Attacker['id'] = $this->getCachedUser($database->getVillageField($data['from'],"owner"),1)['id'];
$AttackerID = $Attacker['id'];
- $owntribe = $database->getUserArray($database->getVillageField($data['from'],"owner"), 1)["tribe"];
- $ownally = $database->getUserArray($database->getVillageField($data['from'],"owner"), 1)["alliance"];
+ $owntribe = $this->getCachedUser($database->getVillageField($data['from'],"owner"),1)['tribe'];
+ $ownally = $this->getCachedUser($database->getVillageField($data['from'],"owner"),1)['alliance'];
$from = $database->getMInfo($data['from']);
$fromF = $database->getVillage($data['from']);
//It's a village
if ($isoasis == 0){
- $DefenderUserData = $database->getUserArray($database->getVillageField($data['to'],"owner"), 1);
+ $DefenderUserData = $this->getCachedUser($database->getVillageField($data['to'],"owner"),1);
$Defender['id'] = $DefenderUserData["id"];
$DefenderID = $Defender['id'];
$targettribe = $DefenderUserData["tribe"];
@@ -1094,7 +1116,7 @@ class Automation {
}else{ //It's an oasis
- $DefenderUserData = $database->getUserArray($database->getOasisField($data['to'], "owner"), 1);
+ $DefenderUserData = $this->getCachedUser($database->getOasisField($data['to'], "owner"),1);
$Defender['id'] = $DefenderUserData["id"];
$DefenderID = $Defender['id'];
$targettribe = $DefenderUserData["tribe"];
@@ -3707,175 +3729,253 @@ class Automation {
private function starvation() {
global $database, $technology;
- //Starvation is disabled during Easter/Holidays/Christmas
+ // Starvation este dezactivat in perioada de pace (sarbatori)
if(PEACE) return;
$time = time();
- //Update starvation in every village
- $starvarray = $database->getProfileVillages(0, 7);
- foreach($starvarray as $starv) $database->addStarvationData($starv['wref']);
+ // 1. Actualizeaza datele de starvation pentru toate satele
+ $this->starvationUpdateAllVillages();
- //Load villages with minus prod
- $starvarray = [];
- $starvarray = $database->getStarvation();
+ // 2. Incarca satele cu productie negativa
+ $starvarray = $this->starvationGetVillagesWithDeficit();
+ if (empty($starvarray)) return;
- $vilIDs = [];
- foreach ($starvarray as $starv) $vilIDs[] = $starv['wref'];
+ $vilIDs = array_column($starvarray, 'wref');
- //Cache
+ // 3. Pregateste cache-urile pentru a reduce query-urile
+ $this->starvationPrepareCaches($vilIDs);
+
+ foreach ($starvarray as $starv) {
+ $this->starvationProcessVillage($starv, $time);
+ }
+ }
+
+ /**
+ * Actualizeaza tabela de starvation pentru toate satele
+ */
+ private function starvationUpdateAllVillages() {
+ global $database;
+ $starvarray = $database->getProfileVillages(0, 7);
+ foreach($starvarray as $starv) {
+ $database->addStarvationData($starv['wref']);
+ }
+ }
+
+ /**
+ * Returneaza satele cu deficit de crop
+ */
+ private function starvationGetVillagesWithDeficit() {
+ global $database;
+ return $database->getStarvation();
+ }
+
+ /**
+ * Pregateste cache-urile pentru trupe
+ */
+ private function starvationPrepareCaches(array $vilIDs) {
+ global $database;
$database->getEnforceVillage($vilIDs, 0);
$database->getOasisEnforce($vilIDs, 2);
$database->getOasisEnforce($vilIDs, 3);
$database->getPrisoners($vilIDs, 1);
$database->getMovement(3, $vilIDs, 0);
$database->getMovement(4, $vilIDs, 1);
+ }
- foreach ($starvarray as $starv)
- {
- $unitarrays = $this->getAllUnits($starv['wref']);
+ /**
+ * Proceseaza un singur sat pentru starvation
+ */
+ private function starvationProcessVillage($starv, $time) {
+ global $database, $technology;
- $upkeep = $starv['pop'] + $technology->getUpkeep($unitarrays, 0, $starv['wref']);
-
- $enforceArrays = $prisonerArrays = $unitArrays = $attackArrays = $allTroopsArray = $starvingTroops = $killedUnits = [];
-
- $enforceArrays = [$database->getOasisEnforce($starv['wref'], 2),
- $database->getOasisEnforce($starv['wref'], 3),
- $database->getEnforceVillage($starv['wref'], 2),
- $database->getEnforceVillage($starv['wref'], 3)];
-
- $prisonerArrays = [$database->getPrisoners($starv['wref'], 1)];
-
- $unitArrays = ($database->getUnitsNumber($starv['wref'], 0) > 0) ? [[$database->getUnit($starv['wref'])]] : [];
+ $unitarrays = $this->getAllUnits($starv['wref']);
+ // Formula originala de upkeep - pastrata exact
+ $upkeep = $starv['pop'] + $technology->getUpkeep($unitarrays, 0, $starv['wref']);
- $attackArrays = [$database->getMovement(3, $starv['wref'], 0),
- $database->getMovement(4, $starv['wref'], 1)];
+ $troopData = $this->starvationFindFirstTroopGroup($starv);
+ if (empty($troopData['troops'])) {
+ // Nu exista trupe, verifica doar resetarea
+ $this->starvationCheckReset($starv, $upkeep);
+ return;
+ }
- $allTroopsArray = [$enforceArrays, $prisonerArrays, $unitArrays, $attackArrays];
+ $starvingTroops = $troopData['troops'];
+ $type = $troopData['type'];
+ $subtype = $troopData['subtype'];
- //Find the first not-empty array
- foreach($allTroopsArray as $type => $allTroops)
- {
- if(!empty($allTroops)){
- foreach($allTroops as $subtype => $troops){
- if(!empty($troops)){
- $starvingTroops = reset($troops);
- break 2;
- }
- }
- }
- }
+ $timedif = $time - $starv['starvupdate'];
+ $cropProd = $database->getCropProdstarv($starv['wref']) - $starv['starv'];
+
+ if($cropProd < 0){
+ // Calcul deficit - formula originala pastrata
+ $starvsec = (abs($cropProd) / 3600);
+ $difcrop = ($timedif * $starvsec);
+ $oldcrop = $database->getVillageField($starv['wref'], 'crop');
- //If the player has no troops, then skip the next instructions
- if(empty($starvingTroops)) continue;
-
- //Counting
- $timedif = $time - $starv['starvupdate'];
- $cropProd = $database->getCropProdstarv($starv['wref']) - $starv['starv'];
- if($cropProd < 0){
- $starvsec = (abs($cropProd) / 3600);
- $difcrop = ($timedif * $starvsec); //crop eat up over time
- $newcrop = 0;
- $oldcrop = $database->getVillageField($starv['wref'], 'crop');
-
- //If the grain is then tries to send all
- if ($oldcrop > 100){
- $difcrop = $difcrop - $oldcrop;
- if($difcrop < 0){
- $difcrop = 0;
- $newcrop = $oldcrop - $difcrop;
- $database->setVillageField($starv['wref'], 'crop', $newcrop);
- }
- }
-
- if($difcrop > 0 && $oldcrop <= 0){
- $tribe = $database->getUserField(($type == 2) ? $starv['owner'] : $database->getVillageField($starvingTroops['from'], "owner"), "tribe", 0);
- $start = ($special = in_array($type, [1, 3])) ? 1 : ($tribe - 1) * 10 + 1;
- $end = ($special) ? 10 : $tribe * 10 ;
- $utype = ($special) ? 't' : 'u';
- $heroType = ($special) ? 't11' : 'hero';
-
- $totalUnits = 0;
- $counting = true;
- while($difcrop > 0)
- {
- //S earch the highest troop
- $maxcount = $maxtype = 0;
- for($i = $start ; $i <= $end ; $i++)
- {
- $units = (isset($starvingTroops[$utype.$i]) ? $starvingTroops[$utype.$i] : 0);
- if($counting) $totalUnits += $units;
- if($units > $maxcount){
- $maxcount = $units;
- $maxtype = $i;
- }
- }
- if($counting) $counting = false;
-
- if($maxtype > 0){
- $starvingTroops[$utype.$maxtype]--;
- if ( !isset($killedUnits[$maxtype]) ) {
- $killedUnits[$maxtype] = 0;
- }
- $killedUnits[$maxtype]++;
- $difcrop -= $GLOBALS['u'.(($special) ? $maxtype + ($tribe - 1) * 10 : $maxtype)]['crop'];
- }
- else break;
- }
-
- $totalKilledUnits = array_sum($killedUnits);
- if($starvingTroops[$heroType] > 0 && ($totalUnits == 0 || $totalUnits == $totalKilledUnits)){
- $totalKilledUnits += $starvingTroops[$heroType];
- $totalUnits += $starvingTroops[$heroType];
- $starvingTroops['heroinfo'] = $database->getHero(($type == 2) ? $starv['owner'] : $database->getVillageField(($type == 3 && $subtype == 1) ? $starvingTroops['to'] : $starvingTroops['from'], "owner"))[0];
- $database->modifyHero("dead", 1, $starvingTroops['heroinfo']['heroid']);
- $database->modifyHero("health", 0, $starvingTroops['heroinfo']['heroid']);
- $newCrop = $GLOBALS['h'.$starvingTroops['heroinfo']['unit'].'_full'][min($starvingTroops['heroinfo']['level'], 60)]['crop'] + $difcrop;
- }
- else if($maxtype == 0) $newCrop = 0;
- else $newCrop = $GLOBALS['u'.$maxtype]['crop'];
-
- if($totalKilledUnits > 0){
- switch($type){
- case 0:
- if($totalKilledUnits < $totalUnits){
- $database->modifyEnforce($starvingTroops['id'], array_keys($killedUnits), array_values($killedUnits), 0);
- }
- else $database->deleteReinf($starvingTroops['id']);
- break;
-
- case 1:
- if($totalKilledUnits < $totalUnits){
- $database->modifyPrisoners($starvingTroops['id'], array_keys($killedUnits), array_values($killedUnits), 0);
- $database->modifyUnit($starvingTroops['wref'], ["99o"], [$totalKilledUnits], [0]);
- }else{
- $database->deletePrisoners($starvingTroops['id']);
- $database->modifyUnit($starvingTroops['wref'], ["99o"], [$totalUnits], [0]);
- }
- break;
-
- case 2:
- $database->modifyUnit($starv['wref'], array_keys($killedUnits), array_values($killedUnits), [0]);
- break;
-
- case 3:
- if($totalKilledUnits < $totalUnits){
- $database->modifyAttack2($starvingTroops['id'], array_keys($killedUnits), array_values($killedUnits), 0);
- }
- else $database->setMovementProc($starvingTroops['moveid']);
- break;
- }
-
- $database->modifyResource($starv['wref'], 0, 0, 0, max($newCrop, 0), 1);
- $database->setVillageField($starv['wref'], ['starv', 'starvupdate'], [$upkeep, $time]);
- }
+ // Consuma mai intai crop-ul din hambar
+ if ($oldcrop > 100) {
+ $difcrop = $difcrop - $oldcrop;
+ if($difcrop < 0){
+ $difcrop = 0;
+ $newcrop = $oldcrop - $difcrop;
+ $database->setVillageField($starv['wref'], 'crop', $newcrop);
}
}
- $crop = $database->getCropProdstarv($starv['wref'], false);
- if ($crop > $upkeep) $database->setVillageFields($starv['wref'], ['starv', 'starvupdate'], [0, 0]);
+ if($difcrop > 0 && $oldcrop <= 0){
+ $this->starvationKillTroops($starv, $starvingTroops, $type, $subtype, $difcrop, $upkeep, $time);
+ }
+ }
- unset ($unitarrays, $type, $subtype);
+ $this->starvationCheckReset($starv, $upkeep);
+ }
+
+ /**
+ * Gaseste primul grup de trupe care poate muri de foame
+ * Ordinea: enforce oasis, enforce village, prisoners, unitati proprii, atacuri
+ */
+ private function starvationFindFirstTroopGroup($starv) {
+ global $database;
+
+ $enforceArrays = [
+ $database->getOasisEnforce($starv['wref'], 2),
+ $database->getOasisEnforce($starv['wref'], 3),
+ $database->getEnforceVillage($starv['wref'], 2),
+ $database->getEnforceVillage($starv['wref'], 3)
+ ];
+
+ $prisonerArrays = [$database->getPrisoners($starv['wref'], 1)];
+ $unitArrays = ($database->getUnitsNumber($starv['wref'], 0) > 0) ? [[$database->getUnit($starv['wref'])]] : [];
+ $attackArrays = [
+ $database->getMovement(3, $starv['wref'], 0),
+ $database->getMovement(4, $starv['wref'], 1)
+ ];
+
+ $allTroopsArray = [$enforceArrays, $prisonerArrays, $unitArrays, $attackArrays];
+
+ foreach($allTroopsArray as $type => $allTroops) {
+ if(!empty($allTroops)){
+ foreach($allTroops as $subtype => $troops){
+ if(!empty($troops)){
+ return [
+ 'troops' => reset($troops),
+ 'type' => $type,
+ 'subtype' => $subtype
+ ];
+ }
+ }
+ }
+ }
+ return ['troops' => [], 'type' => null, 'subtype' => null];
+ }
+
+ /**
+ * Ucide trupele conform deficitului de crop
+ */
+ private function starvationKillTroops($starv, &$starvingTroops, $type, $subtype, $difcrop, $upkeep, $time) {
+ global $database;
+
+ $tribe = $database->getUserField(($type == 2) ? $starv['owner'] : $database->getVillageField($starvingTroops['from'], "owner"), "tribe", 0);
+ $special = in_array($type, [1, 3]);
+ $start = $special ? 1 : ($tribe - 1) * 10 + 1;
+ $end = $special ? 10 : $tribe * 10;
+ $utype = $special ? 't' : 'u';
+ $heroType = $special ? 't11' : 'hero';
+
+ $killedUnits = [];
+ $totalUnits = 0;
+ $counting = true;
+
+ while($difcrop > 0) {
+ $maxcount = $maxtype = 0;
+ for($i = $start; $i <= $end; $i++) {
+ $units = (isset($starvingTroops[$utype.$i]) ? $starvingTroops[$utype.$i] : 0);
+ if($counting) $totalUnits += $units;
+ if($units > $maxcount){
+ $maxcount = $units;
+ $maxtype = $i;
+ }
+ }
+ if($counting) $counting = false;
+
+ if($maxtype > 0){
+ $starvingTroops[$utype.$maxtype]--;
+ $killedUnits[$maxtype] = ($killedUnits[$maxtype] ?? 0) + 1;
+ // Formula originala de consum crop per unitate
+ $unitIndex = $special ? $maxtype + ($tribe - 1) * 10 : $maxtype;
+ $difcrop -= $GLOBALS['u'.$unitIndex]['crop'];
+ } else break;
+ }
+
+ $totalKilledUnits = array_sum($killedUnits);
+ $newCrop = 0;
+
+ // Verifica daca eroul moare
+ if($starvingTroops[$heroType] > 0 && ($totalUnits == 0 || $totalUnits == $totalKilledUnits)){
+ $totalKilledUnits += $starvingTroops[$heroType];
+ $totalUnits += $starvingTroops[$heroType];
+ $heroOwner = ($type == 2) ? $starv['owner'] : $database->getVillageField(($type == 3 && $subtype == 1) ? $starvingTroops['to'] : $starvingTroops['from'], "owner");
+ $heroInfo = $database->getHero($heroOwner)[0];
+ $database->modifyHero("dead", 1, $heroInfo['heroid']);
+ $database->modifyHero("health", 0, $heroInfo['heroid']);
+ $newCrop = $GLOBALS['h'.$heroInfo['unit'].'_full'][min($heroInfo['level'], 60)]['crop'] + $difcrop;
+ } else if($maxtype > 0) {
+ $newCrop = $GLOBALS['u'.$maxtype]['crop'];
+ }
+
+ if($totalKilledUnits > 0) {
+ $this->starvationApplyDatabaseChanges($starv, $starvingTroops, $type, $killedUnits, $totalUnits, $totalKilledUnits, $newCrop, $upkeep, $time);
+ }
+ }
+
+ /**
+ * Aplica modificarile in baza de date
+ */
+ private function starvationApplyDatabaseChanges($starv, $starvingTroops, $type, $killedUnits, $totalUnits, $totalKilledUnits, $newCrop, $upkeep, $time) {
+ global $database;
+
+ switch($type){
+ case 0: // enforce
+ if($totalKilledUnits < $totalUnits){
+ $database->modifyEnforce($starvingTroops['id'], array_keys($killedUnits), array_values($killedUnits), 0);
+ } else {
+ $database->deleteReinf($starvingTroops['id']);
+ }
+ break;
+ case 1: // prisoners
+ if($totalKilledUnits < $totalUnits){
+ $database->modifyPrisoners($starvingTroops['id'], array_keys($killedUnits), array_values($killedUnits), 0);
+ $database->modifyUnit($starvingTroops['wref'], ["99o"], [$totalKilledUnits], [0]);
+ } else {
+ $database->deletePrisoners($starvingTroops['id']);
+ $database->modifyUnit($starvingTroops['wref'], ["99o"], [$totalUnits], [0]);
+ }
+ break;
+ case 2: // unitati proprii
+ $database->modifyUnit($starv['wref'], array_keys($killedUnits), array_values($killedUnits), [0]);
+ break;
+ case 3: // atacuri in miscare
+ if($totalKilledUnits < $totalUnits){
+ $database->modifyAttack2($starvingTroops['id'], array_keys($killedUnits), array_values($killedUnits), 0);
+ } else {
+ $database->setMovementProc($starvingTroops['moveid']);
+ }
+ break;
+ }
+
+ $database->modifyResource($starv['wref'], 0, 0, 0, max($newCrop, 0), 1);
+ $database->setVillageField($starv['wref'], ['starv', 'starvupdate'], [$upkeep, $time]);
+ }
+
+ /**
+ * Verifica daca starvation poate fi resetat
+ */
+ private function starvationCheckReset($starv, $upkeep) {
+ global $database;
+ $crop = $database->getCropProdstarv($starv['wref'], false);
+ if ($crop > $upkeep) {
+ $database->setVillageFields($starv['wref'], ['starv', 'starvupdate'], [0, 0]);
}
}
diff --git a/GameEngine/Battle.php b/GameEngine/Battle.php
index 1d2034c5..e297e985 100644
--- a/GameEngine/Battle.php
+++ b/GameEngine/Battle.php
@@ -7,7 +7,7 @@
## Version: 08.05.2026 ##
## Filename: Battle.php ##
## Developed by: Dzoki & Dixie ##
-## Refactored by: Shadow ##
+## Refactored by: Shadow (cata7007@gmail.com) ##
## Fixed by: InCube - double troops ##
## Reworked/Fix: ronix ##
## Thanks to: Akakori, Elmar & Kirilloid ##
diff --git a/GameEngine/Building.php b/GameEngine/Building.php
index 71d9aa64..0f3734b0 100755
--- a/GameEngine/Building.php
+++ b/GameEngine/Building.php
@@ -7,7 +7,7 @@
## Version: 12.05.2026 ##
## Filename: Building.php ##
## Developed by: Dzoki & Dixie ##
-## Refactored by: Shadow ##
+## Refactored by: Shadow (cata7007@gmail.com) ##
## Reworked/Fix: ronix ##
## License: TravianZ Project ##
## Copyright: TravianZ (c) 2010-2026. All rights reserved. ##
diff --git a/GameEngine/Chat.php b/GameEngine/Chat.php
index e82c80b2..1660b27b 100755
--- a/GameEngine/Chat.php
+++ b/GameEngine/Chat.php
@@ -5,7 +5,7 @@
## --------------------------------------------------------------------------- ##
## Filename Chat.php ##
## Developed by: TTMMTT ##
-## Refactor by: Shadow ##
+## Refactored by: Shadow (cata7007@gmail.com) ##
## License: TravianZ Project ##
## Copyright: TravianZ (c) 2010-2026. All rights reserved. ##
## URLs: https://travianz.org ##
diff --git a/GameEngine/Database.php b/GameEngine/Database.php
index 9fc3de36..403c5302 100755
--- a/GameEngine/Database.php
+++ b/GameEngine/Database.php
@@ -1,17 +1,23 @@
selectQueryCount++;
+ elseif (stripos($trim, 'INSERT') === 0) $this->insertQueryCount++;
+ elseif (stripos($trim, 'UPDATE') === 0) $this->updateQueryCount++;
+ elseif (stripos($trim, 'DELETE') === 0) $this->deleteQueryCount++;
+ elseif (stripos($trim, 'REPLACE') === 0) $this->replaceQueryCount++;
+ }
+
+ private function fetchCached(string $key, string $sql, callable $fetcher) {
+ if (isset($this->queryCache[$key])) return $this->queryCache[$key];
+ $this->countQueryType($sql);
+ $res = mysqli_query($this->dblink, $sql);
+ $data = $fetcher($res);
+ $this->queryCache[$key] = $data;
+ return $data;
+ }
+
+ private function clearQueryCache(?string $prefix = null): void {
+ if ($prefix === null) $this->queryCache = [];
+ else foreach (array_keys($this->queryCache) as $k) if (strpos($k, $prefix)===0) unset($this->queryCache[$k]);
+ }
+ // === END REFACTOR v2 ===
+
/**
*
@@ -631,7 +670,7 @@ class MYSQLi_DB implements IDbConnection {
References: lietuvis10
***************************/
-public function getBestOasisCropBonus($x, $y) {
+ public function getBestOasisCropBonus($x, $y) {
$x = (int)$x;
$y = (int)$y;
@@ -656,18 +695,19 @@ public function getBestOasisCropBonus($x, $y) {
References: Result
***************************/
function mysqli_fetch_all($result) {
- list($result) = $this->escape_input($result);
-
+ // REFACTORIZAT: garantăm array pentru PHP 7.2+, eliminăm escape pe resource
$all = [];
- if($result) {
+ if($result instanceof \mysqli_result) {
while($row = mysqli_fetch_assoc($result)) {
$all[] = $row;
}
- return $all;
+ mysqli_free_result($result);
}
+ return $all;
}
function query_return($q) {
+ $this->countQueryType($q);
$result = mysqli_query($this->dblink,$q);
return $this->mysqli_fetch_all($result);
}
@@ -677,6 +717,8 @@ public function getBestOasisCropBonus($x, $y) {
References: Query
***************************/
function query($query) {
+ // REFACTORIZAT: contorizare centralizată
+ $this->countQueryType($query);
return mysqli_query($this->dblink,$query);
}
@@ -839,31 +881,45 @@ public function getBestOasisCropBonus($x, $y) {
}
function updateUserField($ref, $field, $value, $switch) {
- list($ref) = $this->escape_input($ref);
+ list($ref) = $this->escape_input($ref);
- if (!is_array($field)) {
- $field = [$field];
- $value = [$value];
- }
+ if (!is_array($field)) {
+ $field = [$field];
+ $value = [$value];
+ }
- $pairs = [];
- foreach ($field as $index => $fieldName) {
- $pairs[] = $this->escape($fieldName) . ' = ' . (Math::isInt($value[$index]) ? $value[$index] : '"'.$this->escape($value[$index]).'"');
- }
+ $pairs = [];
+ foreach ($field as $i => $f) {
+ $pairs[] = $this->escape($f) . ' = ' . (is_numeric($value[$i]) ? $value[$i] : "'".$this->escape($value[$i])."'");
+ }
- if(!$switch) $q = "UPDATE " . TB_PREFIX . "users SET ".implode(', ', $pairs)." where username = '$ref'";
- else $q = "UPDATE " . TB_PREFIX . "users SET ".implode(', ', $pairs)." where id = " . (int) $ref;
+ $q = !$switch
+ ? "UPDATE ".TB_PREFIX."users SET ".implode(', ', $pairs)." WHERE username = '$ref'"
+ : "UPDATE ".TB_PREFIX."users SET ".implode(', ', $pairs)." WHERE id = ".(int)$ref;
- // update cached values
- if ($ret = mysqli_query($this->dblink,$q)) {
- foreach ($field as $index => $fieldName) {
- if (isset(self::$fieldsCache[$ref.($switch ? 0 : 1)][$fieldName]))
- self::$fieldsCache[$ref.($switch ? 0 : 1)][$fieldName] = $value[$index];
- }
- }
+ $ret = mysqli_query($this->dblink, $q);
- return $ret;
- }
+ // === FIX CACHE - ștergem tot ce ține de user ===
+ $uid = $switch ? (int)$ref : (int)$this->getUserField($ref, 'id', 0, false);
+
+ // 1. cache-ul de fields
+ unset(self::$fieldsCache[$uid.'0'], self::$fieldsCache[$uid.'1']);
+ unset(self::$fieldsCache[$ref.'0'], self::$fieldsCache[$ref.'1']);
+
+ // 2. cache-ul de query-uri
+ $this->clearQueryCache('userarray');
+ $this->clearQueryCache('getUser');
+
+ // 3. forțăm și sesiunea dacă e userul curent
+ global $session;
+ if (isset($session) && $session->uid == $uid && in_array('alliance', (array)$field)) {
+ $idx = array_search('alliance', (array)$field);
+ $session->alliance = $value[$idx];
+ $session->userinfo['alliance'] = $value[$idx];
+ }
+
+ return $ret;
+}
// no need to cache this method
function getSitee($uid) {
@@ -938,42 +994,6 @@ public function getBestOasisCropBonus($x, $y) {
}
return $result;
-
- /*list($ref, $field, $mode) = $this->escape_input($ref, $field, $mode);
-
- // first of all, check if we should be using cache and whether the field
- // required is already cached
- if ($use_cache && ($cachedValue = self::returnCachedContent(self::$fieldsCache, $ref.$mode)) && !is_null($cachedValue)) {
- // check if we have the requested field type cached
- if (isset($cachedValue[$field])) {
- return $cachedValue[$field];
- }
- }
-
- // update for Multihunter's username and ID
- if (($mode && $ref == '') || (!$mode && $ref == 0)) {
- $ref = 'Multihunter';
- $mode = 1;
- }
-
- if(!$mode) {
- $q = "SELECT $field FROM " . TB_PREFIX . "users where id = " . (int) $ref;
- } else {
- $q = "SELECT $field FROM " . TB_PREFIX . "users where username = '$ref'";
- }
-
- $result = mysqli_query($this->dblink,$q) or die(mysqli_error($this->dblink));
-
- if ($result) {
- $dbarray = mysqli_fetch_array($result);
- self::$fieldsCache[$ref.$mode][$field] = $dbarray[$field];
- } elseif($field=="username") {
- self::$fieldsCache[$ref.$mode][$field] = "??";
- } else {
- self::$fieldsCache[$ref.$mode][$field] = 0;
- }
-
- return self::$fieldsCache[$ref.$mode][$field];*/
}
function getUserFields($ref, $fields, $mode, $use_cache = true) {
@@ -985,59 +1005,6 @@ public function getBestOasisCropBonus($x, $y) {
// return all data, don't waste time by selecting fields one by one
return $this->getUserArray($ref, ($mode ? 0 : 1), $use_cache);
-
- /*list($ref, $fields, $mode) = $this->escape_input($ref, $fields, $mode);
-
- // update for Multihunter's username and ID
- if (($mode && $ref == '') || (!$mode && $ref == 0)) {
- $ref = 'Multihunter';
- $mode = 1;
- }
-
- // check fields one by one to see which ones we can return cached
- if ($use_cache) {
- $allFieldsFound = false;
- $fieldsLeft = [];
- $fieldValues = [];
-
- // split fields
- $fields = explode(',', str_replace(', ', ',', $fields));
-
- // iterate over all the fields and see what we have cached
- foreach ($fields as $fieldName) {
- if (($cached = self::returnCachedContent(self::$fieldsCache, $ref.$mode)) && !is_null($cached) && isset($cached[$fieldName])) {
- $fieldValues[$fieldName] = $cached[$fieldName];
- } else {
- $fieldsLeft[] = $fieldName;
- }
- }
-
- // check if we should return here (if we have all the values) or continue with the rest below
- if (!count($fieldsLeft)) {
- return $fieldValues;
- }
- }
-
- if(!$mode) {
- $q = "SELECT ".implode(', ', $fieldsLeft)." FROM " . TB_PREFIX . "users where id = " . (int) $ref;
- } else {
- $q = "SELECT ".implode(', ', $fieldsLeft)." FROM " . TB_PREFIX . "users where username = '$ref'";
- }
-
- $result = mysqli_query($this->dblink,$q) or die(mysqli_error($this->dblink));
- if($result) {
- $ret = mysqli_fetch_array($result, MYSQLI_ASSOC);
- } else {
- $ret = [0];
- }
-
- // cache results and return everything that we have
- foreach ($ret as $fieldName => $fieldValue) {
- $fieldValues[$fieldName] = $fieldValue;
- self::$fieldsCache[$ref.$mode][$fieldName] = $fieldValue;
- }
-
- return $fieldValues;*/
}
// no need to cache this method
@@ -1207,9 +1174,9 @@ public function getBestOasisCropBonus($x, $y) {
if(!$mode) $q = "SELECT * FROM " . TB_PREFIX . "users where username = '$ref' LIMIT 1";
else $q = "SELECT * FROM " . TB_PREFIX . "users where id = " . (int) $ref . " LIMIT 1";
- $result = mysqli_query($this->dblink,$q);
-
- self::$fieldsCache[$ref.$mode] = mysqli_fetch_array($result);
+ // ACTIVAT CACHE GENERIC
+ $cacheKey = "userarray_".$ref."_".$mode;
+ self::$fieldsCache[$ref.$mode] = $this->fetchCached($cacheKey, $q, function($res){ return mysqli_fetch_array($res, MYSQLI_ASSOC); });
return self::$fieldsCache[$ref.$mode];
}
@@ -1899,9 +1866,9 @@ public function getBestOasisCropBonus($x, $y) {
break;
}
- $result = mysqli_query($this->dblink,$q);
-
- self::$villageFieldsCache[$vid.$mode] = mysqli_fetch_array($result, MYSQLI_ASSOC);
+ // ACTIVAT CACHE GENERIC
+ $cacheKey = "village_".$vid."_".$mode;
+ self::$villageFieldsCache[$vid.$mode] = $this->fetchCached($cacheKey, $q, function($res){ return mysqli_fetch_array($res, MYSQLI_ASSOC); });
return self::$villageFieldsCache[$vid.$mode];
}
@@ -1978,7 +1945,7 @@ public function getBestOasisCropBonus($x, $y) {
$result = mysqli_query($this->dblink,$q);
if (!$arrayPassed) {
- $result = $this->mysqli_fetch_all($result);
+ $result = $this->mysqli_fetch_all($result);
self::$userVillagesCache[ $uid[0].$mode ] = $result;
// cache each village individually into the fields cache as well
@@ -2080,7 +2047,9 @@ public function getBestOasisCropBonus($x, $y) {
}
$q = "SELECT * FROM " . TB_PREFIX . "wdata where id IN(".implode(', ', $vid).")";
- $result = $this->mysqli_fetch_all(mysqli_query($this->dblink,$q));
+ // ACTIVAT CACHE GENERIC
+ $cacheKey = "wdata_".md5($q);
+ $result = $this->fetchCached($cacheKey, $q, function($res){ return $this->mysqli_fetch_all($res); });
// return a single value
if (!$array_passed) {
@@ -2200,30 +2169,11 @@ public function getBestOasisCropBonus($x, $y) {
}else $result = 0;
return $result;
-
- /*list($ref, $field) = $this->escape_input((int) $ref, $field);
-
- $q = "SELECT $field FROM " . TB_PREFIX . "vdata where wref = $ref";
- $result = mysqli_query($this->dblink,$q);
- if($result){
- $dbarray = mysqli_fetch_array($result);
- return $dbarray[$field];
- }elseif($field=="name"){
- return "??";
- }else return 0;*/
}
function getVillageFields($ref, $fields, $use_cache = true) {
// return all data, don't waste time by selecting fields one by one
return $this->getVillage($ref, 0, $use_cache);
-
- /*list($ref, $field) = $this->escape_input((int) $ref, $fields);
-
- $q = "SELECT $field FROM " . TB_PREFIX . "vdata where wref = $ref";
- $result = mysqli_query($this->dblink,$q);
- if($result) {
- return mysqli_fetch_array($result, MYSQLI_ASSOC);
- } else return 0;*/
}
function getOasisField($ref, $field, $use_cache = true) {
@@ -2235,11 +2185,6 @@ public function getBestOasisCropBonus($x, $y) {
function getOasisFields($ref, $use_cache = true) {
// return all data, don't waste time by selecting fields one by one
return $this->getOasisV($ref, $use_cache);
-
- /*list($ref, $fields) = $this->escape_input((int) $ref, $fields);
-
- $q = "SELECT $fields FROM " . TB_PREFIX . "odata where wref = $ref";
- return mysqli_fetch_array(mysqli_query($this->dblink,$q), MYSQLI_ASSOC);*/
}
function setVillageField($ref, $field, $value) {
@@ -2339,9 +2284,9 @@ public function getBestOasisCropBonus($x, $y) {
}
$q = "SELECT * from " . TB_PREFIX . "fdata where vref = $vid";
- $result = mysqli_query($this->dblink,$q);
-
- self::$resourceLevelsCache[$vid] = mysqli_fetch_assoc($result);
+ // ACTIVAT CACHE GENERIC
+ $cacheKey = "reslvl_".$vid;
+ self::$resourceLevelsCache[$vid] = $this->fetchCached($cacheKey, $q, function($res){ return mysqli_fetch_assoc($res); });
return self::$resourceLevelsCache[$vid];
}
@@ -3004,9 +2949,7 @@ public function getBestOasisCropBonus($x, $y) {
list($id) = $this->escape_input($id);
$qs = "DELETE from " . TB_PREFIX . "forum_topic where id = '$id'";
- // $q = "DELETE from ".TB_PREFIX."forum_post where topic = '$id'";//
- return mysqli_query($this->dblink,$qs); //
- // mysqli_query($this->dblink,$q);
+ return mysqli_query($this->dblink,$qs);
}
function DeletePost($id) {
@@ -3551,8 +3494,6 @@ public function getBestOasisCropBonus($x, $y) {
return self::$userAllianceCache[$id];
}
- /////////////ADDED BY BRAINIAC - THANK YOU
-
function modifyResource($vid, $wood, $clay, $iron, $crop, $mode) {
list($vid, $wood, $clay, $iron, $crop, $mode) = $this->escape_input((int) $vid, $wood, $clay, $iron, $crop, $mode);
$sign = (!$mode ? '-' : '+');
@@ -3834,8 +3775,9 @@ public function getBestOasisCropBonus($x, $y) {
OR (f40t = ".$field.($lvl !== false ? ' AND f40 '.$lvlComparisonSign.' '.$lvl : '').")
)";
- $result = mysqli_query($this->dblink,$q);
- $row = mysqli_fetch_array($result);
+ // ACTIVAT CACHE GENERIC
+ $cacheKey = "sftc_".$uid."_".$field."_".$lvlComparisonSign."_".($lvl===false?'x':$lvl);
+ $row = $this->fetchCached($cacheKey, $q, function($res){ return mysqli_fetch_array($res, MYSQLI_ASSOC); });
self::$singleFieldTypeCountCache[$uid.$field.$lvlComparisonSign.($lvl ? 1 : 0)] = $row["Total"];
return self::$singleFieldTypeCountCache[$uid.$field.$lvlComparisonSign.($lvl ? 1 : 0)];
@@ -4890,278 +4832,177 @@ References: User ID/Message ID, Mode
* and 0 when the player was evicted from
* the alliance due to Embassy damage.
*/
- public function checkAllianceEmbassiesStatus($userData, $demolition = false, $use_cache = true) {
- // TODO: refactor this and break it into more smaler methods
- //global $session;
+ public
+ /**
+ * REFACTORIZAT 14.05.2026 - împărțit în metode mici, păstrată logica originală
+ * Verifică statutul ambasadelor și gestionează ieșirea din alianță
+ */
+ function checkAllianceEmbassiesStatus($userData, $demolition = false, $use_cache = true) {
+ // fără alianță = nimic de făcut
+ if (empty($userData['alliance'])) {
+ return true;
+ }
- if ($userData['alliance']) {
- // check whether this player is an alliance owner
- $isOwner = ($userData['alliance'] && $this->isAllianceOwner($userData['id'], $use_cache) == $userData['alliance']);
+ $isOwner = ($this->isAllianceOwner($userData['id'], $use_cache) == $userData['alliance']);
+
+ if (!$isOwner) {
+ return $this->handleNonOwnerEmbassyCheck($userData, $demolition, $use_cache);
+ } else {
+ return $this->handleOwnerEmbassyCheck($userData, $demolition, $use_cache);
+ }
+ }
- $minimumExistingEmbassyRecords = 1;
+ // === METODE NOI EXTRAS DIN checkAllianceEmbassiesStatus ===
- // if they are not an alliance owner, simply check whether we have any Embassies
- // at lvl 1+ standing somewhere
- if (!$isOwner) {
- // TODO: replace magic numbers by constants (18 = Embassy)
- if ($this->getSingleFieldTypeCount($userData['id'], 18, '>=', 1, $use_cache) < $minimumExistingEmbassyRecords) {
+ private function handleNonOwnerEmbassyCheck(array $userData, bool $demolition, bool $use_cache) {
+ // constantă magică 18 = Embassy - păstrată pentru compatibilitate
+ $hasEmbassy = $this->getSingleFieldTypeCount($userData['id'], 18, '>=', 1, $use_cache) >= 1;
+
+ if ($hasEmbassy) {
+ return true; // are ambasadă, rămâne în alianță
+ }
- // the player has no more Embassies, evict them from the alliance
- mysqli_query($this->dblink, 'UPDATE '.TB_PREFIX.'users SET alliance = 0 WHERE id = '.$userData['id']);
+ // nu mai are ambasadă - evict
+ $this->evictUserFromAlliance($userData['id']);
+ $this->deleteAlliPermissions($userData['id']);
- // unset the alliance in session, if we're evicting
- // currently logged-in player
- //if ($session->uid == $userData['id']) {
- // $_SESSION['alliance_user'] = 0;
- //}
+ $msgTitle = $demolition ? 'You left the alliance' : 'An attack has forced you to leave the alliance';
+ $msgBody = $demolition
+ ? "Hi, ".$userData['username']."!\n\nThis is to inform you that due to a finished demolition of your last Embassy, you have now successfully left your alliance.\n\nYours sincerely,\nServer Robot :)"
+ : "Hi, ".$userData['username']."!\n\nThis is to inform you that due to a successful attack and destruction of your last Embassy, you have been forced to leave your alliance.\n\nTo re-establish your position in this alliance, you will need to build a new Embassy and ask the leader to send you an invite again.\n\nYours sincerely,\nServer Robot :)";
- // notify them via in-game messaging, if we come from a demolition,
- // otherwise return a result which can be used in battle reports
- if ($demolition) {
- $this->sendMessage(
- $userData['id'],
- 4,
- 'You left the alliance',
- $this->escape("Hi, ".$userData['username']."!\n\nThis is to inform you that due to a finished demolition of your last Embassy, you have now successfully left your alliance.\n\nYours sincerely,\nServer Robot :)"),
- 0,
- 0,
- 0,
- 0,
- 0,
- true);
- $this->deleteAlliPermissions($userData['id']);
- } else {
- // player has been removed from the alliance
- $this->sendMessage(
- $userData['id'],
- 4,
- 'An attack has forced you to leave the alliance',
- $this->escape("Hi, ".$userData['username']."!\n\nThis is to inform you that due to a successful attack and destruction of your last Embassy, you have been forced to leave your alliance.\n\nTo re-establish your position in this alliance, you will need to build a new Embassy and ask the leader to send you an invite again.\n\nYours sincerely,\nServer Robot :)"),
- 0,
- 0,
- 0,
- 0,
- 0,
- true);
- $this->deleteAlliPermissions($userData['id']);
- return 0;
- }
+ $this->sendMessage($userData['id'], 4, $msgTitle, $this->escape($msgBody), 0,0,0,0,0,true);
+
+ return $demolition ? true : 0;
+ }
- }
- } else {
- // the player IS an alliance owner, check if we need to take any action
- $membersCount = $this->countAllianceMembers($userData['alliance'], $use_cache);
- $minAllianceEmbassyLevel = $this->getMinEmbassyLevel($membersCount);
+ private function handleOwnerEmbassyCheck(array $userData, bool $demolition, bool $use_cache) {
+ $membersCount = $this->countAllianceMembers($userData['alliance'], $use_cache);
+ $minLevel = max(3, $this->getMinEmbassyLevel($membersCount)); // liderul are nevoie minim nivel 3
- // in this case, the minimum Embassy level cannot go below 3,
- // since this player is a leader and as such, he needs at least
- // a level 3 Embassy
- if ($minAllianceEmbassyLevel < 3) {
- $minAllianceEmbassyLevel = 3;
- }
+ $hasSufficientEmbassy = $this->getSingleFieldTypeCount($userData['id'], 18, '>=', $minLevel, false, $use_cache) >= 1;
+ $needsAction = ($userData['lvl'] <= $minLevel) && !$hasSufficientEmbassy;
- $takeAction = (
- // was the Embassy taken below a threshold level?
- ($userData['lvl'] <= $minAllianceEmbassyLevel)
- &&
- // check for standing Embassies with sufficient level
- // TODO: replace magic numbers by constants (18 = Embassy)
- ($this->getSingleFieldTypeCount($userData['id'], 18, '>=', $minAllianceEmbassyLevel, false, $use_cache) < $minimumExistingEmbassyRecords)
- );
+ if (!$needsAction) {
+ return true;
+ }
- // the Embassy got damaged below a sufficient level and there are no more Embassies
- // at that level standing on this player's account, additional actions are needed
- if ($takeAction) {
+ $members = $this->getAllMember($userData['alliance'], 0, $use_cache);
- // load all alliance members
- $members = $this->getAllMember($userData['alliance'], 0, $use_cache);
+ if ($demolition) {
+ return $this->disbandAllianceOnDemolition($userData, $members);
+ } else {
+ return $this->handleOwnerAttackLoss($userData, $members, $minLevel, $membersCount, $use_cache);
+ }
+ }
- // if we come from demolition, we need to evict all new members
- // that accepted an invitation while level 3 of the last
- // Embassy was already under demolition. The demolition dialog itself
- // already checks if there are no more people other than the owner
- // present before the demolition is allowed.
- if ($demolition) {
- $evicts = [];
- foreach ($members as $member) {
- // evict the player from the alliance
- $evicts[] = $member['id'];
+ public function evictUserFromAlliance(int $uid): void {
+ $this->query('UPDATE '.TB_PREFIX.'users SET alliance = 0 WHERE id = '.$uid);
+ $this->clearQueryCache('alliance'); // invalidăm cache-ul de alianță
+ }
- // notify them via in-game messaging
- $this->sendMessage(
- $member['id'],
- 4,
- 'Your alliance was disbanded',
- (
- ($member['id'] == $userData['id'])
- ?
- $this->escape("Hi, ".$userData['username']."!\n\nThis is to inform you that due to a finished demolition of your last Embassy at level 3, and the fact that you were the leader of your alliance, this alliance has been disbanded.\n\nIn order to found a new alliance, please build a level 3 Embassy again in one of your villages.\n\nYours sincerely,\nServer Robot :)")
- :
- $this->escape("Hi, ".$member['username']."!\n\nThis is to inform you that due to a demolition of your alliance founder's last Embassy below level 3, this alliance has been disbanded.\n\n\You can now accept invitations from other alliances or found a new alliance yourself.\n\nYours sincerely,\nServer Robot :)")
- ),
- 0,
- 0,
- 0,
- 0,
- 0,
- true);
- $this->deleteAlliPermissions($member['id']);
- }
+ private function disbandAllianceOnDemolition(array $ownerData, array $members): bool {
+ $evicts = [];
+ foreach ($members as $member) {
+ $evicts[] = $member['id'];
+ $isOwner = ($member['id'] == $ownerData['id']);
+ $title = 'Your alliance was disbanded';
+ $body = $isOwner
+ ? "Hi, ".$ownerData['username']."!\n\nThis is to inform you that due to a finished demolition of your last Embassy at level 3, and the fact that you were the leader of your alliance, this alliance has been disbanded.\n\nIn order to found a new alliance, please build a level 3 Embassy again in one of your villages.\n\nYours sincerely,\nServer Robot :)"
+ : "Hi, ".$member['username']."!\n\nThis is to inform you that due to a demolition of your alliance founder's last Embassy below level 3, this alliance has been disbanded.\n\nYou can now accept invitations from other alliances or found a new alliance yourself.\n\nYours sincerely,\nServer Robot :)";
+
+ $this->sendMessage($member['id'], 4, $title, $this->escape($body), 0,0,0,0,0,true);
+ $this->deleteAlliPermissions($member['id']);
+ }
+ if ($evicts) {
+ $this->query('UPDATE '.TB_PREFIX.'users SET alliance = 0 WHERE id IN('.implode(',', $evicts).')');
+ }
+ $this->deleteAlliance($ownerData['alliance']);
+ return true;
+ }
- mysqli_query($this->dblink, 'UPDATE '.TB_PREFIX.'users SET alliance = 0 WHERE id IN('.implode(',', $evicts).")");
- } else {
- // we come from a battle result, therefore we need to check
- // for the first player in the alliance who has a sufficient
- // level Embassy and to which we can auto-reassign the leadership
- $newLeaderFound = false;
-
- // in case we'll need these later to disband the alliance,
- // we'll collect them inside this foeach loop
- $memberIDs = [];
-
- // no need for this whole foreach loop if this player is the lone
- // founder and member of their alliance
- if ($membersCount > 1) {
- foreach ($members as $member) {
- if (!$newLeaderFound && $this->getSingleFieldTypeCount($member['id'], 18, '>=', $minAllianceEmbassyLevel) >= 1) {
- // found a new leader for the alliance
- $newLeaderFound = true;
- $newleader = $member['id'];
- $q = "UPDATE " . TB_PREFIX . "alidata set leader = ".(int) $newleader." where id = ".(int) $userData['alliance'];
- $this->query($q);
- $this->updateAlliPermissions($newleader, $userData['alliance'], "Leader", 1, 1, 1, 1, 1, 1, 1);
- Automation::updateMax($newleader);
-
- // update permissions for the old leader
- $this->updateAlliPermissions($userData['id'], $userData['alliance'], "Former Leader", 0, 0, 0, 0, 0, 0, 0);
-
- // notify new leader via in-game messaging
- $this->sendMessage(
- $newleader,
- 4,
- 'You are now the alliance leader',
- $this->escape("Hi, ".$member['username']."!\n\nThis is to inform you that there was a successful attack on player ".$userData['username']." which has damaged their Embassy badly enough that they are no longer able to sustain the leadership of your alliance.\n\nSince your Embassy level is of a sufficient level, you have been auto-elected to the position of a new leader of your alliance with all duties and responsibilities thereof.\n\nYours sincerely,\nServer Robot :)"),
- 0,
- 0,
- 0,
- 0,
- 0,
- true);
- }
-
- $memberIDs[] = $member['id'];
- }
- } else {
- // if there is only 1 member and it's the actual founder
- $memberIDs[] = $userData['id'];
- }
-
- // if there wasn't anyone with a sufficient level of Embassy
- // among the existing members, disperse this alliance
- if (!$newLeaderFound) {
-
- // evict all members from the alliance
- mysqli_query($this->dblink, 'UPDATE '.TB_PREFIX.'users SET alliance = 0 WHERE id IN('.implode(',', $memberIDs).")");
-
- // notify all of them via in-game messaging
- foreach ($members as $member) {
- $this->sendMessage(
- $member['id'],
- 4,
- 'Your alliance was dispersed',
- (
- ($member['id'] == $userData['id'])
- ?
- $this->escape("Hi, ".$userData['username']."!\n\nThis is to inform you that due to a successful attack that has degraded your last Embassy to a level ".($membersCount > 1 ? "which is unable to hold all ".$membersCount." alliance members, and because there was no other alliance member with an Embassy on a high enough level to overtake the leadership," : "lower then 3 - which is required to found and hold your own alliance - ")." your alliance has been dispersed.\n\nYours sincerely,\nServer Robot :)")
- :
- $this->escape("Hi, ".$member['username']."!\n\nThis is to inform you that due to a successful attack on your alliance leader's Embassy by another player that degraded it below threshold allowed to hold all ".$membersCount." alliance members, and because there was no other alliance member with an Embassy on a high enough level to overtake the leadership, your alliance has been dispersed.\n\nYours sincerely,\nServer Robot :)")
- ),
- 0,
- 0,
- 0,
- 0,
- 0,
- true);
- }
- $this->deleteAlliPermissions($member['id']);
- } else {
- // let's determine whether to keep currently attacked player
- // in the alliance or not
- if ($userData['lvl'] > 0 || $this->getSingleFieldTypeCount($member['id'], 18, '>=', 1, $use_cache) >= $minimumExistingEmbassyRecords) {
- $keepCurrentPlayer = true;
- } else {
- $keepCurrentPlayer = false;
- }
-
- // if a new leader was found, notify all alliance member of this change
- // notify all of them via in-game messaging
- foreach ($members as $member) {
- // don't send duplicate messages to the new leader
- if ($member['id'] != $newleader) {
- // also, don't send to the attacked player if we're
- // not keeping them in alliance
- if ($keepCurrentPlayer || (!$keepCurrentPlayer && $member['id'] != $userData['id']))
- $this->sendMessage(
- $member['id'],
- 4,
- 'Your alliance has a new leader',
- (
- ($member['id'] == $userData['id'])
- ?
- $this->escape("Hi, ".$userData['username']."!\n\nThis is to inform you that due to a successful attack that has degraded your last Embassy to a level which is unable to hold all ".$membersCount." alliance members, another alliance member who meets these criteria has been auto-elected as a new alliance leader.\n\nAdditionally - due to the Embassy destruction - you have been forcefuly evicted from your alliance.\n\nPlease re-establish the connection with your alliance by building a new Embassy and contacting the new leader for an invitation.\n\nYours sincerely,\nServer Robot :)")
- :
- $this->escape("Hi, ".$member['username']."!\n\nThis is to inform you that due to a successful attack on your alliance leader's Embassy by another player, another alliance member with enough Embassy capacity has been auto-elected as the new alliance leader.\n\nYours sincerely,\nServer Robot :)")
- ),
- 0,
- 0,
- 0,
- 0,
- 0,
- true);
- }
- $this->deleteAlliPermissions($member['id']);
- }
-
- // evict current player from the alliance
- // if this was their last Embassy and was completely destroyed
- if (!$keepCurrentPlayer) {
- mysqli_query($this->dblink, 'UPDATE '.TB_PREFIX.'users SET alliance = 0 WHERE id = '.$userData['id']);
-
- // unset the alliance in session, if we're evicting
- // currently logged-in player
- if ($session->uid == $userData['id']) {
- $_SESSION['alliance_user'] = 0;
- }
-
- // notify the evicted player
- $this->sendMessage(
- $userData['id'],
- 4,
- 'An attack has forced you to leave the alliance',
- $this->escape("Hi, ".$userData['username']."!\n\nThis is to inform you that due to a successful attack and destruction of your last Embassy, you have been forced to leave your alliance.\n\nTo re-establish your position in this alliance, you will need to build a new Embassy and ask the newly auto-elected leader to send you an invite again.\n\nYours sincerely,\nServer Robot :)"),
- 0,
- 0,
- 0,
- 0,
- 0,
- true);
- }
- $this->deleteAlliPermissions($userData['id']);
- }
- }
-
- // execute a method that will delete an alliance
- // if no members are left in it
- $this->deleteAlliance($userData['alliance']);
-
- return isset($newLeaderFound) && $newLeaderFound === true;
+ private function handleOwnerAttackLoss(array $ownerData, array $members, int $minLevel, int $membersCount, bool $use_cache): bool {
+ $newLeaderId = null;
+
+ // căutăm primul membru cu ambasadă suficientă
+ if ($membersCount > 1) {
+ foreach ($members as $member) {
+ if ($this->getSingleFieldTypeCount($member['id'], 18, '>=', $minLevel) >= 1) {
+ $newLeaderId = (int)$member['id'];
+ $this->promoteNewAllianceLeader($ownerData['alliance'], $newLeaderId, $ownerData['id'], $member['username'], $ownerData);
+ break;
}
}
}
- // no changes in player-to-alliance relationship
- return true;
- }
+ if (!$newLeaderId) {
+ // niciun lider eligibil - dispersăm alianța
+ return $this->disperseAllianceNoLeader($ownerData, $members, $membersCount);
+ }
+
+ // avem lider nou - notificăm și eventual evictăm ownerul vechi
+ return $this->notifyLeaderChange($ownerData, $members, $newLeaderId, $minLevel, $use_cache);
+ }
+
+ public function promoteNewAllianceLeader(int $allyId, int $newLeaderId, int $oldLeaderId, string $newLeaderName, array $oldLeaderData, ?string $customMessage = null): void {
+ $this->query("UPDATE ".TB_PREFIX."alidata SET leader = $newLeaderId WHERE id = $allyId");
+ $this->updateAlliPermissions($newLeaderId, $allyId, "Leader", 1,1,1,1,1,1,1);
+ if (class_exists('Automation')) { Automation::updateMax($newLeaderId); }
+ $this->updateAlliPermissions($oldLeaderId, $allyId, "Former Leader", 0,0,0,0,0,0,0);
+
+ if ($customMessage === null) {
+ $msg = "Hi, $newLeaderName!\n\nThis is to inform you that there was a successful attack on player ".$oldLeaderData['username']." which has damaged their Embassy badly enough that they are no longer able to sustain the leadership of your alliance.\n\nSince your Embassy level is of a sufficient level, you have been auto-elected to the position of a new leader of your alliance with all duties and responsibilities thereof.\n\nYours sincerely,\nServer Robot :)";
+ $title = 'You are now the alliance leader';
+ } else {
+ $msg = $customMessage;
+ $title = 'You are now leader of your alliance';
+ }
+ $this->sendMessage($newLeaderId, 4, $title, $this->escape($msg), 0,0,0,0,0,true);
+ $this->clearQueryCache('alliance');
+ }
+
+ private function disperseAllianceNoLeader(array $ownerData, array $members, int $membersCount): bool {
+ $ids = array_column($members, 'id');
+ if ($ids) {
+ $this->query('UPDATE '.TB_PREFIX.'users SET alliance = 0 WHERE id IN('.implode(',', $ids).')');
+ }
+ foreach ($members as $m) {
+ $isOwner = ($m['id'] == $ownerData['id']);
+ $title = 'Your alliance was dispersed';
+ $body = $isOwner
+ ? "Hi, ".$ownerData['username']."!\n\nThis is to inform you that due to a successful attack that has degraded your last Embassy to a level ".($membersCount>1?"which is unable to hold all $membersCount alliance members, and because there was no other alliance member with an Embassy on a high enough level to overtake the leadership,":"lower then 3 - which is required to found and hold your own alliance - ")." your alliance has been dispersed.\n\nYours sincerely,\nServer Robot :)"
+ : "Hi, ".$m['username']."!\n\nThis is to inform you that due to a successful attack on your alliance leader's Embassy by another player that degraded it below threshold allowed to hold all $membersCount alliance members, and because there was no other alliance member with an Embassy on a high enough level to overtake the leadership, your alliance has been dispersed.\n\nYours sincerely,\nServer Robot :)";
+ $this->sendMessage($m['id'], 4, $title, $this->escape($body), 0,0,0,0,0,true);
+ $this->deleteAlliPermissions($m['id']);
+ }
+ $this->deleteAlliance($ownerData['alliance']);
+ return false;
+ }
+
+ private function notifyLeaderChange(array $ownerData, array $members, int $newLeaderId, int $minLevel, bool $use_cache): bool {
+ $keepOwner = ($ownerData['lvl'] > 0) || ($this->getSingleFieldTypeCount($ownerData['id'], 18, '>=', 1, $use_cache) >= 1);
+
+ foreach ($members as $m) {
+ if ($m['id'] == $newLeaderId) continue;
+ if (!$keepOwner && $m['id'] == $ownerData['id']) continue;
+
+ $isOwner = ($m['id'] == $ownerData['id']);
+ $title = 'Your alliance has a new leader';
+ $body = $isOwner
+ ? "Hi, ".$ownerData['username']."!\n\nThis is to inform you that due to a successful attack that has degraded your last Embassy to a level which is unable to hold all ".count($members)." alliance members, another alliance member who meets these criteria has been auto-elected as a new alliance leader.\n\nAdditionally - due to the Embassy destruction - you have been forcefuly evicted from your alliance.\n\nPlease re-establish the connection with your alliance by building a new Embassy and contacting the new leader for an invitation.\n\nYours sincerely,\nServer Robot :)"
+ : "Hi, ".$m['username']."!\n\nThis is to inform you that due to a successful attack on your alliance leader's Embassy by another player, another alliance member with enough Embassy capacity has been auto-elected as the new alliance leader.\n\nYours sincerely,\nServer Robot :)";
+ $this->sendMessage($m['id'], 4, $title, $this->escape($body), 0,0,0,0,0,true);
+ }
+
+ if (!$keepOwner) {
+ $this->evictUserFromAlliance($ownerData['id']);
+ $msg = "Hi, ".$ownerData['username']."!\n\nThis is to inform you that due to a successful attack and destruction of your last Embassy, you have been forced to leave your alliance.\n\nTo re-establish your position in this alliance, you will need to build a new Embassy and ask the newly auto-elected leader to send you an invite again.\n\nYours sincerely,\nServer Robot :)";
+ $this->sendMessage($ownerData['id'], 4, 'An attack has forced you to leave the alliance', $this->escape($msg), 0,0,0,0,0,true);
+ }
+ $this->deleteAlliance($ownerData['alliance']);
+ return true;
+ }
+
function checkEmbassiesAfterBattle($vid, $current_level, $use_cache = true) {
$userData = $this->getUserArray($this->getVillageField($vid, "owner"), 1);
diff --git a/GameEngine/Form.php b/GameEngine/Form.php
index d15a7124..cc61fba7 100755
--- a/GameEngine/Form.php
+++ b/GameEngine/Form.php
@@ -6,7 +6,7 @@
## Filename Form.php ##
## License: TravianZ Project ##
## Copyright: TravianZ (c) 2010-2026. All rights reserved. ##
-## Refactored by: Shadow ##
+## Refactored by: Shadow (cata7007@gmail.com) ##
## ##
## Refactor: Incremental cleanup (compatibility preserved) ##
## Notes: PHP 7+ / legacy safe ##
diff --git a/GameEngine/Generator.php b/GameEngine/Generator.php
index e8a78e70..185d3c72 100755
--- a/GameEngine/Generator.php
+++ b/GameEngine/Generator.php
@@ -7,7 +7,7 @@
## Version: 18.05.2026 ##
## Filename: Generator.php ##
## Developed by: Dzoki ##
-## Refactored by: Shadow ##
+## Refactored by: Shadow (cata7007@gmail.com) ##
## License: TravianZ Project ##
## Copyright: TravianZ (c) 2010-2026. All rights reserved. ##
## ##
diff --git a/GameEngine/Logging.php b/GameEngine/Logging.php
index 2b235471..ccb5df34 100755
--- a/GameEngine/Logging.php
+++ b/GameEngine/Logging.php
@@ -7,7 +7,7 @@
## Version: 12.05.2026 ##
## Filename: Logging.php ##
## Developed by: Shadow ##
-## Refactored by: Shadow ##
+## Refactored by: Shadow (cata7007@gmail.com) ##
## License: TravianZ Project ##
## Copyright: TravianZ (c) 2010-2026. All rights reserved. ##
## ##
diff --git a/GameEngine/Market.php b/GameEngine/Market.php
index 9e8bd712..7fda94dd 100755
--- a/GameEngine/Market.php
+++ b/GameEngine/Market.php
@@ -5,7 +5,7 @@
## Filename Market.php ##
## Developed by: Dzoki ##
## Some fixes: aggenkeech ##
-## Refactored by: Shadow ##
+## Refactored by: Shadow (cata7007@gmail.com) ##
## License: TravianZ Project ##
## Copyright: TravianZ (c) 2010-2026. All rights reserved. ##
## URLs: https://travianz.org ##
diff --git a/GameEngine/Profile.php b/GameEngine/Profile.php
index c2d602db..5a8519fc 100755
--- a/GameEngine/Profile.php
+++ b/GameEngine/Profile.php
@@ -6,7 +6,7 @@
## Filename Profile.php ##
## Filename: Account.php ##
## Developed by: Dzoki ##
-## Refactored by: Shadow ##
+## Refactored by: Shadow (cata7007@gmail.com) ##
## License: TravianZ Project ##
## Copyright: TravianZ (c) 2010-2026. All rights reserved. ##
## URLs: https://travianz.org ##
diff --git a/version.php b/version.php
index e392e01c..d44a2234 100644
--- a/version.php
+++ b/version.php
@@ -86,10 +86,10 @@ $developers = [
["Shadow", "Project Owner"],
["Advocaite", "Developer"],
["yi12345", "Alumni Developer"],
- ["NarcisRO", "bug hunter"],
+ ["iopietro", "Alumni Developer"],
["brainiacX", "Alumni Developer"],
["InCube", "Alumni Developer"],
- ["akshay9", "Alumni Developer"],
+ ["martinambrus", "Alumni Developer"],
["KFCSpike", "Alumni Developer"],
["nean", "Alumni Developer"],
["hexcoded", "Alumni Developer"],
@@ -114,8 +114,8 @@ $developers = [
["aggenkeech", "Alumni Developer"],
["Niko28", "Alumni Developer"],
["221V", "Developer"],
- ["martinambrus", "Alumni Developer"],
- ["iopietro", "Alumni Developer"],
+ ["akshay9", "Alumni Developer"],
+ ["NarcisRO", "Bug Hunter"],
["Vladyslav", "Rigorous game tester"],
["AL-Kateb", "Alumni Developer"],
["hdmaniak2", " Active Developer"],