hanli/src/Core/GameSession.php
hant cb4b955bca Implement Server-Sent Events (SSE) for real-time battle streaming
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>
2025-12-07 10:50:30 +08:00

161 lines
4.2 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
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,
];
}
}