Merge branch 'fix_missing_stop'

This commit is contained in:
2026-02-23 18:09:41 -06:00
5 changed files with 130 additions and 158 deletions

View File

@@ -14,18 +14,14 @@ static void js_enet_host_finalizer(JSRuntime *rt, JSValue val)
if (host) enet_host_destroy(host); if (host) enet_host_destroy(host);
} }
//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) static void js_enet_peer_finalizer(JSRuntime *rt, JSValue val)
{ {
ENetPeer *peer = JS_GetOpaque(val, enet_peer_class_id); ENetPeer *peer = JS_GetOpaque(val, enet_peer_class_id);
if (peer && peer->data) {
JS_FreeValueRT(rt, *(JSValue*)peer->data); JS_FreeValueRT(rt, *(JSValue*)peer->data);
free(peer->data); free(peer->data);
} }
}
// Initialize the ENet library. Must be called before using any ENet functionality. // Initialize the ENet library. Must be called before using any ENet functionality.
static JSValue js_enet_initialize(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) static JSValue js_enet_initialize(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
@@ -34,8 +30,7 @@ static JSValue js_enet_initialize(JSContext *ctx, JSValueConst this_val, int arg
return JS_NULL; return JS_NULL;
} }
// Deinitialize the ENet library, cleaning up all resources. Call this when you no longer // Deinitialize the ENet library, cleaning up all resources.
// need any ENet functionality.
static JSValue js_enet_deinitialize(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) static JSValue js_enet_deinitialize(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{ {
enet_deinitialize(); enet_deinitialize();
@@ -43,14 +38,7 @@ static JSValue js_enet_deinitialize(JSContext *ctx, JSValueConst this_val, int a
} }
// Create an ENet host for either a client-like unbound host or a server bound to a specific // Create an ENet host for either a client-like unbound host or a server bound to a specific
// address and port: // address and port.
//
// - If no argument is provided, creates an unbound "client-like" host with default settings
// (maximum 32 peers, 2 channels, unlimited bandwidth).
// - If you pass an "ip:port" string (e.g. "127.0.0.1:7777"), it creates a server bound to
// that address. The server supports up to 32 peers, 2 channels, and unlimited bandwidth.
//
// Throws an error if host creation fails for any reason.
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; ENetHost *host;
@@ -132,68 +120,75 @@ static JSValue peer_get_value(JSContext *ctx, ENetPeer *peer)
return JS_DupValue(ctx, *(JSValue*)peer->data); return JS_DupValue(ctx, *(JSValue*)peer->data);
} }
// Poll for and process any available network events (connect, receive, disconnect, or none) // Poll for and process any available network events from this host,
// from this host, calling the provided callback for each event. This function loops until // calling the provided callback for each event.
// no more events are available in the current timeframe.
//
// :param callback: A function called once for each available event, receiving an event
// object as its single argument.
// :param timeout: (optional) Timeout in milliseconds. Defaults to 0 (non-blocking).
// :return: None
static JSValue js_enet_host_service(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) static JSValue js_enet_host_service(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{ {
ENetHost *host = JS_GetOpaque(this_val, enet_host_id); ENetHost *host = JS_GetOpaque(this_val, enet_host_id);
if (!host) return JS_EXCEPTION; if (!host) return JS_EXCEPTION;
if (argc < 1 || !JS_IsFunction(argv[0])) return JS_RaiseDisrupt(ctx, "Expected a callback function as first argument"); if (argc < 1 || !JS_IsFunction(argv[0]))
return JS_RaiseDisrupt(ctx, "Expected a callback function as first argument");
double secs; enet_uint32 timeout_ms = 0;
if (argc >= 2 && !JS_IsNull(argv[1])) {
double secs = 0;
JS_ToFloat64(ctx, &secs, argv[1]); JS_ToFloat64(ctx, &secs, argv[1]);
if (secs > 0) timeout_ms = (enet_uint32)(secs * 1000.0);
}
JS_FRAME(ctx);
JSGCRef event_ref = { .val = JS_NULL, .prev = NULL };
JS_PushGCRef(ctx, &event_ref);
ENetEvent event; ENetEvent event;
while (enet_host_service(host, &event, secs*1000.0f) > 0) { while (enet_host_service(host, &event, timeout_ms) > 0) {
JSValue event_obj = JS_NewObject(ctx); event_ref.val = JS_NewObject(ctx);
JS_SetPropertyStr(ctx, event_obj, "peer", peer_get_value(ctx, event.peer));
JSValue peer_val = peer_get_value(ctx, event.peer);
JS_SetPropertyStr(ctx, event_ref.val, "peer", peer_val);
switch (event.type) { switch (event.type) {
case ENET_EVENT_TYPE_CONNECT: case ENET_EVENT_TYPE_CONNECT: {
JS_SetPropertyStr(ctx, event_obj, "type", JS_NewString(ctx, "connect")); JSValue type_str = JS_NewString(ctx, "connect");
JS_SetPropertyStr(ctx, event_ref.val, "type", type_str);
break; break;
case ENET_EVENT_TYPE_RECEIVE: }
JS_SetPropertyStr(ctx, event_obj, "type", JS_NewString(ctx, "receive")); case ENET_EVENT_TYPE_RECEIVE: {
JS_SetPropertyStr(ctx, event_obj, "channelID", JS_NewInt32(ctx, event.channelID)); JSValue type_str = JS_NewString(ctx, "receive");
JS_SetPropertyStr(ctx, event_ref.val, "type", type_str);
// Pass raw data as string or ArrayBuffer JS_SetPropertyStr(ctx, event_ref.val, "channelID", JS_NewInt32(ctx, event.channelID));
if (event.packet->dataLength > 0) { if (event.packet->dataLength > 0) {
JSValue data_val = js_new_blob_stoned_copy(ctx, event.packet->data, event.packet->dataLength); JSValue data_val = js_new_blob_stoned_copy(ctx, event.packet->data, event.packet->dataLength);
JS_SetPropertyStr(ctx, event_obj, "data", data_val); JS_SetPropertyStr(ctx, event_ref.val, "data", data_val);
} }
enet_packet_destroy(event.packet); enet_packet_destroy(event.packet);
break; break;
case ENET_EVENT_TYPE_DISCONNECT: }
JS_SetPropertyStr(ctx, event_obj, "type", JS_NewString(ctx, "disconnect")); case ENET_EVENT_TYPE_DISCONNECT: {
break; JSValue type_str = JS_NewString(ctx, "disconnect");
case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: JS_SetPropertyStr(ctx, event_ref.val, "type", type_str);
JS_SetPropertyStr(ctx, event_obj, "type", JS_NewString(ctx, "disconnect_timeout")); break;
break; }
case ENET_EVENT_TYPE_NONE: case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: {
JS_SetPropertyStr(ctx, event_obj, "type", JS_NewString(ctx, "none")); JSValue type_str = JS_NewString(ctx, "disconnect_timeout");
JS_SetPropertyStr(ctx, event_ref.val, "type", type_str);
break;
}
case ENET_EVENT_TYPE_NONE: {
JSValue type_str = JS_NewString(ctx, "none");
JS_SetPropertyStr(ctx, event_ref.val, "type", type_str);
break; break;
} }
// TODO: raise exception?
JS_FreeValue(ctx, event_obj);
} }
return JS_NULL; JS_FreeValue(ctx, JS_Call(ctx, argv[0], JS_NULL, 1, &event_ref.val));
} }
// Initiate a connection from this host to a remote server. Throws an error if the JS_RETURN_NULL();
// connection cannot be started. }
//
// :param hostname: The hostname or IP address of the remote server (e.g. "example.com" or "127.0.0.1"). // Initiate a connection from this host to a remote server.
// :param port: The port number to connect to.
// :return: An ENetPeer object representing the connection.
static JSValue js_enet_host_connect(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) static JSValue js_enet_host_connect(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{ {
ENetHost *host = JS_GetOpaque(this_val, enet_host_id); ENetHost *host = JS_GetOpaque(this_val, enet_host_id);
@@ -218,8 +213,6 @@ static JSValue js_enet_host_connect(JSContext *ctx, JSValueConst this_val, int a
} }
// Flush all pending outgoing packets for this host immediately. // Flush all pending outgoing packets for this host immediately.
//
// :return: None
static JSValue js_enet_host_flush(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) static JSValue js_enet_host_flush(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{ {
ENetHost *host = JS_GetOpaque(this_val, enet_host_id); ENetHost *host = JS_GetOpaque(this_val, enet_host_id);
@@ -228,16 +221,13 @@ static JSValue js_enet_host_flush(JSContext *ctx, JSValueConst this_val, int arg
return JS_NULL; return JS_NULL;
} }
// Broadcast a string or ArrayBuffer to all connected peers on channel 0. // Broadcast a string or blob to all connected peers on channel 0.
//
// :param data: A string or ArrayBuffer to broadcast to all peers.
// :return: None
static JSValue js_enet_host_broadcast(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) static JSValue js_enet_host_broadcast(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{ {
ENetHost *host = JS_GetOpaque(this_val, enet_host_id); ENetHost *host = JS_GetOpaque(this_val, enet_host_id);
if (!host) return JS_EXCEPTION; if (!host) return JS_EXCEPTION;
if (argc < 1) return JS_RaiseDisrupt(ctx, "Expected a string or ArrayBuffer to broadcast"); if (argc < 1) return JS_RaiseDisrupt(ctx, "Expected a string or blob to broadcast");
const char *data_str = NULL; const char *data_str = NULL;
size_t data_len = 0; size_t data_len = 0;
@@ -250,7 +240,7 @@ static JSValue js_enet_host_broadcast(JSContext *ctx, JSValueConst this_val, int
buf = js_get_blob_data(ctx, &data_len, argv[0]); buf = js_get_blob_data(ctx, &data_len, argv[0]);
if (!buf) return JS_EXCEPTION; if (!buf) return JS_EXCEPTION;
} else { } else {
return JS_RaiseDisrupt(ctx, "broadcast() only accepts a string or ArrayBuffer"); return JS_RaiseDisrupt(ctx, "broadcast() only accepts a string or blob");
} }
ENetPacket *packet = enet_packet_create(data_str ? (const void*)data_str : (const void*)buf, data_len, ENET_PACKET_FLAG_RELIABLE); ENetPacket *packet = enet_packet_create(data_str ? (const void*)data_str : (const void*)buf, data_len, ENET_PACKET_FLAG_RELIABLE);
@@ -261,30 +251,25 @@ static JSValue js_enet_host_broadcast(JSContext *ctx, JSValueConst this_val, int
return JS_NULL; return JS_NULL;
} }
static JSValue js_enet_host_get_port(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) // Host property getters
static JSValue js_enet_host_get_port(JSContext *js, JSValueConst self)
{ {
ENetHost *host = JS_GetOpaque(self, enet_host_id); ENetHost *host = JS_GetOpaque(self, enet_host_id);
if (!host) return JS_EXCEPTION; if (!host) return JS_EXCEPTION;
return JS_NewInt32(js, host->address.port); return JS_NewInt32(js, host->address.port);
} }
static JSValue js_enet_host_get_address(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) static JSValue js_enet_host_get_address(JSContext *js, JSValueConst self)
{ {
ENetHost *me = JS_GetOpaque(self, enet_host_id); ENetHost *me = JS_GetOpaque(self, enet_host_id);
if (!me) return JS_EXCEPTION; if (!me) return JS_EXCEPTION;
char ip_str[128]; char ip_str[128];
if (enet_address_get_host_ip(&me->address, ip_str, sizeof(ip_str)) != 0) if (enet_address_get_host_ip(&me->address, ip_str, sizeof(ip_str)) != 0)
return JS_NULL; return JS_NULL;
return JS_NewString(js, ip_str); return JS_NewString(js, ip_str);
} }
// Peer-level operations // Peer-level operations
// Request a graceful disconnection from this peer. The connection will close after
// pending data is sent.
//
// :return: None
static JSValue js_enet_peer_disconnect(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) static JSValue js_enet_peer_disconnect(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{ {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id); ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
@@ -293,16 +278,12 @@ static JSValue js_enet_peer_disconnect(JSContext *ctx, JSValueConst this_val, in
return JS_NULL; return JS_NULL;
} }
// Send a string or ArrayBuffer to this peer on channel 0.
//
// :param data: A string or ArrayBuffer to send.
// :return: None
static JSValue js_enet_peer_send(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) static JSValue js_enet_peer_send(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{ {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id); ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) return JS_EXCEPTION; if (!peer) return JS_EXCEPTION;
if (argc < 1) return JS_RaiseDisrupt(ctx, "Expected a string or ArrayBuffer to send"); if (argc < 1) return JS_RaiseDisrupt(ctx, "Expected a string or blob to send");
const char *data_str = NULL; const char *data_str = NULL;
size_t data_len = 0; size_t data_len = 0;
@@ -315,7 +296,7 @@ static JSValue js_enet_peer_send(JSContext *ctx, JSValueConst this_val, int argc
buf = js_get_blob_data(ctx, &data_len, argv[0]); buf = js_get_blob_data(ctx, &data_len, argv[0]);
if (!buf) return JS_EXCEPTION; if (!buf) return JS_EXCEPTION;
} else { } else {
return JS_RaiseDisrupt(ctx, "send() only accepts a string or ArrayBuffer"); return JS_RaiseDisrupt(ctx, "send() only accepts a string or blob");
} }
ENetPacket *packet = enet_packet_create(data_str ? (const void*)data_str : (const void*)buf, data_len, ENET_PACKET_FLAG_RELIABLE); ENetPacket *packet = enet_packet_create(data_str ? (const void*)data_str : (const void*)buf, data_len, ENET_PACKET_FLAG_RELIABLE);
@@ -326,9 +307,6 @@ static JSValue js_enet_peer_send(JSContext *ctx, JSValueConst this_val, int argc
return JS_NULL; return JS_NULL;
} }
// Immediately terminate the connection to this peer, discarding any pending data.
//
// :return: None
static JSValue js_enet_peer_disconnect_now(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) static JSValue js_enet_peer_disconnect_now(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{ {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id); ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
@@ -337,9 +315,6 @@ static JSValue js_enet_peer_disconnect_now(JSContext *ctx, JSValueConst this_val
return JS_NULL; return JS_NULL;
} }
// Request a disconnection from this peer after all queued packets are sent.
//
// :return: None
static JSValue js_enet_peer_disconnect_later(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) static JSValue js_enet_peer_disconnect_later(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{ {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id); ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
@@ -348,9 +323,6 @@ static JSValue js_enet_peer_disconnect_later(JSContext *ctx, JSValueConst this_v
return JS_NULL; return JS_NULL;
} }
// Reset this peer's connection, immediately dropping it and clearing its internal state.
//
// :return: None
static JSValue js_enet_peer_reset(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) static JSValue js_enet_peer_reset(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{ {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id); ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
@@ -359,9 +331,6 @@ static JSValue js_enet_peer_reset(JSContext *ctx, JSValueConst this_val, int arg
return JS_NULL; return JS_NULL;
} }
// Send a ping request to this peer to measure latency.
//
// :return: None
static JSValue js_enet_peer_ping(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) static JSValue js_enet_peer_ping(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{ {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id); ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
@@ -370,13 +339,6 @@ static JSValue js_enet_peer_ping(JSContext *ctx, JSValueConst this_val, int argc
return JS_NULL; return JS_NULL;
} }
// Configure the throttling behavior for this peer, controlling how ENet adjusts its sending
// rate based on packet loss or congestion.
//
// :param interval: The interval (ms) between throttle adjustments.
// :param acceleration: The factor to increase sending speed when conditions improve.
// :param deceleration: The factor to decrease sending speed when conditions worsen.
// :return: None
static JSValue js_enet_peer_throttle_configure(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) static JSValue js_enet_peer_throttle_configure(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{ {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id); ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
@@ -412,12 +374,10 @@ static JSClassDef enet_host = {
static JSClassDef enet_peer_class = { static JSClassDef enet_peer_class = {
"ENetPeer", "ENetPeer",
.finalizer = js_enet_peer_finalizer, .finalizer = js_enet_peer_finalizer,
// .gc_mark = js_enet_peer_mark
}; };
JSValue js_enet_resolve_hostname(JSContext *js, JSValue self, int argc, JSValue *argv) static JSValue js_enet_resolve_hostname(JSContext *js, JSValue self, int argc, JSValue *argv)
{ {
// TODO: implement
const char *hostname = JS_ToCString(js, argv[0]); const char *hostname = JS_ToCString(js, argv[0]);
JS_FreeCString(js, hostname); JS_FreeCString(js, hostname);
return JS_NULL; return JS_NULL;
@@ -435,18 +395,19 @@ static const JSCFunctionListEntry js_enet_host_funcs[] = {
JS_CFUNC_DEF("connect", 2, js_enet_host_connect), JS_CFUNC_DEF("connect", 2, js_enet_host_connect),
JS_CFUNC_DEF("flush", 0, js_enet_host_flush), JS_CFUNC_DEF("flush", 0, js_enet_host_flush),
JS_CFUNC_DEF("broadcast", 1, js_enet_host_broadcast), JS_CFUNC_DEF("broadcast", 1, js_enet_host_broadcast),
// JS_CGETSET_DEF("port", js_enet_host_get_port, NULL), JS_CFUNC0_DEF("port", js_enet_host_get_port),
// JS_CGETSET_DEF("address", js_enet_host_get_address, NULL), JS_CFUNC0_DEF("address", js_enet_host_get_address),
}; };
static JSValue js_enet_peer_get_rtt(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) // Peer property getters (zero-arg methods)
static JSValue js_enet_peer_get_rtt(JSContext *ctx, JSValueConst this_val)
{ {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id); ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) return JS_EXCEPTION; if (!peer) return JS_EXCEPTION;
return JS_NewInt32(ctx, peer->roundTripTime); return JS_NewInt32(ctx, peer->roundTripTime);
} }
static JSValue js_enet_peer_get_incoming_bandwidth(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) static JSValue js_enet_peer_get_incoming_bandwidth(JSContext *ctx, JSValueConst this_val)
{ {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id); ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) return JS_EXCEPTION; if (!peer) return JS_EXCEPTION;
@@ -454,7 +415,7 @@ static JSValue js_enet_peer_get_incoming_bandwidth(JSContext *ctx, JSValueConst
return JS_NewInt32(ctx, peer->incomingBandwidth); return JS_NewInt32(ctx, peer->incomingBandwidth);
} }
static JSValue js_enet_peer_get_outgoing_bandwidth(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) static JSValue js_enet_peer_get_outgoing_bandwidth(JSContext *ctx, JSValueConst this_val)
{ {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id); ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) return JS_EXCEPTION; if (!peer) return JS_EXCEPTION;
@@ -462,14 +423,14 @@ static JSValue js_enet_peer_get_outgoing_bandwidth(JSContext *ctx, JSValueConst
return JS_NewInt32(ctx, peer->outgoingBandwidth); return JS_NewInt32(ctx, peer->outgoingBandwidth);
} }
static JSValue js_enet_peer_get_last_send_time(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) static JSValue js_enet_peer_get_last_send_time(JSContext *ctx, JSValueConst this_val)
{ {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id); ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) return JS_EXCEPTION; if (!peer) return JS_EXCEPTION;
return JS_NewInt32(ctx, peer->lastSendTime); return JS_NewInt32(ctx, peer->lastSendTime);
} }
static JSValue js_enet_peer_get_last_receive_time(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) static JSValue js_enet_peer_get_last_receive_time(JSContext *ctx, JSValueConst this_val)
{ {
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id); ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) return JS_EXCEPTION; if (!peer) return JS_EXCEPTION;
@@ -528,16 +489,17 @@ static JSValue js_enet_peer_get_reliable_data_in_transit(JSContext *ctx, JSValue
static JSValue js_enet_peer_get_port(JSContext *js, JSValueConst self) static JSValue js_enet_peer_get_port(JSContext *js, JSValueConst self)
{ {
ENetPeer *peer = JS_GetOpaque(self, enet_peer_class_id); ENetPeer *peer = JS_GetOpaque(self, enet_peer_class_id);
if (!peer) return JS_EXCEPTION;
return JS_NewUint32(js, peer->address.port); return JS_NewUint32(js, peer->address.port);
} }
static JSValue js_enet_peer_get_address(JSContext *js, JSValueConst self) static JSValue js_enet_peer_get_address(JSContext *js, JSValueConst self)
{ {
ENetPeer *peer = JS_GetOpaque(self, enet_peer_class_id); ENetPeer *peer = JS_GetOpaque(self, enet_peer_class_id);
if (!peer) return JS_EXCEPTION;
char ip_str[128]; char ip_str[128];
if (enet_address_get_host_ip(&peer->address, ip_str, sizeof(ip_str)) != 0) if (enet_address_get_host_ip(&peer->address, ip_str, sizeof(ip_str)) != 0)
return JS_NULL; return JS_NULL;
return JS_NewString(js, ip_str); return JS_NewString(js, ip_str);
} }
@@ -550,24 +512,26 @@ static const JSCFunctionListEntry js_enet_peer_funcs[] = {
JS_CFUNC_DEF("ping", 0, js_enet_peer_ping), JS_CFUNC_DEF("ping", 0, js_enet_peer_ping),
JS_CFUNC_DEF("throttle_configure", 3, js_enet_peer_throttle_configure), JS_CFUNC_DEF("throttle_configure", 3, js_enet_peer_throttle_configure),
JS_CFUNC_DEF("timeout", 3, js_enet_peer_timeout), JS_CFUNC_DEF("timeout", 3, js_enet_peer_timeout),
// JS_CGETSET_DEF("rtt", js_enet_peer_get_rtt, NULL), JS_CFUNC0_DEF("rtt", js_enet_peer_get_rtt),
// JS_CGETSET_DEF("incoming_bandwidth", js_enet_peer_get_incoming_bandwidth, NULL), JS_CFUNC0_DEF("incoming_bandwidth", js_enet_peer_get_incoming_bandwidth),
// JS_CGETSET_DEF("outgoing_bandwidth", js_enet_peer_get_outgoing_bandwidth, NULL), JS_CFUNC0_DEF("outgoing_bandwidth", js_enet_peer_get_outgoing_bandwidth),
// JS_CGETSET_DEF("last_send_time", js_enet_peer_get_last_send_time, NULL), JS_CFUNC0_DEF("last_send_time", js_enet_peer_get_last_send_time),
// JS_CGETSET_DEF("last_receive_time", js_enet_peer_get_last_receive_time, NULL), JS_CFUNC0_DEF("last_receive_time", js_enet_peer_get_last_receive_time),
// JS_CGETSET_DEF("mtu", js_enet_peer_get_mtu, NULL), JS_CFUNC0_DEF("mtu", js_enet_peer_get_mtu),
// JS_CGETSET_DEF("outgoing_data_total", js_enet_peer_get_outgoing_data_total, NULL), JS_CFUNC0_DEF("outgoing_data_total", js_enet_peer_get_outgoing_data_total),
// JS_CGETSET_DEF("incoming_data_total", js_enet_peer_get_incoming_data_total, NULL), JS_CFUNC0_DEF("incoming_data_total", js_enet_peer_get_incoming_data_total),
// JS_CGETSET_DEF("rtt_variance", js_enet_peer_get_rtt_variance, NULL), JS_CFUNC0_DEF("rtt_variance", js_enet_peer_get_rtt_variance),
// JS_CGETSET_DEF("packet_loss", js_enet_peer_get_packet_loss, NULL), JS_CFUNC0_DEF("packet_loss", js_enet_peer_get_packet_loss),
// JS_CGETSET_DEF("state", js_enet_peer_get_state, NULL), JS_CFUNC0_DEF("state", js_enet_peer_get_state),
// JS_CGETSET_DEF("reliable_data_in_transit", js_enet_peer_get_reliable_data_in_transit, NULL), JS_CFUNC0_DEF("reliable_data_in_transit", js_enet_peer_get_reliable_data_in_transit),
// JS_CGETSET_DEF("port", js_enet_peer_get_port, NULL), JS_CFUNC0_DEF("port", js_enet_peer_get_port),
// JS_CGETSET_DEF("address", js_enet_peer_get_address, NULL), JS_CFUNC0_DEF("address", js_enet_peer_get_address),
}; };
JSValue js_core_enet_use(JSContext *ctx) JSValue js_core_internal_enet_use(JSContext *ctx)
{ {
enet_initialize();
JS_FRAME(ctx); JS_FRAME(ctx);
JS_NewClassID(&enet_host_id); JS_NewClassID(&enet_host_id);

View File

@@ -1143,6 +1143,8 @@ var root = null
var receive_fn = null var receive_fn = null
var greeters = {} // Router functions for when messages are received for a specific actor var greeters = {} // Router functions for when messages are received for a specific actor
var enet = use_core('internal/enet')
var peers = {} var peers = {}
var id_address = {} var id_address = {}
var peer_queue = {} var peer_queue = {}
@@ -1151,24 +1153,24 @@ var portal_fn = null
function peer_connection(peer) { function peer_connection(peer) {
return { return {
latency: peer.rtt, latency: peer.rtt(),
bandwidth: { bandwidth: {
incoming: peer.incoming_bandwidth, incoming: peer.incoming_bandwidth(),
outgoing: peer.outgoing_bandwidth outgoing: peer.outgoing_bandwidth()
}, },
activity: { activity: {
last_sent: peer.last_send_time, last_sent: peer.last_send_time(),
last_received: peer.last_receive_time last_received: peer.last_receive_time()
}, },
mtu: peer.mtu, mtu: peer.mtu(),
data: { data: {
incoming_total: peer.incoming_data_total, incoming_total: peer.incoming_data_total(),
outgoing_total: peer.outgoing_data_total, outgoing_total: peer.outgoing_data_total(),
reliable_in_transit: peer.reliable_data_in_transit reliable_in_transit: peer.reliable_data_in_transit()
}, },
latency_variance: peer.rtt_variance, latency_variance: peer.rtt_variance(),
packet_loss: peer.packet_loss, packet_loss: peer.packet_loss(),
state: peer.state state: peer.state()
} }
} }
@@ -1190,7 +1192,7 @@ $_.connection = function(callback, actor, config) {
// takes a function input value that will eventually be called with the current time in number form. // takes a function input value that will eventually be called with the current time in number form.
$_.portal = function(fn, port) { $_.portal = function(fn, port) {
if (portal) { if (portal) {
log.error(`Already started a portal listening on ${portal.port}`) log.error(`Already started a portal listening on ${portal.port()}`)
disrupt disrupt
} }
if (!port) { if (!port) {
@@ -1200,43 +1202,49 @@ $_.portal = function(fn, port) {
log.system(`starting a portal on port ${port}`) log.system(`starting a portal on port ${port}`)
portal = enet.create_host({address: "any", port}) portal = enet.create_host({address: "any", port})
portal_fn = fn portal_fn = fn
enet_check()
} }
function handle_host(e) { function handle_host(e) {
var queue = null var queue = null
var data = null var data = null
var addr = null
var port = null
if (e.type == "connect") { if (e.type == "connect") {
log.system(`connected a new peer: ${e.peer.address}:${e.peer.port}`) addr = e.peer.address()
peers[`${e.peer.address}:${e.peer.port}`] = e.peer port = e.peer.port()
queue = peer_queue.get(e.peer) log.system(`connected a new peer: ${addr}:${port}`)
peers[`${addr}:${port}`] = e.peer
queue = peer_queue[e.peer]
if (queue) { if (queue) {
arrfor(queue, (msg, index) => e.peer.send(nota.encode(msg))) arrfor(queue, (msg, index) => e.peer.send(nota.encode(msg)))
log.system(`sent queue out of queue`) log.system(`sent queue out of queue`)
peer_queue.delete(e.peer) delete peer_queue[e.peer]
} }
} else if (e.type == "disconnect") { } else if (e.type == "disconnect") {
peer_queue.delete(e.peer) delete peer_queue[e.peer]
arrfor(array(peers), function(id, index) { arrfor(array(peers), function(id, index) {
if (peers[id] == e.peer) delete peers[id] if (peers[id] == e.peer) delete peers[id]
}) })
log.system('portal got disconnect from ' + e.peer.address + ":" + e.peer.port) log.system('portal got disconnect from ' + e.peer.address() + ":" + e.peer.port())
} else if (e.type == "receive") { } else if (e.type == "receive") {
data = nota.decode(e.data) data = nota.decode(e.data)
if (data.replycc && !data.replycc.address) { if (data.replycc && !data.replycc.address) {
data.replycc[ACTORDATA].address = e.peer.address data.replycc[ACTORDATA].address = e.peer.address()
data.replycc[ACTORDATA].port = e.peer.port data.replycc[ACTORDATA].port = e.peer.port()
} }
if (data.data) populate_actor_addresses(data.data, e) if (data.data) populate_actor_addresses(data.data, e)
turn(data) handle_message(data)
send_messages()
} }
} }
function populate_actor_addresses(obj, e) { function populate_actor_addresses(obj, e) {
if (!is_object(obj)) return if (!is_object(obj)) return
if (obj[ACTORDATA] && !obj[ACTORDATA].address) { if (obj[ACTORDATA] && !obj[ACTORDATA].address) {
obj[ACTORDATA].address = e.peer.address obj[ACTORDATA].address = e.peer.address()
obj[ACTORDATA].port = e.peer.port obj[ACTORDATA].port = e.peer.port()
} }
arrfor(array(obj), function(key, index) { arrfor(array(obj), function(key, index) {
if (key in obj) if (key in obj)
@@ -1307,8 +1315,6 @@ $_.delay = function delay(fn, seconds) {
return function() { actor_mod.removetimer(id) } return function() { actor_mod.removetimer(id) }
} }
var enet = use_core('enet')
// causes this actor to stop when another actor stops. // causes this actor to stop when another actor stops.
var couplings = {} var couplings = {}
$_.couple = function couple(actor) { $_.couple = function couple(actor) {
@@ -1368,7 +1374,8 @@ function actor_send(actor, message) {
if (!portal) { if (!portal) {
log.system(`creating a contactor ...`) log.system(`creating a contactor ...`)
portal = enet.create_host({address:"any"}) portal = enet.create_host({address:"any"})
log.system(`allowing contact to port ${portal.port}`) log.system(`allowing contact to port ${portal.port()}`)
enet_check()
} }
log.system(`no peer! connecting to ${actor[ACTORDATA].address}:${actor[ACTORDATA].port}`) log.system(`no peer! connecting to ${actor[ACTORDATA].address}:${actor[ACTORDATA].port}`)
peer = portal.connect(actor[ACTORDATA].address, actor[ACTORDATA].port) peer = portal.connect(actor[ACTORDATA].address, actor[ACTORDATA].port)
@@ -1564,7 +1571,7 @@ function handle_message(msg) {
var fn = null var fn = null
if (msg[SYSYM]) { if (msg[SYSYM]) {
handle_sysym(msg[SYSYM], msg.from) handle_sysym(msg[SYSYM])
return return
} }
@@ -1596,7 +1603,7 @@ function enet_check()
$_.delay(enet_check, ENETSERVICE); $_.delay(enet_check, ENETSERVICE);
} }
// enet_check(); // enet_check started on demand when $portal() is called
// Finally, run the program // Finally, run the program
actor_mod.setname(_cell.args.program) actor_mod.setname(_cell.args.program)
@@ -1747,8 +1754,6 @@ $_.clock(_ => {
} }
env.args = _cell.args.arg env.args = _cell.args.arg
env.log = log env.log = log
os.print(`[debug] env keys: ${text(array(env), ',')}\n`)
os.print(`[debug] $stop in env: ${text(env['$stop'] != null)}\n`)
env = stone(env) env = stone(env)
var native_build = null var native_build = null

View File

@@ -82,7 +82,7 @@ scripts = [
'internal/os.c', 'internal/os.c',
'internal/fd.c', 'internal/fd.c',
'net/http.c', 'net/http.c',
'net/enet.c', 'internal/enet.c',
'archive/miniz.c', 'archive/miniz.c',
'source/cJSON.c' 'source/cJSON.c'
] ]

View File

@@ -1,10 +1,11 @@
var blob = use('blob') var blob = use('blob')
var time = use('time') var time = use('time')
var random = use('random').random_fit
return { return {
test_guid: function() { test_guid: function() {
var st = time.number() var st = time.number()
var guid = blob(256, $random_fit) var guid = blob(256, random)
stone(guid) stone(guid)
var btime = time.number()-st var btime = time.number()-st
st = time.number() st = time.number()

View File

@@ -10,5 +10,7 @@ $portal(e => {
$receiver(e => { $receiver(e => {
log.console(`Got message: ${e}`) log.console(`Got message: ${e}`)
send(e, {greet: "Hello back!"}) send(e, {greet: "Hello back!"})
$delay(_ => $stop(), 0.2)
}) })
// stop after portal is confirmed working
var _t = $delay(_ => $stop(), 0.5)