diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml
index 35b8ea8..1d62c7d 100644
--- a/.github/workflows/sonarcloud.yml
+++ b/.github/workflows/sonarcloud.yml
@@ -14,6 +14,15 @@ jobs:
- uses: actions/checkout@v6.0.2
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
+ - name: Set up PHP with Xdebug
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.4'
+ coverage: xdebug
+ - name: Install dependencies
+ run: composer install --prefer-dist --no-progress --no-interaction
+ - name: Run PHPUnit with coverage
+ run: php -d xdebug.mode=coverage vendor/bin/phpunit --coverage-clover=coverage.xml
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
diff --git a/.gitignore b/.gitignore
index eff6085..bf0ecf3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,6 @@ vendor/
# PHPUnit cache
.phpunit.result.cache
+# PHPUnit coverage output
+coverage.xml
+
diff --git a/Handler/AllyListHandler.php b/Handler/AllyListHandler.php
new file mode 100644
index 0000000..38d1c59
--- /dev/null
+++ b/Handler/AllyListHandler.php
@@ -0,0 +1,46 @@
+requireGrant($this->getRequiredGrant())) {
+ return;
+ }
+ if (!isset($data['tag'], $data['allyList'])) {
+ throw new UnexpectedValueException('Allylist: Missing Value');
+ }
+
+ $tag = filter_var($data['tag']);
+ $members = [];
+ foreach ($data['allyList'] as $member) {
+ if (!isset($member['player'], $member['points'], $member['rank'], $member['coords'])) {
+ throw new UnexpectedValueException('Allylist: Missing Ally Detailed list');
+ }
+ $members[] = [
+ 'pseudo' => filter_var($member['player']),
+ 'points' => filter_var($member['points'], FILTER_SANITIZE_NUMBER_INT),
+ 'coords' => explode(':', $member['coords']),
+ 'rang' => filter_var($member['rank'], FILTER_SANITIZE_NUMBER_INT),
+ ];
+ }
+
+ $this->registerCallback('ally_list', ['list' => $members, 'tag' => $tag]);
+ $this->io->set(['type' => 'ally_list', 'tag' => $tag]);
+ $this->logAction('ally_list', ['tag' => $tag]);
+ }
+
+ public function getType(): string
+ {
+ return 'ally_list';
+ }
+
+ public function getRequiredGrant(): string
+ {
+ return 'ranking';
+ }
+}
diff --git a/Handler/BuildingsHandler.php b/Handler/BuildingsHandler.php
new file mode 100644
index 0000000..309123e
--- /dev/null
+++ b/Handler/BuildingsHandler.php
@@ -0,0 +1,70 @@
+requireGrant($this->getRequiredGrant())) {
+ return;
+ }
+
+ $coordinates = filter_var($data['planet']['coords']);
+ $planetName = filter_var($data['planet']['name']);
+ $planetType = filter_var($data['planet']['type']);
+ $planetId = filter_var($data['planet']['id'], FILTER_VALIDATE_INT);
+ if (!isset($coordinates, $planetName, $planetType)) {
+ throw new UnexpectedValueException('Buildings- Missing data');
+ }
+
+ $buildings = $data['buildings'];
+ $coordinates = Check::coords($coordinates);
+ [$galaxy, $system, $position] = explode(':', $coordinates);
+ $planetType = $this->resolvePlanetType($planetType);
+
+ $columns = ['id', 'galaxy', 'system', 'row', 'name'];
+ $values = [$planetId, $galaxy, $system, $position, $planetName];
+ foreach ($this->database['buildings'] as $code) {
+ if (isset($buildings[$code])) {
+ $columns[] = $code;
+ $values[] = (int)$buildings[$code];
+ }
+ }
+
+ $updatePairs = ['name = VALUES(name)', 'galaxy = VALUES(galaxy)', 'system = VALUES(system)', 'row = VALUES(row)'];
+ foreach ($this->database['buildings'] as $code) {
+ if (isset($buildings[$code])) {
+ $updatePairs[] = "`$code` = VALUES(`$code`)";
+ }
+ }
+
+ $query = "INSERT INTO " . TABLE_USER_BUILDING . " (`" . implode('`, `', $columns) . "`)
+ VALUES (" . implode(', ', array_map(static function ($value) {
+ return is_numeric($value) ? $value : "'$value'";
+ }, $values)) . ")
+ ON DUPLICATE KEY UPDATE " . implode(', ', $updatePairs);
+ $this->db->sql_query($query);
+
+ $this->setPageUpdatedResponse('buildings', $coordinates);
+ $this->registerCallback('buildings', [
+ 'coords' => explode(':', $coordinates),
+ 'planet_type' => $planetType,
+ 'planet_name' => $planetName,
+ 'buildings' => $buildings,
+ ]);
+ $this->logAction('buildings', ['coords' => $coordinates, 'planet_name' => $planetName]);
+ }
+
+ public function getType(): string
+ {
+ return 'buildings';
+ }
+
+ public function getRequiredGrant(): string
+ {
+ return 'empire';
+ }
+}
diff --git a/Handler/CombatReportHandler.php b/Handler/CombatReportHandler.php
new file mode 100644
index 0000000..c12ed60
--- /dev/null
+++ b/Handler/CombatReportHandler.php
@@ -0,0 +1,218 @@
+ 'PT', 203 => 'GT', 204 => 'CLE', 205 => 'CLO', 206 => 'CR',
+ 207 => 'VB', 208 => 'VC', 209 => 'REC', 210 => 'SE', 211 => 'BMD',
+ 212 => 'SAT', 213 => 'DST', 214 => 'EDLM', 215 => 'TRA', 217 => 'FOR',
+ 218 => 'FAU', 219 => 'ECL',
+ ];
+
+ private const DEFENSE_TECHNOLOGIES = [
+ 401 => 'LM', 402 => 'LLE', 403 => 'LLO', 404 => 'CG', 405 => 'AI',
+ 406 => 'LP', 407 => 'PB', 408 => 'GB', 502 => 'MIC', 503 => 'MIP',
+ ];
+
+ public function handle(array $data): void
+ {
+ $reportType = $data['_xtense_request_type'] ?? 'rc';
+ $rawPayload = $data['_xtense_raw_payload'] ?? json_encode($data);
+ unset($data['_xtense_request_type'], $data['_xtense_raw_payload']);
+ $api = filter_var($data['ogapilnk'] ?? '', FILTER_DEFAULT) ?: '';
+
+ if (!isset($data['timestamp'])) {
+ throw new UnexpectedValueException('Combat Report: Timestamp not sent');
+ }
+ if (!$this->requireGrant($this->getRequiredGrant())) {
+ return;
+ }
+
+ $this->registerCallback('rc', ['json' => $rawPayload, 'api' => $api]);
+ $timestamp = (int)$data['timestamp'];
+ $existing = $this->db->sql_fetch_row($this->db->sql_query(
+ "SELECT `id_rc` FROM " . TABLE_PARSEDRC . " WHERE `dateRC` = '{$timestamp}'"
+ ));
+ if (isset($existing[0])) {
+ $this->io->set(['type' => $reportType]);
+ $this->logAction($reportType);
+ return;
+ }
+
+ $coordinates = $this->parseCoordinates($data['coordinates'])['coords'];
+ $rounds = $data['combatRounds'] ?? [];
+ $winner = match ($data['result']['winner'] ?? null) {
+ 'draw' => 'N',
+ 'attacker' => 'A',
+ 'defender' => 'D',
+ default => throw new UnexpectedValueException('Combat Report: Result not found'),
+ };
+
+ $loot = $this->resourceAmounts($data['result']['loot']['resources'] ?? []);
+ $debris = $this->resourceAmounts($data['result']['debris']['resources'] ?? []);
+ $losses = ['attacker' => 0, 'defender' => 0];
+ foreach ($data['result']['totalValueOfUnitsLost'] ?? [] as $loss) {
+ if (isset($losses[$loss['side'] ?? ''])) {
+ $losses[$loss['side']] = (int)($loss['value'] ?? 0);
+ }
+ }
+ $moonChance = (int)($data['result']['moonCreation']['chance'] ?? 0);
+
+ $defenderObject = $data['defenderSpaceObject'] ?? [];
+ $objectType = $this->db->sql_escape_string((string)($defenderObject['type'] ?? ''));
+ $parsedCoordinates = $this->parseCoordinates($coordinates);
+ $objectResult = $this->db->sql_query("SELECT id FROM " . TABLE_USER_BUILDING . "
+ WHERE galaxy = {$parsedCoordinates['galaxy']} AND system = {$parsedCoordinates['system']}
+ AND row = {$parsedCoordinates['row']} AND type = '{$objectType}'
+ ORDER BY last_update DESC LIMIT 1");
+ $objectRow = $this->db->sql_fetch_row($objectResult);
+ $astroObjectId = isset($objectRow[0]) ? (int)$objectRow[0] : null;
+ if ($astroObjectId === null) {
+ throw new UnexpectedValueException("Combat Report: Astro object not found for coordinates {$coordinates} with type {$objectType}");
+ }
+
+ $this->db->sql_query("INSERT INTO " . TABLE_PARSEDRC . " (
+ `dateRC`, `astro_object_id`, `nb_rounds`, `victoire`, `pertes_A`, `pertes_D`,
+ `gain_M`, `gain_C`, `gain_D`, `debris_M`, `debris_C`, `lune`
+ ) VALUES (
+ {$timestamp}, {$astroObjectId}, " . count($rounds) . ", '{$winner}', {$losses['attacker']}, {$losses['defender']},
+ {$loot['metal']}, {$loot['crystal']}, {$loot['deuterium']}, {$debris['metal']}, {$debris['crystal']}, {$moonChance}
+ )");
+ $reportId = (int)$this->db->sql_insertid();
+
+ $combatants = $this->indexCombatants($data['fleets'] ?? []);
+ foreach ($rounds as $roundNumber => $round) {
+ $statistics = $this->roundStatistics($round['statistics'] ?? []);
+ $this->db->sql_query("INSERT INTO " . TABLE_PARSEDRCROUND . " (
+ `id_rc`, `numround`, `attaque_tir`, `attaque_puissance`, `defense_bouclier`,
+ `attaque_bouclier`, `defense_tir`, `defense_puissance`
+ ) VALUES (
+ {$reportId}, " . (int)$roundNumber . ", {$statistics['attacker']['hits']}, {$statistics['attacker']['strength']},
+ {$statistics['defender']['absorbedDamage']}, {$statistics['attacker']['absorbedDamage']},
+ {$statistics['defender']['hits']}, {$statistics['defender']['strength']}
+ )");
+ $roundId = (int)$this->db->sql_insertid();
+ if ($roundId === 0) {
+ throw new UnexpectedValueException("Combat Report: Failed to insert round {$roundNumber}");
+ }
+
+ foreach ($round['fleets'] ?? [] as $fleet) {
+ $this->insertRoundFleet($roundId, $astroObjectId, $fleet, $combatants);
+ }
+ }
+
+ $this->io->set(['type' => $reportType]);
+ $this->logAction($reportType);
+ }
+
+ private function resourceAmounts(array $resources): array
+ {
+ $amounts = ['metal' => 0, 'crystal' => 0, 'deuterium' => 0];
+ foreach ($resources as $resource) {
+ $name = $resource['resource'] ?? '';
+ if (array_key_exists($name, $amounts)) {
+ $amounts[$name] = (int)($resource['amount'] ?? 0);
+ }
+ }
+ return $amounts;
+ }
+
+ private function indexCombatants(array $fleets): array
+ {
+ $combatants = ['attacker' => [], 'defender' => []];
+ foreach ($fleets as $fleet) {
+ $side = $fleet['side'] ?? '';
+ if (!isset($combatants[$side])) {
+ continue;
+ }
+ $research = ['armor' => 0, 'weapon' => 0, 'shield' => 0];
+ foreach ($fleet['combatResearchPercentage'] ?? [] as $technology) {
+ $key = match ((int)($technology['id'] ?? 0)) {
+ 109 => 'armor',
+ 110 => 'weapon',
+ 111 => 'shield',
+ default => null,
+ };
+ if ($key !== null) {
+ $research[$key] = (int)($technology['percentage'] ?? 0);
+ }
+ }
+ $player = $fleet['player'] ?? [];
+ $combatants[$side][(string)($player['name'] ?? '')] = $research;
+ }
+ return $combatants;
+ }
+
+ private function roundStatistics(array $statistics): array
+ {
+ $result = [
+ 'attacker' => ['hits' => 0, 'strength' => 0, 'absorbedDamage' => 0],
+ 'defender' => ['hits' => 0, 'strength' => 0, 'absorbedDamage' => 0],
+ ];
+ foreach ($statistics as $stat) {
+ $side = $stat['side'] ?? '';
+ if (!isset($result[$side])) {
+ continue;
+ }
+ foreach (['hits', 'strength', 'absorbedDamage'] as $field) {
+ $result[$side][$field] = (int)($stat[$field] ?? 0);
+ }
+ }
+ return $result;
+ }
+
+ private function insertRoundFleet(int $roundId, int $astroObjectId, array $fleet, array $combatants): void
+ {
+ $side = $fleet['side'] ?? '';
+ if (!in_array($side, ['attacker', 'defender'], true) || !isset($fleet['player'])) {
+ return;
+ }
+
+ $player = $fleet['player'];
+ $playerName = $this->db->sql_escape_string((string)($player['name'] ?? 'Unknown'));
+ $playerId = (int)($player['id'] ?? -1);
+ $research = $combatants[$side][(string)($player['name'] ?? '')] ?? ['armor' => 0, 'weapon' => 0, 'shield' => 0];
+ $units = $side === 'attacker'
+ ? $this->database['fleet']
+ : array_values(array_unique(array_merge($this->database['fleet'], ['SAT', 'FOR'], $this->database['defense'])));
+ $mapping = self::FLEET_TECHNOLOGIES + ($side === 'defender' ? self::DEFENSE_TECHNOLOGIES : []);
+ $unitCounts = array_fill_keys($units, 0);
+
+ foreach ($fleet['technologies'] ?? [] as $technology) {
+ $technologyId = (int)($technology['technologyId'] ?? 0);
+ $unitCode = $mapping[$technologyId] ?? null;
+ if ($unitCode !== null && array_key_exists($unitCode, $unitCounts)) {
+ $unitCounts[$unitCode] = (int)($technology['remaining'] ?? 0);
+ }
+ }
+
+ $unitValues = [];
+ foreach ($units as $unit) {
+ $unitValues[] = in_array($unit, ['PB', 'GB'], true)
+ ? (int)($unitCounts[$unit] > 0)
+ : $unitCounts[$unit];
+ }
+ $table = $side === 'attacker' ? TABLE_ROUND_ATTACK : TABLE_ROUND_DEFENSE;
+ $columns = $side === 'attacker'
+ ? '`id_rcround`, `player`, `astro_object_id`, `Armes`, `Bouclier`, `Protection`'
+ : '`id_rcround`, `player_id`, `astro_object_id`, `Armes`, `Bouclier`, `Protection`';
+ $playerValue = $side === 'attacker' ? "'{$playerName}'" : (string)$playerId;
+ $unitColumns = '`' . implode('`, `', $units) . '`';
+ $this->db->sql_query("INSERT INTO {$table} ({$columns}, {$unitColumns}) VALUES (
+ {$roundId}, {$playerValue}, {$astroObjectId}, {$research['weapon']}, {$research['shield']}, {$research['armor']}, " . implode(', ', $unitValues) . ')');
+ }
+
+ public function getType(): string
+ {
+ return 'rc';
+ }
+
+ public function getRequiredGrant(): string
+ {
+ return 'messages';
+ }
+}
diff --git a/Handler/DefenseHandler.php b/Handler/DefenseHandler.php
new file mode 100644
index 0000000..2d198e3
--- /dev/null
+++ b/Handler/DefenseHandler.php
@@ -0,0 +1,75 @@
+requireGrant($this->getRequiredGrant())) {
+ return;
+ }
+
+ $coordinates = filter_var($data['planet']['coords']);
+ $planetName = filter_var($data['planet']['name']);
+ $planetId = filter_var($data['planet']['id'], FILTER_VALIDATE_INT);
+ $planetType = filter_var($data['planet']['type']);
+ $defense = $data['defense'] ?? [];
+ if (!isset($coordinates, $planetName, $planetType)) {
+ throw new UnexpectedValueException('Defense: Missing Planet Details');
+ }
+
+ $parsed = $this->parseCoordinates($coordinates);
+ $planetType = $this->resolvePlanetType($planetType);
+ $planetTypeString = $this->planetTypeToString($planetType);
+ $escapedName = $this->db->sql_escape_string($planetName);
+ $playerId = (int)($this->userData['player_id'] ?? -1);
+
+ $this->db->sql_query("INSERT INTO " . TABLE_USER_BUILDING . "
+ (id, type, galaxy, system, row, name, player_id)
+ VALUES ({$planetId}, '{$planetTypeString}', {$parsed['galaxy']}, {$parsed['system']}, {$parsed['row']}, '{$escapedName}', {$playerId})
+ ON DUPLICATE KEY UPDATE name = '{$escapedName}'");
+
+ $fields = [];
+ $values = [];
+ $updates = [];
+ $callbackDefense = [];
+ foreach ($this->database['defense'] as $code) {
+ if (isset($defense[$code])) {
+ $level = (int)$defense[$code];
+ $fields[] = "`$code`";
+ $values[] = $level;
+ $updates[] = "`$code` = {$level}";
+ $callbackDefense[$code] = $level;
+ }
+ }
+
+ if ($fields !== []) {
+ $this->db->sql_query("INSERT INTO " . TABLE_GAME_PLAYER_DEFENSE . "
+ (astro_object_id, " . implode(', ', $fields) . ")
+ VALUES ({$planetId}, " . implode(', ', $values) . ")
+ ON DUPLICATE KEY UPDATE " . implode(', ', $updates));
+ }
+
+ $this->setPageUpdatedResponse('defense', $parsed['coords']);
+ $this->registerCallback('defense', [
+ 'coords' => explode(':', $parsed['coords']),
+ 'planet_type' => $planetType,
+ 'planet_name' => $planetName,
+ 'defense' => $callbackDefense,
+ ]);
+ $this->logAction('defense', ['coords' => $parsed['coords'], 'planet_name' => $planetName]);
+ }
+
+ public function getType(): string
+ {
+ return 'defense';
+ }
+
+ public function getRequiredGrant(): string
+ {
+ return 'empire';
+ }
+}
diff --git a/Handler/FleetHandler.php b/Handler/FleetHandler.php
new file mode 100644
index 0000000..e2d86db
--- /dev/null
+++ b/Handler/FleetHandler.php
@@ -0,0 +1,79 @@
+requireGrant($this->getRequiredGrant())) {
+ return;
+ }
+
+ $coordinates = filter_var($data['planet']['coords']);
+ $planetName = filter_var($data['planet']['name']);
+ $planetType = filter_var($data['planet']['type']);
+ $planetId = filter_var($data['planet']['id'], FILTER_VALIDATE_INT);
+ $fleet = $data['fleet'] ?? [];
+ if (!isset($coordinates, $planetName, $planetType)) {
+ throw new UnexpectedValueException('Fleet: Missing Planet Details');
+ }
+
+ $parsed = $this->parseCoordinates($coordinates);
+ $planetType = $this->resolvePlanetType($planetType);
+ $planetTypeString = $this->planetTypeToString($planetType);
+ $escapedName = $this->db->sql_escape_string($planetName);
+ $playerId = (int)($this->userData['player_id'] ?? -1);
+
+ $this->db->sql_query("INSERT INTO " . TABLE_USER_BUILDING . "
+ (id, type, galaxy, system, row, name, player_id)
+ VALUES ({$planetId}, '{$planetTypeString}', {$parsed['galaxy']}, {$parsed['system']}, {$parsed['row']}, '{$escapedName}', {$playerId})
+ ON DUPLICATE KEY UPDATE name = '{$escapedName}'");
+
+ $this->upsertValues(TABLE_GAME_PLAYER_FLEET, 'astro_object_id', $planetId, $this->database['fleet'], $fleet);
+ $this->upsertValues(TABLE_USER_BUILDING, 'id', $planetId, $this->database['fleet_production'], $fleet);
+
+ $this->setPageUpdatedResponse('fleet', $parsed['coords']);
+ $this->registerCallback('fleet', [
+ 'coords' => explode(':', $parsed['coords']),
+ 'planet_type' => $planetType,
+ 'planet_name' => $planetName,
+ 'fleet' => $fleet,
+ ]);
+ $this->logAction('fleet', ['coords' => $parsed['coords'], 'planet_name' => $planetName]);
+ }
+
+ private function upsertValues(string $table, string $keyColumn, int|false $key, array $codes, array $data): void
+ {
+ $fields = [];
+ $values = [];
+ $updates = [];
+ foreach ($codes as $code) {
+ if (isset($data[$code])) {
+ $level = (int)$data[$code];
+ $fields[] = "`$code`";
+ $values[] = $level;
+ $updates[] = "`$code` = {$level}";
+ }
+ }
+ if ($fields === []) {
+ return;
+ }
+
+ $this->db->sql_query("INSERT INTO {$table} ({$keyColumn}, " . implode(', ', $fields) . ")
+ VALUES ({$key}, " . implode(', ', $values) . ")
+ ON DUPLICATE KEY UPDATE " . implode(', ', $updates));
+ }
+
+ public function getType(): string
+ {
+ return 'fleet';
+ }
+
+ public function getRequiredGrant(): string
+ {
+ return 'empire';
+ }
+}
diff --git a/Handler/MessageHandler.php b/Handler/MessageHandler.php
new file mode 100644
index 0000000..7674e83
--- /dev/null
+++ b/Handler/MessageHandler.php
@@ -0,0 +1,285 @@
+requireGrant($this->getRequiredGrant())) {
+ return;
+ }
+
+ $lineType = $data['type'] ?? '';
+ $returnAsSpy = ($data['_xtense_return_as'] ?? null) === 'spy';
+ unset($data['_xtense_return_as'], $data['_xtense_raw_payload']);
+ switch ($lineType) {
+ case 'msg':
+ $this->handlePersonalMessage($data);
+ break;
+ case 'ally_msg':
+ $this->handleAllyMessage($data);
+ break;
+ case 'spy':
+ case 'spy_shared':
+ $this->handleSpyReport($data);
+ break;
+ case 'ennemy_spy':
+ $this->handleEnemySpy($data);
+ break;
+ case 'rc_cdr':
+ $this->handleRecyclerReport($data);
+ break;
+ case 'expedition':
+ case 'expedition_shared':
+ $this->handleExpedition($data);
+ break;
+ default:
+ throw new UnexpectedValueException('Message category not found ' . $lineType);
+ }
+
+ $this->io->set(['type' => ($returnAsSpy ? 'spy' : 'messages')]);
+ $this->logAction('messages');
+ }
+
+ private function handlePersonalMessage(array $line): void
+ {
+ if (!isset($line['coords'], $line['from'], $line['subject'], $line['message'])) {
+ throw new UnexpectedValueException('Personal Message: Incomplete Metadata ');
+ }
+ $coordinates = Check::coords($line['coords']);
+ $this->registerCallback('msg', [
+ 'coords' => explode(':', $coordinates),
+ 'from' => filter_var($line['from']),
+ 'subject' => filter_var($line['subject']),
+ 'message' => filter_var($line['message']),
+ 'time' => $line['date'] ?? null,
+ ]);
+ }
+
+ private function handleAllyMessage(array $line): void
+ {
+ if (!isset($line['from'], $line['tag'], $line['message'])) {
+ throw new UnexpectedValueException('Alliance Message: Incomplete Metadata ');
+ }
+ $this->registerCallback('ally_msg', [
+ 'from' => filter_var($line['from']),
+ 'tag' => filter_var($line['tag']),
+ 'message' => filter_var($line['message']),
+ 'time' => $line['date'] ?? null,
+ ]);
+ }
+
+ private function handleSpyReport(array $line): void
+ {
+ if (!isset(
+ $line['player']['name'],
+ $line['planet']['name'],
+ $line['planet']['coordinates'],
+ $line['planet']['id'],
+ $line['resources'],
+ $line['proba'],
+ $line['activity'],
+ $line['date']
+ )) {
+ throw new UnexpectedValueException('Spy: Incomplete Metadata ');
+ }
+
+ global $databaseSpyId;
+ $coordinates = Check::coords($line['planet']['coordinates']);
+ $content = [];
+ foreach (['resources', 'buildings', 'lfBuildings', 'research', 'lfResearch', 'fleet', 'defense'] as $section) {
+ if (!isset($line[$section]) || !is_array($line[$section])) {
+ $line[$section] = [];
+ }
+ if ($line[$section] !== []) {
+ $content += $line[$section];
+ }
+ }
+
+ $spy = [
+ 'proba' => min(100, (int)filter_var($line['proba'], FILTER_SANITIZE_NUMBER_INT)),
+ 'activite' => min(59, (int)filter_var($line['activity'], FILTER_SANITIZE_NUMBER_INT)),
+ 'coords' => $coordinates,
+ 'content' => $content,
+ 'time' => (int)filter_var($line['date'], FILTER_SANITIZE_NUMBER_INT),
+ 'player_name' => filter_var($line['player']['name']),
+ 'planet_name' => filter_var($line['planet']['name']),
+ 'planet_id' => (int)$line['planet']['id'],
+ ];
+ $this->registerCallback($line['type'], $spy);
+
+ $databaseSpyId = is_array($databaseSpyId ?? null) ? $databaseSpyId : [];
+ $spyDatabase = [];
+ foreach ($databaseSpyId as $mapping) {
+ $spyDatabase += $mapping;
+ }
+ $fields = ['`astro_object_id`', '`planet_name`', '`metal`', '`crystal`', '`deuterium`', '`sender_id`', '`proba`', '`activite`', '`dateRE`'];
+ $values = [
+ $spy['planet_id'],
+ "'" . $this->db->sql_escape_string($spy['planet_name']) . "'",
+ (int)($content['metal'] ?? 0),
+ (int)($content['crystal'] ?? 0),
+ (int)($content['deuterium'] ?? 0),
+ (int)$this->userData['id'],
+ $spy['proba'],
+ $spy['activite'],
+ $spy['time'],
+ ];
+ foreach ($spyDatabase as $code => $column) {
+ if (in_array($column, ['metal', 'crystal', 'deuterium'], true) || !isset($content[$code])) {
+ continue;
+ }
+ $fields[] = '`' . $column . '`';
+ $values[] = (int)$content[$code];
+ }
+
+ $exists = $this->db->sql_numrows($this->db->sql_query(
+ "SELECT `id` FROM " . TABLE_PARSEDSPY . " WHERE `astro_object_id` = '{$spy['planet_id']}' AND `dateRE` = '{$spy['time']}'"
+ ));
+ if ($exists) {
+ return;
+ }
+
+ $this->db->sql_query('INSERT INTO ' . TABLE_PARSEDSPY . ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')');
+ $spiedPlayerId = -1;
+ $playerResult = $this->db->sql_query("SELECT `id` FROM " . TABLE_GAME_PLAYER . " WHERE `name` = '" . $this->db->sql_escape_string($spy['player_name']) . "'");
+ if ($this->db->sql_numrows($playerResult) > 0) {
+ $playerRow = $this->db->sql_fetch_row($playerResult);
+ $spiedPlayerId = (int)$playerRow[0];
+ }
+
+ $parsed = $this->parseCoordinates($coordinates);
+ $planetType = (($line['planet']['type'] ?? null) === '3') ? 'moon' : 'planet';
+ $insertFields = ['id', 'galaxy', 'system', 'row', 'type', 'name', 'last_update', 'last_update_user_id'];
+ $insertValues = [
+ $spy['planet_id'], $parsed['galaxy'], $parsed['system'], $parsed['row'],
+ "'{$planetType}'", "'" . $this->db->sql_escape_string($spy['planet_name']) . "'",
+ $spy['time'], (int)$this->userData['id'],
+ ];
+ $updates = ['name = VALUES(name)', 'last_update = VALUES(last_update)', 'last_update_user_id = VALUES(last_update_user_id)', 'type = VALUES(type)', 'galaxy = VALUES(galaxy)', 'system = VALUES(system)', 'row = VALUES(row)'];
+ if ($spiedPlayerId !== -1) {
+ $insertFields[] = 'player_id';
+ $insertValues[] = $spiedPlayerId;
+ $updates[] = 'player_id = VALUES(player_id)';
+ }
+ $this->appendMappedUpsertFields($line['buildings'] ?? [], $databaseSpyId['buildings'] ?? [], $insertFields, $insertValues, $updates);
+ $this->db->sql_query('INSERT INTO ' . TABLE_USER_BUILDING . ' (`' . implode('`, `', $insertFields) . '`) VALUES (' . implode(', ', $insertValues) . ') ON DUPLICATE KEY UPDATE ' . implode(', ', $updates));
+
+ $this->appendCategoryUpsert($spy['planet_id'], $line['defense'] ?? [], $databaseSpyId['defense'] ?? [], TABLE_GAME_PLAYER_DEFENSE);
+ $this->appendCategoryUpsert($spy['planet_id'], $line['fleet'] ?? [], $databaseSpyId['fleet'] ?? [], TABLE_GAME_PLAYER_FLEET);
+ if ($spiedPlayerId !== -1) {
+ $this->appendCategoryUpsert($spiedPlayerId, $line['research'] ?? [], $databaseSpyId['labo'] ?? [], TABLE_USER_TECHNOLOGY, 'player_id');
+ }
+
+ $this->db->sql_query('UPDATE ' . TABLE_USER . ' SET `spy_imports` = spy_imports + 1 WHERE `id` = ' . (int)$this->userData['id']);
+ update_statistic('spyimports', '1');
+ $this->logAction('messages', ['added_spy' => $spy['planet_name'], 'added_spy_coords' => $spy['coords']]);
+ }
+
+ private function appendMappedUpsertFields(array $data, array $mapping, array &$fields, array &$values, array &$updates): void
+ {
+ foreach ($mapping as $code => $column) {
+ if (isset($data[$code])) {
+ $fields[] = $column;
+ $values[] = (int)$data[$code];
+ $updates[] = "`{$column}` = VALUES(`{$column}`)";
+ }
+ }
+ }
+
+ private function appendCategoryUpsert(int $objectId, array $data, array $mapping, string $table, string $keyColumn = 'astro_object_id'): void
+ {
+ $fields = [];
+ $values = [];
+ $updates = [];
+ foreach ($mapping as $code => $column) {
+ if (isset($data[$code])) {
+ $level = (int)$data[$code];
+ $fields[] = "`{$column}`";
+ $values[] = $level;
+ $updates[] = "`{$column}` = {$level}";
+ }
+ }
+ if ($fields === []) {
+ return;
+ }
+ $this->db->sql_query("INSERT INTO {$table} ({$keyColumn}, " . implode(', ', $fields) . ') VALUES (' . $objectId . ', ' . implode(', ', $values) . ') ON DUPLICATE KEY UPDATE ' . implode(', ', $updates));
+ }
+
+ private function handleEnemySpy(array $line): void
+ {
+ if (!isset($line['from'], $line['to'], $line['proba'], $line['date'])) {
+ throw new UnexpectedValueException('Ennemy Spy: Incomplete Metadata ');
+ }
+ $probability = filter_var($line['proba'], FILTER_SANITIZE_NUMBER_INT);
+ $from = Check::coords($line['from']);
+ $to = Check::coords($line['to']);
+ $userId = (int)($this->userData['user_id'] ?? $this->userData['id']);
+ $query = "SELECT spy_id FROM " . TABLE_PARSEDSPYEN . " WHERE sender_id = '{$userId}' AND dateSpy = '{$line['date']}'";
+ if ($this->db->sql_numrows($this->db->sql_query($query)) === 0) {
+ $this->db->sql_query("INSERT INTO " . TABLE_PARSEDSPYEN . " (`dateSpy`, `from`, `to`, `proba`, `sender_id`) VALUES ('{$line['date']}', '{$from}', '{$to}', '{$probability}', '{$userId}')");
+ }
+ $this->registerCallback('ennemy_spy', [
+ 'from' => explode(':', $from),
+ 'to' => explode(':', $to),
+ 'proba' => (int)$probability,
+ 'time' => $line['date'],
+ ]);
+ add_log('info', ['toolbar' => $this->toolbarInfo, 'message' => 'a été espionné avec une probabilité de ' . $probability]);
+ }
+
+ private function handleRecyclerReport(array $line): void
+ {
+ $required = ['nombre', 'coords', 'M_recovered', 'C_recovered', 'M_total', 'C_total', 'date'];
+ foreach ($required as $field) {
+ if (!isset($line[$field])) {
+ throw new UnexpectedValueException('Harvesting Report: Incomplete Metadata ');
+ }
+ }
+ $values = [];
+ foreach (['nombre', 'M_recovered', 'C_recovered', 'M_total', 'C_total'] as $field) {
+ $values[$field] = filter_var($line[$field], FILTER_SANITIZE_NUMBER_INT);
+ }
+ $coordinates = Check::coords($line['coords']);
+ $userId = (int)($this->userData['user_id'] ?? $this->userData['id']);
+ $query = "SELECT `id_rec` FROM " . TABLE_PARSEDREC . " WHERE `sender_id` = '{$userId}' AND `dateRec` = '{$line['date']}'";
+ if ($this->db->sql_numrows($this->db->sql_query($query)) === 0) {
+ $this->db->sql_query("INSERT INTO " . TABLE_PARSEDREC . " (`dateRec`, `coordinates`, `nbRec`, `M_total`, `C_total`, `M_recovered`, `C_recovered`, `sender_id`) VALUES ('{$line['date']}', '{$coordinates}', '{$values['nombre']}', '{$values['M_total']}', '{$values['C_total']}', '{$values['M_recovered']}', '{$values['C_recovered']}', '{$userId}')");
+ }
+ $this->registerCallback('rc_cdr', [
+ 'nombre' => (int)$values['nombre'],
+ 'coords' => explode(':', $coordinates),
+ 'M_reco' => (int)$values['M_recovered'],
+ 'C_reco' => (int)$values['C_recovered'],
+ 'M_total' => (int)$values['M_total'],
+ 'C_total' => (int)$values['C_total'],
+ 'time' => $line['date'],
+ ]);
+ }
+
+ private function handleExpedition(array $line): void
+ {
+ if (!isset($line['coords'], $line['content'])) {
+ throw new UnexpectedValueException('Expedition Message: Incomplete Metadata ');
+ }
+ $coordinates = Check::coords(filter_var($line['coords']), 1);
+ $this->registerCallback($line['type'], [
+ 'time' => $line['date'] ?? null,
+ 'coords' => explode(':', $coordinates),
+ 'content' => filter_var($line['content']),
+ ]);
+ }
+
+ public function getType(): string
+ {
+ return 'messages';
+ }
+
+ public function getRequiredGrant(): string
+ {
+ return 'messages';
+ }
+}
diff --git a/Handler/OverviewHandler.php b/Handler/OverviewHandler.php
new file mode 100644
index 0000000..ca8d2df
--- /dev/null
+++ b/Handler/OverviewHandler.php
@@ -0,0 +1,144 @@
+requireGrant($this->getRequiredGrant())) {
+ return;
+ }
+
+ $playerDetails = filter_var_array($data['playerdetails'], [
+ 'player_name' => FILTER_DEFAULT,
+ 'player_id' => FILTER_DEFAULT,
+ 'playerclass_explorer' => FILTER_DEFAULT,
+ 'playerclass_miner' => FILTER_DEFAULT,
+ 'playerclass_warrior' => FILTER_VALIDATE_INT,
+ 'player_officer_commander' => FILTER_VALIDATE_INT,
+ 'player_officer_amiral' => FILTER_VALIDATE_INT,
+ 'player_officer_engineer' => FILTER_VALIDATE_INT,
+ 'player_officer_geologist' => FILTER_VALIDATE_INT,
+ 'player_officer_technocrate' => FILTER_VALIDATE_INT,
+ ]);
+
+ $universeDetails = filter_var_array($data['unidetails'], [
+ 'uni_version' => FILTER_DEFAULT,
+ 'uni_url' => FILTER_DEFAULT,
+ 'uni_lang' => FILTER_DEFAULT,
+ 'uni_name' => FILTER_DEFAULT,
+ 'uni_time' => FILTER_VALIDATE_INT,
+ 'uni_speed' => FILTER_VALIDATE_INT,
+ 'uni_speed_fleet_peaceful' => FILTER_VALIDATE_INT,
+ 'uni_speed_fleet_war' => FILTER_VALIDATE_INT,
+ 'uni_speed_fleet_holding' => FILTER_VALIDATE_INT,
+ 'uni_donut_g' => FILTER_VALIDATE_INT,
+ 'uni_donut_s' => FILTER_VALIDATE_INT,
+ ]);
+
+ $planetName = filter_var($data['planet']['name'], FILTER_DEFAULT);
+ $planetId = filter_var($data['planet']['id'], FILTER_VALIDATE_INT);
+ $resources = filter_var_array($data['ressources'], FILTER_VALIDATE_INT);
+ $temperatureMin = filter_var($data['temperature_min'], FILTER_VALIDATE_INT);
+ $temperatureMax = filter_var($data['temperature_max'], FILTER_VALIDATE_INT);
+ $fields = filter_var($data['fields'], FILTER_VALIDATE_INT);
+ $coordinates = $this->parseCoordinates($data['planet']['coords'])['coords'];
+ $planetType = $this->resolvePlanetType($data['planet']['type']);
+ $ogameTimestamp = $universeDetails['uni_time'];
+
+ $playerClass = 'none';
+ if ($playerDetails['playerclass_miner'] == 1) {
+ $playerClass = 'COL';
+ } elseif ($playerDetails['playerclass_warrior'] == 1) {
+ $playerClass = 'GEN';
+ } elseif ($playerDetails['playerclass_explorer'] == 1) {
+ $playerClass = 'EXP';
+ }
+
+ $gamePlayerId = (int)$playerDetails['player_id'];
+ $currentOgspyUserId = (int)$this->userData['id'];
+ if ($gamePlayerId > 0) {
+ $gamePlayerName = $this->db->sql_escape_string($playerDetails['player_name']);
+ $officerCommander = (int)$playerDetails['player_officer_commander'];
+ $officerAmiral = (int)$playerDetails['player_officer_amiral'];
+ $officerEngineer = (int)$playerDetails['player_officer_engineer'];
+ $officerGeologist = (int)$playerDetails['player_officer_geologist'];
+ $officerTechnocrate = (int)$playerDetails['player_officer_technocrate'];
+
+ $query = "
+ INSERT INTO " . TABLE_GAME_PLAYER . " (
+ `id`, `name`, `class`,
+ `off_commandant`, `off_amiral`, `off_ingenieur`, `off_geologue`, `off_technocrate`,
+ `datadate`, `status`, `ally_id`
+ ) VALUES (
+ {$gamePlayerId}, '{$gamePlayerName}', '{$playerClass}',
+ {$officerCommander}, {$officerAmiral}, {$officerEngineer}, {$officerGeologist}, {$officerTechnocrate},
+ {$ogameTimestamp}, '', -1
+ )
+ ON DUPLICATE KEY UPDATE
+ `name` = VALUES(`name`),
+ `class` = VALUES(`class`),
+ `off_commandant` = VALUES(`off_commandant`),
+ `off_amiral` = VALUES(`off_amiral`),
+ `off_ingenieur` = VALUES(`off_ingenieur`),
+ `off_geologue` = VALUES(`off_geologue`),
+ `off_technocrate` = VALUES(`off_technocrate`),
+ `datadate` = VALUES(`datadate`)";
+ $this->db->sql_query($query);
+ }
+
+ $this->db->sql_query("UPDATE " . TABLE_USER . " SET `player_id` = {$gamePlayerId} WHERE `id` = {$currentOgspyUserId}");
+ $this->db->sql_query("INSERT INTO " . TABLE_CONFIG . " (name, value) VALUES ('speed_uni', '{$universeDetails['uni_speed']}') ON DUPLICATE KEY UPDATE value = VALUES(value)");
+ $this->db->sql_query("INSERT INTO " . TABLE_CONFIG . " (name, value) VALUES ('speed_fleet_peaceful', '{$universeDetails['uni_speed_fleet_peaceful']}') ON DUPLICATE KEY UPDATE value = VALUES(value)");
+ $this->db->sql_query("INSERT INTO " . TABLE_CONFIG . " (name, value) VALUES ('speed_fleet_war', '{$universeDetails['uni_speed_fleet_war']}') ON DUPLICATE KEY UPDATE value = VALUES(value)");
+ $this->db->sql_query("INSERT INTO " . TABLE_CONFIG . " (name, value) VALUES ('speed_fleet_holding', '{$universeDetails['uni_speed_fleet_holding']}') ON DUPLICATE KEY UPDATE value = VALUES(value)");
+ generate_config_cache();
+
+ if (isset($data['boosters'])) {
+ $boosters = booster_encode(update_boosters($data['boosters'], $ogameTimestamp));
+ } else {
+ $boosters = booster_encodev(0, 0, 0, 0, 0, 0, 0, 0);
+ }
+
+ [$galaxy, $system, $position] = explode(':', $coordinates);
+ $this->db->sql_query("INSERT INTO " . TABLE_USER_BUILDING . "
+ (`player_id`, `id`, `type`, `galaxy`, `system`, `row`, `name`, `fields`, `boosters`, `temperature_min`, `temperature_max`)
+ VALUES ({$gamePlayerId}, {$planetId}, '{$planetType}', {$galaxy}, {$system}, {$position}, '{$planetName}', {$fields}, '{$boosters}', {$temperatureMin}, {$temperatureMax})
+ ON DUPLICATE KEY UPDATE
+ type = VALUES(type),
+ name = VALUES(name),
+ player_id = VALUES(player_id),
+ fields = VALUES(fields),
+ boosters = VALUES(boosters),
+ temperature_min = VALUES(temperature_min),
+ temperature_max = VALUES(temperature_max),
+ galaxy = VALUES(galaxy),
+ system = VALUES(system),
+ row = VALUES(row)");
+
+ $this->setPageUpdatedResponse('overview', $coordinates);
+ $this->registerCallback('overview', [
+ 'coords' => explode(':', $coordinates),
+ 'planet_type' => $planetType,
+ 'planet_name' => $planetName,
+ 'fields' => $fields,
+ 'temperature_min' => $temperatureMin,
+ 'temperature_max' => $temperatureMax,
+ 'ressources' => $resources,
+ ]);
+ $this->logAction('overview', ['coords' => $coordinates, 'planet_name' => $planetName]);
+ }
+
+ public function getType(): string
+ {
+ return 'overview';
+ }
+
+ public function getRequiredGrant(): string
+ {
+ return 'empire';
+ }
+}
diff --git a/Handler/RankingHandler.php b/Handler/RankingHandler.php
new file mode 100644
index 0000000..e243e3a
--- /dev/null
+++ b/Handler/RankingHandler.php
@@ -0,0 +1,168 @@
+requireGrant($this->getRequiredGrant())) {
+ return;
+ }
+ if (((int)$offset - 1) % 100 !== 0) {
+ throw new UnexpectedValueException('Ranking: Offset not found');
+ }
+ if (!is_array($entries)) {
+ throw new UnexpectedValueException('Rankings: Incomplete Ranking');
+ }
+
+ $table = $this->getTargetTable($type1, $type2, $type3);
+ $timestamp = (int)($GLOBALS['timestamp'] ?? time());
+ $senderId = (int)$this->userData['id'];
+ $processed = [];
+ $values = [];
+ $isPlayerMilitary = $type1 === 'player' && $table === TABLE_RANK_PLAYER_MILITARY;
+
+ foreach ($entries as $entry) {
+ if (!is_array($entry)) {
+ continue;
+ }
+ if ($type1 === 'ally' && !isset($entry['ally_id'], $entry['points'], $entry['mean'], $entry['members'])) {
+ throw new UnexpectedValueException('Ranking Ally: Missing alliance data');
+ }
+ $entry['rank'] = (int)($entry['rank'] ?? 0);
+ $entry['points'] = (int)($entry['points'] ?? 0);
+
+ if ($type1 === 'player') {
+ $entry['player_name'] = filter_var($entry['player_name'] ?? '', FILTER_DEFAULT);
+ $entry['ally_tag'] = filter_var($entry['ally_tag'] ?? '', FILTER_DEFAULT);
+ $entry['ally_id'] = !empty($entry['ally_id']) ? (int)$entry['ally_id'] : -1;
+ $entry['player_id'] = !empty($entry['player_id']) ? (int)$entry['player_id'] : -1;
+
+ if ($entry['player_id'] > 0 && $entry['player_name'] !== '') {
+ $playerName = $this->db->sql_escape_string($entry['player_name']);
+ $this->db->sql_query("INSERT INTO " . TABLE_GAME_PLAYER . " (id, name, ally_id, datadate)
+ VALUES ({$entry['player_id']}, '{$playerName}', {$entry['ally_id']}, " . time() . ")
+ ON DUPLICATE KEY UPDATE name = VALUES(name), ally_id = VALUES(ally_id), datadate = VALUES(datadate)");
+ }
+ if ($entry['ally_id'] > 0 && $entry['ally_tag'] !== '') {
+ $allyTag = $this->db->sql_escape_string($entry['ally_tag']);
+ $this->db->sql_query("INSERT INTO " . TABLE_GAME_ALLY . " (id, tag, datadate)
+ VALUES ({$entry['ally_id']}, '{$allyTag}', " . time() . ")
+ ON DUPLICATE KEY UPDATE tag = VALUES(tag), datadate = VALUES(datadate)");
+ }
+
+ $playerName = $this->db->sql_escape_string($entry['player_name']);
+ $allyTag = $this->db->sql_escape_string($entry['ally_tag']);
+ $rowValues = "({$timestamp}, {$entry['rank']}, '{$playerName}', {$entry['player_id']}, '{$allyTag}', {$entry['ally_id']}, {$entry['points']}, {$senderId}";
+ if ($isPlayerMilitary) {
+ $rowValues .= ', ' . (int)($entry['nb_spacecraft'] ?? 0);
+ }
+ $values[] = $rowValues . ')';
+ } else {
+ $entry['ally_tag'] = filter_var($entry['ally_tag'] ?? '', FILTER_DEFAULT);
+ $entry['ally_name'] = filter_var($entry['ally'] ?? '', FILTER_DEFAULT);
+ $allyTag = $this->db->sql_escape_string($entry['ally_tag']);
+ $allyName = $this->db->sql_escape_string($entry['ally_name']);
+ $allyId = (int)$entry['ally_id'];
+ $points = (int)$entry['points'];
+ $mean = (int)$entry['mean'];
+ $members = (int)$entry['members'];
+
+ if ($allyId > 0 && $allyTag !== '' && $allyName !== '') {
+ $this->db->sql_query("INSERT INTO " . TABLE_GAME_ALLY . " (id, name, tag, datadate)
+ VALUES ({$allyId}, '{$allyName}', '{$allyTag}', " . time() . ")
+ ON DUPLICATE KEY UPDATE name = VALUES(name), tag = VALUES(tag), datadate = VALUES(datadate)");
+ }
+ $values[] = "({$timestamp}, {$entry['rank']}, '{$allyTag}', {$allyId}, {$points}, {$senderId}, {$members}, {$mean})";
+ }
+
+ $processed[] = $entry;
+ }
+
+ if ($values !== []) {
+ if ($type1 === 'player') {
+ $columns = '`datadate`, `rank`, `player`, `player_id`, `ally`, `ally_id`, `points`, `sender_id`';
+ if ($isPlayerMilitary) {
+ $columns .= ', `nb_spacecraft`';
+ }
+ $this->db->sql_query("REPLACE INTO {$table} ({$columns}) VALUES " . implode(',', $values));
+ } else {
+ $this->db->sql_query("REPLACE INTO {$table} (datadate, rank, ally, ally_id, points, sender_id, number_member, points_per_member) VALUES " . implode(',', $values));
+ $this->db->sql_query("UPDATE " . TABLE_USER . " SET rank_imports = rank_imports + " . count($values) . " WHERE id = {$senderId}");
+ }
+ }
+
+ $callbackType2 = $type2 === 'fleet' ? $type2 . $type3 : $type2;
+ $this->registerCallback('ranking_' . $type1 . '_' . $callbackType2, [
+ 'data' => $processed,
+ 'offset' => (int)$offset,
+ 'time' => $timestamp,
+ ]);
+ $this->io->set(['type' => 'ranking', 'type1' => $type1, 'type2' => $callbackType2, 'offset' => (int)$offset]);
+ update_statistic('rankimports', 100);
+ $this->logAction('ranking', [
+ 'type1' => $type1,
+ 'type2' => $callbackType2,
+ 'offset' => (int)$offset,
+ 'time' => $timestamp,
+ ]);
+ }
+
+ private function getTargetTable(string $type1, string $type2, string $type3): string
+ {
+ if ($type1 === 'player') {
+ return match ($type2) {
+ 'points' => TABLE_RANK_PLAYER_POINTS,
+ 'economy' => TABLE_RANK_PLAYER_ECO,
+ 'research' => TABLE_RANK_PLAYER_TECHNOLOGY,
+ 'fleet' => match ($type3) {
+ '' => TABLE_RANK_PLAYER_MILITARY,
+ '5' => TABLE_RANK_PLAYER_MILITARY_BUILT,
+ '6' => TABLE_RANK_PLAYER_MILITARY_DESTRUCT,
+ '4' => TABLE_RANK_PLAYER_MILITARY_LOOSE,
+ '7' => TABLE_RANK_PLAYER_HONOR,
+ default => throw new OutOfRangeException('Ranking Player: Unexpected Ranking type for type3: ' . $type3),
+ },
+ default => throw new UnexpectedValueException('Ranking Player: Unexpected Ranking type for type2: ' . $type2),
+ };
+ }
+
+ return match ($type2) {
+ 'points' => TABLE_RANK_ALLY_POINTS,
+ 'economy' => TABLE_RANK_ALLY_ECO,
+ 'research' => TABLE_RANK_ALLY_TECHNOLOGY,
+ 'fleet' => match ($type3) {
+ '' => TABLE_RANK_ALLY_MILITARY,
+ '5' => TABLE_RANK_ALLY_MILITARY_BUILT,
+ '6' => TABLE_RANK_ALLY_MILITARY_DESTRUCT,
+ '4' => TABLE_RANK_ALLY_MILITARY_LOOSE,
+ '7' => TABLE_RANK_ALLY_HONOR,
+ default => throw new OutOfRangeException('Ranking Ally: Unexpected Ranking type for type3: ' . $type3),
+ },
+ default => throw new UnexpectedValueException('Ranking Ally: Unexpected Ranking type for type2: ' . $type2),
+ };
+ }
+
+ public function getType(): string
+ {
+ return 'ranking';
+ }
+
+ public function getRequiredGrant(): string
+ {
+ return 'ranking';
+ }
+}
diff --git a/Handler/ResearchsHandler.php b/Handler/ResearchsHandler.php
new file mode 100644
index 0000000..2843615
--- /dev/null
+++ b/Handler/ResearchsHandler.php
@@ -0,0 +1,62 @@
+requireGrant($this->getRequiredGrant())) {
+ return;
+ }
+
+ $coordinates = filter_var($data['planet']['coords']);
+ $planetName = filter_var($data['planet']['name']);
+ $planetType = filter_var($data['planet']['type']);
+ $researches = $data['researchs'];
+ if (!isset($coordinates, $planetName, $planetType)) {
+ throw new UnexpectedValueException('Researchs: Missing Planet Details');
+ }
+
+ $coordinates = Check::coords($coordinates);
+ $planetType = ((int)$planetType === TYPE_PLANET) ? TYPE_PLANET : TYPE_MOON;
+ if (!isset($this->userData['player_id']) || $this->userData['player_id'] <= 0) {
+ throw new UnexpectedValueException('Researchs: Player ID not available');
+ }
+
+ $playerId = (int)$this->userData['player_id'];
+ $fields = [];
+ $values = [];
+ $updates = [];
+ foreach ($this->database['labo'] as $code) {
+ if (isset($researches[$code])) {
+ $fields[] = "`$code`";
+ $values[] = (int)$researches[$code];
+ $updates[] = "`$code` = " . (int)$researches[$code];
+ }
+ }
+
+ if ($fields !== []) {
+ $this->db->sql_query("INSERT INTO " . TABLE_USER_TECHNOLOGY . "
+ (`player_id`, " . implode(', ', $fields) . ")
+ VALUES ({$playerId}, " . implode(', ', $values) . ")
+ ON DUPLICATE KEY UPDATE " . implode(', ', $updates));
+ }
+
+ $this->setPageUpdatedResponse('labo', $coordinates);
+ $this->registerCallback('research', ['research' => $researches]);
+ $this->logAction('research');
+ }
+
+ public function getType(): string
+ {
+ return 'researchs';
+ }
+
+ public function getRequiredGrant(): string
+ {
+ return 'empire';
+ }
+}
diff --git a/Handler/ResourceSettingsHandler.php b/Handler/ResourceSettingsHandler.php
new file mode 100644
index 0000000..4707ee9
--- /dev/null
+++ b/Handler/ResourceSettingsHandler.php
@@ -0,0 +1,95 @@
+requireGrant($this->getRequiredGrant())) {
+ return;
+ }
+
+ $coordinates = filter_var($data['planet']['coords']);
+ $planetName = filter_var($data['planet']['name']);
+ $planetType = filter_var($data['planet']['type'], FILTER_VALIDATE_INT);
+ $planetId = filter_var($data['planet']['id'], FILTER_VALIDATE_INT);
+ $resourceSettings = isset($data['resourceSettings']) && is_array($data['resourceSettings'])
+ ? $data['resourceSettings']
+ : null;
+
+ if (!isset($coordinates, $planetName, $planetType)
+ || $planetId === false
+ || $planetType === false
+ || $resourceSettings === null
+ ) {
+ throw new UnexpectedValueException('ResourceSettings: Missing or invalid planet details or resourceSettings data');
+ }
+
+ $parsed = $this->parseCoordinates($coordinates);
+ $coordinates = $parsed['coords'];
+ $planetTypeConstant = $this->resolvePlanetType($planetType);
+ $planetTypeString = $this->planetTypeToString($planetTypeConstant);
+
+ $settingsMap = [
+ 'M_percentage' => 'M_percentage',
+ 'C_Percentage' => 'C_Percentage',
+ 'D_percentage' => 'D_percentage',
+ 'CES_percentage' => 'CES_percentage',
+ 'CEF_percentage' => 'CEF_percentage',
+ 'SAT_percentage' => 'Sat_percentage',
+ 'FOR_percentage' => 'FOR_percentage',
+ ];
+
+ $columns = ['id', 'galaxy', 'system', 'row', 'name', 'type'];
+ $values = [
+ $planetId,
+ $parsed['galaxy'],
+ $parsed['system'],
+ $parsed['row'],
+ $this->db->sql_escape_string($planetName),
+ $this->db->sql_escape_string($planetTypeString),
+ ];
+ foreach ($settingsMap as $jsonKey => $databaseColumn) {
+ $columns[] = $databaseColumn;
+ $values[] = isset($resourceSettings[$jsonKey]) ? (int)$resourceSettings[$jsonKey] : 100;
+ }
+
+ $updatePairs = ['name', 'galaxy', 'system', 'row', 'type'];
+ $updatePairs = array_map(static function ($column) {
+ return "$column = VALUES($column)";
+ }, $updatePairs);
+ foreach ($settingsMap as $databaseColumn) {
+ $updatePairs[] = "`$databaseColumn` = VALUES(`$databaseColumn`)";
+ }
+
+ $query = "INSERT INTO " . TABLE_USER_BUILDING . " (`" . implode('`, `', $columns) . "`)
+ VALUES (" . implode(', ', array_map(static function ($value) {
+ return is_numeric($value) ? $value : "'" . $value . "'";
+ }, $values)) . ")
+ ON DUPLICATE KEY UPDATE " . implode(', ', $updatePairs);
+ $this->db->sql_query($query);
+
+ // The existing Xtense protocol reports resource settings as a buildings page update.
+ $this->setPageUpdatedResponse('buildings', $coordinates);
+ $this->registerCallback('resourceSettings', [
+ 'coords' => explode(':', $coordinates),
+ 'planet_type' => $planetTypeConstant,
+ 'planet_name' => $planetName,
+ 'resourceSettings' => $resourceSettings,
+ ]);
+ $this->logAction('resourceSettings', ['coords' => $coordinates, 'planet_name' => $planetName]);
+ }
+
+ public function getType(): string
+ {
+ return 'resourceSettings';
+ }
+
+ public function getRequiredGrant(): string
+ {
+ return 'empire';
+ }
+}
diff --git a/Handler/SystemHandler.php b/Handler/SystemHandler.php
new file mode 100644
index 0000000..0fbd2fd
--- /dev/null
+++ b/Handler/SystemHandler.php
@@ -0,0 +1,134 @@
+requireGrant($this->getRequiredGrant())) {
+ return;
+ }
+
+ $galaxy = filter_var($data['galaxy'], FILTER_SANITIZE_NUMBER_INT);
+ $system = filter_var($data['system'], FILTER_SANITIZE_NUMBER_INT);
+ if (!isset($galaxy, $system)) {
+ throw new UnexpectedValueException('Galaxy: Missing Planet position');
+ }
+
+ $galaxy = (int)$galaxy;
+ $system = (int)$system;
+ if ($galaxy > $this->server_config['num_of_galaxies'] || $system > $this->server_config['num_of_systems']) {
+ $this->io->set(['type' => 'plugin univers', 'access' => 'system']);
+ $this->io->status(0);
+ return;
+ }
+
+ $rows = $data['rows'];
+ $systemData = [];
+ for ($position = 1; $position < 16; $position++) {
+ if (isset($rows[$position])) {
+ $line = $rows[$position];
+ $line['player_name'] = filter_var($line['player_name']);
+ $line['planet_name'] = filter_var($line['planet_name']);
+ $line['planet_id'] = filter_var($line['planet_id'], FILTER_VALIDATE_INT);
+ $line['ally_tag'] = filter_var($line['ally_tag']);
+ if (isset($line['debris'])) {
+ $line['debris'] = filter_var_array($line['debris'], [
+ 'metal' => FILTER_SANITIZE_NUMBER_INT,
+ 'crystal' => FILTER_SANITIZE_NUMBER_INT,
+ 'deuterium' => FILTER_SANITIZE_NUMBER_INT,
+ ]);
+ }
+ if (isset($line['status'])) {
+ $line['status'] = filter_var($line['status']);
+ }
+ $systemData[$position] = $line;
+ continue;
+ }
+
+ $systemData[$position] = [
+ 'planet_id' => '',
+ 'planet_name' => '',
+ 'player_name' => '',
+ 'status' => '',
+ 'ally_tag' => '',
+ 'debris' => ['metal' => 0, 'cristal' => 0, 'deuterium' => 0],
+ 'moon' => 0,
+ 'activity' => '',
+ ];
+ }
+
+ foreach ($systemData as $position => $line) {
+ $status = Check::player_status_forbidden($line['status']) ? '' : $line['status'];
+ $playerId = isset($line['player_id']) ? (int)$line['player_id'] : -1;
+ $planetId = isset($line['planet_id']) ? (int)$line['planet_id'] : -1;
+ $moon = isset($line['moon']) ? (int)$line['moon'] : -1;
+ $moonId = isset($line['moon_id']) ? (int)$line['moon_id'] : -1;
+ $allyId = isset($line['ally_id']) ? (int)$line['ally_id'] : -1;
+ $allyTag = $line['ally_tag'] ?? '';
+ $currentTime = time();
+
+ $this->db->sql_query("INSERT INTO " . TABLE_USER_BUILDING . "
+ (`id`, `galaxy`, `system`, `row`, `name`, `player_id`, `last_update`, `last_update_user_id`)
+ VALUES ({$planetId}, {$galaxy}, {$system}, {$position}, '{$line['planet_name']}', {$playerId}, {$currentTime}, " . (int)$this->userData['id'] . ")
+ ON DUPLICATE KEY UPDATE
+ `name` = '{$line['planet_name']}',
+ `player_id` = {$playerId},
+ `last_update` = {$currentTime},
+ `last_update_user_id` = " . (int)$this->userData['id']);
+
+ if ($moonId !== -1 && $moon === 1) {
+ $this->db->sql_query("INSERT INTO " . TABLE_USER_BUILDING . "
+ (`id`, `type`, `galaxy`, `system`, `row`, `name`, `player_id`, `last_update_moon`, `last_update_user_id`)
+ VALUES ({$moonId}, 'moon', {$galaxy}, {$system}, {$position}, '{$line['planet_name']} - Moon', {$playerId}, {$currentTime}, " . (int)$this->userData['id'] . ")
+ ON DUPLICATE KEY UPDATE
+ `name` = '{$line['planet_name']} - Moon',
+ `player_id` = {$playerId},
+ `last_update_moon` = {$currentTime},
+ `last_update_user_id` = " . (int)$this->userData['id']);
+ }
+
+ if ($playerId !== -1) {
+ $playerName = $this->db->sql_escape_string($line['player_name']);
+ $escapedStatus = $this->db->sql_escape_string($status);
+ $this->db->sql_query("INSERT INTO " . TABLE_GAME_PLAYER . "
+ (`id`, `name`, `status`, `ally_id`, `datadate`)
+ VALUES ({$playerId}, '{$playerName}', '{$escapedStatus}', {$allyId}, {$currentTime})
+ ON DUPLICATE KEY UPDATE
+ `name` = '{$playerName}',
+ `status` = '{$escapedStatus}',
+ `ally_id` = {$allyId},
+ `datadate` = {$currentTime}");
+ }
+
+ if ($allyId > 0) {
+ $this->db->sql_query("INSERT INTO " . TABLE_GAME_ALLY . " (`id`, `tag`, `datadate`)
+ VALUES ({$allyId}, '{$allyTag}', {$currentTime})
+ ON DUPLICATE KEY UPDATE `tag` = '{$allyTag}', `datadate` = {$currentTime}");
+ }
+ }
+
+ $this->db->sql_query("UPDATE " . TABLE_USER . " SET `planet_imports` = `planet_imports` + 15 WHERE `id` = " . (int)$this->userData['id']);
+ $this->registerCallback('system', [
+ 'data' => $rows,
+ 'galaxy' => $galaxy,
+ 'system' => $system,
+ ]);
+ $this->io->set(['type' => 'system', 'galaxy' => $galaxy, 'system' => $system]);
+ update_statistic('planetimports', 15);
+ $this->logAction('system', ['coords' => $galaxy . ':' . $system]);
+ }
+
+ public function getType(): string
+ {
+ return 'system';
+ }
+
+ public function getRequiredGrant(): string
+ {
+ return 'system';
+ }
+}
diff --git a/phpunit.xml b/phpunit.xml
index 3ce1727..3fd596e 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -10,4 +10,11 @@
./tests/unit
+
+
+ ./Handler
+ ./includes
+ ./xtense.php
+
+
diff --git a/sonar-project.properties b/sonar-project.properties
index 91bad32..2287431 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -1,5 +1,6 @@
sonar.projectKey=OGSteam_mod-xtense
sonar.organization=ogsteam
+sonar.php.coverage.reportPaths=coverage.xml
# This is the name and version displayed in the SonarCloud UI.
#sonar.projectName=ogspy
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index f3366c7..3bf8433 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -56,6 +56,7 @@
'TABLE_USER' => $p . 'user',
'TABLE_MOD' => $p . 'mod',
'TABLE_STATISTIC' => $p . 'statistics',
+ 'TABLE_PARSEDSPY' => $p . 'game_spy',
'TABLE_USER_BUILDING' => $p . 'game_astro_object',
'TABLE_GAME_PLAYER' => $p . 'game_player',
'TABLE_GAME_ALLY' => $p . 'game_ally',
@@ -78,6 +79,10 @@
'TABLE_RANK_ALLY_MILITARY_LOOSE' => $p . 'game_rank_ally_military_loose',
'TABLE_RANK_ALLY_MILITARY_DESTRUCT' => $p . 'game_rank_ally_military_destruct',
'TABLE_RANK_ALLY_HONOR' => $p . 'game_rank_ally_honor',
+ 'TABLE_PARSEDRC' => $p . 'game_rc',
+ 'TABLE_PARSEDRCROUND' => $p . 'game_rc_round',
+ 'TABLE_ROUND_ATTACK' => $p . 'game_rc_round_attack',
+ 'TABLE_ROUND_DEFENSE' => $p . 'game_rc_round_defense',
] as $constName => $constValue) {
if (!defined($constName)) define($constName, $constValue);
}
@@ -92,6 +97,9 @@ function generate_config_cache(): void {}
if (!function_exists('booster_encode')) {
function booster_encode(array $boosters): string { return ''; }
}
+if (!function_exists('booster_decode')) {
+ function booster_decode(): array { return []; }
+}
if (!function_exists('booster_encodev')) {
function booster_encodev(int ...$args): string { return ''; }
}
diff --git a/tests/unit/CombatReportHandlerTest.php b/tests/unit/CombatReportHandlerTest.php
index 4c58462..7025fa7 100644
--- a/tests/unit/CombatReportHandlerTest.php
+++ b/tests/unit/CombatReportHandlerTest.php
@@ -121,6 +121,17 @@ public function testComplexFixtureDefenderHasDefenseAndFleetTechnologies(): void
// Handler — main RC record (TABLE_PARSEDRC)
// -------------------------------------------------------------------------
+ private function getMainReportInsert(): string
+ {
+ foreach ($this->db->getQueriesContaining('game_rc') as $query) {
+ if (stripos($query, 'INSERT INTO ogspy_game_rc (') !== false) {
+ return $query;
+ }
+ }
+
+ self::fail('Expected main combat-report INSERT');
+ }
+
public function testHandleInsertsMainRcRecord(): void
{
$data = $this->getDecodedData('rc_complex');
@@ -141,8 +152,7 @@ public function testHandleRecordsWinnerAttacker(): void
$handler = $this->createHandler('CombatReportHandler');
$handler->handle($data);
- $inserts = $this->db->getQueriesContaining('game_rc');
- $sql = $inserts[0];
+ $sql = $this->getMainReportInsert();
$this->assertStringContainsString("'A'", $sql, "Winner must be encoded as 'A'");
}
@@ -152,7 +162,7 @@ public function testHandleRecordsLootValues(): void
$handler = $this->createHandler('CombatReportHandler');
$handler->handle($data);
- $sql = $this->db->getQueriesContaining('game_rc')[0];
+ $sql = $this->getMainReportInsert();
$this->assertStringContainsString('200000', $sql, 'Loot metal missing');
$this->assertStringContainsString('150000', $sql, 'Loot crystal missing');
$this->assertStringContainsString('50000', $sql, 'Loot deuterium missing');
@@ -164,7 +174,7 @@ public function testHandleRecordsDebrisValues(): void
$handler = $this->createHandler('CombatReportHandler');
$handler->handle($data);
- $sql = $this->db->getQueriesContaining('game_rc')[0];
+ $sql = $this->getMainReportInsert();
$this->assertStringContainsString('250000', $sql, 'Debris metal missing');
$this->assertStringContainsString('120000', $sql, 'Debris crystal missing');
}
@@ -175,7 +185,7 @@ public function testHandleRecordsMoonCreationChance(): void
$handler = $this->createHandler('CombatReportHandler');
$handler->handle($data);
- $sql = $this->db->getQueriesContaining('game_rc')[0];
+ $sql = $this->getMainReportInsert();
$this->assertStringContainsString('12', $sql, 'Moon chance (12) missing from RC insert');
}
@@ -185,7 +195,7 @@ public function testHandleRecordsUnitLosses(): void
$handler = $this->createHandler('CombatReportHandler');
$handler->handle($data);
- $sql = $this->db->getQueriesContaining('game_rc')[0];
+ $sql = $this->getMainReportInsert();
$this->assertStringContainsString('2500000', $sql, 'Attacker losses missing');
$this->assertStringContainsString('5000000', $sql, 'Defender losses missing');
}
diff --git a/tests/unit/MessageHandlerTest.php b/tests/unit/MessageHandlerTest.php
new file mode 100644
index 0000000..2d7c2ba
--- /dev/null
+++ b/tests/unit/MessageHandlerTest.php
@@ -0,0 +1,45 @@
+createHandler('MessageHandler');
+ $handler->handle([
+ 'type' => 'msg',
+ 'coords' => '4:246:12',
+ 'from' => 'Marshal Galileo',
+ 'subject' => 'Fleet movement',
+ 'message' => 'Fleet arrives soon',
+ 'date' => 1776500000,
+ ]);
+
+ $this->assertTrue($this->callbackHandler->hasCallForType('msg'));
+ $params = $this->callbackHandler->getCallsForType('msg')[0]['params'];
+ $this->assertSame(['4', '246', '12'], $params['coords']);
+ $this->assertSame('Fleet arrives soon', $params['message']);
+ $this->assertSame('messages', $this->getIoResponse()['type']);
+ }
+
+ public function testReturnAsSpyChangesResponseType(): void
+ {
+ $handler = $this->createHandler('MessageHandler');
+ $handler->handle([
+ 'type' => 'msg',
+ 'coords' => '4:246:12',
+ 'from' => 'Marshal Galileo',
+ 'subject' => 'Fleet movement',
+ 'message' => 'Fleet arrives soon',
+ '_xtense_return_as' => 'spy',
+ ]);
+
+ $this->assertSame('spy', $this->getIoResponse()['type']);
+ }
+
+ public function testUnknownMessageTypeThrows(): void
+ {
+ $handler = $this->createHandler('MessageHandler');
+ $this->expectException(UnexpectedValueException::class);
+ $handler->handle(['type' => 'unknown']);
+ }
+}
diff --git a/tests/unit/MessageSubtypeHandlerTest.php b/tests/unit/MessageSubtypeHandlerTest.php
new file mode 100644
index 0000000..65beef1
--- /dev/null
+++ b/tests/unit/MessageSubtypeHandlerTest.php
@@ -0,0 +1,87 @@
+createHandler('MessageHandler');
+ $handler->handle([
+ 'type' => 'ally_msg',
+ 'from' => 'Marshal Galileo',
+ 'tag' => 'SLD',
+ 'message' => 'Alliance message',
+ 'date' => 1776500000,
+ ]);
+
+ $this->assertTrue($this->callbackHandler->hasCallForType('ally_msg'));
+ $this->assertSame('SLD', $this->callbackHandler->getCallsForType('ally_msg')[0]['params']['tag']);
+ }
+
+ public function testSpyReportPersistsAndRegistersCallback(): void
+ {
+ $data = $this->getDecodedData('messages_spy');
+ $data['buildings'] = false;
+ $data['lfBuildings'] = false;
+ $data['research'] = false;
+ $data['lfResearch'] = false;
+ $data['fleet'] = false;
+ $data['defense'] = false;
+ $handler = $this->createHandler('MessageHandler');
+ $handler->handle($data);
+
+ $this->assertTrue($this->callbackHandler->hasCallForType('spy'));
+ $this->assertNotEmpty($this->db->getQueriesContaining('INSERT INTO ogspy_game_spy'));
+ $this->assertNotEmpty($this->db->getQueriesContaining('INSERT INTO ogspy_game_astro_object'));
+ $this->assertSame('messages', $this->getIoResponse()['type']);
+ }
+
+ public function testEnemySpyRegistersCallbackAndPersistsReport(): void
+ {
+ $handler = $this->createHandler('MessageHandler');
+ $handler->handle([
+ 'type' => 'ennemy_spy',
+ 'from' => '4:246:12',
+ 'to' => '4:246:7',
+ 'proba' => '65',
+ 'date' => 1776500000,
+ ]);
+
+ $this->assertTrue($this->callbackHandler->hasCallForType('ennemy_spy'));
+ $this->assertNotEmpty($this->db->getQueriesContaining('ogspy_parsedSpyEn'));
+ }
+
+ public function testRecyclerReportRegistersNormalizedCallback(): void
+ {
+ $handler = $this->createHandler('MessageHandler');
+ $handler->handle([
+ 'type' => 'rc_cdr',
+ 'nombre' => '5',
+ 'coords' => '4:246:12',
+ 'M_recovered' => '1200',
+ 'C_recovered' => '800',
+ 'M_total' => '2500',
+ 'C_total' => '1700',
+ 'date' => 1776500000,
+ ]);
+
+ $this->assertTrue($this->callbackHandler->hasCallForType('rc_cdr'));
+ $params = $this->callbackHandler->getCallsForType('rc_cdr')[0]['params'];
+ $this->assertSame(1200, $params['M_reco']);
+ $this->assertNotEmpty($this->db->getQueriesContaining('ogspy_parsedRec'));
+ }
+
+ public function testExpeditionRegistersExpeditionCallback(): void
+ {
+ $handler = $this->createHandler('MessageHandler');
+ $handler->handle([
+ 'type' => 'expedition_shared',
+ 'coords' => '4:246:16',
+ 'content' => 'Resources found',
+ 'date' => 1776500000,
+ ]);
+
+ $this->assertTrue($this->callbackHandler->hasCallForType('expedition_shared'));
+ $params = $this->callbackHandler->getCallsForType('expedition_shared')[0]['params'];
+ $this->assertSame(['4', '246', '16'], $params['coords']);
+ }
+}
diff --git a/tests/unit/RankingHandlerTest.php b/tests/unit/RankingHandlerTest.php
index 0e1fc6a..bebeb1b 100644
--- a/tests/unit/RankingHandlerTest.php
+++ b/tests/unit/RankingHandlerTest.php
@@ -122,6 +122,60 @@ public function testFleetMilitaryTableQueryIncludesNbSpacecraft(): void
// Validation
// -------------------------------------------------------------------------
+ public function testAllianceRankingRejectsMissingPointsBeforeWrites(): void
+ {
+ $data = [
+ 'type1' => 'ally',
+ 'type2' => 'points',
+ 'type3' => '',
+ 'offset' => '1',
+ 'time' => '1776500000',
+ 'n' => [[
+ 'ally_id' => 500003,
+ 'rank' => 1,
+ 'ally' => 'Suivre Les Directives',
+ 'ally_tag' => 'SLD',
+ 'mean' => 100,
+ 'members' => 5,
+ ]],
+ ];
+ $handler = $this->createHandler('RankingHandler');
+
+ try {
+ $handler->handle($data);
+ self::fail('Expected missing alliance points to be rejected');
+ } catch (UnexpectedValueException $exception) {
+ $this->assertSame('Ranking Ally: Missing alliance data', $exception->getMessage());
+ }
+
+ $this->assertSame([], $this->db->getQueries());
+ }
+
+ public function testAllianceRankingCallbackPreservesAllyName(): void
+ {
+ $data = [
+ 'type1' => 'ally',
+ 'type2' => 'points',
+ 'type3' => '',
+ 'offset' => '1',
+ 'time' => '1776500000',
+ 'n' => [[
+ 'ally_id' => 500003,
+ 'rank' => 1,
+ 'ally' => 'Suivre Les Directives',
+ 'ally_tag' => 'SLD',
+ 'points' => 12345,
+ 'mean' => 2469,
+ 'members' => 5,
+ ]],
+ ];
+ $handler = $this->createHandler('RankingHandler');
+ $handler->handle($data);
+
+ $calls = $this->callbackHandler->getCallsForType('ranking_ally_points');
+ $this->assertSame('Suivre Les Directives', $calls[0]['params']['data'][0]['ally_name']);
+ }
+
public function testRankingThrowsOnInvalidOffset(): void
{
$data = $this->getDecodedData('ranking_player_points');
diff --git a/tests/unit/SimplePayloadHandlersTest.php b/tests/unit/SimplePayloadHandlersTest.php
new file mode 100644
index 0000000..91af180
--- /dev/null
+++ b/tests/unit/SimplePayloadHandlersTest.php
@@ -0,0 +1,55 @@
+createHandler('DefenseHandler');
+ $handler->handle([
+ 'planet' => ['coords' => '4:246:12', 'name' => 'Home', 'id' => 33717002, 'type' => 0],
+ 'defense' => ['LM' => 15, 'PB' => 1],
+ ]);
+
+ $queries = $this->db->getQueriesContaining('game_player_defense');
+ $this->assertCount(1, $queries);
+ $this->assertStringContainsString('`LM`', $queries[0]);
+ $this->assertSame('defense', $this->getIoResponse()['page']);
+ $this->assertTrue($this->callbackHandler->hasCallForType('defense'));
+ }
+
+ public function testFleetHandlerStoresFleetAndProductionValues(): void
+ {
+ $handler = $this->createHandler('FleetHandler');
+ $handler->handle([
+ 'planet' => ['coords' => '4:246:12', 'name' => 'Home', 'id' => 33717002, 'type' => 0],
+ 'fleet' => ['PT' => 5, 'SAT' => 2],
+ ]);
+
+ $this->assertNotEmpty($this->db->getQueriesContaining('game_player_fleet'));
+ $productionQueries = $this->db->getQueriesContaining('game_astro_object');
+ $this->assertNotEmpty($productionQueries);
+ $this->assertStringContainsString('`SAT`', end($productionQueries));
+ $this->assertSame('fleet', $this->getIoResponse()['page']);
+ $this->assertTrue($this->callbackHandler->hasCallForType('fleet'));
+ }
+
+ public function testAllyListHandlerBuildsCallbackAndResponse(): void
+ {
+ $handler = $this->createHandler('AllyListHandler');
+ $handler->handle([
+ 'tag' => 'SLD',
+ 'allyList' => [[
+ 'player' => 'Marshal Galileo',
+ 'points' => '12345',
+ 'rank' => '42',
+ 'coords' => '4:246:12',
+ ]],
+ ]);
+
+ $calls = $this->callbackHandler->getCallsForType('ally_list');
+ $this->assertCount(1, $calls);
+ $this->assertSame('SLD', $calls[0]['params']['tag']);
+ $this->assertSame(['4', '246', '12'], $calls[0]['params']['list'][0]['coords']);
+ $this->assertSame('ally_list', $this->getIoResponse()['type']);
+ }
+}
diff --git a/tests/unit/SpyDatabase.php b/tests/unit/SpyDatabase.php
index 8fdf140..6a8245e 100644
--- a/tests/unit/SpyDatabase.php
+++ b/tests/unit/SpyDatabase.php
@@ -26,8 +26,16 @@ public function sql_fetch_assoc($result): array
return [];
}
+ public function sql_numrows($result): int
+ {
+ return 0;
+ }
+
public function sql_fetch_row($result): array
{
+ if (str_contains($this->getLastQuery() ?? '', 'SELECT id FROM ogspy_game_astro_object')) {
+ return [33717423];
+ }
return [null];
}
diff --git a/xtense.php b/xtense.php
index d763e9e..5728dfa 100755
--- a/xtense.php
+++ b/xtense.php
@@ -10,31 +10,35 @@
const IN_SPYOGAME = true;
const IN_XTENSE = true;
-
date_default_timezone_set(@date_default_timezone_get());
-$currentFolder = getcwd();
if (str_contains(getcwd(), 'mod')) {
chdir('../../');
}
-$_SERVER['SCRIPT_FILENAME'] = str_replace(basename(__FILE__), 'index.php', preg_replace('#/mod/(.*)/#', '/', $_SERVER['SCRIPT_FILENAME']));
-include_once("common.php");
-list($root, $active) = $db->sql_fetch_row($db->sql_query("SELECT `root`, `active` FROM " . TABLE_MOD . " WHERE `action` = 'xtense'"));
+$_SERVER['SCRIPT_FILENAME'] = str_replace(
+ basename(__FILE__),
+ 'index.php',
+ preg_replace('#/mod/(.*)/#', '/', $_SERVER['SCRIPT_FILENAME'])
+);
+include_once 'common.php';
+list($root, $active) = $db->sql_fetch_row($db->sql_query(
+ "SELECT `root`, `active` FROM " . TABLE_MOD . " WHERE `action` = 'xtense'"
+));
-// CORS: allow extension and web origins; auth is in POST body, no cookies involved.
-$request_method = strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
+// Xtense authenticates in the POST body and does not use browser cookies.
+$requestMethod = strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
header('Access-Control-Max-Age: 86400');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With');
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('X-Content-Type-Options: nosniff');
-if ($request_method === 'OPTIONS') {
+if ($requestMethod === 'OPTIONS') {
http_response_code(204);
exit;
}
-if ($request_method !== 'POST') {
+if ($requestMethod !== 'POST') {
header('Allow: POST, OPTIONS');
header('Content-Type: text/plain; charset=utf-8');
http_response_code(405);
@@ -42,15 +46,33 @@
exit;
}
-require_once("mod/$root/includes/config.php");
-require_once("mod/$root/includes/functions.php");
-require_once("mod/$root/includes/CallbackHandler.php");
-require_once("mod/$root/includes/Callback.php");
-require_once("mod/$root/includes/Io.php");
-require_once("mod/$root/includes/Check.php");
-require_once("mod/$root/includes/auth.php");
+require_once "mod/{$root}/includes/config.php";
+require_once "mod/{$root}/includes/functions.php";
+require_once "mod/{$root}/includes/CallbackHandler.php";
+require_once "mod/{$root}/includes/Callback.php";
+require_once "mod/{$root}/includes/Io.php";
+require_once "mod/{$root}/includes/Check.php";
+require_once "mod/{$root}/includes/auth.php";
+require_once "mod/{$root}/includes/AbstractHandler.php";
+
+$handlerClasses = [
+ 'OverviewHandler',
+ 'BuildingsHandler',
+ 'ResourceSettingsHandler',
+ 'DefenseHandler',
+ 'ResearchsHandler',
+ 'FleetHandler',
+ 'SystemHandler',
+ 'RankingHandler',
+ 'CombatReportHandler',
+ 'AllyListHandler',
+ 'MessageHandler',
+];
+foreach ($handlerClasses as $handlerClass) {
+ require_once "mod/{$root}/Handler/{$handlerClass}.php";
+}
-$start_time = microtime(true);
+$startTime = microtime(true);
$io = new Io();
$time = time() - 60 * 4;
if ($time > mktime(0, 0, 0) && $time < mktime(8, 0, 0)) {
@@ -64,1824 +86,90 @@
}
$json = file_get_contents('php://input');
-$received_content = json_decode($json, true);
-//print_r($received_content);
-
-if (!is_array($received_content)) {
+$receivedContent = json_decode($json, true);
+if (!is_array($receivedContent)) {
header('Content-Type: application/json; charset=utf-8');
http_response_code(400);
echo json_encode(['status' => 0, 'error' => 'Invalid JSON payload']);
exit;
}
-$args = array(
+$receivedGameData = filter_var_array($receivedContent, [
'type' => FILTER_SANITIZE_SPECIAL_CHARS,
'toolbar_version' => FILTER_SANITIZE_SPECIAL_CHARS,
'toolbar_type' => FILTER_SANITIZE_SPECIAL_CHARS,
'mod_min_version' => FILTER_SANITIZE_SPECIAL_CHARS,
'univers' => FILTER_VALIDATE_URL,
'password' => FILTER_DEFAULT,
- 'data' => FILTER_DEFAULT
-);
+ 'data' => FILTER_DEFAULT,
+]);
-$received_game_data = filter_var_array($received_content, $args);
-//print_r($received_game_data);
-
-if (!isset($received_game_data['type'])) {
+if (!isset($receivedGameData['type'])) {
header('Content-Type: application/json; charset=utf-8');
http_response_code(400);
echo json_encode(['status' => 0, 'error' => 'Xtense data not provided']);
exit;
}
-xtense_check_before_auth($received_game_data['toolbar_version'], $received_game_data['mod_min_version'], $active, $received_game_data['univers']);
-$xtense_user_data = xtense_check_auth($received_game_data['password']);
+xtense_check_before_auth(
+ $receivedGameData['toolbar_version'],
+ $receivedGameData['mod_min_version'],
+ $active,
+ $receivedGameData['univers']
+);
+$xtense_user_data = xtense_check_auth($receivedGameData['password']);
$xtense_user_data = xtense_check_user_rights($xtense_user_data);
-
$call = new CallbackHandler();
-// Xtense : Ajout de la version et du type de barre utilisée par l'utilisateur
-$current_user_id = $xtense_user_data['id'];
-$db->sql_query("UPDATE " . TABLE_USER . " SET `xtense_version` = '" . $received_game_data['toolbar_version'] . "', `xtense_type` = '" . $received_game_data['toolbar_type'] . "' WHERE `id` = $current_user_id");
-$toolbar_info = $received_game_data['toolbar_type'] . " V" . $received_game_data['toolbar_version'];
-
-// Récupération des données de jeu
-$data = json_decode($received_game_data['data'], true);
-
-// Meilleur Endroit pour voir ce que l'on récupère de l'extension :-)
-//print_r($data);
+$currentUserId = $xtense_user_data['id'];
+$db->sql_query("UPDATE " . TABLE_USER . " SET `xtense_version` = '" . $receivedGameData['toolbar_version'] . "', `xtense_type` = '" . $receivedGameData['toolbar_type'] . "' WHERE `id` = {$currentUserId}");
+$toolbarInfo = $receivedGameData['toolbar_type'] . ' V' . $receivedGameData['toolbar_version'];
+$data = json_decode($receivedGameData['data'], true);
try {
-
-switch ($received_game_data['type']) {
- case 'overview':
- { //PAGE OVERVIEW
- if (!$xtense_user_data['grant']['empire']) {
- $io->set(array(
- 'type' => 'plugin grant',
- 'access' => 'empire'
- ));
- $io->status(0);
- } else {
-
- $player_details = filter_var_array($data['playerdetails'], [
- 'player_name' => FILTER_DEFAULT,
- 'player_id' => FILTER_DEFAULT,
- 'playerclass_explorer' => FILTER_DEFAULT,
- 'playerclass_miner' => FILTER_DEFAULT,
- 'playerclass_warrior' => FILTER_VALIDATE_INT,
- 'player_officer_commander' => FILTER_VALIDATE_INT,
- 'player_officer_amiral' => FILTER_VALIDATE_INT,
- 'player_officer_engineer' => FILTER_VALIDATE_INT,
- 'player_officer_geologist' => FILTER_VALIDATE_INT,
- 'player_officer_technocrate' => FILTER_VALIDATE_INT
- ]);
-
- $uni_details = filter_var_array(
- $data['unidetails'],
- [
- 'uni_version' => FILTER_DEFAULT,
- 'uni_url' => FILTER_DEFAULT,
- 'uni_lang' => FILTER_DEFAULT,
- 'uni_name' => FILTER_DEFAULT,
- 'uni_time' => FILTER_VALIDATE_INT,
- 'uni_speed' => FILTER_VALIDATE_INT, // speed_uni
- 'uni_speed_fleet_peaceful' => FILTER_VALIDATE_INT,
- 'uni_speed_fleet_war' => FILTER_VALIDATE_INT,
- 'uni_speed_fleet_holding' => FILTER_VALIDATE_INT,
- 'uni_donut_g' => FILTER_VALIDATE_INT,
- 'uni_donut_s' => FILTER_VALIDATE_INT
- ]
- );
-
- $planet_name = filter_var($data['planet']['name'], FILTER_DEFAULT);
- $planet_id = filter_var($data['planet']['id'], FILTER_VALIDATE_INT);
- $ressources = filter_var_array($data['ressources'], FILTER_VALIDATE_INT);
- $temperature_min = filter_var($data['temperature_min'], FILTER_VALIDATE_INT);
- $temperature_max = filter_var($data['temperature_max'], FILTER_VALIDATE_INT);
- $fields = filter_var($data['fields'], FILTER_VALIDATE_INT);
-
- $coords = Check::coords($data['planet']['coords']);
- $planet_type = ((int)$data['planet']['type'] == 0 ? TYPE_PLANET : TYPE_MOON);
- $ogame_timestamp = $uni_details['uni_time'];
-
- $userclass = 'none';
- if ($player_details['playerclass_miner'] == 1) {
- $userclass = 'COL';
- } elseif ($player_details['playerclass_warrior'] == 1) {
- $userclass = 'GEN';
- } elseif ($player_details['playerclass_explorer'] == 1) {
- $userclass = 'EXP';
- }
-
- // Met à jour les informations du joueur dans la table ogspy_game_player (TABLE_GAME_PLAYER)
- // et lie l'ID du joueur de jeu à l'utilisateur OGSpy dans la table ogspy_user (TABLE_USER)
- $gamePlayerId = (int)$player_details['player_id'];
- if ($gamePlayerId > 0) { // S'assurer que player_id est valide
- $gamePlayerName = $db->sql_escape_string($player_details['player_name']);
- $officerCommander = (int)$player_details['player_officer_commander'];
- $officerAmiral = (int)$player_details['player_officer_amiral'];
- $officerEngineer = (int)$player_details['player_officer_engineer'];
- $officerGeologist = (int)$player_details['player_officer_geologist'];
- $officerTechnocrate = (int)$player_details['player_officer_technocrate'];
- $ogameTimestamp = (int)$uni_details['uni_time'];
- $currentOgspyUserId = (int)$xtense_user_data['id']; // ID de l'utilisateur OGSpy
-
- $queryGamePlayer = "
- INSERT INTO " . TABLE_GAME_PLAYER . " (
- `id`, `name`, `class`,
- `off_commandant`, `off_amiral`, `off_ingenieur`, `off_geologue`, `off_technocrate`,
- `datadate`,
- `status`, `ally_id`
- ) VALUES (
- {$gamePlayerId},
- '{$gamePlayerName}',
- '{$userclass}',
- {$officerCommander},
- {$officerAmiral},
- {$officerEngineer},
- {$officerGeologist},
- {$officerTechnocrate},
- {$ogameTimestamp},
- '',
- -1
- )
- ON DUPLICATE KEY UPDATE
- `name` = VALUES(`name`),
- `class` = VALUES(`class`),
- `off_commandant` = VALUES(`off_commandant`),
- `off_amiral` = VALUES(`off_amiral`),
- `off_ingenieur` = VALUES(`off_ingenieur`),
- `off_geologue` = VALUES(`off_geologue`),
- `off_technocrate` = VALUES(`off_technocrate`),
- `datadate` = VALUES(`datadate`)";
- $db->sql_query($queryGamePlayer);
- }
-
- // Met à jour TABLE_USER pour stocker le player_id (ID du joueur dans le jeu)
- $db->sql_query("UPDATE " . TABLE_USER . " SET `player_id` = {$gamePlayerId} WHERE `id` = {$currentOgspyUserId}");
-
- //Uni Speed
- $db->sql_query("INSERT INTO " . TABLE_CONFIG . " (name, value) VALUES ('speed_uni', '{$uni_details['uni_speed']}') ON DUPLICATE KEY UPDATE value = VALUES(value)");
- //Uni Speed Peaceful
- $db->sql_query("INSERT INTO " . TABLE_CONFIG . " (name, value) VALUES ('speed_fleet_peaceful', '{$uni_details['uni_speed_fleet_peaceful']}') ON DUPLICATE KEY UPDATE value = VALUES(value)");
- //Uni Speed War
- $db->sql_query("INSERT INTO " . TABLE_CONFIG . " (name, value) VALUES ('speed_fleet_war', '{$uni_details['uni_speed_fleet_war']}') ON DUPLICATE KEY UPDATE value = VALUES(value)");
- //Uni Speed holding
- $db->sql_query("INSERT INTO " . TABLE_CONFIG . " (name, value) VALUES ('speed_fleet_holding', '{$uni_details['uni_speed_fleet_holding']}') ON DUPLICATE KEY UPDATE value = VALUES(value)");
- //Update Config Cache
- generate_config_cache();
-
- //boosters
- if (isset($data['boosters'])) {
- $boosters = update_boosters($data['boosters'], $ogame_timestamp); /*Merge des différents boosters*/
- $boosters = booster_encode($boosters); /*Conversion de l'array boosters en string*/
- } else {
- $boosters = booster_encodev(0, 0, 0, 0, 0, 0, 0, 0); /* si aucun booster détecté*/
- }
- //Empire
- list($g, $s, $r) = explode(':', $coords);
- $db->sql_query("INSERT INTO " . TABLE_USER_BUILDING . "
- (`player_id`, `id`, `type`, `galaxy`, `system`, `row`, `name`, `fields`, `boosters`, `temperature_min`, `temperature_max`)
- VALUES
- ({$gamePlayerId}, {$planet_id}, '{$planet_type}' , {$g}, {$s}, {$r},'{$planet_name}', {$fields}, '{$boosters}', {$temperature_min}, {$temperature_max})
- ON DUPLICATE KEY UPDATE
- type = VALUES(type),
- name = VALUES(name),
- player_id = VALUES(player_id),
- fields = VALUES(fields),
- boosters = VALUES(boosters),
- temperature_min = VALUES(temperature_min),
- temperature_max = VALUES(temperature_max),
- galaxy = VALUES(galaxy),
- system = VALUES(system),
- row = VALUES(row)");
-
- $io->set(array(
- 'type' => 'home updated',
- 'page' => 'overview',
- 'planet' => $coords
- ));
-
- // Appel fonction de callback
- $call->add('overview', array(
- 'coords' => explode(':', $coords),
- 'planet_type' => $planet_type,
- 'planet_name' => $planet_name,
- 'fields' => $fields,
- 'temperature_min' => $temperature_min,
- 'temperature_max' => $temperature_max,
- 'ressources' => $ressources
- ));
-
- add_log('overview', array('coords' => $coords, 'planet_name' => $planet_name, 'toolbar' => $toolbar_info));
- }
- }
-
-
- break;
-
- case 'buildings': //PAGE BATIMENTS
-
- if (!$xtense_user_data['grant']['empire']) {
- $io->set(array(
- 'type' => 'plugin grant',
- 'access' => 'empire'
- ));
- $io->status(0);
- } else {
- $coords = filter_var($data['planet']['coords']);
- $planet_name = filter_var($data['planet']['name']);
- $planet_type = filter_var($data['planet']['type']);
- $planet_id = filter_var($data['planet']['id'], FILTER_VALIDATE_INT);
- if (!isset($coords, $planet_name, $planet_type)) {
- throw new UnexpectedValueException("Buildings- Missing data");
- }
- $buildings = $data['buildings'];
-
- $coords = Check::coords($coords);
- list($g, $s, $r) = explode(':', $coords); // ADDED: Parse coordinates
- $planet_type = ((int)$planet_type == TYPE_PLANET ? TYPE_PLANET : TYPE_MOON);
-
- // Construction d'une requête UPSERT pour les bâtiments de la planète
- $buildingColumns = [];
- $buildingValues = [];
-
- // Préparation des colonnes de base
-
- $buildingColumns[] = 'id'; // ADDED
- $buildingValues[] = $planet_id; // ADDED
- $buildingColumns[] = 'galaxy'; // ADDED
- $buildingValues[] = $g; // ADDED
- $buildingColumns[] = 'system'; // ADDED
- $buildingValues[] = $s; // ADDED
- $buildingColumns[] = 'row'; // ADDED
- $buildingValues[] = $r; // ADDED
-
- $buildingColumns[] = 'name'; // RENAMED from 'planet_name'
- $buildingValues[] = $planet_name;
-
- // Ajout des bâtiments
- foreach ($database['buildings'] as $code) {
- if (isset($buildings[$code])) {
- $buildingColumns[] = $code;
- $buildingValues[] = (int)$buildings[$code];
- }
- }
-
- // Préparation des champs pour la clause ON DUPLICATE KEY UPDATE
- $updatePairs = [];
- $updatePairs[] = "name = VALUES(name)"; // CHANGED and RENAMED
- $updatePairs[] = "galaxy = VALUES(galaxy)"; // ADDED
- $updatePairs[] = "system = VALUES(system)"; // ADDED
- $updatePairs[] = "row = VALUES(row)"; // ADDED
-
- foreach ($database['buildings'] as $code) {
- if (isset($buildings[$code])) {
- $updatePairs[] = "`$code` = VALUES(`$code`)"; // CHANGED to use VALUES()
- }
- }
-
- // Construction de la requête complète
- $query = "INSERT INTO " . TABLE_USER_BUILDING . " (`" . implode('`, `', $buildingColumns) . "`)
- VALUES (" . implode(', ', array_map(function ($val) {
- return is_numeric($val) ? $val : "'$val'";
- }, $buildingValues)) . ")
- ON DUPLICATE KEY UPDATE " . implode(', ', $updatePairs);
-
- $db->sql_query($query);
-
- $io->set(array(
- 'type' => 'home updated',
- 'page' => 'buildings',
- 'planet' => $coords
- ));
- }
-
- $call->add('buildings', array(
- 'coords' => explode(':', $coords),
- 'planet_type' => $planet_type,
- 'planet_name' => $planet_name,
- 'buildings' => $buildings
- ));
-
- add_log('buildings', array('coords' => $coords, 'planet_name' => $planet_name, 'toolbar' => $toolbar_info));
-
- break;
-
- case 'resourceSettings':
- if (!$xtense_user_data['grant']['empire']) {
- $io->set(array(
- 'type' => 'plugin grant',
- 'access' => 'empire'
- ));
- $io->status(0);
- } else {
- $coords_str = filter_var($data['planet']['coords']);
- $planet_name = filter_var($data['planet']['name']);
- $planet_type_int = filter_var($data['planet']['type'], FILTER_VALIDATE_INT);
- $planet_id = filter_var($data['planet']['id'], FILTER_VALIDATE_INT);
- $resourceSettings_data = isset($data['resourceSettings']) && is_array($data['resourceSettings']) ? $data['resourceSettings'] : null;
-
- if (!isset($coords_str, $planet_name, $planet_type_int) ||
- $planet_id === false || $planet_type_int === false || $resourceSettings_data === null
- ) {
- throw new UnexpectedValueException("ResourceSettings: Missing or invalid planet details or resourceSettings data");
- }
-
- $coords = Check::coords($coords_str);
- list($g, $s, $r) = explode(':', $coords);
-
- // Constante pour le callback (ex: TYPE_PLANET, TYPE_MOON)
- $planet_type_const = ($planet_type_int == TYPE_PLANET ? TYPE_PLANET : TYPE_MOON);
- // Chaîne pour la base de données ('planet', 'moon')
- $astro_object_type_str = ($planet_type_int == TYPE_PLANET ? 'planet' : 'moon');
-
- $columns = [];
- $values = [];
-
- // Colonnes de base pour TABLE_USER_BUILDING (ogspy_game_astro_object)
- $columns[] = 'id';
- $values[] = $planet_id;
- $columns[] = 'galaxy';
- $values[] = (int)$g;
- $columns[] = 'system';
- $values[] = (int)$s;
- $columns[] = 'row';
- $values[] = (int)$r;
- $columns[] = 'name';
- $values[] = $db->sql_escape_string($planet_name);
- $columns[] = 'type';
- $values[] = $db->sql_escape_string($astro_object_type_str);
-
- // Colonnes pour les pourcentages de ressources
- // Clé JSON => Colonne DB
- $resource_settings_map = [
- 'M_percentage' => 'M_percentage',
- 'C_Percentage' => 'C_Percentage',
- 'D_percentage' => 'D_percentage',
- 'CES_percentage' => 'CES_percentage',
- 'CEF_percentage' => 'CEF_percentage',
- 'SAT_percentage' => 'Sat_percentage', // Correction de casse pour la DB
- 'FOR_percentage' => 'FOR_percentage'
- ];
-
- foreach ($resource_settings_map as $json_key => $db_col_name) {
- $columns[] = $db_col_name;
- // Si la clé n'est pas dans les données JSON, mettre 0 (comme pour 'buildings')
- $values[] = isset($resourceSettings_data[$json_key]) ? (int)$resourceSettings_data[$json_key] : 100;
- }
-
- // Préparation des champs pour la clause ON DUPLICATE KEY UPDATE
- $updatePairs = [];
- $updatePairs[] = "name = VALUES(name)";
- $updatePairs[] = "galaxy = VALUES(galaxy)";
- $updatePairs[] = "system = VALUES(system)";
- $updatePairs[] = "row = VALUES(row)";
- $updatePairs[] = "type = VALUES(type)";
-
- foreach ($resource_settings_map as $json_key => $db_col_name) {
- $updatePairs[] = "`$db_col_name` = VALUES(`$db_col_name`)";
- }
-
- // Construction de la requête complète
- $query = "INSERT INTO " . TABLE_USER_BUILDING . " (`" . implode('`, `', $columns) . "`)
- VALUES (" . implode(', ', array_map(function ($val) {
- // Les valeurs numériques sont directes, les chaînes (déjà échappées) sont entourées d'apostrophes
- return is_numeric($val) ? $val : "'" . $val . "'";
- }, $values)) . ")
- ON DUPLICATE KEY UPDATE " . implode(', ', $updatePairs);
-
- $db->sql_query($query);
-
- $io->set(array(
- 'type' => 'home updated',
- 'page' => 'buildings', // Conformément au code existant pour resourceSettings et à la demande de reproduire 'buildings'
- 'planet' => $coords
- ));
-
- // Appel fonction de callback
- $call->add('resourceSettings', array( // Nom de callback spécifique
- 'coords' => explode(':', $coords),
- 'planet_type' => $planet_type_const, // Utiliser la constante (ex: TYPE_PLANET)
- 'planet_name' => $planet_name,
- 'resourceSettings' => $resourceSettings_data // Renvoyer les données JSON originales
- ));
-
- add_log('resourceSettings', array('coords' => $coords, 'planet_name' => $planet_name, 'toolbar' => $toolbar_info));
- }
-
- break;
-
- case 'defense': //PAGE DEFENSE
- if (!$xtense_user_data['grant']['empire']) {
- $io->set(array(
- 'type' => 'plugin grant',
- 'access' => 'empire'
- ));
- $io->status(0);
- } else {
- $coords = filter_var($data['planet']['coords']);
- $planet_name = filter_var($data['planet']['name']);
- $planet_id = filter_var($data['planet']['id'], FILTER_VALIDATE_INT);
- $planet_type = filter_var($data['planet']['type']);
-
- $defense = $data['defense'];
- //Stop si donnée manquante
- if (!isset($coords, $planet_name, $planet_type)) {
- throw new UnexpectedValueException("Defense: Missing Planet Details");
- }
- $coords = Check::coords($coords);
- list($g, $s, $r) = explode(':', $coords); // Extraire les coordonnées
- $planet_type = ((int)$planet_type == TYPE_PLANET ? TYPE_PLANET : TYPE_MOON);
- $planet_type_str = ((int)$planet_type == TYPE_PLANET ? 'planet' : 'moon');
-
- $db->sql_query("INSERT INTO " . TABLE_USER_BUILDING . "
- (id, type, galaxy, system, row, name, player_id)
- VALUES
- ({$planet_id}, '{$planet_type_str}', {$g}, {$s}, {$r}, '{$planet_name}', {$xtense_user_data['player_id']})
- ON DUPLICATE KEY UPDATE
- name = '{$planet_name}'");
-
- // Préparer les champs pour la requête d'insertion/mise à jour des défenses
- $defense_fields = [];
- $defense_values = [];
- $update_pairs = [];
-
- foreach ($database['defense'] as $code) {
- if (isset($defense[$code])) {
- $defense_fields[] = "`$code`";
- $defense_values[] = (int)$defense[$code];
- $update_pairs[] = "`$code` = " . (int)$defense[$code];
- }
- }
-
- // Construction de la requête UPSERT pour les défenses
- if (!empty($defense_fields)) {
- $fields_str = implode(', ', $defense_fields);
- $values_str = implode(', ', $defense_values);
-
- $db->sql_query("INSERT INTO " . TABLE_GAME_PLAYER_DEFENSE . "
- (astro_object_id, {$fields_str})
- VALUES
- ({$planet_id}, {$values_str})
- ON DUPLICATE KEY UPDATE
- " . implode(", ", $update_pairs));
- }
-
- $io->set(array(
- 'type' => 'home updated',
- 'page' => 'defense',
- 'planet' => $coords
- ));
-
- // Préparer les données pour le callback
- $defenses = array();
- foreach ($database['defense'] as $code) {
- if (isset($defense[$code])) {
- $defenses[$code] = (int)$defense[$code];
- }
- }
-
- $call->add('defense', [
- 'coords' => explode(':', $coords),
- 'planet_type' => $planet_type,
- 'planet_name' => $planet_name,
- 'defense' => $defenses
- ]);
-
- add_log('defense', array('coords' => $coords, 'planet_name' => $planet_name, 'toolbar' => $toolbar_info));
- }
- break;
-
- case 'researchs': //PAGE RECHERCHE
-
- if (!$xtense_user_data['grant']['empire']) {
- $io->set(['type' => 'plugin grant', 'access' => 'empire']);
- $io->status(0);
- } else {
- $coords = filter_var($data['planet']['coords']);
- $planet_name = filter_var($data['planet']['name']);
- $planet_type = filter_var($data['planet']['type']);
- $researchs = $data['researchs'];
-
- if (!isset($coords, $planet_name, $planet_type)) {
- throw new UnexpectedValueException("Researchs: Missing Planet Details");
- }
- $coords = Check::coords($coords);
- $planet_type = ((int)$planet_type == TYPE_PLANET ? TYPE_PLANET : TYPE_MOON);
-
- // Vérifier que l'ID du joueur est disponible
- if (!isset($xtense_user_data['player_id']) || $xtense_user_data['player_id'] <= 0) {
- throw new UnexpectedValueException("Researchs: Player ID not available");
- }
-
- $player_id = (int)$xtense_user_data['player_id'];
-
- // Préparer les champs pour la requête d'insertion/mise à jour des technologies
- $fields = [];
- $values = [];
- $update_parts = [];
-
- foreach ($database['labo'] as $code) {
- if (isset($researchs[$code])) {
- $fields[] = "`$code`";
- $values[] = (int)$researchs[$code];
- $update_parts[] = "`$code` = " . (int)$researchs[$code];
- }
- }
-
- // Construction de la requête UPSERT pour les technologies
- if (!empty($fields)) {
- $fields_str = implode(', ', $fields);
- $values_str = implode(', ', $values);
-
- $db->sql_query("INSERT INTO " . TABLE_USER_TECHNOLOGY . "
- (`player_id`, {$fields_str})
- VALUES
- ({$player_id}, {$values_str})
- ON DUPLICATE KEY UPDATE
- " . implode(", ", $update_parts));
- }
-
- $io->set(array(
- 'type' => 'home updated',
- 'page' => 'labo',
- 'planet' => $coords
- ));
-
- $call->add('research', array(
- 'research' => $researchs
- ));
-
- add_log('research', array('toolbar' => $toolbar_info));
- }
- break;
-
- case 'fleet': //PAGE FLOTTE
-
- if (!$xtense_user_data['grant']['empire']) {
- $io->set(array(
- 'type' => 'plugin grant',
- 'access' => 'empire'
- ));
- $io->status(0);
- } else {
- $coords = filter_var($data['planet']['coords']);
- $planet_name = filter_var($data['planet']['name']);
- $planet_type = filter_var($data['planet']['type']);
- $planet_id = filter_var($data['planet']['id'], FILTER_VALIDATE_INT);
- $fleet = $data['fleet'];
-
- if (!isset($coords, $planet_name, $planet_type)) {
- throw new UnexpectedValueException("Fleet: Missing Planet Details");
- }
- $coords = Check::coords($coords);
- list($g, $s, $r) = explode(':', $coords); // Extraire les coordonnées
- $planet_type = ((int)$planet_type == TYPE_PLANET ? TYPE_PLANET : TYPE_MOON);
- $planet_type_str = ((int)$planet_type == TYPE_PLANET ? 'planet' : 'moon');
-
- $query = "INSERT INTO " . TABLE_USER_BUILDING . " (id, type, galaxy, system, row, name, player_id)
- VALUES ({$planet_id}, '{$planet_type_str}', {$g}, {$s}, {$r}, '{$planet_name}', {$xtense_user_data['player_id']})
- ON DUPLICATE KEY
- UPDATE name = '{$planet_name}'";
-
- $db->sql_query($query);
-
- // Préparer les champs pour la requête d'insertion/mise à jour de la flotte
- $fleet_fields = [];
- $fleet_values = [];
- $update_pairs = [];
-
- foreach ($database['fleet'] as $code) {
- if (isset($fleet[$code])) {
- $fleet_fields[] = "`$code`";
- $fleet_values[] = (int)$fleet[$code];
- $update_pairs[] = "`$code` = " . (int)$fleet[$code];
- }
- }
-
- // Construction de la requête UPSERT pour la flotte
- if (!empty($fleet_fields)) {
- $fields_str = implode(', ', $fleet_fields);
- $values_str = implode(', ', $fleet_values);
-
- $db->sql_query("INSERT INTO " . TABLE_GAME_PLAYER_FLEET . " (astro_object_id, {$fields_str})
- VALUES
- ({$planet_id}, {$values_str})
- ON DUPLICATE KEY UPDATE
- " . implode(", ", $update_pairs));
- }
-
- // Mettre à jour les informations de la flotte de production
-
- foreach ($database['fleet_production'] as $code) {
- if (isset($fleet[$code])) {
- $fleet_production_fields[] = "`$code`";
- $fleet_production_values[] = (int)$fleet[$code];
- $update_prod_pairs[] = "`$code` = " . (int)$fleet[$code];
- }
- }
-
- // Construction de la requête UPSERT pour la flotte
- if (!empty($fleet_production_fields)) {
- $fields_str = implode(', ', $fleet_production_fields);
- $values_str = implode(', ', $fleet_production_values);
-
- $db->sql_query("INSERT INTO " . TABLE_USER_BUILDING . "
- (id, {$fields_str})
- VALUES
- ({$planet_id}, {$values_str})
- ON DUPLICATE KEY UPDATE
- " . implode(", ", $update_prod_pairs));
- }
-
-
- $io->set(array(
- 'type' => 'home updated',
- 'page' => 'fleet',
- 'planet' => $coords
- ));
-
- $call->add('fleet', array(
- 'coords' => explode(':', $coords),
- 'planet_type' => $planet_type,
- 'planet_name' => $planet_name,
- 'fleet' => $fleet
- ));
-
- add_log('fleet', array('coords' => $coords, 'planet_name' => $planet_name, 'toolbar' => $toolbar_info));
- }
- break;
-
- case 'system': //PAGE SYSTEME SOLAIRE
- if (!$xtense_user_data['grant']['system']) {
- $io->set(array(
- 'type' => 'plugin grant',
- 'access' => 'system'
- ));
- $io->status(0);
- } else {
- $galaxy = filter_var($data['galaxy'], FILTER_SANITIZE_NUMBER_INT);
- $system = filter_var($data['system'], FILTER_SANITIZE_NUMBER_INT);
- if (!isset($galaxy, $system)) {
- throw new UnexpectedValueException("Galaxy: Missing Planet position");
- }
-
- if ($galaxy > $server_config['num_of_galaxies'] || $system > $server_config['num_of_systems']) {
- $io->set(array(
- 'type' => 'plugin univers',
- 'access' => 'system'
- ));
- $io->status(0);
- }
-
- $delete = [];
- $update = [];
-
- $query = "SELECT `row` FROM " . TABLE_USER_BUILDING . " WHERE `galaxy` = {$galaxy} AND `system` = {$system}";
- $check = $db->sql_query($query);
- while ($value = $db->sql_fetch_assoc($check)) {
- $update[$value['row']] = true;
- }
-
- $rows = $data['rows'];
- // Recupération des données
- for ($i = 1; $i < 16; $i++) {
- if (isset($rows[$i])) {
- $line = $rows[$i];
-
- $line['player_name'] = filter_var($line['player_name']);
- $line['planet_name'] = filter_var($line['planet_name']);
- $line['planet_id'] = filter_var($line['planet_id'], FILTER_VALIDATE_INT);
- $line['ally_tag'] = filter_var($line['ally_tag']);
-
- if (isset($line['debris'])) {
- $line['debris'] = filter_var_array($line['debris'], [
- 'metal' => FILTER_SANITIZE_NUMBER_INT,
- 'crystal' => FILTER_SANITIZE_NUMBER_INT,
- 'deuterium' => FILTER_SANITIZE_NUMBER_INT,
- ]);
- }
- if (isset($line['status'])) {
- $line['status'] = filter_var($line['status']);
- }
- $system_data[$i] = $line;
- } else {
- $delete[] = $i;
- $system_data[$i] = array(
- 'planet_id' => '',
- 'planet_name' => '',
- 'player_name' => '',
- 'status' => '',
- 'ally_tag' => '',
- 'debris' => array('metal' => 0, 'cristal' => 0, 'deuterium' => 0),
- 'moon' => 0,
- 'activity' => ''
- );
- }
- }
-
- foreach ($system_data as $row => $v) {
- $statusTemp = (Check::player_status_forbidden($v['status']) ? "" : $v['status']); //On supprime les status qui sont subjectifs
-
- //default player_id/ally_id à -1 (cf shemas SQL)
- $v['player_id'] = (isset($v['player_id']) ? (int)$v['player_id'] : -1);
- $v['planet_id'] = (isset($v['planet_id']) ? (int)$v['planet_id'] : -1);
- $v['moon'] = (isset($v['moon']) ? (int)$v['moon'] : -1);
- $v['moon_id'] = (isset($v['moon_id']) ? (int)$v['moon_id'] : -1);
- $v['ally_id'] = (isset($v['ally_id']) ? (int)$v['ally_id'] : -1);
- $v['ally_tag'] = $v['ally_tag'] ?? '';
-
- $db->sql_query("INSERT INTO " . TABLE_USER_BUILDING . "
- (`id`, `galaxy`, `system`, `row`, `name`, `player_id`, `last_update`, `last_update_user_id`)
- VALUES
- ({$v['planet_id']},{$galaxy}, {$system}, {$row}, '{$v['planet_name']}', {$v['player_id']}, " . time() . ", {$xtense_user_data['id']} )
- ON DUPLICATE KEY UPDATE
- `name` = '{$v['planet_name']}',
- `player_id` = {$v['player_id']},
- `last_update` = " . time() . ",
- `last_update_user_id` = {$xtense_user_data['id']}");
-
- // Création des lignes pour les lunes
-
- if (isset($v['moon_id']) && $v['moon'] == 1) {
- $db->sql_query("INSERT INTO " . TABLE_USER_BUILDING . "
- (`id`, `type`, `galaxy`, `system`, `row`, `name`, `player_id`, `last_update_moon`, `last_update_user_id`)
- VALUES
- ({$v['moon_id']}, 'moon',{$galaxy}, {$system}, {$row}, '{$v['planet_name']} - Moon', {$v['player_id']}, " . time() . ", {$xtense_user_data['id']} )
- ON DUPLICATE KEY UPDATE
- `name` = '{$v['planet_name']} - Moon',
- `player_id` = {$v['player_id']},
- `last_update_moon` = " . time() . ",
- `last_update_user_id` = {$xtense_user_data['id']}");
- }
-
-
- // UPSERT pour la table game_player pour mettre à jour le statut
- if ($v['player_id'] != -1) {
-
- $currentTime = time();
- $playerId = (int)$v['player_id'];
- $playerName = $db->sql_escape_string($v['player_name']);
- $status = $db->sql_escape_string($statusTemp);
- $allyId = (int)$v['ally_id'];
-
- $db->sql_query("INSERT INTO " . TABLE_GAME_PLAYER . "
- (`id`, `name`, `status`, `ally_id`, `datadate`)
- VALUES
- ($playerId, '$playerName', '$status', $allyId, $currentTime)
- ON DUPLICATE KEY UPDATE
- `name` = '$playerName',
- `status` = '$status',
- `ally_id` = $allyId,
- `datadate` = $currentTime");
- }
- //La table game ally ne peut se mettre à jour, champs ally non alimenté (toutes les infos sont dans page rank)
- // UPSERT pour la table game_player pour mettre à jour le statut
- if ($v['ally_id'] > 0) {
-
- $currentTime = time();
- $allytag = $v['ally_tag'];
- $allyId = $v['ally_id'];
-
- $db->sql_query("INSERT INTO " . TABLE_GAME_ALLY . "
- (`id`, `tag`, `datadate`)
- VALUES
- ($allyId, '$allytag', $currentTime)
- ON DUPLICATE KEY UPDATE
- `tag` = '$allytag',
- `datadate` = $currentTime");
- }
- }
-
- if (!empty($delete)) {
- $toDelete = array();
- foreach ($delete as $n) {
- $toDelete[] = $galaxy . ':' . $system . ':' . $n;
- }
- }
-
- $db->sql_query("UPDATE " . TABLE_USER . " SET `planet_imports` = `planet_imports` + 15 WHERE `id` = " . $xtense_user_data['id']);
-
- $call->add('system', array(
- 'data' => $data['rows'],
- 'galaxy' => $galaxy,
- 'system' => $system
- ));
-
- $io->set(array(
- 'type' => 'system',
- 'galaxy' => $galaxy,
- 'system' => $system
- ));
-
- update_statistic('planetimports', 15);
- add_log('system', array('coords' => $galaxy . ':' . $system, 'toolbar' => $toolbar_info));
- }
- break;
-
- case 'ranking': //PAGE STATS
-
- $type1 = filter_var($data['type1']);
- $type2 = filter_var($data['type2']);
- $type3 = filter_var($data['type3']) ?? 0;
- $offset = filter_var($data['offset'], FILTER_SANITIZE_NUMBER_INT);
- $date = filter_var($data['time']);
-
- if (!isset($type1, $type2, $offset, $data['n'], $date)) {
- throw new UnexpectedValueException("Rankings: Incomplete Ranking");
- }
-
- if (!$xtense_user_data['grant']['ranking']) {
- $io->set(array(
- 'type' => 'plugin grant',
- 'access' => 'ranking'
- ));
- $io->status(0);
- } else {
- if ($type1 != ('player' || 'ally')) {
- throw new UnexpectedValueException("Ranking: Unexpected Ranking type for type1");
- }
- }
- //Vérification Offset
- if ((($offset - 1) % 100) != 0) {
- throw new UnexpectedValueException("Ranking: Offset not found");
- }
-
- $n = $data['n'];
- $total = 0;
- $count = count($n);
-
- if ($type1 == 'player') {
- $table = match ($type2) {
- 'points' => TABLE_RANK_PLAYER_POINTS,
- 'economy' => TABLE_RANK_PLAYER_ECO,
- 'research' => TABLE_RANK_PLAYER_TECHNOLOGY,
- 'fleet' => match ($type3) {
- '' => TABLE_RANK_PLAYER_MILITARY,
- '5' => TABLE_RANK_PLAYER_MILITARY_BUILT,
- '6' => TABLE_RANK_PLAYER_MILITARY_DESTRUCT,
- '4' => TABLE_RANK_PLAYER_MILITARY_LOOSE,
- '7' => TABLE_RANK_PLAYER_HONOR,
- default => throw new OutOfRangeException("Ranking Player: Unexpected Ranking type for type3: " . $type3),
- },
- default => throw new UnexpectedValueException("Ranking Player: Unexpected Ranking type for type2: " . $type2),
- };
- } else {
- $table = match ($type2) {
- 'points' => TABLE_RANK_ALLY_POINTS,
- 'economy' => TABLE_RANK_ALLY_ECO,
- 'research' => TABLE_RANK_ALLY_TECHNOLOGY,
- 'fleet' => match ($type3) {
- '' => TABLE_RANK_ALLY_MILITARY,
- '5' => TABLE_RANK_ALLY_MILITARY_BUILT,
- '6' => TABLE_RANK_ALLY_MILITARY_DESTRUCT,
- '4' => TABLE_RANK_ALLY_MILITARY_LOOSE,
- '7' => TABLE_RANK_ALLY_HONOR,
- default => throw new OutOfRangeException("Ranking Ally: Unexpected Ranking type for type3: " . $type3),
- },
- default => throw new UnexpectedValueException("Ranking Ally: Unexpected Ranking type for type2: " . $type2),
- };
- }
-
- $query = array();
-
- if ($type1 == 'player') {
- foreach ($n as $data) {
-
- // Vérification des données essentielles
- $data['player_name'] = filter_var($data['player_name']);
- $data['ally_tag'] = filter_var($data['ally_tag']);
-
- if (!empty($data['points'])) {
- $data['points'] = (int)$data['points'];
- }
-
- $data['ally_id'] = !empty($data['ally_id']) ? (int)$data['ally_id'] : -1;
- $data['player_id'] = !empty($data['player_id']) ? (int)$data['player_id'] : -1;
-
- //Compléter table GAME PLAYER pour les joueurs qui n'ont pas de compte
-
- if ($data['player_id'] > 0 && !empty($data['player_name'])) {
- $db->sql_query("INSERT INTO " . TABLE_GAME_PLAYER . "
- (id, name, ally_id, datadate)
- VALUES
- ({$data['player_id']}, '{$data['player_name']}', {$data['ally_id']}, " . time() . ")
- ON DUPLICATE KEY UPDATE
- name = VALUES(name),
- ally_id = VALUES(ally_id),
- datadate = VALUES(datadate)");
- }
- //Compléter table GAME ALLY pour les alliances qui n'ont pas de compte
- // Insert or update alliance data in the GAME ALLY table
- if ($data['ally_id'] > 0 && !empty($data['ally_tag'])) {
- $db->sql_query("INSERT INTO " . TABLE_GAME_ALLY . "
- (id, tag, datadate)
- VALUES
- ({$data['ally_id']}, '{$data['ally_tag']}', " . time() . ")
- ON DUPLICATE KEY UPDATE
- tag = VALUES(tag),
- datadate = VALUES(datadate)");
- }
-
-
- if ($table == TABLE_RANK_PLAYER_MILITARY) {
- $query[] = "({$timestamp}, {$data['rank']}, '{$data['player_name']}' , {$data['player_id']}, '{$data['ally_tag']}', {$data['ally_id']}, {$data['points']}, {$xtense_user_data['id']}, {$data['nb_spacecraft']} )";
- } else {
- $query[] = "({$timestamp}, {$data['rank']}, '{$data['player_name']}' , {$data['player_id']}, '{$data['ally_tag']}', {$data['ally_id']}, {$data['points']}, {$xtense_user_data['id']} )";
- }
- $total++;
- $datas[] = $data;
-
- if (!empty($query)) {
- if ($table == TABLE_RANK_PLAYER_MILITARY) {
- $db->sql_query("REPLACE INTO " . $table . " (`datadate`, `rank`, `player`, `player_id`, `ally`, `ally_id`, `points`, `sender_id`, `nb_spacecraft`) VALUES " . implode(',', $query));
- } else {
- $db->sql_query("REPLACE INTO " . $table . " (`datadate`, `rank`, `player`, `player_id`, `ally`, `ally_id`, `points`, `sender_id`) VALUES " . implode(',', $query));
- }
- }
- }
- } else {
- $fields = 'datadate, rank, ally, ally_id, points, sender_id, number_member, points_per_member';
- foreach ($n as $data) {
- $data['ally_tag'] = filter_var($data['ally_tag']);
- $data['ally_name'] = filter_var($data['ally']);
- $data['ally_id'] = filter_var($data['ally_id'] ?? throw new UnexpectedValueException("Ranking Ally: Alliance Id not found"), FILTER_SANITIZE_NUMBER_INT);
- $data['points'] = filter_var($data['points'] ?? throw new UnexpectedValueException("Ranking Ally: No points sent"), FILTER_SANITIZE_NUMBER_INT);
- $data['mean'] = filter_var($data['mean'] ?? throw new UnexpectedValueException("Ranking Ally: No mean found"), FILTER_SANITIZE_NUMBER_INT);
- $data['members'] = filter_var($data['members'] ?? throw new UnexpectedValueException("Ranking Ally: Nb players not found"), FILTER_SANITIZE_NUMBER_INT);
-
- // Insert or update alliance data in the GAME ALLY table
- if ($data['ally_id'] > 0 && !empty($data['ally_tag'] && !empty($data['ally_name']))) {
- $db->sql_query("INSERT INTO " . TABLE_GAME_ALLY . "
- (id, name, tag, datadate)
- VALUES
- ({$data['ally_id']}, '{$data['ally_name']}', '{$data['ally_tag']}', " . time() . ")
- ON DUPLICATE KEY UPDATE
- name = VALUES(name),
- tag = VALUES(tag),
- datadate = VALUES(datadate)");
- }
-
-
- $query[] = "({$timestamp}, {$data['rank']} , '{$data['ally_tag']}' , {$data['ally_id']} , {$data['points']} , {$xtense_user_data['id']} , {$data['members']} ,{$data['mean']} )";
- $datas[] = $data;
- $total++;
- }
-
- if (!empty($query)) {
- $db->sql_query("REPLACE INTO " . $table . " (" . $fields . ") VALUES " . implode(',', $query));
- }
-
- $db->sql_query("UPDATE " . TABLE_USER . " SET rank_imports = rank_imports + " . $total . " WHERE id = " . $xtense_user_data['id']);
- }
-
- $type2 = (($type2 == 'fleet') ? $type2 . $type3 : $type2);
-
- $call->add('ranking_' . $type1 . '_' . $type2, array(
- 'data' => $datas,
- 'offset' => $offset,
- 'time' => $timestamp
- ));
-
- $io->set(array(
- 'type' => 'ranking',
- 'type1' => $type1,
- 'type2' => $type2,
- 'offset' => $offset
- ));
-
- update_statistic('rankimports', 100);
- add_log('ranking', array('type1' => $type1, 'type2' => $type2, 'offset' => $offset, 'time' => $timestamp, 'toolbar' => $toolbar_info));
-
- break;
-
- case 'rc'://PAGE RC
- case 'rc_shared':
- $combatReport = $data;
- $ogapilnk = filter_var($combatReport['ogapilnk']);
-
- if (!isset($json)) {
- throw new UnexpectedValueException("Combat Report: JSON Report not sent");
- }
- $ogapilnk = $ogapilnk ?? '';
-
- if (!$xtense_user_data['grant']['messages']) {
- $io->set(array(
- 'type' => 'plugin grant',
- 'access' => 'messages'
- ));
- $io->status(0);
- } else {
- $call->add('rc', array(
- 'json' => $json,
- 'api' => $ogapilnk
- ));
-
- $exist = $db->sql_fetch_row($db->sql_query("SELECT `id_rc` FROM " . TABLE_PARSEDRC . " WHERE `dateRC` = '" . (int)$combatReport['timestamp'] . "'"));
-
- if (!isset($exist[0])) {
-// Récupération des coordonnées
- $coordinates = $combatReport['coordinates']; // "4:252:12"
-
-// Récupération du nombre de rounds
- $nbRounds = count($combatReport['combatRounds']);
-
-// L'objet JSON n'existe plus dans cette structure, utilisez directement $combatReport
-// Remplacez $jsonObj par $combatReport dans le code
-
-// Récupération du gagnant
- $winner = match ($combatReport['result']['winner']) {
- 'draw' => 'N',
- 'attacker' => 'A',
- 'defender' => 'D',
- default => throw new UnexpectedValueException("Combat Report: Result not found"),
- };
-
-// Récupération des ressources du butin
- $lootMetal = 0;
- $lootCrystal = 0;
- $lootDeuterium = 0;
-
- foreach ($combatReport['result']['loot']['resources'] as $resource) {
- switch ($resource['resource']) {
- case 'metal':
- $lootMetal = $resource['amount'];
- break;
- case 'crystal':
- $lootCrystal = $resource['amount'];
- break;
- case 'deuterium':
- $lootDeuterium = $resource['amount'];
- break;
- }
- }
-
-// Récupération des débris (si disponibles)
- $debrisMetal = 0;
- $debrisCrystal = 0;
-
- if (!empty($combatReport['result']['debris']['resources'])) {
- foreach ($combatReport['result']['debris']['resources'] as $debris) {
- switch ($debris['resource']) {
- case 'metal':
- $debrisMetal = $debris['amount'];
- break;
- case 'crystal':
- $debrisCrystal = $debris['amount'];
- break;
- }
- }
- }
-
-// Récupération des pertes par camp
- $lostUnitsAttacker = 0;
- $lostUnitsDefender = 0;
-
- foreach ($combatReport['result']['totalValueOfUnitsLost'] as $loss) {
- switch ($loss['side']) {
- case 'attacker':
- $lostUnitsAttacker = $loss['value'];
- break;
- case 'defender':
- $lostUnitsDefender = $loss['value'];
- break;
- }
- }
-
-// Chance de création de lune
- $moon = 0;
- if (isset($combatReport['result']['moonCreation']) && is_array($combatReport['result']['moonCreation'])) {
- $moon = (int)($combatReport['result']['moonCreation']['chance'] ?? 0);
- }
-
-// Récupération de l'astro_object_id depuis les coordonnées et le type d'objet
- list($g, $s, $r) = explode(':', $coordinates);
-
-// Déterminer le type d'objet astro depuis les données du RC
- $defenderSpaceObject = $combatReport['defenderSpaceObject'];
- $astro_object_type = $defenderSpaceObject['type']; // 'planet' ou 'moon'
-
- $astro_object_result = $db->sql_query("SELECT id FROM " . TABLE_USER_BUILDING . "
- WHERE galaxy = {$g} AND system = {$s} AND row = {$r} AND type = '{$astro_object_type}'
- ORDER BY last_update DESC LIMIT 1");
- $astro_object_row = $db->sql_fetch_row($astro_object_result);
- $astro_object_id = $astro_object_row[0] ?? null;
-
- if ($astro_object_id === null) {
- throw new UnexpectedValueException("Combat Report: Astro object not found for coordinates {$coordinates} with type {$astro_object_type}");
- }
-
-// Mise à jour de la requête SQL
- $db->sql_query(
- "INSERT INTO " . TABLE_PARSEDRC . " (
- `dateRC`, `astro_object_id`, `nb_rounds`, `victoire`, `pertes_A`, `pertes_D`, `gain_M`, `gain_C`, `gain_D`, `debris_M`, `debris_C`, `lune`
- ) VALUES (
- '{$combatReport['timestamp']}',
- '{$astro_object_id}',
- '{$nbRounds}',
- '{$winner}',
- '{$lostUnitsAttacker}',
- '{$lostUnitsDefender}',
- '{$lootMetal}',
- '{$lootCrystal}',
- '{$lootDeuterium}',
- '{$debrisMetal}',
- '{$debrisCrystal}',
- '{$moon}'
- )"
- );
- $id_rc = $db->sql_insertid();
-
- foreach ($combatReport['fleets'] as $fleet) {
- if ($fleet['side'] === 'attacker') {
- // Extraire les pourcentages de recherche de combat en utilisant les IDs
- $armorPercent = 0;
- $weaponPercent = 0;
- $shieldPercent = 0;
-
- if (isset($fleet['combatResearchPercentage']) && is_array($fleet['combatResearchPercentage'])) {
- foreach ($fleet['combatResearchPercentage'] as $research) {
- switch ($research['id']) {
- case 109: // Armor/Protection
- $armorPercent = $research['percentage'];
- break;
- case 110: // Weapon/Armes
- $weaponPercent = $research['percentage'];
- break;
- case 111: // Shield/Bouclier
- $shieldPercent = $research['percentage'];
- break;
- }
- }
- }
-
- $attackers[$fleet['fleetId']] = array(
- 'coords' => $fleet['spaceObject']['coordinates']['galaxy'] . ':' .
- $fleet['spaceObject']['coordinates']['system'] . ':' .
- $fleet['spaceObject']['coordinates']['position'],
- 'planetType' => $fleet['spaceObject']['type'],
- 'name' => $fleet['player']['name'],
- 'armor' => $armorPercent,
- 'weapon' => $weaponPercent,
- 'shield' => $shieldPercent
- );
- } else {
- // Extraire les pourcentages de recherche de combat en utilisant les IDs pour les défenseurs
- $armorPercent = 0;
- $weaponPercent = 0;
- $shieldPercent = 0;
-
- if (isset($fleet['combatResearchPercentage']) && is_array($fleet['combatResearchPercentage'])) {
- foreach ($fleet['combatResearchPercentage'] as $research) {
- switch ($research['id']) {
- case 109: // Armor/Protection
- $armorPercent = $research['percentage'];
- break;
- case 110: // Weapon/Armes
- $weaponPercent = $research['percentage'];
- break;
- case 111: // Shield/Bouclier
- $shieldPercent = $research['percentage'];
- break;
- }
- }
- }
-
- $defenders[] = array(
- 'coords' => $fleet['spaceObject']['coordinates']['galaxy'] . ':' .
- $fleet['spaceObject']['coordinates']['system'] . ':' .
- $fleet['spaceObject']['coordinates']['position'],
- 'planetType' => $fleet['spaceObject']['type'],
- 'name' => $fleet['player']['name'],
- 'armor' => $armorPercent,
- 'weapon' => $weaponPercent,
- 'shield' => $shieldPercent
- );
- }
- }
-
- for ($i = 0; $i < $nbRounds; $i++) {
- $round = $combatReport['combatRounds'][$i];
-
- // Initialisation des variables pour les statistiques
- $a_nb = 0;
- $d_nb = 0;
- $a_shoot = 0;
- $d_shoot = 0;
- $a_bcl = 0;
- $d_bcl = 0;
-
- // Parcourir les statistiques pour identifier attaquant et défenseur
- foreach ($round['statistics'] as $stat) {
- if ($stat['side'] === 'attacker') {
- $a_nb = $stat['hits'] ?? 0;
- $a_shoot = $stat['strength'] ?? 0;
- $a_bcl = $stat['absorbedDamage'] ?? 0;
- } elseif ($stat['side'] === 'defender') {
- $d_nb = $stat['hits'] ?? 0;
- $d_shoot = $stat['strength'] ?? 0;
- $d_bcl = $stat['absorbedDamage'] ?? 0;
- }
- }
-
- $db->sql_query(
- "INSERT INTO " . TABLE_PARSEDRCROUND . " (
- `id_rc`, `numround`, `attaque_tir`, `attaque_puissance`, `defense_bouclier`, `attaque_bouclier`, `defense_tir`, `defense_puissance`
- ) VALUES (
- '{$id_rc}', '{$i}', '{$a_nb}', '{$a_shoot}', '{$d_bcl}', '{$a_bcl}', '{$d_nb}', '{$d_shoot}'
- )"
- );
- $id_rcround = $db->sql_insertid();
-
- if (!$id_rcround) {
- throw new UnexpectedValueException("Combat Report: Failed to insert round {$i}");
- }
-
-
-
- // Utilisation de la configuration existante pour le mapping ID -> code
- $fleetMapping = $databaseSpyId['fleet'];
- $defenseMapping = $databaseSpyId['defense'];
-
- // Colonnes pour attaquants (vaisseaux uniquement)
- $attackerColumns = $database['fleet'];
-
- // Colonnes pour défenseurs (vaisseaux + défenses)
- $defenderColumns = array_merge($database['fleet'], ['SAT', 'FOR'], $database['defense']);
-
- // Traitement des flottes par round
- foreach ($round['fleets'] as $fleet) {
- if (!isset($fleet['side'], $fleet['player'])) {
- continue;
- }
-
- $playerId = isset($fleet['player']['id']) ? (int)$fleet['player']['id'] : -1;
- $playerName = isset($fleet['player']['name']) ? $db->sql_escape_string($fleet['player']['name']) : 'Unknown';
-
- if ($fleet['side'] === 'attacker') {
- // Traitement des attaquants - Trouver l'attaquant par nom au lieu de fleetId
- $attacker = null;
- if (isset($attackers) && is_array($attackers)) {
- foreach ($attackers as $att) {
- if (isset($att['name']) && $att['name'] === $fleet['player']['name']) {
- $attacker = $att;
- }
- }
- }
- // Traitement des attaquants - Recherche directe par nom
-
- if (isset($attackers[$fleet['player']['name']])) {
- $attacker = $attackers[$fleet['player']['name']];
- // Initialiser les compteurs à 0
- $fleetCounts = array_fill_keys($attackerColumns, 0);
- }
-
- // Traiter les technologies du round (vaisseaux restants après combat)
- if (isset($fleet['technologies']) && is_array($fleet['technologies'])) {
- foreach ($fleet['technologies'] as $tech) {
- if (!isset($tech['technologyId'])) continue;
-
- $techId = (int)$tech['technologyId'];
- $remaining = isset($tech['remaining']) ? (int)$tech['remaining'] : 0;
-
- // Utiliser le mapping de la configuration
- if (isset($fleetMapping[$techId]) && in_array($fleetMapping[$techId], $attackerColumns)) {
- $shipCode = $fleetMapping[$techId];
- $fleetCounts[$shipCode] = $remaining;
- }
- }
- }
-
- // Récupérer les stats de combat
- $weapons = (isset($attacker) && is_array($attacker) && isset($attacker['weapon'])) ? (int)$attacker['weapon'] : 0;
- $shields = (isset($attacker) && is_array($attacker) && isset($attacker['shield'])) ? (int)$attacker['shield'] : 0;
- $armor = (isset($attacker) && is_array($attacker) && isset($attacker['armor'])) ? (int)$attacker['armor'] : 0;
-
- // Préparer les valeurs pour l'insertion
- $values = [];
- foreach ($attackerColumns as $column) {
- $values[] = $fleetCounts[$column];
- }
-
- $query = "INSERT INTO " . TABLE_ROUND_ATTACK . " (
- `id_rcround`, `player`, `astro_object_id`, `Armes`, `Bouclier`, `Protection`,
- `" . implode('`, `', $attackerColumns) . "`
- ) VALUES (
- {$id_rcround}, '{$playerName}', {$astro_object_id}, {$weapons}, {$shields}, {$armor},
- " . implode(', ', $values) . "
- )";
-
- $db->sql_query($query);
-
- } elseif ($fleet['side'] === 'defender') {
- // Traitement des défenseurs
-
- // Recherche du défenseur par nom
- $defender = null;
- if (isset($defenders) && is_array($defenders)) {
- foreach ($defenders as $def) {
- if (isset($def['name']) && $def['name'] === $fleet['player']['name']) {
- $defender = $def;
- break;
- }
- }
- }
-
- // Initialiser les compteurs à 0
- $unitCounts = array_fill_keys($defenderColumns, 0);
-
- // Traiter les technologies du round (unités restantes après combat)
- if (isset($fleet['technologies']) && is_array($fleet['technologies'])) {
- foreach ($fleet['technologies'] as $tech) {
- if (!isset($tech['technologyId'])) continue;
-
- $techId = (int)$tech['technologyId'];
- $remaining = isset($tech['remaining']) ? (int)$tech['remaining'] : 0;
-
- $unitCode = null;
- // Chercher dans les mappings de flotte et défense
- if (isset($fleetMapping[$techId])) {
- $unitCode = $fleetMapping[$techId];
- } elseif (isset($defenseMapping[$techId])) {
- $unitCode = $defenseMapping[$techId];
- }
-
- if ($unitCode && in_array($unitCode, $defenderColumns)) {
- $unitCounts[$unitCode] = $remaining;
- }
- }
- }
-
- // Récupérer les stats de combat du défenseur
- $weapons = (isset($defender) && is_array($defender) && isset($defender['weapon'])) ? (int)$defender['weapon'] : 0;
- $shields = (isset($defender) && is_array($defender) && isset($defender['shield'])) ? (int)$defender['shield'] : 0;
- $armor = (isset($defender) && is_array($defender) && isset($defender['armor'])) ? (int)$defender['armor'] : 0;
-
- // Préparer les valeurs pour l'insertion
- $values = [];
- foreach ($defenderColumns as $column) {
- // Traitement spécial pour les boucliers (valeurs binaires)
- if ($column === 'PB' || $column === 'GB') {
- $values[] = ($unitCounts[$column] > 0) ? 1 : 0;
- } else {
- $values[] = $unitCounts[$column];
- }
- }
-
- $query = "INSERT INTO " . TABLE_ROUND_DEFENSE . " (
- `id_rcround`, `player_id`, `astro_object_id`, `Armes`, `Bouclier`, `Protection`,
- `" . implode('`, `', $defenderColumns) . "`
- ) VALUES (
- {$id_rcround}, {$playerId}, {$astro_object_id}, {$weapons}, {$shields}, {$armor},
- " . implode(', ', $values) . "
- )";
-
- $db->sql_query($query);
- }
- }
-
-
- }
- }
- $io->set(array(
- 'type' => $received_game_data['type'],
- ));
-
- add_log($received_game_data['type'], array('toolbar' => $toolbar_info));
- }
-
- break;
-
- case 'ally_list': //PAGE ALLIANCE
- if (!$xtense_user_data['grant']['ranking']) {
- $io->set(array(
- 'type' => 'plugin grant',
- 'access' => 'ranking'
- ));
- $io->status(0);
- } else {
- if (!isset($data['tag'], $data['allyList'])) {
- throw new UnexpectedValueException("Allylist: Missing Value");
- }
-
- if (!isset($data['tag'])) {
- break;
- } //Pas d'alliance
- $tag = filter_var($data['tag']);
- $list = array();
-
- foreach ($data['allyList'] as $data) {
-
- if (!isset($data['player'], $data['points'], $data['rank'], $data['coords'])) {
- throw new UnexpectedValueException("Allylist: Missing Ally Detailed list");
- }
-
- $list[] = array(
- 'pseudo' => filter_var($data['player']),
- 'points' => filter_var($data['points'], FILTER_SANITIZE_NUMBER_INT),
- 'coords' => explode(':', $data['coords']),
- 'rang' => filter_var($data['rank'], FILTER_SANITIZE_NUMBER_INT)
- );
- }
-
- $call->add('ally_list', array(
- 'list' => $list,
- 'tag' => $tag
- ));
-
- $io->set(array(
- 'type' => 'ally_list',
- 'tag' => $tag
- ));
-
- add_log('ally_list', array(
- 'tag' => $tag,
- 'toolbar' => $toolbar_info
- ));
- }
- break;
-
- case 'messages': //PAGE MESSAGES
- if (!$xtense_user_data['grant']['messages']) {
- $io->set(array(
- 'type' => 'plugin grant',
- 'access' => 'messages'
- ));
- $io->status(0);
- } else {
- $line = $data;
- switch ($line['type']) {
- case 'msg': //MESSAGE PERSO
- if (!isset($line['coords'], $line['from'], $line['subject'], $line['message'])) {
- throw new UnexpectedValueException("Personal Message: Incomplete Metadata ");
- }
- $line['coords'] = Check::coords($line['coords']);
- $line['from'] = filter_var($line['from']);
- $line['message'] = filter_var($line['message']);
- $line['subject'] = filter_var($line['subject']);
-
- $msg = array(
- 'coords' => explode(':', $line['coords']),
- 'from' => $line['from'],
- 'subject' => $line['subject'],
- 'message' => $line['message'],
- 'time' => $line['date']
- );
- $call->add('msg', $msg);
- break;
-
- case 'ally_msg': //MESSAGE ALLIANCE
- if (!isset($line['from'], $line['tag'], $line['message'])) {
- throw new UnexpectedValueException("Alliance Message: Incomplete Metadata ");
- }
-
- $line['from'] = filter_var($line['from']);
- $line['tag'] = filter_var($line['tag']);
- $line['message'] = filter_var($line['message']);
-
- $ally_msg = array(
- 'from' => $line['from'],
- 'tag' => $line['tag'],
- 'message' => $line['message'],
- 'time' => $line['date']
- );
- $call->add('ally_msg', $ally_msg);
- break;
-
- case 'spy': //RAPPORT ESPIONNAGE
- case 'spy_shared':
- if (!isset(
- $line['player']['name'],
- $line['planet']['name'],
- $line['planet']['coordinates'],
- $line['planet']['id'],
- $line['resources'],
- $line['proba'],
- $line['activity'],
- $line['date']
- )) {
- throw new UnexpectedValueException("Spy: Incomplete Metadata ");
- }
-
- $coords = Check::coords($line['planet']['coordinates']);
- // Construction de $content à partir des nouvelles sections du JSON
- $content = array();
- if (isset($line['resources']) && is_array($line['resources'])) {
- $content = $content + $line['resources'];
- }
- if (isset($line['buildings']) && is_array($line['buildings'])) {
- $content = $content + $line['buildings'];
- }
- if (isset($line['lfBuildings']) && is_array($line['lfBuildings'])) {
- $content = $content + $line['lfBuildings'];
- }
- if (isset($line['research']) && is_array($line['research'])) {
- $content = $content + $line['research'];
- }
- if (isset($line['lfResearch']) && is_array($line['lfResearch'])) {
- $content = $content + $line['lfResearch'];
- }
- if (isset($line['fleet']) && is_array($line['fleet'])) {
- $content = $content + $line['fleet'];
- }
- if (isset($line['defense']) && is_array($line['defense'])) {
- $content = $content + $line['defense'];
- }
-
- $playerName = filter_var($line['player']['name']);
- $planetName = filter_var($line['planet']['name']);
- $moon = ($line['planet']['type'] === "3");
- $proba = filter_var($line['proba'], FILTER_SANITIZE_NUMBER_INT);
- $activite = filter_var($line['activity'], FILTER_SANITIZE_NUMBER_INT);
- $date = filter_var($line['date'], FILTER_SANITIZE_NUMBER_INT);
-
- $proba = $proba > 100 ? 100 : $proba;
- $activite = $activite > 59 ? 59 : $activite;
- $spy = array(
- 'proba' => $proba,
- 'activite' => $activite,
- 'coords' => $coords,
- 'content' => $content,
- 'time' => $date,
- 'player_name' => $playerName,
- 'planet_name' => $planetName,
- 'planet_id' => $line['planet']['id']
- );
- $call->add($line['type'], $spy);
-
- $spyDB = [];
- foreach ($databaseSpyId as $arr) {
- $spyDB = $spyDB + $arr;
- }
-
- // Initialisation des tableaux pour les champs et les valeurs
- $query_fields = [];
- $query_values = [];
-
- // Champs de base et valeurs correspondantes avec backticks pour les noms de champs
- $query_fields = [
- '`astro_object_id`', '`planet_name`', '`metal`', '`crystal`', '`deuterium`',
- '`sender_id`', '`proba`', '`activite`', '`dateRE`'
- ];
- $query_values = [
- (int)$spy['planet_id'],
- "'" . $db->sql_escape_string($spy['planet_name']) . "'",
- (isset($spy['content']['metal']) ? (int)$spy['content']['metal'] : 0),
- (isset($spy['content']['crystal']) ? (int)$spy['content']['crystal'] : 0),
- (isset($spy['content']['deuterium']) ? (int)$spy['content']['deuterium'] : 0),
- (int)$xtense_user_data['id'],
- (int)$spy['proba'],
- (int)$spy['activite'],
- (int)$spy['time']
- ];
-
- // On traite le contenu additionnel du rapport d'espionnage
- foreach ($spyDB as $code => $name) {
- if ($name === 'metal' || $name === 'crystal' || $name === 'deuterium') {
- continue;
- }
- if (isset($spy['content'][$code])) {
- $field = $name;
- $query_fields[] = '`' . $field . '`';
- $query_values[] = (int)$spy['content'][$code];
- }
- }
-
- // Construction des chaînes finales pour la requête SQL
- $fields_for_db = implode(', ', $query_fields);
- $values_for_db = implode(', ', $query_values);
-
- $spy_time = $spy['time'];
- $test = $db->sql_numrows($db->sql_query("SELECT `id` FROM " . TABLE_PARSEDSPY . " WHERE `astro_object_id` = '{$spy['planet_id']}' AND `dateRE` = '$spy_time'"));
- if (!$test) {
- $db->sql_query("INSERT INTO " . TABLE_PARSEDSPY . " ( " . $fields_for_db . ") VALUES (" . $values_for_db . ")");
-
- // On cherche l'ID du joueur espionné
- $spied_player_id = -1;
- $spied_player_id_result = $db->sql_query("SELECT `id` FROM " . TABLE_GAME_PLAYER . " WHERE `name` = '" . $db->sql_escape_string($spy['player_name']) . "'");
- if ($db->sql_numrows($spied_player_id_result) > 0) {
- $spied_player_id = (int)$db->sql_fetch_row($spied_player_id_result)[0];
- }
-
- list($g, $s, $r) = explode(':', $spy['coords']);
- $planet_type = $moon ? 'moon' : 'planet';
-
- // Préparation de la mise à jour de TABLE_USER_BUILDING
- $update_pairs = [];
- $insert_fields = ['id', 'galaxy', 'system', 'row', 'type', 'name', 'last_update', 'last_update_user_id'];
- $insert_values = [
- (int)$spy['planet_id'],
- (int)$g,
- (int)$s,
- (int)$r,
- "'" . $planet_type . "'",
- "'" . $db->sql_escape_string($spy['planet_name']) . "'",
- (int)$spy['time'],
- (int)$xtense_user_data['id']
- ];
-
- $update_pairs[] = 'name = VALUES(name)';
- $update_pairs[] = 'last_update = VALUES(last_update)';
- $update_pairs[] = 'last_update_user_id = VALUES(last_update_user_id)';
- $update_pairs[] = 'type = VALUES(type)';
- $update_pairs[] = 'galaxy = VALUES(galaxy)';
- $update_pairs[] = 'system = VALUES(system)';
- $update_pairs[] = 'row = VALUES(row)';
-
- if ($spied_player_id !== -1) {
- $insert_fields[] = 'player_id';
- $insert_values[] = (int)$spied_player_id;
- $update_pairs[] = 'player_id = VALUES(player_id)';
- }
-
- // Bâtiments
- if (isset($line['buildings']) && is_array($line['buildings'])) {
- foreach ($databaseSpyId['buildings'] as $code => $name) {
- if (isset($line['buildings'][$code])) {
- $insert_fields[] = $name;
- $insert_values[] = (int)$line['buildings'][$code];
- $update_pairs[] = "`" . $name . "` = VALUES(`" . $name . "`)";
- }
- }
- }
-
- $query = "INSERT INTO " . TABLE_USER_BUILDING . " (`" . implode('`, `', $insert_fields) . "`)
- VALUES (" . implode(', ', $insert_values) . ")
- ON DUPLICATE KEY UPDATE " . implode(', ', $update_pairs);
- $db->sql_query($query);
-
- // Défenses
- if (isset($line['defense']) && is_array($line['defense'])) {
- $defense_fields = [];
- $defense_values = [];
- $defense_update_pairs = [];
-
- foreach ($databaseSpyId['defense'] as $code => $name) {
- if (isset($line['defense'][$code])) {
- $level = (int)$line['defense'][$code];
- $defense_fields[] = "`$name`";
- $defense_values[] = $level;
- $defense_update_pairs[] = "`$name` = $level";
- }
- }
-
- if (!empty($defense_fields)) {
- $db->sql_query("INSERT INTO " . TABLE_GAME_PLAYER_DEFENSE . " (astro_object_id, " . implode(', ', $defense_fields) . ")
- VALUES (" . (int)$spy['planet_id'] . ", " . implode(', ', $defense_values) . ")
- ON DUPLICATE KEY UPDATE " . implode(", ", $defense_update_pairs));
- }
- }
-
- // Flotte
- if (isset($line['fleet']) && is_array($line['fleet'])) {
- $fleet_fields = [];
- $fleet_values = [];
- $fleet_update_pairs = [];
-
- foreach ($databaseSpyId['fleet'] as $code => $name) {
- if (isset($line['fleet'][$code])) {
- $level = (int)$line['fleet'][$code];
- $fleet_fields[] = "`$name`";
- $fleet_values[] = $level;
- $fleet_update_pairs[] = "`$name` = $level";
- }
- }
-
- if (!empty($fleet_fields)) {
- $db->sql_query("INSERT INTO " . TABLE_GAME_PLAYER_FLEET . " (astro_object_id, " . implode(', ', $fleet_fields) . ")
- VALUES (" . (int)$spy['planet_id'] . ", " . implode(', ', $fleet_values) . ")
- ON DUPLICATE KEY UPDATE " . implode(", ", $fleet_update_pairs));
- }
- }
-
- // Recherches
- if ($spied_player_id !== -1 && isset($line['research']) && is_array($line['research'])) {
- $research_fields = [];
- $research_values = [];
- $research_update_parts = [];
-
- foreach ($databaseSpyId['labo'] as $code => $name) {
- if (isset($line['research'][$code])) {
- $level = (int)$line['research'][$code];
- $research_fields[] = "`$name`";
- $research_values[] = $level;
- $research_update_parts[] = "`$name` = $level";
- }
- }
-
- if (!empty($research_fields)) {
- $db->sql_query("INSERT INTO " . TABLE_USER_TECHNOLOGY . " (`player_id`, " . implode(', ', $research_fields) . ")
- VALUES (" . (int)$spied_player_id . ", " . implode(', ', $research_values) . ")
- ON DUPLICATE KEY UPDATE " . implode(", ", $research_update_parts));
- }
- }
-
- // Mise à jour des statistiques
- $db->sql_query('UPDATE ' . TABLE_USER . ' SET `spy_imports` = spy_imports + 1 WHERE `id` = ' . $xtense_user_data['id']);
- update_statistic('spyimports', '1');
- add_log('messages', array('added_spy' => $spy['planet_name'], 'added_spy_coords' => $spy['coords'], 'toolbar' => $toolbar_info));
- }
- break;
-
- case 'ennemy_spy': //RAPPORT ESPIONNAGE ENNEMIS
- if (!isset($line['from'], $line['to'], $line['proba'], $line['date'])) {
- throw new UnexpectedValueException("Ennemy Spy: Incomplete Metadata ");
- }
-
- $line['proba'] = filter_var($line['proba'], FILTER_SANITIZE_NUMBER_INT);
- $line['from'] = Check::coords($line['from']);
- $line['to'] = Check::coords($line['to']);
-
- $query = "SELECT spy_id FROM " . TABLE_PARSEDSPYEN . " WHERE sender_id = '" . $xtense_user_data['user_id'] . "' AND dateSpy = '{$line['date']}'";
- if ($db->sql_numrows($db->sql_query($query)) == 0) {
- $db->sql_query("INSERT INTO " . TABLE_PARSEDSPYEN . " (`dateSpy`, `from`, `to`, `proba`, `sender_id`) VALUES ('" . $line['date'] . "', '" . $line['from'] . "', '" . $line['to'] . "', '" . $line['proba'] . "', '" . $xtense_user_data['user_id'] . "')");
- }
- $ennemy_spy = array(
- 'from' => explode(':', $line['from']),
- 'to' => explode(':', $line['to']),
- 'proba' => (int)$line['proba'],
- 'time' => $line['date']
- );
- $call->add('ennemy_spy', $ennemy_spy);
- add_log('info', array('toolbar' => $toolbar_info, 'message' => "a été espionné avec une probabilité de " . $line['proba']));
- break;
-
- case 'rc_cdr': //RAPPORT RECYCLAGE
- if (!isset($line['nombre'],
- $line['coords'],
- $line['M_recovered'],
- $line['C_recovered'],
- $line['M_total'],
- $line['C_total'],
- $line['date'])) {
- throw new UnexpectedValueException("Harvesting Report: Incomplete Metadata ");
- }
-
- $line['nombre'] = filter_var($line['nombre'], FILTER_SANITIZE_NUMBER_INT);
- $line['coords'] = Check::coords($line['coords']);
- $line['M_recovered'] = filter_var($line['M_recovered'], FILTER_SANITIZE_NUMBER_INT);
- $line['C_recovered'] = filter_var($line['C_recovered'], FILTER_SANITIZE_NUMBER_INT);
- $line['M_total'] = filter_var($line['M_total'], FILTER_SANITIZE_NUMBER_INT);
- $line['C_total'] = filter_var($line['C_total'], FILTER_SANITIZE_NUMBER_INT);
-
- $query = "SELECT `id_rec` FROM " . TABLE_PARSEDREC . " WHERE `sender_id` = '" . $xtense_user_data['user_id'] . "' AND `dateRec` = '{$line['date']}'";
- if ($db->sql_numrows($db->sql_query($query)) == 0) {
- $db->sql_query("INSERT INTO " . TABLE_PARSEDREC . " (`dateRec`, `coordinates`, `nbRec`, `M_total`, `C_total`, `M_recovered`, `C_recovered`, `sender_id`) VALUES ('" . $line['date'] . "', '" . $line['coords'] . "', '" . $line['nombre'] . "', '" . $line['M_total'] . "', '" . $line['C_total'] . "', '" . $line['M_recovered'] . "', '" . $line['C_recovered'] . "', '" . $xtense_user_data['user_id'] . "')");
- }
- $rc_cdr = array(
- 'nombre' => (int)$line['nombre'],
- 'coords' => explode(':', $line['coords']),
- 'M_reco' => (int)$line['M_recovered'],
- 'C_reco' => (int)$line['C_recovered'],
- 'M_total' => (int)$line['M_total'],
- 'C_total' => (int)$line['C_total'],
- 'time' => $line['date']
- );
- $call->add('rc_cdr', $rc_cdr);
- break;
-
- case 'expedition': //RAPPORT EXPEDITION
- case 'expedition_shared':
- if (!isset($line['coords'], $line['content'])) {
- throw new UnexpectedValueException("Expedition Message: Incomplete Metadata ");
- }
-
- $line['content'] = filter_var($line['content']);
- $line['coords'] = Check::coords($line['coords'], 1);
-
- $expedition = array(
- 'time' => $line['date'],
- 'coords' => explode(':', $line['coords']),
- 'content' => $line['content']
- );
- $call->add($line['type'], $expedition);
- break;
-
- default:
- throw new UnexpectedValueException("Message category not found " . $line['type']);
- }
- $io->set(array(
- 'type' => (isset($pub_returnAs) && $pub_returnAs == 'spy' ? 'spy' : 'messages')
- ));
- }
- break;
-
- default:
- throw new UnexpectedValueException("Game Data category not found " . $received_game_data['type']);
-}
-
-$call->apply();
-
+ $handlerClass = match ($receivedGameData['type']) {
+ 'overview' => 'OverviewHandler',
+ 'buildings' => 'BuildingsHandler',
+ 'resourceSettings' => 'ResourceSettingsHandler',
+ 'defense' => 'DefenseHandler',
+ 'researchs' => 'ResearchsHandler',
+ 'fleet' => 'FleetHandler',
+ 'system' => 'SystemHandler',
+ 'ranking' => 'RankingHandler',
+ 'rc', 'rc_shared' => 'CombatReportHandler',
+ 'ally_list' => 'AllyListHandler',
+ 'messages' => 'MessageHandler',
+ default => throw new UnexpectedValueException('Game Data category not found ' . $receivedGameData['type']),
+ };
+
+ $handlerData = $data;
+ if ($handlerClass === 'CombatReportHandler') {
+ $handlerData['_xtense_request_type'] = $receivedGameData['type'];
+ $handlerData['_xtense_raw_payload'] = $json;
+ } elseif ($handlerClass === 'MessageHandler') {
+ $handlerData['_xtense_raw_payload'] = $json;
+ $handlerData['_xtense_return_as'] = $pub_returnAs ?? null;
+ }
+
+ $handler = new $handlerClass(
+ $db,
+ $log,
+ $io,
+ $call,
+ $server_config,
+ $xtense_user_data,
+ $database,
+ $toolbarInfo
+ );
+ $handler->handle($handlerData);
+ $call->apply();
} catch (\Throwable $e) {
- $log->error("Xtense error: " . $e->getMessage(), ['exception' => $e]);
+ $log->error('Xtense error: ' . $e->getMessage(), ['exception' => $e]);
$io->set('error', $e->getMessage());
$io->status(0);
}
-$io->set('execution', str_replace(',', '.', round((microtime(true) - $start_time) * 1000, 2)));
+$io->set('execution', str_replace(',', '.', round((microtime(true) - $startTime) * 1000, 2)));
$io->send();
$db->sql_close();
-
-exit();
+exit;
\ No newline at end of file