Redesign web battle system from buffered to streaming architecture: Backend Changes: - New SSEOutput class for real-time event streaming to clients - GameSession::streamBattle() for SSE-based battle execution - Enhanced Screen::delay() to support SSE timing and buffering modes - New /api/game/battle-stream endpoint handling SSE connections Frontend Changes: - Enhanced sendInput() to detect battle command (input "1") - New streamBattle() function using EventSource for SSE connections - Real-time log display matching terminal experience - Event handlers for start, message, complete, error events Benefits: ✓ Real-time streaming instead of waiting for complete battle ✓ Web frontend experience identical to terminal ✓ Lightweight implementation without WebSocket ✓ Automatic browser reconnection support ✓ ANSI colors fully preserved ✓ Backward compatible for non-battle screens 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
161 lines
4.2 KiB
PHP
161 lines
4.2 KiB
PHP
<?php
|
||
namespace Game\Core;
|
||
|
||
/**
|
||
* Web 游戏会话 - 使用完整的 Game 逻辑
|
||
*/
|
||
class GameSession
|
||
{
|
||
private Game $game;
|
||
private WebOutput $output;
|
||
private string $userId;
|
||
|
||
public function __construct(string $userId)
|
||
{
|
||
$this->userId = $userId;
|
||
$this->output = new WebOutput();
|
||
|
||
// 设置 Web 输入模式
|
||
$webInput = WebInput::getInstance();
|
||
$webInput->setWebMode(true);
|
||
|
||
// 创建 Game 实例,使用 WebOutput 和用户 ID
|
||
$this->game = new Game(null, $this->output, $userId);
|
||
}
|
||
|
||
/**
|
||
* 渲染当前界面(不需要输入)
|
||
*/
|
||
public function render(): string
|
||
{
|
||
$this->output->clear();
|
||
|
||
try {
|
||
$this->runCurrentState();
|
||
} catch (NeedInputException $e) {
|
||
// 正常情况,界面渲染完成,等待输入
|
||
}
|
||
|
||
return $this->output->getOutput();
|
||
}
|
||
|
||
/**
|
||
* 处理用户输入并返回新界面(支持时间戳数据)
|
||
*/
|
||
public function handleInput(string $input): string|array
|
||
{
|
||
$this->output->clear();
|
||
|
||
// 将输入推入队列
|
||
$webInput = WebInput::getInstance();
|
||
$webInput->clear();
|
||
$webInput->push($input);
|
||
|
||
try {
|
||
$this->runCurrentState();
|
||
} catch (NeedInputException $e) {
|
||
// 需要更多输入,返回当前输出
|
||
}
|
||
|
||
// 保存状态
|
||
$this->game->saveState();
|
||
|
||
// 如果启用了计时模式(战斗中),返回JSON格式的时间戳数据
|
||
if ($this->output->isTimingEnabled()) {
|
||
return $this->output->getTimedOutput();
|
||
}
|
||
|
||
return $this->output->getOutput();
|
||
}
|
||
|
||
/**
|
||
* SSE 流式战斗模式
|
||
* 当用户进入战斗时,使用SSE直接推送日志
|
||
*/
|
||
public function streamBattle(string $input): void
|
||
{
|
||
// 使用SSEOutput替换普通输出
|
||
$sseOutput = new \Game\Core\SSEOutput();
|
||
|
||
// 替换game的output对象
|
||
$this->game->output = $sseOutput;
|
||
|
||
// 将输入推入队列
|
||
$webInput = WebInput::getInstance();
|
||
$webInput->clear();
|
||
$webInput->push($input);
|
||
|
||
try {
|
||
$this->runCurrentState();
|
||
} catch (NeedInputException $e) {
|
||
// 战斗中可能需要更多输入
|
||
}
|
||
|
||
// 保存状态
|
||
$this->game->saveState();
|
||
|
||
// 发送完成信号
|
||
$sseOutput->complete();
|
||
}
|
||
|
||
/**
|
||
* 执行当前状态的逻辑
|
||
*/
|
||
private function runCurrentState(): void
|
||
{
|
||
switch ($this->game->state) {
|
||
case Game::MENU:
|
||
(new \Game\Modules\Menu($this->game))->show();
|
||
break;
|
||
case Game::DUNGEON_SELECT:
|
||
(new \Game\Modules\DungeonSelectPanel($this->game))->show();
|
||
break;
|
||
case Game::BATTLE:
|
||
(new \Game\Modules\Battle($this->game))->start();
|
||
break;
|
||
case Game::STATS:
|
||
(new \Game\Modules\StatsPanel($this->game))->show();
|
||
break;
|
||
case Game::INVENTORY:
|
||
(new \Game\Modules\InventoryPanel($this->game))->show();
|
||
break;
|
||
case Game::EQUIPMENT_ENHANCE:
|
||
(new \Game\Modules\EquipmentEnhancePanel($this->game))->show();
|
||
break;
|
||
case Game::NPC:
|
||
(new \Game\Modules\NpcPanel($this->game))->show();
|
||
break;
|
||
case Game::TALENT:
|
||
(new \Game\Modules\TalentPanel($this->game))->show();
|
||
break;
|
||
case Game::EXIT:
|
||
exit;
|
||
$this->output->writeln("再见!");
|
||
$this->game->saveState();
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取当前游戏状态
|
||
*/
|
||
public function getState(): int
|
||
{
|
||
return $this->game->state;
|
||
}
|
||
|
||
/**
|
||
* 获取玩家信息
|
||
*/
|
||
public function getPlayerInfo(): array
|
||
{
|
||
$stats = $this->game->player->getStats();
|
||
return [
|
||
'level' => $this->game->player->level,
|
||
'hp' => $this->game->player->hp,
|
||
'maxHp' => $stats['maxHp'],
|
||
'spiritStones' => $this->game->player->spiritStones,
|
||
];
|
||
}
|
||
}
|