chore: harden php8 compatibility, optimize installer/croppers, and refresh docs/admin ux

This commit is contained in:
levi
2026-03-14 16:50:27 -03:00
parent 8a91e89644
commit a0ef06a35f
29 changed files with 871 additions and 418 deletions
+4 -1
View File
@@ -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
View File
@@ -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']);
+8 -4
View File
@@ -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
View File
@@ -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];
}
}
+6 -6
View File
@@ -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');
+13 -5
View File
@@ -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
View File
@@ -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']));