|
|
|
|
@@ -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)
|
|
|
|
|
})
|
|
|
|
|
@@ -131,7 +131,10 @@ 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({
|
|
|
|
|
$_.send({
|
|
|
|
|
__ACTORDATA__: {
|
|
|
|
|
address: record.address,
|
|
|
|
|
port: record.port
|
|
|
|
|
}), record, callback)
|
|
|
|
|
}
|
|
|
|
|
}, 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) {
|
|
|
|
|
|
|
|
|
|
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,6 +852,7 @@ function handle_actor_disconnect(id) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handle_message(msg) {
|
|
|
|
|
if (msg instanceof ArrayBuffer)
|
|
|
|
|
msg = nota.decode(msg)
|
|
|
|
|
|
|
|
|
|
if (msg.target) {
|
|
|
|
|
@@ -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,6 +875,7 @@ function handle_message(msg) {
|
|
|
|
|
delete replies[msg.return]
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (receive_fn)
|
|
|
|
|
receive_fn(letter)
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
@@ -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)
|
|
|
|
|
|