## TR server lifecycle manager — lazy singleton for integration tests. ## Start with ensureServer(), query with getServerUrl(). import std/[osproc, net, os, strutils, times, streams, exitprocs] const DefaultJar = "/home/davide/Projects/tank-royale/server/build/libs/robocode-tankroyale-server-0.35.5-all.jar" ReadySignal = "Starting server on port" StartupTimeout = 15.0 # seconds var serverProc: Process = nil serverPort: int = 0 proc findFreePort(): int = let s = newSocket() s.bindAddr(Port(0)) let (_, port) = s.getLocalAddr() result = port.int s.close() proc ensureServer*() = if serverProc != nil: return let jar = getEnv("TR_SERVER_JAR", DefaultJar) if not fileExists(jar): raise newException(IOError, "TR server JAR not found: " & jar & "\nSet TR_SERVER_JAR env var to override.") serverPort = findFreePort() serverProc = startProcess("java", args = @["-jar", jar, "--port", $serverPort, "--enable-initial-position", "--tps", "-1"], options = {poUsePath, poStdErrToStdOut}) exitprocs.addExitProc(proc() = if serverProc != nil: serverProc.terminate() serverProc.close() ) # Wait for ready signal let deadline = epochTime() + StartupTimeout var ready = false while epochTime() < deadline: var line: string discard serverProc.outputStream.readLine(line) if ReadySignal in line: ready = true break if not ready: serverProc.terminate() serverProc.close() serverProc = nil raise newException(TimeoutError, "TR server did not start within " & $StartupTimeout & "s") proc getServerUrl*(): string = if serverPort == 0: raise newException(CatchableError, "Server not started — call ensureServer() first") "ws://localhost:" & $serverPort when isMainModule: echo "Starting TR server..." ensureServer() echo "Server ready at: ", getServerUrl() echo "Sleeping 2s then exiting (addExitProc will kill server)..." os.sleep(2000) echo "Done."