coupling
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 1m13s
Build and Deploy / build-windows (CLANG64) (push) Failing after 14m3s

This commit is contained in:
2025-03-05 13:03:44 -06:00
parent 23d764c534
commit a8865594ca
5 changed files with 264 additions and 88 deletions

View File

@@ -524,6 +524,9 @@ var util = use('util')
var $_ = {}
var underlings = new Set()
var overling = undefined
var host = enet.create_host()
$_.host = host
@@ -540,11 +543,31 @@ $_.contact = function(callback, record)
}
$_.connection = function(callback, actor, config)
{
}
$_.connection = function(callback, actor, config) {
var peer = actor.peer;
callback({
latency: peer.rtt,
bandwidth: {
incoming: peer.incoming_bandwidth,
outgoing: peer.outgoing_bandwidth
},
activity: {
last_sent: peer.last_send_time,
last_received: peer.last_receive_time,
},
mtu: peer.mtu,
data: {
incoming_total: peer.incoming_data_total,
outgoing_total: peer.outgoing_data_total,
reliable_in_transit: peer.reliable_data_in_transit
},
latency_variance: peer.rtt_variance,
packet_loss: peer.packet_loss,
state: peer.state
});
};
var portal = undefined
$_.portal = function(fn, port)
{
@@ -555,19 +578,21 @@ $_.receiver = function(fn)
receive_fn = fn;
}
var underlings = {}
var greeters = {}
$_.start = function(cb, prg, arg)
{
var guid = util.guid()
underlings[guid] = cb
greeters[guid] = cb
os.createprocess(["./prosperon", "spawn", "--program", prg, "--overling", $_.host.port(), "--guid", guid])
guid2actor.set(guid, {peer:undefined, guid:guid})
}
$_.stop = function(actor)
{
if (!actor)
if (!actor) {
os.exit(0)
}
actor.peer.send({
type:"stop",
@@ -585,17 +610,45 @@ $_.delay = function(fn, seconds)
return function() { os.removetimer(id); }
}
var guid2actor = new Map()
var couplings = new Set()
$_.couple = function(actor)
{
couplings.add(actor)
}
use('cmd')(prosperon.argv)
if (prosperon.overling)
$_.host.connect("localhost", args.overling)
if (prosperon.program)
actor.spawn(prosperon.program)
if (!prosperon.guid) prosperon.guid = util.guid()
var ar = 60 // seconds before reclamation
var unneeded_timer = $_.delay($_.stop, ar)
function handle_receive(e)
{
var data = e.data
switch(data.type) {
case "greet":
if (underlings[data.guid]) underlings[data.guid]({
type: "greet",
data: {peer:e.peer}
})
if (greeters[data.guid]) {
var actor = guid2actor.get(data.guid)
if (!actor) throw new Error(`No registered actor for guid ${data.guid}`)
actor.peer = e.peer
guid2actor.set(e.peer, actor)
greeters[data.guid]({
type: "greet",
data: actor
})
greeters[data.guid] = undefined
}
break
case "stop":
console.log("STOPPING!")
@@ -603,10 +656,25 @@ function handle_receive(e)
}
}
function handle_actor_disconnect(actor)
{
console.log(`actor ${json.encode(actor)} disconnected`)
guid2actor.delete(actor.guid)
guid2actor.delete(actor.peer)
if (couplings.has(actor)) {
console.log(`I was connected to it, so I'm dying`)
$_.stop()
}
}
var hang = 0.016
while (1) {
os.waitevent(_ => {}, hang)
os.waitevent(e => {
unneeded_timer()
unneded_timer = $_.delay($_.stop, ar)
}, hang)
host.service(e => {
unneeded_timer()
switch(e.type) {
case "connect":
console.log(`connected. sending greet with guid ${prosperon.guid} to peer ${e.peer}`)
@@ -621,9 +689,10 @@ while (1) {
break;
case "disconnect":
console.log(`this peer left: ${e.peer}`)
handle_actor_disconnect(guid2actor.get(e.peer))
break
}
unneeded_timer = $_.delay($_.stop, ar)
}, hang);
}

View File

@@ -64,28 +64,14 @@ Cmdline.register_order(
"Make documentation."
})
function spawn_root(script)
{
return actor.spawn(script, {}, function(underling, msg) {
if (msg.message !== "created") return;
Object.defineProperty(underling, 'then', {
configurable:false,
writable:false,
value:function() {
os.exit(0);
}
});
})
}
Cmdline.register_order(
"play",
function (argv) {
var app
if (io.exists("main.js"))
app = spawn_root("main.js")
app = actor.spawn("main.js")
else
app = spawn_root("nogame.js")
app = actor.spawn("nogame.js")
// rm actor so it can't be tampered
globalThis.actor = undefined
@@ -135,28 +121,6 @@ Cmdline.register_order(
"OBJECT ?FILE?",
);
Cmdline.register_order(
"run",
function (script) {
var s = os.now()
script = script.join(" ");
if (!script) {
console.print("Need something to run.");
return;
}
try {
spawn_root(script)
} catch(e) {
console.error(e);
os.exit(1);
}
},
"Run a given script. SCRIPT can be the script itself, or a file containing the script",
"SCRIPT",
);
Cmdline.orders.script = Cmdline.orders.run;
Cmdline.print_order = function (fn) {
if (typeof fn === "string") fn = Cmdline.orders[fn];
@@ -185,17 +149,13 @@ function parse_args(argv)
Cmdline.register_order(
"spawn",
function(argv) {
var args = parse_args(argv)
console.log(json.encode(args));
prosperon.args = parse_args(argv)
if (!args.program)
os.exit()
prosperon.guid = args.guid
console.log(`going to connect to ${args.overling}`)
if (args.overling)
$_.host.connect("localhost", args.overling);
// spawn_root(args.program)
prosperon.guid = prosperon.args.guid
prosperon.overling = prosperon.args.overling
prosperon.program = args.program
},
"Spawn a new prosperon actor.",
"TOPIC"
@@ -236,11 +196,16 @@ Cmdline.register_order(
function cmd_args(cmds) {
cmds.shift()
if (cmds.length === 0) cmds[0] = "play";
if (cmds.length === 0) {
cmds[0] = "spawn";
cmds[1] = "--program"
cmds[2] = "main.js"
}
else if (!Cmdline.orders[cmds[0]]) {
// assume it's a script
cmds[1] = cmds[0]
cmds[0] = "run"
cmds[2] = cmds[0]
cmds[1] = "--program"
cmds[0] = "spawn"
}
Cmdline.orders[cmds[0]](cmds.slice(1));

View File

@@ -5746,17 +5746,15 @@ static const JSCFunctionListEntry js_input_funcs[] = {
};
JSC_CCALL(os_guid,
char guid[33];
for (int i = 0; i < 4; i++) {
int r = rand();
for (int j = 0; j < 8; j++) {
guid[i*8+j] = "0123456789abcdef"[r%16];
r /= 16;
}
}
SDL_GUID guid;
for (int i = 0; i < 16; i++)
guid.data[i] = rand() % 256;
guid[32] = 0;
return JS_NewString(js,guid);
char guid_str[33];
SDL_GUIDToString(guid, guid_str, 33);
return JS_NewString(js,guid_str);
)
JSC_SCALL(os_openurl,

View File

@@ -4,6 +4,8 @@
#include <stdio.h>
#include <string.h>
#include <math.h>
#define countof(a) (sizeof(a)/sizeof(*(a)))
static JSClassID enet_host_id;
@@ -17,10 +19,16 @@ static void js_enet_host_finalizer(JSRuntime *rt, JSValue val) {
}
}
static void js_enet_peer_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
{
ENetPeer *peer = JS_GetOpaque(val, enet_peer_class_id);
JS_MarkValue(rt, *(JSValue*)peer->data, mark_func);
}
static void js_enet_peer_finalizer(JSRuntime *rt, JSValue val) {
ENetPeer *peer = JS_GetOpaque(val, enet_peer_class_id);
// No explicit cleanup needed for ENetPeer itself
(void)peer;
JS_FreeValueRT(rt, *(JSValue*)peer->data);
free(peer->data);
}
/* ENet init/deinit */
@@ -105,6 +113,16 @@ RET:
return obj;
}
static JSValue peer_get_value(JSContext *ctx, ENetPeer *peer)
{
if (!peer->data) {
peer->data = malloc(sizeof(JSValue));
*(JSValue*)peer->data = JS_NewObjectClass(ctx, enet_peer_class_id);
JS_SetOpaque(*(JSValue*)peer->data, peer);
}
return JS_DupValue(ctx, *(JSValue*)peer->data);
}
/* Host service: poll for events */
static JSValue js_enet_host_service(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) {
@@ -127,9 +145,8 @@ static JSValue js_enet_host_service(JSContext *ctx, JSValueConst this_val,
ENetEvent event;
while (enet_host_service(host, &event, secs*1000.0f) > 0) {
JSValue event_obj = JS_NewObject(ctx);
JSValue peer_obj = JS_NewObjectClass(ctx, enet_peer_class_id);
JS_SetOpaque(peer_obj, event.peer);
JS_SetPropertyStr(ctx, event_obj, "peer", peer_obj);
printf("hit from peer with js value %p\n", event.peer->data);
JS_SetPropertyStr(ctx, event_obj, "peer", peer_get_value(ctx, event.peer));
switch (event.type) {
case ENET_EVENT_TYPE_CONNECT: {
@@ -221,12 +238,7 @@ static JSValue js_enet_host_connect(JSContext *ctx, JSValueConst this_val,
return JS_ThrowInternalError(ctx, "Failed to initiate connection.");
}
JSValue peer_obj = JS_NewObjectClass(ctx, enet_peer_class_id);
if (JS_IsException(peer_obj)) {
return peer_obj;
}
JS_SetOpaque(peer_obj, peer);
return peer_obj;
return peer_get_value(ctx, peer);
}
/* Flush queued packets */
@@ -438,6 +450,7 @@ static JSClassDef enet_host = {
static JSClassDef enet_peer_class = {
"ENetPeer",
.finalizer = js_enet_peer_finalizer,
.gc_mark = js_enet_peer_mark
};
/* Function lists */
@@ -456,6 +469,122 @@ static const JSCFunctionListEntry js_enet_host_funcs[] = {
JS_CFUNC_DEF("address", 0, js_enet_host_get_address),
};
/* Getter for roundTripTime (rtt) */
static JSValue js_enet_peer_get_rtt(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) {
return JS_EXCEPTION;
}
return JS_NewInt32(ctx, peer->roundTripTime); // RTT in milliseconds
}
/* Getter for incomingBandwidth */
static JSValue js_enet_peer_get_incoming_bandwidth(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) {
return JS_EXCEPTION;
}
if (peer->incomingBandwidth == 0)
return JS_NewFloat64(ctx, INFINITY);
return JS_NewInt32(ctx, peer->incomingBandwidth); // Bytes per second
}
/* Getter for outgoingBandwidth */
static JSValue js_enet_peer_get_outgoing_bandwidth(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) {
return JS_EXCEPTION;
}
if (peer->outgoingBandwidth == 0)
return JS_NewFloat64(ctx, INFINITY);
return JS_NewInt32(ctx, peer->outgoingBandwidth); // Bytes per second
}
/* Getter for lastSendTime */
static JSValue js_enet_peer_get_last_send_time(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) {
return JS_EXCEPTION;
}
return JS_NewInt32(ctx, peer->lastSendTime); // Timestamp in milliseconds
}
/* Getter for lastReceiveTime */
static JSValue js_enet_peer_get_last_receive_time(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) {
return JS_EXCEPTION;
}
return JS_NewInt32(ctx, peer->lastReceiveTime); // Timestamp in milliseconds
}
#include <math.h> // Ensure this is included for INFINITY
/* Getter for mtu */
static JSValue js_enet_peer_get_mtu(JSContext *ctx, JSValueConst this_val) {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) {
return JS_NewFloat64(ctx, INFINITY);
}
return JS_NewInt32(ctx, peer->mtu);
}
/* Getter for outgoingDataTotal */
static JSValue js_enet_peer_get_outgoing_data_total(JSContext *ctx, JSValueConst this_val) {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) {
return JS_NewFloat64(ctx, INFINITY);
}
return JS_NewInt32(ctx, peer->outgoingDataTotal);
}
/* Getter for incomingDataTotal */
static JSValue js_enet_peer_get_incoming_data_total(JSContext *ctx, JSValueConst this_val) {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) {
return JS_NewFloat64(ctx, INFINITY);
}
return JS_NewInt32(ctx, peer->incomingDataTotal);
}
/* Getter for roundTripTimeVariance */
static JSValue js_enet_peer_get_rtt_variance(JSContext *ctx, JSValueConst this_val) {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) {
return JS_NewFloat64(ctx, INFINITY);
}
return JS_NewInt32(ctx, peer->roundTripTimeVariance);
}
/* Getter for packetLoss */
static JSValue js_enet_peer_get_packet_loss(JSContext *ctx, JSValueConst this_val) {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) {
return JS_NewFloat64(ctx, INFINITY);
}
return JS_NewInt32(ctx, peer->packetLoss); // Scaled by ENET_PEER_PACKET_LOSS_SCALE
}
/* Getter for state */
static JSValue js_enet_peer_get_state(JSContext *ctx, JSValueConst this_val) {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) {
return JS_NewInt32(ctx, -1); // Invalid state
}
return JS_NewInt32(ctx, peer->state); // ENetPeerState enum value
}
/* Getter for reliableDataInTransit */
static JSValue js_enet_peer_get_reliable_data_in_transit(JSContext *ctx, JSValueConst this_val) {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) {
return JS_NewFloat64(ctx, INFINITY);
}
return JS_NewInt32(ctx, peer->reliableDataInTransit);
}
static const JSCFunctionListEntry js_enet_peer_funcs[] = {
JS_CFUNC_DEF("send", 1, js_enet_peer_send),
JS_CFUNC_DEF("disconnect", 0, js_enet_peer_disconnect),
@@ -465,6 +594,18 @@ static const JSCFunctionListEntry js_enet_peer_funcs[] = {
JS_CFUNC_DEF("ping", 0, js_enet_peer_ping),
JS_CFUNC_DEF("throttle_configure",3, js_enet_peer_throttle_configure),
JS_CFUNC_DEF("timeout", 3, js_enet_peer_timeout),
JS_CGETSET_DEF("rtt", js_enet_peer_get_rtt, NULL),
JS_CGETSET_DEF("incoming_bandwidth",js_enet_peer_get_incoming_bandwidth, NULL),
JS_CGETSET_DEF("outgoing_bandwidth",js_enet_peer_get_outgoing_bandwidth, NULL),
JS_CGETSET_DEF("last_send_time", js_enet_peer_get_last_send_time, NULL),
JS_CGETSET_DEF("last_receive_time",js_enet_peer_get_last_receive_time, NULL),
JS_CGETSET_DEF("mtu", js_enet_peer_get_mtu, NULL),
JS_CGETSET_DEF("outgoing_data_total", js_enet_peer_get_outgoing_data_total, NULL),
JS_CGETSET_DEF("incoming_data_total", js_enet_peer_get_incoming_data_total, NULL),
JS_CGETSET_DEF("rtt_variance", js_enet_peer_get_rtt_variance, NULL),
JS_CGETSET_DEF("packet_loss", js_enet_peer_get_packet_loss, NULL),
JS_CGETSET_DEF("state", js_enet_peer_get_state, NULL),
JS_CGETSET_DEF("reliable_data_in_transit", js_enet_peer_get_reliable_data_in_transit, NULL),
};
/* Module entry point */

View File

@@ -1,15 +1,18 @@
var os = use('os')
var guy
$_.start(e => {
console.log("Got a message: " + json.encode(e))
switch(e.type) {
case "greet":
$_.connection(e => console.log(json.encode(e)), e.data)
$_.delay(_ => {
console.log(`sending stop message to ${json.encode(e.data)}`)
$_.stop(e.data)
}, 1);
$_.couple(e.data)
}
console.log(json.encode(e))
}, "tests/underling.js");
$_.contact((actor, reason) => {
}, {
address: "localhost",
});