Merge pull request #137 from newtcv/master

PHP 8 hardening, installer/croppers performance improvements, and admin/docs updates
This commit is contained in:
Catalin Novgorodschi
2026-03-16 10:43:15 +02:00
committed by GitHub
29 changed files with 871 additions and 418 deletions
+13 -6
View File
@@ -1,11 +1,18 @@
# MySQL Database Configuration # MariaDB Database Configuration (preferred)
MYSQL_ROOT_PASSWORD=rootpassword MARIADB_ROOT_PASSWORD=rootpassword
MYSQL_DATABASE=travian MARIADB_DATABASE=travian
MYSQL_USER=travianz MARIADB_USER=travianz
MYSQL_PASSWORD=travianzpass MARIADB_PASSWORD=travianzpass
# Legacy compatibility keys (optional)
# These keys inherit directly from MARIADB_* to avoid duplicate values.
MYSQL_ROOT_PASSWORD=${MARIADB_ROOT_PASSWORD}
MYSQL_DATABASE=${MARIADB_DATABASE}
MYSQL_USER=${MARIADB_USER}
MYSQL_PASSWORD=${MARIADB_PASSWORD}
# Application Configuration # Application Configuration
# These values will be used during the installation wizard # These values will be used during the installation wizard.
# Hostname for database connection (use 'db' when running in Docker) # Hostname for database connection (use 'db' when running in Docker)
DB_HOST=db DB_HOST=db
DB_PORT=3306 DB_PORT=3306
+5 -4
View File
@@ -13,6 +13,7 @@
################################################################################# #################################################################################
function get_map_tile_info($coord_x, $coord_y){ // todo mv to queries file function get_map_tile_info($coord_x, $coord_y){ // todo mv to queries file
$tbp = TB_PREFIX;
$q = 'SELECT map_data.`id` AS village_id, map_data.`fieldtype`, map_data.`oasistype`, map_data.`occupied`, map_data.`image`, '. $q = 'SELECT map_data.`id` AS village_id, map_data.`fieldtype`, map_data.`oasistype`, map_data.`occupied`, map_data.`image`, '.
'oasis_data.`type`, '. 'oasis_data.`type`, '.
'CASE '. 'CASE '.
@@ -21,10 +22,10 @@ function get_map_tile_info($coord_x, $coord_y){ // todo mv to queries file
'ELSE village_data.`owner` '. 'ELSE village_data.`owner` '.
'END AS owner_id, '. 'END AS owner_id, '.
'u.`username` '. 'u.`username` '.
'FROM (SELECT * FROM `s1_wdata` WHERE `x` = '.$coord_x.' AND `y` = '.$coord_y.') AS map_data '. 'FROM (SELECT * FROM `'.$tbp.'wdata` WHERE `x` = '.$coord_x.' AND `y` = '.$coord_y.') AS map_data '.
'LEFT JOIN `s1_odata` AS oasis_data ON map_data.`id` = oasis_data.`wref` '. 'LEFT JOIN `'.$tbp.'odata` AS oasis_data ON map_data.`id` = oasis_data.`wref` '.
'LEFT JOIN `s1_vdata` AS village_data ON village_data.`wref` = map_data.`id` '. 'LEFT JOIN `'.$tbp.'vdata` AS village_data ON village_data.`wref` = map_data.`id` '.
'LEFT JOIN s1_users AS u ON u.`id` = COALESCE(oasis_data.`owner`, village_data.`owner`, 0);'; // todo check this db tables fields -- del doublings like oasistype = '2' and image = 'o2' 'LEFT JOIN `'.$tbp.'users` AS u ON u.`id` = COALESCE(oasis_data.`owner`, village_data.`owner`, 0);'; // todo check this db tables fields -- del doublings like oasistype = '2' and image = 'o2'
$result = mysqli_query($GLOBALS['link'], $q); $result = mysqli_query($GLOBALS['link'], $q);
return mysqli_fetch_assoc($result); return mysqli_fetch_assoc($result);
} }
+6 -2
View File
@@ -12,7 +12,11 @@ include_once("../GameEngine/Generator.php");
include_once("../GameEngine/Technology.php"); include_once("../GameEngine/Technology.php");
include_once("../GameEngine/Message.php"); include_once("../GameEngine/Message.php");
if(isset($_GET['nid']) && is_numeric($_GET['nid'])) $msg = $database->getMessage($_GET['nid'], 3); $msg = [];
$allMessages = [];
$nid = (isset($_GET['nid']) && is_numeric($_GET['nid'])) ? (int) $_GET['nid'] : 0;
if($nid > 0) $msg = $database->getMessage($nid, 3);
else else
{ {
$sql = "SELECT * FROM " . TB_PREFIX . "mdata ORDER BY time DESC "; $sql = "SELECT * FROM " . TB_PREFIX . "mdata ORDER BY time DESC ";
@@ -75,5 +79,5 @@ echo stripslashes(nl2br($bbcoded));
<div id="read_foot" class="msg_foot"></div> <div id="read_foot" class="msg_foot"></div>
</div><?php </div><?php
} }
else echo "Message ID ".$_GET['nid']." doesn't exist!"; else echo "Message ID ".$nid." doesn't exist!";
?> ?>
+10 -4
View File
@@ -12,7 +12,13 @@ include_once("../GameEngine/Generator.php");
include_once("../GameEngine/Technology.php"); include_once("../GameEngine/Technology.php");
include_once("../GameEngine/Message.php"); include_once("../GameEngine/Message.php");
if ($_GET['bid']) $rep = $database->getNotice2($_GET['bid']); $rep = null;
$rep1 = [];
$bid = isset($_GET['bid']) ? (int) $_GET['bid'] : 0;
if ($bid > 0) {
$rep = $database->getNotice2($bid);
}
else else
{ {
$sql = "SELECT * FROM " . TB_PREFIX . "ndata ORDER BY time DESC "; $sql = "SELECT * FROM " . TB_PREFIX . "ndata ORDER BY time DESC ";
@@ -20,7 +26,7 @@ else
$rep1 = $database->mysqli_fetch_all($result); $rep1 = $database->mysqli_fetch_all($result);
} }
if($rep1) if(!empty($rep1))
{ {
?> ?>
<link href="../<?php echo GP_LOCATE; ?>lang/en/lang.css?f4b7d" rel="stylesheet" type="text/css"> <link href="../<?php echo GP_LOCATE; ?>lang/en/lang.css?f4b7d" rel="stylesheet" type="text/css">
@@ -34,7 +40,7 @@ if($rep1)
</div> </div>
<?php <?php
} }
elseif($rep) elseif(!empty($rep))
{ {
?> ?>
<link href="../<?php echo GP_LOCATE; ?>lang/en/lang.css?f4b7d" rel="stylesheet" type="text/css"> <link href="../<?php echo GP_LOCATE; ?>lang/en/lang.css?f4b7d" rel="stylesheet" type="text/css">
@@ -50,5 +56,5 @@ elseif($rep)
$message->readingNotice = $rep; $message->readingNotice = $rep;
include ("../Templates/Notice/".$message->getReportType($rep['ntype']).".tpl"); include ("../Templates/Notice/".$message->getReportType($rep['ntype']).".tpl");
} }
else echo "Report ID ".$_GET['bid']." doesn't exist!"; else echo "Report ID ".$bid." doesn't exist!";
?> ?>
+6 -4
View File
@@ -1,4 +1,4 @@
<?php <?php
################################################################################# #################################################################################
## -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =- ## ## -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =- ##
## --------------------------------------------------------------------------- ## ## --------------------------------------------------------------------------- ##
@@ -12,10 +12,12 @@
include_once("../../GameEngine/config.php"); include_once("../../GameEngine/config.php");
include_once("../../GameEngine/Database.php"); include_once("../../GameEngine/Database.php");
if (!isset($_SESSION)) { if (session_status() !== PHP_SESSION_ACTIVE) {
session_start(); session_start();
}
if (!isset($_SESSION['access']) || (int)$_SESSION['access'] !== (int)ADMIN) {
die("<h1><font color=\"red\">Access Denied: You are not Admin!</font></h1>");
} }
if($_SESSION['access'] != ADMIN) die("<h1><font color=\"red\">Access Denied: You are not Admin!</font></h1>");
set_time_limit(0); set_time_limit(0);
// TODO: truncate ALL tables (in a single query, not like this), // TODO: truncate ALL tables (in a single query, not like this),
// then perform install steps (createDbStructure() && populateWorldData()) // then perform install steps (createDbStructure() && populateWorldData())
+98
View File
@@ -0,0 +1,98 @@
<?php
#################################################################################
## -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =- ##
## --------------------------------------------------------------------------- ##
## Filename users.tpl ##
## Purpose: Admin users list with pagination ##
## ##
#################################################################################
$page = isset($_GET['upage']) ? (int)$_GET['upage'] : 1;
if ($page < 1) {
$page = 1;
}
$perPage = 100;
$offset = ($page - 1) * $perPage;
$totalRes = mysqli_query($GLOBALS['link'], "SELECT COUNT(*) AS cnt FROM " . TB_PREFIX . "users");
$totalRow = $totalRes ? mysqli_fetch_assoc($totalRes) : ['cnt' => 0];
$totalUsers = (int)($totalRow['cnt'] ?? 0);
$totalPages = max(1, (int)ceil($totalUsers / $perPage));
if ($page > $totalPages) {
$page = $totalPages;
$offset = ($page - 1) * $perPage;
}
$sql = "SELECT id, username, email, access, tribe, gold, timestamp " .
"FROM " . TB_PREFIX . "users " .
"ORDER BY id DESC LIMIT " . (int)$perPage . " OFFSET " . (int)$offset;
$result = mysqli_query($GLOBALS['link'], $sql);
function tribeLabel($tribe) {
$tribe = (int)$tribe;
if ($tribe === 1) return 'Roman';
if ($tribe === 2) return 'Teuton';
if ($tribe === 3) return 'Gaul';
return 'N/A';
}
?>
<table id="member" cellpadding="1" cellspacing="1">
<thead>
<tr>
<th colspan="7">Users list (<?php echo $totalUsers; ?>)</th>
</tr>
<tr>
<td>ID</td>
<td>Username</td>
<td>Email</td>
<td>Access</td>
<td>Tribe</td>
<td>Gold</td>
<td>Last activity</td>
</tr>
</thead>
<tbody>
<?php if ($result && mysqli_num_rows($result) > 0) { ?>
<?php while ($row = mysqli_fetch_assoc($result)) { ?>
<tr>
<td><?php echo (int)$row['id']; ?></td>
<td>
<a href="?p=player&amp;uid=<?php echo (int)$row['id']; ?>">
<?php echo htmlspecialchars((string)$row['username'], ENT_QUOTES, 'UTF-8'); ?>
</a>
</td>
<td>
<?php if (!empty($row['email'])) { ?>
<a href="mailto:<?php echo htmlspecialchars((string)$row['email'], ENT_QUOTES, 'UTF-8'); ?>">
<?php echo htmlspecialchars((string)$row['email'], ENT_QUOTES, 'UTF-8'); ?>
</a>
<?php } else { ?>
-
<?php } ?>
</td>
<td><?php echo (int)$row['access']; ?></td>
<td><?php echo tribeLabel($row['tribe']); ?></td>
<td><?php echo (int)$row['gold']; ?></td>
<td><?php echo !empty($row['timestamp']) ? date('d.m.Y H:i:s', (int)$row['timestamp']) : '-'; ?></td>
</tr>
<?php } ?>
<?php } else { ?>
<tr>
<td colspan="7" class="hab">No users found.</td>
</tr>
<?php } ?>
</tbody>
</table>
<div style="margin-top:10px;">
<?php if ($page > 1) { ?>
<a href="?p=users&amp;upage=<?php echo $page - 1; ?>">&laquo; Previous</a>
<?php } ?>
<span style="margin:0 10px;">Page <?php echo $page; ?> / <?php echo $totalPages; ?></span>
<?php if ($page < $totalPages) { ?>
<a href="?p=users&amp;upage=<?php echo $page + 1; ?>">Next &raquo;</a>
<?php } ?>
</div>
+5
View File
@@ -131,6 +131,10 @@ if (!empty($_GET['p'])) {
$subpage = 'Create Users'; $subpage = 'Create Users';
break; break;
case 'users':
$subpage = 'Users List';
break;
case 'admin_log': case 'admin_log':
$subpage = 'Admin Log'; $subpage = 'Admin Log';
break; break;
@@ -559,6 +563,7 @@ if (!empty($_GET['p'])) {
</li> </li>
<li class="sub"><a href="#">Users</a> <li class="sub"><a href="#">Users</a>
<ul> <ul>
<li><a href="?p=users">List Users</a></li>
<li><a href="?p=addUsers">Create Users</a></li> <li><a href="?p=addUsers">Create Users</a></li>
</ul> </ul>
</li> </li>
+73
View File
@@ -0,0 +1,73 @@
# Changelog
All notable changes to this project are documented in this file.
Format inspired by Keep a Changelog and Semantic Versioning principles.
## [Unreleased]
## [2026-03-14] - Stability, PHP 8, Docker and Installer Hardening
### Added
- New root changelog file.
- Admin panel option to list users in the Users menu (`?p=users`).
- New admin users list page with pagination and links to player details: `Admin/Templates/users.tpl`.
- Installer SQL defaults in config form now read from `.env` (with fallback to legacy `MYSQL_*` keys).
- Random database prefix generation per installation session.
- Installer timezone option for Brazil (`America/Sao_Paulo`) and default timezone set to Sao Paulo.
- SSE explicit error payload in croppers builder to avoid infinite reconnect loops.
### Changed
- Docker runtime upgraded to PHP 8.3 (Apache).
- Docker Compose database moved to MariaDB latest.
- Compose configuration modernized (v2 style), env naming standardized to `MARIADB_*` with compatibility support.
- README and Docker docs updated to reflect PHP 8/MariaDB and modern compose commands.
- Installer timezone id mapping fixed in `install/index.php` to match form options.
- Croppers build progress behavior adjusted to report incremental progress in batches.
### Fixed
- PHP 8 compatibility:
- Replaced deprecated `each()` usage.
- Added safe guards for `magic_quotes_*` checks.
- Multiple warnings/fatals from undefined variables and null array offsets across game/admin/installer pages.
- Duplicate constant definition warnings in language file (preventing header issues).
- Duplicate key failures for croppers index creation in PHP 8 (`mysqli_sql_exception` behavior) by making index ops idempotent.
- Installer and runtime DB connection robustness:
- host:port normalization,
- retry logic,
- throwable-safe handling.
- Duplicate primary key errors in units insertion by making `addUnits` idempotent (`ON DUPLICATE KEY UPDATE`).
- Admin template warnings in report/message pages (undefined keys/indexes).
- Admin map tile query now uses dynamic table prefix (`TB_PREFIX`) instead of hardcoded `s1_` tables.
- Session/header issues in admin reset script:
- removed output before `<?php`,
- proper session start checks,
- safe admin access check.
- Mass message and system message flows no longer trigger undefined control variable warnings.
- Plus menu warning fixed by correcting `isset($_GET['id'])` condition precedence.
- Plus page now displays active option remaining time with seconds and end time.
### Performance
- World data generation optimized for large maps:
- temporary session tuning for bulk writes,
- temporary drop/recreate of secondary indexes in `wdata` during heavy insert.
- Croppers generation optimized:
- per-tile bonus query replaced with chunk-level batch bonus calculation,
- reduced lock contention and improved perceived progress updates.
- Build/installer flow updated to avoid stalled progress UI states.
### Security and Safety
- Stricter checks around request/session variables to prevent warnings escalating into header failures.
- More defensive null-safe access patterns in DB result handling.
- Better error propagation in SSE endpoints.
### Developer Experience
- `.env.example` restored and aligned with MariaDB naming.
- Legacy `MYSQL_*` compatibility keys now inherit values from `MARIADB_*`.
- Multiple PHP syntax validation passes performed after patches.
---
## Notes
- This changelog reflects the current hardening/migration cycle and operational fixes applied to make the project run cleanly on PHP 8 + MariaDB.
- If you want strict version tags (e.g. `v8-migration.1`, `v8-migration.2`), convert the dated section above into tagged releases as part of your next release process.
+30 -31
View File
@@ -29,21 +29,21 @@ cp .env.example .env
Edit `.env` file to set your database credentials: Edit `.env` file to set your database credentials:
```env ```env
MYSQL_ROOT_PASSWORD=yourStrongRootPassword MARIADB_ROOT_PASSWORD=yourStrongRootPassword
MYSQL_DATABASE=travian MARIADB_DATABASE=travian
MYSQL_USER=travianz MARIADB_USER=travianz
MYSQL_PASSWORD=yourStrongPassword MARIADB_PASSWORD=yourStrongPassword
``` ```
### 3. Start the Containers ### 3. Start the Containers
```bash ```bash
docker-compose up -d docker compose up -d
``` ```
This command will: This command will:
- Build the TravianZ web application container - Build the TravianZ web application container
- Start a MySQL 5.7 database container - Start a MariaDB (latest) database container
- Start a phpMyAdmin container for database management - Start a phpMyAdmin container for database management
- Set up a network for all containers to communicate - Set up a network for all containers to communicate
@@ -75,35 +75,35 @@ After starting the containers, the following services will be available:
- **TravianZ Web Application:** http://localhost:8080 - **TravianZ Web Application:** http://localhost:8080
- **phpMyAdmin:** http://localhost:8081 - **phpMyAdmin:** http://localhost:8081
- **MySQL Database:** localhost:3306 (for external connections) - **MariaDB Database:** localhost:3306 (for external connections)
## Container Management ## Container Management
### View Running Containers ### View Running Containers
```bash ```bash
docker-compose ps docker compose ps
``` ```
### View Logs ### View Logs
```bash ```bash
# All containers # All containers
docker-compose logs docker compose logs
# Specific container # Specific container
docker-compose logs web docker compose logs web
docker-compose logs db docker compose logs db
docker-compose logs phpmyadmin docker compose logs phpmyadmin
# Follow logs in real-time # Follow logs in real-time
docker-compose logs -f web docker compose logs -f web
``` ```
### Stop Containers ### Stop Containers
```bash ```bash
docker-compose down docker compose down
``` ```
### Stop and Remove All Data ### Stop and Remove All Data
@@ -111,13 +111,13 @@ docker-compose down
**WARNING:** This will delete all database data! **WARNING:** This will delete all database data!
```bash ```bash
docker-compose down -v docker compose down -v
``` ```
### Restart Containers ### Restart Containers
```bash ```bash
docker-compose restart docker compose restart
``` ```
### Rebuild Containers ### Rebuild Containers
@@ -125,8 +125,8 @@ docker-compose restart
If you make changes to the Dockerfile or application code: If you make changes to the Dockerfile or application code:
```bash ```bash
docker-compose down docker compose down
docker-compose up -d --build docker compose up -d --build
``` ```
## Accessing the Containers ## Accessing the Containers
@@ -160,12 +160,12 @@ docker exec -it travianz-web chmod -R 777 /var/www/html/var
1. Make sure the database container is running: 1. Make sure the database container is running:
```bash ```bash
docker-compose ps docker compose ps
``` ```
2. Check database logs: 2. Check database logs:
```bash ```bash
docker-compose logs db docker compose logs db
``` ```
3. Verify the hostname is set to `db` (not `localhost` or `127.0.0.1`) 3. Verify the hostname is set to `db` (not `localhost` or `127.0.0.1`)
@@ -176,7 +176,7 @@ If you need to start the installation over:
1. Stop containers: 1. Stop containers:
```bash ```bash
docker-compose down -v docker compose down -v
``` ```
2. Remove the installed flag: 2. Remove the installed flag:
@@ -187,14 +187,14 @@ If you need to start the installation over:
3. Start containers again: 3. Start containers again:
```bash ```bash
docker-compose up -d docker compose up -d
``` ```
4. Access the installation wizard again at http://localhost:8080/install 4. Access the installation wizard again at http://localhost:8080/install
### Port Already in Use ### Port Already in Use
If port 8080 or 8081 is already in use, edit `docker-compose.yml` and change the ports: If port 8080 or 8081 is already in use, edit `docker compose` configuration (`docker-compose.yml`) and change the ports:
```yaml ```yaml
services: services:
@@ -211,13 +211,13 @@ services:
### Backup Database ### Backup Database
```bash ```bash
docker exec travianz-db mysqldump -u root -p travian > backup_$(date +%Y%m%d).sql docker exec travianz-db mariadb-dump -u root -p travian > backup_$(date +%Y%m%d).sql
``` ```
### Restore Database ### Restore Database
```bash ```bash
docker exec -i travianz-db mysql -u root -p travian < backup_20231125.sql docker exec -i travianz-db mariadb -u root -p travian < backup_20231125.sql
``` ```
### Backup Application Files ### Backup Application Files
@@ -237,7 +237,7 @@ For production environments, consider the following:
2. **Use SSL/TLS:** Set up a reverse proxy (nginx/traefik) with Let's Encrypt 2. **Use SSL/TLS:** Set up a reverse proxy (nginx/traefik) with Let's Encrypt
3. **Limit Database Access:** Remove the database port exposure in `docker-compose.yml` 3. **Limit Database Access:** Remove the database port exposure in the `docker compose` configuration (`docker-compose.yml`)
4. **Regular Backups:** Set up automated backup scripts 4. **Regular Backups:** Set up automated backup scripts
@@ -257,15 +257,14 @@ services:
## Performance Optimization ## Performance Optimization
### MySQL Tuning ### MariaDB Tuning
Edit `docker-compose.yml` to add MySQL configuration: Edit `docker-compose.yml` to add MariaDB configuration:
```yaml ```yaml
services: services:
db: db:
command: > command: >
--default-authentication-plugin=mysql_native_password
--sql_mode="" --sql_mode=""
--max_connections=200 --max_connections=200
--innodb_buffer_pool_size=512M --innodb_buffer_pool_size=512M
@@ -299,8 +298,8 @@ To update TravianZ to the latest version:
```bash ```bash
git pull origin main git pull origin main
docker-compose down docker compose down
docker-compose up -d --build docker compose up -d --build
``` ```
## Support ## Support
+6 -10
View File
@@ -1,9 +1,9 @@
FROM php:7.4-apache FROM php:8.3-apache
# Install system dependencies # Install system dependencies
RUN apt-get update && apt-get install -y \ RUN apt-get update && apt-get install -y \
libpng-dev \ libpng-dev \
libjpeg-dev \ libjpeg62-turbo-dev \
libfreetype6-dev \ libfreetype6-dev \
libzip-dev \ libzip-dev \
zip \ zip \
@@ -26,18 +26,14 @@ RUN a2enmod rewrite headers
# Set working directory # Set working directory
WORKDIR /var/www/html WORKDIR /var/www/html
# Copy application files # Runtime source code is bind-mounted by docker-compose.
COPY . /var/www/html/ # Keep only minimal folder bootstrap inside the image.
RUN mkdir -p /var/www/html/var
# Set permissions
RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 755 /var/www/html \
&& chmod -R 777 /var/www/html/var
# Configure Apache to use /var/www/html as DocumentRoot # Configure Apache to use /var/www/html as DocumentRoot
RUN sed -i 's!/var/www/html!/var/www/html!g' /etc/apache2/sites-available/000-default.conf RUN sed -i 's!/var/www/html!/var/www/html!g' /etc/apache2/sites-available/000-default.conf
# Expose port 80 # Expose Apache port
EXPOSE 80 EXPOSE 80
# Start Apache # Start Apache
+4 -1
View File
@@ -1192,7 +1192,7 @@ class Automation {
if($traps >= $totalTroops){ if($traps >= $totalTroops){
for($i = 1; $i <= 11; $i++) ${'traped'.$i} = $data['t'.$i]; for($i = 1; $i <= 11; $i++) ${'traped'.$i} = $data['t'.$i];
} }
else else if($totalTroops > 0)
{ {
$multiplier = $traps / $totalTroops; $multiplier = $traps / $totalTroops;
@@ -1213,6 +1213,9 @@ class Automation {
} }
} }
} }
else {
for($i = 1; $i <= 11; $i++) ${'traped'.$i} = 0;
}
} }
if(empty($scout) || $NatarCapital){ if(empty($scout) || $NatarCapital){
+12 -10
View File
@@ -477,6 +477,8 @@ class Battle {
$result['Attack_points'] = $rap; $result['Attack_points'] = $rap;
$result['Defend_points'] = $rdp; $result['Defend_points'] = $rdp;
$winner = ($rap > $rdp); $winner = ($rap > $rdp);
$safeRap = max(1, (float) $rap);
$safeRdp = max(1, (float) $rdp);
// Formula for calculating the Morale bonus // Formula for calculating the Morale bonus
// WW villages aren't affected by this bonus // WW villages aren't affected by this bonus
@@ -495,7 +497,7 @@ class Battle {
// $type = 1 Scout, 2 Enforcement // $type = 1 Scout, 2 Enforcement
// $type = 3 Normal, 4 Raid // $type = 3 Normal, 4 Raid
if($type == 1){ if($type == 1){
$holder = pow((($rdp * $moralbonus) / $rap), $Mfactor); $holder = pow((($rdp * $moralbonus) / $safeRap), $Mfactor);
if($holder > 1) $holder = 1; if($holder > 1) $holder = 1;
if ($rdp > $rap) $holder = 1; if ($rdp > $rap) $holder = 1;
@@ -507,7 +509,7 @@ class Battle {
//Defender result //Defender result
$result[2] = 0; $result[2] = 0;
}else if($type == 4) { }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); $holder = $holder / (1 + $holder);
//Attacker result //Attacker result
$result[1] = $winner ? $holder : 1 - $holder; $result[1] = $winner ? $holder : 1 - $holder;
@@ -518,7 +520,7 @@ class Battle {
}else if($type == 3){ }else if($type == 3){
// Attacker // Attacker
$result[1] = ($winner) ? pow((($rdp * $moralbonus) / $rap), $Mfactor) : 1; $result[1] = ($winner) ? pow((($rdp * $moralbonus) / $safeRap), $Mfactor) : 1;
if ($result[1] > 1){ if ($result[1] > 1){
$result[1] = 1; $result[1] = 1;
@@ -527,9 +529,9 @@ class Battle {
} }
// Defender // 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) { if ($result[2] > 1) {
$result[2] = 1; $result[2] = 1;
@@ -569,14 +571,14 @@ class Battle {
$catpMoraleBonus = min(max(($attpop / ($defpop > 0 ? $defpop : 1)) ** 0.3, 1), 3); $catpMoraleBonus = min(max(($attpop / ($defpop > 0 ? $defpop : 1)) ** 0.3, 1), 3);
//New level of the building (only for warsim.php) //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[3] = $this->calculateNewBuildingLevel($tblevel, $catapultsDamage);
$result[4] = $tblevel; $result[4] = $tblevel;
//Results for Automation.php //Results for Automation.php
$result['catapults']['upgrades'] = $upgrades; $result['catapults']['upgrades'] = $upgrades;
$result['catapults']['durability'] = $durability; $result['catapults']['durability'] = $durability;
$result['catapults']['attackDefenseRatio'] = $rap / $rdp; $result['catapults']['attackDefenseRatio'] = $safeRap / $safeRdp;
$result['catapults']['strongerBuildings'] = $strongerbuildings; $result['catapults']['strongerBuildings'] = $strongerbuildings;
$result['catapults']['moraleBonus'] = $catpMoraleBonus; $result['catapults']['moraleBonus'] = $catpMoraleBonus;
} }
@@ -590,19 +592,19 @@ class Battle {
$durability = ($stonemason > 0 ? $bid34[$stonemason]['attri'] / 100 : 1); $durability = ($stonemason > 0 ? $bid34[$stonemason]['attri'] / 100 : 1);
// New level of the building (only for warsim.php) // 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[7] = $this->calculateNewBuildingLevel($walllevel, $ramsDamage);
$result[8] = $walllevel; $result[8] = $walllevel;
// Results for Automation.php // Results for Automation.php
$result['rams']['upgrades'] = $upgrades; $result['rams']['upgrades'] = $upgrades;
$result['rams']['durability'] = $durability; $result['rams']['durability'] = $durability;
$result['rams']['attackDefenseRatio'] = $rap / $rdp; $result['rams']['attackDefenseRatio'] = $safeRap / $safeRdp;
$result['rams']['strongerBuildings'] = $strongerbuildings; $result['rams']['strongerBuildings'] = $strongerbuildings;
$result['rams']['moraleBonus'] = 1; $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; $result['moralBonus'] = $moralbonus;
$total_att_units = count($units['Att_unit']); $total_att_units = count($units['Att_unit']);
+8 -4
View File
@@ -865,10 +865,14 @@ class Building {
$rclay = $uprequire['clay'] - $village->aclay; $rclay = $uprequire['clay'] - $village->aclay;
$rcrop = $uprequire['crop'] - $village->acrop; $rcrop = $uprequire['crop'] - $village->acrop;
$riron = $uprequire['iron'] - $village->airon; $riron = $uprequire['iron'] - $village->airon;
$rwtime = $rwood / $village->getProd("wood") * 3600; $woodProd = $village->getProd("wood");
$rcltime = $rclay / $village->getProd("clay") * 3600; $clayProd = $village->getProd("clay");
$rctime = $rcrop / $village->getProd("crop") * 3600; $cropProd = $village->getProd("crop");
$ritime = $riron / $village->getProd("iron") * 3600; $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 = max($rwtime, $rctime, $rcltime, $ritime);
$reqtime += time(); $reqtime += time();
return $generator->procMtime($reqtime); return $generator->procMtime($reqtime);
+261 -128
View File
@@ -434,28 +434,40 @@ class MYSQLi_DB implements IDbConnection {
* @see \App\Database\IDbConnection::connect() * @see \App\Database\IDbConnection::connect()
*/ */
public function connect() { public function connect() {
// try to connect $host = (string) $this->hostname;
try { $port = (int) $this->port;
$this->dblink = mysqli_connect( $this->hostname, $this->username, $this->password, $this->dbname, $this->port ); if ($port <= 0) $port = 3306;
} catch (\Exception $exception) {
$this->dblink = mysqli_connect( $this->hostname . ':' . $this->port, $this->username, $this->password );
// return on error // If host is in form host:port (common in env files), split it once.
if (mysqli_error($this->dblink)) { if (strpos($host, ':') !== false && substr_count($host, ':') === 1) {
return false; $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 $attempts = 15;
if (mysqli_error($this->dblink)) { $waitMicros = 1000000; // 1 second
return false;
} else { for ($i = 0; $i < $attempts; $i++) {
// connected and DB exists, we're good to go try {
return true; $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"; $q = "SELECT timestamp from " . TB_PREFIX . "deleting where uid = $uid LIMIT 1";
$result = mysqli_query($this->dblink,$q); $result = mysqli_query($this->dblink,$q);
$dbarray = mysqli_fetch_array($result); $dbarray = mysqli_fetch_array($result);
return $dbarray['timestamp']; return isset($dbarray['timestamp']) ? (int)$dbarray['timestamp'] : 0;
} }
function modifyGold($userid, $amt, $mode) { function modifyGold($userid, $amt, $mode) {
@@ -1728,10 +1740,10 @@ public function getBestOasisCropBonus($x, $y) {
function getVillageState($wref, $use_cache = true) { function getVillageState($wref, $use_cache = true) {
// retrieve this value from the full village data cache // retrieve this value from the full village data cache
if ($use_cache && ($cachedValue = self::returnCachedContent(self::$villageFieldsCacheByWorldID, $wref)) && !is_null($cachedValue)) { 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 { } else {
$vil = $this->getVillageByWorldID($wref, $use_cache); $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 // return a single value
if (!$array_passed) { if (!$array_passed) {
self::$villageFieldsCacheByWorldID[$vid[0]] = $result[0]; self::$villageFieldsCacheByWorldID[$vid[0]] = (isset($result[0]) && is_array($result[0])) ? $result[0] : [];
} else { } else {
if ($result && count($result)) { if ($result && count($result)) {
foreach ( $result as $record ) { foreach ( $result as $record ) {
@@ -3715,7 +3727,7 @@ public function getBestOasisCropBonus($x, $y) {
'LIMIT 1'); 'LIMIT 1');
$row = mysqli_fetch_array($result, MYSQLI_ASSOC); $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]; 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"; $q = "SELECT f" . $field . " from " . TB_PREFIX . "fdata where vref = $vid LIMIT 1";
$result = mysqli_query($this->dblink,$q); $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]; return self::$resourceLevelsCache[$vid.$field];
} }
@@ -5874,22 +5886,36 @@ References: User ID/Message ID, Mode
*/ */
function addUnits($vid, $troopsArray = null) { 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(empty($vid)) return;
if (!is_array($vid)) $vid = [$vid]; if (!is_array($vid)) $vid = [$vid];
$types = $values = ""; $types = "";
$typeKeys = [];
$values = [];
if($troopsArray != null){ if($troopsArray != null){
$types = $troopsArray[0]; $typeKeys = $troopsArray[0];
$values = $troopsArray[1]; $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); return mysqli_query($this->dblink,$q);
} }
@@ -6965,13 +6991,47 @@ References: User ID/Message ID, Mode
public function populateWorldData() { public function populateWorldData() {
global $autoprefix; global $autoprefix;
$wdataTable = TB_PREFIX . "wdata";
$droppedIndexes = [];
try { try {
// check if we don't already have world data // 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)) { if ($data_exist && count($data_exist)) {
return false; 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 // load the data generation SQL file
$str = file_get_contents($autoprefix."var/db/datagen-world-data.sql"); $str = file_get_contents($autoprefix."var/db/datagen-world-data.sql");
$str = preg_replace(["'%PREFIX%'", "'%WORLDSIZE%'"], [TB_PREFIX, WORLD_MAX], $str); $str = preg_replace(["'%PREFIX%'", "'%WORLDSIZE%'"], [TB_PREFIX, WORLD_MAX], $str);
@@ -6988,8 +7048,31 @@ References: User ID/Message ID, Mode
if (!$result) { if (!$result) {
return -1; return -1;
} }
} catch (\Exception $e) { } catch (\Throwable $e) {
return -1; 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; 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 { 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); $TBP = defined('TB_PREFIX') ? TB_PREFIX : 's1_';
@ini_set('memory_limit', '1G'); $CROP_TABLE = $TBP . 'croppers';
$WDATA = $TBP . 'wdata';
$TBP = defined('TB_PREFIX') ? TB_PREFIX : 's1_'; $total = 0;
$CROP_TABLE = $TBP . 'croppers'; $inTransaction = false;
$WDATA = $TBP . 'wdata';
// Count once if caller didn't try {
if ($countTotal <= 0) { if ($countTotal <= 0) {
$row = mysqli_fetch_assoc(mysqli_query($this->dblink, $row = mysqli_fetch_assoc(mysqli_query($this->dblink, "SELECT COUNT(*) cnt FROM `$WDATA` WHERE fieldtype IN (1,6)"));
"SELECT COUNT(*) cnt FROM `$WDATA` WHERE fieldtype IN (1,6)")); $countTotal = (int)($row['cnt'] ?? 0);
$countTotal = (int)($row['cnt'] ?? 0); }
}
if ($truncateFirst) { if ($truncateFirst && !mysqli_query($this->dblink, "TRUNCATE TABLE `$CROP_TABLE`")) {
if (!mysqli_query($this->dblink, "TRUNCATE TABLE `$CROP_TABLE`")) { return ['ok'=>false,'msg'=>'TRUNCATE failed: '.mysqli_error($this->dblink)];
return ['ok'=>false,'msg'=>'TRUNCATE failed: '.mysqli_error($this->dblink)]; }
}
}
// Session-level speed knobs (local to this connection) $sessionTuningStatements = [
@mysqli_query($this->dblink, "SET innodb_flush_log_at_trx_commit=2"); "SET SESSION innodb_flush_log_at_trx_commit=2",
@mysqli_query($this->dblink, "SET sync_binlog=0"); "SET SESSION sync_binlog=0",
@mysqli_query($this->dblink, "SET unique_checks=0"); "SET SESSION unique_checks=0",
@mysqli_query($this->dblink, "SET foreign_key_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.
}
}
// Read big windows; write in safe slices to avoid max_allowed_packet if ($batch < 1000) $batch = 1000;
if ($batch < 1000) $batch = 1000; if ($batch > 100000) $batch = 100000;
if ($batch > 100000) $batch = 100000; if($countTotal < 1000) $sliceSize = 200;
if($countTotal < 1000) $sliceSize = 200; elseif($countTotal < 5000) $sliceSize = 500;
elseif($countTotal < 5000) $sliceSize = 500; else $sliceSize = 1000;
elseif($countTotal > 5000) $sliceSize = 1000;
$total = 0; $lastId = 0;
$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];
}
// Cursor pagination (no OFFSET) $rows = [];
while (true) { while ($r = mysqli_fetch_assoc($res)) { $rows[] = $r; }
$res = mysqli_query( if (!$rows) break;
$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 = []; mysqli_begin_transaction($this->dblink);
while ($r = mysqli_fetch_assoc($res)) { $rows[] = $r; } $inTransaction = true;
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 = [];
$n = count($rows); // Compute oasis crop bonus in batch for the whole chunk.
for ($i = 0; $i < $n; $i += $sliceSize) { $bonusByWref = [];
$chunk = array_slice($rows, $i, $sliceSize); $wrefs = [];
$values = []; foreach ($chunk as $r) {
$wrefs[] = (int)$r['wref'];
}
foreach ($chunk as $r) { if (!empty($wrefs)) {
$x = (int)$r['x']; $bonusSql = "SELECT
$y = (int)$r['y']; 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";
// Your existing helper: $bonusRes = mysqli_query($this->dblink, $bonusSql);
$bonus = (int)$this->getBestOasisCropBonus($x, $y); if (!$bonusRes) {
if ($bonus < 0) $bonus = 0; mysqli_rollback($this->dblink);
if ($bonus > 150) $bonus = 150; $inTransaction = false;
return ['ok'=>false,'msg'=>'BONUS SELECT failed: '.mysqli_error($this->dblink),'processed'=>$total,'target'=>$countTotal];
}
$values[] = sprintf("(%d,%d,%d,%d,%d)", while ($bonusRow = mysqli_fetch_assoc($bonusRes)) {
(int)$r['wref'], $x, $y, (int)$r['fieldtype'], $bonus $bonusByWref[(int)$bonusRow['wref']] = (int)($bonusRow['bonus'] ?? 0);
); }
} }
if ($values) { foreach ($chunk as $r) {
// ODKU is cheaper than REPLACE (no DELETE) $x = (int)$r['x'];
$sql = "INSERT INTO `$CROP_TABLE` $y = (int)$r['y'];
(`wref`,`x`,`y`,`fieldtype`,`best_oasis_bonus`) $bonus = (int)($bonusByWref[(int)$r['wref']] ?? 0);
VALUES ".implode(',', $values)." if ($bonus < 0) $bonus = 0;
ON DUPLICATE KEY UPDATE if ($bonus > 150) $bonus = 150;
`x`=VALUES(`x`), $values[] = sprintf("(%d,%d,%d,%d,%d)", (int)$r['wref'], $x, $y, (int)$r['fieldtype'], $bonus);
`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 if ($values) {
$total += count($chunk); $sql = "INSERT INTO `$CROP_TABLE`
if ($reporter) { (`wref`,`x`,`y`,`fieldtype`,`best_oasis_bonus`)
$pct = $countTotal ? min(100, (int)floor(($total / $countTotal) * 100)) : 0; VALUES ".implode(',', $values)."
$reporter($total, $countTotal, $pct); 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];
}
}
mysqli_commit($this->dblink); $total += count($chunk);
if ($reporter) {
$pct = $countTotal ? min(100, (int)floor(($total / $countTotal) * 100)) : 0;
$reporter($total, $countTotal, $pct);
}
}
// advance cursor mysqli_commit($this->dblink);
$lastId = (int)$rows[$n - 1]['wref']; $inTransaction = false;
} $lastId = (int)$rows[$n - 1]['wref'];
}
// Restore checks (optional) foreach(["SET SESSION unique_checks=1", "SET SESSION foreign_key_checks=1"] as $stmt){
@mysqli_query($this->dblink, "SET unique_checks=1"); try {
@mysqli_query($this->dblink, "SET foreign_key_checks=1"); mysqli_query($this->dblink, $stmt);
} catch (Throwable $e) {
// Ignore restore failures if the engine does not support the setting.
}
}
// Analyze once at the end @mysqli_query($this->dblink, "ANALYZE TABLE `$CROP_TABLE`");
@mysqli_query($this->dblink, "ANALYZE TABLE `$CROP_TABLE`"); if ($reporter) { $reporter($total, $countTotal, 100); }
return ['ok'=>true,'msg'=>'Croppers populated','processed'=>$total,'target'=>$countTotal];
if ($reporter) { $reporter($total, $countTotal, 100); } } catch (Throwable $e) {
return ['ok'=>true,'msg'=>'Croppers populated','processed'=>$total,'target'=>$countTotal]; 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('X_MAS', 'Merry Christmas');
define('NEW_YEAR', 'Happy New Year'); define('NEW_YEAR', 'Happy New Year');
define('EASTER', 'Happy Easter'); define('EASTER', 'Happy Easter');
define('PEACE', 'Peace'); if(!defined('PEACE')) define('PEACE', 'Peace');
define('GOLD', 'Gold'); define('GOLD', 'Gold');
define('GOLD_IMG', '<img src=\"/img/x.gif\" class=\"gold\" alt=\"'.GOLD.'\" title=\"'.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('ACTIVE', 'Active');
define('ONLINE', 'Online'); define('ONLINE', 'Online');
define('TUTORIAL', 'Tutorial'); define('TUTORIAL', 'Tutorial');
define('FAQ', 'Faq'); if(!defined('FAQ')) define('FAQ', 'Faq');
define('SPIELREGELN', 'Game Rules'); if(!defined('SPIELREGELN')) define('SPIELREGELN', 'Game Rules');
define('PLAYER_STATISTICS', 'Player statistics'); define('PLAYER_STATISTICS', 'Player statistics');
define('TOTAL_PLAYERS', PLAYERS.' in total'); define('TOTAL_PLAYERS', PLAYERS.' in total');
define('ACTIVE_PLAYERS', 'Active players'); 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('BECOME_COMUNITY2', 'Become a part of one of<br>the biggest gaming<br>communities in the<br>world.');
define('NEWS', 'News'); define('NEWS', 'News');
define('SCREENSHOTS', 'Screenshots'); define('SCREENSHOTS', 'Screenshots');
define('FAQ', 'FAQ'); if(!defined('FAQ')) define('FAQ', 'FAQ');
define('SPIELREGELN', 'Rules'); if(!defined('SPIELREGELN')) define('SPIELREGELN', 'Rules');
define('AGB', 'Terms and Conditions'); 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('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.'); 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_MH', 'This player is Multihunter');
define('PLAYER_BANNED', 'This player is BANNED'); define('PLAYER_BANNED', 'This player is BANNED');
define('PLAYER_VACATION', 'This player is on VACATION'); define('PLAYER_VACATION', 'This player is on VACATION');
define('BANNED', 'Banned'); if(!defined('BANNED')) define('BANNED', 'Banned');
define('GENDER', 'Gender'); define('GENDER', 'Gender');
define('GENDER0', 'n/a'); define('GENDER0', 'n/a');
define('MALE0', 'm'); define('MALE0', 'm');
+13 -5
View File
@@ -684,11 +684,19 @@ class Technology {
$resarray['iron'] = ${'r'.$id}['iron']; $resarray['iron'] = ${'r'.$id}['iron'];
$resarray['crop'] = ${'r'.$id}['crop']; $resarray['crop'] = ${'r'.$id}['crop'];
} }
$rwtime = ($resarray['wood']-$village->awood) / $village->getProd("wood") * 3600; $woodNeed = $resarray['wood'] - $village->awood;
$rcltime = ($resarray['clay']-$village->aclay) / $village->getProd("clay") * 3600; $clayNeed = $resarray['clay'] - $village->aclay;
$ritime = ($resarray['iron']-$village->airon) / $village->getProd("iron") * 3600; $ironNeed = $resarray['iron'] - $village->airon;
$rctime = ($resarray['crop']-$village->acrop) / $village->getProd("crop") * 3600; $cropNeed = $resarray['crop'] - $village->acrop;
if($village->getProd("crop") >= 0) { $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(); $reqtime = max($rwtime,$rcltime,$ritime,$rctime) + time();
} else { } else {
$reqtime = max($rwtime,$rcltime,$ritime); $reqtime = max($rwtime,$rcltime,$ritime);
+15 -3
View File
@@ -24,7 +24,7 @@ class Units {
switch($post['c']) { switch($post['c']) {
case 1: 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 else
{ {
$post = $this->loadUnits($post); $post = $this->loadUnits($post);
@@ -51,7 +51,7 @@ class Units {
break; break;
case 4: 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 else
{ {
$post = $this->loadUnits($post); $post = $this->loadUnits($post);
@@ -98,11 +98,23 @@ class Units {
public function checkErrors(&$post){ public function checkErrors(&$post){
global $database, $village, $session, $generator; 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 // Search by town name
// Coordinates and look confirm name people // Coordinates and look confirm name people
if(isset($post['x']) && isset($post['y']) && $post['x'] != "" && $post['y'] != "") { if(isset($post['x']) && isset($post['y']) && $post['x'] != "" && $post['y'] != "") {
$vid = $database->getVilWref($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'])); else if(isset($post['dname']) && !empty($post['dname'])) $vid = $database->getVillageByName(stripslashes($post['dname']));
+130 -71
View File
@@ -1,97 +1,156 @@
[![Code Triagers Badge](https://www.codetriage.com/shadowss/travianz/badges/users.svg)](https://www.codetriage.com/shadowss/travianz) # TravianZ
[![Maintenance](https://img.shields.io/maintenance/yes/2025.svg)](https://github.com/Shadowss/TravianZ) [![Maintenance](https://img.shields.io/maintenance/yes/2025.svg)](https://github.com/Shadowss/TravianZ)
[![GitHub Release](https://img.shields.io/github/release/Shadowss/TravianZ/all.svg)](https://github.com/Shadowss/TravianZ) [![GitHub Release](https://img.shields.io/github/release/Shadowss/TravianZ/all.svg)](https://github.com/Shadowss/TravianZ)
[![Github All Releases](https://img.shields.io/github/downloads/Shadowss/TravianZ/total.svg)](https://github.com/Shadowss/TravianZ)
[![GitHub contributors](https://img.shields.io/github/contributors/Shadowss/TravianZ.svg)](https://github.com/Shadowss/TravianZ) [![GitHub contributors](https://img.shields.io/github/contributors/Shadowss/TravianZ.svg)](https://github.com/Shadowss/TravianZ)
[![license](https://img.shields.io/github/license/Shadowss/TravianZ.svg)](https://github.com/Shadowss/TravianZ) [![license](https://img.shields.io/github/license/Shadowss/TravianZ.svg)](https://github.com/Shadowss/TravianZ)
[![GitHub last commit](https://img.shields.io/github/last-commit/Shadowss/TravianZ.svg)](https://github.com/Shadowss/TravianZ)
[![Proudly Coded in PHPStorm](https://img.shields.io/badge/coded%20in-PHPStorm-BD5CF3.svg)](https://www.jetbrains.com/buy/opensource/?product=phpstorm)
[![Join the chat at https://gitter.im/TravianZ-V8/Lobby](https://badges.gitter.im/TravianZ-V8/Lobby.svg)](https://gitter.im/TravianZ-V8/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Watch how this was made on YouTube](https://img.shields.io/badge/The%20making%20of...-YouTube-FF0000.svg)](https://www.youtube.com/watch?v=1XiHhpGUmQg&list=PLzV5avt1FFHorlIeoL9YX0pdb9bj-FO84)
======
[![GitHub forks](https://img.shields.io/github/forks/Shadowss/TravianZ.svg?style=social&label=Fork)](https://github.com/Shadowss/TravianZ)
[![GitHub stars](https://img.shields.io/github/stars/Shadowss/TravianZ.svg?style=social&label=Stars)](https://github.com/Shadowss/TravianZ)
[![GitHub watchers](https://img.shields.io/github/watchers/Shadowss/TravianZ.svg?style=social&label=Watch)](https://github.com/Shadowss/TravianZ)
[![GitHub followers](https://img.shields.io/github/followers/Shadowss.svg?style=social&label=Follow)](https://github.com/Shadowss/TravianZ)
[![Twitter URL](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/cata7007)
======
TravianZ Version **v.8.3.5** - BETA 1 TravianZ is an open-source browser strategy game inspired by classic Travian-like gameplay.
======
**Note:** this game is still in a pre-release state, although at this point it is very playable, tested and found to be fairly stable
**WARNING:** please note that ***this is in no way an upgrade*** from the old 8.3.2 version, so please ***do not try to just copy your files over***, This repository currently targets modern local/server setups with PHP 8.x and MariaDB.
since the installer logic has changed and you would just crash your old version
**Docker Quick Start:** ## Project Status
Get up and running quickly with Docker: - Version line: `v8.3.5` (legacy naming)
- Stability: playable and actively maintained
- Migration note: this is not a drop-in upgrade over older `8.3.2` deployments
If you are upgrading from an older installation, do a fresh install and migrate data carefully.
## Quick Start (Docker)
```bash ```bash
# Clone the repository
git clone https://github.com/Shadowss/TravianZ.git git clone https://github.com/Shadowss/TravianZ.git
cd TravianZ cd TravianZ
# Copy environment file
cp .env.example .env cp .env.example .env
docker compose up -d
# Start containers
docker-compose up -d
# Open browser to http://localhost:8080/install
``` ```
For detailed Docker setup instructions, see [DOCKER_README.md](DOCKER_README.md) Then open:
**Quick links:** - `http://localhost:8080/install`
* [Download and Updates](https://github.com/Shadowss/TravianZ) &raquo;&raquo; https://github.com/Shadowss/TravianZ
* [Wiki](https://github.com/Shadowss/TravianZ/wiki)
* [Game Mechanics](http://travian.wikia.com/wiki/Travian_Wiki)
* [The Making Of](https://www.youtube.com/watch?v=1XiHhpGUmQg&list=PLzV5avt1FFHorlIeoL9YX0pdb9bj-FO84) - YouTube Videos about this project
* [Donate to TravianZ](https://www.paypal.me/cata7007)
**Minimum requirements:** Detailed container guide: [DOCKER_README.md](DOCKER_README.md)
* [PHP](http://php.net/) 7.0.0+
* [MySQL Community Server](https://dev.mysql.com/downloads/mysql/) 5.5+
* or alternatively, [MariaDB](https://downloads.mariadb.org/) 5.5+
* Runs fine on Ubuntu 20, Apache2 2.4, MySQL Server 8.0 and PHP 7.4
**Dedicated or shared hosting?** ## System Requirements
We *strongly* recommend using a ***dedicated hosting*** for this game. Big Travian servers used to host Recommended:
thousands of players and the original servers still had about 300ms page display time. The legacy code
in this clone (which was created in 2013) is right now doing about **400 MySQL queries** (questions
to the database) per single page refresh - which is usually well beyond what most shared hostings can support
with a big enough player base.
**Support & Bug reports** - PHP `8.3+`
- MariaDB `latest stable` (or MySQL-compatible server)
- Apache or Nginx with PHP support
- Linux server with enough CPU/RAM for your expected player count
We are usually available to chat at our [Gitter channel](https://gitter.im/TravianZ-V8/Lobby), if you like to ask Notes:
any questions or just talk to the developers about how great their day is today :) As for bug reports, please use
the [Issues tab](https://github.com/Shadowss/TravianZ/issues) and create new issue, whether it's an error in game
or you have a feature request to be included in the play.
**The team** - The game is query-heavy by design (legacy architecture), so shared hosting can become a bottleneck quickly.
* [Shadowss](https://github.com/Shadowss) - project owner and an occasional developer / tester - For medium/large servers, prefer dedicated or well-sized VPS infrastructure.
* [lietuvis10](https://github.com/lietuvis10) - active developer
* [iopietro](https://github.com/iopietro) - alumni developer
* [AL-Kateb](https://github.com/AL-Kateb) - alumni developer
* [martinambrus](https://github.com/martinambrus) - alumni developer
* [Vladyslav](https://github.com/velhbxtyrj) - rigorous game tester
* [phaze1G](https://github.com/phaze1G) - game designer / css support
**Thanks** ## Installation (Web Installer)
Many thanks to all those who recently played around with the project and tested it both, locally and on their 1. Start services (Docker) or prepare your web+DB stack.
own servers. This includes [ZZJHONS](https://github.com/ZZJHONS), [DracoMilesX](https://github.com/DracoMilesX), 2. Open `http://your-host/install`.
[phaze1G](https://github.com/phaze1G) , [martinambrus](https://github.com/martinambrus) and many others who were too shy to let us know that they use and enjoy 3. Fill database settings:
running this game :) - Host: `db` (Docker) or your DB host
- Port: usually `3306`
- DB/User/Password from your environment
4. Complete installer steps:
- DB structure
- World data
- Croppers build
5. After success, access the game root.
Also, our thanks go to all both, the original and occasional developers, especially [yi12345](https://github.com/yi12345/), ## Environment Configuration
[advocaite](https://github.com/advocaite/), [brainiacX](https://github.com/brainiacX/), [ronix](https://github.com/ronix/),
[MisterX](https://github.com/MisterX/), [Elio](https://github.com/eliopinho/) and many others who were part of this
project's history.
Last but not least, our thanks go to [JetBrains](https://www.jetbrains.com/) for lending us a one-year full-featured Use `.env` (copy from `.env.example`) to manage deployment values.
[open-source PHPStorm](https://www.jetbrains.com/buy/opensource/?product=phpstorm) (and other products) license!
Thanks guys, you're awesome :) Main keys:
- `MARIADB_ROOT_PASSWORD`
- `MARIADB_DATABASE`
- `MARIADB_USER`
- `MARIADB_PASSWORD`
- `DB_HOST`
- `DB_PORT`
Legacy compatibility keys (`MYSQL_*`) are still supported and can inherit MariaDB values.
## Admin Panel
Admin entrypoint:
- `http://your-host/Admin/admin.php`
Recent improvements include:
- Users list under the `Users` menu
- Better null/undefined handling in admin templates
- Dynamic table prefix support in map tile queries
## Performance Notes
For large worlds (for example `400x400`), generation tasks can be expensive.
Recent optimizations include:
- world data generation tuning for bulk operations
- croppers generation batching and progress streaming
- safer DB/session handling during installer workflows
For production-like loads, monitor:
- DB CPU and slow queries
- PHP-FPM/Apache worker limits
- disk I/O during installer and reset operations
## Troubleshooting
Common checks:
1. If installer cannot connect to DB:
- verify `DB_HOST`, port, user and password
- in Docker, host should be `db`, not `localhost`
2. If permissions fail during install:
- ensure web user can write required runtime files/folders
3. If pages show warnings after PHP upgrade:
- ensure latest code is deployed
- clear opcode/cache and retry
For container-specific troubleshooting, see [DOCKER_README.md](DOCKER_README.md).
## Development
Useful commands:
```bash
# Start stack
docker compose up -d
# Logs
docker compose logs -f web
# Validate PHP files
find . -name '*.php' -not -path './var/*' -print0 | xargs -0 -n1 php -l
```
Repository references:
- Change history: [CHANGELOG.md](CHANGELOG.md)
- Contribution guide: [CONTRIBUTING.md](CONTRIBUTING.md)
- Code of conduct: [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)
## Community and Support
- Issues: https://github.com/Shadowss/TravianZ/issues
- Wiki: https://github.com/Shadowss/TravianZ/wiki
- Chat: https://gitter.im/TravianZ-V8/Lobby
## Credits
Thanks to the original and current maintainers, contributors, testers, and the TravianZ community.
Special acknowledgement to all legacy authors and maintainers who kept this project alive through multiple iterations.
## License
This project is licensed under the terms described in [LICENSE](LICENSE).
+5 -3
View File
@@ -71,13 +71,15 @@ class Security
if(self::$instance === NULL) if(self::$instance === NULL)
{ {
// Check for magic quotes // Check for magic quotes
if(get_magic_quotes_runtime()) if(function_exists('get_magic_quotes_runtime') && get_magic_quotes_runtime())
{ {
// Dear lord!! This is bad and deprected. Sort it out ;) // Dear lord!! This is bad and deprected. Sort it out ;)
set_magic_quotes_runtime(0); if(function_exists('set_magic_quotes_runtime')) {
set_magic_quotes_runtime(0);
}
} }
if(get_magic_quotes_gpc()) if(function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc())
{ {
// This is also bad and deprected. See http://php.net/magic_quotes for more information. // This is also bad and deprected. See http://php.net/magic_quotes for more information.
$this->magic_quotes_gpc = TRUE; $this->magic_quotes_gpc = TRUE;
+25 -79
View File
@@ -52,6 +52,22 @@ $datetimedp = $golds['dp'];
// Retrieve the current date/time // Retrieve the current date/time
$date2 = strtotime("NOW"); $date2 = strtotime("NOW");
function formatRemainingTime($endTimestamp, $nowTimestamp) {
$remaining = (int)$endTimestamp - (int)$nowTimestamp;
if ($remaining <= 0) {
return '';
}
$days = intdiv($remaining, 86400);
$remaining %= 86400;
$hours = intdiv($remaining, 3600);
$remaining %= 3600;
$mins = intdiv($remaining, 60);
$secs = $remaining % 60;
return 'Remaining: <b>'.$days.'</b> days <b>'.$hours.'</b> hours <b>'.$mins.'</b> mins <b>'.$secs.'</b> secs (until '.date('H:i:s', (int)$endTimestamp).')';
}
if ($datetimep == 0) echo "get PLUS<br>"; if ($datetimep == 0) echo "get PLUS<br>";
else else
{ {
@@ -59,17 +75,7 @@ else
print "Your PLUS advantage has ended.<br>"; print "Your PLUS advantage has ended.<br>";
mysqli_query($database->dblink, "UPDATE " . TB_PREFIX . "users set plus = '0' where `id`='" . $session->uid . "'") or die(mysqli_error($database->dblink)); mysqli_query($database->dblink, "UPDATE " . TB_PREFIX . "users set plus = '0' where `id`='" . $session->uid . "'") or die(mysqli_error($database->dblink));
} else { } else {
echo "<font color='#B3B3B3' size='1'>" . formatRemainingTime($datetimep, $date2) . "</font>";
$holdtotmin = (($datetimep - $date2) / 60);
$holdtothr = (($datetimep - $date2) / 3600);
$holdtotday = intval(($datetimep - $date2) / 86400);
echo "<font color='#B3B3B3' size='1'>Remaining: <b>" . $holdtotday . "</b> days";
$holdhr = intval($holdtothr - ($holdtotday * 24));
echo " <b>" . ($holdhr) . "</b> hours ";
$holdmr = intval($holdtotmin - (($holdhr * 60) + ($holdtotday * 1440)));
echo "<b> " . ($holdmr) . "</b> mins</font>";
} }
} }
?> ?>
@@ -120,23 +126,8 @@ if (PLUS_TIME >= 86400) {
<?php <?php
$tl_b1 = $golds['b1']; $tl_b1 = $golds['b1'];
if ($tl_b1 < $date2) { if ($tl_b1 >= $date2) {
print " "; echo "<font color='#B3B3B3' size='1'>" . formatRemainingTime($tl_b1, $date2) . "</font> ";
} else {
$holdtotmin1 = (($tl_b1 - $date2) / 60);
$holdtothr1 = (($tl_b1 - $date2) / 3600);
$holdtotday1 = intval(($tl_b1 - $date2) / 86400);
$holdhr1 = intval($holdtothr1 - ($holdtotday1 * 24));
$holdmr1 = intval($holdtotmin1 - (($holdhr1 * 60) + ($holdtotday1 * 1440)));
}
if ($tl_b1 < $date2) {
print " ";
} else {
echo "<font color='#B3B3B3' size='1'>Remaining <b> " . $holdtotday1 . "</b> days ";
echo "<b> " . ($holdhr1) . "</b> hours ";
echo "<b> " . ($holdmr1) . "</b> mins</font> ";
} }
?> ?>
&nbsp; </span> &nbsp; </span>
@@ -189,23 +180,8 @@ if ($session->access != BANNED) {
<?php <?php
$tl_b2 = $golds['b2']; $tl_b2 = $golds['b2'];
if ($tl_b2 < $date2) { if ($tl_b2 >= $date2) {
print " "; echo "<font color='#B3B3B3' size='1'>" . formatRemainingTime($tl_b2, $date2) . "</font>";
} else {
$holdtotmin2 = (($tl_b2 - $date2) / 60);
$holdtothr2 = (($tl_b2 - $date2) / 3600);
$holdtotday2 = intval(($tl_b2 - $date2) / 86400);
$holdhr2 = intval($holdtothr2 - ($holdtotday2 * 24));
$holdmr2 = intval($holdtotmin2 - (($holdhr2 * 60) + ($holdtotday2 * 1440)));
}
if ($tl_b2 < $date2) {
print " ";
} else {
echo "<font color='#B3B3B3' size='1'>Remaining: <b> " . $holdtotday2 . "</b> days ";
echo "<b> " . ($holdhr2) . "</b> hours ";
echo "<b> " . ($holdmr2) . "</b> mins<font>";
} }
?> ?>
&nbsp; </span> &nbsp; </span>
@@ -259,23 +235,8 @@ if ($session->access != BANNED) {
<?php <?php
$tl_b3 = $golds['b3']; $tl_b3 = $golds['b3'];
if ($tl_b3 < $date2) { if ($tl_b3 >= $date2) {
print " "; echo "<font color='#B3B3B3' size='1'>" . formatRemainingTime($tl_b3, $date2) . "</font>";
} else {
$holdtotmin3 = (($tl_b3 - $date2) / 60);
$holdtothr3 = (($tl_b3 - $date2) / 3600);
$holdtotday3 = intval(($tl_b3 - $date2) / 86400);
$holdhr3 = intval($holdtothr3 - ($holdtotday3 * 24));
$holdmr3 = intval($holdtotmin3 - (($holdhr3 * 60) + ($holdtotday3 * 1440)));
}
if ($tl_b3 < $date2) {
print " ";
} else {
echo "<font color='#B3B3B3' size='1'>Remaining: <b> " . $holdtotday3 . "</b> days ";
echo "<b> " . ($holdhr3) . "</b> hours ";
echo "<b> " . ($holdmr3) . "</b> mins</font>";
} }
?> ?>
&nbsp; </span> &nbsp; </span>
@@ -327,23 +288,8 @@ if ($session->access != BANNED) {
<?php <?php
$tl_b4 = $golds['b4']; $tl_b4 = $golds['b4'];
if ($tl_b4 < $date2) { if ($tl_b4 >= $date2) {
print " "; echo "<font color='#B3B3B3' size='1'>" . formatRemainingTime($tl_b4, $date2) . "</font>";
} else {
$holdtotmin4 = (($tl_b4 - $date2) / 60);
$holdtothr4 = (($tl_b4 - $date2) / 3600);
$holdtotday4 = intval(($tl_b4 - $date2) / 86400);
$holdhr4 = intval($holdtothr4 - ($holdtotday4 * 24));
$holdmr4 = intval($holdtotmin4 - (($holdhr4 * 60) + ($holdtotday4 * 1440)));
}
if ($tl_b4 < $date2) {
print " ";
} else {
echo "<font color='#B3B3B3' size='1'>Remaining: <b> " . $holdtotday4 . "</b> days ";
echo "<b> " . ($holdhr4) . "</b> hours ";
echo "<b> " . ($holdmr4) . "</b> mins</font>";
} }
?> ?>
&nbsp; </span> &nbsp; </span>
+1 -1
View File
@@ -6,7 +6,7 @@
if(!isset($_GET['id']) && @(basename($_SERVER['REQUEST_URI']) !== 'a2b2.php')) { if(!isset($_GET['id']) && @(basename($_SERVER['REQUEST_URI']) !== 'a2b2.php')) {
echo "class=\"selected\""; echo "class=\"selected\"";
} }
if(isset($_GET['id']) && ($_GET['id'] == 1) || strlen($_GET['id']) === 3) { if(isset($_GET['id']) && (($_GET['id'] == 1) || strlen((string)$_GET['id']) === 3)) {
echo "class=\"selected\""; echo "class=\"selected\"";
} }
+31 -10
View File
@@ -42,6 +42,27 @@ $csrf = $_SESSION['csrf_cb'];
function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); } 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) // Ensure table exists (minimal schema, unsigned tinyints)
mysqli_query($database->dblink, "CREATE TABLE IF NOT EXISTS `$CROP_TABLE` ( mysqli_query($database->dblink, "CREATE TABLE IF NOT EXISTS `$CROP_TABLE` (
`wref` INT UNSIGNED NOT NULL PRIMARY KEY, `wref` INT UNSIGNED NOT NULL PRIMARY KEY,
@@ -53,10 +74,10 @@ mysqli_query($database->dblink, "CREATE TABLE IF NOT EXISTS `$CROP_TABLE` (
CHECK (`best_oasis_bonus` IN (0,25,50,75,100,125,150)) CHECK (`best_oasis_bonus` IN (0,25,50,75,100,125,150))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"); ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
// Helpful indexes (ignore errors if already exist) // Helpful indexes (idempotent, avoids PHP8 mysqli duplicate-index exceptions)
@mysqli_query($database->dblink, "CREATE INDEX `idx_ft_bonus_xy` ON `$CROP_TABLE` (`fieldtype`, `best_oasis_bonus`, `x`, `y`)"); createIndexIfMissing($database->dblink, $CROP_TABLE, 'idx_ft_bonus_xy', '`fieldtype`, `best_oasis_bonus`, `x`, `y`');
@mysqli_query($database->dblink, "CREATE INDEX `idx_xy` ON `$CROP_TABLE` (`x`, `y`)"); createIndexIfMissing($database->dblink, $CROP_TABLE, 'idx_xy', '`x`, `y`');
@mysqli_query($database->dblink, "CREATE INDEX `idx_bonus` ON `$CROP_TABLE` (`best_oasis_bonus`)"); createIndexIfMissing($database->dblink, $CROP_TABLE, 'idx_bonus', '`best_oasis_bonus`');
// ---------- Helpers ---------- // ---------- Helpers ----------
function worldSizeLabel(): string { function worldSizeLabel(): string {
@@ -110,12 +131,12 @@ if ($action === 'truncate') {
$notice = "Croppers table truncated."; $notice = "Croppers table truncated.";
} }
if ($action === 'reindex') { if ($action === 'reindex') {
@mysqli_query($database->dblink, "DROP INDEX `idx_ft_bonus_xy` ON `$CROP_TABLE`"); dropIndexIfExists($database->dblink, $CROP_TABLE, 'idx_ft_bonus_xy');
@mysqli_query($database->dblink, "DROP INDEX `idx_xy` ON `$CROP_TABLE`"); dropIndexIfExists($database->dblink, $CROP_TABLE, 'idx_xy');
@mysqli_query($database->dblink, "DROP INDEX `idx_bonus` ON `$CROP_TABLE`"); dropIndexIfExists($database->dblink, $CROP_TABLE, 'idx_bonus');
@mysqli_query($database->dblink, "CREATE INDEX `idx_ft_bonus_xy` ON `$CROP_TABLE` (`fieldtype`, `best_oasis_bonus`, `x`, `y`)"); createIndexIfMissing($database->dblink, $CROP_TABLE, 'idx_ft_bonus_xy', '`fieldtype`, `best_oasis_bonus`, `x`, `y`');
@mysqli_query($database->dblink, "CREATE INDEX `idx_xy` ON `$CROP_TABLE` (`x`, `y`)"); createIndexIfMissing($database->dblink, $CROP_TABLE, 'idx_xy', '`x`, `y`');
@mysqli_query($database->dblink, "CREATE INDEX `idx_bonus` ON `$CROP_TABLE` (`best_oasis_bonus`)"); createIndexIfMissing($database->dblink, $CROP_TABLE, 'idx_bonus', '`best_oasis_bonus`');
$notice = "Indexes rebuilt."; $notice = "Indexes rebuilt.";
} }
if ($action === 'estimate') { if ($action === 'estimate') {
+7 -10
View File
@@ -1,5 +1,3 @@
version: '3.8'
services: services:
web: web:
build: build:
@@ -10,7 +8,6 @@ services:
- "8080:80" - "8080:80"
volumes: volumes:
- ./:/var/www/html - ./:/var/www/html
- ./var:/var/www/html/var
environment: environment:
- APACHE_DOCUMENT_ROOT=/var/www/html - APACHE_DOCUMENT_ROOT=/var/www/html
depends_on: depends_on:
@@ -20,13 +17,13 @@ services:
restart: unless-stopped restart: unless-stopped
db: db:
image: mysql:5.7 image: mariadb:latest
container_name: travianz-db container_name: travianz-db
environment: environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpassword} MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD:-${MYSQL_ROOT_PASSWORD:-rootpassword}}
MYSQL_DATABASE: ${MYSQL_DATABASE:-travian} MARIADB_DATABASE: ${MARIADB_DATABASE:-${MYSQL_DATABASE:-travian}}
MYSQL_USER: ${MYSQL_USER:-travianz} MARIADB_USER: ${MARIADB_USER:-${MYSQL_USER:-travianz}}
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-travianzpass} MARIADB_PASSWORD: ${MARIADB_PASSWORD:-${MYSQL_PASSWORD:-travianzpass}}
volumes: volumes:
- db-data:/var/lib/mysql - db-data:/var/lib/mysql
- ./var/db:/docker-entrypoint-initdb.d:ro - ./var/db:/docker-entrypoint-initdb.d:ro
@@ -35,7 +32,7 @@ services:
networks: networks:
- travianz-network - travianz-network
restart: unless-stopped restart: unless-stopped
command: --default-authentication-plugin=mysql_native_password --sql_mode="" command: --sql_mode=""
phpmyadmin: phpmyadmin:
image: phpmyadmin/phpmyadmin:latest image: phpmyadmin/phpmyadmin:latest
@@ -44,7 +41,7 @@ services:
PMA_HOST: db PMA_HOST: db
PMA_PORT: 3306 PMA_PORT: 3306
PMA_USER: root PMA_USER: root
PMA_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpassword} PMA_PASSWORD: ${MARIADB_ROOT_PASSWORD:-${MYSQL_ROOT_PASSWORD:-rootpassword}}
ports: ports:
- "8081:80" - "8081:80"
depends_on: depends_on:
+3 -2
View File
@@ -67,8 +67,8 @@ $reporter = function($done, $target, $pct) use (&$lastPing) {
if (connection_aborted()) { exit; } // client left if (connection_aborted()) { exit; } // client left
}; };
// Run it (fresh world => truncateFirst=true; big batch on dedicated server) // Run it (fresh world => truncateFirst=true). Slightly smaller batch keeps progress updates more frequent.
$out = $database->populateCroppers($total, true, 20000, $reporter); $out = $database->populateCroppers($total, true, 2000, $reporter);
if (!empty($out['ok'])) { if (!empty($out['ok'])) {
sse_send([ sse_send([
@@ -79,6 +79,7 @@ if (!empty($out['ok'])) {
]); ]);
} else { } else {
sse_send([ sse_send([
'error'=>true,
'pct'=>0, 'pct'=>0,
'done'=>0, 'done'=>0,
'total'=>(int)$total, 'total'=>(int)$total,
+6 -3
View File
@@ -7,7 +7,7 @@ include("templates/script.tpl");
if(!isset($_GET['s'])) { if(!isset($_GET['s'])) {
$_GET['s']=0; $_GET['s']=0;
} }
$tz=(isset($_GET['t']))? $_GET['t']:1; $tz=(isset($_GET['t']))? (int)$_GET['t'] : 13;
switch($tz) { switch($tz) {
case 1: $t_zone="Africa/Dakar";break; case 1: $t_zone="Africa/Dakar";break;
case 2: $t_zone="America/New_York";break; case 2: $t_zone="America/New_York";break;
@@ -18,8 +18,11 @@ $tz=(isset($_GET['t']))? $_GET['t']:1;
case 7: $t_zone="Australia/Melbourne";break; case 7: $t_zone="Australia/Melbourne";break;
case 8: $t_zone="Europe/Bucharest";break; case 8: $t_zone="Europe/Bucharest";break;
case 9: $t_zone="Europe/London";break; case 9: $t_zone="Europe/London";break;
case 10: $t_zone="Indian/Maldives";break; case 10: $t_zone="Europe/Bratislava";break;
case 11: $t_zone="Pacific/Fiji";break; case 11: $t_zone="Indian/Maldives";break;
case 12: $t_zone="Pacific/Fiji";break;
case 13: $t_zone="America/Sao_Paulo";break;
default: $t_zone="America/Sao_Paulo";break;
} }
date_default_timezone_set($t_zone); date_default_timezone_set($t_zone);
?> ?>
+65 -6
View File
@@ -19,6 +19,64 @@
if(isset($_GET['c']) && $_GET['c'] == 1) { if(isset($_GET['c']) && $_GET['c'] == 1) {
echo "<div class=\"headline\"><span class=\"f10 c5\">Error creating constant.php check cmod.</span></div><br>"; echo "<div class=\"headline\"><span class=\"f10 c5\">Error creating constant.php check cmod.</span></div><br>";
} }
@session_start();
$envPath = dirname(__DIR__, 2) . '/.env';
$envDefaults = [];
if(file_exists($envPath)) {
$lines = @file($envPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if(is_array($lines)) {
foreach($lines as $line) {
$line = trim($line);
if($line === '' || $line[0] === '#') {
continue;
}
$eqPos = strpos($line, '=');
if($eqPos === false) {
continue;
}
$key = trim(substr($line, 0, $eqPos));
$value = trim(substr($line, $eqPos + 1));
if($key === '') {
continue;
}
if((strlen($value) >= 2) && (($value[0] === '"' && substr($value, -1) === '"') || ($value[0] === "'" && substr($value, -1) === "'"))) {
$value = substr($value, 1, -1);
}
$envDefaults[$key] = $value;
}
// Resolve ${VAR} references using parsed values first, then process env.
foreach($envDefaults as $key => $value) {
$envDefaults[$key] = preg_replace_callback('/\$\{([A-Z0-9_]+)\}/i', function($m) use ($envDefaults) {
$ref = $m[1];
if(isset($envDefaults[$ref])) return $envDefaults[$ref];
$fromEnv = getenv($ref);
return ($fromEnv !== false) ? $fromEnv : '';
}, $value);
}
}
}
$dbHost = $envDefaults['DB_HOST'] ?? 'localhost';
$dbPort = $envDefaults['DB_PORT'] ?? '3306';
$dbUser = $envDefaults['MARIADB_USER'] ?? ($envDefaults['MYSQL_USER'] ?? '');
$dbPass = $envDefaults['MARIADB_PASSWORD'] ?? ($envDefaults['MYSQL_PASSWORD'] ?? '');
$dbName = $envDefaults['MARIADB_DATABASE'] ?? ($envDefaults['MYSQL_DATABASE'] ?? '');
if(empty($_SESSION['install_random_prefix'])) {
try {
$_SESSION['install_random_prefix'] = 's' . substr(bin2hex(random_bytes(2)), 0, 4) . '_';
} catch (Throwable $e) {
$_SESSION['install_random_prefix'] = 's' . str_pad((string) mt_rand(0, 9999), 4, '0', STR_PAD_LEFT) . '_';
}
}
$dbPrefix = $_SESSION['install_random_prefix'];
?> ?>
<form action="process.php" method="post" id="dataform"> <form action="process.php" method="post" id="dataform">
@@ -41,6 +99,7 @@ echo "<div class=\"headline\"><span class=\"f10 c5\">Error creating constant.php
<select name="tzone" onChange="refresh(this.value)"> <select name="tzone" onChange="refresh(this.value)">
<option value="1,Africa/Dakar" <?php if ($tz==1) echo "selected";?>>Africa</option> <option value="1,Africa/Dakar" <?php if ($tz==1) echo "selected";?>>Africa</option>
<option value="2,America/New_York" <?php if ($tz==2) echo "selected";?>>America</option> <option value="2,America/New_York" <?php if ($tz==2) echo "selected";?>>America</option>
<option value="13,America/Sao_Paulo" <?php if ($tz==13) echo "selected";?>>Brazil (Sao Paulo)</option>
<option value="3,Antarctica/Casey" <?php if ($tz==3) echo "selected";?>>Antarctica</option> <option value="3,Antarctica/Casey" <?php if ($tz==3) echo "selected";?>>Antarctica</option>
<option value="4,Arctic/Longyearbyen" <?php if ($tz==4) echo "selected";?>>Arctic</option> <option value="4,Arctic/Longyearbyen" <?php if ($tz==4) echo "selected";?>>Arctic</option>
<option value="5,Asia/Kuala_Lumpur" <?php if ($tz==5) echo "selected";?>>Asia</option> <option value="5,Asia/Kuala_Lumpur" <?php if ($tz==5) echo "selected";?>>Asia</option>
@@ -416,27 +475,27 @@ echo "<div class=\"headline\"><span class=\"f10 c5\">Error creating constant.php
<table> <table>
<tr> <tr>
<td><span class="f9 c6">Hostname:</span></td> <td><span class="f9 c6">Hostname:</span></td>
<td><input name="sserver" type="text" id="sserver" value="localhost"></td> <td><input name="sserver" type="text" id="sserver" value="<?php echo htmlspecialchars($dbHost, ENT_QUOTES, 'UTF-8'); ?>"></td>
</tr> </tr>
<tr> <tr>
<td><span class="f9 c6">Port:</span></td> <td><span class="f9 c6">Port:</span></td>
<td><input name="sport" type="text" id="sport" value="3306"></td> <td><input name="sport" type="text" id="sport" value="<?php echo htmlspecialchars($dbPort, ENT_QUOTES, 'UTF-8'); ?>"></td>
</tr> </tr>
<tr> <tr>
<td><span class="f9 c6">Username:</span></td> <td><span class="f9 c6">Username:</span></td>
<td><input name="suser" type="text" id="suser" value=""></td> <td><input name="suser" type="text" id="suser" value="<?php echo htmlspecialchars($dbUser, ENT_QUOTES, 'UTF-8'); ?>"></td>
</tr> </tr>
<tr> <tr>
<td><span class="f9 c6">Password:</span></td> <td><span class="f9 c6">Password:</span></td>
<td><input type="password" name="spass" id="spass"></td> <td><input type="password" name="spass" id="spass" value="<?php echo htmlspecialchars($dbPass, ENT_QUOTES, 'UTF-8'); ?>"></td>
</tr> </tr>
<tr> <tr>
<td><span class="f9 c6">DB name:</span></td> <td><span class="f9 c6">DB name:</span></td>
<td><input type="text" name="sdb" id="sdb"></td> <td><input type="text" name="sdb" id="sdb" value="<?php echo htmlspecialchars($dbName, ENT_QUOTES, 'UTF-8'); ?>"></td>
</tr> </tr>
<tr> <tr>
<td><span class="f9 c6">Prefix:</span></td> <td><span class="f9 c6">Prefix:</span></td>
<td><input type="text" name="prefix" id="prefix" value="s1_" size="5"></td> <td><input type="text" name="prefix" id="prefix" value="<?php echo htmlspecialchars($dbPrefix, ENT_QUOTES, 'UTF-8'); ?>" size="7"></td>
</tr> </tr>
<td><span class="f9 c6">Type:</span></td> <td><span class="f9 c6">Type:</span></td>
<td> <td>
+2 -2
View File
@@ -24,7 +24,7 @@ if($_POST && count($_POST))
$strMailtext = ""; $strMailtext = "";
while(list($strName,$value) = each($_POST)) foreach($_POST as $strName => $value)
{ {
if(is_array($value)) if(is_array($value))
{ {
@@ -39,7 +39,7 @@ if($_POST && count($_POST))
} }
} }
if(get_magic_quotes_gpc()) if(function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc())
{ {
$strMailtext = stripslashes($strMailtext); $strMailtext = stripslashes($strMailtext);
} }
+5
View File
@@ -17,6 +17,11 @@ AccessLogger::logRequest();
$max_per_pass = 1000; $max_per_pass = 1000;
// Default flow flags used later in template conditions.
$NextStep = false;
$NextStep2 = false;
$done = false;
if (mysqli_num_rows(mysqli_query($database->dblink,"SELECT id FROM ".TB_PREFIX."users WHERE access = 9 AND id = ".(int) $session->uid)) != '1') die("Hacking attemp!"); if (mysqli_num_rows(mysqli_query($database->dblink,"SELECT id FROM ".TB_PREFIX."users WHERE access = 9 AND id = ".(int) $session->uid)) != '1') die("Hacking attemp!");
if (@$_POST['submit'] == "Send") if (@$_POST['submit'] == "Send")
+7
View File
@@ -15,6 +15,13 @@ include_once("GameEngine/Account.php");
AccessLogger::logRequest(); AccessLogger::logRequest();
$max_per_pass = 1000; $max_per_pass = 1000;
// Default flow flags used later in template conditions.
$NextStep = false;
$NextStep2 = false;
$done = false;
$Interupt = false;
if (mysqli_num_rows(mysqli_query($database->dblink,"SELECT id FROM ".TB_PREFIX."users WHERE access = 9 AND id = ".$session->uid)) != '1') die("Hacking attempt!"); if (mysqli_num_rows(mysqli_query($database->dblink,"SELECT id FROM ".TB_PREFIX."users WHERE access = 9 AND id = ".$session->uid)) != '1') die("Hacking attempt!");
if(isset($_GET['del'])){ if(isset($_GET['del'])){