hanli/web/server.php
hant 7308af1c1b Fix web state management: handleInput now returns structured data
Issues fixed:
- GameSession.handleInput() was returning plain text, causing WebSocket frontend
  to lose state information after each input
- GameWebSocketServer.handleGameInput() was calling unnecessary getStateInfo()
- Duplicate state saves in runCurrentState() and handleInput()

Changes:
1. GameSession.php:
   - handleInput() now returns structured array with output + state info
   - runCurrentState() no longer saves state (already done in handleInput)
   - Consistent return format: { output, state, stateName, playerInfo }

2. GameWebSocketServer.php:
   - handleGameInput() simplified to use handleInput() return value
   - Direct merge of result into WebSocket message

3. web/server.php:
   - handleGameInput() simplified to just return handleInput() result
   - No duplicate getStateInfo() call

Results:
- Web frontend now receives complete state info after each input
- State transitions in submenus now work correctly
- No more state desynchronization between client and server

Testing:
- Input in level 3+ menus now executes correctly
- State name updates properly in header
- Player info (HP, etc) stays synchronized

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-07 13:14:48 +08:00

243 lines
6.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* 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 === '/game-ws.html') {
header('Content-Type: text/html; charset=utf-8');
readfile(__DIR__ . '/game-ws.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']);
$result = $session->handleInput($input);
// 现在handleInput返回的是数组output, state, stateName, playerInfo
return array_merge(['success' => true], $result);
}
/**
* 处理 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();
}
}