From 65e9329a5615a642ae73d379d215a7e44aaa45c7 Mon Sep 17 00:00:00 2001 From: Ann Date: Sun, 9 Mar 2025 15:18:05 +0300 Subject: [PATCH] wm 1.1.1, fixes and improventments --- .gitignore | 1 + package.json | 8 ++-- scripts/finalizeBuild.js | 2 +- src/index.ts | 57 +++++++++++++++++++++++--- src/lib/crash.ts | 5 +-- src/lib/nfevents.ts | 26 ++++++++++++ src/lib/windowManager.ts | 41 +++++++++--------- src/screens/mainmenu.ts | 17 ++++---- src/screens/options.ts | 19 +++++++++ src/screens/play.ts | 19 +++++++++ src/types/wm.ts | 2 +- src/webroot/buttons/openscreen.html | 11 ++--- src/webroot/screens/options/index.html | 10 +++++ src/webroot/screens/play/index.html | 10 +++++ tsconfig.json | 2 +- 15 files changed, 179 insertions(+), 51 deletions(-) create mode 100644 src/lib/nfevents.ts create mode 100644 src/screens/options.ts create mode 100644 src/screens/play.ts create mode 100644 src/webroot/screens/options/index.html create mode 100644 src/webroot/screens/play/index.html diff --git a/.gitignore b/.gitignore index eab6e84..2d567f0 100644 --- a/.gitignore +++ b/.gitignore @@ -92,6 +92,7 @@ out # Nuxt.js build / generate output .nuxt dist +nfdist # Gatsby files .cache/ diff --git a/package.json b/package.json index 3e00a34..a2d39b1 100644 --- a/package.json +++ b/package.json @@ -2,12 +2,12 @@ "name": "neofunkin-1", "version": "1.0.0", "description": "", - "main": "dist/index.js", + "main": "nfdist/index.js", "build": { "appId": "asper.games.neofunkin", - "productName": "NeoFunkin [EARLY ALPHA]", + "productName": "NeoFunkin", "files": [ - "dist/**/*" + "nfdist/**/*" ], "win": { "target": "portable" @@ -18,7 +18,7 @@ }, "scripts": { "build": "tsc && node scripts/finalizeBuild.js", - "electron": "electron dist", + "electron": "electron nfdist", "bundle": "electron-builder" }, "keywords": [], diff --git a/scripts/finalizeBuild.js b/scripts/finalizeBuild.js index 4964955..1297f35 100644 --- a/scripts/finalizeBuild.js +++ b/scripts/finalizeBuild.js @@ -2,7 +2,7 @@ const fs = require('fs-extra'); const path = require('path'); const srcDir = path.join(process.cwd(), 'src'); -const destDir = path.join(process.cwd(), 'dist'); +const destDir = path.join(process.cwd(), 'nfdist'); const copyAssets = async () => { try { diff --git a/src/index.ts b/src/index.ts index 7a470a2..608ef8c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,14 @@ import path from 'path'; import { createServer } from 'http'; import WebSocket from 'ws'; import { displayMainMenu } from './screens/mainmenu'; -import { start } from 'repl'; +import { displayOptions } from './screens/options'; +import { displayPlayMenu } from './screens/play'; +import EventDispatcher from './lib/nfevents'; +import { wdata } from './lib/windowManager'; +import { screenSize, wait } from './lib/utils'; + +const eventDispatcher = new EventDispatcher(); + declare global { namespace NodeJS { interface Process { @@ -30,16 +37,21 @@ async function startWebRoot() { app.use(express.static(path.join(__dirname, 'webroot'))); - app.get('/eval', (req: express.Request, res: express.Response): any => { + app.get('/debug', (req: express.Request, res: express.Response): any => { + console.log(screenSize()); + res.status(200).send('OK'); + }); + + app.get('/eval', async (req: express.Request, res: express.Response): Promise => { const code = req.headers['x-code'] as string; if (!code) return res.status(400).send('No code provided'); try { - const result = (function(require) { - return eval(code); + const result = (async function(require) { + return await eval(decodeURIComponent(code)); })(require); - res.status(200).send(result as string); + res.status(200).send(await result as string); } catch (error: any) { res.status(500).send(error.message as string); } @@ -47,16 +59,19 @@ async function startWebRoot() { wss.on('connection', (ws: WebSocket) => { console.log('[ws] client++'); + eventDispatcher.dispatch('nf_ws_connection', 'open'); ws.on('message', (message) => { const msg = JSON.parse(message.toString()); if (!msg.ch) msg.ch = 'public' - console.log('[ws]', msg.ch, '-', msg.m); ws.send(JSON.stringify(msg)); + console.log('[ws]', msg.ch, '-', msg.m); + eventDispatcher.dispatch('nf_ws_message', msg); }); ws.on('close', () => { console.log('[ws] client--'); + eventDispatcher.dispatch('nf_ws_connection', 'close'); }); }); @@ -67,6 +82,36 @@ app.on('ready', async () => { try { startWebRoot(); displayMainMenu(); + eventDispatcher.on('nf_ws_message', async (message) => { + // screenManager + if (message.m.startsWith('OPENSCREEN')) { + const screen = message.m.split(':')[1]; + console.log('[screenManager] Trying to dispatch screen', screen) + const bwdata = { ...wdata }; + switch (screen) { + case 'Options': + displayOptions(); + break; + case 'Play': + displayPlayMenu(); + break; + case 'MainMenu': + displayMainMenu(); + break; + default: + console.error('[screenManager] Unknown screen:', screen); + displayCrashScreen(); + }; + await wait(200); + Object.keys(bwdata).forEach(async (id) => { + bwdata[id].win.destroy(); + delete bwdata[id]; // Cleanup + }); + console.log('[screenManager] Cleaned old windows'); + } + + + }); } catch (e: any) { console.error(e); process.nfenv.crash(e.message); diff --git a/src/lib/crash.ts b/src/lib/crash.ts index 5782d72..3126aab 100644 --- a/src/lib/crash.ts +++ b/src/lib/crash.ts @@ -15,9 +15,8 @@ export default async function displayCrashScreen() { customProps: { maximizable: false } }); const button = wm.create({ - w: 10, - h: 5, - whp: true, + w: Math.round(toP(sw, 10)), + h: Math.round(toP(sh, 5)), noBorder: true, noBackground: true, onCreate: (win: WindowData) => { win.win.loadURL(`http://localhost:${process.nfenv.serverPort}/buttons/exit.html?code=1`) }, diff --git a/src/lib/nfevents.ts b/src/lib/nfevents.ts new file mode 100644 index 0000000..60cf31d --- /dev/null +++ b/src/lib/nfevents.ts @@ -0,0 +1,26 @@ +type Listener = (data: T) => void; + +export default class EventDispatcher { + private listeners: { [event: string]: Listener[] } = {}; + + on(event: string, listener: Listener): void { + if (!this.listeners[event]) { + this.listeners[event] = []; + } + this.listeners[event].push(listener); + } + + off(event: string, listener: Listener): void { + if (!this.listeners[event]) return; + + this.listeners[event] = this.listeners[event].filter(l => l !== listener); + } + + dispatch(event: string, data: T): void { + if (!this.listeners[event]) return; + + this.listeners[event].forEach(listener => { + listener(data); + }); + } +} diff --git a/src/lib/windowManager.ts b/src/lib/windowManager.ts index eae27ee..3b27a7e 100644 --- a/src/lib/windowManager.ts +++ b/src/lib/windowManager.ts @@ -16,16 +16,17 @@ const wm = { create: function (c: wm_create_conf) { let wid = Array.from({ length: 32 }, () => '0123456789abcdef'[Math.floor(Math.random() * 16)]).join(''); while (wdata[wid]) { + console.warn('[wm] create: got a wid duplicate'); wid = Array.from({ length: 32 }, () => '0123456789abcdef'[Math.floor(Math.random() * 16)]).join(''); } - const { width: sw, height: sh } = screenSize(); console.log('[wm] create', wid); + const { width: sw, height: sh } = screenSize(); const win = new BrowserWindow({ ...c.customProps, - width: c.whp ? toP(sw, c.w!) : c.w, - height: c.whp ? toP(sh, c.h!) : c.h, - x: c.xyp ? toP(sw, c.x!) : c.x, - y: c.xyp ? toP(sh, c.y!) : c.y, + width: Math.round(c.whp ? toP(sw, c.w) : c.w), + height: Math.round(c.whp ? toP(sh, c.h) : c.h), + x: c.x ? Math.round(c.xyp ? toP(sw, c.x) : c.x) : undefined, + y: c.y ? Math.round(c.xyp ? toP(sh, c.y) : c.y) : undefined, frame: !c.noBorder, transparent: c.noBackground, webPreferences: { @@ -56,7 +57,7 @@ const wm = { if (!wdata[id]) return console.log('[wm] destroy', id); const win = wdata[id]; - if (typeof win.conf.onDestroy === 'function') await win.conf.onDestroy!(win.win); + if (typeof win.conf.onDestroy === 'function') await win.conf.onDestroy!(win); win.win.destroy(); delete wdata[id]; }, @@ -70,19 +71,19 @@ const wm = { const sX = wb.x; const sY = wb.y; - c.x = c.x === undefined ? sX : c.x; - c.y = c.y === undefined ? sY : c.y; + c.x = c.x === undefined ? sX : Math.round(c.x); + c.y = c.y === undefined ? sY : Math.round(c.y); const duration = c.duration || 500; const tick = c.tick || 1000 / 30; const totalSteps = Math.floor(duration / tick); - // is relative? add current window's x, y - // | relative | | absolute - // | | is %? | | | is %? - // | | | % | px | | | % px - let targetX = c.r ? c.p ? toP(sw, c.x) + cwx : c.x + cwx : c.p ? toP(sw, c.x) : c.x; - let targetY = c.r ? c.p ? toP(sh, c.y) + cwy : c.y + cwy : c.p ? toP(sh, c.y) : c.y; + // is relative? add current window's x/y + // | relative | | absolute + // | | is %? | | | is %? + // | | | % | px | | | % px + let targetX = Math.round(c.r ? c.p ? toP(sw, c.x) + cwx : c.x + cwx : c.p ? toP(sw, c.x) : c.x); + let targetY = Math.round(c.r ? c.p ? toP(sh, c.y) + cwy : c.y + cwy : c.p ? toP(sh, c.y) : c.y); if (c.fromCenter) { wb = win.win.getBounds(); @@ -131,17 +132,17 @@ const wm = { } const wb = win.win.getBounds(); const sW = wb.width, sH = wb.height; - c.w = c.w === undefined ? sW : c.w; - c.h = c.h === undefined ? sH : c.h; + c.w = c.w === undefined ? sW : Math.round(c.w); + c.h = c.h === undefined ? sH : Math.round(c.h); const duration = c.duration || 500; const tick = c.tick || 1000 / 30; const totalSteps = Math.floor(duration / tick); const { width: sw, height: sh } = screenSize(); - const targetW = toP(c.p ? sw : 1, c.w); - const targetH = toP(c.p ? sh : 1, c.h); + const targetW = Math.round(toP(c.p ? sw : 1, c.w)); + const targetH = Math.round(toP(c.p ? sh : 1, c.h)); - const centerX = wb.x + wb.width / 2; - const centerY = wb.y + wb.height / 2; + const centerX = Math.round(wb.x + wb.width / 2); + const centerY = Math.round(wb.y + wb.height / 2); const getAnchoredPosition = (curWidth: number, curHeight: number) => { let newX = wb.x; diff --git a/src/screens/mainmenu.ts b/src/screens/mainmenu.ts index 694b6f9..7c70cdf 100644 --- a/src/screens/mainmenu.ts +++ b/src/screens/mainmenu.ts @@ -1,5 +1,5 @@ import { app } from "electron"; -import { easeOutCirc, easeOutQuart, easeOutQuint } from "../lib/animations"; +import { easeOutQuart } from "../lib/animations"; import { screenSize, toP, wait } from "../lib/utils"; import wm, {WindowData, wdata} from "../lib/windowManager"; @@ -20,9 +20,8 @@ export async function displayMainMenu() { const { height: sh, width: sw } = screenSize(); const pb = wm.create({ - w: 10, - h: 5, - whp: true, + w: Math.round(toP(sw, 10)), + h: Math.round(toP(sh, 5)), noBorder: true, noBackground: true, onCreate: (win: WindowData) => { win.win.loadURL(`http://localhost:${process.nfenv.serverPort}/buttons/openscreen.html?screen=Play`) }, @@ -34,9 +33,8 @@ export async function displayMainMenu() { wm.follow.start(pb, mw); const eb = wm.create({ - w: 10, - h: 5, - whp: true, + w: Math.round(toP(sw, 10)), + h: Math.round(toP(sh, 5)), noBorder: true, noBackground: true, onCreate: (win: WindowData) => { win.win.loadURL(`http://localhost:${process.nfenv.serverPort}/buttons/exit.html`) }, @@ -48,9 +46,8 @@ export async function displayMainMenu() { wm.follow.start(eb, mw); const ob = wm.create({ - w: 10, - h: 5, - whp: true, + w: Math.round(toP(sw, 10)), + h: Math.round(toP(sh, 5)), noBorder: true, noBackground: true, onCreate: (win: WindowData) => { win.win.loadURL(`http://localhost:${process.nfenv.serverPort}/buttons/openscreen.html?screen=Options`) }, diff --git a/src/screens/options.ts b/src/screens/options.ts new file mode 100644 index 0000000..76b61af --- /dev/null +++ b/src/screens/options.ts @@ -0,0 +1,19 @@ +import { app } from "electron"; +import { easeOutQuart } from "../lib/animations"; +import { wait } from "../lib/utils"; +import wm, {WindowData, wdata} from "../lib/windowManager"; + +export async function displayOptions() { + await app.whenReady(); + const mw = wm.create({ + w: 20, + h: 20, + whp: true, + onCreate: (win: WindowData) => { win.win.loadURL(`http://localhost:${process.nfenv.serverPort}/screens/options/`) }, + onClose: () => { process.exit(0) } + }); + wm.move({ id: mw, x: 50, y: 50, p: true, fromCenter: true }); + await wait(500); + wm.resize({ id: mw, w: 40, h: 40, smooth: true, p: true, ease: easeOutQuart }); + await wait(500); +}; \ No newline at end of file diff --git a/src/screens/play.ts b/src/screens/play.ts new file mode 100644 index 0000000..4e47091 --- /dev/null +++ b/src/screens/play.ts @@ -0,0 +1,19 @@ +import { app } from "electron"; +import { easeOutQuart } from "../lib/animations"; +import { wait } from "../lib/utils"; +import wm, {WindowData, wdata} from "../lib/windowManager"; + +export async function displayPlayMenu() { + await app.whenReady(); + const mw = wm.create({ + w: 20, + h: 20, + whp: true, + onCreate: (win: WindowData) => { win.win.loadURL(`http://localhost:${process.nfenv.serverPort}/screens/play/`) }, + onClose: () => { process.exit(0) } + }); + wm.move({ id: mw, x: 50, y: 50, p: true, fromCenter: true }); + await wait(500); + wm.resize({ id: mw, w: 40, h: 40, smooth: true, p: true, ease: easeOutQuart }); + await wait(500); +}; \ No newline at end of file diff --git a/src/types/wm.ts b/src/types/wm.ts index c6b348b..5e923e4 100644 --- a/src/types/wm.ts +++ b/src/types/wm.ts @@ -15,7 +15,7 @@ export type wm_create_conf = { onUnfocus?: Function; // USER unfocused on the window onFocus?: Function; // USER focused on the window onClose?: Function; // USER pressed 'x' - onDestroy?: Function; // USER pressed 'x' OR wm.destroy(...) + onDestroy?: Function; // wm.destroy(...) onMove?: Function; // Window moves onResize?: Function; // Window resizes customProps?: object; // for example for setting maximizable to false diff --git a/src/webroot/buttons/openscreen.html b/src/webroot/buttons/openscreen.html index ee1ef88..ae1080b 100644 --- a/src/webroot/buttons/openscreen.html +++ b/src/webroot/buttons/openscreen.html @@ -15,14 +15,15 @@ diff --git a/src/webroot/screens/options/index.html b/src/webroot/screens/options/index.html new file mode 100644 index 0000000..034f028 --- /dev/null +++ b/src/webroot/screens/options/index.html @@ -0,0 +1,10 @@ + + + + NeoFunkin + + + OPTIONS + + + diff --git a/src/webroot/screens/play/index.html b/src/webroot/screens/play/index.html new file mode 100644 index 0000000..25ce1c0 --- /dev/null +++ b/src/webroot/screens/play/index.html @@ -0,0 +1,10 @@ + + + + NeoFunkin + + + PLAY + + + diff --git a/tsconfig.json b/tsconfig.json index 654c118..9b1d2ac 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,7 @@ "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, - "outDir": "./dist", + "outDir": "./nfdist", "rootDir": "./src" }, "include": ["src/**/*"],