hide actor data
Some checks failed
Build and Deploy / package-dist (push) Blocked by required conditions
Build and Deploy / deploy-itch (push) Blocked by required conditions
Build and Deploy / deploy-gitea (push) Blocked by required conditions
Build and Deploy / build-linux (push) Successful in 1m16s
Build and Deploy / build-windows (CLANG64) (push) Failing after 10m57s
Some checks failed
Build and Deploy / package-dist (push) Blocked by required conditions
Build and Deploy / deploy-itch (push) Blocked by required conditions
Build and Deploy / deploy-gitea (push) Blocked by required conditions
Build and Deploy / build-linux (push) Successful in 1m16s
Build and Deploy / build-windows (CLANG64) (push) Failing after 10m57s
This commit is contained in:
@@ -529,49 +529,70 @@ var util = use('util')
|
||||
var math = use('math')
|
||||
var crypto = use('crypto')
|
||||
|
||||
var REPLY = Symbol()
|
||||
var REPLYCC = Symbol()
|
||||
var ACTORID = Symbol()
|
||||
var HEADER = Symbol()
|
||||
var ACTORDATA = Symbol()
|
||||
var PEER = Symbol()
|
||||
|
||||
var ar = 5 // seconds before reclamation
|
||||
var ar = 1 // seconds before reclamation
|
||||
|
||||
function print_actor()
|
||||
{
|
||||
return json.encode(this[ACTORDATA], 1);
|
||||
}
|
||||
|
||||
function json_actor() {
|
||||
var ret = this[ACTORDATA]
|
||||
|
||||
// The idea here is that, if we're serializing an actor, it's being sent over the wire
|
||||
if (!ret.portal)
|
||||
ret.portal = local_portal
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
function create_actor(data) {
|
||||
return {
|
||||
[ACTORDATA]: data,
|
||||
toString: print_actor,
|
||||
toJSON: json_actor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
When an actor object like $_ is passed to another machine or function, it appears like this
|
||||
[ACTORDATA]: {
|
||||
id: local to the machine. Currently, the local port it is listening on
|
||||
address: the IP address of the portal that connects it, if set
|
||||
port: the public port of the portal that connects it, if set
|
||||
|
||||
There is a map of actor.id -> peer, so messages can be sent.
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
Each actor has an id. When an actor object is received, that is essentially just an id. The method to reach the id is in routing tables local to the actor that has obtained the object. If actor.id === prosperon.id, it's localhost.
|
||||
var $_ = {
|
||||
toString: print_actor,
|
||||
toJSON: json_actor
|
||||
}
|
||||
|
||||
Currently, each actor has an enet peer, so we're focused on that. Eventually, there might also be a thread local mailbox.
|
||||
*/
|
||||
|
||||
var $_ = {}
|
||||
|
||||
$_.random = crypto.random;
|
||||
$_.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."
|
||||
$_.clock = function(fn) { return os.now() }
|
||||
$_.clock[prosperon.DOC] = "takes a function input value that will eventually be called with the current time in number form."
|
||||
|
||||
var underlings = new Set()
|
||||
var overling = undefined
|
||||
var root = undefined // set with the root of all actors
|
||||
var root = undefined
|
||||
|
||||
globalThis.$_ = $_
|
||||
|
||||
var receive_fn
|
||||
var receive_fn = undefined
|
||||
var greeters = {}
|
||||
|
||||
var peers = {} // mapping of guids to peers
|
||||
var greeters = {} // mapping of underling guids to their system callback functions
|
||||
var peer2id = new WeakMap() // local bound peers to relevant id
|
||||
$_.is_actor = function(actor) {
|
||||
if (actor[ACTORDATA]) return true
|
||||
return false
|
||||
}
|
||||
|
||||
function peer_connection(peer)
|
||||
{
|
||||
function peer_connection(peer) {
|
||||
return {
|
||||
latency: peer.rtt,
|
||||
bandwidth: {
|
||||
@@ -595,309 +616,256 @@ function peer_connection(peer)
|
||||
}
|
||||
|
||||
$_.connection = function(callback, actor, config) {
|
||||
var peer = peers[actor.id]
|
||||
var peer = peers[actor[ACTORDATA].id]
|
||||
if (peer) {
|
||||
callback(peer_connection(peer))
|
||||
return
|
||||
}
|
||||
|
||||
if (os.mailbox_exist(actor.id)) {
|
||||
if (os.mailbox_exist(actor[ACTORDATA].id)) {
|
||||
callback({type:"local"})
|
||||
return
|
||||
}
|
||||
|
||||
throw new Error(`Could not get connection information for ${actor}`)
|
||||
};
|
||||
$_.connection[prosperon.DOC] = "takes a callback function, an actor object, and a configuration record for getting information about the status of a connection to the actor. The configuration record is used to request the sort of information that needs to be communicated."
|
||||
}
|
||||
$_.connection[prosperon.DOC] = "takes a callback function, an actor object, and a configuration record..."
|
||||
|
||||
var peers = {}
|
||||
var peer_queue = new WeakMap()
|
||||
var portal = undefined
|
||||
var portal_fn
|
||||
$_.portal = function(fn, port)
|
||||
{
|
||||
if (portal)
|
||||
throw new Error(`Already started a portal listening on ${portal.port}`)
|
||||
var local_portal
|
||||
|
||||
// The portal function is the only way an actor object can leave the machine; so on its way out, it needs tagged with the portal data
|
||||
$_.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", // any can connect
|
||||
port,
|
||||
})
|
||||
|
||||
if (!port) throw new Error("Requires a valid port.")
|
||||
portal = enet.create_host({address: "any", port})
|
||||
local_portal = `localhost:${port}`
|
||||
portal_fn = fn
|
||||
}
|
||||
$_.portal[prosperon.DOC] = "starts apublic address that performs introduction services. It listens on a specified port for contacts by external actors that need to acquire an actor object. The function will receive the record containing the request. The record can have a reply sent through it. A portal can respond by beginning a new actor, or finding an existing actor, or by forwarding the contact message to another actor. This is how distributed Misty networks are bootstrapped."
|
||||
$_.portal[prosperon.DOC] = "starts a public address that performs introduction services..."
|
||||
|
||||
function handle_portal(e)
|
||||
{
|
||||
function handle_host(e) {
|
||||
switch (e.type) {
|
||||
case "connect":
|
||||
console.log('portal got connect')
|
||||
console.log(`connected a new peer: ${e.peer.address}:${e.peer.port}`)
|
||||
peers[`${e.peer.address}:${e.peer.port}`] = e.peer
|
||||
var queue = peer_queue.get(e.peer)
|
||||
if (queue) {
|
||||
for (var msg of queue) e.peer.send(msg)
|
||||
console.log(`sent ${json.encode(msg)} out of queue`)
|
||||
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":
|
||||
portal_fn(e.data.data)
|
||||
handle_message(e.data)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var peer2contact = new WeakMap() //
|
||||
$_.contact = function(callback, record)
|
||||
{
|
||||
if (!callback) throw new Error('Contact requires a callback function')
|
||||
console.log(`connecting to ${json.encode(record)}`)
|
||||
var peer = host.connect(record.address, record.port)
|
||||
peer2contact.set(peer, {
|
||||
callback,
|
||||
record
|
||||
})
|
||||
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)
|
||||
}
|
||||
$_.contact[prosperon.DOC] = `The contact function sends a message to a portal on another machine to obtain an actor object.
|
||||
$_.contact[prosperon.DOC] = "The contact function sends a message to a portal..."
|
||||
|
||||
The callback is a function with a actor input and a reason input. If successful, actor is bound to an actor object. If not successful, actor is null and reason may contain an explanation.`
|
||||
$_.receiver = function(fn) { receive_fn = fn }
|
||||
$_.receiver[prosperon.DOC] = "registers a function that will receive all messages..."
|
||||
|
||||
$_.receiver = function(fn)
|
||||
{
|
||||
receive_fn = fn;
|
||||
}
|
||||
$_.receiver[prosperon.DOC] = `registers a function that will receive all messages sent to the actor except for delay events, reply messages (which are sent to the send callback), the unneeded message, and portal contact messages.`
|
||||
|
||||
$_.start = function(cb, prg, arg)
|
||||
{
|
||||
$_.start = function(cb, prg, arg) {
|
||||
var id = util.guid()
|
||||
greeters[id] = cb
|
||||
var argv = [
|
||||
"./prosperon",
|
||||
"spawn",
|
||||
"--id", id,
|
||||
"--overling", prosperon.id,
|
||||
"--root", root
|
||||
]
|
||||
|
||||
if (prg)
|
||||
argv = argv.concat(['--program', prg])
|
||||
|
||||
if (arg)
|
||||
argv = argv.concat(cmd.encode(arg))
|
||||
|
||||
var argv = ["./prosperon", "spawn", "--id", id, "--overling", prosperon.id, "--root", root]
|
||||
if (prg) argv = argv.concat(['--program', prg])
|
||||
if (arg) argv = argv.concat(cmd.encode(arg))
|
||||
underlings.add(id)
|
||||
|
||||
os.createthread(argv);
|
||||
os.createthread(argv)
|
||||
}
|
||||
$_.start[prosperon.DOC] = `The start function creates a new actor. The callback function receives messages about the new actor, starting with a message containing the new actor's address object.
|
||||
$_.start[prosperon.DOC] = "The start function creates a new actor..."
|
||||
|
||||
The program text identifies the executable in the program shop that the new actor runs.
|
||||
|
||||
The arguments array contains up to four arguments of type logical, number, text, or actor address object.
|
||||
|
||||
The current actor is the overling of the new actor, and it is notified when the new actor stops. The new actor is an underling of the current actor.`
|
||||
|
||||
$_.stop = function(actor)
|
||||
{
|
||||
if (!actor)
|
||||
destroyself()
|
||||
|
||||
if (!underlings.has(actor.id))
|
||||
$_.stop = function(actor) {
|
||||
if (!actor) destroyself()
|
||||
if (!underlings.has(actor[ACTORDATA].id))
|
||||
throw new Error('Can only call stop on an underling or self.')
|
||||
|
||||
actor_send(actor, {type:"stop", id: prosperon.id})
|
||||
}
|
||||
$_.stop[prosperon.DOC] = `The stop function stops an underling.`
|
||||
$_.stop[prosperon.DOC] = "The stop function stops an underling."
|
||||
|
||||
var unneeded_fn = $_.stop
|
||||
var unneeded_time = ar
|
||||
|
||||
$_.unneeded = function(fn, seconds = ar)
|
||||
{
|
||||
$_.unneeded = function(fn, seconds = ar) {
|
||||
if (typeof fn !== 'function') throw new Error('Must supply a function to unneeded.')
|
||||
unneeded_fn = fn
|
||||
unneeded_time = seconds
|
||||
}
|
||||
$_.unneeded[prosperon.DOC] = `registers a function that is called when the actor has not received a message in the recent seconds. The default for seconds is the ar timer. This likely means that the actor is no longer needed. The actor should finish its work and then @.stop().`
|
||||
$_.unneeded[prosperon.DOC] = "registers a function that is called when the actor..."
|
||||
|
||||
var timers = []
|
||||
var timer_id = 0
|
||||
|
||||
$_.delay = function(fn, seconds)
|
||||
{
|
||||
timers[timer_id++] = {
|
||||
seconds,
|
||||
fn
|
||||
}
|
||||
|
||||
$_.delay = function(fn, seconds) {
|
||||
timers[timer_id++] = {seconds, fn}
|
||||
return function() { delete timers[timer_id] }
|
||||
}
|
||||
$_.delay[prosperon.DOC] = `used to schedule the invocation of a function at a later time. Any value returned from the delayed invocation is ignored`
|
||||
$_.delay[prosperon.DOC] = "used to schedule the invocation of a function..."
|
||||
|
||||
// Set of actor guids
|
||||
var couplings = new Set()
|
||||
|
||||
$_.couple = function(actor)
|
||||
{
|
||||
console.log(`coupled to ${actor.id}`)
|
||||
couplings.add(actor.id)
|
||||
$_.couple = function(actor) {
|
||||
console.log(`coupled to ${actor[ACTORDATA].id}`)
|
||||
couplings.add(actor[ACTORDATA].id)
|
||||
}
|
||||
$_.couple[prosperon.DOC] = `causes this actor to stop when another actor stops.`
|
||||
$_.couple[prosperon.DOC] = "causes this actor to stop when another actor stops."
|
||||
|
||||
// Shuffsles the message to the actor with whatever means available
|
||||
function actor_send(actor, message)
|
||||
{
|
||||
if (typeof message !== 'object')
|
||||
throw new Error('Must send an object record.')
|
||||
|
||||
if (receive_fn && actor.id === prosperon.id) { // handle loopback case
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
var peer = peers[actor.id]
|
||||
if (peer) {
|
||||
peer.send(message)
|
||||
if (actor[ACTORDATA].id && os.mailbox_exist(actor[ACTORDATA].id)) {
|
||||
os.mailbox_push(actor[ACTORDATA].id, message)
|
||||
return
|
||||
}
|
||||
|
||||
os.mailbox_push(actor.id, message)
|
||||
if (actor[ACTORDATA].address) {
|
||||
if (actor[ACTORDATA].id) message.target = actor[ACTORDATA].id
|
||||
else message.type = "contact"
|
||||
var peer = peers[actor[ACTORDATA].address + ":" + actor[ACTORDATA].port]
|
||||
if (!peer) {
|
||||
if (!contactor && !portal) {
|
||||
console.log(`creating a contactor ...`)
|
||||
contactor = enet.create_host()
|
||||
}
|
||||
peer = (contactor || portal).connect(actor[ACTORDATA].address, actor[ACTORDATA].port)
|
||||
peer_queue.set(peer, [message])
|
||||
} else peer.send(message)
|
||||
return
|
||||
}
|
||||
|
||||
throw new Error(`Unable to send message to actor ${json.encode(actor)}`)
|
||||
}
|
||||
|
||||
// Map of reply IDs to functions
|
||||
var replies = {}
|
||||
|
||||
// Map of a message object to a peer for replying directly
|
||||
var reply_cc = new WeakMap()
|
||||
var contact_replies = new WeakMap()
|
||||
|
||||
$_.send = function(actor, message, reply)
|
||||
{
|
||||
if (typeof message !== 'object')
|
||||
throw new Error('Must send an object record.')
|
||||
$_.send = function(actor, message, reply) {
|
||||
if (typeof message !== 'object') throw new Error('Message must be an object')
|
||||
|
||||
console.log(`sending to ${json.encode(actor)} ...`)
|
||||
var send = {type:"user", data: message}
|
||||
|
||||
var send = {
|
||||
type:"user",
|
||||
data: message
|
||||
}
|
||||
|
||||
if (actor[REPLYCC]) {
|
||||
console.log(`replying to a message: ${json.encode(actor)}`)
|
||||
console.log(`replycc and reply are ${actor[REPLYCC]} and ${actor[REPLY]}`)
|
||||
actor.id = actor[REPLYCC]
|
||||
send.return = actor[REPLY]
|
||||
if (actor[HEADER]) { // This means it's a response message
|
||||
var header = actor[HEADER]
|
||||
console.log(`replying to a message: ${json.encode(header)}`)
|
||||
// Only set id if ACTORDATA exists, otherwise create it
|
||||
if (!actor[ACTORDATA]) actor[ACTORDATA] = {}
|
||||
actor[ACTORDATA].id = header.replycc
|
||||
send.return = header.reply
|
||||
}
|
||||
|
||||
if (reply) {
|
||||
var id = util.guid()
|
||||
replies[id] = reply
|
||||
send.reply = id
|
||||
send.id = prosperon.id
|
||||
send.replycc = prosperon.id
|
||||
}
|
||||
|
||||
actor_send(actor, send)
|
||||
}
|
||||
$_.send[prosperon.DOC] = `sends a message to another actor. The left expression must resolve to an actor address object or a message object that is expecting a reply because it was sent with a callback. The right expression is a record containing the outgoing message. The outgoing message record must not contain functions or patterns or cyclic structures.
|
||||
|
||||
If a callback function is included, then the callback function will receive the reply, not the receiver function.`
|
||||
$_.send[prosperon.DOC] = "sends a message to another actor..."
|
||||
|
||||
var cmd = use('cmd')
|
||||
cmd.process(prosperon.argv)
|
||||
|
||||
if (!prosperon.args.id)
|
||||
prosperon.id = util.guid()
|
||||
else
|
||||
prosperon.id = prosperon.args.id;
|
||||
if (!prosperon.args.id) prosperon.id = util.guid()
|
||||
else prosperon.id = prosperon.args.id
|
||||
|
||||
if (prosperon.args.overling)
|
||||
overling = { id: prosperon.args.overling }
|
||||
$_[ACTORDATA] = {id: prosperon.id}
|
||||
$_.toJSON = json_actor
|
||||
$_.toSring = print_actor
|
||||
|
||||
if (prosperon.args.root)
|
||||
root = { id: prosperon.args.root }
|
||||
else
|
||||
root = { 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} }
|
||||
|
||||
// now can start the mailbox
|
||||
os.mailbox_start(prosperon.id);
|
||||
os.mailbox_start(prosperon.id)
|
||||
|
||||
// send simple greet so overling knows it started
|
||||
if (overling)
|
||||
actor_send(overling, {type:'greet', id: prosperon.id})
|
||||
|
||||
if (prosperon.args.program)
|
||||
actor.spawn(prosperon.args.program)
|
||||
if (overling) actor_send(overling, {type:'greet', id: prosperon.id})
|
||||
if (prosperon.args.program) actor.spawn(prosperon.args.program)
|
||||
|
||||
var unneeded_timer = $_.delay($_.stop, ar)
|
||||
|
||||
function destroyself()
|
||||
{
|
||||
if (overling)
|
||||
actor_send(overling, { type: "stopped" , id: prosperon.id})
|
||||
|
||||
function destroyself() {
|
||||
if (overling) actor_send(overling, { type: "stopped", id: prosperon.id })
|
||||
os.exit(0)
|
||||
}
|
||||
|
||||
function handle_actor_disconnect(id)
|
||||
{
|
||||
function handle_actor_disconnect(id) {
|
||||
var greeter = greeters[id]
|
||||
if (greeter) {
|
||||
greeter({
|
||||
type: "stopped"
|
||||
})
|
||||
// Greeter can now be removed
|
||||
greeter({type: "stopped", id})
|
||||
delete greeters[id]
|
||||
}
|
||||
|
||||
console.log(`actor ${id} disconnected`)
|
||||
|
||||
if (couplings.has(id))
|
||||
$_.stop()
|
||||
|
||||
if (couplings.has(id)) $_.stop()
|
||||
delete peers[id]
|
||||
}
|
||||
|
||||
/*
|
||||
msg format is:
|
||||
id: id of the actor that sent the message
|
||||
reply: reply id for a response
|
||||
*/
|
||||
|
||||
function handle_message(msg)
|
||||
{
|
||||
function handle_message(msg) {
|
||||
if (msg.target) {
|
||||
if (msg.target !== prosperon.id) {
|
||||
os.mailbox_push(msg.target, msg)
|
||||
return
|
||||
}
|
||||
}
|
||||
unneeded_timer()
|
||||
switch (msg.type) {
|
||||
case "user":
|
||||
console.log(`handling message ${json.encode(msg)}`)
|
||||
var letter = msg.data
|
||||
delete msg.data
|
||||
letter[HEADER] = msg
|
||||
if (msg.return) {
|
||||
console.log(`Message has a return address.`)
|
||||
console.log(`Received a message for the return id ${msg.return}`);
|
||||
var fn = replies[msg.return]
|
||||
if (!fn)
|
||||
throw new Error(`Could not find return function for message ${msg.return}`)
|
||||
|
||||
fn(msg)
|
||||
if (!fn) throw new Error(`Could not find return function for message ${msg.return}`)
|
||||
fn(letter)
|
||||
delete replies[msg.return]
|
||||
return
|
||||
}
|
||||
|
||||
if (receive_fn) {
|
||||
if (msg.reply) {
|
||||
msg.data[REPLY] = msg.reply
|
||||
msg.data[REPLYCC] = msg.id
|
||||
}
|
||||
|
||||
receive_fn(msg.data)
|
||||
}
|
||||
receive_fn(letter)
|
||||
break
|
||||
|
||||
case "stop":
|
||||
if (msg.id !== overling.id)
|
||||
throw new Error(`Got a message from an actor ${msg.id} to stop, that was not our overling (${overling.id}).`);
|
||||
if (msg.id !== overling[ACTORDATA].id)
|
||||
throw new Error(`Got a message from an actor ${msg.id} to stop...`)
|
||||
|
||||
destroyself()
|
||||
break
|
||||
|
||||
case "contact":
|
||||
if (portal_fn) portal_fn(msg.data)
|
||||
if (portal_fn) {
|
||||
var letter = msg.data
|
||||
letter[HEADER] = msg
|
||||
delete msg.data
|
||||
portal_fn(letter)
|
||||
}
|
||||
else throw new Error('Got a contact message, but no portal is established.')
|
||||
break
|
||||
|
||||
case "stopped":
|
||||
@@ -906,40 +874,19 @@ function handle_message(msg)
|
||||
|
||||
case "greet":
|
||||
var greeter = greeters[msg.id]
|
||||
if (greeter) greeter({
|
||||
type: "actor_started",
|
||||
actor: { id: 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)
|
||||
}
|
||||
|
||||
function handle_connect(e)
|
||||
{
|
||||
var contact = peer2contact.get(e.peer)
|
||||
if (contact) {
|
||||
// We have successfully made contact. now send the request.
|
||||
e.peer.send({
|
||||
type: "contact",
|
||||
data: contact.record
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// For a local greet
|
||||
e.peer.send({ id: prosperon.id })
|
||||
}
|
||||
|
||||
var hang = 0.01
|
||||
var last_t = os.now()
|
||||
|
||||
while (1) {
|
||||
if (portal)
|
||||
portal.service(handle_portal, hang)
|
||||
|
||||
if (portal) portal.service(handle_host, hang)
|
||||
if (contactor) contactor.service(handle_host, hang)
|
||||
os.mailbox_service(prosperon.id, handle_message)
|
||||
|
||||
var elapsed = os.now() - last_t
|
||||
last_t = os.now()
|
||||
for (var i in timers) {
|
||||
|
||||
@@ -7159,11 +7159,9 @@ JSC_CCALL(os_mailbox_push,
|
||||
if (argc < 2) return JS_ThrowInternalError(js, "Need an actor and an array buffer.");
|
||||
char *id = JS_ToCString(js,argv[0]);
|
||||
void *nota = value2nota(js, argv[1]);
|
||||
printf("Sending to %s\n", id);
|
||||
|
||||
int mailbox_index = shgeti(mailboxes, id);
|
||||
if (mailbox_index == -1) {
|
||||
printf("COULD NOT FIND MAILBLOX FOR %s\n", id);
|
||||
JS_FreeCString(js,id);
|
||||
return JS_ThrowInternalError(js, "No mailbox found for given ID.");
|
||||
}
|
||||
@@ -7172,7 +7170,6 @@ JSC_CCALL(os_mailbox_push,
|
||||
|
||||
SDL_LockMutex(mb->mutex);
|
||||
arrput(mb->messages, nota);
|
||||
printf("Added a letter, now the mailbox %s has %d\n", id, arrlen(mb->messages));
|
||||
SDL_UnlockMutex(mb->mutex);
|
||||
|
||||
JS_FreeCString(js,id);
|
||||
@@ -7184,7 +7181,6 @@ JSC_CCALL(os_mailbox_service, // grab all from our mailbox and
|
||||
|
||||
int mb_index = shgeti(mailboxes, id);
|
||||
if (mb_index == -1) {
|
||||
printf("could not find mailbox for %s\n", id);
|
||||
JS_FreeCString(js,id);
|
||||
JS_FreeValue(js,fn);
|
||||
return JS_ThrowInternalError(js, "No mailbox found for given ID.");
|
||||
@@ -7196,7 +7192,6 @@ JSC_CCALL(os_mailbox_service, // grab all from our mailbox and
|
||||
SDL_LockMutex(mb->mutex);
|
||||
int count = arrlen(mb->messages);
|
||||
if (count > 0) {
|
||||
printf("servicing %d letters for %s\n", count, id);
|
||||
arrsetlen(temp,count);
|
||||
memcpy(temp, mb->messages, sizeof(void*) * count);
|
||||
arrsetlen(mb->messages,0);
|
||||
@@ -7319,7 +7314,6 @@ JSC_CCALL(js_cycle_hook,
|
||||
if (JS_IsUndefined(argv[0]))
|
||||
js_debug_sethook(js,NULL,JS_HOOK_CYCLE);
|
||||
else {
|
||||
printf("SETTING DEBUG HOOK\n");
|
||||
prosperon_rt *rt = JS_GetContextOpaque(js);
|
||||
JS_FreeValue(js,rt->cycle_fn);
|
||||
rt->cycle_fn = JS_DupValue(js,argv[0]);
|
||||
|
||||
@@ -47,113 +47,71 @@ static JSValue js_enet_deinitialize(JSContext *ctx, JSValueConst this_val,
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue js_enet_host_create(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv) {
|
||||
static JSValue js_enet_host_create(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
ENetHost *host;
|
||||
ENetAddress address;
|
||||
JSValue obj;
|
||||
|
||||
// Default parameters (if not provided by user)
|
||||
ENetAddress *send = &address;
|
||||
size_t peer_count = 1000;
|
||||
size_t channel_limit = 0;
|
||||
enet_uint32 incoming_bandwidth = 0;
|
||||
enet_uint32 outgoing_bandwidth = 0;
|
||||
JSValue obj;
|
||||
|
||||
// If no arguments or first arg is not an object, create a client-like host.
|
||||
if (argc < 1 || !JS_IsObject(argv[0])) {
|
||||
address.host = ENET_HOST_ANY;
|
||||
address.port = 0;
|
||||
|
||||
host = enet_host_create(&address,
|
||||
peer_count,
|
||||
channel_limit,
|
||||
incoming_bandwidth,
|
||||
outgoing_bandwidth);
|
||||
if (!host) {
|
||||
return JS_ThrowInternalError(ctx, "Failed to create ENet host with 'any:0'.");
|
||||
}
|
||||
goto RET;
|
||||
host = enet_host_create(NULL, peer_count, channel_limit, incoming_bandwidth, outgoing_bandwidth);
|
||||
if (!host) return JS_ThrowInternalError(ctx, "Failed to create ENet client host");
|
||||
goto wrap;
|
||||
}
|
||||
|
||||
// Now parse the object
|
||||
JSValue configObj = argv[0];
|
||||
JSValue config_obj = argv[0];
|
||||
JSValue addr_val = JS_GetPropertyStr(ctx, config_obj, "address");
|
||||
const char *addr_str = JS_IsString(addr_val) ? JS_ToCString(ctx, addr_val) : NULL;
|
||||
JS_FreeValue(ctx, addr_val);
|
||||
|
||||
// 1) address
|
||||
JSValue addrVal = JS_GetPropertyStr(ctx, configObj, "address");
|
||||
const char *addrStr = NULL;
|
||||
if (JS_IsString(addrVal)) {
|
||||
addrStr = JS_ToCString(ctx, addrVal);
|
||||
}
|
||||
JS_FreeValue(ctx, addrVal);
|
||||
|
||||
// If address not given or not string, default to "any".
|
||||
if (!addrStr) {
|
||||
addrStr = "any";
|
||||
}
|
||||
|
||||
// 2) port
|
||||
JSValue portVal = JS_GetPropertyStr(ctx, configObj, "port");
|
||||
if (!addr_str)
|
||||
send = NULL;
|
||||
else {
|
||||
JSValue port_val = JS_GetPropertyStr(ctx, config_obj, "port");
|
||||
int32_t port32 = 0;
|
||||
JS_ToInt32(ctx, &port32, portVal); // if invalid or undefined, remains 0
|
||||
JS_FreeValue(ctx, portVal);
|
||||
JS_ToInt32(ctx, &port32, port_val);
|
||||
JS_FreeValue(ctx, port_val);
|
||||
|
||||
// 3) channels -> channel_limit
|
||||
JSValue chanVal = JS_GetPropertyStr(ctx, configObj, "channels");
|
||||
JS_ToUint32(ctx, &channel_limit, chanVal); // default 0 if missing
|
||||
JS_FreeValue(ctx, chanVal);
|
||||
|
||||
// 4) incoming_bandwidth
|
||||
JSValue inBWVal = JS_GetPropertyStr(ctx, configObj, "incoming_bandwidth");
|
||||
JS_ToUint32(ctx, &incoming_bandwidth, inBWVal);
|
||||
JS_FreeValue(ctx, inBWVal);
|
||||
|
||||
// 5) outgoing_bandwidth
|
||||
JSValue outBWVal = JS_GetPropertyStr(ctx, configObj, "outgoing_bandwidth");
|
||||
JS_ToUint32(ctx, &outgoing_bandwidth, outBWVal);
|
||||
JS_FreeValue(ctx, outBWVal);
|
||||
|
||||
// Populate ENetAddress
|
||||
if (strcmp(addrStr, "any") == 0) {
|
||||
if (strcmp(addr_str, "any") == 0)
|
||||
address.host = ENET_HOST_ANY;
|
||||
} else if (strcmp(addrStr, "broadcast") == 0) {
|
||||
else if (strcmp(addr_str, "broadcast") == 0)
|
||||
address.host = ENET_HOST_BROADCAST;
|
||||
} else {
|
||||
int err = enet_address_set_host_ip(&address, addrStr);
|
||||
else {
|
||||
int err = enet_address_set_host_ip(&address, addr_str);
|
||||
if (err != 0) {
|
||||
// Free addrStr only if it came from JS_ToCString
|
||||
if (addrStr && addrStr != "any" && addrStr != "broadcast") {
|
||||
JS_FreeCString(ctx, addrStr);
|
||||
}
|
||||
return JS_ThrowInternalError(
|
||||
ctx, "Failed to set host IP from '%s'. Error code: %d", addrStr, err
|
||||
);
|
||||
JS_FreeCString(ctx, addr_str);
|
||||
return JS_ThrowInternalError(ctx, "Failed to set host IP from '%s'. Error: %d", addr_str, err);
|
||||
}
|
||||
}
|
||||
address.port = (enet_uint16)port32;
|
||||
|
||||
// Now that we're done using addrStr, free it if it was allocated.
|
||||
if (addrStr && addrStr != "any" && addrStr != "broadcast") {
|
||||
JS_FreeCString(ctx, addrStr);
|
||||
JS_FreeCString(ctx, addr_str);
|
||||
}
|
||||
|
||||
// Finally, create the host
|
||||
host = enet_host_create(&address,
|
||||
peer_count,
|
||||
channel_limit,
|
||||
incoming_bandwidth,
|
||||
outgoing_bandwidth);
|
||||
if (!host) {
|
||||
return JS_ThrowInternalError(ctx, "Failed to create ENet host.");
|
||||
}
|
||||
JSValue chan_val = JS_GetPropertyStr(ctx, config_obj, "channels");
|
||||
JS_ToUint32(ctx, &channel_limit, chan_val);
|
||||
JS_FreeValue(ctx, chan_val);
|
||||
|
||||
RET:
|
||||
// Wrap up in a QuickJS object
|
||||
JSValue in_bw_val = JS_GetPropertyStr(ctx, config_obj, "incoming_bandwidth");
|
||||
JS_ToUint32(ctx, &incoming_bandwidth, in_bw_val);
|
||||
JS_FreeValue(ctx, in_bw_val);
|
||||
|
||||
JSValue out_bw_val = JS_GetPropertyStr(ctx, config_obj, "outgoing_bandwidth");
|
||||
JS_ToUint32(ctx, &outgoing_bandwidth, out_bw_val);
|
||||
JS_FreeValue(ctx, out_bw_val);
|
||||
|
||||
host = enet_host_create(send, peer_count, channel_limit, incoming_bandwidth, outgoing_bandwidth);
|
||||
if (!host) return JS_ThrowInternalError(ctx, "Failed to create ENet host");
|
||||
|
||||
wrap:
|
||||
obj = JS_NewObjectClass(ctx, enet_host_id);
|
||||
if (JS_IsException(obj)) {
|
||||
enet_host_destroy(host);
|
||||
return obj;
|
||||
}
|
||||
// Associate our C pointer with this JS object
|
||||
JS_SetOpaque(obj, host);
|
||||
return obj;
|
||||
}
|
||||
@@ -509,11 +467,18 @@ static JSClassDef enet_peer_class = {
|
||||
.gc_mark = js_enet_peer_mark
|
||||
};
|
||||
|
||||
JSValue js_enet_resolve_hostname(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
char *hostname = JS_ToCString(js,argv[0]);
|
||||
ENetAddress addr;
|
||||
}
|
||||
|
||||
/* Function lists */
|
||||
static const JSCFunctionListEntry js_enet_funcs[] = {
|
||||
JS_CFUNC_DEF("initialize", 0, js_enet_initialize),
|
||||
JS_CFUNC_DEF("deinitialize", 0, js_enet_deinitialize),
|
||||
JS_CFUNC_DEF("create_host", 1, js_enet_host_create),
|
||||
JS_CFUNC_DEF("resolve_hostname", 1, js_enet_resolve_hostname),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_enet_host_funcs[] = {
|
||||
|
||||
@@ -1,12 +1,31 @@
|
||||
// Test to connect to a portal
|
||||
|
||||
$_.contact((actor, reason) => {
|
||||
if (actor)
|
||||
console.log(`Got an actor: ${actor}`)
|
||||
function contact_fn(actor,reason) {
|
||||
if (actor) {
|
||||
console.log(`Got an actor: ${json.encode(actor)}`)
|
||||
|
||||
$_.send(actor, {greet: "Hello!"})
|
||||
}
|
||||
else
|
||||
console.log(`Did not get an actor: ${reason}`)
|
||||
}, {
|
||||
console.log(`Did not get an actor: ${json.encode(reason)}`)
|
||||
}
|
||||
|
||||
$_.contact(contact_fn,
|
||||
{
|
||||
address: "localhost",
|
||||
port: 5678,
|
||||
password: "abc123"
|
||||
});
|
||||
|
||||
$_.contact(contact_fn, {
|
||||
address: "localhost",
|
||||
port: 5678,
|
||||
password: "123abc"
|
||||
})
|
||||
|
||||
$_.contact(contact_fn, {
|
||||
address: "localhost",
|
||||
port:1111,
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
var os = use('os')
|
||||
|
||||
console.log("START")
|
||||
$_.start(e => {
|
||||
switch(e.type) {
|
||||
case "actor_started":
|
||||
|
||||
@@ -3,14 +3,8 @@
|
||||
var password = "abc123"
|
||||
|
||||
$_.portal(e => {
|
||||
console.log(`got message with password ${e.password}`)
|
||||
console.log(json.encode(e))
|
||||
if (e.password !== password)
|
||||
throw new Error("Password does not match.");
|
||||
|
||||
console.log(`passwords matched.`)
|
||||
|
||||
$_.send(e, {
|
||||
actor: $_
|
||||
});
|
||||
$_.send(e, {reason:"Password does not match."});
|
||||
else
|
||||
$_.send(e, $_)
|
||||
}, 5678);
|
||||
|
||||
@@ -12,8 +12,3 @@ $_.start(e => {
|
||||
console.log('Contact actor finished starting.')
|
||||
}, "tests/contact.js")
|
||||
}, "tests/portal.js")
|
||||
|
||||
$_.delay(_ => {
|
||||
for (var c of children) $_.stop(c)
|
||||
$_.stop()
|
||||
}, 3)
|
||||
|
||||
Reference in New Issue
Block a user