remove more unneeded from the std

This commit is contained in:
2025-11-29 17:26:29 -06:00
parent 9f696a0342
commit 0ddbc3e953
24 changed files with 33 additions and 2594 deletions

View File

@@ -238,7 +238,6 @@ src += ['quickjs.c', 'libregexp.c', 'libunicode.c', 'cutils.c', 'dtoa.c']
scripts = [
'nota.c',
'js.c',
'mersenne.c',
'qop.c',
'wildstar.c',
'fit.c',

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,22 @@
(function engine() {
globalThis.cell = prosperon
cell.DOC = Symbol()
var ACTORDATA = cell.hidden.actorsym
var SYSYM = '__SYSTEM__'
// Get hidden modules from cell.hidden before stripping it
var hidden = cell.hidden
var os = hidden.os;
var use_dyn = hidden.use_dyn
var dylib_ext = hidden.dylib_ext
var load_internal = hidden.load_internal
cell.os = null
var dylib_ext
switch(os.platform()) {
case 'Windows': dylib_ext = '.dll'; break;
case 'Darwin': dylib_ext = '.dylib'; break;
case 'Linux': dylib_ext = '.so'; break;
}
var load_internal = os.load_internal
function use_embed(name) {
return load_internal(`js_${name}_use`)
@@ -21,8 +27,6 @@ var wota = use_embed('wota')
var nota = use_embed('nota')
var enet = use_embed('enet')
var fd = use_embed('fd')
var os = use_embed('os')
var console_mod = use_embed('console')
var ENETSERVICE = 0.1
var REPLYTIMEOUT = 60 // seconds before replies are ignored
@@ -141,8 +145,6 @@ var qop = use_embed('qop')
var core_qop = qop.open(hidden.core_qop_blob)
var utf8 = use_embed('utf8')
delete cell.hidden
function disrupt(err)
{
if (overling) {

View File

@@ -1,15 +0,0 @@
var event = this
event.push_event[cell.DOC] = `Push a custom user event into SDL's queue, passing a callback function.
:param event: A function to call when this event is consumed.
:return: None
`
event.engine_input[cell.DOC] = `Poll all system events (keyboard, mouse, etc.) and call the given function with each event object.
:param callback: A function that executes on each event consumed from the poll.
:return: None
`
return event

View File

@@ -1,401 +0,0 @@
var graphics = this
var io = use('cellfs')
var time = use('time')
var res = use('resources')
var json = use('json')
var os = use('os')
var staef = use('staef')
var qoi = use('qoi')
var LASTUSE = Symbol()
var LOADING = Symbol()
var cache = {}
// cpu is the surface
graphics.Image = function(surfaceData) {
this.cpu = surfaceData || null;
this.texture = 0;
this.surface = this.cpu;
this.width = surfaceData?.width || 0;
this.height = surfaceData?.height || 0;
this.rect = {x:0, y:0, width:this.width, height:this.height};
this[LOADING] = false;
this[LASTUSE] = time.number();
}
graphics.Image.prototype.unload_cpu = function() {
this.cpu = null;
this.surface = null;
}
function calc_image_size(img) {
if (!img.rect) return
if (img.texture) {
return [img.texture.width * img.rect.width, img.texture.height * img.rect.height]
} else if (img.cpu) {
return [img.cpu.width * img.rect.width, img.cpu.height * img.rect.height]
}
return [0, 0]
}
function decorate_rect_px(img) {
// default UV rect is the whole image if none supplied
img.rect ??= {x:0, y:0, width:1, height:1} // [u0,v0,uw,vh] in 0-1
var width = 0, height = 0;
if (img.texture) {
width = img.texture.width;
height = img.texture.height;
} else if (img.cpu) {
width = img.cpu.width;
height = img.cpu.height;
} else {
return;
}
// store pixel-space version: [x, y, w, h] in texels
img.rect_px = {
x:Math.round(img.rect.x * width),
y:Math.round(img.rect.y * height),
width:Math.round(img.rect.width * width),
height:Math.round(img.rect.height * height)
}
}
function make_handle(obj)
{
var img = new graphics.Image(obj);
decorate_rect_px(img);
return img;
}
function wrapSurface(surf, maybeRect){
def h = make_handle(surf);
if(maybeRect) h.rect = maybeRect; /* honour frame sub-rect */
return h;
}
function wrapFrames(arr){ /* [{surface,time,rect}, …] → [{image,time}] */
return arr.map(f => {
// Handle both surface objects and objects with surface property
var surf = f.surface || f;
return {
image: wrapSurface(surf),
time: f.time || 0,
rect: f.rect /* keep for reference */
}
});
}
function makeAnim(frames, loop=true){
return { frames, loop }
}
function decode_image(bytes, ext)
{
switch(ext) {
case 'gif': return graphics.make_gif(bytes) // returns array of surfaces
case 'ase':
case 'aseprite': return graphics.make_aseprite(bytes)
case 'qoi': return qoi.decode(bytes) // returns single surface
default:
// Try QOI first since it's fast to check
var qoi_result = qoi.decode(bytes)
if (qoi_result) return qoi_result
// Fall back to make_texture for other formats
return graphics.image_decode(bytes) // returns single surface
}
}
function create_image(path){
try{
def bytes = io.slurpbytes(path);
let raw = decode_image(bytes, path.ext());
/* ── Case A: single surface (from make_texture) ────────────── */
if(raw && raw.width && raw.pixels && !Array.isArray(raw)) {
return new graphics.Image(raw)
}
/* ── Case B: array of surfaces (from make_gif) ────────────── */
if(Array.isArray(raw)) {
// Single frame GIF returns array with one surface
if(raw.length == 1 && !raw[0].time) {
return new graphics.Image(raw[0])
}
// Multiple frames - create animation
return makeAnim(wrapFrames(raw), true);
}
if(typeof raw == 'object' && !raw.width) {
if(raw.surface)
return new graphics.Image(raw.surface)
if(raw.frames && Array.isArray(raw.frames) && raw.loop != null)
return makeAnim(wrapFrames(raw.frames), !!raw.loop);
def anims = {};
for(def [name, anim] of Object.entries(raw)){
if(anim && Array.isArray(anim.frames))
anims[name] = makeAnim(wrapFrames(anim.frames), !!anim.loop);
else if(anim && anim.surface)
anims[name] = new graphics.Image(anim.surface);
}
if(Object.keys(anims).length) return anims;
}
throw new Error('Unsupported image structure from decoder');
}catch(e){
log.error(`Error loading image ${path}: ${e.message}`);
throw e;
}
}
var image = {}
image.dimensions = function() {
var width = 0, height = 0;
if (this.texture) {
width = this.texture.width;
height = this.texture.height;
} else if (this.cpu) {
width = this.cpu.width;
height = this.cpu.height;
}
return [width, height].scale([this.rect[2], this.rect[3]])
}
var spritesheet
var sheet_frames = []
var sheetsize = 1024
/**
Pack multiple images into a single texture sheet for efficiency.
Currently unimplemented (returns immediately).
*/
function pack_into_sheet(images) {
return
// This code is currently disabled with an immediate return.
// Implementation details commented out below.
}
graphics.is_image = function(obj) {
if (obj.texture && obj.rect) return true
}
graphics.texture_from_data = function(data)
{
if (!(data instanceof ArrayBuffer)) return null
var image = graphics.make_texture(data);
var img = make_handle(image)
if (renderer_actor) img.gpu;
return img;
}
graphics.from_surface = function(surf)
{
return make_handle(surf)
}
graphics.from = function(id, data)
{
if (typeof id != 'string')
throw new Error('Expected a string ID')
if (data instanceof ArrayBuffer)
return graphics.texture_from_data(data)
}
graphics.texture = function texture(path) {
if (path instanceof graphics.Image) return path
if (typeof path != 'string')
throw new Error('need a string for graphics.texture')
var parts = path.split(':')
var id = parts[0]
var animName = parts[1]
var frameIndex = parts[2]
// Handle the case where animName is actually a frame index (e.g., "gears:0")
if (animName != null && frameIndex == null && !isNaN(parseInt(animName))) {
frameIndex = parseInt(animName)
animName = null
}
if (!cache[id]) {
var ipath = res.find_image(id)
if (!ipath) {
// If still not found, return notex
return graphics.texture('notex')
}
var result = create_image(ipath)
cache[id] = result
}
var cached = cache[id]
// No further path specifiers and no frame index - return the whole thing
if (!animName && frameIndex == null) return cached
// Handle frame index without animation name (e.g., "gears:0")
if (!animName && frameIndex != null) {
// If cached is a single animation (has .frames property)
if (cached.frames && Array.isArray(cached.frames)) {
var idx = parseInt(frameIndex)
if (isNaN(idx)) return cached
// Wrap the index
idx = idx % cached.frames.length
return cached.frames[idx].image
}
// If cached is a single Image, any frame index just returns the image
if (cached instanceof graphics.Image) {
return cached
}
}
// If cached is a single Image, treat it as a single-frame animation
if (cached instanceof graphics.Image) {
if (frameIndex != null) {
// For single images, any frame index just returns the image
return cached
}
// animName without frameIndex for single image - return as single-frame array
return [cached]
}
// If cached is a single animation (has .frames property)
if (cached.frames && Array.isArray(cached.frames)) {
if (frameIndex != null) {
var idx = parseInt(frameIndex)
if (isNaN(idx)) return cached
// Wrap the index
idx = idx % cached.frames.length
return cached.frames[idx].image
}
// Just animation name for single animation - return the animation
return cached
}
// If cached is an object of multiple animations
if (typeof cached == 'object' && !cached.frames) {
var anim = cached[animName]
if (!anim)
throw new Error(`animation ${animName} not found in ${id}`)
if (frameIndex != null) {
var idx = parseInt(frameIndex)
if (isNaN(idx)) return anim
if (anim instanceof graphics.Image) {
// Single image animation - any frame index returns the image
return anim
} else if (anim.frames && Array.isArray(anim.frames)) {
// Multi-frame animation - wrap the index
idx = idx % anim.frames.length
return anim.frames[idx].image
}
}
// Just animation name - return the animation
return anim
}
return cached
}
graphics.tex_hotreload = function tex_hotreload(file) {
var basename = file.split('/').pop().split('.')[0]
// Check if this basename exists in our cache
if (!(basename in cache)) return
// Find the full path for this image
var fullpath = res.find_image(basename)
if (!fullpath) return
var img = create_image(fullpath)
var oldimg = cache[basename]
// Preserve the GPU texture ID if it exists
var oldGPU = oldimg.gpu
// Update the CPU surface data
oldimg.cpu = img.cpu
oldimg.surface = img.cpu
// Clear GPU texture to force reload
oldimg.gpu = 0
oldimg.texture = 0
oldimg[LOADING] = false
// Update dimensions
if (img.cpu) {
oldimg.width = img.cpu.width
oldimg.height = img.cpu.height
oldimg.rect = {x:0, y:0, width:img.cpu.width, height:img.cpu.height}
decorate_rect_px(oldimg)
}
}
/**
Merges specific properties from nv into ov, using an array of property names.
*/
function merge_objects(ov, nv, arr) {
arr.forEach(x => ov[x] = nv[x])
}
/**
Unimplemented function for creating a spritesheet out of multiple images.
*/
function make_spritesheet(paths, width, height) {
return
}
/**
Stores previously loaded fonts. Keyed by e.g. "path.ttf.16" -> fontObject.
*/
var fontcache = {}
var datas = []
graphics.get_font = function get_font(path) {
if (typeof path != 'string') return path
var parts = path.split('.')
var size = 16 // default size
if (!isNaN(parts[1])) {
path = parts[0]
size = Number(parts[1])
}
var fullpath = res.find_font(path)
if (!fullpath) throw new Error(`Cannot load font ${path}`)
var fontstr = `${fullpath}.${size}`
if (fontcache[fontstr]) return fontcache[fontstr]
var data = io.slurpbytes(fullpath)
var font = new staef.font(data, size)
fontcache[fontstr] = font
return font
}
graphics.queue_sprite_mesh = function(queue) {
var sprites = queue.filter(x => x.type == 'sprite')
if (sprites.length == 0) return []
var mesh = graphics.make_sprite_mesh(sprites)
for (var i = 0; i < sprites.length; i++) {
sprites[i].mesh = mesh
sprites[i].first_index = i*6
sprites[i].num_indices = 6
}
return [mesh.pos, mesh.uv, mesh.color, mesh.indices]
}
return graphics

View File

@@ -1,159 +0,0 @@
var io = this
function remove_dir(path) {
var files = io.enumerate(path, false) // non-recursive first
// Delete all files and subdirectories
for (var i = 0; i < files.length; i++) {
var file = files[i]
if (io.is_directory(file)) {
remove_dir(file) // Recurse into subdirectory
} else {
try {
io.rm(file)
} catch (e) {
log.error("Failed to remove " + file + ": " + e)
}
}
}
// Now remove the empty directory
try {
io.rm(path)
} catch (e) {
log.error("Failed to remove directory " + path + ": " + e)
}
}
io.rmdir = remove_dir
io.rm[cell.DOC] = `Remove the file or empty directory at the given path.
:param path: The file or empty directory to remove. Must be empty if a directory.
:return: None
`
io.mkdir[cell.DOC] = `Create a directory at the given path.
:param path: The directory path to create.
:return: None
`
io.exists[cell.DOC] = `Return a boolean indicating whether the file or directory at the given path exists.
:param path: The file or directory path to check.
:return: True if the path exists, otherwise false.
`
io.stat[cell.DOC] = `Return an object describing file metadata for the given path. The object includes
filesize, modtime, createtime, and accesstime. Throw an error if the path does not exist.
:param path: The file or directory to retrieve metadata for.
:return: An object with metadata (filesize, modtime, createtime, accesstime).
`
io.slurpbytes[cell.DOC] = `Read the entire file at the given path as a raw ArrayBuffer. Throw on error.
:param path: The file path to read from.
:return: An ArrayBuffer containing the files raw bytes.
`
io.slurp[cell.DOC] = `Read the entire file at the given path as a string. Throw on error.
:param path: The file path to read from.
:return: A string with the files contents.
`
io.slurpwrite[cell.DOC] = `Write data (string or ArrayBuffer) to the given file path. Overwrite if it exists.
Throw on error.
:param data: The data to write (string or ArrayBuffer).
:param path: The file path to write to.
:return: None
`
io.mount[cell.DOC] = `Mount a directory or archive at the specified mount point. An null mount
point mounts to '/'. Throw on error.
:param archiveOrDir: The directory or archive to mount.
:param mountPoint: The path at which to mount. If omitted or null, '/' is used.
:return: None
`
io.unmount[cell.DOC] = `Unmount a previously mounted directory or archive. Throw on error.
:param path: The directory or archive mount point to unmount.
:return: None
`
io.writepath[cell.DOC] = `Set the write directory. Subsequent writes will go here by default. Throw on error.
:param path: The directory path to set as writable.
:return: None
`
io.match[cell.DOC] = `Return boolean indicating whether the given wildcard pattern matches the provided
string. Dots must match dots. Case is not ignored.
Patterns can incorporate:
'?' - Matches exactly one character (except leading dots or slashes).
'*' - Matches zero or more characters (excluding path separators).
'**' - Matches zero or more characters, including path separators.
'[abc]' - A bracket expression; matches any single character from the set. Ranges like [a-z], [0-9] also work.
'[[:alpha:]]' - POSIX character classes can be used inside brackets.
'\\' - Backslash escapes the next character.
'!' - If placed immediately inside brackets (like [!abc]), it negates the set.
:param pattern: The wildcard pattern to compare.
:param string: The string to test against the wildcard pattern.
:return: True if matched, otherwise false.
`
io.globfs[cell.DOC] = `Return an array of files that do not match any of the provided glob patterns. It
recursively enumerates the filesystem within PHYSFS. Each pattern is treated as an
"ignore" rule, similar to .gitignore usage.
:param patterns: An array of glob patterns to ignore. Any file matching one of these is skipped.
:return: An array of matching file paths.
`
io.enumerate[cell.DOC] = `Return an array of files within the given directory, optionally recursing into
subdirectories.
:param path: The directory to list.
:param recurse: Whether to recursively include subdirectories (true or false).
:return: An array of file (and directory) paths found.
`
io.basedir[cell.DOC] = `Return the application's base directory (where the executable is located).
:return: A string with the base directory path.
`
io.prefdir[cell.DOC] = `Get the user-and-app-specific path where files can be written.
:param org: The name of your organization.
:param app: The name of your application.
:return: A string with the user's directory path.
`
io.open[cell.DOC] = `Open a file for writing, returning a file object that can be used for further
operations. Throw on error.
:param path: The file path to open for writing.
:return: A file object for subsequent write operations.
`
io.realdir[cell.DOC] = `Return the actual, real directory (on the host filesystem) that contains the given
file path. Return null if not found.
:param path: The file path whose real directory is requested.
:return: A string with the real directory path, or null.
`
io.searchpath[cell.DOC] = `Return an array of all directories in the current paths.
:return: An array of directory paths in the search path.
`
return io

View File

@@ -1,28 +1,27 @@
var json = {}
var utf8 = use('utf8')
var JS = JSON
JSON = null
// Produce a JSON text from a Javascript object. If a record value, at any level, contains a json() method, it will be called, and the value it returns (usually a simpler record) will be JSONified.
// If the record does not have a json() method, and if whitelist is a record, then only the keys that are associated with true in the whitelist are included.
// If the space input is true, then line breaks and extra whitespace will be included in the text.
json.encode = function encode(val,replacer,space = 1,whitelist)
{
return JSON.stringify(val, replacer, space)
return JS.stringify(val, replacer, space)
}
json.encode[cell.DOC] = `Produce a JSON text from a Javascript object. If a record value, at any level, contains a json() method, it will be called, and the value it returns (usually a simpler record) will be JSONified.
If the record does not have a json() method, and if whitelist is a record, then only the keys that are associated with true in the whitelist are included.
If the space input is true, then line breaks and extra whitespace will be included in the text.`
// The text text is parsed, and the resulting value (usually a record or an array) is returned.
// The optional reviver input is a method that will be called for every key and value at every level of the result. Each value will be replaced by the result of the reviver function. This can be used to reform data-only records into method-bearing records, or to transform date strings into seconds.
json.decode = function decode(text,reviver)
{
if (typeof text != 'string') {
text = utf8.decode(text)
if (!text) throw new Error("couldn't parse text: not a string")
}
return JSON.parse(text,reviver)
return JS.parse(text,reviver)
}
json.decode[cell.DOC] = `The text text is parsed, and the resulting value (usually a record or an array) is returned.
The optional reviver input is a method that will be called for every key and value at every level of the result. Each value will be replaced by the result of the reviver function. This can be used to reform data-only records into method-bearing records, or to transform date strings into seconds.`
return json

View File

@@ -1,124 +0,0 @@
var blob = use('blob')
var utf8 = use('utf8')
var INT = new blob(8, false)
stone(INT)
var FP_HEADER = new blob(64)
FP_HEADER.write_fit(0,56)
FP_HEADER.write_fit(1,8)
stone(FP_HEADER)
var ARRAY = new blob(8)
ARRAY.write_fit(2,8)
stone(ARRAY)
var RECORD = new blob(8)
RECORD.write_fit(3,8)
stone(RECORD)
var BLOB = new blob(8)
BLOB.write_fit(4,8)
stone(BLOB)
var TEXT = new blob(8)
TEXT.write_fit(5,8)
stone(TEXT)
var NULL_SYMBOL = new blob(64)
NULL_SYMBOL.write_fit(0,56)
NULL_SYMBOL.write_fit(7,8)
stone(NULL_SYMBOL)
var FALSE_SYMBOL = new blob(64)
FALSE_SYMBOL.write_fit(2,56)
FALSE_SYMBOL.write_fit(7,8)
stone(FALSE_SYMBOL)
var TRUE_SYMBOL = new blob(64)
TRUE_SYMBOL.write_fit(3,56)
TRUE_SYMBOL.write_fit(7,8)
stone(TRUE_SYMBOL)
var PRIVATE_SYMBOL = new blob(64)
PRIVATE_SYMBOL.write_fit(8,56)
PRIVATE_SYMBOL.write_fit(7,8)
stone(PRIVATE_SYMBOL)
var SYSTEM_SYMBOL = new blob(64)
SYSTEM_SYMBOL.write_fit(9,56)
SYSTEM_SYMBOL.write_fit(7,8)
stone(SYSTEM_SYMBOL)
var key_cache = {}
function encode_key(key)
{
if (key_cache[key])
return key_cache[key]
var encoded_key = utf8.encode(key)
var cached_blob = new blob(64 + encoded_key.length)
cached_blob.write_fit(utf8.byte_length(key), 56)
cached_blob.write_blob(TEXT)
cached_blob.write_blob(encoded_key)
stone(cached_blob)
key_cache[key] = cached_blob
return cached_blob
}
function encode_val(b, val)
{
var type = typeof val
if (type == 'number') {
b.write_blob(FP_HEADER)
b.write_number(val)
} else if (type == 'string') {
b.write_fit(utf8.byte_length(val), 56)
b.write_blob(TEXT)
b.write_blob(utf8.encode(val))
} else if (type == 'boolean') {
if (val)
b.write_blob(TRUE_SYMBOL)
else
b.write_blob(FALSE_SYMBOL)
} else if (type == 'null') {
b.write_blob(NULL_SYMBOL)
} else if (type == 'object') {
if (Array.isArray(val)) {
b.write_fit(val.length, 56)
b.write_blob(ARRAY)
for (var v of val)
encode_val(b, v)
} else if (val instanceof blob) {
b.write_fit(val.length, 56)
b.write_blob(BLOB)
b.write_blob(val)
} else {
var keys = Object.keys(val)
b.write_fit(keys.length, 56)
b.write_blob(RECORD)
for (var key of keys) {
if (typeof val[key] == 'function') continue
b.write_blob(encode_key(key))
encode_val(b, val[key])
}
}
}
}
function encode(val)
{
var b = new blob(8*256)// guess a good length
encode_val(b,val)
return stone(b)
}
function decode(b)
{
return null
}
return { INT, FP_HEADER, ARRAY, RECORD, BLOB, TEXT, NULL_SYMBOL, FALSE_SYMBOL, TRUE_SYMBOL, PRIVATE_SYMBOL, SYSTEM_SYMBOL, encode, decode }

View File

@@ -1,38 +0,0 @@
var math = this
math.dot[cell.DOC] = "Compute the dot product between two numeric arrays, returning a scalar. Extra elements are ignored."
math.project[cell.DOC] = "Project one vector onto another, returning a new array of the same dimension."
math.rotate[cell.DOC] = "Rotate a 2D point (or array of length 2) by the given angle (in turns) around an optional pivot."
math.midpoint[cell.DOC] = "Compute the midpoint of two arrays of numbers. Only the first two entries are used if 2D is intended."
math.reflect[cell.DOC] = "Reflect a vector across a plane normal. Both arguments must be numeric arrays."
math.distance[cell.DOC] = "Compute the Euclidean distance between two numeric arrays of matching length."
math.direction[cell.DOC] = "Compute the normalized direction vector from the first array to the second."
math.angle[cell.DOC] = "Given a 2D vector, return its angle from the X-axis in radians or some chosen units."
math.norm[cell.DOC] = "Return a normalized copy of the given numeric array. For 2D/3D/4D or arbitrary length."
math.angle_between[cell.DOC] = "Compute the angle between two vectors (2D/3D/4D)."
math.lerp[cell.DOC] = "Linear interpolation between two numbers: lerp(a, b, t)."
math.gcd[cell.DOC] = "Compute the greatest common divisor of two integers."
math.lcm[cell.DOC] = "Compute the least common multiple of two integers."
math.clamp[cell.DOC] = "Clamp a number between low and high. clamp(value, low, high)."
math.angledist[cell.DOC] = "Compute the signed distance between two angles in 'turn' units, e.g. 0..1 range."
math.jitter[cell.DOC] = "Apply a random +/- percentage noise to a number. Example: jitter(100, 0.05) -> ~95..105."
math.mean[cell.DOC] = "Compute the arithmetic mean of an array of numbers."
math.sum[cell.DOC] = "Sum all elements of an array of numbers."
math.sigma[cell.DOC] = "Compute standard deviation of an array of numbers."
math.median[cell.DOC] = "Compute the median of an array of numbers."
math.length[cell.DOC] = "Return the length of a vector (i.e. sqrt of sum of squares)."
math.from_to[cell.DOC] = "Return an array of points from a start to an end, spaced out by a certain distance."
math.TAU = Math.PI * 2;
math.deg2rad = function (deg) { return deg * 0.0174533; };
math.rad2deg = function (rad) { return rad / 0.0174533; };
math.turn2rad = function (x) { return x * Math.TAU; };
math.rad2turn = function (x) { return x / Math.TAU; };
math.turn2deg = function (x) { return x * 360; };
math.deg2turn = function (x) { return x / 360; };
math.rand_int = function(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
return math

View File

@@ -1,148 +0,0 @@
#include "quickjs.h"
#include "cell.h"
#include <stdint.h>
#include "qjs_macros.h"
// Random number generation constants for MT19937-64
#define STATE_VECTOR_LENGTH 312
#define STATE_VECTOR_M 156
#define NN STATE_VECTOR_LENGTH
#define MM STATE_VECTOR_M
#define MATRIX_A 0xB5026F5AA96619E9ULL
#define UM 0xFFFFFFFF80000000ULL /* Most significant 33 bits */
#define LM 0x7FFFFFFFULL /* Least significant 31 bits */
typedef struct tagMTRand {
uint64_t mt[STATE_VECTOR_LENGTH];
int32_t index;
} MTRand;
// Random number generation functions
static void m_seedRand(MTRand* rand, uint64_t seed) {
rand->mt[0] = seed;
for(rand->index = 1; rand->index < NN; rand->index++) {
rand->mt[rand->index] = (6364136223846793005ULL * (rand->mt[rand->index-1] ^ (rand->mt[rand->index-1] >> 62)) + rand->index);
}
}
static int64_t genRandLong(MTRand* rand) {
int i;
uint64_t x;
static uint64_t mag01[2] = {0ULL, MATRIX_A};
if (rand->index >= NN) { /* generate NN words at one time */
/* if init_genrand64() has not been called, */
/* a default initial seed is used */
if (rand->index == NN+1)
m_seedRand(rand, 5489ULL);
for (i = 0; i < NN-MM; i++) {
x = (rand->mt[i] & UM) | (rand->mt[i+1] & LM);
rand->mt[i] = rand->mt[i+MM] ^ (x>>1) ^ mag01[(int)(x&1ULL)];
}
for (; i < NN-1; i++) {
x = (rand->mt[i] & UM) | (rand->mt[i+1] & LM);
rand->mt[i] = rand->mt[i+(MM-NN)] ^ (x>>1) ^ mag01[(int)(x&1ULL)];
}
x = (rand->mt[NN-1] & UM) | (rand->mt[0] & LM);
rand->mt[NN-1] = rand->mt[MM-1] ^ (x>>1) ^ mag01[(int)(x&1ULL)];
rand->index = 0;
}
x = rand->mt[rand->index++];
x ^= (x >> 29) & 0x5555555555555555ULL;
x ^= (x << 17) & 0x71D67FFFEDA60000ULL;
x ^= (x << 37) & 0xFFF7EEE000000000ULL;
x ^= (x >> 43);
return (int64_t)(x & 0x000FFFFFFFFFFFFFULL); /* return 52-bit value safe for JS */
}
static double genRand(MTRand* rand) {
/* generates a random number on [0,1)-real-interval */
return (genRandLong(rand) >> 11) * (1.0/9007199254740992.0);
}
/* JS Class Definition */
static JSClassID js_mersenne_class_id;
static void js_mersenne_finalizer(JSRuntime *rt, JSValue val) {
MTRand *mrand = JS_GetOpaque(val, js_mersenne_class_id);
js_free_rt(rt, mrand);
}
static JSClassDef js_mersenne_class = {
"Mersenne",
.finalizer = js_mersenne_finalizer,
};
static MTRand *js2mersenne(JSContext *js, JSValue v) {
return JS_GetOpaque(v, js_mersenne_class_id);
}
/* Methods */
JSC_CCALL(mersenne_get,
MTRand *mrand = js2mersenne(js, self);
if (!mrand) return JS_ThrowTypeError(js, "Invalid mersenne context");
return JS_NewFloat64(js, genRand(mrand));
)
static const JSCFunctionListEntry js_mersenne_funcs[] = {
JS_CFUNC_DEF("get", 0, js_mersenne_get),
};
/* Factory Function */
static JSValue js_mersenne_use_call(JSContext *js, JSValueConst func_obj,
JSValueConst this_val, int argc, JSValueConst *argv)
{
uint64_t seed;
if (argc == 0 || JS_IsNull(argv[0])) {
// Use OS random
extern int randombytes(void *buf, size_t n);
randombytes(&seed, 8);
} else {
if (JS_ToFloat64(js, (double*)&seed, argv[0])) {
// Fallback to number if bigint fails or is not provided as bigint
double d;
if (JS_ToFloat64(js, &d, argv[0])) return JS_EXCEPTION;
seed = (uint64_t)d;
}
}
MTRand *mrand = js_malloc(js, sizeof(MTRand));
if (!mrand) return JS_ThrowOutOfMemory(js);
m_seedRand(mrand, seed);
JSValue obj = JS_NewObjectClass(js, js_mersenne_class_id);
if (JS_IsException(obj)) {
js_free(js, mrand);
return obj;
}
JS_SetOpaque(obj, mrand);
// Store seed as a read-only property
JS_DefinePropertyValueStr(js, obj, "seed",
JS_NewFloat64(js, seed),
JS_PROP_ENUMERABLE | JS_PROP_CONFIGURABLE // Read-only (no WRITABLE)
);
return obj;
}
JSValue js_mersenne_use(JSContext *js)
{
JS_NewClassID(&js_mersenne_class_id);
JS_NewClass(JS_GetRuntime(js), js_mersenne_class_id, &js_mersenne_class);
JSValue proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, proto, js_mersenne_funcs, sizeof(js_mersenne_funcs)/sizeof(JSCFunctionListEntry));
JS_SetClassProto(js, js_mersenne_class_id, proto);
// Return the factory function
return JS_NewCFunction2(js, js_mersenne_use_call, "mersenne", 1, JS_CFUNC_generic, 0);
}

View File

@@ -1,94 +0,0 @@
// Module resolver for handling different import styles
// Works with the PhysFS mount system set up by shop.js
var ModuleResolver = {}
// Resolve module imports according to the specification
ModuleResolver.resolve = function(request, from_path) {
// Handle scheme-qualified imports
if (request.includes('://')) {
var parts = request.split('://')
var scheme = parts[0]
var path = parts[1]
// Direct mapping to mount points
return '/' + scheme + '/' + path
}
// Handle relative imports
if (request.startsWith('./') || request.startsWith('../')) {
// Relative imports are resolved from the importing module's directory
if (from_path) {
var dir = from_path.substring(0, from_path.lastIndexOf('/'))
return resolve_relative(dir, request)
}
return request
}
// Handle bare imports
// PhysFS will search through all mounted directories
// The mount order ensures proper precedence
return request
}
// Helper to resolve relative paths
function resolve_relative(base, relative) {
var parts = base.split('/')
var rel_parts = relative.split('/')
for (var i = 0; i < rel_parts.length; i++) {
var part = rel_parts[i]
if (part == '.') {
continue
} else if (part == '..') {
parts.pop()
} else {
parts.push(part)
}
}
return parts.join('/')
}
// Get the shop configuration if available
ModuleResolver.get_shop_config = function() {
try {
var shop = use('shop')
if (shop) {
return shop.load_config()
}
} catch (e) {
// Shop not available yet
}
return null
}
// Check if a bare import should be routed to an alias
ModuleResolver.check_alias = function(request) {
var config = ModuleResolver.get_shop_config()
if (!config) return null
var first_segment = request.split('/')[0]
// Check dependencies
if (config.dependencies && config.dependencies[first_segment]) {
return '/' + request
}
// Check aliases
if (config.aliases && config.aliases[first_segment]) {
var actual = config.aliases[first_segment]
return '/' + actual + request.substring(first_segment.length)
}
// Check for single-alias fallback
if (config.dependencies && Object.keys(config.dependencies).length == 1) {
// If only one dependency and no local file matches, route there
var only_dep = Object.keys(config.dependencies)[0]
return '/' + only_dep + '/' + request
}
return null
}
return ModuleResolver

View File

@@ -406,14 +406,12 @@ static JSValue js_os_load_internal(JSContext *js, JSValue self, int argc, JSValu
#else
handle = dlopen(NULL, RTLD_LAZY);
#endif
if (argc < 1) {
if (argc < 1)
return JS_ThrowTypeError(js, "load_internal requires a symbol name");
}
const char *symbol_name = JS_ToCString(js, argv[0]);
if (!symbol_name) {
if (!symbol_name)
return JS_ThrowTypeError(js, "symbol name must be a string");
}
JSValue (*symbol)(JSContext *js);
#if defined(_WIN32)
@@ -424,18 +422,9 @@ static JSValue js_os_load_internal(JSContext *js, JSValue self, int argc, JSValu
JS_FreeCString(js, symbol_name);
if (!symbol) {
const char *error_msg = "Symbol not found";
#ifndef _WIN32
const char *dl_error = dlerror();
if (dl_error) {
error_msg = dl_error;
}
#endif
return JS_ThrowReferenceError(js, "Failed to get symbol: %s", error_msg);
}
if (!symbol)
return JS_NULL;
// Return the symbol as a pointer value
return symbol(js);
}

View File

@@ -1,69 +0,0 @@
// cell patch <module> - Create a patch for a module
var io = use('cellfs')
var shop = use('shop')
if (args.length < 1) {
log.console("Usage: cell patch <module>")
log.console("Example: cell patch jj_mod")
log.console("")
log.console("This creates a patch file in .cell/patches/ that will be")
log.console("applied when building the module.")
$_.stop()
return
}
var module_name = args[0]
if (!io.exists('.cell/shop.toml')) {
log.error("No shop.toml found. Run 'cell init' first.")
$_.stop()
return
}
var config = shop.load_config()
if (!config || !config.dependencies || !config.dependencies[module_name]) {
log.error("Module '" + module_name + "' not found in dependencies")
$_.stop()
return
}
// Ensure patches directory exists
if (!io.exists('.cell/patches')) {
io.mkdir('.cell/patches')
}
var patch_file = '.cell/patches/' + module_name + '-fix.patch'
if (io.exists(patch_file)) {
log.console("Patch already exists: " + patch_file)
log.console("Edit it directly or delete it to create a new one.")
$_.stop()
return
}
// Create patch template
var patch_template = `# Patch for ${module_name}
#
# To create a patch:
# 1. Make a copy of the module: cp -r .cell/modules/${module_name}@* /tmp/${module_name}-orig
# 2. Edit files in .cell/modules/${module_name}@*
# 3. Generate patch: diff -ruN /tmp/${module_name}-orig .cell/modules/${module_name}@* > ${patch_file}
#
# This patch will be automatically applied during 'cell build'
`
io.slurpwrite(patch_file, patch_template)
// Add to shop.toml
if (!config.patches) {
config.patches = {}
}
config.patches[module_name] = patch_file
shop.save_config(config)
log.console("Created patch skeleton: " + patch_file)
log.console("Follow the instructions in the file to create your patch.")
log.console("The patch will be applied automatically during 'cell build'.")
$_.stop()

View File

@@ -1,166 +0,0 @@
var io = use('cellfs')
Object.defineProperty(Function.prototype, "hashify", {
value: function () {
var hash = {}
var fn = this
function hashified(...args) {
var key = args[0]
if (hash[key] == null) hash[key] = fn(...args)
return hash[key]
}
return hashified
},
})
// Merge of the old resources.js and packer.js functionalities
var Resources = {}
// Recognized resource extensions
Resources.scripts = ["js"]
Resources.images = ["qoi", "png", "gif", "jpg", "jpeg", "ase", "aseprite"]
Resources.sounds = ["wav", "flac", "mp3", "qoa"]
Resources.fonts = ["ttf"]
// Helper function: get extension from path in lowercase (e.g., "image.png" -> "png")
function getExtension(path) {
var idx = path.lastIndexOf('.')
if (idx < 0) return ''
return path.substring(idx + 1).toLowerCase()
}
// Return true if ext is in at least one of the recognized lists
function isRecognizedExtension(ext) {
if (!ext) return false
if (Resources.scripts.includes(ext)) return true
if (Resources.images.includes(ext)) return true
if (Resources.sounds.includes(ext)) return true
if (Resources.fonts.includes(ext)) return true
if (Resources.lib.includes('.' + ext)) return true // for .so or .dll
return false
}
function find_in_path(filename, exts = []) {
if (typeof filename != 'string') return null
if (filename.includes('.')) {
var candidate = filename // possibly need "/" ?
if (io.exists(candidate) && !io.is_directory(candidate)) return candidate
return null
}
// Only check extensions if exts is provided and not empty
if (exts.length > 0) {
for (var ext of exts) {
var candidate = filename + '.' + ext
if (io.exists(candidate) && !io.is_directory(candidate)) return candidate
}
} else {
// Fallback to extensionless file only if no extensions are specified
var candidate = filename
if (io.exists(candidate) && !io.is_directory(candidate)) return candidate
}
return null
}
// Return a canonical path (the real directory plus the path)
Resources.canonical = function(file) {
return io.realdir(file) + file
}
// The resource finders
Resources.find_image = function(file) {
return find_in_path(file, Resources.images)
}.hashify()
Resources.find_sound = function(file) {
return find_in_path(file, Resources.sounds)
}.hashify()
Resources.find_script = function(file) {
return find_in_path(file, Resources.scripts)
}.hashify()
Resources.find_font = function(file) {
return find_in_path(file, Resources.fonts)
}.hashify()
// .prosperonignore reading helper
function read_ignore(dir) {
var path = dir + '/.prosperonignore'
var patterns = []
if (io.exists(path)) {
var lines = io.slurp(path).split('\n')
for (var line of lines) {
line = line.trim()
if (!line || line.startsWith('#')) continue
patterns.push(line)
}
}
return patterns
}
// Return a list of recognized files in the directory (and subdirectories),
// skipping those matched by .prosperonignore. Directory paths are skipped.
Resources.getAllFiles = function(dir = "") {
var patterns = read_ignore(dir)
var all = io.globfs(patterns, dir)
var results = []
for (var f of all) {
var fullPath = dir + '/' + f
try {
var st = io.stat(fullPath)
// skip directories (filesize=0) or unrecognized extension
if (!st.filesize) continue
var ext = getExtension(f)
if (!isRecognizedExtension(ext)) continue
results.push(fullPath)
} catch(e) {}
}
return results
}
Resources.getAllFiles[cell.DOC] = `
Return a list of recognized files in the given directory that are not matched by
.prosperonignore, skipping directories. Recognized extensions include scripts,
images, sounds, fonts, and libs.
:param dir: The directory to search.
:return: An array of recognized file paths.
`
// Categorize files by resource type
Resources.gatherStats = function(filePaths) {
var stats = {
scripts:0, images:0, sounds:0, fonts:0, lib:0, other:0, total:filePaths.length
}
for (var path of filePaths) {
var ext = getExtension(path)
if (Resources.scripts.includes(ext)) {
stats.scripts++
continue
}
if (Resources.images.includes(ext)) {
stats.images++
continue
}
if (Resources.sounds.includes(ext)) {
stats.sounds++
continue
}
if (Resources.fonts.includes(ext)) {
stats.fonts++
continue
}
stats.other++
}
return stats
}
Resources.gatherStats[cell.DOC] = `
Analyze a list of recognized files and categorize them by scripts, images, sounds,
fonts, libs, or other. Return a stats object with these counts and the total.
:param filePaths: An array of file paths to analyze.
:return: { scripts, images, sounds, fonts, lib, other, total }
`
return Resources

View File

@@ -1,30 +0,0 @@
// Test runner - runs test suites in parallel and reports results
var def = arg
if (arg.length == 0)
arg = [
'send',
'stop',
'blob',
'clock',
'couple',
'disrupt',
'empty', // this one should be an error
'text',
'http',
'use',
'parseq',
'kill'
]
for (var test of def)
$_.start(e => {
}, 'tests/' + test, $_)
$_.delay($_.stop, 1)
$_.receiver(e => {
log.console(json.encode(e))
})

View File

@@ -1,47 +0,0 @@
// cell vendor - Copy all dependencies into modules/ for hermetic builds
var io = use('cellfs')
var shop = use('shop')
if (!io.exists('.cell/shop.toml')) {
log.error("No shop.toml found. Run 'cell init' first.")
$_.stop()
return
}
var config = shop.load_config()
if (!config || !config.dependencies) {
log.console("No dependencies to vendor")
$_.stop()
return
}
log.console("Vendoring dependencies...")
for (var alias in config.dependencies) {
var locator = config.dependencies[alias]
var parsed = shop.parse_locator(locator)
if (!parsed) {
log.error("Invalid locator: " + locator)
continue
}
var module_dir = '.cell/modules/' + alias + '@' + parsed.version
if (config.replace && config.replace[locator]) {
// Already using local path
log.console(alias + " - using local path: " + config.replace[locator])
} else if (!io.exists(module_dir)) {
log.console(alias + " - not found at " + module_dir)
log.console(" Run 'cell get " + locator + "' to fetch it")
} else {
log.console(alias + " - already vendored at " + module_dir)
}
}
log.console("")
log.console("All dependencies are vendored in .cell/modules/")
log.console("This ensures hermetic, reproducible builds.")
$_.stop()

View File

@@ -843,13 +843,13 @@ void script_startup(cell_rt *prt)
// Add core QOP blob to hidden
JSValue globalThis = JS_GetGlobalObject(js);
JSValue prosp = JS_GetPropertyStr(js, globalThis, "prosperon");
JSValue hidden = JS_GetPropertyStr(js, prosp, "hidden");
JSValue cell = JS_GetPropertyStr(js, globalThis, "cell");
JSValue hidden = JS_GetPropertyStr(js, cell, "hidden");
size_t archive_size = qop_core.data_size - qop_core.files_offset;
JSValue blob = js_new_blob_stoned_copy(js, qop_core.data + qop_core.files_offset, archive_size);
JS_SetPropertyStr(js, hidden, "core_qop_blob", blob);
JS_FreeValue(js, hidden);
JS_FreeValue(js, prosp);
JS_FreeValue(js, cell);
JS_FreeValue(js, globalThis);
// Find and load engine.cm from QOP archive

View File

@@ -87,14 +87,7 @@ double js2angle(JSContext *js,JSValue v) {
return n * HMM_TurnToRad;
}
static uint32_t rng_state = 123456789;
static uint32_t xorshift32(){
uint32_t x = rng_state;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
return rng_state = x;
}
JSValue js_os_use(JSContext *js);
void ffi_load(JSContext *js)
{
@@ -103,19 +96,13 @@ void ffi_load(JSContext *js)
JSValue globalThis = JS_GetGlobalObject(js);
JSValue prosp = JS_NewObject(js);
JS_SetPropertyStr(js,globalThis,"prosperon", prosp);
JSValue cell = JS_NewObject(js);
JS_SetPropertyStr(js,globalThis,"cell", cell);
JSValue hidden_fn = JS_NewObject(js);
#if defined(_WIN32)
JS_SetPropertyStr(js, hidden_fn, "dylib_ext", JS_NewString(js, ".dll"));
#elif defined(__APPLE__)
JS_SetPropertyStr(js, hidden_fn, "dylib_ext", JS_NewString(js, ".dylib"));
#else
JS_SetPropertyStr(js, hidden_fn, "dylib_ext", JS_NewString(js, ".so"));
#endif
JS_SetPropertyStr(js, cell, "hidden", hidden_fn);
JS_SetPropertyStr(js, hidden_fn, "os", js_os_use(js));
const char actorsym_script[] = "var sym = Symbol(`actordata`); sym;";
@@ -132,7 +119,5 @@ void ffi_load(JSContext *js)
rt->init_wota = NULL;
}
JS_SetPropertyStr(js, prosp, "hidden", hidden_fn);
JS_FreeValue(js,globalThis);
}

View File

@@ -1,18 +0,0 @@
// Test that actors have access to $_
log.console("Testing actor access to $_:");
// In an actor script, $_ should be available
if (typeof $_ != 'null') {
log.console("✓ Actor has access to $_");
log.console(" $_.random is a", typeof $_.random);
log.console(" $_.clock is a", typeof $_.clock);
// Test spawning another actor
var child = this.spawn('test_child_actor');
// Test using a module
var testModule = use('test_module');
log.console("✓ Module loaded, result:", testModule.test());
} else {
log.error("✗ Actor does NOT have access to $_");
}

View File

@@ -1,18 +0,0 @@
// Test module to verify argument passing
log.console("Test args module loaded");
log.console("Number of arguments:", arg.length);
log.console("Arguments received:", arg);
function createMessage(prefix) {
prefix = prefix || "default";
return prefix + ": " + arg.join(", ");
}
return {
args: arg,
message: createMessage(arg[0]),
allArgs: function() {
return "All arguments: " + arg.join(", ");
}
};

View File

@@ -1,8 +0,0 @@
// Child actor test
log.console("Child actor spawned");
if (typeof $_ != 'null') {
log.console("✓ Child actor has access to $_");
} else {
log.error("✗ Child actor does NOT have access to $_");
}

View File

@@ -1,38 +0,0 @@
// Test event watching functionality
use('input');
// Start watching events
input.watch($_);
$_.receiver(msg => {
if (msg.type) {
log.console("Received event:", msg.type);
// Log specific event details
switch(msg.type) {
case "key_down":
case "key_up":
log.console(" Key:", msg.key, "Scancode:", msg.scancode, "Down:", msg.down);
break;
case "mouse_motion":
log.console(" Mouse position:", msg.pos, "Delta:", msg.d_pos);
break;
case "mouse_button_down":
case "mouse_button_up":
log.console(" Button:", msg.button, "Position:", msg.mouse, "Down:", msg.down);
break;
}
// Stop watching after receiving 10 events
if (!$_.event_count) $_.event_count = 0;
$_.event_count++;
if ($_.event_count >= 10) {
log.console("Received 10 events, stopping watch");
input.unwatch($_);
}
}
});
log.console("Event watcher started. Press keys or move mouse to generate events.");
log.console("Will stop after 10 events.");

View File

@@ -1,15 +0,0 @@
// Test module - should NOT have access to $_
function test() {
if (typeof $_ != 'null') {
log.error("✗ Module incorrectly has access to $_!");
return "ERROR: Module has $_ access";
} else {
log.console("✓ Module correctly does NOT have access to $_");
return "Module loaded without $_ access";
}
}
return {
test: test
};

View File

@@ -1,18 +0,0 @@
// Test script to verify use() with arguments
log.console("Testing use() with arguments:");
// Test 1: Load module without arguments
var module1 = use('test_args');
log.console("Module 1 message:", module1.message);
log.console("Module 1 args:", module1.args);
// Test 2: Load module with arguments
var module2 = use('test_args', 'hello', 'world', 123);
log.console("Module 2 message:", module2.message);
log.console("Module 2 all args:", module2.allArgs());
// Test 3: Verify modules are cached (should return same as module1)
var module3 = use('test_args');
log.console("Module 3 (cached) message:", module3.message);
log.console("Are module1 and module3 the same?", module1 == module3);