Files
TravianZ/build_croppers.php
T
novgorodschi catalin e1dd434f5b Fix Statistiken & Credits
Fix Statistiken & Credits
2026-06-26 12:42:16 +03:00

386 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
#################################################################################
## -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =- ##
## --------------------------------------------------------------------------- ##
## Filename : build_croppers.php ##
## Type : In Game AutoBuild Croppers (c9/c15) ##
## --------------------------------------------------------------------------- ##
## Developed by : Dzoki ##
## Refactored by : Shadow ##
## Redesign by : Shadow ##
## --------------------------------------------------------------------------- ##
## Contact : cata7007@gmail.com ##
## Project : TravianZ ##
## URLs: : https://travianz.org ##
## GitHub : https://github.com/Shadowss/TravianZ ##
## --------------------------------------------------------------------------- ##
## License : TravianZ Project ##
## Copyright : TravianZ (c) 2010-2026. All rights reserved. ##
## --------------------------------------------------------------------------- ##
#################################################################################
use App\Utils\AccessLogger;
include_once("GameEngine/Generator.php");
$start_timer = $generator->pageLoadTimeStart();
include_once("GameEngine/Session.php");
include_once("GameEngine/config.php");
include_once("GameEngine/Database.php");
include_once("GameEngine/Village.php");
AccessLogger::logRequest();
// ---------- Admin gate ----------
if (!isset($session) || !isset($session->access) || (int)$session->access < 8) {
header('Location: dorf1.php');
exit;
}
// ---------- Setup ----------
@session_start();
$TBP = defined('TB_PREFIX') ? TB_PREFIX : 's1_';
$CROP_TABLE = $TBP . 'croppers';
$WDATA = $TBP . 'wdata';
// Build an absolute-safe asset prefix for CSS/JS
$assetBase = $session->gpack ?: GP_LOCATE;
$assetBase = '/'.ltrim($assetBase, '/');
// CSRF
if (empty($_SESSION['csrf_cb'])) {
$_SESSION['csrf_cb'] = bin2hex(random_bytes(16));
}
$csrf = $_SESSION['csrf_cb'];
function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
function indexExists($db, $tableName, $indexName) {
$sql = "SELECT 1 FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = '".mysqli_real_escape_string($db, $tableName)."' AND index_name = '".mysqli_real_escape_string($db, $indexName)."' LIMIT 1";
$result = mysqli_query($db, $sql);
if (!$result) {
return false;
}
return mysqli_num_rows($result) > 0;
}
function createIndexIfMissing($db, $tableName, $indexName, $columnsSql) {
if (!indexExists($db, $tableName, $indexName)) {
mysqli_query($db, "CREATE INDEX `{$indexName}` ON `{$tableName}` ({$columnsSql})");
}
}
function dropIndexIfExists($db, $tableName, $indexName) {
if (indexExists($db, $tableName, $indexName)) {
mysqli_query($db, "DROP INDEX `{$indexName}` ON `{$tableName}`");
}
}
// Ensure table exists (minimal schema, unsigned tinyints)
mysqli_query($database->dblink, "CREATE TABLE IF NOT EXISTS `$CROP_TABLE` (
`wref` INT UNSIGNED NOT NULL PRIMARY KEY,
`x` INT NOT NULL,
`y` INT NOT NULL,
`fieldtype` TINYINT UNSIGNED NOT NULL,
`best_oasis_bonus` TINYINT UNSIGNED NOT NULL,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CHECK (`best_oasis_bonus` IN (0,25,50,75,100,125,150))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
// Helpful indexes (idempotent, avoids PHP8 mysqli duplicate-index exceptions)
createIndexIfMissing($database->dblink, $CROP_TABLE, 'idx_ft_bonus_xy', '`fieldtype`, `best_oasis_bonus`, `x`, `y`');
createIndexIfMissing($database->dblink, $CROP_TABLE, 'idx_xy', '`x`, `y`');
createIndexIfMissing($database->dblink, $CROP_TABLE, 'idx_bonus', '`best_oasis_bonus`');
// ---------- Helpers ----------
function worldSizeLabel(): string {
if (defined('WORLD_MIN') && defined('WORLD_MAX')) {
$min = (int)WORLD_MIN; $max = (int)WORLD_MAX;
return ($max - $min + 1) . "×" . ($max - $min + 1) . " (" . $min . " .. " . $max . ")";
}
if (defined('WORLD_MAX')) {
$max = (int)WORLD_MAX; $min = -$max;
return ($max - $min + 1) . "×" . ($max - $min + 1) . " (" . $min . " .. " . $max . ")";
}
return "unknown";
}
function getCounts($db, $WDATA, $CROP_TABLE) {
$c1 = mysqli_fetch_assoc(mysqli_query($db, "SELECT COUNT(*) c FROM `$WDATA` WHERE fieldtype IN (1,6)"));
$c2 = mysqli_fetch_assoc(mysqli_query($db, "SELECT COUNT(*) c FROM `$CROP_TABLE`"));
$lu = mysqli_fetch_assoc(mysqli_query($db, "SELECT MAX(updated_at) lu FROM `$CROP_TABLE`"));
return [
'croppers_world' => (int)($c1['c'] ?? 0),
'croppers_table' => (int)($c2['c'] ?? 0),
'last_updated' => $lu['lu'] ?? null,
];
}
// stream log
function startStreaming() {
@ini_set('output_buffering','off');
@ini_set('zlib.output_compression', 0);
while (ob_get_level()) { @ob_end_flush(); }
ob_implicit_flush(true);
echo "<pre id=\"log\" style=\"background:#0b0f17;color:#d7e1f8;padding:12px;border-radius:12px;max-height:60vh;overflow:auto;\">";
echo htmlspecialchars("[".date('H:i:s')."] Croppers builder started")."\n";
flush();
}
function logLine($msg) { echo htmlspecialchars("[".date('H:i:s')."] ".$msg)."\n"; flush(); }
function endStreaming() { echo "</pre>"; flush(); }
// ---------- Actions ----------
$action = $_POST['action'] ?? null;
$okCsrf = isset($_POST['csrf']) && hash_equals($_SESSION['csrf_cb'], $_POST['csrf']);
$notice = null;
if ($action && !$okCsrf) {
$notice = "Invalid CSRF token. Please reload the page.";
$action = null;
}
if ($action === 'truncate') {
mysqli_query($database->dblink, "TRUNCATE TABLE `$CROP_TABLE`");
$notice = "Croppers table truncated.";
}
if ($action === 'reindex') {
dropIndexIfExists($database->dblink, $CROP_TABLE, 'idx_ft_bonus_xy');
dropIndexIfExists($database->dblink, $CROP_TABLE, 'idx_xy');
dropIndexIfExists($database->dblink, $CROP_TABLE, 'idx_bonus');
createIndexIfMissing($database->dblink, $CROP_TABLE, 'idx_ft_bonus_xy', '`fieldtype`, `best_oasis_bonus`, `x`, `y`');
createIndexIfMissing($database->dblink, $CROP_TABLE, 'idx_xy', '`x`, `y`');
createIndexIfMissing($database->dblink, $CROP_TABLE, 'idx_bonus', '`best_oasis_bonus`');
$notice = "Indexes rebuilt.";
}
if ($action === 'estimate') {
$notice = "Estimated counts refreshed.";
}
$stats = getCounts($database->dblink, $WDATA, $CROP_TABLE);
$worldLabel = worldSizeLabel();
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title><?php echo SERVER_NAME ?> - Mass Message</title>
<link rel="shortcut icon" href="favicon.ico"/>
<meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="imagetoolbar" content="no" />
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<script src="mt-full.js?0ac37" type="text/javascript"></script>
<script src="unx.js?f4b7h" type="text/javascript"></script>
<script src="new.js?0ac37" type="text/javascript"></script>
<link href="<?php echo GP_LOCATE; ?>lang/en/lang.css?f4b7d" rel="stylesheet" type="text/css" />
<link href="<?php echo GP_LOCATE; ?>lang/en/compact.css?f4b7i" rel="stylesheet" type="text/css" />
<?php
if($session->gpack == null || GP_ENABLE == false) {
echo "
<link href='".GP_LOCATE."travian.css?e21d2' rel='stylesheet' type='text/css' />
<link href='".GP_LOCATE."lang/en/lang.css?e21d2' rel='stylesheet' type='text/css' />";
} else {
echo "
<link href='".$session->gpack."travian.css?e21d2' rel='stylesheet' type='text/css' />
<link href='".$session->gpack."lang/en/lang.css?e21d2' rel='stylesheet' type='text/css' />";
}
?>
<script language="javascript" type="text/javascript">
function smilie(text) {
document.myform.message.value += text;
}
</script>
<script language="javascript">
function toggleDisplay(e){
element = document.getElementById(e).style;
element.display == 'none' ? element.display = 'block' :
element.display='none';
}
</script>
<script type="text/javascript">
window.addEvent('domready', start);
</script>
<?php
if($session->gpack == null || GP_ENABLE == false) {
echo "
<link href='".GP_LOCATE."travian.css?e21d2' rel='stylesheet' type='text/css' />
<link href='".GP_LOCATE."lang/en/lang.css?e21d2' rel='stylesheet' type='text/css' />";
} else {
echo "
<link href='".$session->gpack."travian.css?e21d2' rel='stylesheet' type='text/css' />
<link href='".$session->gpack."lang/en/lang.css?e21d2' rel='stylesheet' type='text/css' />";
}
?>
<script type="text/javascript">
window.addEvent('domready', start);
</script>
<style>
.cb-container{ max-width:980px;margin:0 auto;padding:0 12px; }
.cb-grid{ display:grid; grid-template-columns:1fr 1fr; gap:12px; }
.cb-card{ background:#fff;border:1px solid #dcdde1;border-radius:12px;padding:14px; box-shadow:0 1px 2px rgba(0,0,0,.04); }
.cb-title{ font-size:20px;margin:0 0 8px 0; }
.cb-muted{ color:#666;font-size:12px; }
.cb-kpis{ display:flex; gap:16px; margin-top:8px; flex-wrap:wrap; }
.cb-kpi{ background:#f7f9fb;border-radius:10px;padding:10px 12px; border:1px solid #e6ecf3; }
.cb-actions form{ display:flex; flex-wrap:wrap; gap:8px; align-items:center; }
.cb-btn{ background:#2f7d32;color:#fff;border:none;border-radius:10px;padding:10px 14px;cursor:pointer; }
.cb-btn.red{ background:#b23b3b; }
.cb-btn.gray{ background:#5c6b7a; }
.cb-input{ padding:8px 10px;border:1px solid #c9d3df;border-radius:10px;width:120px; }
.cb-note{ background:#fff4cc;border:1px solid #f5d36b;border-radius:10px;padding:10px 12px;margin-top:8px; }
.cb-notice{ background:#e8f6ff;border:1px solid #b3e0ff;color:#0b5380;border-radius:10px;padding:10px 12px;margin:12px 0; }
@media (max-width: 900px){ .cb-grid{ grid-template-columns:1fr; } }
pre#log { margin-top:14px; }
</style>
</head>
<body class="v35 ie ie8">
<div class="wrapper">
<img style="filter:chroma();" src="img/x.gif" id="msfilter" alt="" />
<div id="dynamic_header"></div>
<?php include("Templates/header.tpl"); ?>
<div id="mid">
<?php include("Templates/menu.tpl"); ?>
<!-- IMPORTANT: Use the normal game content style instead of "login" -->
<div id="content" class="player">
<div class="cb-container">
<h1 style="text-align:center;margin:12px 0 16px;">Build crop finder</h1>
<?php if ($notice): ?>
<div class="cb-notice"><?php echo h($notice); ?></div>
<?php endif; ?>
<div class="cb-grid">
<div class="cb-card">
<h3 class="cb-title">Status</h3>
<div class="cb-muted">World: <?php echo h($worldLabel); ?></div>
<div class="cb-kpis">
<div class="cb-kpi"><b>9c/15c in map:</b> <?php echo number_format($stats['croppers_world']); ?></div>
<div class="cb-kpi"><b>Rows in table:</b> <?php echo number_format($stats['croppers_table']); ?></div>
<div class="cb-kpi"><b>Last updated:</b> <?php echo $stats['last_updated'] ? h($stats['last_updated']) : '—'; ?></div>
</div>
<div class="cb-note">
The croppers table stores only <b>wref,x,y,fieldtype,best_oasis_bonus</b>.
Ownership and occupied status are pulled live from <code>vdata/users</code> by the finder.
</div>
</div>
<div class="cb-card cb-actions">
<h3 class="cb-title">Actions</h3>
<form method="post">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>" />
<label>Batch size:</label>
<input class="cb-input" type="number" min="1000" max="20000" step="1000" name="batch" value="<?php echo isset($_POST['batch']) ? (int)$_POST['batch'] : 5000; ?>" />
<button class="cb-btn" name="action" value="build">Build / Rebuild</button>
<button class="cb-btn gray" name="action" value="estimate" type="submit">Estimate</button>
<button class="cb-btn gray" name="action" value="reindex" type="submit">Reindex</button>
<button class="cb-btn red" name="action" value="truncate" type="submit" onclick="return confirm('Really truncate the table?');">Truncate</button>
</form>
<div class="cb-muted" style="margin-top:8px;">
Building streams progress below. You can leave this page; the process stops when the request ends.
</div>
</div>
</div>
<?php
// Stream build logs INSIDE the content area
if ($action === 'build' && $okCsrf) {
$batch = max(1000, min(20000, (int)($_POST['batch'] ?? 5000)));
startStreaming();
$cnt = mysqli_fetch_assoc(mysqli_query($database->dblink, "SELECT COUNT(*) AS c FROM `$WDATA` WHERE `fieldtype` IN (1,6)"));
$target = (int)($cnt['c'] ?? 0);
logLine("Detected $target croppers.");
$offset = 0; $total = 0;
while (true) {
$sql = "SELECT id AS wref, x, y, fieldtype
FROM `$WDATA`
WHERE `fieldtype` IN (1,6)
LIMIT $offset, $batch";
$res = mysqli_query($database->dblink, $sql);
if (!$res) { logLine('Query failed: '.mysqli_error($database->dblink)); break; }
$rows = [];
while ($r = mysqli_fetch_assoc($res)) { $rows[] = $r; }
if (!$rows) break;
$values = [];
foreach ($rows as $r) {
$x = (int)$r['x']; $y = (int)$r['y'];
$bonus = (int)$database->getBestOasisCropBonus($x, $y);
if (!in_array($bonus, [0,25,50,75,100,125,150], true)) {
$bonus = max(0, min(150, $bonus));
}
$values[] = sprintf("(%d,%d,%d,%d,%d)",
(int)$r['wref'], $x, $y, (int)$r['fieldtype'], $bonus);
}
if ($values) {
$sql = "REPLACE INTO `$CROP_TABLE`
(`wref`,`x`,`y`,`fieldtype`,`best_oasis_bonus`)
VALUES ".implode(',', $values);
if (!mysqli_query($database->dblink, $sql)) {
logLine('Upsert failed: '.mysqli_error($database->dblink));
break;
}
}
$countThis = count($rows);
$total += $countThis;
$offset += $batch;
logLine("Processed $total / $target");
}
@mysqli_query($database->dblink, "ANALYZE TABLE `$CROP_TABLE`");
logLine("Analyze complete.");
logLine("Done.");
endStreaming();
// Refresh stats after build
$stats = getCounts($database->dblink, $WDATA, $CROP_TABLE);
}
?>
</div>
</div>
<br /><br /><br /><br />
<div id="side_info">
<?php
include("Templates/multivillage.tpl");
include("Templates/quest.tpl");
include("Templates/news.tpl");
if(!NEW_FUNCTIONS_DISPLAY_LINKS) {
echo "<br><br><br><br>";
include("Templates/links.tpl");
}
?>
</div>
<div class="clear"></div>
</div>
<div class="footer-stopper"></div>
<div class="clear"></div>
<?php include("Templates/footer.tpl"); include("Templates/res.tpl"); ?>
<div id="stime">
<div id="ltime">
<div id="ltimeWrap">
<?php echo CALCULATED_IN;?> <b><?php echo round(($generator->pageLoadTimeEnd()-$start_timer)*1000); ?></b> ms
<br /><?php echo SERVER_TIME;?> <span id="tp1" class="b"><?php echo date('H:i:s'); ?></span>
</div>
</div>
</div>
<div id="ce"></div>
</div>
</body>
</html>