Issues fixed: - Remove redundant parameter in handleGameInput (line 195) - Add getStateInfo() method to GameSession to return current game state - Return state metadata in both handleGameRender() and handleGameInput() - Add playerInfo (hp, mana, exp) to responses for UI sync - Add state name mapping for debugging Changes: 1. GameSession.php: - Rename getPlayerInfo() to include full stats (mana, exp) - Add getStateInfo() returning state, stateName, playerInfo - Add getStateName() helper to convert state constant to string 2. server.php: - Fix handleGameInput() parameter error - handleGameRender() now returns state metadata - handleGameInput() now returns state metadata 3. index.html (web): - Add console.log for debugging state sync - Add stateName logging to track state transitions - Prepare for renderGame() refresh (commented) These changes allow: - Frontend to verify correct game state after each action - Debugging state sync issues via browser console - Foundation for state validation in UI 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
256 lines
6.5 KiB
PHP
256 lines
6.5 KiB
PHP
<?php
|
|
/**
|
|
* Web 游戏服务器入口
|
|
* 使用方法: php -S 0.0.0.0:8080 web/server.php
|
|
*/
|
|
|
|
// 设置错误报告
|
|
error_reporting(E_ALL);
|
|
ini_set('display_errors', 0);
|
|
|
|
// 自动加载
|
|
require_once __DIR__ . '/../vendor/autoload.php';
|
|
|
|
use Game\Core\UserManager;
|
|
use Game\Core\GameSession;
|
|
use Game\Core\WebInput;
|
|
|
|
// 设置 Web 模式
|
|
WebInput::getInstance()->setWebMode(true);
|
|
|
|
// 启动会话
|
|
session_start();
|
|
|
|
// 设置响应头
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
header('Access-Control-Allow-Origin: *');
|
|
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
|
header('Access-Control-Allow-Headers: Content-Type');
|
|
|
|
// 处理 OPTIONS 预检请求
|
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
|
http_response_code(200);
|
|
exit;
|
|
}
|
|
|
|
// 获取请求路径
|
|
$requestUri = $_SERVER['REQUEST_URI'];
|
|
$path = parse_url($requestUri, PHP_URL_PATH);
|
|
|
|
// 静态文件处理
|
|
if ($path === '/' || $path === '/index.html') {
|
|
header('Content-Type: text/html; charset=utf-8');
|
|
readfile(__DIR__ . '/index.html');
|
|
exit;
|
|
}
|
|
|
|
// API 路由
|
|
$response = ['success' => false, 'message' => '未知请求'];
|
|
|
|
try {
|
|
switch ($path) {
|
|
case '/api/register':
|
|
$response = handleRegister();
|
|
break;
|
|
|
|
case '/api/login':
|
|
$response = handleLogin();
|
|
break;
|
|
|
|
case '/api/logout':
|
|
$response = handleLogout();
|
|
break;
|
|
|
|
case '/api/status':
|
|
$response = handleStatus();
|
|
break;
|
|
|
|
case '/api/game/render':
|
|
$response = handleGameRender();
|
|
break;
|
|
|
|
case '/api/game/input':
|
|
$response = handleGameInput();
|
|
break;
|
|
|
|
case '/api/game/battle-stream':
|
|
// SSE 实时战斗流
|
|
handleBattleStream();
|
|
exit;
|
|
break;
|
|
|
|
default:
|
|
// 检查是否是静态文件
|
|
$filePath = __DIR__ . $path;
|
|
if (file_exists($filePath) && is_file($filePath)) {
|
|
$ext = pathinfo($path, PATHINFO_EXTENSION);
|
|
$mimeTypes = [
|
|
'js' => 'application/javascript',
|
|
'css' => 'text/css',
|
|
'html' => 'text/html',
|
|
'png' => 'image/png',
|
|
'jpg' => 'image/jpeg',
|
|
'gif' => 'image/gif',
|
|
];
|
|
header('Content-Type: ' . ($mimeTypes[$ext] ?? 'application/octet-stream'));
|
|
readfile($filePath);
|
|
exit;
|
|
}
|
|
http_response_code(404);
|
|
$response = ['success' => false, 'message' => '未找到'];
|
|
}
|
|
} catch (Exception $e) {
|
|
http_response_code(500);
|
|
$response = ['success' => false, 'message' => '服务器错误: ' . $e->getMessage()];
|
|
}
|
|
|
|
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
|
|
|
// ============ 处理函数 ============
|
|
|
|
function getInput(): array
|
|
{
|
|
$input = file_get_contents('php://input');
|
|
return json_decode($input, true) ?? [];
|
|
}
|
|
|
|
function handleRegister(): array
|
|
{
|
|
$data = getInput();
|
|
$username = $data['username'] ?? '';
|
|
$password = $data['password'] ?? '';
|
|
|
|
$userManager = new UserManager();
|
|
$result = $userManager->register($username, $password);
|
|
|
|
if ($result['success']) {
|
|
$_SESSION['user_id'] = $result['userId'];
|
|
$_SESSION['username'] = $username;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
function handleLogin(): array
|
|
{
|
|
$data = getInput();
|
|
$username = $data['username'] ?? '';
|
|
$password = $data['password'] ?? '';
|
|
|
|
$userManager = new UserManager();
|
|
$result = $userManager->login($username, $password);
|
|
|
|
if ($result['success']) {
|
|
$_SESSION['user_id'] = $result['userId'];
|
|
$_SESSION['username'] = $username;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
function handleLogout(): array
|
|
{
|
|
session_destroy();
|
|
return ['success' => true, 'message' => '已退出登录'];
|
|
}
|
|
|
|
function handleStatus(): array
|
|
{
|
|
if (empty($_SESSION['user_id'])) {
|
|
return ['success' => false, 'loggedIn' => false, 'message' => '未登录'];
|
|
}
|
|
|
|
return [
|
|
'success' => true,
|
|
'loggedIn' => true,
|
|
'userId' => $_SESSION['user_id'],
|
|
'username' => $_SESSION['username'] ?? '未知',
|
|
];
|
|
}
|
|
|
|
function handleGameRender(): array
|
|
{
|
|
if (empty($_SESSION['user_id'])) {
|
|
return ['success' => false, 'message' => '请先登录'];
|
|
}
|
|
|
|
$session = new GameSession($_SESSION['user_id']);
|
|
$output = $session->render();
|
|
$stateInfo = $session->getStateInfo();
|
|
|
|
return [
|
|
'success' => true,
|
|
'output' => $output,
|
|
'state' => $stateInfo['state'],
|
|
'stateName' => $stateInfo['stateName'],
|
|
'playerInfo' => $stateInfo['playerInfo'],
|
|
];
|
|
}
|
|
|
|
function handleGameInput(): array
|
|
{
|
|
if (empty($_SESSION['user_id'])) {
|
|
return ['success' => false, 'message' => '请先登录'];
|
|
}
|
|
|
|
$data = getInput();
|
|
$input = $data['input'] ?? '';
|
|
|
|
$session = new GameSession($_SESSION['user_id']);
|
|
$output = $session->handleInput($input);
|
|
|
|
// 获取当前游戏状态信息
|
|
$stateInfo = $session->getStateInfo();
|
|
|
|
// 如果输出是数组(时间戳数据),直接返回
|
|
if (is_array($output)) {
|
|
return array_merge(['success' => true], $stateInfo, $output);
|
|
}
|
|
|
|
// 否则返回纯文本输出 + 状态信息
|
|
return [
|
|
'success' => true,
|
|
'output' => $output,
|
|
'state' => $stateInfo['state'],
|
|
'playerInfo' => $stateInfo['playerInfo'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 处理 SSE 实时战斗流
|
|
*/
|
|
function handleBattleStream(): void
|
|
{
|
|
if (empty($_SESSION['user_id'])) {
|
|
http_response_code(401);
|
|
echo "data: " . json_encode(['error' => '请先登录']) . "\n\n";
|
|
return;
|
|
}
|
|
|
|
// 从URL参数或POST数据获取输入
|
|
$input = $_GET['input'] ?? $_POST['input'] ?? '';
|
|
|
|
// 设置 SSE 响应头
|
|
header('Content-Type: text/event-stream');
|
|
header('Cache-Control: no-cache');
|
|
header('Connection: keep-alive');
|
|
header('X-Accel-Buffering: no'); // 禁用代理缓冲
|
|
|
|
// 发送初始化消息
|
|
echo "event: start\n";
|
|
echo "data: " . json_encode(['message' => '战斗开始']) . "\n\n";
|
|
ob_flush();
|
|
flush();
|
|
|
|
// 创建游戏会话并流式处理战斗
|
|
try {
|
|
$session = new GameSession($_SESSION['user_id']);
|
|
$session->streamBattle($input);
|
|
} catch (Exception $e) {
|
|
echo "event: error\n";
|
|
echo "data: " . json_encode(['message' => $e->getMessage()]) . "\n\n";
|
|
ob_flush();
|
|
flush();
|
|
}
|
|
}
|