make game directory more configurable

pull/68/head
Alexander Bentkamp 3 years ago
parent dd7f1a8e20
commit eaf0d13c2f

@ -60,7 +60,7 @@
}, },
"scripts": { "scripts": {
"start": "concurrently -n server,client -c blue,green \"npm run start_server\" \"npm run start_client\"", "start": "concurrently -n server,client -c blue,green \"npm run start_server\" \"npm run start_client\"",
"start_server": "cd server && lake build && (cd adam && lake exe cache get && lake build) && (cd nng && lake build) && NODE_ENV=development nodemon -e mjs --exec \"node ./index.mjs\"", "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", "start_client": "NODE_ENV=development webpack-dev-server --hot",
"build": "npm run build_server && npm run build_client", "build": "npm run build_server && npm run build_client",
"build_server": "server/build.sh", "build_server": "server/build.sh",

@ -359,25 +359,22 @@ section Initialization
fileMap := default fileMap := default
def compileHeader (m : DocumentMeta) (hOut : FS.Stream) (opts : Options) (hasWidgets : Bool) def compileHeader (m : DocumentMeta) (hOut : FS.Stream) (opts : Options) (hasWidgets : Bool)
(levelModule : Name) (initParams : InitializeParams): IO (Syntax × Task (Except Error (Snapshot × SearchPath))) := do (levelParams : Game.DidOpenLevelParams) (initParams : InitializeParams): IO (Syntax × Task (Except Error (Snapshot × SearchPath))) := do
let some gameDir := GameServer.gameDirFromInitParams initParams
| throwServerError s!"Invalid rootUri: {initParams.rootUri?}"
-- Determine search paths of the game project by running `lake env printenv LEAN_PATH`. -- Determine search paths of the game project by running `lake env printenv LEAN_PATH`.
let out ← IO.Process.output let out ← IO.Process.output
{ cwd := gameDir, cmd := "lake", args := #["env","printenv","LEAN_PATH"] } { cwd := levelParams.gameDir, cmd := "lake", args := #["env","printenv","LEAN_PATH"] }
if out.exitCode != 0 then if out.exitCode != 0 then
throwServerError s!"Error while running Lake: {out.stderr}" throwServerError s!"Error while running Lake: {out.stderr}"
-- Make the paths relative to the current directory -- Make the paths relative to the current directory
let paths : List System.FilePath := System.SearchPath.parse out.stdout.trim let paths : List System.FilePath := System.SearchPath.parse out.stdout.trim
let currentDir ← IO.currentDir let currentDir ← IO.currentDir
let paths := paths.map fun p => currentDir / (gameDir : System.FilePath) / p let paths := paths.map fun p => currentDir / (levelParams.gameDir : System.FilePath) / p
-- Set the search path -- Set the search path
Lean.searchPathRef.set paths Lean.searchPathRef.set paths
let env ← importModules [{ module := `Init : Import }, { module := levelModule : Import }] {} 0 let env ← importModules [{ module := `Init : Import }, { module := levelParams.levelModule : Import }] {} 0
-- return (env, paths) -- return (env, paths)
-- use empty header -- use empty header
@ -425,7 +422,7 @@ section Initialization
(levelParams : Game.DidOpenLevelParams) : IO (WorkerContext × WorkerState) := do (levelParams : Game.DidOpenLevelParams) : IO (WorkerContext × WorkerState) := do
let clientHasWidgets := initParams.initializationOptions?.bind (·.hasWidgets?) |>.getD false let clientHasWidgets := initParams.initializationOptions?.bind (·.hasWidgets?) |>.getD false
let (headerStx, headerTask) ← compileHeader meta o opts (hasWidgets := clientHasWidgets) let (headerStx, headerTask) ← compileHeader meta o opts (hasWidgets := clientHasWidgets)
levelParams.levelModule initParams levelParams initParams
let cancelTk ← CancelToken.new let cancelTk ← CancelToken.new
let ctx := let ctx :=
{ hIn := i { hIn := i

@ -5,6 +5,7 @@ open Lean
structure GameServerState := structure GameServerState :=
(env : Lean.Environment) (env : Lean.Environment)
(game : Name) (game : Name)
(gameDir : String)
abbrev GameServerM := StateT GameServerState Server.Watchdog.ServerM abbrev GameServerM := StateT GameServerState Server.Watchdog.ServerM
@ -57,6 +58,7 @@ structure LoadLevelParams where
structure DidOpenLevelParams where structure DidOpenLevelParams where
uri : String uri : String
gameDir : String
levelModule : Name levelModule : Name
tactics : Array ComputedInventoryItem tactics : Array ComputedInventoryItem
lemmas : Array ComputedInventoryItem lemmas : Array ComputedInventoryItem
@ -90,7 +92,8 @@ def handleDidOpenLevel (params : Json) : GameServerM Unit := do
fw.stdin.writeLspNotification { fw.stdin.writeLspNotification {
method := "$/game/didOpenLevel" method := "$/game/didOpenLevel"
param := { param := {
uri := m.uri uri := m.uri
gameDir := (← get).gameDir
levelModule := lvl.module levelModule := lvl.module
tactics := lvl.tactics.computed tactics := lvl.tactics.computed
lemmas := lvl.lemmas.computed lemmas := lvl.lemmas.computed

@ -12,24 +12,13 @@ open Meta
namespace GameServer namespace GameServer
def splitRootUri (initParams : Lsp.InitializeParams) (i : Nat): Option String := Id.run do
let some rootUri := initParams.rootUri?
| return none
let rootUriParts := rootUri.splitOn "/"
if rootUriParts.length == 3 then
return rootUriParts[i]?
return none
def levelIdFromFileName? (initParams : Lsp.InitializeParams) (fileName : String) : Option LevelId := Id.run do def levelIdFromFileName? (initParams : Lsp.InitializeParams) (fileName : String) : Option LevelId := Id.run do
let fileParts := fileName.splitOn "/" let fileParts := fileName.splitOn "/"
if fileParts.length == 3 then if fileParts.length == 3 then
if let (some level, some game) := (fileParts[2]!.toNat?, splitRootUri initParams 2) then if let (some level, some game) := (fileParts[2]!.toNat?, initParams.rootUri?) then
return some {game, world := fileParts[1]!, level := level} return some {game, world := fileParts[1]!, level := level}
return none return none
def gameDirFromInitParams (initParams : Lsp.InitializeParams) : Option String :=
(splitRootUri initParams 0).map (s!"../../{·}")
def getLevelByFileName? [Monad m] [MonadEnv m] (initParams : Lsp.InitializeParams) (fileName : String) : m (Option GameLevel) := do def getLevelByFileName? [Monad m] [MonadEnv m] (initParams : Lsp.InitializeParams) (fileName : String) : m (Option GameLevel) := do
let some levelId := levelIdFromFileName? initParams fileName let some levelId := levelIdFromFileName? initParams fileName
| return none | return none

@ -98,8 +98,7 @@ def initAndRunWatchdog (args : List String) (i o e : FS.Stream) : IO Unit := do
if args.length < 4 then if args.length < 4 then
throwServerError s!"Expected 3 command line arguments in addition to `--server`: throwServerError s!"Expected 3 command line arguments in addition to `--server`:
game directory, the name of the main module, and the name of the game" game directory, the name of the main module, and the name of the game"
let gameId := args[1]! let gameDir := args[1]!
let gameDir := s!"../../{gameId}"
let module := args[2]! let module := args[2]!
let gameName := args[3]! let gameName := args[3]!
let workerPath := "./gameserver" let workerPath := "./gameserver"
@ -110,10 +109,10 @@ def initAndRunWatchdog (args : List String) (i o e : FS.Stream) : IO Unit := do
let i ← maybeTee "wdIn.txt" false i let i ← maybeTee "wdIn.txt" false i
let o ← maybeTee "wdOut.txt" true o let o ← maybeTee "wdOut.txt" true o
let e ← maybeTee "wdErr.txt" true e let e ← maybeTee "wdErr.txt" true e
let state := {env := ← createEnv gameDir module, game := gameName} let state := {env := ← createEnv gameDir module, game := gameName, gameDir := gameDir}
let initRequest ← i.readLspRequestAs "initialize" InitializeParams let initRequest ← i.readLspRequestAs "initialize" InitializeParams
-- We misuse the `rootUri` field to store gameId, module, and gameName -- We misuse the `rootUri` field to the gameName
let rootUri? := s!"{gameId}/{module}/{gameName}" let rootUri? := gameName
let initRequest := {initRequest with param := {initRequest.param with rootUri?}} let initRequest := {initRequest with param := {initRequest.param with rootUri?}}
o.writeLspResponse { o.writeLspResponse {
id := initRequest.id id := initRequest.id

@ -3,6 +3,8 @@
# Operate in the directory where this file is located # Operate in the directory where this file is located
cd $(dirname $0) cd $(dirname $0)
# TODO: Pull prebuilt docker images from GitHub
# Build elan image if not already present # Build elan image if not already present
docker build --pull --rm -f elan.Dockerfile -t elan:latest . docker build --pull --rm -f elan.Dockerfile -t elan:latest .

@ -10,11 +10,13 @@ const games = {
adam: { adam: {
name: "Adam", name: "Adam",
module: "Adam", module: "Adam",
dir: "../../../../Robo",
queueLength: 5 queueLength: 5
}, },
nng: { nng: {
name: "NNG", name: "NNG",
module: "NNG", module: "NNG",
dir: "../../../../NNG4",
queueLength: 5 queueLength: 5
} }
} }
@ -42,11 +44,11 @@ const queueLength = 5
function startServerProcess(gameId) { function startServerProcess(gameId) {
const serverProcess = isDevelopment const serverProcess = isDevelopment
? cp.spawn("./gameserver", ? cp.spawn("./gameserver",
["--server", gameId, games[gameId].module, games[gameId].name], ["--server", games[gameId].dir, games[gameId].module, games[gameId].name],
{ cwd: "./build/bin/" }) { cwd: "./build/bin/" })
: cp.spawn("docker", : cp.spawn("docker",
["run", "--runtime=runsc", "--network=none", "--rm", "-i", `${gameId}:latest`, ["run", "--runtime=runsc", "--network=none", "--rm", "-i", `${gameId}:latest`,
"./gameserver", "--server", gameId, games[gameId].module, games[gameId].name], "./gameserver", "--server", "/game/", games[gameId].module, games[gameId].name],
{ cwd: "." }) { cwd: "." })
serverProcess.on('error', error => serverProcess.on('error', error =>
console.error(`Launching Lean Server failed: ${error}`) console.error(`Launching Lean Server failed: ${error}`)

Loading…
Cancel
Save