This commit is contained in:
Catalin Novgorodschi
2026-02-11 15:17:21 +02:00
17 changed files with 2459 additions and 989 deletions
+233 -91
View File
@@ -619,26 +619,25 @@ class MYSQLi_DB implements IDbConnection {
References: lietuvis10
***************************/
public function getBestOasisCropBonus($x, $y) {
$x = (int)$x;
$y = (int)$y;
// Adjust oasis type codes if your fork differs:
// - 50% crop only: type IN (12)
// - 25% crop (pure or mixed w/ wood/clay/iron): type IN (4,9,10,11)
$sql = "SELECT COALESCE(SUM(bonus), 0) AS total FROM (SELECT CASE
WHEN o.type IN (12) THEN 50 WHEN o.type IN (4,9,10,11) THEN 25 ELSE 0
END AS bonus FROM " . TB_PREFIX . "wdata w JOIN " . TB_PREFIX . "odata o ON o.wref = w.id
WHERE w.fieldtype = 0 AND ABS(w.x - $x) <= 3 AND ABS(w.y - $y) <= 3
AND o.type IN (12,4,9,10,11) -- only crop-giving oases ORDER BY bonus DESC LIMIT 3) t";
public function getBestOasisCropBonus($x, $y) {
$x = (int)$x;
$y = (int)$y;
$q = mysqli_query($this->dblink, $sql);
$row = mysqli_fetch_assoc($q);
$total = (int)($row['total'] ?? 0);
if ($total > 150) $total = 150; // safety cap
return $total;
}
// Adjust oasis type codes if your fork differs:
// - 50% crop only: type IN (12)
// - 25% crop (pure or mixed w/ wood/clay/iron): type IN (4,9,10,11)
$sql = "SELECT COALESCE(SUM(bonus), 0) AS total FROM (SELECT CASE
WHEN o.type IN (12) THEN 50 WHEN o.type IN (4,9,10,11) THEN 25 ELSE 0
END AS bonus FROM " . TB_PREFIX . "wdata w JOIN " . TB_PREFIX . "odata o ON o.wref = w.id
WHERE w.fieldtype = 0 AND ABS(w.x - $x) <= 3 AND ABS(w.y - $y) <= 3 AND o.type IN (12,4,9,10,11)
ORDER BY bonus DESC LIMIT 3) t";
$q = mysqli_query($this->dblink, $sql);
$row = mysqli_fetch_assoc($q);
$total = (int)($row['total'] ?? 0);
if ($total > 150) $total = 150; // safety cap
return $total;
}
/***************************
Function to process MYSQLi->fetch_all (Only exist in MYSQL)
@@ -7014,94 +7013,237 @@ References: User ID/Message ID, Mode
return true;
}
/*** Build/rebuild the croppers precompute table from wdata. */
public function TotalCroppers(): int {
$TBP = defined('TB_PREFIX') ? TB_PREFIX : 's1_';
$WDATA = $TBP . 'wdata';
$res = mysqli_query($this->dblink, "SELECT COUNT(*) AS cnt FROM `$WDATA` WHERE fieldtype IN (1,6)");
if (!$res) {
throw new Exception('Count query failed: ' . mysqli_error($this->dblink));
}
$row = mysqli_fetch_assoc($res);
return (int)($row['cnt'] ?? 0);
}
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');
$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);
}
if ($truncateFirst) {
if (!mysqli_query($this->dblink, "TRUNCATE TABLE `$CROP_TABLE`")) {
return ['ok'=>false,'msg'=>'TRUNCATE failed: '.mysqli_error($this->dblink)];
}
}
// 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");
// 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;
$total = 0;
$lastId = 0;
// 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];
}
$rows = [];
while ($r = mysqli_fetch_assoc($res)) { $rows[] = $r; }
if (!$rows) break;
mysqli_begin_transaction($this->dblink);
$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'];
// Your existing helper:
$bonus = (int)$this->getBestOasisCropBonus($x, $y);
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
);
}
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];
}
}
// progress after each slice
$total += count($chunk);
if ($reporter) {
$pct = $countTotal ? min(100, (int)floor(($total / $countTotal) * 100)) : 0;
$reporter($total, $countTotal, $pct);
}
}
mysqli_commit($this->dblink);
// advance cursor
$lastId = (int)$rows[$n - 1]['wref'];
}
// Restore checks (optional)
@mysqli_query($this->dblink, "SET unique_checks=1");
@mysqli_query($this->dblink, "SET foreign_key_checks=1");
// Analyze once at the end
@mysqli_query($this->dblink, "ANALYZE TABLE `$CROP_TABLE`");
if ($reporter) { $reporter($total, $countTotal, 100); }
return ['ok'=>true,'msg'=>'Croppers populated','processed'=>$total,'target'=>$countTotal];
}
// no need to cache, not used in any loops or more than once for each page load
public function getAvailableExpansionTraining() {
global $building, $session, $technology, $village;
$vilData = $this->getVillage($village->wid);
$maxslots = (($vilData['exp1'] == 0 ? 1 : 0) + ($vilData['exp2'] == 0 ? 1 : 0) + ($vilData['exp3'] == 0 ? 1 : 0));
$residence = $building->getTypeLevel(25);
$palace = $building->getTypeLevel(26);
$vilData = $this->getVillage($village->wid);
$maxslots = (($vilData['exp1'] == 0 ? 1 : 0) + ($vilData['exp2'] == 0 ? 1 : 0) + ($vilData['exp3'] == 0 ? 1 : 0));
$residence = $building->getTypeLevel(25);
$palace = $building->getTypeLevel(26);
if($residence > 0) {
$maxslots -= (3 - floor($residence / 10));
}
if($residence > 0) {
$maxslots -= (3 - floor($residence / 10));
}
if($palace > 0) {
$maxslots -= (3 - floor(($palace - 5) / 5));
}
if($palace > 0) {
$maxslots -= (3 - floor(($palace - 5) / 5));
}
$q = "SELECT (u10+u20+u30) as R1, (u9+u19+u29) as R2 FROM " . TB_PREFIX . "units WHERE vref = ". (int) $village->wid;
$result = mysqli_query($this->dblink,$q);
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
$settlers = $row['R1'];
$chiefs = $row['R2'];
// Units at home
$q = "SELECT (u10+u20+u30) as R1, (u9+u19+u29) as R2
FROM " . TB_PREFIX . "units
WHERE vref = " . (int)$village->wid;
$result = mysqli_query($this->dblink,$q);
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
$settlers = (int)$row['R1'];
$chiefs = (int)$row['R2'];
$settlers += 3 * count($this->getMovement(5, $village->wid, 0));
$current_movement = $this->getMovement(3, $village->wid, 0);
if(!empty($current_movement)) {
foreach($current_movement as $build) {
$settlers += $build['t10'];
$chiefs += $build['t9'];
}
}
// Movements
$settlers += 3 * count($this->getMovement(5, $village->wid, 0));
$current_movement = $this->getMovement(4, $village->wid, 1);
if(!empty($current_movement)) {
foreach($current_movement as $build) {
$settlers += $build['t10'];
$chiefs += $build['t9'];
}
}
$current_movement = $this->getMovement(3, $village->wid, 0);
if(!empty($current_movement)) {
foreach($current_movement as $build) {
$settlers += (int)$build['t10'];
$chiefs += (int)$build['t9'];
}
}
$q = "SELECT (u10+u20+u30) FROM " . TB_PREFIX . "enforcement WHERE `from` = ".(int) $village->wid;
$result = mysqli_query($this->dblink,$q);
$row = mysqli_fetch_row($result);
if(!empty($row)) {
foreach($row as $reinf) {
$settlers += $reinf[0];
}
}
$current_movement = $this->getMovement(4, $village->wid, 1);
if(!empty($current_movement)) {
foreach($current_movement as $build) {
$settlers += (int)$build['t10'];
$chiefs += (int)$build['t9'];
}
}
$q = "SELECT (u9+u19+u29) FROM " . TB_PREFIX . "enforcement WHERE `from` = ".(int) $village->wid;
$result = mysqli_query($this->dblink,$q);
$row = mysqli_fetch_row($result);
if(!empty($row)) {
foreach($row as $reinf) {
$chiefs += $reinf[0];
}
}
// FIX: Count ALL reinforcements properly (SUM over ALL rows)
$q = "SELECT COALESCE(SUM(u10+u20+u30),0) AS s
FROM " . TB_PREFIX . "enforcement
WHERE `from` = " . (int)$village->wid;
$result = mysqli_query($this->dblink,$q);
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
$settlers += (int)$row['s'];
$trainlist = $technology->getTrainingList(4);
if(!empty($trainlist)) {
foreach($trainlist as $train) {
if($train['unit'] % 10 == 0) {
$settlers += $train['amt'];
}
if($train['unit'] % 10 == 9) {
$chiefs += $train['amt'];
}
}
}
$q = "SELECT COALESCE(SUM(u9+u19+u29),0) AS c
FROM " . TB_PREFIX . "enforcement
WHERE `from` = " . (int)$village->wid;
$result = mysqli_query($this->dblink,$q);
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
$chiefs += (int)$row['c'];
$trappedTroops = $this->getPrisoners($village->wid, 1);
if(!empty($trappedTroops)){
foreach($trappedTroops as $trapped){
$settlers += $trapped['t10'];
$chiefs += $trapped['t9'];
}
}
// Training queue (your existing logic)
$trainlist = $technology->getTrainingList(4);
if(!empty($trainlist)) {
foreach($trainlist as $train) {
if($train['unit'] % 10 == 0) {
$settlers += (int)$train['amt'];
}
if($train['unit'] % 10 == 9) {
$chiefs += (int)$train['amt'];
}
}
}
$settlerslots = ($maxslots * 3) - ($chiefs * 3) - $settlers;
$chiefslots = $maxslots - $chiefs - floor(($settlers + 2) / 3);
// Trapped troops
$trappedTroops = $this->getPrisoners($village->wid, 1);
if(!empty($trappedTroops)){
foreach($trappedTroops as $trapped){
$settlers += (int)$trapped['t10'];
$chiefs += (int)$trapped['t9'];
}
}
if(!$technology->getTech(($session->tribe - 1) * 10 + 9)) {
$chiefslots = 0;
}
// Slot math (unchanged, but clamp to 0 to avoid negatives)
$settlerslots = ($maxslots * 3) - ($chiefs * 3) - $settlers;
$chiefslots = $maxslots - $chiefs - floor(($settlers + 2) / 3);
if(!$technology->getTech(($session->tribe - 1) * 10 + 9)) {
$chiefslots = 0;
}
if ($settlerslots < 0) $settlerslots = 0;
if ($chiefslots < 0) $chiefslots = 0;
return ["chiefs" => $chiefslots, "settlers" => $settlerslots];
}
return ["chiefs" => $chiefslots, "settlers" => $settlerslots];
}
/**
* Calculates how much artifacts affect troops speed, cranny efficency, etc.