From 88d5f6455b66bf550bb956ea6bc2291be8194b84 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Thu, 13 Mar 2025 15:21:11 -0500 Subject: [PATCH] portal spawning works --- scripts/core/engine.js | 153 +++++++++++++++++++++++++++-------------- source/jsffi.c | 9 ++- source/script.h | 1 + tests/contact.js | 8 ++- tests/overling.js | 4 +- tests/portal.js | 6 ++ tests/portalspawner.js | 13 ++-- 7 files changed, 127 insertions(+), 67 deletions(-) diff --git a/scripts/core/engine.js b/scripts/core/engine.js index bb1c9bfa..6119db66 100644 --- a/scripts/core/engine.js +++ b/scripts/core/engine.js @@ -1,8 +1,6 @@ (function engine() { prosperon.DOC = Symbol('+documentation+') // Symbol for documentation references -prosperon.id = 'newguy'; - var listeners = new Map() prosperon.on = function(type, callback) { @@ -26,6 +24,8 @@ prosperon.dispatch = function(type, data) { var os = use_embed('os') var js = use_embed('js') +prosperon.on('exit', _ => { console.log('exiting') }) + prosperon.on('SIGINT', function() { os.exit(1) }) @@ -130,8 +130,11 @@ var res_cache = {} function console_rec(category, priority, line, file, msg) { var now = time.now() - - return `[${prosperon.id.substring(0,6)}] [${time.text(now, "mb d yyyy h:nn:ss")}] ${file}:${line}: [${category} ${priority}]: ${msg}\n` + + var id = prosperon.name ? prosperon.name : prosperon.id + id = id.substring(0,6) + + return `[${id}] [${time.text(now, "mb d yyyy h:nn:ss")}] ${file}:${line}: [${category} ${priority}]: ${msg}\n` } io.mkdir('.prosperon') @@ -140,8 +143,6 @@ var logfile = io.open('.prosperon/log.txt') function pprint(msg, lvl = 0) { if (!logfile) return - if (typeof msg === "object") msg = JSON.stringify(msg, null, 2) - var file = "nofile" var line = 0 @@ -524,6 +525,29 @@ var fnname = "doc" script = `(function ${fnname}() { ${script}; })` js.eval(DOCPATH, script)() +/* + When handling a message, the message appears like this: + { + type: type of message + - contact: used for contact messages + - stop: used to issue stop command + - etc + reply: ID this message will respond to (callback saved on the actor) + replycc: the actor that is waiting for the reply + target: ID of the actor that's supposed to receive the message. Only added to non direct sends (out of portals) + return: reply ID so the replycc actor can know what callback to send the message to + + data: the actual content of the message + } + + actors look like + { + id: the GUID of this actor + address: the IP this actor can be found at + port: the port of the IP the actor can be found at + } +*/ + var enet = use('enet') var util = use('util') var math = use('math') @@ -531,31 +555,36 @@ var crypto = use('crypto') var nota = use('nota') var HEADER = Symbol() -var PEER = Symbol() -var ar = 1 // seconds before reclamation +var ar = 60 // seconds before reclamation + +var $actor = { + toString: print_actor +} function print_actor() { return json.encode(this.__ACTORDATA__, 1) } -function json_actor() { - if (!this.__ACTORDATA__.portal) this.__ACTORDATA__.portal = local_portal - return this +function create_actor(data = {}) { + var newactor = Object.create($actor) + Object.defineProperty(data, 'address', { + get: function() { return local_address }, + set: function(x) {}, + enumerable:true + }) + + Object.defineProperty(data, 'port', { + get: function() { return local_port }, + set: function(x) {}, + enumerable:true + }) + + newactor.__ACTORDATA__ = data + return newactor } -function create_actor(data) { - return { - __ACTORDATA__: data, - toString: print_actor, - toJSON: json_actor - } -} - -var $_ = { - toString: print_actor, - toJSON: json_actor -} +var $_ = create_actor() $_.random = crypto.random $_.random[prosperon.DOC] = "returns a number between 0 and 1. There is a 50% chance that the result is less than 0.5." @@ -571,7 +600,7 @@ globalThis.$_ = $_ var receive_fn = undefined var greeters = {} -$_.is_actor = function(actor) { return !!actor.__ACTORDATA__ } +$_.is_actor = function(actor) { return actor.__ACTORDATA__ } function peer_connection(peer) { return { @@ -610,19 +639,23 @@ $_.connection = function(callback, actor, config) { } $_.connection[prosperon.DOC] = "takes a callback function, an actor object, and a configuration record..." -var peers = {} -var peer_queue = new WeakMap() +var peers = {} // mapping of host to connected peers, so localhost:5678 -> some peer +var id_address = {} // mapping of id to a host, so some guid afnui3289 ... -> localhost:5678 +var peer_queue = new WeakMap() // holds messages for peers that have not yet connected var portal = undefined -var portal_fn -var local_portal +var portal_fn = undefined +var local_address = undefined +var local_port = undefined $_.portal = function(fn, port) { if (portal) throw new Error(`Already started a portal listening on ${portal.port}`) console.log(`starting a portal on port ${port}`) if (!port) throw new Error("Requires a valid port.") portal = enet.create_host({address: "any", port}) - local_portal = `localhost:${port}` + local_address = 'localhost' + local_port = port portal_fn = fn + console.log(`I am now ${$_}`) } $_.portal[prosperon.DOC] = "starts a public address that performs introduction services..." @@ -638,24 +671,35 @@ function handle_host(e) { peer_queue.delete(e.peer) } break + case "disconnect": peer_queue.delete(e.peer) for (var id in peers) if (peers[id] === e.peer) delete peers[id] console.log('portal got disconnect') break + case "receive": - handle_message(e.data) + // if e.data has a replycc, but it has no address, associate it with this peer + var data = nota.decode(e.data) + if (data.replycc && !data.replycc.address) { + data.replycc.__ACTORDATA__.address = e.peer.address + data.replycc.__ACTORDATA__.port = e.peer.port + } + + handle_message(data) + break } } var contactor = undefined $_.contact = function(callback, record) { - var sendto = { __ACTORDATA__: {address: record.address, port: record.port} } - $_.send(create_actor({ - address: record.address, - port: record.port - }), record, callback) + $_.send({ + __ACTORDATA__: { + address: record.address, + port: record.port + } + }, record, callback) } $_.contact[prosperon.DOC] = "The contact function sends a message to a portal..." @@ -712,8 +756,11 @@ $_.couple[prosperon.DOC] = "causes this actor to stop when another actor stops." function actor_send(actor, message) { if (!$_.is_actor(actor)) throw new Error(`Must send to an actor object. Attempted send to ${json.encode(actor)}`) if (typeof message !== 'object') throw new Error('Must send an object record.') - if (receive_fn && actor.__ACTORDATA__.id === prosperon.id) { - receive_fn(message.data) + + if (actor.__ACTORDATA__.id === prosperon.id) { + if (receive_fn) + receive_fn(message.data) + return } @@ -748,19 +795,20 @@ $_.send = function(actor, message, reply) { if (typeof message !== 'object') throw new Error('Message must be an object') var send = {type:"user", data: message} - if (actor[HEADER]) { + if (actor[HEADER] && actor[HEADER].replycc) { var header = actor[HEADER] - console.log(`replying to a message: ${json.encode(header)}`) - if (!actor.__ACTORDATA__) actor.__ACTORDATA__ = {} - actor.__ACTORDATA__.id = header.replycc + if (!header.replycc || !$_.is_actor(header.replycc)) throw new Error(`Supplied actor had a return, but it's not a valid actor! ${json.encode(actor[HEADER])}`); + + var header = actor[HEADER] + actor = header.replycc send.return = header.reply } if (reply) { var id = util.guid() replies[id] = reply - send.reply = id - send.replycc = prosperon.id + send.reply = id // the reply function to hit, local to this actor + send.replycc = $_ // the actor itself } actor_send(actor, send) @@ -773,13 +821,11 @@ cmd.process(prosperon.argv) if (!prosperon.args.id) prosperon.id = util.guid() else prosperon.id = prosperon.args.id -$_.__ACTORDATA__ = {id: prosperon.id} -$_.toJSON = json_actor -$_.toSring = print_actor +$_.__ACTORDATA__.id = prosperon.id if (prosperon.args.overling) overling = { __ACTORDATA__: {id: prosperon.args.overling} } -if (prosperon.args.root) root = { __ACTORDATA__: {id: prosperon.args.root} } -else root = { __ACTORDATA__: {id: prosperon.id} } +if (prosperon.args.root) root = json.decode(prosperon.args.root) +else root = $_ os.mailbox_start(prosperon.id) @@ -789,6 +835,7 @@ if (prosperon.args.program) actor.spawn(prosperon.args.program) var unneeded_timer = $_.delay($_.stop, ar) function destroyself() { + console.log(`Got the message to destroy self.`) if (overling) actor_send(overling, { type: "stopped", id: prosperon.id }) os.exit(0) } @@ -805,8 +852,9 @@ function handle_actor_disconnect(id) { } function handle_message(msg) { - msg = nota.decode(msg) - + if (msg instanceof ArrayBuffer) + msg = nota.decode(msg) + if (msg.target) { if (msg.target !== prosperon.id) { os.mailbox_push(msg.target, nota.encode(msg)) @@ -816,7 +864,6 @@ function handle_message(msg) { unneeded_timer() switch (msg.type) { case "user": - console.log(`handling message ${json.encode(msg)}`) var letter = msg.data delete msg.data letter[HEADER] = msg @@ -828,7 +875,8 @@ function handle_message(msg) { delete replies[msg.return] return } - receive_fn(letter) + if (receive_fn) + receive_fn(letter) break case "stop": @@ -854,7 +902,6 @@ function handle_message(msg) { case "greet": var greeter = greeters[msg.id] - console.log(`got a greet from message ${json.encode(msg)}`) if (greeter) greeter({type: "actor_started", actor: create_actor(msg)}) } unneeded_timer = $_.delay(unneeded_fn, unneeded_time) diff --git a/source/jsffi.c b/source/jsffi.c index adfb75ee..667838f0 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -7676,13 +7676,16 @@ static void signal_handler(int sig) { } if (!str) return; -// script_evalf("prosperon.dispatch('%s')", str); + JSContext *js = SDL_GetTLS(&js_id); + + printf("dispatching to js %p\n", js); + script_evalf(js, "prosperon.dispatch('%s')", str); } static void exit_handler() { -// script_evalf("prosperon.dispatch('exit')"); -// script_stop(); + JSContext *js = SDL_GetTLS(&js_id); + script_evalf(js, "prosperon.dispatch('exit')"); } #include "monocypher.h" diff --git a/source/script.h b/source/script.h index db4ffe21..3a7651d2 100644 --- a/source/script.h +++ b/source/script.h @@ -5,6 +5,7 @@ #include extern SDL_TLSID on_exception; +extern SDL_TLSID js_id; void script_startup(int argc, char **argv); void script_stop(JSContext*); diff --git a/tests/contact.js b/tests/contact.js index 741b88e5..c7563693 100644 --- a/tests/contact.js +++ b/tests/contact.js @@ -4,7 +4,10 @@ function contact_fn(actor,reason) { if (actor) { console.log(`Got an actor: ${json.encode(actor)}`) - $_.send(actor, {greet: "Hello!"}) + $_.send(actor, {greet: "Hello!"}, e => { + console.log(`Got the response ${json.encode(e)}. Goodbye!`); + $_.stop() + }) } else console.log(`Did not get an actor: ${json.encode(reason)}`) @@ -17,7 +20,7 @@ $_.contact(contact_fn, password: "abc123" }); -$_.contact(contact_fn, { +/*$_.contact(contact_fn, { address: "localhost", port: 5678, password: "123abc" @@ -29,3 +32,4 @@ $_.contact(contact_fn, { }) +*/ diff --git a/tests/overling.js b/tests/overling.js index ce338f3d..e8083c73 100644 --- a/tests/overling.js +++ b/tests/overling.js @@ -1,6 +1,6 @@ var os = use('os') -console.log("START") +$_.delay(_ => { $_.start(e => { switch(e.type) { case "actor_started": @@ -12,5 +12,7 @@ $_.start(e => { $_.couple(e.actor) $_.stop(e.actor) + $_.stop() } }, "tests/underling.js"); +}, 3) diff --git a/tests/portal.js b/tests/portal.js index c649f03a..26dc58c7 100644 --- a/tests/portal.js +++ b/tests/portal.js @@ -8,3 +8,9 @@ $_.portal(e => { else $_.send(e, $_) }, 5678); + +$_.receiver(e => { + console.log(`Got message: ${json.encode(e)}`) + $_.send(e, {greet: "Hello back!"}) + $_.delay(_ => $_.stop(), 0.2) +}) diff --git a/tests/portalspawner.js b/tests/portalspawner.js index eeff158e..350c2fdc 100644 --- a/tests/portalspawner.js +++ b/tests/portalspawner.js @@ -4,11 +4,8 @@ var os = use('os') var children = [] -$_.start(e => { - console.log('Portal actor finished starting.') - children.push(e.actor) - $_.start(e => { - children.push(e.actor) - console.log('Contact actor finished starting.') - }, "tests/contact.js") -}, "tests/portal.js") +os.createprocess(["./prosperon", "tests/portal.js"]) + +$_.delay(_ => { + os.createprocess(["./prosperon", "tests/contact.js"]) +}, 0.2)