Add ink integration UI and media playback
This commit is contained in:
Vendored
+27
-17
@@ -58,14 +58,16 @@ const socket_io_1 = require("socket.io");
|
||||
const dotenv = __importStar(require("dotenv"));
|
||||
const fs_1 = require("fs");
|
||||
const zork_llm_engine_1 = require("./engine/zork-llm-engine");
|
||||
const game_config_1 = require("./config/game-config");
|
||||
dotenv.config();
|
||||
const app = (0, express_1.default)();
|
||||
const server = http_1.default.createServer(app);
|
||||
const io = new socket_io_1.Server(server);
|
||||
const DEFAULT_PORT = 3002;
|
||||
const PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : DEFAULT_PORT;
|
||||
const PORT_RANGE = 10;
|
||||
const PORT_RANGE = 300;
|
||||
const DEBUG_ENABLED = /^(1|true|yes|on)$/i.test(process.env.ZORK_DEBUG ?? '');
|
||||
const engineConfig = (0, game_config_1.loadGameConfig)(process.env.ZORK_CONFIG_FILE || './config/engines/zork.json', 'zork');
|
||||
function debugLog(message, details) {
|
||||
if (!DEBUG_ENABLED)
|
||||
return;
|
||||
@@ -85,18 +87,18 @@ app.use(express_1.default.static(path_1.default.join(__dirname, '../public'), {
|
||||
res.setHeader('Expires', '0');
|
||||
},
|
||||
}));
|
||||
app.get('/api/game-config', (_req, res) => {
|
||||
res.json((0, game_config_1.clientGameConfig)(engineConfig));
|
||||
});
|
||||
// One engine instance per connected socket
|
||||
const sessions = new Map();
|
||||
// Save-game slot maps: socketId → Map<slotNumber, serialisedJson>
|
||||
const saveSlots = new Map();
|
||||
function toLegacyNarrative(turn) {
|
||||
const text = (turn.paragraphs ?? [])
|
||||
.map((p) => String(p?.text ?? '').trim())
|
||||
.filter(Boolean)
|
||||
.join('\n\n');
|
||||
function toClientTurn(turn) {
|
||||
return {
|
||||
text,
|
||||
...turn,
|
||||
gameState: {
|
||||
...turn.gameState,
|
||||
currentRoomId: turn.gameState?.statusLine,
|
||||
statusLine: turn.gameState?.statusLine,
|
||||
},
|
||||
@@ -109,7 +111,10 @@ function normalizeSaveSlot(slot) {
|
||||
function getOrCreateEngine(socketId) {
|
||||
let engine = sessions.get(socketId);
|
||||
if (!engine) {
|
||||
engine = new zork_llm_engine_1.ZorkLlmEngine();
|
||||
engine = new zork_llm_engine_1.ZorkLlmEngine({
|
||||
storyPath: (0, game_config_1.projectPath)(process.env.ZORK_STORY_FILE || engineConfig.paths.mainGameFile),
|
||||
promptDir: (0, game_config_1.projectPath)(engineConfig.paths.promptDir || 'data/zork-prompts'),
|
||||
});
|
||||
sessions.set(socketId, engine);
|
||||
}
|
||||
return engine;
|
||||
@@ -130,7 +135,7 @@ async function handleGameApi(socket, method, args) {
|
||||
case 'newGame()': {
|
||||
const engine = getOrCreateEngine(socket.id);
|
||||
const turn = await engine.newGame();
|
||||
socket.emit('narrativeResponse', toLegacyNarrative(turn));
|
||||
socket.emit('narrativeResponse', toClientTurn(turn));
|
||||
return {
|
||||
success: true,
|
||||
result: true,
|
||||
@@ -146,7 +151,7 @@ async function handleGameApi(socket, method, args) {
|
||||
}
|
||||
const engine = getOrCreateEngine(socket.id);
|
||||
const turn = await engine.loadGame(slots.get(slot));
|
||||
socket.emit('narrativeResponse', toLegacyNarrative(turn));
|
||||
socket.emit('narrativeResponse', toClientTurn(turn));
|
||||
socket.emit('gameLoaded', { slot });
|
||||
return { success: true, result: true, running: true, slot };
|
||||
}
|
||||
@@ -184,8 +189,8 @@ async function handleGameApi(socket, method, args) {
|
||||
}
|
||||
}
|
||||
function checkRuntimeConfiguration() {
|
||||
const storyPath = path_1.default.resolve(process.env.ZORK_STORY_FILE ?? './data/z-code/zork1.bin');
|
||||
const promptDir = path_1.default.resolve('./data/zork-prompts');
|
||||
const storyPath = (0, game_config_1.projectPath)(process.env.ZORK_STORY_FILE ?? engineConfig.paths.mainGameFile);
|
||||
const promptDir = (0, game_config_1.projectPath)(engineConfig.paths.promptDir || 'data/zork-prompts');
|
||||
const promptFiles = [
|
||||
'character-generation.yml',
|
||||
'text-rewriter.yml',
|
||||
@@ -221,6 +226,7 @@ function checkRuntimeConfiguration() {
|
||||
}
|
||||
io.on('connection', (socket) => {
|
||||
console.log(`[zork] Client connected: ${socket.id}`);
|
||||
socket.emit('gameConfig', (0, game_config_1.clientGameConfig)(engineConfig));
|
||||
socket.on('gameApi', async (request, respond) => {
|
||||
try {
|
||||
const result = await handleGameApi(socket, String(request?.method ?? ''), Array.isArray(request?.args) ? request.args : []);
|
||||
@@ -257,7 +263,7 @@ io.on('connection', (socket) => {
|
||||
paragraphs: turn.paragraphs.length,
|
||||
statusLine: turn.gameState?.statusLine,
|
||||
});
|
||||
socket.emit('narrativeResponse', toLegacyNarrative(turn));
|
||||
socket.emit('narrativeResponse', toClientTurn(turn));
|
||||
}
|
||||
catch (error) {
|
||||
console.error('[zork] playerCommand error:', error);
|
||||
@@ -291,6 +297,7 @@ function ensureDirectories() {
|
||||
if (!(0, fs_1.existsSync)(dir))
|
||||
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
||||
}
|
||||
(0, game_config_1.ensureConfiguredAssetDirectories)(engineConfig);
|
||||
}
|
||||
function ensureKokoroJs() {
|
||||
const src = path_1.default.join(__dirname, '../node_modules/kokoro-js/dist/index.js');
|
||||
@@ -309,13 +316,15 @@ async function startServer(initialPort, range) {
|
||||
while (port < initialPort + range) {
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
server.listen(port, () => {
|
||||
server.removeAllListeners('error');
|
||||
server.removeAllListeners('listening');
|
||||
server.once('listening', () => {
|
||||
console.log(`[zork] Zork Narrator server running on http://localhost:${port}`);
|
||||
resolve();
|
||||
});
|
||||
server.on('error', (err) => {
|
||||
if (err.code === 'EADDRINUSE') {
|
||||
console.log(`Port ${port} in use, trying ${port + 1}…`);
|
||||
server.once('error', (err) => {
|
||||
if (err.code === 'EADDRINUSE' || err.code === 'EACCES') {
|
||||
console.log(`Port ${port} unavailable (${err.code}), trying ${port + 1}...`);
|
||||
server.close();
|
||||
port++;
|
||||
reject();
|
||||
@@ -324,6 +333,7 @@ async function startServer(initialPort, range) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
server.listen(port);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user