stdout
This commit is contained in:
+29
-82
@@ -1,75 +1,16 @@
|
||||
|
||||
const IO = {
|
||||
_resolveGetLine: null,
|
||||
_resolveRead: null,
|
||||
_readPointer: null,
|
||||
_nbytes: 0,
|
||||
bufferStdIn : "",
|
||||
putStrListeners: [],
|
||||
listenPutStr(callback) {
|
||||
this.putStrListeners.push(callback)
|
||||
},
|
||||
putStr(str) {
|
||||
console.log('PUTSTR' + str)
|
||||
str = str.split('\n')[2]
|
||||
this.putStrListeners.forEach((listener) => {
|
||||
listener(str)
|
||||
})
|
||||
},
|
||||
async getLine() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._resolveGetLine = resolve
|
||||
this.flushStdIn();
|
||||
});
|
||||
},
|
||||
async read(ptr, nbytes) {
|
||||
this._nbytes = nbytes
|
||||
this._readPointer = ptr
|
||||
return new Promise((resolve, reject) => {
|
||||
this._resolveRead = resolve
|
||||
this.flushStdIn();
|
||||
});
|
||||
},
|
||||
flushStdIn() {
|
||||
if(this._resolveGetLine) {
|
||||
var lineBreak = this.bufferStdIn.indexOf("\n")
|
||||
if (lineBreak != -1) {
|
||||
this._resolveGetLine(stringToNewUTF8(this.bufferStdIn.substring(0,lineBreak+1)))
|
||||
this.bufferStdIn = this.bufferStdIn.substring(lineBreak+1)
|
||||
this._resolveGetLine = null
|
||||
}
|
||||
}
|
||||
if(this._resolveRead) {
|
||||
// console.log(`read: ${this.bufferStdIn}`)
|
||||
stringToUTF8(this.bufferStdIn, this._readPointer, this._nbytes);
|
||||
this._resolveRead()
|
||||
this.bufferStdIn = ""
|
||||
this._resolveRead = null
|
||||
}
|
||||
},
|
||||
putLine(data) {
|
||||
console.log("Client: ",data)
|
||||
const str = data + '\r\n'
|
||||
const byteLength = lengthBytesUTF8(str)
|
||||
this.bufferStdIn += `Content-Length: ${byteLength}\r\n\r\n` + str
|
||||
this.flushStdIn();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var input = ""
|
||||
var i = 0;
|
||||
|
||||
function submit (ev) {
|
||||
ev.preventDefault()
|
||||
return false;
|
||||
}
|
||||
|
||||
var stdoutBuffer = ""
|
||||
var stderrBuffer = ""
|
||||
var messageBuffer = []
|
||||
var initialized = false;
|
||||
|
||||
var headerMode = true;
|
||||
var header="";
|
||||
var re = /Content-Length: (\d+)\r\n/i;
|
||||
var contentLength = 0;
|
||||
var content = []
|
||||
var utf8decoder = new TextDecoder();
|
||||
|
||||
|
||||
function flushMessageBuffer(){
|
||||
if (initialized) {
|
||||
while(messageBuffer.length > 0) {
|
||||
@@ -83,16 +24,29 @@ var Module = {
|
||||
"arguments": ["--worker"],
|
||||
"preRun": [function() {
|
||||
function stdin() {
|
||||
if (i < input.length) {
|
||||
i++;
|
||||
return input.charCodeAt(i);// Return ASCII code of character, or null if no input
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function stdout(asciiCode) {
|
||||
stdoutBuffer += String.fromCharCode(asciiCode)
|
||||
if (headerMode) {
|
||||
header += String.fromCharCode(asciiCode)
|
||||
if (header.endsWith('\r\n\r\n')) {
|
||||
const found = header.match(re)
|
||||
if (found == null) { console.error(`Invalid header: ${header}`) }
|
||||
contentLength = parseInt(found[1])
|
||||
content = []
|
||||
headerMode = false
|
||||
}
|
||||
} else {
|
||||
content.push(asciiCode)
|
||||
if (content.length == contentLength) {
|
||||
const message = utf8decoder.decode(new Uint8Array(content))
|
||||
console.log(`Server: ${message}`)
|
||||
postMessage(message);
|
||||
headerMode = true
|
||||
header = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function stderr(asciiCode) {
|
||||
@@ -114,19 +68,12 @@ importScripts("server.js")
|
||||
|
||||
|
||||
onmessage = (ev) => {
|
||||
console.log(`Client: ${ev.data}`)
|
||||
messageBuffer.push(ev.data);
|
||||
flushMessageBuffer();
|
||||
}
|
||||
|
||||
IO.listenPutStr(message => {
|
||||
postMessage(message)
|
||||
})
|
||||
|
||||
setInterval(() => {
|
||||
if (stdoutBuffer !== "") {
|
||||
console.log(stdoutBuffer);
|
||||
stdoutBuffer = ""
|
||||
}
|
||||
if (stderrBuffer !== "") {
|
||||
console.error(stderrBuffer);
|
||||
stderrBuffer = ""
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import Lean.Server.Watchdog
|
||||
import GameServer.Game
|
||||
|
||||
namespace WasmServer.Watchdog
|
||||
open Lean
|
||||
open Server
|
||||
open Watchdog
|
||||
open IO
|
||||
open Lsp
|
||||
open JsonRpc
|
||||
open System.Uri
|
||||
|
||||
def counter := IO.mkRef 0
|
||||
|
||||
|
||||
def readLspRequestAs (s : String) (expectedMethod : String) (α : Type) [FromJson α] : IO (Request α) := do
|
||||
let j ← ofExcept (Json.parse s)
|
||||
let m ← match fromJson? j with
|
||||
| Except.ok (m : JsonRpc.Message) => pure m
|
||||
| Except.error inner => throw $ userError s!"JSON '{j.compress}' did not have the format of a JSON-RPC message.\n{inner}"
|
||||
let initRequest ← match m with
|
||||
| Message.request id method params? =>
|
||||
if method = expectedMethod then
|
||||
let j := toJson params?
|
||||
match fromJson? j with
|
||||
| Except.ok v => pure $ JsonRpc.Request.mk id expectedMethod (v : α)
|
||||
| Except.error inner => throw $ userError s!"Unexpected param '{j.compress}' for method '{expectedMethod}'\n{inner}"
|
||||
else
|
||||
throw $ userError s!"Expected method '{expectedMethod}', got method '{method}'"
|
||||
| _ => throw $ userError s!"Expected JSON-RPC request, got: '{(toJson m).compress}'"
|
||||
|
||||
|
||||
@[export game_send_message]
|
||||
def sendMessage (s : String) : IO Unit := do
|
||||
-- IO.println s!"received {s}"
|
||||
-- if args.length < 2 then
|
||||
-- throwServerError s!"Expected 1-3 command line arguments in addition to `--server`:
|
||||
-- game directory, the name of the main module (optional), and the name of the game (optional)."
|
||||
-- let gameDir := args[1]!
|
||||
-- let module := if args.length < 3 then defaultGameModule else args[2]!
|
||||
-- let gameName := if args.length < 4 then defaultGameName else args[3]!
|
||||
-- let workerPath := "./gameserver"
|
||||
-- -- TODO: Do the following commands slow us down?
|
||||
-- let srcSearchPath ← initSrcSearchPath (← getBuildDir)
|
||||
-- let references ← IO.mkRef (← loadReferences)
|
||||
-- let fileWorkersRef ← IO.mkRef (RBMap.empty : FileWorkerMap)
|
||||
-- -- let i ← maybeTee "wdIn.txt" false i
|
||||
-- -- let o ← maybeTee "wdOut.txt" true o
|
||||
-- -- let e ← maybeTee "wdErr.txt" true e
|
||||
-- let state : GameServerState := {
|
||||
-- env := ← importModules #[] {} 0 --← createEnv gameDir module,
|
||||
-- game := "TEST",
|
||||
-- gameDir := "test",
|
||||
-- inventory := #[]
|
||||
-- difficulty := 0
|
||||
-- }
|
||||
let initRequest ← readLspRequestAs s "initialize" InitializeParams
|
||||
|
||||
-- We misuse the `rootUri` field to the gameName
|
||||
let rootUri? := "TEST"
|
||||
let initRequest := {initRequest with param := {initRequest.param with rootUri?}}
|
||||
let o ← IO.getStdout
|
||||
o.writeLspResponse {
|
||||
id := initRequest.id
|
||||
result := {
|
||||
capabilities := mkLeanServerCapabilities
|
||||
serverInfo? := some {
|
||||
name := "Lean 4 Game Server"
|
||||
version? := "0.1.1"
|
||||
}
|
||||
: InitializeResult
|
||||
}
|
||||
}
|
||||
-- let context : ServerContext := {
|
||||
-- hIn := i
|
||||
-- hOut := o
|
||||
-- hLog := e
|
||||
-- args := args
|
||||
-- fileWorkersRef := fileWorkersRef
|
||||
-- initParams := initRequest.param
|
||||
-- workerPath
|
||||
-- srcSearchPath
|
||||
-- references
|
||||
-- }
|
||||
-- discard $ ReaderT.run (StateT.run initAndRunWatchdogAux state) context
|
||||
|
||||
return ()
|
||||
@@ -1,5 +1,6 @@
|
||||
import GameServer.FileWorker
|
||||
import GameServer.Watchdog
|
||||
import GameServer.WasmServer
|
||||
import GameServer.Commands
|
||||
|
||||
-- TODO: The only reason we import `Commands` is so that it gets built to on `lake build`
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import Lean.Server.Watchdog
|
||||
|
||||
open Lean
|
||||
open Server
|
||||
open Watchdog
|
||||
open Lsp
|
||||
open IO
|
||||
open JsonRpc
|
||||
|
||||
#check JsonRpc.Request
|
||||
|
||||
@[export game_send_message]
|
||||
def sendMessage (s : String) : IO Unit := do
|
||||
let expectedMethod := "initialize"
|
||||
let j ← ofExcept (Json.parse s)
|
||||
let m ← match fromJson? j with
|
||||
| Except.ok (m : JsonRpc.Message) => pure m
|
||||
| Except.error inner => throw $ userError s!"JSON '{j.compress}' did not have the format of a JSON-RPC message.\n{inner}"
|
||||
let initRequest ← match m with
|
||||
| Message.request id method params? =>
|
||||
if method = expectedMethod then
|
||||
let j := toJson params?
|
||||
match fromJson? j with
|
||||
| Except.ok v => pure $ JsonRpc.Request.mk id expectedMethod (v : InitializeParams)
|
||||
| Except.error inner => throw $ userError s!"Unexpected param '{j.compress}' for method '{expectedMethod}'\n{inner}"
|
||||
else
|
||||
throw $ userError s!"Expected method '{expectedMethod}', got method '{method}'"
|
||||
| _ => throw $ userError s!"Expected JSON-RPC request, got: '{(toJson m).compress}'"
|
||||
IO.println s!"{initRequest.param.editDelay}"
|
||||
return ()
|
||||
@@ -10,8 +10,3 @@ lean_exe gameserver {
|
||||
root := `Main
|
||||
supportInterpreter := true
|
||||
}
|
||||
|
||||
|
||||
@[default_target]
|
||||
lean_lib WasmServer where
|
||||
defaultFacets := #[LeanLib.staticFacet]
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ extern lean_object* game_send_message(lean_object*, lean_object*);
|
||||
extern void lean_initialize_runtime_module();
|
||||
extern void lean_initialize();
|
||||
extern void lean_io_mark_end_initialization();
|
||||
extern lean_object * initialize_WasmServer(uint8_t builtin, lean_object *);
|
||||
extern lean_object * initialize_GameServer_WasmServer(uint8_t builtin, lean_object *);
|
||||
|
||||
int main() {
|
||||
lean_initialize();
|
||||
@@ -16,7 +16,7 @@ int main() {
|
||||
// use same default as for Lean executables
|
||||
uint8_t builtin = 1;
|
||||
lean_object * io_world = lean_io_mk_world();
|
||||
res = initialize_WasmServer(builtin, io_world);
|
||||
res = initialize_GameServer_WasmServer(builtin, io_world);
|
||||
if (lean_io_result_is_ok(res)) {
|
||||
lean_dec_ref(res);
|
||||
} else {
|
||||
|
||||
@@ -10,5 +10,5 @@ OUT_DIR=../client/public
|
||||
LEAN_SYSROOT=/home/alex/Projects/lean4/build/release/stage1
|
||||
LEAN_LIBDIR=$LEAN_SYSROOT/lib/lean
|
||||
|
||||
emcc -o $OUT_DIR/server.js main.c -I $LEAN_SYSROOT/include -L $LEAN_LIBDIR build/ir/WasmServer.c -lInit -lLean -lleancpp -lleanrt \
|
||||
emcc -o $OUT_DIR/server.js main.c -I $LEAN_SYSROOT/include -L $LEAN_LIBDIR build/ir/GameServer/*.c -lInit -lLean -lleancpp -lleanrt \
|
||||
-sFORCE_FILESYSTEM -lnodefs.js -s EXIT_RUNTIME=0 -s MAIN_MODULE=1 -s LINKABLE=1 -s EXPORT_ALL=1 -s ALLOW_MEMORY_GROWTH=1 -fwasm-exceptions -pthread -flto
|
||||
|
||||
Reference in New Issue
Block a user