Файл: pages/account/reg.php
Строк: 85
<?php
// Physical module map v50.6: public URL stays clean while code lives in /pages.
if (!defined('GAME_ROOT')) define('GAME_ROOT', dirname(__DIR__, 2));
chdir(GAME_ROOT);
if (PHP_SAPI !== 'cli') { $_SERVER['PHP_SELF']='/reg.php'; $_SERVER['SCRIPT_NAME']='/reg.php'; }
/* Обновление игры под PHP 8.4 / PDO, безопасная регистрация и стартовое снаряжение: by Метриум (Стэлп) */
require_once './system/common.php';
require_once './system/functions.php';
require_once './system/user.php';
require_once './system/starter.php';
if ($user) { header('location: /'); exit; }
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') { header('Allow: POST'); http_response_code(405); exit('Метод не разрешён.'); }
security_require_csrf();
if (!security_honeypot_ok()) { http_response_code(400); exit('Ошибка регистрации.'); }
if (!isset($_POST['create_character'])) { http_response_code(400); exit('Действие не принято.'); }
if (!isset($_GET['unic_code'])) { header('location: /'); exit; }
$unic_code = (string)($_GET['unic_code'] ?? '');
if (!security_flow_valid($unic_code, 'start', true)) { http_response_code(409); exit('Сессия создания персонажа истекла. Начните заново.'); }
// Несколько независимых лимитов: защищают таблицу users от быстрого спама.
if (!security_rate_limit('registration_hour', 2, 3600) || !security_rate_limit('registration_day', 5, 86400)) {
http_response_code(429); exit('Слишком много созданий персонажей. Попробуйте позже.');
}
if (!empty($_SESSION['_character_created_at']) && (time()-(int)$_SESSION['_character_created_at']) < 300) {
http_response_code(429); exit('Персонаж уже создавался в этой сессии.');
}
$ip = security_client_ip();
$ua = trim((string)($_SERVER['HTTP_USER_AGENT'] ?? ''));
if ($ua === '' || strlen($ua) > 1000) { http_response_code(400); exit('Некорректный клиент.'); }
// Ограничение старых/несохранённых персонажей с одного IP.
$existingTotal = (int)db_prepare('SELECT COUNT(*) FROM `users` WHERE `ip`=?', [$ip])->fetchColumn();
$existingDraft = (int)db_prepare('SELECT COUNT(*) FROM `users` WHERE `ip`=? AND `save`=0', [$ip])->fetchColumn();
$existingBans = (int)db_prepare('SELECT COUNT(*) FROM `ban` WHERE `ip`=? AND `time`>?', [$ip,time()])->fetchColumn();
if ($existingBans > 0 || $existingDraft >= 2 || $existingTotal >= 5) {
http_response_code(429); exit('Лимит создания персонажей с этого подключения исчерпан.');
}
$state = starter_state();
$ref = (int)($state['ref'] ?? 0);
$password = (string)random_int(100000, 999999);
$pdo = db_pdo();
try {
$pdo->beginTransaction();
$stmt = $pdo->prepare('INSERT INTO `users` (`login`,`password`,`sex`,`ip`,`ua`,`online`,`last_update`) VALUES (?,?,?,?,?,?,?)');
$stmt->execute(['Незнакомец', security_password_hash($password), (int)$state['sex'], $ip, substr($ua,0,255), time(), time()]);
$id = (int)$pdo->lastInsertId();
if ($id <= 0) throw new RuntimeException('Не удалось создать персонажа');
// Нормальная стартовая экономика: премиальное золото не выдаётся сотнями тысяч.
$pdo->prepare('UPDATE `users` SET `g`=0,`s`=5000,`nagrada`=1,`new`=0,`hunt_battles`=15,`hunt_battles_max`=15 WHERE `id`=?')->execute([$id]);
// Выбранные 8 предметов создаются в инвентаре, сразу надеваются и начисляют реальные параметры.
starter_grant_and_equip($pdo, $id);
if ($ref > 0 && $ref !== $id) {
$refStmt = $pdo->prepare('SELECT `id` FROM `users` WHERE `id`=? LIMIT 1');
$refStmt->execute([$ref]);
if ($refStmt->fetchColumn()) {
$pdo->prepare('INSERT INTO `ref` (`user`,`ho`) VALUES (?,?)')->execute([$ref,$id]);
$pdo->prepare('UPDATE `users` SET `g`=`g`+25 WHERE `id`=?')->execute([$ref]);
}
}
// Системная почта и контакт — только если системный пользователь существует.
$sys = $pdo->query('SELECT `id` FROM `users` WHERE `id`=2 LIMIT 1')->fetchColumn();
if ($sys) {
$text = '[color=orange]Приветствуем в игре! Стартовое снаряжение уже надето на вашего героя.[/color]';
$pdo->prepare('INSERT INTO `mail` (`from`,`time`,`read`,`to`,`text`) VALUES (0,?,0,?,?)')->execute([time(),$id,$text]);
$pdo->prepare('INSERT INTO `contacts` (`user`,`ho`,`time`) VALUES (?,2,?)')->execute([$id,time()]);
}
$pdo->commit();
$_SESSION['_character_created_at'] = time();
starter_clear();
security_auth_login($id);
header('location: /arena');
exit;
} catch (Throwable $e) {
if ($pdo->inTransaction()) $pdo->rollBack();
if (function_exists('app_log_write')) app_log_write('REGISTRATION', $e->getMessage(), ['ip'=>$ip]);
http_response_code(500);
exit('Не удалось создать персонажа. Ошибка записана в журнал.');
}