init
This commit is contained in:
commit
6bbf8cfaef
136
.gitignore
vendored
Normal file
136
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# vitepress build output
|
||||
**/.vitepress/dist
|
||||
|
||||
# vitepress cache directory
|
||||
**/.vitepress/cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
1279
package-lock.json
generated
Normal file
1279
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
package.json
Normal file
24
package.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "duelfi-game-manager",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "tsc",
|
||||
"serve": "node dist/index.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"devDependencies": {
|
||||
"@types/express": "^5.0.1",
|
||||
"@types/node": "^22.14.1",
|
||||
"express": "^5.1.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"fs": "^0.0.1-security",
|
||||
"tcp-port-used": "^1.0.2"
|
||||
}
|
||||
}
|
||||
18
src/data.ts
Normal file
18
src/data.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import type { Settings } from "./types";
|
||||
|
||||
export const settings:Settings = {
|
||||
games:[
|
||||
{
|
||||
name:"tetris",
|
||||
path:"/root/games/tetris/tetris",
|
||||
timeout:250
|
||||
},
|
||||
{
|
||||
name:"snakes",
|
||||
path:"/root/games/snakes/snakes",
|
||||
timeout:250
|
||||
}
|
||||
],
|
||||
port_range_start:6000,
|
||||
port_range_end:8000,
|
||||
}
|
||||
42
src/game_starter.ts
Normal file
42
src/game_starter.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { spawn } from "node:child_process";
|
||||
import { settings } from "./data";
|
||||
import { Log } from "./logger";
|
||||
import type { Game } from "./types";
|
||||
import net from 'node:net';
|
||||
|
||||
const tcpPortUsed = require('tcp-port-used');
|
||||
|
||||
|
||||
export async function StartGame(gameName:string, address:string) : Promise<Game | undefined> {
|
||||
const game = settings.games.find(game => game.name === gameName);
|
||||
if(!game){
|
||||
Log(`Game ${gameName} not found`, 'game_starter');
|
||||
return;
|
||||
}
|
||||
|
||||
let port = 2;
|
||||
let portInUse = true;
|
||||
while(portInUse) {
|
||||
port = Math.floor(Math.random() * (settings.port_range_end - settings.port_range_start + 1)) + settings.port_range_start;
|
||||
//Check if port is already in use
|
||||
portInUse = await tcpPortUsed.check(port, '127.0.0.1');
|
||||
if(portInUse){
|
||||
Log(`Port ${port} is already in use, trying another port`, 'game_starter');
|
||||
}
|
||||
}
|
||||
const gameProcess = spawn(game.path, [port.toString()]);
|
||||
|
||||
gameProcess.on('error', (error) => {
|
||||
Log(`Error starting game ${gameName}: ${error.message}`, 'game_starter');
|
||||
return;
|
||||
});
|
||||
|
||||
return {
|
||||
name: gameName,
|
||||
address: address,
|
||||
pid: gameProcess.pid ?? 2,
|
||||
port: port,
|
||||
init_time: new Date().getTime(),
|
||||
}
|
||||
|
||||
}
|
||||
71
src/index.ts
Normal file
71
src/index.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import express, { type Request, type Response } from 'express';
|
||||
import { settings } from './data';
|
||||
import type { Game } from './types';
|
||||
import { StartGame } from './game_starter';
|
||||
import { Log } from './logger';
|
||||
import { spawn } from 'node:child_process';
|
||||
|
||||
const app = express();
|
||||
const port = process.env.PORT || 3035;
|
||||
|
||||
//dictionary of games with address as key
|
||||
const games:Record<string, Game> = {};
|
||||
|
||||
function killGameProcess(pid: number) {
|
||||
try {
|
||||
if (process.platform === 'win32') {
|
||||
spawn('taskkill', ['/pid', pid.toString(), '/f']);
|
||||
} else {
|
||||
// For Linux/Unix systems
|
||||
spawn('kill', ['-9', pid.toString()]);
|
||||
}
|
||||
Log(`Killed game process with PID ${pid}`, 'game_manager');
|
||||
} catch (error) {
|
||||
Log(`Error killing process ${pid}: ${error}`, 'game_manager');
|
||||
}
|
||||
}
|
||||
|
||||
app.get('/getGamePort', async (req: Request, res: Response) => {
|
||||
const {gameName, address} = req.query;
|
||||
|
||||
if(!gameName || !address){
|
||||
res.status(400).send('Missing gameName or address');
|
||||
return;
|
||||
}
|
||||
|
||||
const addressString = address.toString();
|
||||
let game:Game | undefined;
|
||||
if(!games[addressString]){
|
||||
game = await StartGame(gameName.toString(), addressString);
|
||||
if(game){
|
||||
games[addressString] = game;
|
||||
|
||||
// Get the game timeout from settings
|
||||
const gameSettings = settings.games.find(g => g.name === gameName);
|
||||
if (gameSettings?.timeout) {
|
||||
// Set timeout to kill the game
|
||||
setTimeout(() => {
|
||||
if (game?.pid) {
|
||||
killGameProcess(game.pid);
|
||||
delete games[addressString];
|
||||
}
|
||||
}, gameSettings.timeout);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
game = games[addressString];
|
||||
}
|
||||
|
||||
if(!game){
|
||||
res.status(500).send('Failed to start game');
|
||||
return;
|
||||
}
|
||||
|
||||
const gamePort = game.port;
|
||||
res.send(`${gamePort}`);
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server running at http://localhost:${port}`);
|
||||
});
|
||||
|
||||
6
src/logger.ts
Normal file
6
src/logger.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import * as fs from 'fs';
|
||||
|
||||
export function Log(message: string, title = 'log') {
|
||||
console.log(`[${new Date().toISOString()}] ${title}: ${message}`);
|
||||
fs.appendFileSync(`logs/${title}.txt`, `${new Date().toISOString()}: ${message}\n`);
|
||||
}
|
||||
20
src/types.ts
Normal file
20
src/types.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
export interface Game{
|
||||
pid:number;
|
||||
address:string;
|
||||
name:string;
|
||||
port:number;
|
||||
init_time:number;
|
||||
}
|
||||
|
||||
|
||||
export interface Settings{
|
||||
games:GameData[];
|
||||
port_range_start:number;
|
||||
port_range_end:number;
|
||||
}
|
||||
|
||||
export interface GameData{
|
||||
name:string;
|
||||
path:string;
|
||||
timeout:number;
|
||||
}
|
||||
13
tsconfig.json
Normal file
13
tsconfig.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"outDir": "./dist",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user