attempt fix local network
Some checks failed
Build and Deploy / build-macos (push) Failing after 4s
Build and Deploy / build-linux (push) Failing after 1m33s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Some checks failed
Build and Deploy / build-macos (push) Failing after 4s
Build and Deploy / build-linux (push) Failing after 1m33s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
This commit is contained in:
@@ -285,12 +285,16 @@ function startServer() {
|
||||
isMyTurn = true;
|
||||
updateTitle();
|
||||
|
||||
console.log("Starting server with actor:", json.encode($_));
|
||||
|
||||
$_.portal(e => {
|
||||
console.log("Portal received contact message");
|
||||
// Reply with this actor to establish connection
|
||||
console.log (json.encode($_))
|
||||
$_.send(e, $_);
|
||||
console.log("Portal replied with server actor");
|
||||
console.log("Portal received contact message:", json.encode(e));
|
||||
|
||||
// The proper Misty pattern: Portal should only reply with an actor reference
|
||||
// Use a clean actor object, not application data
|
||||
$_.send(e, { id: $_.id }); // Send a clean actor reference
|
||||
|
||||
console.log("Portal replied with server actor reference");
|
||||
}, 5678);
|
||||
}
|
||||
|
||||
@@ -298,17 +302,36 @@ function joinServer() {
|
||||
gameState = 'searching';
|
||||
updateTitle();
|
||||
|
||||
console.log("Client attempting to join server with client actor:", json.encode($_));
|
||||
|
||||
function contact_fn(actor, reason) {
|
||||
console.log("CONTACTED!", actor ? "SUCCESS" : "FAILED", reason);
|
||||
console.log("Contact callback received:", actor ? "SUCCESS" : "FAILED", reason);
|
||||
|
||||
if (actor) {
|
||||
opponent = actor;
|
||||
console.log("Connection established with server, sending join request");
|
||||
|
||||
// Send a greet message with our actor object
|
||||
$_.send(opponent, {
|
||||
type: 'greet',
|
||||
client_actor: $_
|
||||
});
|
||||
// Ensure we have a clean actor reference with just the id
|
||||
if (typeof actor === 'object' && actor.id) {
|
||||
// Store server actor reference for ongoing communication
|
||||
opponent = { id: actor.id }; // Clean actor reference
|
||||
console.log("Connection established with server actor:", json.encode(opponent));
|
||||
|
||||
// Now that we have the server actor reference, send application message
|
||||
// This follows the two-phase Misty pattern:
|
||||
// 1. First establish actor connection (done with contact)
|
||||
// 2. Then send application messages
|
||||
console.log("Sending greet message to server");
|
||||
$_.send(opponent, {
|
||||
type: 'greet',
|
||||
client_actor: { id: $_.id } // Send clean actor reference
|
||||
});
|
||||
|
||||
// Update game state now that we're connected
|
||||
gameState = 'connected';
|
||||
updateTitle();
|
||||
} else {
|
||||
console.log("Received invalid actor reference:", json.encode(actor));
|
||||
gameState = 'waiting';
|
||||
updateTitle();
|
||||
}
|
||||
} else {
|
||||
console.log(`Failed to connect: ${json.encode(reason)}`);
|
||||
gameState = 'waiting';
|
||||
@@ -316,6 +339,7 @@ function joinServer() {
|
||||
}
|
||||
}
|
||||
|
||||
// Initial contact phase - get actor reference only
|
||||
$_.contact(contact_fn, {
|
||||
address: "localhost",
|
||||
port: 5678
|
||||
@@ -325,11 +349,7 @@ function joinServer() {
|
||||
var os = use('os')
|
||||
|
||||
// Set up IO actor subscription
|
||||
var ioguy = {
|
||||
__ACTORDATA__: {
|
||||
id: os.ioactor()
|
||||
}
|
||||
};
|
||||
var ioguy = { id: os.ioactor() };
|
||||
|
||||
$_.send(ioguy, {
|
||||
type: "subscribe",
|
||||
@@ -344,18 +364,22 @@ $_.receiver(e => {
|
||||
if (e.type === 'greet') {
|
||||
console.log("Server received greet from client");
|
||||
// Store the client's actor object for ongoing communication
|
||||
opponent = e.client_actor;
|
||||
console.log("Stored client actor:", json.encode(opponent));
|
||||
gameState = 'connected';
|
||||
updateTitle();
|
||||
|
||||
// Send game_start to the client
|
||||
console.log("Sending game_start to client");
|
||||
$_.send(opponent, {
|
||||
type: 'game_start',
|
||||
your_color: 'black'
|
||||
});
|
||||
console.log("game_start message sent to client");
|
||||
if (e.client_actor && e.client_actor.id) {
|
||||
opponent = { id: e.client_actor.id }; // Clean actor reference
|
||||
console.log("Stored client actor:", json.encode(opponent));
|
||||
gameState = 'connected';
|
||||
updateTitle();
|
||||
|
||||
// Send game_start to the client
|
||||
console.log("Sending game_start to client");
|
||||
$_.send(opponent, {
|
||||
type: 'game_start',
|
||||
your_color: 'black'
|
||||
});
|
||||
console.log("game_start message sent to client");
|
||||
} else {
|
||||
console.log("Invalid client actor in greet message:", json.encode(e));
|
||||
}
|
||||
}
|
||||
else if (e.type === 'game_start') {
|
||||
console.log("Game starting, I am:", e.your_color);
|
||||
|
||||
@@ -574,8 +574,6 @@ actor.delay = function(fn, seconds) {
|
||||
}
|
||||
actor.delay.doc = `Call 'fn' after 'seconds' with 'this' set to the actor.`
|
||||
|
||||
|
||||
|
||||
var act = use('actor')
|
||||
actor[UNDERLINGS] = new Set()
|
||||
|
||||
@@ -619,36 +617,10 @@ var nota = use('nota')
|
||||
var dying = false
|
||||
|
||||
var HEADER = Symbol()
|
||||
var ACTORDATA = Symbol()
|
||||
|
||||
var $actor = {
|
||||
toString: print_actor
|
||||
}
|
||||
|
||||
function print_actor() {
|
||||
return json.encode(this.__ACTORDATA__, 1)
|
||||
}
|
||||
|
||||
function create_actor(data = {}) {
|
||||
var newactor = Object.create($actor)
|
||||
|
||||
// Store actual address/port values on the data object
|
||||
data._address = data._address || local_address
|
||||
data._port = data._port || local_port
|
||||
|
||||
Object.defineProperty(data, 'address', {
|
||||
get: function() { return this._address || local_address },
|
||||
set: function(x) { this._address = x },
|
||||
enumerable: true
|
||||
})
|
||||
|
||||
Object.defineProperty(data, 'port', {
|
||||
get: function() { return this._port || local_port },
|
||||
set: function(x) { this._port = x },
|
||||
enumerable: true
|
||||
})
|
||||
|
||||
newactor.__ACTORDATA__ = data
|
||||
return newactor
|
||||
function create_actor(id = util.guid()) {
|
||||
return {id}
|
||||
}
|
||||
|
||||
var $_ = create_actor()
|
||||
@@ -669,7 +641,7 @@ var receive_fn = undefined
|
||||
var greeters = {}
|
||||
|
||||
$_.is_actor = function(actor) {
|
||||
return actor.__ACTORDATA__
|
||||
return "id" in actor || ACTORDATA in actor
|
||||
}
|
||||
|
||||
function peer_connection(peer) {
|
||||
@@ -696,12 +668,12 @@ function peer_connection(peer) {
|
||||
}
|
||||
|
||||
$_.connection = function(callback, actor, config) {
|
||||
var peer = peers[actor.__ACTORDATA__.id]
|
||||
var peer = peers[actor.id]
|
||||
if (peer) {
|
||||
callback(peer_connection(peer))
|
||||
return
|
||||
}
|
||||
if (os.mailbox_exist(actor.__ACTORDATA__.id)) {
|
||||
if (os.mailbox_exist(actor.id)) {
|
||||
callback({type:"local"})
|
||||
return
|
||||
}
|
||||
@@ -709,15 +681,22 @@ $_.connection = function(callback, actor, config) {
|
||||
}
|
||||
$_.connection[prosperon.DOC] = "takes a callback function, an actor object, and a configuration record..."
|
||||
|
||||
var peers = {}
|
||||
var id_address = {}
|
||||
var peer_queue = new WeakMap()
|
||||
var portal = undefined
|
||||
var portal_fn = undefined
|
||||
var local_address = undefined
|
||||
var local_port = undefined
|
||||
// 1) id → peer (live ENet connection)
|
||||
const peer_by_id = Object.create(null)
|
||||
// 2) id → {address,port} (last seen endpoint)
|
||||
const id_address = Object.create(null)
|
||||
// 3) address:port → peer (fast lookup for incoming events)
|
||||
const peers = Object.create(null)
|
||||
|
||||
var service_delay = 0.01
|
||||
var peer_queue = new WeakMap()
|
||||
var portal, portal_fn
|
||||
var local_address, local_port
|
||||
|
||||
var service_delay = 0.01 // how often to ping enet
|
||||
|
||||
function route_set(id, peer){ peer_by_id[id] = peer }
|
||||
function route_hint(id, address, port){ id_address[id] = {address,port} }
|
||||
function route_peerString(peer){ return `${peer.address}:${peer.port}` }
|
||||
|
||||
$_.portal = function(fn, port) {
|
||||
if (portal) throw new Error(`Already started a portal listening on ${portal.port}`)
|
||||
@@ -727,70 +706,138 @@ $_.portal = function(fn, port) {
|
||||
local_address = 'localhost'
|
||||
local_port = port
|
||||
portal_fn = fn
|
||||
console.log(`I am now ${$_}`)
|
||||
console.log(`Portal initialized with actor ID: ${$_.id}`)
|
||||
}
|
||||
$_.portal[prosperon.DOC] = "starts a public address that performs introduction services..."
|
||||
|
||||
function handle_host(e) {
|
||||
switch (e.type) {
|
||||
case "connect":
|
||||
console.log(`connected a new peer: ${e.peer.address}:${e.peer.port}`)
|
||||
peers[`${e.peer.address}:${e.peer.port}`] = e.peer
|
||||
// Store peer information for future routing
|
||||
var key = route_peerString(e.peer)
|
||||
peers[key] = e.peer
|
||||
console.log(`Connected to peer: ${e.peer.address}:${e.peer.port}`)
|
||||
|
||||
// Check if we have queued messages for this peer
|
||||
var queue = peer_queue.get(e.peer)
|
||||
if (queue) {
|
||||
for (var msg of queue) e.peer.send(nota.encode(msg))
|
||||
console.log(`sent ${json.encode(msg)} out of queue`)
|
||||
if (queue && queue.length > 0) {
|
||||
console.log(`Sending ${queue.length} queued messages to newly connected peer`)
|
||||
|
||||
for (var msg of queue) {
|
||||
try {
|
||||
e.peer.send(nota.encode(msg))
|
||||
console.log(`Sent queued message: ${json.encode(msg)}`)
|
||||
} catch (err) {
|
||||
console.error(`Failed to send queued message: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the queue after sending
|
||||
peer_queue.delete(e.peer)
|
||||
}
|
||||
break
|
||||
|
||||
case "disconnect":
|
||||
// Clean up peer references
|
||||
var key = route_peerString(e.peer)
|
||||
console.log(`Peer disconnected: ${key}`)
|
||||
|
||||
// Remove from peers map
|
||||
delete peers[key]
|
||||
|
||||
// Remove from queue
|
||||
peer_queue.delete(e.peer)
|
||||
for (var id in peers) if (peers[id] === e.peer) delete peers[id]
|
||||
console.log('portal got disconnect')
|
||||
|
||||
// Remove from any ID-based routing
|
||||
for (var id in peer_by_id) {
|
||||
if (peer_by_id[id] === e.peer) {
|
||||
console.log(`Removing route for actor ${id}`)
|
||||
delete peer_by_id[id]
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case "receive":
|
||||
var data = nota.decode(e.data)
|
||||
if (data.replycc && !data.replycc.__ACTORDATA__.address) {
|
||||
data.replycc.__ACTORDATA__.address = e.peer.address
|
||||
data.replycc.__ACTORDATA__.port = e.peer.port
|
||||
// Store in global lookup for future routing
|
||||
id_address[data.replycc.__ACTORDATA__.id] = {
|
||||
address: e.peer.address,
|
||||
port: e.peer.port
|
||||
}
|
||||
}
|
||||
// Also populate address/port for any actor objects in the message data
|
||||
function populate_actor_addresses(obj) {
|
||||
if (typeof obj !== 'object' || obj === null) return
|
||||
if (obj.__ACTORDATA__ && !obj.__ACTORDATA__.address) {
|
||||
obj.__ACTORDATA__.address = e.peer.address
|
||||
obj.__ACTORDATA__.port = e.peer.port
|
||||
// Store in global lookup for future routing
|
||||
id_address[obj.__ACTORDATA__.id] = {
|
||||
address: e.peer.address,
|
||||
port: e.peer.port
|
||||
}
|
||||
}
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
populate_actor_addresses(obj[key])
|
||||
// Decode the message
|
||||
try {
|
||||
var msg = nota.decode(e.data)
|
||||
console.log(`Received message from ${e.peer.address}:${e.peer.port}:`, json.encode(msg))
|
||||
|
||||
// Update routing information based on message contents
|
||||
function touch(obj){
|
||||
if (!obj || typeof obj !== 'object') return
|
||||
|
||||
if (typeof obj.id === 'string'){
|
||||
// Set up routing for this actor ID
|
||||
route_set(obj.id, e.peer)
|
||||
|
||||
// Add address hint if we don't have one
|
||||
if (!id_address[obj.id]) {
|
||||
route_hint(obj.id, e.peer.address, e.peer.port)
|
||||
console.log(`Added route hint for ${obj.id}: ${e.peer.address}:${e.peer.port}`)
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively touch all properties
|
||||
for (const k in obj) {
|
||||
if (obj.hasOwnProperty(k)) touch(obj[k])
|
||||
}
|
||||
}
|
||||
|
||||
// Extract routing information
|
||||
touch(msg)
|
||||
|
||||
// Process the message
|
||||
handle_message(msg)
|
||||
} catch (err) {
|
||||
console.error(`Failed to decode or process received message: ${err.message}`)
|
||||
}
|
||||
if (data.data) populate_actor_addresses(data.data)
|
||||
handle_message(data)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var contactor = undefined
|
||||
$_.contact = function(callback, record) {
|
||||
$_.send({
|
||||
__ACTORDATA__: {
|
||||
address: record.address,
|
||||
port: record.port
|
||||
console.log("Contact function called with:", json.encode(record));
|
||||
|
||||
// Create a pseudo-actor for the initial contact
|
||||
var sendto = {}
|
||||
sendto[ACTORDATA] = record
|
||||
|
||||
// Ensure we send a properly formatted contact message
|
||||
var contactMsg = {
|
||||
type: "contact",
|
||||
data: record
|
||||
};
|
||||
|
||||
// Wrap the original callback to handle the actor reference properly
|
||||
function wrappedCallback(response) {
|
||||
console.log("Contact wrapped callback received:", json.encode(response));
|
||||
|
||||
// First param should be the actor, second param is reason/error
|
||||
if (response && typeof response === 'object') {
|
||||
if ('id' in response) {
|
||||
// This is an actor reference, pass it directly
|
||||
console.log("Identified actor reference in response");
|
||||
callback(response, null);
|
||||
} else if (response.data && typeof response.data === 'object' && 'id' in response.data) {
|
||||
// The actor reference is in the data field
|
||||
console.log("Identified actor reference in response.data");
|
||||
callback(response.data, null);
|
||||
} else {
|
||||
// No actor reference found, must be an error
|
||||
console.log("No actor reference found in response");
|
||||
callback(null, response);
|
||||
}
|
||||
} else {
|
||||
// Error case or unexpected response format
|
||||
console.log("Unexpected response format");
|
||||
callback(null, response);
|
||||
}
|
||||
}, record, callback)
|
||||
}
|
||||
|
||||
// Send the contact message with callback for response
|
||||
$_.send(sendto, contactMsg, wrappedCallback);
|
||||
}
|
||||
|
||||
$_.contact[prosperon.DOC] = "The contact function sends a message to a portal..."
|
||||
@@ -822,7 +869,7 @@ $_.stop = function(actor) {
|
||||
}
|
||||
if (!$_.is_actor(actor))
|
||||
throw new Error('Can only call stop on an actor.')
|
||||
if (!underlings.has(actor.__ACTORDATA__.id))
|
||||
if (!underlings.has(actor.id))
|
||||
throw new Error('Can only call stop on an underling or self.')
|
||||
|
||||
actor_prep(actor, {type:"stop", id: prosperon.id})
|
||||
@@ -842,8 +889,8 @@ $_.delay[prosperon.DOC] = "used to schedule the invocation of a function..."
|
||||
|
||||
var couplings = new Set()
|
||||
$_.couple = function(actor) {
|
||||
console.log(`coupled to ${actor.__ACTORDATA__.id}`)
|
||||
couplings.add(actor.__ACTORDATA__.id)
|
||||
console.log(`coupled to ${actor.id}`)
|
||||
couplings.add(actor.id)
|
||||
}
|
||||
$_.couple[prosperon.DOC] = "causes this actor to stop when another actor stops."
|
||||
|
||||
@@ -851,41 +898,91 @@ function actor_prep(actor, send) {
|
||||
message_queue.push({actor,send});
|
||||
}
|
||||
|
||||
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 (actor.__ACTORDATA__.id === prosperon.id) {
|
||||
if (receive_fn) receive_fn(message.data)
|
||||
return
|
||||
}
|
||||
if (actor.__ACTORDATA__.id && os.mailbox_exist(actor.__ACTORDATA__.id)) {
|
||||
os.mailbox_push(actor.__ACTORDATA__.id, message)
|
||||
return
|
||||
function ensure_route(actor){
|
||||
// Extract the actor ID
|
||||
const id = actor.id
|
||||
|
||||
// First check if we already have a peer for this actor
|
||||
if (peer_by_id[id]) {
|
||||
console.log(`Found existing peer for actor ${id}`)
|
||||
return peer_by_id[id]
|
||||
}
|
||||
|
||||
// Use fallback address lookup if actor doesn't have address info
|
||||
if (!actor.__ACTORDATA__.address && id_address[actor.__ACTORDATA__.id]) {
|
||||
Object.assign(actor.__ACTORDATA__, id_address[actor.__ACTORDATA__.id])
|
||||
}
|
||||
|
||||
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(nota.encode(message))
|
||||
// Check if we have a hint for this actor's address
|
||||
const hint = id_address[id]
|
||||
if (hint) {
|
||||
console.log(`Found address hint for ${id}: ${hint.address}:${hint.port}`)
|
||||
|
||||
// Create host if needed
|
||||
if (!contactor && !portal) {
|
||||
console.log("Creating new contactor host")
|
||||
contactor = enet.create_host()
|
||||
}
|
||||
|
||||
// Connect to the peer
|
||||
const host = contactor || portal
|
||||
console.log(`Connecting to ${hint.address}:${hint.port} via ${contactor ? 'contactor' : 'portal'}`)
|
||||
const p = host.connect(hint.address, hint.port)
|
||||
|
||||
// Initialize queue for this peer
|
||||
peer_queue.set(p, [])
|
||||
return p
|
||||
}
|
||||
|
||||
// Check if we have connection data for this actor
|
||||
var cnn = actor[ACTORDATA]
|
||||
if (cnn) {
|
||||
console.log(`Using ACTORDATA for connection: ${cnn.address}:${cnn.port}`)
|
||||
|
||||
// Create host if needed
|
||||
if (!contactor && !portal) {
|
||||
console.log("Creating new contactor host")
|
||||
contactor = enet.create_host()
|
||||
}
|
||||
|
||||
// Connect to the peer
|
||||
const host = contactor || portal
|
||||
console.log(`Connecting to ${cnn.address}:${cnn.port} via ${contactor ? 'contactor' : 'portal'}`)
|
||||
var p = host.connect(cnn.address, cnn.port)
|
||||
|
||||
// Initialize queue for this peer
|
||||
peer_queue.set(p, [])
|
||||
return p
|
||||
}
|
||||
|
||||
console.log(`No route found for actor ${id}`)
|
||||
return null
|
||||
}
|
||||
|
||||
function actor_send(actor, send){
|
||||
if (!$_.is_actor(actor)) throw Error('bad actor: ' + json.encode(actor))
|
||||
if (actor.id===prosperon.id) { // message to self
|
||||
console.log("actor_send: message to self")
|
||||
if(receive_fn)
|
||||
receive_fn(send.data);
|
||||
return
|
||||
}
|
||||
|
||||
if (os.mailbox_exist(actor.id)){ // message to local mailbox
|
||||
os.mailbox_push(actor.id, send);
|
||||
return
|
||||
}
|
||||
throw new Error(`Unable to send message to actor ${json.encode(actor)}`)
|
||||
|
||||
const peer = ensure_route(actor)
|
||||
if (peer){
|
||||
console.log("actor_send: sending via peer route", peer.address + ":" + peer.port)
|
||||
peer_queue.get(peer)?.push(send) || peer.send(nota.encode(send))
|
||||
return
|
||||
}
|
||||
|
||||
// fallback – forward via parent header if present
|
||||
if (actor[HEADER] && actor[HEADER].replycc){
|
||||
console.log("actor_send: forwarding via parent header")
|
||||
const fwd = {type:'forward', forward_to:actor.id, payload:send}
|
||||
actor_send(actor[HEADER].replycc, fwd); return
|
||||
}
|
||||
console.error("actor_send: no route to actor", actor.id)
|
||||
throw Error(`no route to actor ${actor.id}`)
|
||||
}
|
||||
|
||||
// Holds all messages queued during the current turn.
|
||||
@@ -894,19 +991,28 @@ var message_queue = []
|
||||
function send_messages() {
|
||||
// Attempt to flush the queued messages. If one fails, keep going anyway.
|
||||
var errors = []
|
||||
|
||||
// Process all queued messages
|
||||
while (message_queue.length > 0) {
|
||||
var item = message_queue.shift()
|
||||
var actor = item.actor
|
||||
var send = item.send
|
||||
var {actor,send} = message_queue.shift()
|
||||
|
||||
// console.log("Processing queued message:", json.encode(send))
|
||||
|
||||
try {
|
||||
actor_send(actor, send)
|
||||
// console.log("Message sent successfully")
|
||||
} catch (err) {
|
||||
console.error("Failed to send message:", err.message)
|
||||
errors.push(err)
|
||||
}
|
||||
}
|
||||
if (errors.length > 0) {
|
||||
console.error("Some messages failed to send:", errors)
|
||||
for (var i of errors) console.error(i)
|
||||
|
||||
// Report any send errors
|
||||
if (errors.length) {
|
||||
console.error(`${errors.length} messages failed to send`)
|
||||
for (var i = 0; i < Math.min(errors.length, 3); i++) {
|
||||
console.error(`Error ${i+1}: ${errors[i].message}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -915,15 +1021,30 @@ var replies = {}
|
||||
$_.send = function(actor, message, reply) {
|
||||
if (typeof message !== 'object')
|
||||
throw new Error('Message must be an object')
|
||||
var send = {type:"user", data: message}
|
||||
|
||||
// If message already has type, respect it, otherwise wrap it as user message
|
||||
var send = message.type ? message : {type:"user", data: message}
|
||||
|
||||
// Make sure contact messages have 'data' property
|
||||
if (message.type === 'contact' && !send.data) {
|
||||
console.log("Fixing contact message structure")
|
||||
send.data = message.data || {}
|
||||
}
|
||||
|
||||
if (actor[HEADER] && actor[HEADER].replycc) {
|
||||
if (actor[HEADER] && actor[HEADER].replycc) { // in this case, it's not a true actor, but a message we're responding to
|
||||
console.log("Send: responding to message with header", json.encode(actor[HEADER]))
|
||||
var header = actor[HEADER]
|
||||
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])}`)
|
||||
|
||||
actor = header.replycc
|
||||
send.return = header.reply
|
||||
|
||||
// When replying to a contact message, ensure proper data structure
|
||||
if (header.type === "contact" && send.type === "user") {
|
||||
// Make sure we're sending the actor ID in a proper format for contact responses
|
||||
send.data = send.data || $_;
|
||||
}
|
||||
}
|
||||
|
||||
if (reply) {
|
||||
@@ -931,10 +1052,12 @@ $_.send = function(actor, message, reply) {
|
||||
replies[id] = reply
|
||||
send.reply = id
|
||||
send.replycc = $_
|
||||
console.log("Send: added reply callback with id", id)
|
||||
}
|
||||
|
||||
// Instead of sending immediately, queue it
|
||||
actor_prep(actor,send);
|
||||
// console.log("Send: queuing message", json.encode(send))
|
||||
actor_prep(actor, send);
|
||||
}
|
||||
$_.send[prosperon.DOC] = "sends a message to another actor..."
|
||||
|
||||
@@ -956,9 +1079,10 @@ os.register_actor(prosperon.id, function(msg) {
|
||||
}
|
||||
}, prosperon.args.main)
|
||||
|
||||
$_.__ACTORDATA__.id = prosperon.id
|
||||
$_.id = prosperon.id
|
||||
|
||||
if (prosperon.args.overling) overling = create_actor(prosperon.args.overling)
|
||||
|
||||
if (prosperon.args.overling) overling = {__ACTORDATA__: {id: prosperon.args.overling}}
|
||||
if (prosperon.args.root) root = json.decode(prosperon.args.root)
|
||||
else root = $_
|
||||
|
||||
@@ -977,12 +1101,8 @@ actor.spawn(prosperon.args.program)
|
||||
function destroyself() {
|
||||
console.log(`Got the message to destroy self.`)
|
||||
dying = true
|
||||
for (var i of underlings) {
|
||||
var act = {
|
||||
__ACTORDATA__: {id: i}
|
||||
}
|
||||
$_.stop(act);
|
||||
}
|
||||
for (var i of underlings)
|
||||
$_.stop(create_actor(id));
|
||||
os.destroy()
|
||||
}
|
||||
|
||||
@@ -998,52 +1118,90 @@ function handle_actor_disconnect(id) {
|
||||
}
|
||||
|
||||
function handle_message(msg) {
|
||||
// console.log("Handling message:", json.encode(msg));
|
||||
|
||||
if (msg.target) {
|
||||
if (msg.target !== prosperon.id) {
|
||||
os.mailbox_push(msg.target, msg)
|
||||
return
|
||||
console.log(`Forwarding message to ${msg.target}`);
|
||||
os.mailbox_push(msg.target, msg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (msg.type) {
|
||||
case "user":
|
||||
var letter = msg.data
|
||||
delete msg.data
|
||||
letter[HEADER] = msg
|
||||
var letter = msg.data;
|
||||
delete msg.data;
|
||||
letter[HEADER] = msg;
|
||||
|
||||
if (msg.return) {
|
||||
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(letter)
|
||||
delete replies[msg.return]
|
||||
return
|
||||
console.log(`Received a message for the return id ${msg.return}`);
|
||||
var fn = replies[msg.return];
|
||||
if (!fn) {
|
||||
console.error(`Could not find return function for message ${msg.return}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle contact response specially - first parameter is the actor reference
|
||||
if (letter && letter[HEADER] && letter[HEADER].type === "user" && letter[HEADER].return) {
|
||||
// For contact responses, we need to extract the actor reference
|
||||
// letter.data should contain the actor object from the portal
|
||||
if (letter.data && typeof letter.data === 'object' && letter.data.id) {
|
||||
console.log("Processing contact response with actor data:", json.encode(letter.data));
|
||||
fn(letter.data); // This is the actor reference
|
||||
} else {
|
||||
console.log("Processing contact response with letter as actor:", json.encode(letter));
|
||||
fn(letter); // Fallback to the whole message
|
||||
}
|
||||
} else {
|
||||
fn(letter);
|
||||
}
|
||||
|
||||
delete replies[msg.return];
|
||||
return;
|
||||
}
|
||||
if (receive_fn) receive_fn(letter)
|
||||
break
|
||||
|
||||
if (receive_fn) receive_fn(letter);
|
||||
break;
|
||||
|
||||
case "stop":
|
||||
if (msg.id !== overling.__ACTORDATA__.id)
|
||||
throw new Error(`Got a message from an actor ${msg.id} to stop...`)
|
||||
destroyself()
|
||||
break
|
||||
if (overling && msg.id !== overling.id)
|
||||
throw new Error(`Got a message from an actor ${msg.id} to stop...`);
|
||||
destroyself();
|
||||
break;
|
||||
|
||||
case "contact":
|
||||
if (portal_fn) {
|
||||
var letter2 = msg.data
|
||||
letter2[HEADER] = msg
|
||||
delete msg.data
|
||||
portal_fn(letter2)
|
||||
} else throw new Error('Got a contact message, but no portal is established.')
|
||||
break
|
||||
case "stopped":
|
||||
handle_actor_disconnect(msg.id)
|
||||
break
|
||||
case "greet":
|
||||
var greeter = greeters[msg.id]
|
||||
if (greeter) greeter({type: "actor_started", actor: create_actor(msg)})
|
||||
console.log("Portal received contact message");
|
||||
var letter2 = msg.data;
|
||||
letter2[HEADER] = msg;
|
||||
delete msg.data;
|
||||
console.log("Portal handling contact message:", json.encode(letter2));
|
||||
portal_fn(letter2);
|
||||
} else {
|
||||
console.error('Got a contact message, but no portal is established.');
|
||||
}
|
||||
break;
|
||||
|
||||
case "stopped":
|
||||
handle_actor_disconnect(msg.id);
|
||||
break;
|
||||
|
||||
case "greet":
|
||||
var greeter = greeters[msg.id];
|
||||
if (greeter) {
|
||||
console.log("Greeting actor with id:", msg.id);
|
||||
greeter({type: "actor_started", actor: create_actor(msg)});
|
||||
}
|
||||
break;
|
||||
|
||||
case "ping":
|
||||
// Keep-alive ping, no action needed
|
||||
break;
|
||||
|
||||
default:
|
||||
if (receive_fn) receive_fn(msg)
|
||||
// console.log("Default message handler for type:", msg.type);
|
||||
if (receive_fn) receive_fn(msg);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user