initial misty implementation
Some checks failed
Build and Deploy / build-linux (push) Failing after 1m41s
Build and Deploy / build-windows (CLANG64) (push) Failing after 10m50s
Build and Deploy / package-dist (push) Has been skipped
Build and Deploy / deploy-itch (push) Has been skipped
Build and Deploy / deploy-gitea (push) Has been skipped

This commit is contained in:
2025-03-04 22:40:01 -06:00
parent 925d1fc437
commit c7aee73dcb
6 changed files with 260 additions and 23 deletions

View File

@@ -519,6 +519,62 @@ var fnname = "doc"
script = `(function ${fnname}() { ${script}; })`
js.eval(DOCPATH, script)()
var enet = use('enet')
var $_ = {}
var host = enet.create_host()
$_.host = host
console.log(`made a host with port ${host.port()}`)
globalThis.$_ = $_
var portal = undefined
var receive_fn = undefined;
$_.contact = function(callback, record)
{
}
$_.connection = function(callback, actor, config)
{
}
$_.portal = function(fn, port)
{
}
$_.receiver = function(fn)
{
receive_fn = fn;
}
$_.start = function(cb, prg, arg)
{
}
$_.stop = function(actor)
{
if (!actor)
os.exit(0)
}
$_.unneeded = function(fn, seconds)
{
}
$_.delay = function(fn, seconds)
{
var id = os.addtimer(fn, seconds);
return function() { os.removetimer(id); }
}
use('cmd')(prosperon.argv)
})()

View File

@@ -165,6 +165,47 @@ Cmdline.print_order = function (fn) {
console.print(fn.doc + "\n");
};
function parse_args(argv)
{
var args = {};
for (var i = 0; i < argv.length; i++) {
if (argv[i].startsWith("--")) {
var key = argv[i].slice(2);
if (i + 1 < argv.length && !argv[i + 1].startsWith("--")) {
args[key] = argv[i + 1];
i++; // Skip the value
} else {
args[key] = true; // Flag without value
}
}
}
return args;
}
Cmdline.register_order(
"spawn",
function(argv) {
var args = parse_args(argv)
console.log(json.encode(args));
if (!args.program)
os.exit()
console.log(`going to connect to ${args.overling}`)
if (args.overling) {
// connect to the port
$_.host.connect("localhost", args.overling);
console.log("CONNECTING TO " + args.overling);
while(1) {
os.waitevent(_ => {}, 0.016)
$_.host.service(e => { console.log(json.encode(e)) }, 0.016)
}
}
spawn_root(args.program)
},
"Spawn a new prosperon actor.",
"TOPIC"
);
Cmdline.register_order(
"help",
function (order) {

View File

@@ -28,6 +28,8 @@
#include "cgltf.h"
#include "physfs.h"
void gui_input(SDL_Event *e);
#ifdef _WIN32
#include <windows.h>
#else
@@ -55,6 +57,7 @@ typedef struct rtree rtree;
#include <SDL3/SDL_loadso.h>
#include <SDL3/SDL_cpuinfo.h>
static Uint32 timer_cb_event;
#ifdef __APPLE__
#include <Accelerate/Accelerate.h>
@@ -7132,6 +7135,86 @@ JSValue js_os_version(JSContext *js, JSValue self, int argc, JSValue *argv) {
return JS_UNDEFINED;
}
JSC_CCALL(os_createprocess,
int ac = JS_ArrayLength(js,argv[0]);
const char *args[ac+1];
for (int i = 0; i < ac; i++) {
JSValue astr = JS_GetPropertyUint32(js,argv[0],i);
args[i] = JS_ToCString(js,astr);
JS_FreeValue(js,astr);
}
args[ac] = NULL;
SDL_Process *actor = SDL_CreateProcess(args, 0);
for (int i = 0; i < ac; i++)
JS_FreeCString(js,args[i]);
if (!actor)
return JS_ThrowReferenceError(js, "Unable to create process: %s\n", SDL_GetError());
)
struct { SDL_TimerID key; JSValue *value; } *timer_hash = NULL;
Uint64 os_timer_cb(JSValue *fn, SDL_TimerID id, Uint64 delay)
{
SDL_UserEvent event;
SDL_zero(event);
event.type = timer_cb_event;
event.data1 = fn;
SDL_PushEvent(&event);
hmdel(timer_hash, id);
return 0;
}
JSC_CCALL(os_addtimer,
JSValue *fn = malloc(sizeof(*fn));
*fn = JS_DupValue(js,argv[0]);
double secs;
JS_ToFloat64(js, &secs, argv[1]);
SDL_TimerID id = SDL_AddTimerNS(secs*1000000000.0f, os_timer_cb, fn);
hmput(timer_hash, id, fn);
return JS_NewUint32(js,id);
)
JSC_CCALL(os_removetimer,
SDL_TimerID id;
JS_ToUint32(js,&id, argv[0]);
int rm = SDL_RemoveTimer(id);
if (!rm) return JS_ThrowReferenceError(js,"Could not remove timer id %u: %s\n", id, SDL_GetError());
JSValue *fn = hmget(timer_hash, id);
if (fn != -1) {
JS_FreeValue(js, *fn);
free(fn);
hmdel(timer_hash, id);
}
)
JSC_CCALL(os_waitevent,
SDL_Event event;
double secs;
JS_ToFloat64(js, &secs, argv[1]);
while (SDL_WaitEventTimeout(&event, secs*1000.0f)) {
if (event.type == timer_cb_event) {
JSValue *fn = event.user.data1;
JSValue ret = JS_Call(js, *fn, JS_UNDEFINED, 0, NULL);
JS_FreeValue(js,*fn);
free(fn);
uncaught_exception(js,ret);
} else {
gui_input(&event);
JSValue e = event2js(js,event);
JSValue ret = JS_Call(js,argv[0], JS_UNDEFINED, 1, &e);
uncaught_exception(js,ret);
}
}
return JS_UNDEFINED;
)
static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, make_transform, 0),
MIST_FUNC_DEF(os, clean_transforms, 0),
@@ -7167,7 +7250,11 @@ static const JSCFunctionListEntry js_os_funcs[] = {
// dangerous ones that need disabled for shipping
MIST_FUNC_DEF(os, env, 1),
MIST_FUNC_DEF(os, system, 1),
MIST_FUNC_DEF(os, system, 1),
MIST_FUNC_DEF(os, createprocess, 0),
MIST_FUNC_DEF(os, addtimer, 2),
MIST_FUNC_DEF(os, removetimer, 1),
MIST_FUNC_DEF(os, waitevent, 2),
};
JSC_CCALL(js_dump_class, return js_get_object_class_distribution(js))
@@ -7239,14 +7326,19 @@ static const JSCFunctionListEntry js_video_funcs[] = {
MIST_FUNC_DEF(os, make_video, 1),
};
void gui_input(SDL_Event *e);
// Polls and handles all input events
JSValue js_os_engine_input(JSContext *js, JSValue self, int argc, JSValue *argv) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
#ifndef NEDITOR
if (event.type == timer_cb_event) {
JSValue *fn = event.user.data1;
JSValue ret = JS_Call(js, *fn, JS_UNDEFINED, 0, NULL);
JS_FreeValue(js,*fn);
uncaught_exception(js,ret);
continue;
}
gui_input(&event);
#endif
JSValue e = event2js(js,event);
JSValue ret = JS_Call(js,argv[0], JS_UNDEFINED, 1, &e);
uncaught_exception(js,ret);
@@ -7555,6 +7647,7 @@ static void signal_handler(int sig) {
break;
}
if (!str) return;
script_evalf("prosperon.dispatch('%s')", str);
}
@@ -7655,6 +7748,8 @@ void ffi_load(JSContext *js, int argc, char **argv) {
QJSCLASSPREP_FUNCS(datastream);
QJSCLASSPREP_FUNCS(timer);
timer_cb_event = SDL_RegisterEvents(1);
JS_SetPropertyStr(js, globalThis, "use_dyn", JS_NewCFunction(js,js_os_use_dyn,"use_dyn", 1));
JS_SetPropertyStr(js, globalThis, "use_embed", JS_NewCFunction(js,js_os_use_embed,"use_embed", 1));
@@ -7793,5 +7888,7 @@ void ffi_load(JSContext *js, int argc, char **argv) {
idx_buffer = JS_UNDEFINED;
cycle_fn = JS_UNDEFINED;
JS_FreeValue(js,globalThis);
JS_FreeValue(js,globalThis);
SDL_Init(SDL_INIT_EVENTS);
}

View File

@@ -40,25 +40,34 @@ static JSValue js_enet_deinitialize(JSContext *ctx, JSValueConst this_val,
/* Host creation */
static JSValue js_enet_host_create(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) {
int argc, JSValueConst *argv) {
ENetHost *host;
ENetAddress address;
JSValue obj;
// Default configuration matching the JavaScript object
size_t peer_count = 32; // peer_count: 32
size_t channel_limit = 0; // channel_limit: 0
enet_uint32 incoming_bandwidth = 0; // incoming_bandwidth: 0
enet_uint32 outgoing_bandwidth = 0; // outgoing_bandwidth: 0
if (argc < 1) {
// Create client-like host, unbound
host = enet_host_create(NULL, 32, 2, 0, 0);
// Create client-like host with port 0 and "any" address
address.host = ENET_HOST_ANY;
address.port = 0;
host = enet_host_create(&address, peer_count, channel_limit,
incoming_bandwidth, outgoing_bandwidth);
if (!host) {
return JS_ThrowInternalError(ctx, "Failed to create ENet host (null address).");
return JS_ThrowInternalError(ctx, "Failed to create ENet host (any address).");
}
goto RET;
}
// If arg is provided, interpret as "ip:port" for server
// If argument is provided, interpret as "ip:port" for server
const char *address_str = JS_ToCString(ctx, argv[0]);
if (!address_str) {
if (!address_str)
return JS_EXCEPTION; // memory or conversion error
}
char ip[64];
int port;
@@ -68,14 +77,20 @@ static JSValue js_enet_host_create(JSContext *ctx, JSValueConst this_val,
}
JS_FreeCString(ctx, address_str);
int err = enet_address_set_host_ip(&address, ip);
if (err != 0) {
return JS_ThrowInternalError(ctx, "Failed to set host IP from %s. Error %d.", ip, err);
if (strcmp(ip, "any") == 0)
address.host = ENET_HOST_ANY;
else if (strcmp(ip, "broadcast"))
address.host = ENET_HOST_BROADCAST;
else {
int err = enet_address_set_host_ip(&address, ip);
if (err != 0) {
return JS_ThrowInternalError(ctx, "Failed to set host IP from %s. Error %d.", ip, err);
}
}
address.port = port;
// Create server host with max 32 clients, 2 channels
host = enet_host_create(&address, 32, 2, 0, 0);
// Create host with specified configuration
host = enet_host_create(&address, peer_count, channel_limit, incoming_bandwidth, outgoing_bandwidth);
if (!host) {
return JS_ThrowInternalError(ctx, "Failed to create ENet host.");
}
@@ -93,6 +108,7 @@ RET:
/* Host service: poll for events */
static JSValue js_enet_host_service(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) {
ENetHost *host = JS_GetOpaque(this_val, enet_host_id);
if (!host) {
return JS_EXCEPTION;
@@ -105,14 +121,11 @@ static JSValue js_enet_host_service(JSContext *ctx, JSValueConst this_val,
JSValue callback = argv[0];
JS_DupValue(ctx, callback);
// Optional timeout
int timeout = 0;
if (argc > 1) {
JS_ToInt32(ctx, &timeout, argv[1]);
}
double secs;
JS_ToFloat64(ctx, &secs, argv[1]);
ENetEvent event;
while (enet_host_service(host, &event, timeout) > 0) {
while (enet_host_service(host, &event, secs*1000.0f) > 0) {
JSValue event_obj = JS_NewObject(ctx);
switch (event.type) {
@@ -273,6 +286,20 @@ static JSValue js_enet_host_broadcast(JSContext *ctx, JSValueConst this_val,
return JS_UNDEFINED;
}
static JSValue js_enet_host_get_port(JSContext *js, JSValueConst self, int argc, JSValueConst *argv)
{
ENetHost *host = JS_GetOpaque(self, enet_host_id);
if (!host) return JS_EXCEPTION;
return JS_NewInt32(js, host->address.port);
}
static JSValue js_enet_host_get_address(JSContext *js, JSValueConst self, int argc, JSValueConst *argv)
{
ENetHost *host = JS_GetOpaque(self, enet_host_id);
if (!host) return JS_EXCEPTION;
return JS_NewInt32(js, host->address.host);
}
/* Peer-level operations */
static JSValue js_enet_peer_disconnect(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) {
@@ -429,6 +456,8 @@ static const JSCFunctionListEntry js_enet_host_funcs[] = {
JS_CFUNC_DEF("connect", 2, js_enet_host_connect),
JS_CFUNC_DEF("flush", 0, js_enet_host_flush),
JS_CFUNC_DEF("broadcast", 1, js_enet_host_broadcast),
JS_CFUNC_DEF("port", 0, js_enet_host_get_port),
JS_CFUNC_DEF("address", 0, js_enet_host_get_address),
};
static const JSCFunctionListEntry js_enet_peer_funcs[] = {

9
tests/overling.js Normal file
View File

@@ -0,0 +1,9 @@
var os = use('os')
var newguy = os.createprocess(["./prosperon", "spawn", "--program", "spawn2.js", "--overling", $_.host.port()])
var hang = 0.016
while (1) {
os.waitevent(_ => {}, hang)
$_.host.service(e => {console.log(json.encode(e))}, hang)
}

5
tests/underling.js Normal file
View File

@@ -0,0 +1,5 @@
var os = use('os')
console.log("Created underling")
os.exit()