mirror of
https://github.com/Shadowss/TravianZ.git
synced 2026-07-16 09:36:08 +00:00
chore: harden php8 compatibility, optimize installer/croppers, and refresh docs/admin ux
This commit is contained in:
@@ -1192,7 +1192,7 @@ class Automation {
|
||||
if($traps >= $totalTroops){
|
||||
for($i = 1; $i <= 11; $i++) ${'traped'.$i} = $data['t'.$i];
|
||||
}
|
||||
else
|
||||
else if($totalTroops > 0)
|
||||
{
|
||||
$multiplier = $traps / $totalTroops;
|
||||
|
||||
@@ -1213,6 +1213,9 @@ class Automation {
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for($i = 1; $i <= 11; $i++) ${'traped'.$i} = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($scout) || $NatarCapital){
|
||||
|
||||
+12
-10
@@ -477,6 +477,8 @@ class Battle {
|
||||
$result['Attack_points'] = $rap;
|
||||
$result['Defend_points'] = $rdp;
|
||||
$winner = ($rap > $rdp);
|
||||
$safeRap = max(1, (float) $rap);
|
||||
$safeRdp = max(1, (float) $rdp);
|
||||
|
||||
// Formula for calculating the Morale bonus
|
||||
// WW villages aren't affected by this bonus
|
||||
@@ -495,7 +497,7 @@ class Battle {
|
||||
// $type = 1 Scout, 2 Enforcement
|
||||
// $type = 3 Normal, 4 Raid
|
||||
if($type == 1){
|
||||
$holder = pow((($rdp * $moralbonus) / $rap), $Mfactor);
|
||||
$holder = pow((($rdp * $moralbonus) / $safeRap), $Mfactor);
|
||||
if($holder > 1) $holder = 1;
|
||||
if ($rdp > $rap) $holder = 1;
|
||||
|
||||
@@ -507,7 +509,7 @@ class Battle {
|
||||
//Defender result
|
||||
$result[2] = 0;
|
||||
}else if($type == 4) {
|
||||
$holder = ($winner) ? pow((($rdp * $moralbonus) / $rap), $Mfactor) : pow(($rap / ($rdp * $moralbonus)), $Mfactor);
|
||||
$holder = ($winner) ? pow((($rdp * $moralbonus) / $safeRap), $Mfactor) : pow(($safeRap / max($safeRdp * $moralbonus, 1)), $Mfactor);
|
||||
$holder = $holder / (1 + $holder);
|
||||
//Attacker result
|
||||
$result[1] = $winner ? $holder : 1 - $holder;
|
||||
@@ -518,7 +520,7 @@ class Battle {
|
||||
}else if($type == 3){
|
||||
|
||||
// Attacker
|
||||
$result[1] = ($winner) ? pow((($rdp * $moralbonus) / $rap), $Mfactor) : 1;
|
||||
$result[1] = ($winner) ? pow((($rdp * $moralbonus) / $safeRap), $Mfactor) : 1;
|
||||
|
||||
if ($result[1] > 1){
|
||||
$result[1] = 1;
|
||||
@@ -527,9 +529,9 @@ class Battle {
|
||||
}
|
||||
|
||||
// Defender
|
||||
$result[2] = (!$winner) ? pow(($rap / ($rdp * $moralbonus)), $Mfactor) : 1;
|
||||
$result[2] = (!$winner) ? pow(($safeRap / max($safeRdp * $moralbonus, 1)), $Mfactor) : 1;
|
||||
|
||||
if ($result[1] == 1) $result[2] = pow(($rap / ($rdp * $moralbonus)), $Mfactor);
|
||||
if ($result[1] == 1) $result[2] = pow(($safeRap / max($safeRdp * $moralbonus, 1)), $Mfactor);
|
||||
|
||||
if ($result[2] > 1) {
|
||||
$result[2] = 1;
|
||||
@@ -569,14 +571,14 @@ class Battle {
|
||||
$catpMoraleBonus = min(max(($attpop / ($defpop > 0 ? $defpop : 1)) ** 0.3, 1), 3);
|
||||
|
||||
//New level of the building (only for warsim.php)
|
||||
$catapultsDamage = $this->calculateCatapultsDamage($catp, $upgrades, $durability, $rap / $rdp, $strongerbuildings, $catpMoraleBonus);
|
||||
$catapultsDamage = $this->calculateCatapultsDamage($catp, $upgrades, $durability, $safeRap / $safeRdp, $strongerbuildings, $catpMoraleBonus);
|
||||
$result[3] = $this->calculateNewBuildingLevel($tblevel, $catapultsDamage);
|
||||
$result[4] = $tblevel;
|
||||
|
||||
//Results for Automation.php
|
||||
$result['catapults']['upgrades'] = $upgrades;
|
||||
$result['catapults']['durability'] = $durability;
|
||||
$result['catapults']['attackDefenseRatio'] = $rap / $rdp;
|
||||
$result['catapults']['attackDefenseRatio'] = $safeRap / $safeRdp;
|
||||
$result['catapults']['strongerBuildings'] = $strongerbuildings;
|
||||
$result['catapults']['moraleBonus'] = $catpMoraleBonus;
|
||||
}
|
||||
@@ -590,19 +592,19 @@ class Battle {
|
||||
$durability = ($stonemason > 0 ? $bid34[$stonemason]['attri'] / 100 : 1);
|
||||
|
||||
// New level of the building (only for warsim.php)
|
||||
$ramsDamage = $this->calculateCatapultsDamage($ram, $upgrades, $durability, $rap / $rdp, $strongerbuildings, 1);
|
||||
$ramsDamage = $this->calculateCatapultsDamage($ram, $upgrades, $durability, $safeRap / $safeRdp, $strongerbuildings, 1);
|
||||
$result[7] = $this->calculateNewBuildingLevel($walllevel, $ramsDamage);
|
||||
$result[8] = $walllevel;
|
||||
|
||||
// Results for Automation.php
|
||||
$result['rams']['upgrades'] = $upgrades;
|
||||
$result['rams']['durability'] = $durability;
|
||||
$result['rams']['attackDefenseRatio'] = $rap / $rdp;
|
||||
$result['rams']['attackDefenseRatio'] = $safeRap / $safeRdp;
|
||||
$result['rams']['strongerBuildings'] = $strongerbuildings;
|
||||
$result['rams']['moraleBonus'] = 1;
|
||||
}
|
||||
|
||||
$result[6] = pow($rap / ($rdp * $moralbonus > 0 ? $rdp * $moralbonus : 1), $Mfactor);
|
||||
$result[6] = pow($safeRap / max($safeRdp * $moralbonus, 1), $Mfactor);
|
||||
$result['moralBonus'] = $moralbonus;
|
||||
|
||||
$total_att_units = count($units['Att_unit']);
|
||||
|
||||
@@ -865,10 +865,14 @@ class Building {
|
||||
$rclay = $uprequire['clay'] - $village->aclay;
|
||||
$rcrop = $uprequire['crop'] - $village->acrop;
|
||||
$riron = $uprequire['iron'] - $village->airon;
|
||||
$rwtime = $rwood / $village->getProd("wood") * 3600;
|
||||
$rcltime = $rclay / $village->getProd("clay") * 3600;
|
||||
$rctime = $rcrop / $village->getProd("crop") * 3600;
|
||||
$ritime = $riron / $village->getProd("iron") * 3600;
|
||||
$woodProd = $village->getProd("wood");
|
||||
$clayProd = $village->getProd("clay");
|
||||
$cropProd = $village->getProd("crop");
|
||||
$ironProd = $village->getProd("iron");
|
||||
$rwtime = ($rwood <= 0) ? 0 : ($woodProd > 0 ? $rwood / $woodProd * 3600 : 0);
|
||||
$rcltime = ($rclay <= 0) ? 0 : ($clayProd > 0 ? $rclay / $clayProd * 3600 : 0);
|
||||
$rctime = ($rcrop <= 0) ? 0 : ($cropProd > 0 ? $rcrop / $cropProd * 3600 : 0);
|
||||
$ritime = ($riron <= 0) ? 0 : ($ironProd > 0 ? $riron / $ironProd * 3600 : 0);
|
||||
$reqtime = max($rwtime, $rctime, $rcltime, $ritime);
|
||||
$reqtime += time();
|
||||
return $generator->procMtime($reqtime);
|
||||
|
||||
+261
-128
@@ -434,28 +434,40 @@ class MYSQLi_DB implements IDbConnection {
|
||||
* @see \App\Database\IDbConnection::connect()
|
||||
*/
|
||||
public function connect() {
|
||||
// try to connect
|
||||
try {
|
||||
$this->dblink = mysqli_connect( $this->hostname, $this->username, $this->password, $this->dbname, $this->port );
|
||||
} catch (\Exception $exception) {
|
||||
$this->dblink = mysqli_connect( $this->hostname . ':' . $this->port, $this->username, $this->password );
|
||||
$host = (string) $this->hostname;
|
||||
$port = (int) $this->port;
|
||||
if ($port <= 0) $port = 3306;
|
||||
|
||||
// return on error
|
||||
if (mysqli_error($this->dblink)) {
|
||||
return false;
|
||||
// If host is in form host:port (common in env files), split it once.
|
||||
if (strpos($host, ':') !== false && substr_count($host, ':') === 1) {
|
||||
$hostPort = explode(':', $host, 2);
|
||||
if (isset($hostPort[0], $hostPort[1]) && is_numeric($hostPort[1])) {
|
||||
$host = $hostPort[0];
|
||||
$port = (int) $hostPort[1];
|
||||
}
|
||||
|
||||
// select the DB to use
|
||||
mysqli_select_db($this->dblink, $this->dbname);
|
||||
}
|
||||
|
||||
// return on error
|
||||
if (mysqli_error($this->dblink)) {
|
||||
return false;
|
||||
} else {
|
||||
// connected and DB exists, we're good to go
|
||||
return true;
|
||||
}
|
||||
$attempts = 15;
|
||||
$waitMicros = 1000000; // 1 second
|
||||
|
||||
for ($i = 0; $i < $attempts; $i++) {
|
||||
try {
|
||||
$this->dblink = mysqli_connect($host, $this->username, $this->password, $this->dbname, $port);
|
||||
} catch (\Throwable $exception) {
|
||||
$this->dblink = false;
|
||||
}
|
||||
|
||||
if ($this->dblink instanceof \mysqli) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// During container startup DB may still be booting.
|
||||
if ($i < $attempts - 1) {
|
||||
usleep($waitMicros);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1162,7 +1174,7 @@ public function getBestOasisCropBonus($x, $y) {
|
||||
$q = "SELECT timestamp from " . TB_PREFIX . "deleting where uid = $uid LIMIT 1";
|
||||
$result = mysqli_query($this->dblink,$q);
|
||||
$dbarray = mysqli_fetch_array($result);
|
||||
return $dbarray['timestamp'];
|
||||
return isset($dbarray['timestamp']) ? (int)$dbarray['timestamp'] : 0;
|
||||
}
|
||||
|
||||
function modifyGold($userid, $amt, $mode) {
|
||||
@@ -1728,10 +1740,10 @@ public function getBestOasisCropBonus($x, $y) {
|
||||
function getVillageState($wref, $use_cache = true) {
|
||||
// retrieve this value from the full village data cache
|
||||
if ($use_cache && ($cachedValue = self::returnCachedContent(self::$villageFieldsCacheByWorldID, $wref)) && !is_null($cachedValue)) {
|
||||
return ($cachedValue['occupied'] != 0 || $cachedValue['oasistype'] != 0);
|
||||
return (((int)($cachedValue['occupied'] ?? 0)) != 0 || ((int)($cachedValue['oasistype'] ?? 0)) != 0);
|
||||
} else {
|
||||
$vil = $this->getVillageByWorldID($wref, $use_cache);
|
||||
return ($vil['occupied'] != 0 || $vil['oasistype'] != 0);
|
||||
return (((int)($vil['occupied'] ?? 0)) != 0 || ((int)($vil['oasistype'] ?? 0)) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2075,7 +2087,7 @@ public function getBestOasisCropBonus($x, $y) {
|
||||
|
||||
// return a single value
|
||||
if (!$array_passed) {
|
||||
self::$villageFieldsCacheByWorldID[$vid[0]] = $result[0];
|
||||
self::$villageFieldsCacheByWorldID[$vid[0]] = (isset($result[0]) && is_array($result[0])) ? $result[0] : [];
|
||||
} else {
|
||||
if ($result && count($result)) {
|
||||
foreach ( $result as $record ) {
|
||||
@@ -3715,7 +3727,7 @@ public function getBestOasisCropBonus($x, $y) {
|
||||
'LIMIT 1');
|
||||
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
|
||||
|
||||
self::$fieldLevelsInVillageSearchCache[$vid.$fieldType] = $row['level'];
|
||||
self::$fieldLevelsInVillageSearchCache[$vid.$fieldType] = isset($row['level']) ? (int)$row['level'] : 0;
|
||||
return self::$fieldLevelsInVillageSearchCache[$vid.$fieldType];
|
||||
}
|
||||
|
||||
@@ -3730,9 +3742,9 @@ public function getBestOasisCropBonus($x, $y) {
|
||||
|
||||
$q = "SELECT f" . $field . " from " . TB_PREFIX . "fdata where vref = $vid LIMIT 1";
|
||||
$result = mysqli_query($this->dblink,$q);
|
||||
$row = mysqli_fetch_array($result);
|
||||
$row = $result ? mysqli_fetch_array($result, MYSQLI_ASSOC) : null;
|
||||
|
||||
self::$resourceLevelsCache[$vid.$field] = $row["f" . $field];
|
||||
self::$resourceLevelsCache[$vid.$field] = (is_array($row) && array_key_exists("f".$field, $row)) ? (int)$row["f" . $field] : 0;
|
||||
return self::$resourceLevelsCache[$vid.$field];
|
||||
}
|
||||
|
||||
@@ -5874,22 +5886,36 @@ References: User ID/Message ID, Mode
|
||||
*/
|
||||
|
||||
function addUnits($vid, $troopsArray = null) {
|
||||
list($vid, $type, $values) = $this->escape_input($vid, $type, $values);
|
||||
list($vid) = $this->escape_input($vid);
|
||||
|
||||
if(empty($vid)) return;
|
||||
if (!is_array($vid)) $vid = [$vid];
|
||||
$types = $values = "";
|
||||
$types = "";
|
||||
$typeKeys = [];
|
||||
$values = [];
|
||||
|
||||
if($troopsArray != null){
|
||||
$types = $troopsArray[0];
|
||||
$typeKeys = $troopsArray[0];
|
||||
$values = $troopsArray[1];
|
||||
|
||||
$types = ",u".implode(",u", $types);
|
||||
$types = ",u".implode(",u", $typeKeys);
|
||||
}
|
||||
|
||||
foreach ($vid as $index => $vidValue) $vid[$index] = (int) $vidValue.($troopsArray != null ? ",".implode(",", $values[$index]) : "");
|
||||
foreach ($vid as $index => $vidValue) $vid[$index] = (int) $vidValue.($troopsArray != null ? ",".implode(",", $values[$index]) : "");
|
||||
|
||||
$q = "INSERT into " . TB_PREFIX . "units (vref$types) values (".implode('),(', $vid).")";
|
||||
$duplicateUpdate = "";
|
||||
if(!empty($typeKeys)){
|
||||
$duplicateColumns = [];
|
||||
foreach($typeKeys as $typeKey){
|
||||
$typeKey = (int) $typeKey;
|
||||
$duplicateColumns[] = "u".$typeKey."=VALUES(u".$typeKey.")";
|
||||
}
|
||||
$duplicateUpdate = " ON DUPLICATE KEY UPDATE ".implode(', ', $duplicateColumns);
|
||||
}else{
|
||||
$duplicateUpdate = " ON DUPLICATE KEY UPDATE vref=vref";
|
||||
}
|
||||
|
||||
$q = "INSERT into " . TB_PREFIX . "units (vref$types) values (".implode('),(', $vid).")".$duplicateUpdate;
|
||||
return mysqli_query($this->dblink,$q);
|
||||
}
|
||||
|
||||
@@ -6965,13 +6991,47 @@ References: User ID/Message ID, Mode
|
||||
public function populateWorldData() {
|
||||
global $autoprefix;
|
||||
|
||||
$wdataTable = TB_PREFIX . "wdata";
|
||||
$droppedIndexes = [];
|
||||
|
||||
try {
|
||||
// check if we don't already have world data
|
||||
$data_exist = $this->query_return("SELECT * FROM " . TB_PREFIX . "wdata LIMIT 1");
|
||||
$data_exist = $this->query_return("SELECT * FROM " . $wdataTable . " LIMIT 1");
|
||||
if ($data_exist && count($data_exist)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Best-effort session tuning for faster bulk inserts.
|
||||
$sessionTuningStatements = [
|
||||
"SET SESSION innodb_flush_log_at_trx_commit=2",
|
||||
"SET SESSION sync_binlog=0",
|
||||
"SET SESSION unique_checks=0",
|
||||
"SET SESSION foreign_key_checks=0",
|
||||
];
|
||||
foreach ($sessionTuningStatements as $stmt) {
|
||||
try {
|
||||
mysqli_query($this->dblink, $stmt);
|
||||
} catch (Throwable $e) {
|
||||
// Ignore tuning failures, they are not required for correctness.
|
||||
}
|
||||
}
|
||||
|
||||
// Temporarily drop secondary indexes before heavy INSERT .. SELECT.
|
||||
// Recreated at the end to speed up map generation on larger worlds.
|
||||
$indexesToDrop = ['occupied', 'fieldtype', 'x-y'];
|
||||
foreach ($indexesToDrop as $indexName) {
|
||||
try {
|
||||
$escapedIndexName = mysqli_real_escape_string($this->dblink, $indexName);
|
||||
$res = mysqli_query($this->dblink, "SHOW INDEX FROM `{$wdataTable}` WHERE Key_name = '{$escapedIndexName}'");
|
||||
if ($res && mysqli_num_rows($res) > 0) {
|
||||
mysqli_query($this->dblink, "ALTER TABLE `{$wdataTable}` DROP INDEX `{$indexName}`");
|
||||
$droppedIndexes[$indexName] = true;
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
// If we can't drop an index, continue with generation anyway.
|
||||
}
|
||||
}
|
||||
|
||||
// load the data generation SQL file
|
||||
$str = file_get_contents($autoprefix."var/db/datagen-world-data.sql");
|
||||
$str = preg_replace(["'%PREFIX%'", "'%WORLDSIZE%'"], [TB_PREFIX, WORLD_MAX], $str);
|
||||
@@ -6988,8 +7048,31 @@ References: User ID/Message ID, Mode
|
||||
if (!$result) {
|
||||
return -1;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return -1;
|
||||
} finally {
|
||||
// Recreate dropped indexes to keep runtime query performance unchanged.
|
||||
if (!empty($droppedIndexes)) {
|
||||
try {
|
||||
if (isset($droppedIndexes['occupied'])) {
|
||||
mysqli_query($this->dblink, "CREATE INDEX `occupied` ON `{$wdataTable}` (`occupied`)");
|
||||
}
|
||||
if (isset($droppedIndexes['fieldtype'])) {
|
||||
mysqli_query($this->dblink, "CREATE INDEX `fieldtype` ON `{$wdataTable}` (`fieldtype`)");
|
||||
}
|
||||
if (isset($droppedIndexes['x-y'])) {
|
||||
mysqli_query($this->dblink, "CREATE INDEX `x-y` ON `{$wdataTable}` (`x`, `y`)");
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
// Best effort only; installation can proceed and indexes may be rebuilt manually if needed.
|
||||
}
|
||||
}
|
||||
|
||||
// Restore conservative defaults for this session (best effort).
|
||||
try { mysqli_query($this->dblink, "SET SESSION unique_checks=1"); } catch (Throwable $e) {}
|
||||
try { mysqli_query($this->dblink, "SET SESSION foreign_key_checks=1"); } catch (Throwable $e) {}
|
||||
try { mysqli_query($this->dblink, "SET SESSION innodb_flush_log_at_trx_commit=1"); } catch (Throwable $e) {}
|
||||
try { mysqli_query($this->dblink, "SET SESSION sync_binlog=1"); } catch (Throwable $e) {}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -7012,121 +7095,171 @@ References: User ID/Message ID, Mode
|
||||
}
|
||||
|
||||
public function populateCroppers(int $countTotal = 0, bool $truncateFirst = false, int $batch = 20000, ?callable $reporter = null ): array {
|
||||
|
||||
@set_time_limit(0);
|
||||
@ini_set('memory_limit', '1G');
|
||||
@set_time_limit(0);
|
||||
@ini_set('memory_limit', '1G');
|
||||
|
||||
$TBP = defined('TB_PREFIX') ? TB_PREFIX : 's1_';
|
||||
$CROP_TABLE = $TBP . 'croppers';
|
||||
$WDATA = $TBP . 'wdata';
|
||||
$TBP = defined('TB_PREFIX') ? TB_PREFIX : 's1_';
|
||||
$CROP_TABLE = $TBP . 'croppers';
|
||||
$WDATA = $TBP . 'wdata';
|
||||
|
||||
// Count once if caller didn't
|
||||
if ($countTotal <= 0) {
|
||||
$row = mysqli_fetch_assoc(mysqli_query($this->dblink,
|
||||
"SELECT COUNT(*) cnt FROM `$WDATA` WHERE fieldtype IN (1,6)"));
|
||||
$countTotal = (int)($row['cnt'] ?? 0);
|
||||
}
|
||||
$total = 0;
|
||||
$inTransaction = false;
|
||||
|
||||
if ($truncateFirst) {
|
||||
if (!mysqli_query($this->dblink, "TRUNCATE TABLE `$CROP_TABLE`")) {
|
||||
return ['ok'=>false,'msg'=>'TRUNCATE failed: '.mysqli_error($this->dblink)];
|
||||
}
|
||||
}
|
||||
try {
|
||||
if ($countTotal <= 0) {
|
||||
$row = mysqli_fetch_assoc(mysqli_query($this->dblink, "SELECT COUNT(*) cnt FROM `$WDATA` WHERE fieldtype IN (1,6)"));
|
||||
$countTotal = (int)($row['cnt'] ?? 0);
|
||||
}
|
||||
|
||||
// Session-level speed knobs (local to this connection)
|
||||
@mysqli_query($this->dblink, "SET innodb_flush_log_at_trx_commit=2");
|
||||
@mysqli_query($this->dblink, "SET sync_binlog=0");
|
||||
@mysqli_query($this->dblink, "SET unique_checks=0");
|
||||
@mysqli_query($this->dblink, "SET foreign_key_checks=0");
|
||||
if ($truncateFirst && !mysqli_query($this->dblink, "TRUNCATE TABLE `$CROP_TABLE`")) {
|
||||
return ['ok'=>false,'msg'=>'TRUNCATE failed: '.mysqli_error($this->dblink)];
|
||||
}
|
||||
|
||||
// Read big windows; write in safe slices to avoid max_allowed_packet
|
||||
if ($batch < 1000) $batch = 1000;
|
||||
if ($batch > 100000) $batch = 100000;
|
||||
if($countTotal < 1000) $sliceSize = 200;
|
||||
elseif($countTotal < 5000) $sliceSize = 500;
|
||||
elseif($countTotal > 5000) $sliceSize = 1000;
|
||||
$sessionTuningStatements = [
|
||||
"SET SESSION innodb_flush_log_at_trx_commit=2",
|
||||
"SET SESSION sync_binlog=0",
|
||||
"SET SESSION unique_checks=0",
|
||||
"SET SESSION foreign_key_checks=0",
|
||||
];
|
||||
foreach($sessionTuningStatements as $stmt){
|
||||
try {
|
||||
mysqli_query($this->dblink, $stmt);
|
||||
} catch (Throwable $e) {
|
||||
// Ignore tuning failures, they are not required for correctness.
|
||||
}
|
||||
}
|
||||
|
||||
$total = 0;
|
||||
$lastId = 0;
|
||||
if ($batch < 1000) $batch = 1000;
|
||||
if ($batch > 100000) $batch = 100000;
|
||||
if($countTotal < 1000) $sliceSize = 200;
|
||||
elseif($countTotal < 5000) $sliceSize = 500;
|
||||
else $sliceSize = 1000;
|
||||
|
||||
// Cursor pagination (no OFFSET)
|
||||
while (true) {
|
||||
$res = mysqli_query(
|
||||
$this->dblink,
|
||||
"SELECT id AS wref, x, y, fieldtype
|
||||
FROM `$WDATA`
|
||||
WHERE fieldtype IN (1,6) AND id > $lastId
|
||||
ORDER BY id ASC
|
||||
LIMIT $batch"
|
||||
);
|
||||
if (!$res) {
|
||||
return ['ok'=>false,'msg'=>'SELECT failed: '.mysqli_error($this->dblink),'processed'=>$total,'target'=>$countTotal];
|
||||
}
|
||||
$lastId = 0;
|
||||
while (true) {
|
||||
$res = mysqli_query(
|
||||
$this->dblink,
|
||||
"SELECT id AS wref, x, y, fieldtype
|
||||
FROM `$WDATA`
|
||||
WHERE fieldtype IN (1,6) AND id > $lastId
|
||||
ORDER BY id ASC
|
||||
LIMIT $batch"
|
||||
);
|
||||
if (!$res) {
|
||||
return ['ok'=>false,'msg'=>'SELECT failed: '.mysqli_error($this->dblink),'processed'=>$total,'target'=>$countTotal];
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
while ($r = mysqli_fetch_assoc($res)) { $rows[] = $r; }
|
||||
if (!$rows) break;
|
||||
$rows = [];
|
||||
while ($r = mysqli_fetch_assoc($res)) { $rows[] = $r; }
|
||||
if (!$rows) break;
|
||||
|
||||
mysqli_begin_transaction($this->dblink);
|
||||
mysqli_begin_transaction($this->dblink);
|
||||
$inTransaction = true;
|
||||
|
||||
$n = count($rows);
|
||||
for ($i = 0; $i < $n; $i += $sliceSize) {
|
||||
$chunk = array_slice($rows, $i, $sliceSize);
|
||||
$values = [];
|
||||
$n = count($rows);
|
||||
for ($i = 0; $i < $n; $i += $sliceSize) {
|
||||
$chunk = array_slice($rows, $i, $sliceSize);
|
||||
$values = [];
|
||||
|
||||
foreach ($chunk as $r) {
|
||||
$x = (int)$r['x'];
|
||||
$y = (int)$r['y'];
|
||||
// Compute oasis crop bonus in batch for the whole chunk.
|
||||
$bonusByWref = [];
|
||||
$wrefs = [];
|
||||
foreach ($chunk as $r) {
|
||||
$wrefs[] = (int)$r['wref'];
|
||||
}
|
||||
|
||||
// Your existing helper:
|
||||
$bonus = (int)$this->getBestOasisCropBonus($x, $y);
|
||||
if ($bonus < 0) $bonus = 0;
|
||||
if ($bonus > 150) $bonus = 150;
|
||||
if (!empty($wrefs)) {
|
||||
$bonusSql = "SELECT
|
||||
c.id AS wref,
|
||||
LEAST(
|
||||
150,
|
||||
(50 * LEAST(SUM(CASE WHEN od.type = 12 THEN 1 ELSE 0 END), 3)) +
|
||||
(25 * LEAST(
|
||||
GREATEST(3 - LEAST(SUM(CASE WHEN od.type = 12 THEN 1 ELSE 0 END), 3), 0),
|
||||
SUM(CASE WHEN od.type IN (4,9,10,11) THEN 1 ELSE 0 END)
|
||||
))
|
||||
) AS bonus
|
||||
FROM `$WDATA` c
|
||||
LEFT JOIN `$WDATA` o
|
||||
ON o.fieldtype = 0
|
||||
AND o.x BETWEEN (c.x - 3) AND (c.x + 3)
|
||||
AND o.y BETWEEN (c.y - 3) AND (c.y + 3)
|
||||
LEFT JOIN `{$TBP}odata` od
|
||||
ON od.wref = o.id
|
||||
AND od.type IN (12,4,9,10,11)
|
||||
WHERE c.id IN (".implode(',', $wrefs).")
|
||||
GROUP BY c.id";
|
||||
|
||||
$values[] = sprintf("(%d,%d,%d,%d,%d)",
|
||||
(int)$r['wref'], $x, $y, (int)$r['fieldtype'], $bonus
|
||||
);
|
||||
}
|
||||
$bonusRes = mysqli_query($this->dblink, $bonusSql);
|
||||
if (!$bonusRes) {
|
||||
mysqli_rollback($this->dblink);
|
||||
$inTransaction = false;
|
||||
return ['ok'=>false,'msg'=>'BONUS SELECT failed: '.mysqli_error($this->dblink),'processed'=>$total,'target'=>$countTotal];
|
||||
}
|
||||
|
||||
if ($values) {
|
||||
// ODKU is cheaper than REPLACE (no DELETE)
|
||||
$sql = "INSERT INTO `$CROP_TABLE`
|
||||
(`wref`,`x`,`y`,`fieldtype`,`best_oasis_bonus`)
|
||||
VALUES ".implode(',', $values)."
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`x`=VALUES(`x`),
|
||||
`y`=VALUES(`y`),
|
||||
`fieldtype`=VALUES(`fieldtype`),
|
||||
`best_oasis_bonus`=VALUES(`best_oasis_bonus`)";
|
||||
if (!mysqli_query($this->dblink, $sql)) {
|
||||
mysqli_rollback($this->dblink);
|
||||
return ['ok'=>false,'msg'=>'INSERT failed: '.mysqli_error($this->dblink),'processed'=>$total,'target'=>$countTotal];
|
||||
}
|
||||
}
|
||||
while ($bonusRow = mysqli_fetch_assoc($bonusRes)) {
|
||||
$bonusByWref[(int)$bonusRow['wref']] = (int)($bonusRow['bonus'] ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
// progress after each slice
|
||||
$total += count($chunk);
|
||||
if ($reporter) {
|
||||
$pct = $countTotal ? min(100, (int)floor(($total / $countTotal) * 100)) : 0;
|
||||
$reporter($total, $countTotal, $pct);
|
||||
}
|
||||
}
|
||||
foreach ($chunk as $r) {
|
||||
$x = (int)$r['x'];
|
||||
$y = (int)$r['y'];
|
||||
$bonus = (int)($bonusByWref[(int)$r['wref']] ?? 0);
|
||||
if ($bonus < 0) $bonus = 0;
|
||||
if ($bonus > 150) $bonus = 150;
|
||||
$values[] = sprintf("(%d,%d,%d,%d,%d)", (int)$r['wref'], $x, $y, (int)$r['fieldtype'], $bonus);
|
||||
}
|
||||
|
||||
mysqli_commit($this->dblink);
|
||||
if ($values) {
|
||||
$sql = "INSERT INTO `$CROP_TABLE`
|
||||
(`wref`,`x`,`y`,`fieldtype`,`best_oasis_bonus`)
|
||||
VALUES ".implode(',', $values)."
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`x`=VALUES(`x`),
|
||||
`y`=VALUES(`y`),
|
||||
`fieldtype`=VALUES(`fieldtype`),
|
||||
`best_oasis_bonus`=VALUES(`best_oasis_bonus`)";
|
||||
if (!mysqli_query($this->dblink, $sql)) {
|
||||
mysqli_rollback($this->dblink);
|
||||
$inTransaction = false;
|
||||
return ['ok'=>false,'msg'=>'INSERT failed: '.mysqli_error($this->dblink),'processed'=>$total,'target'=>$countTotal];
|
||||
}
|
||||
}
|
||||
|
||||
// advance cursor
|
||||
$lastId = (int)$rows[$n - 1]['wref'];
|
||||
}
|
||||
$total += count($chunk);
|
||||
if ($reporter) {
|
||||
$pct = $countTotal ? min(100, (int)floor(($total / $countTotal) * 100)) : 0;
|
||||
$reporter($total, $countTotal, $pct);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore checks (optional)
|
||||
@mysqli_query($this->dblink, "SET unique_checks=1");
|
||||
@mysqli_query($this->dblink, "SET foreign_key_checks=1");
|
||||
mysqli_commit($this->dblink);
|
||||
$inTransaction = false;
|
||||
$lastId = (int)$rows[$n - 1]['wref'];
|
||||
}
|
||||
|
||||
// Analyze once at the end
|
||||
@mysqli_query($this->dblink, "ANALYZE TABLE `$CROP_TABLE`");
|
||||
foreach(["SET SESSION unique_checks=1", "SET SESSION foreign_key_checks=1"] as $stmt){
|
||||
try {
|
||||
mysqli_query($this->dblink, $stmt);
|
||||
} catch (Throwable $e) {
|
||||
// Ignore restore failures if the engine does not support the setting.
|
||||
}
|
||||
}
|
||||
|
||||
if ($reporter) { $reporter($total, $countTotal, 100); }
|
||||
return ['ok'=>true,'msg'=>'Croppers populated','processed'=>$total,'target'=>$countTotal];
|
||||
@mysqli_query($this->dblink, "ANALYZE TABLE `$CROP_TABLE`");
|
||||
if ($reporter) { $reporter($total, $countTotal, 100); }
|
||||
return ['ok'=>true,'msg'=>'Croppers populated','processed'=>$total,'target'=>$countTotal];
|
||||
} catch (Throwable $e) {
|
||||
if ($inTransaction) {
|
||||
try {
|
||||
mysqli_rollback($this->dblink);
|
||||
} catch (Throwable $rollbackException) {
|
||||
// Ignore rollback errors after fatal DB exceptions.
|
||||
}
|
||||
}
|
||||
return ['ok'=>false,'msg'=>$e->getMessage(),'processed'=>$total,'target'=>$countTotal];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@ define('WISHES_YOU', 'wishes you');
|
||||
define('X_MAS', 'Merry Christmas');
|
||||
define('NEW_YEAR', 'Happy New Year');
|
||||
define('EASTER', 'Happy Easter');
|
||||
define('PEACE', 'Peace');
|
||||
if(!defined('PEACE')) define('PEACE', 'Peace');
|
||||
|
||||
define('GOLD', 'Gold');
|
||||
define('GOLD_IMG', '<img src=\"/img/x.gif\" class=\"gold\" alt=\"'.GOLD.'\" title=\"'.GOLD.'\">');
|
||||
@@ -626,8 +626,8 @@ define('MODERATOR', 'Moderator');
|
||||
define('ACTIVE', 'Active');
|
||||
define('ONLINE', 'Online');
|
||||
define('TUTORIAL', 'Tutorial');
|
||||
define('FAQ', 'Faq');
|
||||
define('SPIELREGELN', 'Game Rules');
|
||||
if(!defined('FAQ')) define('FAQ', 'Faq');
|
||||
if(!defined('SPIELREGELN')) define('SPIELREGELN', 'Game Rules');
|
||||
define('PLAYER_STATISTICS', 'Player statistics');
|
||||
define('TOTAL_PLAYERS', PLAYERS.' in total');
|
||||
define('ACTIVE_PLAYERS', 'Active players');
|
||||
@@ -645,8 +645,8 @@ define('BECOME_COMUNITY', 'Become part of our community now!');
|
||||
define('BECOME_COMUNITY2', 'Become a part of one of<br>the biggest gaming<br>communities in the<br>world.');
|
||||
define('NEWS', 'News');
|
||||
define('SCREENSHOTS', 'Screenshots');
|
||||
define('FAQ', 'FAQ');
|
||||
define('SPIELREGELN', 'Rules');
|
||||
if(!defined('FAQ')) define('FAQ', 'FAQ');
|
||||
if(!defined('SPIELREGELN')) define('SPIELREGELN', 'Rules');
|
||||
define('AGB', 'Terms and Conditions');
|
||||
define('LEARN1', 'Upgrade your fields and mines to increase your resource production. You will need resources to construct buildings and train soldiers.');
|
||||
define('LEARN2', 'Construct and expand the buildings in your village. Buildings improve your overall infrastructure, increase your resource production and allow you to research, train and upgrade your troops.');
|
||||
@@ -1331,7 +1331,7 @@ define('PLAYER_ADMIN', 'This player is Admin');
|
||||
define('PLAYER_MH', 'This player is Multihunter');
|
||||
define('PLAYER_BANNED', 'This player is BANNED');
|
||||
define('PLAYER_VACATION', 'This player is on VACATION');
|
||||
define('BANNED', 'Banned');
|
||||
if(!defined('BANNED')) define('BANNED', 'Banned');
|
||||
define('GENDER', 'Gender');
|
||||
define('GENDER0', 'n/a');
|
||||
define('MALE0', 'm');
|
||||
|
||||
@@ -684,11 +684,19 @@ class Technology {
|
||||
$resarray['iron'] = ${'r'.$id}['iron'];
|
||||
$resarray['crop'] = ${'r'.$id}['crop'];
|
||||
}
|
||||
$rwtime = ($resarray['wood']-$village->awood) / $village->getProd("wood") * 3600;
|
||||
$rcltime = ($resarray['clay']-$village->aclay) / $village->getProd("clay") * 3600;
|
||||
$ritime = ($resarray['iron']-$village->airon) / $village->getProd("iron") * 3600;
|
||||
$rctime = ($resarray['crop']-$village->acrop) / $village->getProd("crop") * 3600;
|
||||
if($village->getProd("crop") >= 0) {
|
||||
$woodNeed = $resarray['wood'] - $village->awood;
|
||||
$clayNeed = $resarray['clay'] - $village->aclay;
|
||||
$ironNeed = $resarray['iron'] - $village->airon;
|
||||
$cropNeed = $resarray['crop'] - $village->acrop;
|
||||
$woodProd = $village->getProd("wood");
|
||||
$clayProd = $village->getProd("clay");
|
||||
$ironProd = $village->getProd("iron");
|
||||
$cropProd = $village->getProd("crop");
|
||||
$rwtime = ($woodNeed <= 0) ? 0 : ($woodProd > 0 ? $woodNeed / $woodProd * 3600 : 0);
|
||||
$rcltime = ($clayNeed <= 0) ? 0 : ($clayProd > 0 ? $clayNeed / $clayProd * 3600 : 0);
|
||||
$ritime = ($ironNeed <= 0) ? 0 : ($ironProd > 0 ? $ironNeed / $ironProd * 3600 : 0);
|
||||
$rctime = ($cropNeed <= 0) ? 0 : ($cropProd != 0 ? $cropNeed / $cropProd * 3600 : 0);
|
||||
if($cropProd >= 0) {
|
||||
$reqtime = max($rwtime,$rcltime,$ritime,$rctime) + time();
|
||||
} else {
|
||||
$reqtime = max($rwtime,$rcltime,$ritime);
|
||||
|
||||
+15
-3
@@ -24,7 +24,7 @@ class Units {
|
||||
|
||||
switch($post['c']) {
|
||||
case 1:
|
||||
if (isset($post['a']) && $post['a'] == 533374) $this->sendTroops($post);
|
||||
if (isset($post['a']) && $post['a'] == 533374 && empty($post['disabled'])) $this->sendTroops($post);
|
||||
else
|
||||
{
|
||||
$post = $this->loadUnits($post);
|
||||
@@ -51,7 +51,7 @@ class Units {
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (isset($post['a']) && $post['a'] == 533374) $this->sendTroops($post);
|
||||
if (isset($post['a']) && $post['a'] == 533374 && empty($post['disabled'])) $this->sendTroops($post);
|
||||
else
|
||||
{
|
||||
$post = $this->loadUnits($post);
|
||||
@@ -97,12 +97,24 @@ class Units {
|
||||
|
||||
public function checkErrors(&$post){
|
||||
global $database, $village, $session, $generator;
|
||||
|
||||
if(isset($post['x'])) $post['x'] = trim($post['x']);
|
||||
if(isset($post['y'])) $post['y'] = trim($post['y']);
|
||||
|
||||
if(isset($post['x']) && isset($post['y']) && $post['x'] !== "" && $post['y'] !== ""){
|
||||
if(!preg_match('/^-?\d+$/', $post['x']) || !preg_match('/^-?\d+$/', $post['y'])) return "Invalid coordinates";
|
||||
|
||||
$post['x'] = (int) $post['x'];
|
||||
$post['y'] = (int) $post['y'];
|
||||
|
||||
if(abs($post['x']) > WORLD_MAX || abs($post['y']) > WORLD_MAX) return "Coordinates do not exist";
|
||||
}
|
||||
|
||||
// Search by town name
|
||||
// Coordinates and look confirm name people
|
||||
if(isset($post['x']) && isset($post['y']) && $post['x'] != "" && $post['y'] != "") {
|
||||
$vid = $database->getVilWref($post['x'], $post['y']);
|
||||
unset($post['dname'], $post['dname']);
|
||||
unset($post['dname']);
|
||||
}
|
||||
else if(isset($post['dname']) && !empty($post['dname'])) $vid = $database->getVillageByName(stripslashes($post['dname']));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user