Fix TTS module initialization and dependency issues. Update module IDs for consistency, improve circular dependency detection, and fix UI Controller event handling.
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Test Server for AI Interactive Fiction
|
||||
* Simplified version that sends test paragraphs instead of using LLM
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import express from 'express';
|
||||
import http from 'http';
|
||||
import { Server as SocketIOServer } from 'socket.io';
|
||||
import * as dotenv from 'dotenv';
|
||||
import { existsSync, mkdirSync, copyFileSync } from 'fs';
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config();
|
||||
|
||||
// Create Express application
|
||||
const app = express();
|
||||
const server = http.createServer(app);
|
||||
const io = new SocketIOServer(server);
|
||||
|
||||
// Get port from environment variables or use default
|
||||
const DEFAULT_PORT = 3001;
|
||||
const PORT = process.env.PORT ? parseInt(process.env.PORT) : DEFAULT_PORT;
|
||||
const PORT_RANGE = 10; // Try up to 10 ports starting from the default
|
||||
|
||||
// Serve static files from the public directory
|
||||
app.use(express.static(path.join(__dirname, '../public')));
|
||||
|
||||
// Test paragraphs to send to the client
|
||||
const TEST_PARAGRAPHS = [
|
||||
"You stand at the entrance of a mysterious cave. The air is cool and damp, carrying the scent of earth and ancient stone. Shadows dance on the walls as your torch flickers in the gentle breeze.",
|
||||
"As you venture deeper, the passage narrows. Stalactites hang from the ceiling like stone daggers, their surfaces glistening with moisture. The sound of dripping water echoes through the silence.",
|
||||
"Suddenly, the passage opens into a vast chamber. Crystal formations catch the light of your torch, sending rainbow reflections across the walls. In the center of the room stands an ancient stone pedestal, its surface carved with symbols from a forgotten language."
|
||||
];
|
||||
|
||||
// Handle socket connections
|
||||
io.on('connection', (socket) => {
|
||||
console.log(`New client connected: ${socket.id}`);
|
||||
let currentParagraphIndex = 0;
|
||||
|
||||
// Start a new game
|
||||
socket.on('startGame', async () => {
|
||||
try {
|
||||
console.log('Starting test game session');
|
||||
|
||||
// Send introduction to client
|
||||
socket.emit('gameIntroduction', {
|
||||
introduction: "Welcome to the Interactive Fiction Test. This is a simplified version that sends predefined paragraphs instead of using an LLM.",
|
||||
initialRoomDescription: TEST_PARAGRAPHS[0],
|
||||
currentRoomId: "test-room"
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error starting game:', error);
|
||||
socket.emit('error', { message: 'Failed to start game. Please try again.' });
|
||||
}
|
||||
});
|
||||
|
||||
// Process player command
|
||||
socket.on('playerCommand', async (data) => {
|
||||
try {
|
||||
console.log(`Received command: ${data.command}`);
|
||||
|
||||
// Move to the next paragraph
|
||||
currentParagraphIndex = (currentParagraphIndex + 1) % TEST_PARAGRAPHS.length;
|
||||
|
||||
// Send narrative response to client
|
||||
socket.emit('narrativeResponse', {
|
||||
text: TEST_PARAGRAPHS[currentParagraphIndex],
|
||||
gameState: {
|
||||
currentRoomId: "test-room"
|
||||
},
|
||||
suggestions: ["look around", "examine pedestal", "touch crystals"]
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error processing command:', error);
|
||||
socket.emit('error', { message: 'Failed to process command. Please try again.' });
|
||||
}
|
||||
});
|
||||
|
||||
// Handle disconnection
|
||||
socket.on('disconnect', () => {
|
||||
console.log(`Client disconnected: ${socket.id}`);
|
||||
});
|
||||
});
|
||||
|
||||
// Ensure required asset folders exist
|
||||
function ensureDirectories() {
|
||||
const dirs = [
|
||||
path.join(__dirname, '../public'),
|
||||
path.join(__dirname, '../public/js'),
|
||||
path.join(__dirname, '../public/css'),
|
||||
path.join(__dirname, '../public/images'),
|
||||
path.join(__dirname, '../public/fonts')
|
||||
];
|
||||
|
||||
for (const dir of dirs) {
|
||||
if (!existsSync(dir)) {
|
||||
mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy kokoro-js library from node_modules if not already present
|
||||
function ensureKokoroJs() {
|
||||
const source = path.join(__dirname, '../node_modules/kokoro-js/dist/index.js');
|
||||
const destination = path.join(__dirname, '../public/js/kokoro-js.js');
|
||||
|
||||
if (existsSync(source) && !existsSync(destination)) {
|
||||
copyFileSync(source, destination);
|
||||
console.log(`Copied kokoro-js from ${source} to ${destination}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Start the server with port fallback
|
||||
async function startServer(initialPort: number, range: number): Promise<void> {
|
||||
let currentPort = initialPort;
|
||||
const maxPort = initialPort + range;
|
||||
|
||||
// Try ports in the specified range
|
||||
while (currentPort < maxPort) {
|
||||
try {
|
||||
// Ensure directories exist
|
||||
ensureDirectories();
|
||||
|
||||
// Ensure kokoro-js is copied
|
||||
try {
|
||||
ensureKokoroJs();
|
||||
} catch (error) {
|
||||
console.error('Error copying kokoro-js:', error);
|
||||
}
|
||||
|
||||
// Try to start the server on the current port
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
server.listen(currentPort, () => {
|
||||
console.log(`AI Interactive Fiction TEST SERVER running on http://localhost:${currentPort}`);
|
||||
console.log('This server is sending predefined test paragraphs instead of using an LLM');
|
||||
resolve();
|
||||
});
|
||||
|
||||
server.on('error', (error: NodeJS.ErrnoException) => {
|
||||
// If port is in use, try next port
|
||||
if (error.code === 'EADDRINUSE') {
|
||||
console.log(`Port ${currentPort} is in use, trying next port...`);
|
||||
server.close();
|
||||
currentPort++;
|
||||
reject();
|
||||
} else {
|
||||
// For other errors, log and reject
|
||||
console.error('Server error:', error);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// If we reach here, server started successfully
|
||||
return;
|
||||
|
||||
} catch (error) {
|
||||
// If we reach the max port and still fail, throw an error
|
||||
if (currentPort >= maxPort - 1) {
|
||||
throw new Error(`Failed to start server on ports ${initialPort} to ${maxPort - 1}`);
|
||||
}
|
||||
|
||||
// Otherwise try the next port
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start the server when this module is run directly
|
||||
if (require.main === module) {
|
||||
startServer(PORT, PORT_RANGE).catch(error => {
|
||||
console.error('Failed to start server:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export { app, server, io };
|
||||
Reference in New Issue
Block a user