From 3e87bfd6cc9472436395a77b1f07c9890f2af147 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Wed, 4 Jun 2025 13:22:58 -0500 Subject: [PATCH] add to look for same folder name as well as main --- prosperon/examples/chess/main.ce | 398 ---------------------------- prosperon/main.js | 354 ------------------------- prosperon/{main.ce => prosperon.ce} | 1 - scripts/engine.cm | 14 +- source/cell.c | 2 +- source/qjs_sdl.c | 3 +- 6 files changed, 13 insertions(+), 759 deletions(-) delete mode 100644 prosperon/examples/chess/main.ce delete mode 100644 prosperon/main.js rename prosperon/{main.ce => prosperon.ce} (99%) diff --git a/prosperon/examples/chess/main.ce b/prosperon/examples/chess/main.ce deleted file mode 100644 index 68862442..00000000 --- a/prosperon/examples/chess/main.ce +++ /dev/null @@ -1,398 +0,0 @@ -/* main.js – runs the demo with your prototype-based grid */ - -var json = use('json') -var draw2d = use('prosperon/draw2d') - -var blob = use('blob') - -log.console("HERE") - -/*──── import our pieces + systems ───────────────────────────────────*/ -var Grid = use('grid'); // your new ctor -var MovementSystem = use('movement').MovementSystem; -var startingPos = use('pieces').startingPosition; -var rules = use('rules'); - -/*──── build board ───────────────────────────────────────────────────*/ -var grid = new Grid(8, 8); -grid.width = 8; // (the ctor didn't store them) -grid.height = 8; - -var mover = new MovementSystem(grid, rules); -startingPos(grid); - -/*──── networking and game state ─────────────────────────────────────*/ -var gameState = 'waiting'; // 'waiting', 'searching', 'server_waiting', 'connected' -var isServer = false; -var opponent = null; -var myColor = null; // 'white' or 'black' -var isMyTurn = false; - -function updateTitle() { - var title = "Misty Chess - "; - - switch(gameState) { - case 'waiting': - title += "Press S to start server or J to join"; - break; - case 'searching': - title += "Searching for server..."; - break; - case 'server_waiting': - title += "Waiting for player to join..."; - break; - case 'connected': - if (myColor) { - title += (mover.turn === myColor ? "Your turn (" + myColor + ")" : "Opponent's turn (" + mover.turn + ")"); - } else { - title += mover.turn + " turn"; - } - break; - } - - log.console(title) -} - -// Initialize title -updateTitle(); - -/*──── mouse → click-to-move ─────────────────────────────────────────*/ -var selectPos = null; -var hoverPos = null; -var holdingPiece = false; - -var opponentMousePos = null; -var opponentHoldingPiece = false; -var opponentSelectPos = null; - -function handleMouseButtonDown(e) { - if (e.which !== 0) return; - - // Don't allow piece selection unless we have an opponent - if (gameState !== 'connected' || !opponent) return; - - var mx = e.mouse.x; - var my = e.mouse.y; - - var c = [Math.floor(mx / 60), Math.floor(my / 60)]; - if (!grid.inBounds(c)) return; - - var cell = grid.at(c); - if (cell.length && cell[0].colour === mover.turn) { - selectPos = c; - holdingPiece = true; - // Send pickup notification to opponent - if (opponent) { - send(opponent, { - type: 'piece_pickup', - pos: c - }); - } - } else { - selectPos = null; - } -} - -function handleMouseButtonUp(e) { - if (e.which !== 0 || !holdingPiece || !selectPos) return; - - // Don't allow moves unless we have an opponent and it's our turn - if (gameState !== 'connected' || !opponent || !isMyTurn) { - holdingPiece = false; - return; - } - - var mx = e.mouse.x; - var my = e.mouse.y; - - var c = [Math.floor(mx / 60), Math.floor(my / 60)]; - if (!grid.inBounds(c)) { - holdingPiece = false; - return; - } - - if (mover.tryMove(grid.at(selectPos)[0], c)) { - log.console("Made move from", selectPos, "to", c); - // Send move to opponent - log.console("Sending move to opponent:", opponent); - send(opponent, { - type: 'move', - from: selectPos, - to: c - }); - isMyTurn = false; // It's now opponent's turn - log.console("Move sent, now opponent's turn"); - selectPos = null; - updateTitle(); - } - - holdingPiece = false; - - // Send piece drop notification to opponent - if (opponent) { - send(opponent, { - type: 'piece_drop' - }); - } -} - -function handleMouseMotion(e) { - var mx = e.pos.x; - var my = e.pos.y; - - var c = [Math.floor(mx / 60), Math.floor(my / 60)]; - if (!grid.inBounds(c)) { - hoverPos = null; - return; - } - - hoverPos = c; - - // Send mouse position to opponent in real-time - if (opponent && gameState === 'connected') { - send(opponent, { - type: 'mouse_move', - pos: c, - holding: holdingPiece, - selectPos: selectPos - }); - } -} - -function handleKeyDown(e) { - // S key - start server - if (e.scancode === 22 && gameState === 'waiting') { // S key - startServer(); - } - // J key - join server - else if (e.scancode === 13 && gameState === 'waiting') { // J key - joinServer(); - } -} - -/*──── drawing helpers ───────────────────────────────────────────────*/ -/* ── constants ─────────────────────────────────────────────────── */ -var S = 60; // square size in px -var light = [0.93,0.93,0.93,1]; -var dark = [0.25,0.25,0.25,1]; -var allowedColor = [1.0, 0.84, 0.0, 1.0]; // Gold for allowed moves -var myMouseColor = [0.0, 1.0, 0.0, 1.0]; // Green for my mouse -var opponentMouseColor = [1.0, 0.0, 0.0, 1.0]; // Red for opponent mouse - -/* ── draw one 8×8 chess board ──────────────────────────────────── */ -function drawBoard() { - for (var y = 0; y < 8; ++y) - for (var x = 0; x < 8; ++x) { - var isMyHover = hoverPos && hoverPos[0] === x && hoverPos[1] === y; - var isOpponentHover = opponentMousePos && opponentMousePos[0] === x && opponentMousePos[1] === y; - var isValidMove = selectPos && holdingPiece && isValidMoveForTurn(selectPos, [x, y]); - - var color = ((x+y)&1) ? dark : light; - - if (isValidMove) { - color = allowedColor; // Gold for allowed moves - } else if (isMyHover && !isOpponentHover) { - color = myMouseColor; // Green for my mouse - } else if (isOpponentHover) { - color = opponentMouseColor; // Red for opponent mouse - } - - draw2d.rectangle( - { x: x*S, y: y*S, width: S, height: S }, - { thickness: 0 }, - { color: color } - ); - } -} - -function isValidMoveForTurn(from, to) { - if (!grid.inBounds(to)) return false; - - var piece = grid.at(from)[0]; - if (!piece) return false; - - // Check if the destination has a piece of the same color - var destCell = grid.at(to); - if (destCell.length && destCell[0].colour === piece.colour) { - return false; - } - - return rules.canMove(piece, from, to, grid); -} - -/* ── draw every live piece ─────────────────────────────────────── */ -function drawPieces() { - grid.each(function (piece) { - if (piece.captured) return; - - // Skip drawing the piece being held (by me or opponent) - if (holdingPiece && selectPos && - piece.coord[0] === selectPos[0] && - piece.coord[1] === selectPos[1]) { - return; - } - - // Skip drawing the piece being held by opponent - if (opponentHoldingPiece && opponentSelectPos && - piece.coord[0] === opponentSelectPos[0] && - piece.coord[1] === opponentSelectPos[1]) { - return; - } - - var r = { x: piece.coord[0]*S, y: piece.coord[1]*S, - width:S, height:S }; - - draw2d.image(piece.sprite, r); - }); - - // Draw the held piece at the mouse position if we're holding one - if (holdingPiece && selectPos && hoverPos) { - var piece = grid.at(selectPos)[0]; - if (piece) { - var r = { x: hoverPos[0]*S, y: hoverPos[1]*S, - width:S, height:S }; - - draw2d.image(piece.sprite, r); - } - } - - // Draw opponent's held piece if they're dragging one - if (opponentHoldingPiece && opponentSelectPos && opponentMousePos) { - var opponentPiece = grid.at(opponentSelectPos)[0]; - if (opponentPiece) { - var r = { x: opponentMousePos[0]*S, y: opponentMousePos[1]*S, - width:S, height:S }; - - // Draw with slight transparency to show it's the opponent's piece - draw2d.image(opponentPiece.sprite, r); - } - } -} - -function update(dt) -{ - return {} -} - -function draw() -{ - draw2d.clear() - drawBoard() - drawPieces() - return draw2d.get_commands() -} - - -function startServer() { - gameState = 'server_waiting'; - isServer = true; - myColor = 'white'; - isMyTurn = true; - updateTitle(); - - $_.portal(e => { - log.console("Portal received contact message"); - // Reply with this actor to establish connection - log.console (json.encode($_)) - send(e, $_); - log.console("Portal replied with server actor"); - }, 5678); -} - -function joinServer() { - gameState = 'searching'; - updateTitle(); - - function contact_fn(actor, reason) { - log.console("CONTACTED!", actor ? "SUCCESS" : "FAILED", reason); - if (actor) { - opponent = actor; - log.console("Connection established with server, sending join request"); - - // Send a greet message with our actor object - send(opponent, { - type: 'greet', - client_actor: $_ - }); - } else { - log.console(`Failed to connect: ${json.encode(reason)}`); - gameState = 'waiting'; - updateTitle(); - } - } - - $_.contact(contact_fn, { - address: "192.168.0.149", - port: 5678 - }); -} - -$_.receiver(e => { - if (e.kind == 'update') - send(e, update(e.dt)) - else if (e.kind == 'draw') - send(e, draw()) - else if (e.type === 'game_start' || e.type === 'move' || e.type === 'greet') - log.console("Receiver got message:", e.type, e); - - if (e.type === 'greet') { - log.console("Server received greet from client"); - // Store the client's actor object for ongoing communication - opponent = e.client_actor; - log.console("Stored client actor:", json.encode(opponent)); - gameState = 'connected'; - updateTitle(); - - // Send game_start to the client - log.console("Sending game_start to client"); - send(opponent, { - type: 'game_start', - your_color: 'black' - }); - log.console("game_start message sent to client"); - } - else if (e.type === 'game_start') { - log.console("Game starting, I am:", e.your_color); - myColor = e.your_color; - isMyTurn = (myColor === 'white'); - gameState = 'connected'; - updateTitle(); - } else if (e.type === 'move') { - log.console("Received move from opponent:", e.from, "to", e.to); - // Apply opponent's move - var fromCell = grid.at(e.from); - if (fromCell.length) { - var piece = fromCell[0]; - if (mover.tryMove(piece, e.to)) { - isMyTurn = true; // It's now our turn - updateTitle(); - log.console("Applied opponent move, now my turn"); - } else { - log.console("Failed to apply opponent move"); - } - } else { - log.console("No piece found at from position"); - } - } else if (e.type === 'mouse_move') { - // Update opponent's mouse position - opponentMousePos = e.pos; - opponentHoldingPiece = e.holding; - opponentSelectPos = e.selectPos; - } else if (e.type === 'piece_pickup') { - // Opponent picked up a piece - opponentSelectPos = e.pos; - opponentHoldingPiece = true; - } else if (e.type === 'piece_drop') { - // Opponent dropped their piece - opponentHoldingPiece = false; - opponentSelectPos = null; - } else if (e.type === 'mouse_button_down') { - handleMouseButtonDown(e) - } else if (e.type === 'mouse_button_up') { - handleMouseButtonUp(e) - } else if (e.type === 'mouse_motion') { - handleMouseMotion(e) - } else if (e.type === 'key_down') { - handleKeyDown(e) - } -}) diff --git a/prosperon/main.js b/prosperon/main.js deleted file mode 100644 index dc9728e6..00000000 --- a/prosperon/main.js +++ /dev/null @@ -1,354 +0,0 @@ -/** - * Moth Game Framework - * Higher-level game development framework built on top of Prosperon - */ - -var os = use('os'); -var io = use('io'); -var transform = use('transform'); -var rasterize = use('rasterize'); -var video_actor = use('sdl_video') -var input = use('input') - -input.watch($_) - -var geometry = use('geometry') - -function worldToScreenRect({x,y,width,height}, camera, winW, winH) { - var bl = worldToScreenPoint([x,y], camera, winW, winH) - var tr = worldToScreenPoint([x+width, y+height], camera, winW, winH) - - return { - x: Math.min(bl.x, tr.x), - y: Math.min(bl.y, tr.y), - width: Math.abs(tr.x - bl.x), - height: Math.abs(tr.y - bl.y) - } -} - -function worldToScreenPoint([wx, wy], camera, winW, winH) { - // 1) world‐window origin (bottom‐left) - const worldX0 = camera.pos[0] - camera.size[0] * camera.anchor[0]; - const worldY0 = camera.pos[1] - camera.size[1] * camera.anchor[1]; - - // 2) normalized device coords [0..1] - const ndcX = (wx - worldX0) / camera.size[0]; - const ndcY = (wy - worldY0) / camera.size[1]; - - // 3) map into pixel‐space via the fractional viewport - const px = camera.viewport.x * winW - + ndcX * (camera.viewport.width * winW); - const py = camera.viewport.y * winH - + (1 - ndcY) * (camera.viewport.height * winH); - - return [ px, py ]; -} - -function screenToWorldPoint([px, py], camera, winW, winH) { - // 1) undo pixel→NDC within the camera’s viewport - const ndcX = (px - camera.viewport.x * winW) - / (camera.viewport.width * winW) - const ndcY = 1 - (py - camera.viewport.y * winH) - / (camera.viewport.height * winH) - - // 2) compute the world‐window origin (bottom‐left) - const worldX0 = camera.pos[0] - - camera.size[0] * camera.anchor[0] - const worldY0 = camera.pos[1] - - camera.size[1] * camera.anchor[1] - - // 3) map NDC back to world coords - return [ - ndcX * camera.size[0] + worldX0, - ndcY * camera.size[1] + worldY0 - ] -} - -var camera = { - size: [500,500],//{width:500,height:500}, // pixel size the camera "sees", like its resolution - pos: [250,250],//{x:0,y:0}, // where it is - fov:50, - near_z:0, - far_z:1000, - viewport: {x:0,y:0,width:1,height:1}, // viewport it appears on screen - ortho:true, - anchor:[0.5,0.5],//{x:0.5,y:0.5}, - surface: undefined -} - -var util = use('util') -var cammy = util.camera_globals(camera) - -var graphics - -var window -var render - -var gameactor - -var dir = args[0] - -if (!io.exists(args[0] + '/main.js')) - throw Error(`No main.js found in ${args[0]}`) - -log.spam('Starting game in ' + dir) - -io.mount(dir) - -$_.start(e => { - if (gameactor) return - gameactor = e.actor - loop() -}, args[0] + "/main.js") - -send(video_actor, { - kind: "window", - op:"create", - data: { - title: "Moth Test", - width: 500, - height: 500 - } -}, e => { - if (e.error) { - log.error(e.error) - os.exit(1) - } - - window = e.id - - send(video_actor,{ - kind:"window", - op:"makeRenderer", - id:window - }, e => { - if (e.error) { - log.error(e.error) - os.exit(1) - } - - render = e.id - graphics = use('graphics', video_actor, e.id) - }) -}) - -var last = os.now() - -// FPS tracking -var fps_samples = [] -var fps_sample_count = 60 -var fps_sum = 0 - -var images = {} - -// Convert high-level draw commands to low-level renderer commands -function translate_draw_commands(commands) { - if (!graphics) return - var renderer_commands = [] - - commands.forEach(function(cmd) { - if (cmd.material && cmd.material.color) { - renderer_commands.push({ - op: "set", - prop: "drawColor", - value: cmd.material.color - }) - } - - switch(cmd.cmd) { - case "draw_rect": - cmd.rect = worldToScreenRect(cmd.rect, camera,500, 500) - // Handle rectangles with optional rounding and thickness - if (cmd.opt && cmd.opt.radius && cmd.opt.radius > 0) { - // Rounded rectangle - var thickness = (cmd.opt.thickness === 0) ? 0 : (cmd.opt.thickness || 1) - var raster_result = rasterize.round_rect(cmd.rect, cmd.opt.radius, thickness) - - if (raster_result.type === 'rect') { - renderer_commands.push({ - op: "fillRect", - data: {rect: raster_result.data} - }) - } else if (raster_result.type === 'rects') { - raster_result.data.forEach(function(rect) { - renderer_commands.push({ - op: "fillRect", - data: {rect: rect} - }) - }) - } - } else if (cmd.opt && cmd.opt.thickness && cmd.opt.thickness > 0) { - // Outlined rectangle - var raster_result = rasterize.outline_rect(cmd.rect, cmd.opt.thickness) - - if (raster_result.type === 'rect') { - renderer_commands.push({ - op: "fillRect", - data: {rect: raster_result.data} - }) - } else if (raster_result.type === 'rects') { - renderer_commands.push({ - op: "rects", - data: {rects: raster_result.data} - }) - } - } else { - renderer_commands.push({ - op: "fillRect", - data: {rect: cmd.rect} - }) - } - break - - case "draw_circle": - case "draw_ellipse": - cmd.pos = worldToScreenPoint(cmd.pos, camera, 500, 500) - // Rasterize ellipse to points or rects - var radii = cmd.radii || [cmd.radius, cmd.radius] - var raster_result = rasterize.ellipse(cmd.pos, radii, cmd.opt || {}) - - if (raster_result.type === 'points') { - renderer_commands.push({ - op: "point", - data: {points: raster_result.data} - }) - } else if (raster_result.type === 'rects') { - // Use 'rects' operation for multiple rectangles - renderer_commands.push({ - op: "rects", - data: {rects: raster_result.data} - }) - } - break - - case "draw_line": - renderer_commands.push({ - op: "line", - data: {points: cmd.points.map(p => worldToScreenPoint(p, camera, 500, 500))} - }) - break - - case "draw_point": - cmd.pos = worldToScreenPoint(cmd.pos, camera, 500, 500) - renderer_commands.push({ - op: "point", - data: {points: [cmd.pos]} - }) - break - - case "draw_image": - var img = graphics.texture(cmd.image) - if (!img.gpu) break - - cmd.rect.width ??= img.width - cmd.rect.height ??= img.height - cmd.rect = worldToScreenRect(cmd.rect, camera, 500, 500) - - renderer_commands.push({ - op: "texture", - data: { - texture_id: img.gpu.id, - dst: cmd.rect, - src: {x:0,y:0,width:img.width,height:img.height}, - } - }) - break - - case "draw_text": - if (!cmd.text) break - if (!cmd.pos) break - var rect = worldToScreenRect({x:cmd.pos.x, y:cmd.pos.y, width:8, height:8}, camera, 500,500) - var pos = {x: rect.x, y: rect.y} - renderer_commands.push({ - op: "debugText", - data: { - pos, - text: cmd.text - } - }) - break - } - }) - - return renderer_commands -} - -function loop() -{ - os.frame() - var now = os.now() - var dt = now - last - last = now - - // Update the game - send(gameactor, {kind:'update', dt:dt}, e => { - // Get draw commands from game - send(gameactor, {kind:'draw'}, draw_commands => { - var batch_commands = [] - - batch_commands.push({ - op: "set", - prop: "drawColor", - value: [0.1,0.1,0.15,1] - }) - - // Clear the screen - batch_commands.push({ - op: "clear" - }) - - if (draw_commands && draw_commands.length > 0) { - var renderer_commands = translate_draw_commands(draw_commands) - batch_commands = batch_commands.concat(renderer_commands) - } - - batch_commands.push({ - op: "present" - }) - - send(video_actor, { - kind: "renderer", - id: render, - op: "batch", - data: batch_commands - }, _ => { - var diff = os.now() - now - - // Calculate and track FPS - var frame_time = os.now() - last - if (frame_time > 0) { - var current_fps = 1 / frame_time - - // Add to samples - fps_samples.push(current_fps) - fps_sum += current_fps - - // Keep only the last N samples - if (fps_samples.length > fps_sample_count) { - fps_sum -= fps_samples.shift() - } - - // Calculate average FPS - var avg_fps = fps_sum / fps_samples.length - } - - loop() - }) - }) - }) -} - -$_.receiver(e => { - if (e.type === 'quit') - $_.stop() - - if (e.type.includes('mouse')) { - if (e.pos) - e.pos = screenToWorldPoint(e.pos, camera, 500, 500) - - if (e.d_pos) - e.d_pos.y *= -1 - } - - send(gameactor, e) -}) \ No newline at end of file diff --git a/prosperon/main.ce b/prosperon/prosperon.ce similarity index 99% rename from prosperon/main.ce rename to prosperon/prosperon.ce index b07ee356..f27ae36f 100644 --- a/prosperon/main.ce +++ b/prosperon/prosperon.ce @@ -305,7 +305,6 @@ function loop(time) } $_.receiver(e => { - log.console(json.encode(e)) if (e.type === 'quit') $_.stop() diff --git a/scripts/engine.cm b/scripts/engine.cm index 7248e690..f4363a11 100644 --- a/scripts/engine.cm +++ b/scripts/engine.cm @@ -778,9 +778,17 @@ var progPath = cell.args.program if (io.exists(progPath + ACTOR_EXT) && !io.is_directory(progPath + ACTOR_EXT)) { prog = progPath + ACTOR_EXT } else if (io.exists(progPath) && io.is_directory(progPath)) { - var mainPath = progPath + '/main' + ACTOR_EXT - if (io.exists(mainPath) && !io.is_directory(mainPath)) { - prog = mainPath + // First check for folder's name as a file + var folderName = progPath.split('/').pop() + var folderNamePath = progPath + '/' + folderName + ACTOR_EXT + if (io.exists(folderNamePath) && !io.is_directory(folderNamePath)) { + prog = folderNamePath + } else { + // Fall back to main.ce + var mainPath = progPath + '/main' + ACTOR_EXT + if (io.exists(mainPath) && !io.is_directory(mainPath)) { + prog = mainPath + } } } diff --git a/source/cell.c b/source/cell.c index 6fc15258..ad59f3c7 100644 --- a/source/cell.c +++ b/source/cell.c @@ -1584,7 +1584,7 @@ int main(int argc, char **argv) root_cell = create_actor(startwota.data); /* Launch runner threads */ - for (int i = 0; i < cores; i++) { + for (int i = 0; i < cores-1; i++) { // -1 to keep the main thread free char threadname[128]; snprintf(threadname, sizeof(threadname), "actor runner %d", i); SDL_Thread *thread = SDL_CreateThread(crank_actor, threadname, NULL); diff --git a/source/qjs_sdl.c b/source/qjs_sdl.c index 4493f11e..ed0f7fc6 100644 --- a/source/qjs_sdl.c +++ b/source/qjs_sdl.c @@ -108,9 +108,8 @@ JSC_CCALL(input_watch, } } - if (!already_watching) { + if (!already_watching) arrput(event_watchers, strdup(actor->id)); - } SDL_UnlockMutex(event_watchers_mutex);