rubiks-cube-mcp-server
Version:
MCP server for Rubik's Cube solving with real-time 3D visualization
131 lines (130 loc) • 4.99 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.VisualizationServer = void 0;
const express_1 = __importDefault(require("express"));
const http_1 = require("http");
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const WebSocketHandler_js_1 = require("./WebSocketHandler.js");
const APIRoutes_js_1 = require("./APIRoutes.js");
class VisualizationServer {
app;
server;
webSocketHandler;
apiRoutes;
sessions;
constructor() {
this.app = (0, express_1.default)();
this.server = (0, http_1.createServer)(this.app);
this.sessions = new Map();
this.setupTemplateEngine();
this.webSocketHandler = new WebSocketHandler_js_1.WebSocketHandler(this.server, this.sessions);
this.apiRoutes = new APIRoutes_js_1.APIRoutes(this.sessions);
this.setupRoutes();
}
setupTemplateEngine() {
// EJS 템플릿 엔진 설정
this.app.set('view engine', 'ejs');
// 패키지 루트 디렉토리 찾기
const packageRoot = this.findPackageRoot();
this.app.set('views', path_1.default.join(packageRoot, 'views'));
// 정적 파일 서빙
this.app.use(express_1.default.static(path_1.default.join(packageRoot, 'public')));
this.app.use(express_1.default.json());
}
findPackageRoot() {
// 현재 실행 파일의 위치에서 package.json이 있는 디렉토리를 찾음
let currentDir = __dirname;
while (currentDir !== path_1.default.dirname(currentDir)) {
try {
const packageJsonPath = path_1.default.join(currentDir, 'package.json');
if (fs_1.default.existsSync(packageJsonPath)) {
const packageJson = require(packageJsonPath);
if (packageJson.name === 'rubiks-cube-mcp-server') {
return currentDir;
}
}
}
catch (e) {
// continue searching
}
currentDir = path_1.default.dirname(currentDir);
}
// 기본값: 현재 디렉토리에서 2레벨 위
return path_1.default.join(__dirname, '../..');
}
setupRoutes() {
// API 라우트
this.app.use('/api', this.apiRoutes.getRouter());
// 메인 페이지
this.app.get('/', (req, res) => {
const games = Array.from(this.sessions.entries()).map(([id, session]) => ({
id,
title: `Game ${id.split('_')[1]}`,
status: session.status,
moveCount: session.cubeState.moveHistory.length,
createdAt: new Date(session.createdAt).toLocaleString(),
statusIcon: session.status === 'completed' ? '🎉' : '🎲',
statusText: session.status === 'completed' ? 'SOLVED' : 'Active'
}));
res.render('gameList', { games });
});
// 게임 페이지
this.app.get('/game/:gameId', (req, res) => {
const { gameId } = req.params;
const session = this.sessions.get(gameId);
if (!session) {
return res.status(404).send(`
<html>
<body style="font-family: Arial; text-align: center; padding: 50px;">
<h1>Game Not Found</h1>
<p>Game session "${gameId}" not found.</p>
<a href="/">← Back to Games</a>
</body>
</html>
`);
}
res.render('gameView', {
gameId,
session,
cubeState: session.cubeState
});
});
}
// 세션 등록
registerSession(session) {
this.sessions.set(session.id, session);
}
// 세션 업데이트
updateSession(gameId, cubeState) {
const session = this.sessions.get(gameId);
if (session) {
session.cubeState = cubeState;
session.lastActivity = Date.now();
if (cubeState.solved) {
session.status = 'completed';
}
this.webSocketHandler.broadcastGameState(gameId, cubeState, session.status);
}
}
// 서버 시작
start(port = 3000) {
this.server.listen(port, () => {
console.error(`🎲 3D Cube visualization server running on http://localhost:${port}`);
console.error(`🌐 WebSocket enabled for real-time updates`);
});
}
// 서버 종료
stop() {
if (this.server) {
this.server.close(() => {
console.error("✅ Visualization server closed");
});
this.webSocketHandler.close();
}
}
}
exports.VisualizationServer = VisualizationServer;