From 9ba5acef4da8118ce8a55c7e146ae8c149bf45f5 Mon Sep 17 00:00:00 2001 From: Alexander Bentkamp Date: Wed, 10 May 2023 11:44:25 +0200 Subject: [PATCH] support arbitrary docker containers as games --- client/src/App.tsx | 2 +- client/src/LandingPage/index.html | 4 +- client/src/components/LandingPage.tsx | 7 ++-- client/src/components/Level.tsx | 14 +++---- client/src/components/Welcome.tsx | 6 +-- client/src/index.tsx | 7 ++-- package.json | 6 +-- server/build.sh | 22 ---------- server/index.mjs | 58 ++++++++++++++++----------- 9 files changed, 56 insertions(+), 70 deletions(-) delete mode 100755 server/build.sh diff --git a/client/src/App.tsx b/client/src/App.tsx index 3c06cb9..cf2787b 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -16,7 +16,7 @@ function App() { const params = useParams(); return (
- +
diff --git a/client/src/LandingPage/index.html b/client/src/LandingPage/index.html index f35abca..12c9882 100644 --- a/client/src/LandingPage/index.html +++ b/client/src/LandingPage/index.html @@ -23,7 +23,7 @@
-
+
Formaloversum
Erkunde das Leansche Universum mit deinem Robo, @@ -66,7 +66,7 @@
-
+
Natural Number Game
diff --git a/client/src/components/LandingPage.tsx b/client/src/components/LandingPage.tsx index 81b5ff6..7cdf947 100644 --- a/client/src/components/LandingPage.tsx +++ b/client/src/components/LandingPage.tsx @@ -47,8 +47,7 @@ function GameTile({ let navigate = useNavigate(); const routeChange = () =>{ - let path = `game/${gameId}`; - navigate(path); + navigate(gameId); } return
@@ -108,7 +107,7 @@ function LandingPage() { {0 == gameInfo.data?.worldSize[worldId] ? - : - : + }
@@ -272,7 +272,7 @@ function LevelAppBar({isLoading, levelId, worldId, levelTitle}) { return
- + {gameInfo.data?.worlds.nodes[worldId].title && `World: ${gameInfo.data?.worlds.nodes[worldId].title}`} @@ -282,10 +282,10 @@ function LevelAppBar({isLoading, levelId, worldId, levelTitle}) { {levelTitle}
diff --git a/client/src/components/Welcome.tsx b/client/src/components/Welcome.tsx index 0c66c5b..0382b6b 100644 --- a/client/src/components/Welcome.tsx +++ b/client/src/components/Welcome.tsx @@ -39,7 +39,7 @@ function LevelIcon({ worldId, levelId, position }) { // TODO: relative positioning? return ( - + ) @@ -76,13 +76,13 @@ function Welcome() { for (let i = 1; i <= gameInfo.data.worldSize[id]; i++) { svgElements.push( ) } svgElements.push( - + , - // loader: () => redirect("/game/adam") }, { - path: "/game/:gameId", + path: "/g/:owner/:repo", element: , errorElement: , children: [ { - path: "/game/:gameId", + path: "/g/:owner/:repo", element: , }, { - path: "/game/:gameId/world/:worldId/level/:levelId", + path: "/g/:owner/:repo/world/:worldId/level/:levelId", element: , }, ], diff --git a/package.json b/package.json index 4f6fac5..a1c99c5 100644 --- a/package.json +++ b/package.json @@ -66,10 +66,10 @@ "start": "concurrently -n server,client -c blue,green \"npm run start_server\" \"npm run start_client\"", "start_server": "cd server && lake build && NODE_ENV=development nodemon -e mjs --exec \"node ./index.mjs\"", "start_client": "NODE_ENV=development webpack-dev-server --hot", - "build": "npm run build_server && npm run build_client", - "build_server": "server/build.sh", - "build_client": "NODE_ENV=production webpack", + "build": "NODE_ENV=production webpack", "production": "NODE_ENV=production node server/index.mjs", + "build_robo": "rm -rf ./Robo && git clone https://github.com/hhu-adam/Robo && docker build ./Robo --file ./Robo/Dockerfile --tag github-hhu-adam:Robo && rm -rf ./Robo", + "build_nng": "rm -rf ./NNG4 && git clone https://github.com/hhu-adam/NNG4 && docker build ./NNG4 --file ./NNG4/Dockerfile --tag github-hhu-adam:NNG4 && rm -rf ./NNG4", "update_lean": "./UPDATE_LEAN.sh" }, "eslintConfig": { diff --git a/server/build.sh b/server/build.sh deleted file mode 100755 index 7825db7..0000000 --- a/server/build.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env sh - -# Operate in the directory where this file is located -cd $(dirname $0) - -# Build Adam -( rm -rf adam - git clone https://github.com/hhu-adam/Robo adam/ - cd adam - docker rmi adam:latest || true - docker build \ - --rm -f Dockerfile -t adam:latest . -) - -# Build NNG -( rm -rf nng - git clone https://github.com/hhu-adam/NNG4 nng/ - cd nng - docker rmi nng:latest || true - docker build \ - --rm -f Dockerfile -t nng:latest . -) diff --git a/server/index.mjs b/server/index.mjs index b63602e..22ce928 100644 --- a/server/index.mjs +++ b/server/index.mjs @@ -9,14 +9,20 @@ import os from 'os'; import anonymize from 'ip-anonymize'; import { importTrigger, importStatus } from './import.mjs' +/** Preloaded games. The keys refer to the docker tags of the virtual machines. + * The number `queueLength` determines how many instances of the docker container + * get started before any user shows up to have them up and running immediately. + * The values `name`, `module`, and `dir` are just used for development where we + * use a project directory instead of a docker container. +*/ const games = { - adam: { + "github-hhu-adam:Robo": { name: "Adam", module: "Adam", dir: "../../../../Robo", queueLength: 5 }, - nng: { + "github-hhu-adam:NNG4": { name: "NNG", module: "NNG", dir: "../../../../NNG4", @@ -50,17 +56,18 @@ const isDevelopment = environment === 'development' /** We keep queues of started Lean Server processes to be ready when a user arrives */ const queue = {} -const queueLength = 5 -function startServerProcess(gameId) { - const serverProcess = isDevelopment - ? cp.spawn("./gameserver", - ["--server", games[gameId].dir, games[gameId].module, games[gameId].name], +function startServerProcess(tag) { + let serverProcess + if (isDevelopment && games[tag]?.dir) { + serverProcess = cp.spawn("./gameserver", + ["--server", games[tag].dir, games[tag].module, games[tag].name], { cwd: "./build/bin/" }) - : cp.spawn("docker", - ["run", "--runtime=runsc", "--network=none", "--rm", "-i", `${gameId}:latest`, - "./gameserver", "--server", "/game/", games[gameId].module, games[gameId].name], + } else { + serverProcess = cp.spawn("docker", + ["run", "--runtime=runsc", "--network=none", "--rm", "-i", `${tag}`], { cwd: "." }) + } serverProcess.on('error', error => console.error(`Launching Lean Server failed: ${error}`) ); @@ -73,32 +80,35 @@ function startServerProcess(gameId) { } /** start Lean Server processes to refill the queue */ -function fillQueue(gameId) { - while (queue[gameId].length < games[gameId].queueLength) { - const serverProcess = startServerProcess(gameId) - queue[gameId].push(serverProcess) +function fillQueue(tag) { + while (queue[tag].length < games[tag].queueLength) { + const serverProcess = startServerProcess(tag) + queue[tag].push(serverProcess) } } -for (let gameId in games) { - queue[gameId] = [] - fillQueue(gameId) +if (!isDevelopment) { // Don't use queue in development + for (let tag in games) { + queue[tag] = [] + fillQueue(tag) + } } -const urlRegEx = new RegExp("^/websocket/(.*)$") +const urlRegEx = /^\/websocket\/g\/([\w.-]+)\/([\w.-]+)$/ wss.addListener("connection", function(ws, req) { const reRes = urlRegEx.exec(req.url) if (!reRes) { console.error(`Connection refused because of invalid URL: ${req.url}`); return; } - const gameId = reRes[1] - if (!games[gameId]) { console.error(`Unknown game: ${gameId}`); return; } + const owner = reRes[1] + const repo = reRes[2] + const tag = `github-${owner}:${repo}` let ps; - if (isDevelopment) { // Don't use queue in development - ps = startServerProcess(gameId) + if (!queue[tag] || queue[tag].length == 0) { + ps = startServerProcess(tag) } else { - ps = queue[gameId].shift() // Pick the first Lean process; it's likely to be ready immediately - fillQueue(gameId) + ps = queue[tag].shift() // Pick the first Lean process; it's likely to be ready immediately + fillQueue(tag) } socketCounter += 1;