1 Commits

Author SHA1 Message Date
John Alanbrook
216ada5568 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
2025-05-21 15:40:22 -05:00
2 changed files with 375 additions and 193 deletions

View File

@@ -285,12 +285,16 @@ function startServer() {
isMyTurn = true; isMyTurn = true;
updateTitle(); updateTitle();
console.log("Starting server with actor:", json.encode($_));
$_.portal(e => { $_.portal(e => {
console.log("Portal received contact message"); console.log("Portal received contact message:", json.encode(e));
// Reply with this actor to establish connection
console.log (json.encode($_)) // The proper Misty pattern: Portal should only reply with an actor reference
$_.send(e, $_); // Use a clean actor object, not application data
console.log("Portal replied with server actor"); $_.send(e, { id: $_.id }); // Send a clean actor reference
console.log("Portal replied with server actor reference");
}, 5678); }, 5678);
} }
@@ -298,17 +302,36 @@ function joinServer() {
gameState = 'searching'; gameState = 'searching';
updateTitle(); updateTitle();
function contact_fn(actor, reason) { console.log("Client attempting to join server with client actor:", json.encode($_));
console.log("CONTACTED!", 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 function contact_fn(actor, reason) {
console.log("Contact callback received:", actor ? "SUCCESS" : "FAILED", reason);
if (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, { $_.send(opponent, {
type: 'greet', type: 'greet',
client_actor: $_ 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 { } else {
console.log(`Failed to connect: ${json.encode(reason)}`); console.log(`Failed to connect: ${json.encode(reason)}`);
gameState = 'waiting'; gameState = 'waiting';
@@ -316,6 +339,7 @@ function joinServer() {
} }
} }
// Initial contact phase - get actor reference only
$_.contact(contact_fn, { $_.contact(contact_fn, {
address: "localhost", address: "localhost",
port: 5678 port: 5678
@@ -325,11 +349,7 @@ function joinServer() {
var os = use('os') var os = use('os')
// Set up IO actor subscription // Set up IO actor subscription
var ioguy = { var ioguy = { id: os.ioactor() };
__ACTORDATA__: {
id: os.ioactor()
}
};
$_.send(ioguy, { $_.send(ioguy, {
type: "subscribe", type: "subscribe",
@@ -344,7 +364,8 @@ $_.receiver(e => {
if (e.type === 'greet') { if (e.type === 'greet') {
console.log("Server received greet from client"); console.log("Server received greet from client");
// Store the client's actor object for ongoing communication // Store the client's actor object for ongoing communication
opponent = e.client_actor; 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)); console.log("Stored client actor:", json.encode(opponent));
gameState = 'connected'; gameState = 'connected';
updateTitle(); updateTitle();
@@ -356,6 +377,9 @@ $_.receiver(e => {
your_color: 'black' your_color: 'black'
}); });
console.log("game_start message sent to client"); 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') { else if (e.type === 'game_start') {
console.log("Game starting, I am:", e.your_color); console.log("Game starting, I am:", e.your_color);

View File

@@ -574,8 +574,6 @@ actor.delay = function(fn, seconds) {
} }
actor.delay.doc = `Call 'fn' after 'seconds' with 'this' set to the actor.` actor.delay.doc = `Call 'fn' after 'seconds' with 'this' set to the actor.`
var act = use('actor') var act = use('actor')
actor[UNDERLINGS] = new Set() actor[UNDERLINGS] = new Set()
@@ -619,36 +617,10 @@ var nota = use('nota')
var dying = false var dying = false
var HEADER = Symbol() var HEADER = Symbol()
var ACTORDATA = Symbol()
var $actor = { function create_actor(id = util.guid()) {
toString: print_actor return {id}
}
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
} }
var $_ = create_actor() var $_ = create_actor()
@@ -669,7 +641,7 @@ var receive_fn = undefined
var greeters = {} var greeters = {}
$_.is_actor = function(actor) { $_.is_actor = function(actor) {
return actor.__ACTORDATA__ return "id" in actor || ACTORDATA in actor
} }
function peer_connection(peer) { function peer_connection(peer) {
@@ -696,12 +668,12 @@ function peer_connection(peer) {
} }
$_.connection = function(callback, actor, config) { $_.connection = function(callback, actor, config) {
var peer = peers[actor.__ACTORDATA__.id] var peer = peers[actor.id]
if (peer) { if (peer) {
callback(peer_connection(peer)) callback(peer_connection(peer))
return return
} }
if (os.mailbox_exist(actor.__ACTORDATA__.id)) { if (os.mailbox_exist(actor.id)) {
callback({type:"local"}) callback({type:"local"})
return return
} }
@@ -709,15 +681,22 @@ $_.connection = function(callback, actor, config) {
} }
$_.connection[prosperon.DOC] = "takes a callback function, an actor object, and a configuration record..." $_.connection[prosperon.DOC] = "takes a callback function, an actor object, and a configuration record..."
var peers = {} // 1) id → peer (live ENet connection)
var id_address = {} const peer_by_id = Object.create(null)
var peer_queue = new WeakMap() // 2) id → {address,port} (last seen endpoint)
var portal = undefined const id_address = Object.create(null)
var portal_fn = undefined // 3) address:port → peer (fast lookup for incoming events)
var local_address = undefined const peers = Object.create(null)
var local_port = undefined
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) { $_.portal = function(fn, port) {
if (portal) throw new Error(`Already started a portal listening on ${portal.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_address = 'localhost'
local_port = port local_port = port
portal_fn = fn 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..." $_.portal[prosperon.DOC] = "starts a public address that performs introduction services..."
function handle_host(e) { function handle_host(e) {
switch (e.type) { switch (e.type) {
case "connect": case "connect":
console.log(`connected a new peer: ${e.peer.address}:${e.peer.port}`) // Store peer information for future routing
peers[`${e.peer.address}:${e.peer.port}`] = e.peer 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) var queue = peer_queue.get(e.peer)
if (queue) { if (queue && queue.length > 0) {
for (var msg of queue) e.peer.send(nota.encode(msg)) console.log(`Sending ${queue.length} queued messages to newly connected peer`)
console.log(`sent ${json.encode(msg)} out of queue`)
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) peer_queue.delete(e.peer)
} }
break break
case "disconnect": 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) 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 break
case "receive": case "receive":
var data = nota.decode(e.data) // Decode the message
if (data.replycc && !data.replycc.__ACTORDATA__.address) { try {
data.replycc.__ACTORDATA__.address = e.peer.address var msg = nota.decode(e.data)
data.replycc.__ACTORDATA__.port = e.peer.port console.log(`Received message from ${e.peer.address}:${e.peer.port}:`, json.encode(msg))
// Store in global lookup for future routing
id_address[data.replycc.__ACTORDATA__.id] = { // Update routing information based on message contents
address: e.peer.address, function touch(obj){
port: e.peer.port 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}`)
} }
} }
// Also populate address/port for any actor objects in the message data
function populate_actor_addresses(obj) { // Recursively touch all properties
if (typeof obj !== 'object' || obj === null) return for (const k in obj) {
if (obj.__ACTORDATA__ && !obj.__ACTORDATA__.address) { if (obj.hasOwnProperty(k)) touch(obj[k])
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)) { // Extract routing information
populate_actor_addresses(obj[key]) 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 break
} }
} }
var contactor = undefined var contactor = undefined
$_.contact = function(callback, record) { $_.contact = function(callback, record) {
$_.send({ console.log("Contact function called with:", json.encode(record));
__ACTORDATA__: {
address: record.address, // Create a pseudo-actor for the initial contact
port: record.port 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);
} }
}, record, callback) } else {
// Error case or unexpected response format
console.log("Unexpected response format");
callback(null, response);
}
}
// Send the contact message with callback for response
$_.send(sendto, contactMsg, wrappedCallback);
} }
$_.contact[prosperon.DOC] = "The contact function sends a message to a portal..." $_.contact[prosperon.DOC] = "The contact function sends a message to a portal..."
@@ -822,7 +869,7 @@ $_.stop = function(actor) {
} }
if (!$_.is_actor(actor)) if (!$_.is_actor(actor))
throw new Error('Can only call stop on an 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.') throw new Error('Can only call stop on an underling or self.')
actor_prep(actor, {type:"stop", id: prosperon.id}) 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() var couplings = new Set()
$_.couple = function(actor) { $_.couple = function(actor) {
console.log(`coupled to ${actor.__ACTORDATA__.id}`) console.log(`coupled to ${actor.id}`)
couplings.add(actor.__ACTORDATA__.id) couplings.add(actor.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."
@@ -851,41 +898,91 @@ function actor_prep(actor, send) {
message_queue.push({actor,send}); message_queue.push({actor,send});
} }
function actor_send(actor, message) { function ensure_route(actor){
if (!$_.is_actor(actor)) throw new Error(`Must send to an actor object. Attempted send to ${json.encode(actor)}`) // Extract the actor ID
if (typeof message !== 'object') throw new Error('Must send an object record.') const id = actor.id
if (actor.__ACTORDATA__.id === prosperon.id) { // First check if we already have a peer for this actor
if (receive_fn) receive_fn(message.data) if (peer_by_id[id]) {
return console.log(`Found existing peer for actor ${id}`)
} return peer_by_id[id]
if (actor.__ACTORDATA__.id && os.mailbox_exist(actor.__ACTORDATA__.id)) {
os.mailbox_push(actor.__ACTORDATA__.id, message)
return
} }
// Use fallback address lookup if actor doesn't have address info // Check if we have a hint for this actor's address
if (!actor.__ACTORDATA__.address && id_address[actor.__ACTORDATA__.id]) { const hint = id_address[id]
Object.assign(actor.__ACTORDATA__, id_address[actor.__ACTORDATA__.id]) if (hint) {
} console.log(`Found address hint for ${id}: ${hint.address}:${hint.port}`)
if (actor.__ACTORDATA__.address) { // Create host if needed
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) { if (!contactor && !portal) {
console.log(`creating a contactor ...`) console.log("Creating new contactor host")
contactor = enet.create_host() contactor = enet.create_host()
} }
peer = (contactor || portal).connect(actor.__ACTORDATA__.address, actor.__ACTORDATA__.port)
peer_queue.set(peer, [message]) // Connect to the peer
} else { const host = contactor || portal
peer.send(nota.encode(message)) 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 return
} }
throw new Error(`Unable to send message to actor ${json.encode(actor)}`)
if (os.mailbox_exist(actor.id)){ // message to local mailbox
os.mailbox_push(actor.id, send);
return
}
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. // Holds all messages queued during the current turn.
@@ -894,19 +991,28 @@ var message_queue = []
function send_messages() { function send_messages() {
// Attempt to flush the queued messages. If one fails, keep going anyway. // Attempt to flush the queued messages. If one fails, keep going anyway.
var errors = [] var errors = []
// Process all queued messages
while (message_queue.length > 0) { while (message_queue.length > 0) {
var item = message_queue.shift() var {actor,send} = message_queue.shift()
var actor = item.actor
var send = item.send // console.log("Processing queued message:", json.encode(send))
try { try {
actor_send(actor, send) actor_send(actor, send)
// console.log("Message sent successfully")
} catch (err) { } catch (err) {
console.error("Failed to send message:", err.message)
errors.push(err) errors.push(err)
} }
} }
if (errors.length > 0) {
console.error("Some messages failed to send:", errors) // Report any send errors
for (var i of errors) console.error(i) 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) { $_.send = function(actor, message, reply) {
if (typeof message !== 'object') if (typeof message !== 'object')
throw new Error('Message must be an object') throw new Error('Message must be an object')
var send = {type:"user", data: message}
if (actor[HEADER] && actor[HEADER].replycc) { // 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) { // 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] var header = actor[HEADER]
if (!header.replycc || !$_.is_actor(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])}`) throw new Error(`Supplied actor had a return, but it's not a valid actor! ${json.encode(actor[HEADER])}`)
actor = header.replycc actor = header.replycc
send.return = header.reply 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) { if (reply) {
@@ -931,10 +1052,12 @@ $_.send = function(actor, message, reply) {
replies[id] = reply replies[id] = reply
send.reply = id send.reply = id
send.replycc = $_ send.replycc = $_
console.log("Send: added reply callback with id", id)
} }
// Instead of sending immediately, queue it // 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..." $_.send[prosperon.DOC] = "sends a message to another actor..."
@@ -956,9 +1079,10 @@ os.register_actor(prosperon.id, function(msg) {
} }
}, prosperon.args.main) }, 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) if (prosperon.args.root) root = json.decode(prosperon.args.root)
else root = $_ else root = $_
@@ -977,12 +1101,8 @@ actor.spawn(prosperon.args.program)
function destroyself() { function destroyself() {
console.log(`Got the message to destroy self.`) console.log(`Got the message to destroy self.`)
dying = true dying = true
for (var i of underlings) { for (var i of underlings)
var act = { $_.stop(create_actor(id));
__ACTORDATA__: {id: i}
}
$_.stop(act);
}
os.destroy() os.destroy()
} }
@@ -998,52 +1118,90 @@ function handle_actor_disconnect(id) {
} }
function handle_message(msg) { function handle_message(msg) {
// console.log("Handling message:", json.encode(msg));
if (msg.target) { if (msg.target) {
if (msg.target !== prosperon.id) { if (msg.target !== prosperon.id) {
os.mailbox_push(msg.target, msg) console.log(`Forwarding message to ${msg.target}`);
return os.mailbox_push(msg.target, msg);
return;
} }
} }
switch (msg.type) { switch (msg.type) {
case "user": case "user":
var letter = msg.data var letter = msg.data;
delete msg.data delete msg.data;
letter[HEADER] = msg letter[HEADER] = msg;
if (msg.return) { if (msg.return) {
console.log(`Received a message for the return id ${msg.return}`) console.log(`Received a message for the return id ${msg.return}`);
var fn = replies[msg.return] var fn = replies[msg.return];
if (!fn) throw new Error(`Could not find return function for message ${msg.return}`) if (!fn) {
fn(letter) console.error(`Could not find return function for message ${msg.return}`);
delete replies[msg.return] return;
return
} }
if (receive_fn) receive_fn(letter)
break // 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;
case "stop": case "stop":
if (msg.id !== overling.__ACTORDATA__.id) if (overling && msg.id !== overling.id)
throw new Error(`Got a message from an actor ${msg.id} to stop...`) throw new Error(`Got a message from an actor ${msg.id} to stop...`);
destroyself() destroyself();
break break;
case "contact": case "contact":
if (portal_fn) { if (portal_fn) {
var letter2 = msg.data console.log("Portal received contact message");
letter2[HEADER] = msg var letter2 = msg.data;
delete msg.data letter2[HEADER] = msg;
portal_fn(letter2) delete msg.data;
} else throw new Error('Got a contact message, but no portal is established.') console.log("Portal handling contact message:", json.encode(letter2));
break portal_fn(letter2);
case "stopped": } else {
handle_actor_disconnect(msg.id) console.error('Got a contact message, but no portal is established.');
break }
case "greet":
var greeter = greeters[msg.id]
if (greeter) greeter({type: "actor_started", actor: create_actor(msg)})
break; 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": case "ping":
// Keep-alive ping, no action needed // Keep-alive ping, no action needed
break; break;
default: default:
if (receive_fn) receive_fn(msg) // console.log("Default message handler for type:", msg.type);
if (receive_fn) receive_fn(msg);
break; break;
} }
}; };