267 Commits

Author SHA1 Message Date
John Alanbrook
94394c6845 initial attempt 2025-06-21 13:12:23 -05:00
John Alanbrook
b33c7adb07 remove string/number auto type coercion 2025-06-19 23:02:51 -05:00
John Alanbrook
6eb33b8e48 remove unneeded object properties and if 0 code 2025-06-19 08:46:40 -05:00
John Alanbrook
3d5f345236 all compilation is strict 2025-06-18 16:37:55 -05:00
John Alanbrook
4689ea1167 remove module eval branches 2025-06-18 14:10:51 -05:00
John Alanbrook
15d85096a2 remove global symbols 2025-06-18 14:03:12 -05:00
John Alanbrook
f2c2ecf692 remove all private stuff 2025-06-18 13:59:47 -05:00
John Alanbrook
458215f838 remove arguments object 2025-06-18 13:24:22 -05:00
John Alanbrook
7f002e306d remove reflect 2025-06-18 08:46:42 -05:00
John Alanbrook
91a3fef065 finish removing big int stuff; fixes memory leak 2025-06-17 21:36:06 -05:00
John Alanbrook
843b4bd8a8 remove usage of map 2025-06-17 21:16:35 -05:00
John Alanbrook
41fdf49df5 remove bigint 2025-06-17 15:02:40 -05:00
John Alanbrook
c9adbed3ff remove undefined; only use null now 2025-06-17 14:32:27 -05:00
John Alanbrook
3459d85a82 binary logic only works on ints; returns null otherwise 2025-06-17 12:54:20 -05:00
John Alanbrook
9ecdaae7a7 simpler addition rule 2025-06-16 20:40:41 -05:00
John Alanbrook
43b55b29f3 comparing anything but two numbers or two strings is an error 2025-06-16 18:45:57 -05:00
John Alanbrook
a88cee7fae Remove weak-everything. remove throwing on type differences in equality; remove using 'def' as some variable names 2025-06-16 17:02:24 -05:00
John Alanbrook
8b3f5476a9 remove module checks 2025-06-16 15:56:04 -05:00
John Alanbrook
233f59e04a clean up unused functions 2025-06-16 15:49:54 -05:00
John Alanbrook
7b16259c00 Merge branch 'js-rm-eq' into js-rm-modules 2025-06-16 14:20:23 -05:00
John Alanbrook
43baa23dfe Merge remote-tracking branch 'origin/js-rm-htmldda' into js-rm-modules 2025-06-16 14:18:40 -05:00
John Alanbrook
a9ebea9f26 Merge remote-tracking branch 'origin/js-def' into js-rm-modules 2025-06-16 14:16:01 -05:00
John Alanbrook
19b729afbf Merge remote-tracking branch 'origin/js-rm-with' into js-rm-modules 2025-06-16 14:09:29 -05:00
John Alanbrook
47729c225f Merge branch 'js-rm-ta' into js-rm-modules 2025-06-16 14:04:06 -05:00
John Alanbrook
ea6cf5db49 Merge remote-tracking branch 'origin/js-rm-class' into js-rm-modules 2025-06-16 14:01:12 -05:00
John Alanbrook
794baf8598 remove dynamic equality 2025-06-16 13:48:09 -05:00
John Alanbrook
a551368681 def now works as const 2025-06-16 08:07:45 -05:00
John Alanbrook
c20ca8c937 initial attempt 2025-06-15 19:48:48 -05:00
John Alanbrook
0217cf0da6 remove with 2025-06-15 19:40:58 -05:00
John Alanbrook
6bd7251933 initial rm 2025-06-15 10:30:58 -05:00
John Alanbrook
6dc8d97001 remove htmldda 2025-06-15 09:47:09 -05:00
John Alanbrook
9c0565d34f montecarlo faster; rm qjs-layout 2025-06-15 05:18:22 -05:00
John Alanbrook
0702e3495d finish remove? 2025-06-15 05:15:51 -05:00
John Alanbrook
108c39d22d initial attempt 2025-06-14 20:40:28 -05:00
John Alanbrook
946ebe5cd7 remove date intrinsic 2025-06-14 18:04:27 -05:00
John Alanbrook
fa12281ab9 add benchmarks 2025-06-14 17:03:49 -05:00
John Alanbrook
38a52fcb73 add tilemap 2025-06-13 20:48:33 -05:00
John Alanbrook
95a95e55e3 more details on cell 2025-06-13 20:42:53 -05:00
John Alanbrook
fc978a5766 add cell doc 2025-06-11 16:35:46 -05:00
John Alanbrook
9082ee2c47 move quickjs into source 2025-06-11 01:18:26 -05:00
John Alanbrook
b8ad8431f4 optimize 2025-06-11 01:13:58 -05:00
John Alanbrook
1c2b8228fe add num 2025-06-10 04:33:15 -05:00
John Alanbrook
a274fb174f faster text conversion to hex; guid generation now dealt with with -u.random_fit and blob formation; mersenne twister used for -u.random functions 2025-06-08 13:42:20 -05:00
John Alanbrook
3622a5ec58 actor messages now delivered as blobs 2025-06-08 11:06:59 -05:00
John Alanbrook
8a5f8a4d74 even faster wota encoding and decoding 2025-06-08 10:34:12 -05:00
John Alanbrook
c1d341eecd faster wota encoding 2025-06-08 08:35:12 -05:00
John Alanbrook
3176e6775d attempt for jswota in js 2025-06-08 00:49:19 -05:00
John Alanbrook
34dcd0a235 fix actors not running correctly 2025-06-07 23:35:47 -05:00
John Alanbrook
cbda7dfbc9 add utf8 and kim text encoder/decoders 2025-06-07 23:35:19 -05:00
John Alanbrook
d039e2cfe6 use spinlocks and other fixes 2025-06-07 17:09:03 -05:00
John Alanbrook
c02bd06ec0 signal kill works 2025-06-07 13:46:31 -05:00
John Alanbrook
efa63771e6 fix sockets 2025-06-07 12:24:34 -05:00
John Alanbrook
9f6d27fb3c fix multiple main thread actors not working 2025-06-06 21:26:29 -05:00
John Alanbrook
1a61ae6f77 actors keep running if they have messages, until no threads are available 2025-06-06 18:22:12 -05:00
John Alanbrook
83c816fd0e fix actors not freeing correctly if error in startup script 2025-06-06 17:42:28 -05:00
John Alanbrook
adbaa92dd5 fd now uses numbers to fix the inability to exit 2025-06-06 17:29:06 -05:00
John Alanbrook
580df9f233 add fstat; tests/cat as an example 2025-06-06 16:49:19 -05:00
John Alanbrook
d5d17560f9 add chunked reading; example with cat.ce 2025-06-06 16:15:39 -05:00
John Alanbrook
cd05ab97b5 Update agents directive 2025-06-06 14:59:30 -05:00
John Alanbrook
4eecbd692b fix actor not dying if killed while not in a turn 2025-06-06 13:59:14 -05:00
John Alanbrook
72beed7177 fix string leak with blob write_text 2025-06-06 13:58:46 -05:00
John Alanbrook
e0595de71a closes #19: kill underlings with system level interrupts 2025-06-06 12:24:32 -05:00
John Alanbrook
6687008d1a closes #14 2025-06-06 10:46:16 -05:00
John Alanbrook
5b9f1b8f51 closes #12 2025-06-06 10:31:41 -05:00
John Alanbrook
c570de7f41 closes #18: actor data now behind private symbol 2025-06-06 10:18:45 -05:00
John Alanbrook
d0138a6c23 ammend tests 2025-06-06 09:22:56 -05:00
John Alanbrook
29aa25e866 fix bug when calling unneeded inside of unneeded callback 2025-06-06 09:05:35 -05:00
John Alanbrook
ef28be93db fix js leak
Some checks failed
Build and Deploy / build-macos (push) Failing after 2s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-06-06 08:42:16 -05:00
John Alanbrook
0d7be6a94e attempt fix 2025-06-05 16:19:06 -05:00
John Alanbrook
4fe78c4a63 move timer to its own file 2025-06-05 13:34:50 -05:00
John Alanbrook
b52edb2746 fix updated render loop 2025-06-05 12:04:45 -05:00
John Alanbrook
79d5412fe6 massively simplify loop logic 2025-06-05 01:08:27 -05:00
John Alanbrook
fcec2cd1dc sockets and fd 2025-06-04 22:28:06 -05:00
John Alanbrook
2038ce15a7 factor out sdl input
Some checks failed
Build and Deploy / build-macos (push) Failing after 2s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-06-04 16:25:06 -05:00
John Alanbrook
08557011cb single threads; custom timer; letters
Some checks failed
Build and Deploy / build-macos (push) Failing after 2s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-06-04 14:39:58 -05:00
John Alanbrook
3e87bfd6cc add to look for same folder name as well as main
Some checks failed
Build and Deploy / build-macos (push) Failing after 2s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-06-04 13:22:58 -05:00
John Alanbrook
ef86dd3ecf remove some docstrings to save per actor memory 2025-06-03 23:58:44 -05:00
John Alanbrook
c887bcf7b9 no longer need to send ids to window renderer; per actor config 2025-06-03 16:13:54 -05:00
John Alanbrook
709f2459e4 add config options for ar timers 2025-06-03 14:33:51 -05:00
John Alanbrook
cdf8686c64 fix video start
Some checks failed
Build and Deploy / build-macos (push) Failing after 10s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-06-03 08:42:55 -05:00
John Alanbrook
2fdf74f6ee fix blob; throw on non stone reads; update blob test 2025-06-02 13:23:05 -05:00
John Alanbrook
e689679aac add checking for new mod versions 2025-06-02 12:12:05 -05:00
John Alanbrook
f70f65d1c3 add man files; add mod hash checking; add text decoding for blob 2025-06-02 11:10:18 -05:00
John Alanbrook
d9b316270d add text function 2025-06-02 10:35:30 -05:00
John Alanbrook
e2668b330e omit port when sending request if 80 or 443 2025-06-02 09:25:38 -05:00
John Alanbrook
90b5d1430f vendor qjs_miniz 2025-06-02 08:49:02 -05:00
John Alanbrook
7c47c43655 improve globfs performance 2025-06-02 08:48:49 -05:00
John Alanbrook
9e45219706 add clean command 2025-06-02 08:18:26 -05:00
John Alanbrook
d098800c88 fix path mounting 2025-06-02 08:18:08 -05:00
John Alanbrook
3a40076958 fix http; faster blob; list and help commands 2025-06-01 09:34:15 -05:00
John Alanbrook
06108df3d4 fix blob and http 2025-05-31 17:57:17 -05:00
John Alanbrook
a442cf5a4d update to new naming scheme 2025-05-31 16:45:56 -05:00
John Alanbrook
6dee29d213 Merge branch 'master' into modules 2025-05-31 15:58:06 -05:00
John Alanbrook
7711c644a0 congif from .cell/cell.toml
Some checks failed
Build and Deploy / build-macos (push) Failing after 10s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-31 15:55:24 -05:00
John Alanbrook
aab0a56349 fix routing 2025-05-31 15:32:30 -05:00
John Alanbrook
13245bbc98 register root actor 2025-05-31 09:02:53 -05:00
John Alanbrook
c25166d35a no more js leaking on free 2025-05-31 02:39:36 -05:00
John Alanbrook
fc09693c93 test program 2025-05-30 19:11:33 -05:00
John Alanbrook
b71c72db8b remove actors being created via cmd line args 2025-05-30 18:05:02 -05:00
John Alanbrook
66591e32b5 fixes #13: actor files can now be named use
Some checks failed
Build and Deploy / build-macos (push) Failing after 8s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-30 15:34:46 -05:00
John Alanbrook
fba05fa0fb update gitignore 2025-05-30 15:29:22 -05:00
John Alanbrook
11357d4fb5 actor files now .ce; module files now .cm; add toml encoder/decoder and test 2025-05-30 15:25:31 -05:00
John Alanbrook
674eb237e0 start of rename to cell 2025-05-30 12:07:03 -05:00
John Alanbrook
939269b060 initial modules attempt
Some checks failed
Build and Deploy / build-macos (push) Failing after 7s
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
2025-05-29 18:48:19 -05:00
John Alanbrook
f54200a7dd cwd works correctly for when running from a different folder
Some checks failed
Build and Deploy / build-macos (push) Failing after 7s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-29 17:59:33 -05:00
John Alanbrook
9ae2357493 new tracy build options 2025-05-29 14:11:47 -05:00
John Alanbrook
da525cd111 add compiling and saving bytecode 2025-05-29 13:55:03 -05:00
John Alanbrook
c3f07c0ef5 separate out cell stuff & prosperon stuff 2025-05-29 13:54:42 -05:00
John Alanbrook
2e7643aa2a add stone function 2025-05-29 11:28:51 -05:00
John Alanbrook
aca9baf585 add docstring symbol to C level actor 2025-05-29 10:29:57 -05:00
John Alanbrook
b4371ba3e0 improve time 2025-05-29 02:56:30 -05:00
John Alanbrook
4e118dd8e9 fix parseq and parseq test 2025-05-29 02:19:24 -05:00
John Alanbrook
9279e21b84 moth now filters events to the correct space 2025-05-29 01:21:45 -05:00
John Alanbrook
8d9bb4a2c9 use log instead of console 2025-05-29 00:39:14 -05:00
John Alanbrook
1040c61863 fix blob usage errors
Some checks failed
Build and Deploy / build-macos (push) Failing after 8s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-28 23:56:18 -05:00
John Alanbrook
e86bdf52fe switch to blobs from arraybuffers
Some checks failed
Build and Deploy / build-linux (push) Failing after 1m29s
Build and Deploy / build-macos (push) Failing after 7s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
2025-05-28 22:33:32 -05:00
John Alanbrook
53b3f0af9c fix mutex race
Some checks failed
Build and Deploy / build-linux (push) Failing after 1m27s
Build and Deploy / build-macos (push) Failing after 8s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
2025-05-28 18:50:39 -05:00
John Alanbrook
09f48d08b9 update chess to use moth
Some checks failed
Build and Deploy / build-macos (push) Failing after 7s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-28 17:49:37 -05:00
John Alanbrook
4eb592b740 moth handles camera now 2025-05-28 16:47:27 -05:00
John Alanbrook
c603e8f006 separate out blob and quickjs hooks into a blob.h header 2025-05-28 14:55:35 -05:00
John Alanbrook
f334a2ad56 expand qjs_blob 2025-05-28 14:38:43 -05:00
John Alanbrook
a39f287a88 remove prosperon.on and prosperon.dispatch 2025-05-28 13:51:58 -05:00
John Alanbrook
758b3e4704 remove tracy if not specified on cmd line 2025-05-28 13:16:08 -05:00
John Alanbrook
aa70dcbdc2 update 2025-05-28 02:28:20 -05:00
John Alanbrook
3667d53eae tracy is cell level now 2025-05-27 17:06:03 -05:00
John Alanbrook
01df337ccc draw2d now generates high level commands; turned into instructions by moth 2025-05-27 13:46:56 -05:00
John Alanbrook
ad182d68ec announce useful functions in qjs_common.h and remove externs 2025-05-27 10:23:07 -05:00
John Alanbrook
f7dcc8f57c correct pixelformat bug 2025-05-27 10:03:40 -05:00
John Alanbrook
f73f738459 add fd module 2025-05-27 01:52:27 -05:00
John Alanbrook
bf74a3c7d4 move keyboard and mouse functions that are main thread only to sdl_video
Some checks failed
Build and Deploy / build-macos (push) Failing after 7s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
2025-05-27 01:30:50 -05:00
John Alanbrook
e8fb50659d add colorspace support; fix webcam 2025-05-27 01:00:55 -05:00
John Alanbrook
00df0899fa add nv12 pixel 2025-05-26 23:37:10 -05:00
John Alanbrook
ae5ba67fc8 surface pixel handling
Some checks failed
Build and Deploy / build-macos (push) Failing after 6s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-26 22:43:50 -05:00
John Alanbrook
bc929988b2 extend camera
Some checks failed
Build and Deploy / build-macos (push) Failing after 7s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-26 20:55:57 -05:00
John Alanbrook
2346040d46 matching actor2js function; sdl_video returns actor now 2025-05-26 18:00:42 -05:00
John Alanbrook
2eb6b3e0b4 input now contains function to register any actor to OS events 2025-05-26 17:56:43 -05:00
John Alanbrook
2edcd89780 add sdl cursor support 2025-05-26 16:24:19 -05:00
John Alanbrook
a63e5c5b55 move surface to its own module 2025-05-26 15:59:28 -05:00
John Alanbrook
af21e10e97 draw textures with draw2d
Some checks failed
Build and Deploy / build-macos (push) Failing after 6s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-26 13:25:56 -05:00
John Alanbrook
1b97527120 draw2d uses object prototype for command creation 2025-05-26 12:53:41 -05:00
John Alanbrook
8074e2a82e draw2d now can send batches of draws to video backends 2025-05-26 12:48:19 -05:00
John Alanbrook
db1afb6477 drop message sends if message has no return instead of throw error
Some checks failed
Build and Deploy / build-macos (push) Failing after 6s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-26 09:27:58 -05:00
John Alanbrook
45311408d6 render command ops 2025-05-26 00:57:29 -05:00
John Alanbrook
1141fca63a add window message handling for sdl actor 2025-05-25 22:47:35 -05:00
John Alanbrook
7b70def11f more full window object
Some checks failed
Build and Deploy / build-windows (CLANG64) (push) Waiting to run
Build and Deploy / package-dist (push) Blocked by required conditions
Build and Deploy / deploy-itch (push) Blocked by required conditions
Build and Deploy / deploy-gitea (push) Blocked by required conditions
Build and Deploy / build-macos (push) Failing after 5s
Build and Deploy / build-linux (push) Failing after 1m27s
2025-05-25 21:55:21 -05:00
John Alanbrook
aac0c3813b window actor now created when use('sdl_video') is called 2025-05-25 19:06:24 -05:00
John Alanbrook
49786842f0 pull out sdl_video into its own module 2025-05-25 18:02:43 -05:00
John Alanbrook
9f9dfe03a6 removed internal functions from accessible via use
Some checks failed
Build and Deploy / build-macos (push) Failing after 5s
Build and Deploy / build-linux (push) Failing after 2m15s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
2025-05-25 11:51:01 -05:00
John Alanbrook
792da2ce4b Merge remote-tracking branch 'origin/misty'
Some checks failed
Build and Deploy / build-macos (push) Failing after 5s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-24 22:24:01 -05:00
John Alanbrook
0c9d78a3d3 initialize parts of SDL only when required, and return errors
Some checks failed
Build and Deploy / build-macos (push) Failing after 5s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-24 21:30:29 -05:00
John Alanbrook
e929f43a96 add steam module 2025-05-24 21:29:58 -05:00
John Alanbrook
01eff40690 auto latest docker image
Some checks failed
Build and Deploy / build-windows (CLANG64) (push) Waiting to run
Build and Deploy / package-dist (push) Blocked by required conditions
Build and Deploy / deploy-itch (push) Blocked by required conditions
Build and Deploy / deploy-gitea (push) Blocked by required conditions
Build and Deploy / build-macos (push) Failing after 5s
Build and Deploy / build-linux (push) Failing after 2m15s
2025-05-24 09:37:29 -05:00
John Alanbrook
23813a4c31 http downloading in chunks 2025-05-24 00:55:56 -05:00
John Alanbrook
1248b94244 remove curl and openssl dependencies
Some checks failed
Build and Deploy / build-macos (push) Failing after 5s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Successful in 1m44s
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
2025-05-24 00:07:37 -05:00
John Alanbrook
f754d91e14 fix compilation errors on windows and linux
Some checks failed
Build and Deploy / build-macos (push) Failing after 4s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Failing after 1m59s
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
2025-05-23 18:24:52 -05:00
John Alanbrook
7246016b8b example nat 2025-05-23 14:23:52 -05:00
John Alanbrook
b42eec96f6 separate the idea of misty actor and scene tree actor
Some checks failed
Build and Deploy / build-macos (push) Failing after 5s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Failing after 1m30s
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
2025-05-23 12:20:47 -05:00
John Alanbrook
efd98460c5 -u is no longer available as a global, only within the running actor code; enable passing args to use
Some checks failed
Build and Deploy / build-macos (push) Failing after 4s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-22 18:59:57 -05:00
John Alanbrook
698dbd81ae removed unneeded functions and split out actor specific functions 2025-05-22 18:16:51 -05:00
John Alanbrook
13c2a0ba0c sprites, rtrees, and transforms made with constructor functions 2025-05-22 17:00:31 -05:00
John Alanbrook
d0fdb469dd rtrees are now created as a constructor 2025-05-22 16:30:48 -05:00
John Alanbrook
32366483dc split out debug 2025-05-22 16:17:11 -05:00
John Alanbrook
707b2845b1 split out spline and js 2025-05-22 16:05:42 -05:00
John Alanbrook
693087afae move rtree to its own module 2025-05-22 15:42:45 -05:00
John Alanbrook
51940080a8 fully compiles 2025-05-22 13:23:13 -05:00
John Alanbrook
a204fce4b5 initial refactor
Some checks failed
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-macos (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
2025-05-22 11:48:27 -05:00
John Alanbrook
7bab2f1b7a clean up graphics
Some checks failed
Build and Deploy / build-linux (push) Failing after 1m32s
Build and Deploy / build-macos (push) Failing after 7s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
2025-05-22 01:28:03 -05:00
John Alanbrook
f5ee3aada6 fix portal connection
Some checks failed
Build and Deploy / build-macos (push) Failing after 4s
Build and Deploy / build-linux (push) Failing after 1m34s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
2025-05-21 23:56:02 -05:00
John Alanbrook
449e25e0f3 fix non local host networking
Some checks failed
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-macos (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-21 13:02:30 -05:00
John Alanbrook
3cbb95831c networked chess example
Some checks failed
Build and Deploy / build-macos (push) Failing after 4s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-21 12:25:17 -05:00
John Alanbrook
146baf1d23 networked chess 2025-05-21 10:34:33 -05:00
John Alanbrook
e7cc716590 initial moth
Some checks failed
Build and Deploy / build-macos (push) Failing after 10s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-19 09:01:17 -05:00
John Alanbrook
3aa2d549d1 add claude.md 2025-05-18 22:24:31 -05:00
John Alanbrook
901012064a add chess example
Some checks failed
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-macos (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-18 08:57:34 -05:00
John Alanbrook
bf2336a172 add cwd command line arg 2025-05-18 08:56:25 -05:00
John Alanbrook
708a112449 add AGENTS.md and fix rect render
Some checks failed
Build and Deploy / build-macos (push) Failing after 9s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-17 23:06:42 -05:00
John Alanbrook
85ee724754 add texture mode for renderer
Some checks failed
Build and Deploy / build-macos (push) Failing after 5s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-11 09:34:31 -05:00
John Alanbrook
ff2ee3d6db add animation test; help qr API
Some checks failed
Build and Deploy / build-macos (push) Failing after 5s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-09 12:57:54 -05:00
John Alanbrook
6bc04830d3 load image/texture from arraybuffer
Some checks failed
Build and Deploy / build-macos (push) Failing after 3s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-07 08:13:22 -05:00
John Alanbrook
589bb365bd http now returns byte array and content type
Some checks failed
Build and Deploy / build-macos (push) Failing after 4s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-06 22:50:24 -05:00
John Alanbrook
0b8a43eb91 Merge branch 'https' into misty
Some checks failed
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-macos (push) Failing after 3s
Build and Deploy / build-linux (push) Failing after 1m30s
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
2025-05-06 19:07:32 -05:00
John Alanbrook
bb3087dc37 fix qr code encoding to always be array buffers instead of strings 2025-05-06 18:53:08 -05:00
John Alanbrook
92f56570d9 fix qr decoding 2025-05-06 12:48:13 -05:00
John Alanbrook
938da0d4dc Use thin lto for release builds; move 'strip' to meson.build for release
Some checks failed
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Successful in 1m44s
Build and Deploy / build-macos (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
2025-05-05 13:55:09 -05:00
John Alanbrook
3d94859151 macos build
Some checks failed
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / build-macos (push) Failing after 1m46s
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
2025-05-04 17:50:03 -05:00
John Alanbrook
a85b1873dd sprite rework
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-04 11:28:45 -05:00
John Alanbrook
446ad080e1 render sprite geometry 2025-05-02 11:18:13 -05:00
John Alanbrook
ead61e648a changes to update to newer quickjs version
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-05-01 12:50:03 -05:00
John Alanbrook
600fbfd3b7 fast sprite render
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
2025-05-01 01:35:05 -05:00
John Alanbrook
f3031d6cd0 add bcrypt as windows dependency
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
2025-04-30 14:09:11 -05:00
John Alanbrook
7152ae093e audio working from soloud -> sdl3
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-04-29 12:43:25 -05:00
John Alanbrook
2bd93ff9e0 closer to web build 2025-04-29 12:43:17 -05:00
John Alanbrook
f68e45f898 sdl audio
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
2025-04-28 08:48:44 -05:00
John Alanbrook
7eca07c7d1 fix actor delay time always being 0 2025-04-26 09:42:23 -05:00
John Alanbrook
ee4ec2fc39 fix draw.image
Some checks failed
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-04-26 08:08:15 -05:00
John Alanbrook
b93a5a3ac0 refactor draw2d and render
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
2025-04-25 17:56:17 -05:00
John Alanbrook
de63e0e52d round rect and rounded filled rect
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
2025-04-24 14:04:27 -05:00
John Alanbrook
daef2fd2f2 circle and elipse
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
2025-04-24 09:19:56 -05:00
John Alanbrook
6705ce8980 sdl renderer
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-04-23 20:39:07 -05:00
John Alanbrook
f443816355 render
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-04-22 11:18:21 -05:00
John Alanbrook
c8c08d5fbe renderer now has camera matrix
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-04-21 09:23:50 -05:00
John Alanbrook
b8328657df add camera test
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
2025-04-14 22:46:07 -05:00
John Alanbrook
8d235ddf12 add cycle detection with use 2025-04-14 18:01:41 -05:00
John Alanbrook
05f284e3fa remove unnecessary functions
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-04-14 09:05:49 -05:00
John Alanbrook
566baa250c sdl renderer
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
'
2025-04-13 21:32:34 -05:00
John Alanbrook
19a8bd41a9 update sdl3
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-04-03 13:12:57 -05:00
John Alanbrook
58cad839b6 fix crash
Some checks failed
Build and Deploy / build-linux (push) Successful in 1m15s
Build and Deploy / build-windows (CLANG64) (push) Failing after 15m44s
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
2025-03-28 14:44:57 -05:00
John Alanbrook
34035ae6ac sdl renderer backend
Some checks failed
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
2025-03-28 12:29:15 -05:00
John Alanbrook
3a4547fb80 add blob; pull out crypto, time; add sdl_renderer
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-03-27 14:31:02 -05:00
John Alanbrook
86b21bb6dd inital parseq add
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
2025-03-26 09:42:50 -05:00
John Alanbrook
8cf114cbb4 io actor
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-03-23 04:46:16 -05:00
John Alanbrook
f9100da8a2 -u.unneeded 2025-03-22 22:27:36 -05:00
John Alanbrook
f9c1a3e71a root actor runs only on main thread; restrict ffi main thread functions to it 2025-03-22 20:55:20 -05:00
John Alanbrook
73594c8599 fix memory leaking and thread sync problems 2025-03-22 18:24:18 -05:00
John Alanbrook
239f35389e fix crashing assert on free
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-03-22 11:15:40 -05:00
John Alanbrook
95d3296dd9 wota now encodes at the C level; update dmon for macos 13; clean up many warnings
Some checks failed
Build and Deploy / build-linux (push) Successful in 1m15s
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
2025-03-20 17:25:48 -05:00
John Alanbrook
c566f90d16 remove script
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
2025-03-19 17:52:44 -05:00
John Alanbrook
9410af3a69 messages
Some checks failed
Build and Deploy / build-linux (push) Successful in 1m11s
Build and Deploy / build-windows (CLANG64) (push) Failing after 6m17s
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
2025-03-19 12:40:31 -05:00
John Alanbrook
8627fc52ef actors in C now
Some checks failed
Build and Deploy / package-dist (push) Blocked by required conditions
Build and Deploy / deploy-itch (push) Blocked by required conditions
Build and Deploy / deploy-gitea (push) Blocked by required conditions
Build and Deploy / build-linux (push) Successful in 1m15s
Build and Deploy / build-windows (CLANG64) (push) Failing after 11m46s
2025-03-18 18:18:56 -05:00
John Alanbrook
813cc8dbbc make tracy work for multilpe contexts
Some checks failed
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-03-15 20:49:53 -05:00
John Alanbrook
d90d81d7ff fixed memory leak on thread exit
Some checks failed
Build and Deploy / build-linux (push) Failing after 1m41s
Build and Deploy / build-windows (CLANG64) (push) Failing after 6m16s
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
2025-03-15 08:33:37 -05:00
John Alanbrook
b1f62cc58c destroy mailboxes on thread exit
Some checks failed
Build and Deploy / build-linux (push) Failing after 1m42s
Build and Deploy / build-windows (CLANG64) (push) Failing after 10m57s
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
2025-03-13 17:29:42 -05:00
John Alanbrook
38da997069 use wota for on machine message passing 2025-03-13 17:20:04 -05:00
John Alanbrook
88d5f6455b portal spawning works 2025-03-13 15:21:11 -05:00
John Alanbrook
eb3a41be69 add wota replacer and reviver 2025-03-13 15:21:01 -05:00
John Alanbrook
1332af93ab enet and mailboxes now take strings or array buffers
Some checks failed
Build and Deploy / build-linux (push) Successful in 1m11s
Build and Deploy / build-windows (CLANG64) (push) Failing after 6m3s
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
2025-03-13 06:28:00 -05:00
John Alanbrook
93adf50498 add nota replacer and reviver options; don't serialize functions 2025-03-13 06:25:15 -05:00
John Alanbrook
291fd9ead0 hide actor data
Some checks failed
Build and Deploy / package-dist (push) Blocked by required conditions
Build and Deploy / deploy-itch (push) Blocked by required conditions
Build and Deploy / deploy-gitea (push) Blocked by required conditions
Build and Deploy / build-linux (push) Successful in 1m16s
Build and Deploy / build-windows (CLANG64) (push) Failing after 10m57s
2025-03-12 23:09:43 -05:00
John Alanbrook
e86138ec00 multirheading mailboxes fixed
Some checks failed
Build and Deploy / build-linux (push) Successful in 1m20s
Build and Deploy / build-windows (CLANG64) (push) Failing after 10m58s
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
2025-03-11 20:34:39 -05:00
John Alanbrook
c431f117e9 remove atoms for multithreading
Some checks failed
Build and Deploy / build-linux (push) Failing after 1m42s
Build and Deploy / build-windows (CLANG64) (push) Failing after 6m13s
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
2025-03-11 10:13:15 -05:00
John Alanbrook
847a3ef314 threading
Some checks failed
Build and Deploy / package-dist (push) Blocked by required conditions
Build and Deploy / deploy-itch (push) Blocked by required conditions
Build and Deploy / deploy-gitea (push) Blocked by required conditions
Build and Deploy / build-linux (push) Failing after 55s
Build and Deploy / build-windows (CLANG64) (push) Failing after 10m50s
2025-03-10 22:49:48 -05:00
John Alanbrook
bab09fed6d add documentation
Some checks failed
Build and Deploy / build-linux (push) Failing after 1m22s
Build and Deploy / build-windows (CLANG64) (push) Failing after 10m30s
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
2025-03-09 21:53:25 -05:00
John Alanbrook
d56c983e01 add return to callback send 2025-03-09 10:18:43 -05:00
John Alanbrook
69df7302d5 initial attempt at portal and contact
Some checks failed
Build and Deploy / build-linux (push) Failing after 1m41s
Build and Deploy / build-windows (CLANG64) (push) Failing after 9m19s
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
2025-03-06 21:18:05 -06:00
John Alanbrook
01f7e715a4 parent child handshake
Some checks failed
Build and Deploy / build-linux (push) Failing after 1m45s
Build and Deploy / build-windows (CLANG64) (push) Failing after 8m5s
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
2025-03-06 11:31:37 -06:00
John Alanbrook
e71a823848 add actor clock, random, unneeded
Some checks failed
Build and Deploy / build-linux (push) Failing after 1m45s
Build and Deploy / build-windows (CLANG64) (push) Failing after 9m53s
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
2025-03-05 22:34:08 -06:00
John Alanbrook
a8865594ca coupling
Some checks failed
Build and Deploy / package-dist (push) Blocked by required conditions
Build and Deploy / deploy-itch (push) Blocked by required conditions
Build and Deploy / deploy-gitea (push) Blocked by required conditions
Build and Deploy / build-linux (push) Successful in 1m13s
Build and Deploy / build-windows (CLANG64) (push) Failing after 14m3s
2025-03-05 13:03:44 -06:00
John Alanbrook
23d764c534 actor detection
Some checks failed
Build and Deploy / build-linux (push) Failing after 1m42s
Build and Deploy / build-windows (CLANG64) (push) Failing after 8m47s
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
2025-03-05 09:01:29 -06:00
John Alanbrook
c7aee73dcb 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
2025-03-04 22:46:10 -06:00
John Alanbrook
925d1fc437 fix various graphics and sound issues
Some checks failed
Build and Deploy / build-linux (push) Successful in 1m11s
Build and Deploy / build-windows (CLANG64) (push) Failing after 9m56s
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
2025-03-04 22:43:19 -06:00
John Alanbrook
a6dbedb3cd add nota benchmark; builds no longer continue on test fail 2025-03-04 22:41:51 -06:00
John Alanbrook
74a6b9bfe6 Add versioning dropdown to docs 2025-03-04 22:41:51 -06:00
John Alanbrook
40126060fb dramatically improve nota speed for nested arrays and objects
Some checks are pending
Build and Deploy / build-windows (CLANG64) (push) Waiting to run
Build and Deploy / package-dist (push) Blocked by required conditions
Build and Deploy / deploy-itch (push) Blocked by required conditions
Build and Deploy / deploy-gitea (push) Blocked by required conditions
Build and Deploy / build-linux (push) Successful in 1m19s
2025-03-03 18:35:46 -06:00
John Alanbrook
6032c034bc add wota 2025-03-03 18:35:28 -06:00
John Alanbrook
6b4062eee6 add http get
Some checks failed
Build and Deploy / build-linux (push) Failing after 1m3s
Build and Deploy / build-windows (CLANG64) (push) Successful in 9m45s
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
2025-03-03 08:07:16 -06:00
John Alanbrook
0ea21e86eb add qr code encode and decode
All checks were successful
Build and Deploy / build-linux (push) Successful in 1m15s
Build and Deploy / build-windows (CLANG64) (push) Successful in 9m2s
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
2025-03-03 08:06:18 -06:00
John Alanbrook
30c5da879b fix various graphics and sound issues 2025-02-27 16:51:48 -06:00
John Alanbrook
1a76081fec add nota benchmark; builds no longer continue on test fail 2025-02-26 11:16:35 -06:00
John Alanbrook
045c4b49ef Add versioning dropdown to docs 2025-02-25 10:14:17 -06:00
John Alanbrook
af0996f6ab nota write decimal numbers via a string in the qjs_nota implementation, solving windows fp error
All checks were successful
Build and Deploy / build-linux (push) Successful in 1m11s
Build and Deploy / build-windows (CLANG64) (push) Successful in 10m2s
Build and Deploy / package-dist (push) Successful in 8s
Build and Deploy / deploy-gitea (push) Successful in 5s
Build and Deploy / deploy-itch (push) Successful in 9s
2025-02-24 23:15:58 -06:00
John Alanbrook
6c390aeae3 add documentation for dmon, enet, and nota.
All checks were successful
Build and Deploy / build-linux (push) Successful in 1m11s
Build and Deploy / build-windows (CLANG64) (push) Successful in 13m30s
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
2025-02-24 19:04:16 -06:00
John Alanbrook
e9519484cc fix enet; add enet testing
Some checks failed
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
2025-02-24 13:43:01 -06:00
John Alanbrook
b7da920f31 add qjs_soloud to source tree 2025-02-24 13:37:02 -06:00
John Alanbrook
35647a5c5b Minor nota speed improvement; use nota growable array internally so no more fixed size
All checks were successful
Build and Deploy / build-linux (push) Successful in 1m15s
Build and Deploy / build-windows (CLANG64) (push) Successful in 14m57s
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
2025-02-24 11:25:12 -06:00
John Alanbrook
8ea8f7fec7 Add qjs_enet into prosperon source 2025-02-24 11:15:22 -06:00
John Alanbrook
5254b84704 fixed nota encoding/decoding bug with arrays longer than 126 elements 2025-02-23 17:16:00 -06:00
John Alanbrook
f728a217c9 add dmon doc; dmon now dispatches via prosperon.on 2025-02-23 17:15:35 -06:00
John Alanbrook
c27817b73a dmon now only watches top level directory; user must call dmon.watch and supply a dmon watch function
Some checks failed
Build and Deploy / build-linux (push) Successful in 1m7s
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
2025-02-23 16:07:09 -06:00
John Alanbrook
7ea79c8ced Fix nota implementation; add nota test suite 2025-02-23 16:06:40 -06:00
John Alanbrook
fb10c63882 add dmon and nota into source tree, out of subprojects; build on macos
All checks were successful
Build and Deploy / build-linux (push) Successful in 1m9s
Build and Deploy / build-windows (CLANG64) (push) Successful in 33m33s
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
2025-02-23 08:49:49 -06:00
John Alanbrook
96ef8ccba3 Add camera and debug modules
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
2025-02-21 17:15:58 -06:00
John Alanbrook
6148f18340 CLI version ends with a newline 2025-02-21 16:55:45 -06:00
John Alanbrook
867a18e788 Project cleanup. Update stb libraries. Remove unused files. Enable pedantic warning flag and fix all warnings. Fill out spline file. 2025-02-21 16:38:59 -06:00
John Alanbrook
387c4364b5 SDL3 built as a static library as part of build process
All checks were successful
Build and Deploy / build-linux (push) Successful in 1m12s
Build and Deploy / build-windows (CLANG64) (push) Successful in 14m52s
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
2025-02-21 11:48:28 -06:00
John Alanbrook
60dce4a08f imgui is always compiled in, and developer selects to enable or disable its drawing; fix bug with rendering lines that caused prosperon to crash
All checks were successful
Build and Deploy / build-linux (push) Successful in 34s
Build and Deploy / build-windows (CLANG64) (push) Successful in 9m15s
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
2025-02-20 17:28:27 -06:00
John Alanbrook
d2325c20bd Add MSYS2 CI
All checks were successful
Build and Deploy / build-linux (push) Successful in 36s
Build and Deploy / build-windows (CLANG64) (push) Successful in 6m12s
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
2025-02-20 14:31:28 -06:00
John Alanbrook
29295607df Update build action for more useful distribution
All checks were successful
Build and Deploy / build-linux (push) Successful in 41s
Build and Deploy / build-windows (push) Successful in 47s
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
2025-02-19 16:50:25 -06:00
654 changed files with 110503 additions and 15073 deletions

17
.cell/cell.toml Normal file
View File

@@ -0,0 +1,17 @@
sdl_video = "main"
[dependencies]
extramath = "https://gitea.pockle.world/john/extramath@master"
[system]
ar_timer = 60
actor_memory = 0
net_service = 0.1
reply_timeout = 60
actor_max = 10_000
stack_max = 0
[actors]
[actors.prosperon/sdl_video]
main = true
[actors.prosperon/prosperon]
main = true
[actors.prosperon]
main = true

6
.cell/lock.toml Normal file
View File

@@ -0,0 +1,6 @@
[modules]
[modules.extramath]
hash = "MCLZT3JABTAENS4WVXKGWJ7JPBLZER4YQ5VN2PE7ZD2Z4WYGTIMA===="
url = "https://gitea.pockle.world/john/extramath@master"
downloaded = "Monday June 2 12:07:20.42 PM -5 2025 AD"
commit = "84d81a19a8455bcf8dc494739e9e6d545df6ff2c"

View File

@@ -1,13 +1,15 @@
name: Build name: Build and Deploy
on: on:
push: push:
branches: [ "*" ] branches: [ "*" ]
tags: [ "v*" ]
pull_request: pull_request:
release:
types: [published]
jobs: jobs:
# ──────────────────────────────────────────────────────────────
# LINUX BUILD
# ──────────────────────────────────────────────────────────────
build-linux: build-linux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
@@ -15,44 +17,16 @@ jobs:
steps: steps:
- name: Check Out Code - name: Check Out Code
uses: actions/checkout@v3 uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Cache SDL3 (Linux)
uses: actions/cache@v3
with:
path: |
sdl3
sdl3-build
key: sdl3-linux-${{ hashFiles('sdl3/CMakeLists.txt') }}
- name: Build SDL3 (Linux)
run: |
if [ ! -d "sdl3/.git" ]; then
echo "Cloning SDL3 repository..."
git clone --depth 1 --branch main https://github.com/libsdl-org/SDL.git sdl3
else
echo "SDL3 source is already present (possibly from cache)."
fi
mkdir -p sdl3-build
cd sdl3-build
cmake ../sdl3 -GNinja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="${PWD}/installed_sdl3" \
-DSDL_SHARED=ON \
-DSDL_STATIC=OFF
ninja
ninja install
- name: Build Prosperon (Linux) - name: Build Prosperon (Linux)
run: | run: |
export PKG_CONFIG_PATH="${PWD}/sdl3-build/installed_sdl3/lib/pkgconfig:$PKG_CONFIG_PATH" meson setup build -Dbuildtype=release -Db_lto=true -Db_lto_mode=thin -Db_ndebug=true
meson setup build -Dbuildtype=release -Db_lto=true -Db_ndebug=true
meson compile -C build meson compile -C build
- name: Test Prosperon (Linux) - name: Test Prosperon (Linux)
env: env: { TRACY_NO_INVARIANT_CHECK: 1 }
TRACY_NO_INVARIANT_CHECK: 1
run: | run: |
meson test --print-errorlogs -C build meson test --print-errorlogs -C build
@@ -63,102 +37,146 @@ jobs:
name: testlog-linux name: testlog-linux
path: build/meson-logs/testlog.txt path: build/meson-logs/testlog.txt
- name: Create artifact folder (Linux)
if: ${{ github.event_name == 'release' && github.event.action == 'published' }}
run: |
mkdir _pack
cp build/prosperon _pack/
cp sdl3-build/installed_sdl3/lib/libSDL3.so _pack/
- name: Upload Artifact (Linux) - name: Upload Artifact (Linux)
if: ${{ github.event_name == 'release' && github.event.action == 'published' }} if: startsWith(github.ref, 'refs/tags/v')
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: prosperon-artifacts-linux name: prosperon-artifacts-linux
path: _pack path: build/prosperon
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Gitea Registry
uses: docker/login-action@v3
with:
registry: gitea.pockle.world
username: ${{ secrets.USER_GITEA }}
password: ${{ secrets.TOKEN_GITEA }}
- name: Determine Docker Tag
id: docker_tag
run: |
if [[ "${{ github.ref }}" =~ ^refs/tags/v.* ]]; then
TAG=$(echo "${{ github.ref }}" | sed 's#refs/tags/##')
echo "tag=$TAG" >> $GITHUB_OUTPUT
else
echo "tag=latest" >> $GITHUB_OUTPUT
fi
- name: Build and Push Docker Image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: true
tags: gitea.pockle.world/john/prosperon:${{ steps.docker_tag.outputs.tag }}
platforms: linux/amd64
# ──────────────────────────────────────────────────────────────
# WINDOWS BUILD (MSYS2 / CLANG64)
# ──────────────────────────────────────────────────────────────
build-windows: build-windows:
runs-on: ubuntu-latest runs-on: win-native
container: strategy:
image: gitea.pockle.world/john/prosperon/linux:latest matrix: { msystem: [ CLANG64 ] }
steps: steps:
- name: Check Out Code - name: Check Out Code
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Cache SDL3 (Windows cross) - name: Setup MSYS2
uses: actions/cache@v3 uses: msys2/setup-msys2@v2
with: with:
path: | msystem: ${{ matrix.msystem }}
sdl3-win update: true
sdl3-build-win cache: true
key: sdl3-win-${{ hashFiles('sdl3-win/CMakeLists.txt') }} install: |
git zip gzip tar base-devel
pacboy: |
meson
cmake
toolchain
- name: Build SDL3 (Windows cross) - name: Build Prosperon (Windows)
shell: msys2 {0}
run: | run: |
if [ ! -d "sdl3-win/.git" ]; then meson setup build -Dbuildtype=release -Db_lto=true -Db_lto_mode=thin -Db_ndebug=true -Dtracy:only_localhost=true -Dtracy:no_broadcast=true
echo "Cloning SDL3 for Windows cross..."
git clone --depth 1 --branch main https://github.com/libsdl-org/SDL.git sdl3-win
else
echo "SDL3-win source already present (possibly from cache)."
fi
mkdir -p sdl3-build-win
cd sdl3-build-win
cmake ../sdl3-win -GNinja \
-DCMAKE_SYSTEM_NAME=Windows \
-DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc \
-DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ \
-DCMAKE_RC_COMPILER=x86_64-w64-mingw32-windres \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="${PWD}/installed_sdl3_win" \
-DSDL_SHARED=ON \
-DSDL_STATIC=OFF
ninja
ninja install
- name: Configure PKG_CONFIG_PATH (Windows cross)
run: |
echo "PKG_CONFIG_PATH=${GITHUB_WORKSPACE}/sdl3-build-win/installed_sdl3_win/lib/pkgconfig:$PKG_CONFIG_PATH" >> $GITHUB_ENV
- name: Build Prosperon (Windows cross)
run: |
meson setup build -Dbuildtype=release -Db_lto=true -Db_ndebug=true --cross-file mingw32.cross
meson compile -C build meson compile -C build
- name: Test Prosperon - name: Test Prosperon (Windows)
shell: msys2 {0}
env: env:
TRACY_NO_INVARIANT_CHECK: 1 TRACY_NO_INVARIANT_CHECK: 1
run: | run: |
meson test --print-errorlogs -C build meson test --print-errorlogs -C build
- name: Upload Test Log - name: Upload Test Log (Windows)
if: ${{ always() }} if: ${{ always() }}
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: testlog-linux name: testlog-windows
path: build/meson-logs/testlog.txt path: build/meson-logs/testlog.txt
- name: Create package folder
run: |
mkdir _pack
cp build/prosperon.exe _pack/
cp sdl3-build-win/installed_sdl3_win/bin/SDL3.dll _pack/
- name: Upload Artifact (Windows cross) - name: Upload Artifact (Windows)
if: ${{ github.event_name == 'release' && github.event.action == 'published' }} if: startsWith(github.ref, 'refs/tags/v')
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: prosperon-artifacts-windows name: prosperon-artifacts-windows
path: _pack path: build/prosperon.exe
# ──────────────────────────────────────────────────────────────
# MACOS BUILD
# ──────────────────────────────────────────────────────────────
build-macos:
runs-on: macos-latest
steps:
- name: Check Out Code
uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Build Prosperon (macOS)
run: |
meson setup build -Dbuildtype=release -Db_lto=true -Db_lto_mode=thin -Db_ndebug=true
meson compile -C build
- name: Test Prosperon (macOS)
run: |
meson test --print-errorlogs -C build
- name: Upload Test Log (macOS)
if: ${{ always() }}
uses: actions/upload-artifact@v3
with:
name: testlog-macos
path: build/meson-logs/testlog.txt
- name: Upload Artifact (macOS)
if: startsWith(github.ref, 'refs/tags/v')
uses: actions/upload-artifact@v3
with:
name: prosperon-artifacts-macos
path: build/prosperon
# ──────────────────────────────────────────────────────────────
# PACKAGE CROSS-PLATFORM DIST
# ──────────────────────────────────────────────────────────────
package-dist: package-dist:
if: ${{ github.event_name == 'release' && github.event.action == 'published' }} needs: [ build-linux, build-windows, build-macos ]
needs: [build-linux, build-windows] if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check Out Code - name: Check Out Code
uses: actions/checkout@v3 uses: actions/checkout@v3
with: { fetch-depth: 0 }
- name: Get Latest Tag
id: get_tag
run: |
TAG=$(git describe --tags --abbrev=0)
echo "tag=$TAG" >> $GITHUB_OUTPUT
- name: Download Linux Artifacts - name: Download Linux Artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
@@ -172,24 +190,115 @@ jobs:
name: prosperon-artifacts-windows name: prosperon-artifacts-windows
path: windows_artifacts path: windows_artifacts
- name: Create the Dist Folder - name: Download macOS Artifacts
uses: actions/download-artifact@v3
with:
name: prosperon-artifacts-macos
path: mac_artifacts
- name: Create Dist Folder
run: | run: |
mkdir dist mkdir -p dist/linux dist/win dist/mac
cp README.md dist/
cp README.md dist/ cp license.txt dist/
cp license.txt dist/ cp -r examples dist/
cp -r examples dist/ cp linux_artifacts/* dist/linux/
# Make subdirectories for each platform
mkdir dist/linux
mkdir dist/win
# Copy artifacts in
cp linux_artifacts/* dist/linux/
cp windows_artifacts/* dist/win/ cp windows_artifacts/* dist/win/
cp mac_artifacts/* dist/mac/
- name: Package Final Dist
run: |
TAG=${{ steps.get_tag.outputs.tag }}
zip -r "prosperon-${TAG}.zip" dist
echo "Created prosperon-${TAG}.zip"
- name: Upload Final Dist - name: Upload Final Dist
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: prosperon name: "prosperon-${{ steps.get_tag.outputs.tag }}"
path: "prosperon-${{ steps.get_tag.outputs.tag }}.zip"
# ──────────────────────────────────────────────────────────────
# DEPLOY TO ITCH.IO (single ZIP containing all OSes)
# ──────────────────────────────────────────────────────────────
deploy-itch:
needs: [ package-dist ]
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@v3
with: { fetch-depth: 0 }
- name: Get Latest Tag
id: get_tag
run: |
TAG=$(git describe --tags --abbrev=0)
echo "tag=$TAG" >> $GITHUB_OUTPUT
- name: Download Final Distribution
uses: actions/download-artifact@v3
with:
name: "prosperon-${{ steps.get_tag.outputs.tag }}"
path: dist path: dist
- name: Set up Butler
uses: jdno/setup-butler@v1
- name: Push to itch.io
run: |
butler push "dist/prosperon-${{ steps.get_tag.outputs.tag }}.zip" \
${{ secrets.ITCHIO_USERNAME }}/prosperon:universal \
--userversion ${{ steps.get_tag.outputs.tag }}
env:
BUTLER_API_KEY: ${{ secrets.ITCHIO_API_KEY }}
# ──────────────────────────────────────────────────────────────
# DEPLOY TO SELF-HOSTED GITEA
# ──────────────────────────────────────────────────────────────
deploy-gitea:
needs: [ package-dist ]
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@v3
with: { fetch-depth: 0 }
- name: Get Latest Tag & Commit Message
id: get_tag
run: |
TAG=$(git describe --tags --abbrev=0)
COMMIT_MSG=$(git log -1 --pretty=%B "$TAG")
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "commit_msg=$COMMIT_MSG" >> $GITHUB_OUTPUT
- name: Download Final Distribution
uses: actions/download-artifact@v3
with:
name: "prosperon-${{ steps.get_tag.outputs.tag }}"
path: dist
- name: Create / Update Gitea Release
run: |
TAG=${{ steps.get_tag.outputs.tag }}
ZIP=dist/prosperon-${TAG}.zip
BODY=$(echo "${{ steps.get_tag.outputs.commit_msg }}" | jq -R -s '.')
RELEASE=$(curl -s -H "Authorization: token ${{ secrets.TOKEN_GITEA }}" \
"https://gitea.pockle.world/api/v1/repos/john/prosperon/releases/tags/$TAG" | jq -r '.id')
if [ "$RELEASE" = "null" ] || [ -z "$RELEASE" ]; then
RELEASE=$(curl -X POST \
-H "Authorization: token ${{ secrets.TOKEN_GITEA }}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\":\"$TAG\",\"target_commitish\":\"${{ github.sha }}\",\"name\":\"$TAG\",\"body\":$BODY,\"draft\":false,\"prerelease\":false}" \
"https://gitea.pockle.world/api/v1/repos/john/prosperon/releases" | jq -r '.id')
fi
curl -X POST \
-H "Authorization: token ${{ secrets.TOKEN_GITEA }}" \
-H "Content-Type: application/octet-stream" \
--data-binary @"$ZIP" \
"https://gitea.pockle.world/api/v1/repos/john/prosperon/releases/$RELEASE/assets?name=prosperon-${TAG}.zip"
env:
TOKEN_GITEA: ${{ secrets.TOKEN_GITEA }}

View File

@@ -1,64 +0,0 @@
name: Build
jobs:
# ===============================================================
# MACOS BUILD (Using Homebrew SDL3)
# ===============================================================
build-macos:
runs-on: macos-latest
continue-on-error: true
steps:
# 1) Check out code
- name: Check Out Code
uses: actions/checkout@v3
# 2) Install dependencies (SDL3 via Homebrew) + ccache
- name: Install Dependencies (macOS)
run: |
brew update
brew install sdl3 meson ninja cmake ccache
# 3) Configure ccache
- name: Configure ccache
run: |
echo "CMAKE_C_COMPILER_LAUNCHER=ccache" >> $GITHUB_ENV
echo "CMAKE_CXX_COMPILER_LAUNCHER=ccache" >> $GITHUB_ENV
# 4) Cache ccache
- name: Cache ccache
uses: actions/cache@v3
with:
path: ~/Library/Caches/ccache
key: ccache-macos-${{ hashFiles('**/*.c', '**/*.cpp', '**/*.h', '**/CMakeLists.txt', '**/meson.build') }}
restore-keys: |
ccache-macos-
# 5) Build Prosperon (macOS) linking against Homebrew's SDL3
- name: Build Prosperon (macOS)
run: |
# Ensure pkg-config can find Homebrew's SDL3 .pc files
export PKG_CONFIG_PATH="$(brew --prefix sdl3)/lib/pkgconfig:$PKG_CONFIG_PATH"
meson setup build_macos -Dbuildtype=release -Db_lto=true -Db_ndebug=true
meson compile -C build_macos
# 6) Copy SDL3 .dylib from Homebrew for packaging
- name: Copy SDL3 library for packaging
run: |
SDL3_PREFIX=$(brew --prefix sdl3)
mkdir -p sdl3-macos
# Copy all versions of the SDL3 dynamic library
cp -a "${SDL3_PREFIX}/lib/libSDL3*.dylib" sdl3-macos/ || echo "No .dylib found, ignoring"
# 7) Create minimal artifact folder (macOS)
- name: Create artifact folder (macOS)
run: |
mkdir _pack
cp build_macos/prosperon _pack/
cp sdl3-macos/libSDL3*.dylib _pack/ || echo "No .dylib found, ignoring"
# 8) Upload artifact (macOS)
- name: Upload Artifact (macOS)
uses: actions/upload-artifact@v3
with:
name: prosperon-artifacts-macos
path: _pack

12
.gitignore vendored
View File

@@ -6,26 +6,18 @@ build/
*.o *.o
*.a *.a
*.d *.d
tags
Jenkinsfile
*~ *~
*.log *.log
*.gz *.gz
*.tar *.tar
.nova/ .nova/
packer*
primum
sokol-shdc*
source/shaders/*.h source/shaders/*.h
core.cdb
primum.exe
core.cdb.h
jsc
.DS_Store .DS_Store
*.html *.html
.vscode .vscode
*.icns *.icns
game.zip
icon.ico icon.ico
steam/ steam/
subprojects/*/ subprojects/*/
build_dbg/
modules/

26
AGENTS.md Normal file
View File

@@ -0,0 +1,26 @@
# AGENTS.md
## Project Overview
This is a game engine developed using a QuickJS fork as its scripting language. It is an actor based system, based on Douglas Crockford's Misty. It is a Meson compiled project with a number of dependencies.
## File Structure
- `source/`: Contains the C source code
- `scripts/`: Contains script code that is loaded on executable start, and modules
- `shaders/`: Contains shaders that ship with the engine (for shader based backends)
- `benchmarks/`: Benchmark programs for testing speed
- `tests/`: Unit tests
- `examples/`: Contains full game examples
## Coding Practices
- Use K&R style C
- Javascript style prefers objects and prototypical inheritence over ES6 classes, liberal use of closures, and var everywhere
## Instructions
- When generating code, adhere to the coding practices outlined above.
- When adding new features, ensure they align with the project's goals.
- When fixing bugs, review the code carefully before making changes.
- When writing unit tests, cover all important scenarios.
## Compiling, running, and testing
- To compile the code, run "make", which generates a prosperon executable in build_dbg/, and copy it into the root folder
- Run a test by giving it as its command: so ./prosperon tests/overling.js would run the test overling.js, ./prosperon tests/nota.js runs the nota benchmark

405
CLAUDE.md Normal file
View File

@@ -0,0 +1,405 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Build Commands
### Build variants
- `make` - Make and install debug version. Usually all that's needed.
- `make fast` - Build optimized version
- `make release` - Build release version with LTO and optimizations
- `make small` - Build minimal size version
- `make web` - Build for web/emscripten platform
- `make crosswin` - Cross-compile for Windows using mingw32
### Testing
After install with 'make', just run 'cell' and point it at the actor you want to launch. "cell tests/toml" runs the actor "tests/toml.js"
## Scripting language
This is called "cell", but it is is a variant of javascript and extremely similar.
### Common development commands
- `meson setup build_<variant>` - Configure build directory
- `meson compile -C build_<variant>` - Compile in build directory
- `./build_dbg/prosperon examples/<example>` - Run example from build directory
- Copy prosperon to game directory and run: `cp build_dbg/prosperon <game-dir>/ && cd <game-dir> && ./prosperon`
## Architecture Overview
Prosperon is an actor-based game engine inspired by Douglas Crockford's Misty system. Key architectural principles:
### Actor Model
- Each actor runs on its own thread
- Communication only through message passing (no shared JavaScript objects)
- Hierarchical actor system with spawning/killing
- Actor lifecycle: awake, update, draw, garbage collection
### JavaScript Style Guide
- Use `use()` function for imports (Misty-style, not ES6 import/export)
- Prefer closures and javascript objects and prototypes over ES6 style classes
- Follow existing JavaScript patterns in the codebase
- Functions as first-class citizens
- Do not use const or let; only var
### Core Systems
1. **Actor System** (scripts/core/engine.js)
- Message passing via `send()`, `$_.receive()`
- Actor spawning/management
- Register-based component system (update, draw, gui, etc.)
2. **Module System**
- `use()` function for loading modules
- Module paths: `scripts/modules/`, `scripts/modules/ext/`
- Custom QuickJS build with embedded C modules
3. **Build System**
- Meson build configuration (Makefile is convenience wrapper)
- Multiple platform targets (Windows, macOS, Linux, Web)
- Custom QuickJS build in `subprojects/`
- Uses SDL3 for cross-platform support
### Engine Entry Points
- `source/prosperon.c` - Main C entry point
- `scripts/core/engine.js` - JavaScript engine initialization for system
- `scripts/core/base.js` has modifications to this Javascript runtime (for example, additions to the base Array, String, etc)
### Subprojects
- C code has many subprojects, who's source and sometimes documentation can be found in subprojects. subprojects/quickjs/doc has documentation for quickjs
### Resource System
- Scripts are bundled into `core.zip` during build
- Runtime module loading via PhysFS
- Resource paths checked in order: `/`, `scripts/modules/`, `scripts/modules/ext/`
### Notable Dependencies
- QuickJS (custom build) - JavaScript runtime
- SDL3 - Platform abstraction
- Chipmunk2D - Physics
- ENet - Networking
- Soloud - Audio
- Tracy - Profiling (when enabled)
## Development Tips
### Running Games
```bash
# Build first
make debug
# Run example from build directory
./build_dbg/prosperon examples/chess
# Or copy to game directory
cp build_dbg/prosperon examples/chess/
cd examples/chess
./prosperon
```
### Documentation
- Documentation is found in docs
- Documentation for the JS modules loaded with 'use' is docs/api/modules
- .md files directly in docs gives a high level overview
- docs/dull is what this specific Javascript system is (including alterations from quickjs/es6)
### Shader Development
- Shaders are in `shaders/` directory as HLSL
- Compile script: `shaders/compile.sh`
- Outputs to platform-specific formats: `dxil/`, `msl/`, `spv/`
### Example Games
Located in `examples/` directory:
- `chess` - Chess implementation (has its own Makefile)
- `pong` - Classic pong game
- `snake` - Snake game
- `tetris` - Tetris clone
- `bunnymark` - Performance test
### Testing
```bash
# Run all tests
meson test -C build_dbg
# Run specific test
./build_dbg/prosperon tests/spawn_actor.js
```
### Debugging
- Use debug build: `make debug`
- Tracy profiler support when enabled
- Console logging available via `log.console()`, `log.error()`, etc.
- Log files written to `.prosperon/log.txt`
# Project Structure Notes
## Core JavaScript Modules
- JavaScript modules are defined using the MISTUSE macro in jsffi.c
- The `js_os_funcs`, `js_io_funcs`, etc. arrays define the available functions for each module
- New functions are added with MIST_FUNC_DEF(module, function, args_count)
## File I/O
- `io.slurp(path)` - Reads a file as text
- `io.slurpbytes(path)` - Reads a file as an ArrayBuffer
- `io.slurpwrite(path, data)` - Writes data (string or ArrayBuffer) to a file
- `io.exists(path)` - Checks if a file exists
## Script Loading
- The `use(path)` function in engine.js loads JavaScript modules
- Script loading happens in prosperon.c and the engine.js script
- jsffi.c contains the C hooks for the QuickJS JavaScript engine
- Added functionality for bytecode compilation and loading:
- `os.compile_bytecode(source, filename)` - Compiles JS to bytecode, returns ArrayBuffer
- `os.eval_bytecode(bytecode)` - Evaluates bytecode from an ArrayBuffer
- `compile(scriptPath)` - Compiles a JS file to a .jso bytecode file
- Modified `use()` to check for .jso files before loading .js files
## QuickJS Bytecode API
- `JS_Eval` with JS_EVAL_FLAG_COMPILE_ONLY - Compiles without executing
- `JS_WriteObject` with JS_WRITE_OBJ_BYTECODE - Serializes to bytecode
- `JS_ReadObject` with JS_READ_OBJ_BYTECODE - Deserializes and loads bytecode
- Bytecode files use .jso extension alongside .js files
## Available JavaScript APIs
### Core APIs
- `actor` - Base prototype for all actor objects
- `$_` - Special global for actor messaging
- `prosperon` - Global engine interface
- `console` - Logging and debugging interface
### Framework APIs
- `moth` - Higher-level game framework that simplifies Prosperon usage
- Handles window creation, game loop, and event dispatching
- Provides simple configuration via config.js
- Auto-initializes systems like rendering and input
- Manages camera, resolution, and FPS automatically
### Rendering
- `draw2d` - 2D drawing primitives
- `render` - Low-level rendering operations
- `graphics` - Higher-level graphics utilities
- `camera` - Camera controls and transformations
- `sprite` - Sprite rendering and management
### Physics and Math
- `math` - Mathematical utilities
- `geometry` - Geometric calculations and shapes
- `transform` - Object transformations
### Input and Events
- `input` - Mouse, keyboard, and touch handling
- `event` - Event management system
### Networking
- `enet` - Networking through ENet library
- `http` - HTTP client capabilities
### Audio
- `sound` - Audio playback using SoLoud
### Utility Modules
- `time` - Time management and delays
- **Must be imported with `use('time')`**
- No `time.now()` function - use:
- `time.number()` - Number representation of current time
- `time.record()` - Struct representation of current time
- `time.text()` - Text representation of current time
- `io` - File I/O operations
- `json` - JSON parsing and serialization
- `util` - General utilities
- `color` - Color manipulation
- `miniz` - Compression utilities
- `nota` - Structured data format
- `wota` - Serialization format
- `qr` - QR code generation/reading
- `tween` - Animation tweening
- `spline` - Spline calculations
- `imgui` - Immediate mode GUI
## Game Development Patterns
### Project Structure
- Game config is typically in `config.js`
- Main entry point is `main.js`
- Resource loading through `resources.js`
### Actor Pattern Usage
- Create actors with `actor.spawn(script, config)`
- Start actors with `$_.start(callback, script)` - the system automatically sends a greeting, callback receives {type: 'greet', actor: actor_ref}
- No need to manually send greetings - `$_.start` handles this automatically
- Manage actor hierarchy with overlings and underlings
- Schedule actor tasks with `$_.delay()` method
- Clean up with `kill()` and `garbage()`
### Actor Messaging with Callbacks
When sending a message with a callback, respond by sending to the message itself:
```javascript
// Sender side:
send(actor, {type: 'status'}, response => {
log.console(response); // Handle the response
});
// Receiver side:
$_.receiver(msg => {
if (msg.type === 'status') {
send(msg, {status: 'ok'}); // Send response to the message itself
}
});
```
**Critical Rules for Message Callbacks**:
- **A message can only be used ONCE as a send target** - after sending a response to a message, it cannot be used again
- If you need to send multiple updates (like progress), only the download request message should be used for the final response
- Status requests should each get their own individual response
- Actor objects and message headers are completely opaque - never try to access internal properties
- Never access `msg.__HEADER__` or similar - the actor system handles routing internally
- Use `$_.delay()` to schedule work and avoid blocking the message receiver
### Game Loop Registration
- Register functions like `update`, `draw`, `gui`, etc.
- Set function.layer property to control execution order
- Use `Register` system to manage callbacks
### Program vs Module Pattern
- Programs are actor scripts that don't return values, they execute top-to-bottom
- Modules are files that return single values (usually objects) that get frozen
- Programs can spawn other programs as underlings
- Programs have lifecycle hooks: awake, update, draw, garbage, etc.
## Technical Capabilities
### Graphics Pipeline
- Supports multiple render backends (Direct3D, Metal, Vulkan via SDL3)
- Custom shader system with cross-platform compilation
- Sprite batching for efficient 2D rendering
- Camera systems for both 2D and 3D
### Asset Support
- Images: PNG, JPG, QOI, etc.
- Audio: Various formats through SoLoud
- Models: Basic 3D model support
- Custom formats: Aseprite animations, etc.
### Developer Tools
- Built-in documentation system with `cell.DOC`
- Tracy profiler integration for performance monitoring
- Imgui debugging tools
- Console logging with various severity levels
## Misty Networking Patterns
Prosperon implements the Misty actor networking model. Understanding these patterns is critical for building distributed applications.
### Portal Reply Pattern
Portals must reply with an actor object, not application data:
```javascript
// CORRECT: Portal replies with actor
$_.portal(e => {
send(e, $_); // Reply with server actor
}, 5678);
// WRONG: Portal sends application data
$_.portal(e => {
send(e, {type: 'game_start'}); // This breaks the pattern
}, 5678);
```
### Two-Phase Connection Protocol
Proper Misty networking follows a two-phase pattern:
**Phase 1: Actor Connection**
- Client contacts portal using `$_.contact()`
- Portal replies with an actor object
- This establishes the communication channel
**Phase 2: Application Communication**
- Client sends application messages to the received actor
- Normal bidirectional messaging begins
- Application logic handles game/service initialization
### Message Handling Best Practices
Messages should be treated as opaque objects with your application data:
```javascript
// CORRECT: Store actor references separately
var players = {};
$_.receiver(msg => {
if (msg.type === 'join_game' && msg.player_id) {
// Store the message for later response
players[msg.player_id] = msg;
// Later, respond to the stored message
send(players[msg.player_id], {type: 'game_start'});
}
});
// WRONG: Trying to access internal message properties
$_.receiver(msg => {
var sender = msg.__HEADER__.replycc; // Never do this!
});
```
### Return ID Lifecycle
- Each reply callback gets a unique return ID
- Return IDs are consumed once and then deleted
- Reusing message objects with return headers causes "Could not find return function" errors
- Always create clean actor references for ongoing communication
### Actor Object Transparency
Actor objects must be completely opaque black boxes that work identically regardless of transport:
```javascript
// Actor objects work transparently for:
// - Same-process communication (fastest - uses mailbox)
// - Inter-process communication (uses mailbox)
// - Network communication (uses ENet)
// The actor shouldn't know or care about the transport mechanism
send(opponent, {type: 'move', from: [0,0], to: [1,1]});
```
**Key Implementation Details:**
- `actor_send()` in `scripts/core/engine.js` handles routing based on available actor data
- Actor objects sent in message data automatically get address/port populated when received over network
- Three communication pathways: `os.mailbox_exist()` check → mailbox send → network send
- Actor objects must contain all necessary routing information for transparent messaging
### Common Networking Bugs
1. **Portal sending application data**: Portal should only establish actor connections
2. **Return ID collision**: Reusing messages with return headers for multiple sends
3. **Mixed phases**: Trying to do application logic during connection establishment
4. **Header pollution**: Using received message objects as actor references
5. **Missing actor address info**: Actor objects in message data need network address population (fixed in engine.js:746-766)
### Example: Correct Chess Networking
```javascript
// Server: Portal setup
$_.portal(e => {
send(e, $_); // Just reply with actor
}, 5678);
// Client: Two-phase connection
$_.contact((actor, reason) => {
if (actor) {
opponent = actor;
send(opponent, {type: 'join_game'}); // Phase 2: app messaging
}
}, {address: "localhost", port: 5678});
// Server: Handle application messages
$_.receiver(e => {
if (e.type === 'join_game') {
opponent = e.__HEADER__.replycc;
send(opponent, {type: 'game_start', your_color: 'black'});
}
});
```
## Memory Management
- When working with a conversational AI system like Claude, it's important to maintain a clean and focused memory
- Regularly review and update memories to ensure they remain relevant and helpful
- Delete or modify memories that are no longer accurate or useful
- Prioritize information that can genuinely assist in future interactions

54
Dockerfile Normal file
View File

@@ -0,0 +1,54 @@
# Builder stage
FROM ubuntu:plucky AS builder
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 python3-pip \
libasound2-dev \
libpulse-dev \
libudev-dev \
libwayland-dev \
wayland-protocols \
libxkbcommon-dev \
libx11-dev \
libxext-dev \
libxrandr-dev \
libxcursor-dev \
libxi-dev \
libxinerama-dev \
libxss-dev \
libegl1-mesa-dev \
libgl1-mesa-dev \
cmake \
ninja-build \
git \
build-essential \
binutils \
pkg-config \
meson \
zip && \
rm -rf /var/lib/apt/lists/*
WORKDIR /app
RUN git clone https://gitea.pockle.world/john/prosperon.git
WORKDIR /app/prosperon
RUN git checkout jsffi_refactor
RUN meson setup build -Dbuildtype=release -Db_lto=true -Db_lto_mode=thin -Db_ndebug=true
RUN meson compile -C build
# Runtime stage
FROM ubuntu:latest
# Install minimal runtime dependencies (e.g., for dynamically linked libraries)
RUN apt-get update && apt-get install -y libstdc++6 && rm -rf /var/lib/apt/lists/*
# Copy the compiled prosperon binary from the build stage
COPY --from=builder /app/prosperon/build/prosperon /usr/local/bin/prosperon
# Create an entrypoint script
RUN echo '#!/bin/bash' > /entrypoint.sh && \
echo '/usr/local/bin/prosperon "$@" &' >> /entrypoint.sh && \
echo 'tail -f /dev/null' >> /entrypoint.sh && \
chmod +x /entrypoint.sh
WORKDIR /workdir
ENTRYPOINT ["/entrypoint.sh"]

View File

@@ -1,22 +1,22 @@
debug: FORCE debug: FORCE
meson setup build_dbg -Dbuildtype=debugoptimized meson setup build_dbg -Dbuildtype=debugoptimized
meson compile -C build_dbg meson install --only-changed -C build_dbg
fast: FORCE fast: FORCE
meson setup build_fast meson setup build_fast
meson compile -C build_fast meson install -C build_fast
release: FORCE release: FORCE
meson setup -Dbuildtype=release -Db_lto=true -Db_ndebug=true build_release meson setup -Dbuildtype=release -Db_lto=true -Db_lto_mode=thin -Db_ndebug=true build_release
meson compile -C build_release meson install -C build_release
sanitize: FORCE sanitize: FORCE
meson setup -Db_sanitize=address -Db_sanitize=memory -Db_sanitize=leak -Db_sanitize=undefined build_sani meson setup -Db_sanitize=address -Db_sanitize=memory -Db_sanitize=leak -Db_sanitize=undefined build_sani
meson compile -C build_sani meson install -C build_sani
small: FORCE small: FORCE
meson setup -Dbuildtype=minsize -Db_lto=true -Db_ndebug=true build_small meson setup -Dbuildtype=minsize -Db_lto=true -Db_ndebug=true build_small
meson compile -C build_small meson install -C build_small
web: FORCE web: FORCE
meson setup -Deditor=false -Dbuildtype=minsize -Db_lto=true -Db_ndebug=true --cross-file emscripten.cross build_web meson setup -Deditor=false -Dbuildtype=minsize -Db_lto=true -Db_ndebug=true --cross-file emscripten.cross build_web

View File

@@ -1,9 +1,7 @@
Thank you for using Prosperon! Thank you for using Prosperon!
Provided are prosperon builds for all available platforms, including SDL3 for each respective one. SDL3 must be present in the same folder as prosperon to run! Provided are prosperon builds for all available platforms. Simply run prosperon for your platform in a game folder to play!
To get started, take a dive into the provided example games in the examples folder. Just copy the prosperon executable for your platform, along with its SDL3 library, into any provided example folder, then run it! To get started, take a dive into the provided example games in the examples folder. You can either copy the prosperon executable into an example directory and run it there, or run `prosperon path/to/example` from the project root.
NOTE: For MacOS, SDL3 must first be installed with homebrew. After installing homebrew, run `brew install sdl3`. This will be fixed in a future release!
You can take a look through the docs folder for the prosperon manual to learn all about it. The manual is available on the web at [docs.prosperon.dev](https://docs.prosperon.dev). You can take a look through the docs folder for the prosperon manual to learn all about it. The manual is available on the web at [docs.prosperon.dev](https://docs.prosperon.dev).

43
benchmarks/binarytree.ce Normal file
View File

@@ -0,0 +1,43 @@
function mainThread() {
var maxDepth = Math.max(6, Number(arg[0] || 16));
var stretchDepth = maxDepth + 1;
var check = itemCheck(bottomUpTree(stretchDepth));
log.console(`stretch tree of depth ${stretchDepth}\t check: ${check}`);
var longLivedTree = bottomUpTree(maxDepth);
for (let depth = 4; depth <= maxDepth; depth += 2) {
var iterations = 1 << maxDepth - depth + 4;
work(iterations, depth);
}
log.console(`long lived tree of depth ${maxDepth}\t check: ${itemCheck(longLivedTree)}`);
}
function work(iterations, depth) {
let check = 0;
for (let i = 0; i < iterations; i++)
check += itemCheck(bottomUpTree(depth));
log.console(`${iterations}\t trees of depth ${depth}\t check: ${check}`);
}
function TreeNode(left, right) {
return {left, right};
}
function itemCheck(node) {
if (node.left == null)
return 1;
return 1 + itemCheck(node.left) + itemCheck(node.right);
}
function bottomUpTree(depth) {
return depth > 0
? new TreeNode(bottomUpTree(depth - 1), bottomUpTree(depth - 1))
: new TreeNode(null, null);
}
mainThread()
$_.stop()

View File

@@ -0,0 +1,24 @@
var blob = use('blob')
function eratosthenes (n) {
var sieve = new blob(n, true)
var sqrtN = Math.trunc(Math.sqrt(n));
for (i = 2; i <= sqrtN; i++)
if (sieve.read_logical(i))
for (j = i * i; j <= n; j += i)
sieve.write_bit(j, false);
return sieve;
}
var sieve = eratosthenes(10000000);
stone(sieve)
var c = 0
for (var i = 0; i < sieve.length; i++)
if (sieve.read_logical(i)) c++
log.console(c)
$_.stop()

58
benchmarks/fannkuch.ce Normal file
View File

@@ -0,0 +1,58 @@
function fannkuch(n) {
var perm1 = [n]
for (let i = 0; i < n; i++) perm1[i] = i
var perm = [n]
var count = [n]
var f = 0, flips = 0, nperm = 0, checksum = 0
var i, k, r
r = n
while (r > 0) {
i = 0
while (r != 1) { count[r-1] = r; r -= 1 }
while (i < n) { perm[i] = perm1[i]; i += 1 }
// Count flips and update max and checksum
f = 0
k = perm[0]
while (k != 0) {
i = 0
while (2*i < k) {
let t = perm[i]; perm[i] = perm[k-i]; perm[k-i] = t
i += 1
}
k = perm[0]
f += 1
}
if (f > flips) flips = f
if ((nperm & 0x1) == 0) checksum += f; else checksum -= f
// Use incremental change to generate another permutation
var more = true
while (more) {
if (r == n) {
log.console( checksum )
return flips
}
let p0 = perm1[0]
i = 0
while (i < r) {
let j = i + 1
perm1[i] = perm1[j]
i = j
}
perm1[r] = p0
count[r] -= 1
if (count[r] > 0) more = false; else r += 1
}
nperm += 1
}
return flips;
}
var n = arg[0] || 10
log.console(`Pfannkuchen(${n}) = ${fannkuch(n)}`)
$_.stop()

View File

@@ -0,0 +1,20 @@
#!/bin/bash
# Run hyperfine with parameter lists
# This will create a cross-product of all libraries × all scenarios
hyperfine \
--warmup 3 \
--runs 20 \
-i \
--export-csv wota_vs_nota_vs_json.csv \
--export-json wota_vs_nota_vs_json.json \
--export-markdown wota_vs_nota_vs_json.md \
--parameter-list lib wota,nota,json \
--parameter-list scen empty,integers,floats,strings,objects,nested,large_array \
'cell benchmarks/wota_nota_json {lib} {scen}'
echo "Benchmark complete! Results saved to:"
echo " - wota_vs_nota_vs_json.csv"
echo " - wota_vs_nota_vs_json.json"
echo " - wota_vs_nota_vs_json.md"

395
benchmarks/js_perf.ce Normal file
View File

@@ -0,0 +1,395 @@
var time = use('time')
////////////////////////////////////////////////////////////////////////////////
// JavaScript Performance Benchmark Suite
// Tests core JS operations: property access, function calls, arithmetic, etc.
////////////////////////////////////////////////////////////////////////////////
// Test configurations
const iterations = {
simple: 10000000,
medium: 1000000,
complex: 100000
};
////////////////////////////////////////////////////////////////////////////////
// Utility: measureTime(fn) => how long fn() takes in seconds
////////////////////////////////////////////////////////////////////////////////
function measureTime(fn) {
var start = time.number();
fn();
var end = time.number();
return (end - start);
}
////////////////////////////////////////////////////////////////////////////////
// Benchmark: Property Access
////////////////////////////////////////////////////////////////////////////////
function benchPropertyAccess() {
var obj = {
a: 1, b: 2, c: 3, d: 4, e: 5,
nested: { x: 10, y: 20, z: 30 }
};
var readTime = measureTime(function() {
var sum = 0;
for (var i = 0; i < iterations.simple; i++) {
sum += obj.a + obj.b + obj.c + obj.d + obj.e;
sum += obj.nested.x + obj.nested.y + obj.nested.z;
}
});
var writeTime = measureTime(function() {
for (var i = 0; i < iterations.simple; i++) {
obj.a = i;
obj.b = i + 1;
obj.c = i + 2;
obj.nested.x = i * 2;
obj.nested.y = i * 3;
}
});
return { readTime: readTime, writeTime: writeTime };
}
////////////////////////////////////////////////////////////////////////////////
// Benchmark: Function Calls
////////////////////////////////////////////////////////////////////////////////
function benchFunctionCalls() {
function add(a, b) { return a + b; }
function multiply(a, b) { return a * b; }
function complexCalc(a, b, c) { return (a + b) * c / 2; }
var obj = {
method: function(x) { return x * 2; },
nested: {
deepMethod: function(x, y) { return x + y; }
}
};
var simpleCallTime = measureTime(function() {
var result = 0;
for (var i = 0; i < iterations.simple; i++) {
result = add(i, 1);
result = multiply(result, 2);
}
});
var methodCallTime = measureTime(function() {
var result = 0;
for (var i = 0; i < iterations.simple; i++) {
result = obj.method(i);
result = obj.nested.deepMethod(result, i);
}
});
var complexCallTime = measureTime(function() {
var result = 0;
for (var i = 0; i < iterations.medium; i++) {
result = complexCalc(i, i + 1, i + 2);
}
});
return {
simpleCallTime: simpleCallTime,
methodCallTime: methodCallTime,
complexCallTime: complexCallTime
};
}
////////////////////////////////////////////////////////////////////////////////
// Benchmark: Array Operations
////////////////////////////////////////////////////////////////////////////////
function benchArrayOps() {
var pushTime = measureTime(function() {
var arr = [];
for (var i = 0; i < iterations.medium; i++) {
arr.push(i);
}
});
var arr = [];
for (var i = 0; i < 10000; i++) arr.push(i);
var accessTime = measureTime(function() {
var sum = 0;
for (var i = 0; i < iterations.medium; i++) {
sum += arr[i % 10000];
}
});
var iterateTime = measureTime(function() {
var sum = 0;
for (var j = 0; j < 1000; j++) {
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
}
}
});
return {
pushTime: pushTime,
accessTime: accessTime,
iterateTime: iterateTime
};
}
////////////////////////////////////////////////////////////////////////////////
// Benchmark: Object Creation
////////////////////////////////////////////////////////////////////////////////
function benchObjectCreation() {
var literalTime = measureTime(function() {
for (var i = 0; i < iterations.medium; i++) {
var obj = { x: i, y: i * 2, z: i * 3 };
}
});
function Point(x, y) {
this.x = x;
this.y = y;
}
var constructorTime = measureTime(function() {
for (var i = 0; i < iterations.medium; i++) {
var p = new Point(i, i * 2);
}
});
var protoObj = {
x: 0,
y: 0,
move: function(dx, dy) {
this.x += dx;
this.y += dy;
}
};
var prototypeTime = measureTime(function() {
for (var i = 0; i < iterations.medium; i++) {
var obj = Object.create(protoObj);
obj.x = i;
obj.y = i * 2;
}
});
return {
literalTime: literalTime,
constructorTime: constructorTime,
prototypeTime: prototypeTime
};
}
////////////////////////////////////////////////////////////////////////////////
// Benchmark: String Operations
////////////////////////////////////////////////////////////////////////////////
function benchStringOps() {
var concatTime = measureTime(function() {
var str = "";
for (var i = 0; i < iterations.complex; i++) {
str = "test" + i + "value";
}
});
var strings = [];
for (var i = 0; i < 1000; i++) {
strings.push("string" + i);
}
var joinTime = measureTime(function() {
for (var i = 0; i < iterations.complex; i++) {
var result = strings.join(",");
}
});
var splitTime = measureTime(function() {
var str = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p";
for (var i = 0; i < iterations.medium; i++) {
var parts = str.split(",");
}
});
return {
concatTime: concatTime,
joinTime: joinTime,
splitTime: splitTime
};
}
////////////////////////////////////////////////////////////////////////////////
// Benchmark: Arithmetic Operations
////////////////////////////////////////////////////////////////////////////////
function benchArithmetic() {
var intMathTime = measureTime(function() {
var result = 1;
for (var i = 0; i < iterations.simple; i++) {
result = ((result + i) * 2 - 1) / 3;
result = result % 1000 + 1;
}
});
var floatMathTime = measureTime(function() {
var result = 1.5;
for (var i = 0; i < iterations.simple; i++) {
result = Math.sin(result) + Math.cos(i * 0.01);
result = Math.sqrt(Math.abs(result)) + 0.1;
}
});
var bitwiseTime = measureTime(function() {
var result = 0;
for (var i = 0; i < iterations.simple; i++) {
result = (result ^ i) & 0xFFFF;
result = (result << 1) | (result >> 15);
}
});
return {
intMathTime: intMathTime,
floatMathTime: floatMathTime,
bitwiseTime: bitwiseTime
};
}
////////////////////////////////////////////////////////////////////////////////
// Benchmark: Closure Operations
////////////////////////////////////////////////////////////////////////////////
function benchClosures() {
function makeAdder(x) {
return function(y) { return x + y; };
}
var closureCreateTime = measureTime(function() {
var funcs = [];
for (var i = 0; i < iterations.medium; i++) {
funcs.push(makeAdder(i));
}
});
var adders = [];
for (var i = 0; i < 1000; i++) {
adders.push(makeAdder(i));
}
var closureCallTime = measureTime(function() {
var sum = 0;
for (var i = 0; i < iterations.medium; i++) {
sum += adders[i % 1000](i);
}
});
return {
closureCreateTime: closureCreateTime,
closureCallTime: closureCallTime
};
}
////////////////////////////////////////////////////////////////////////////////
// Main benchmark runner
////////////////////////////////////////////////////////////////////////////////
log.console("JavaScript Performance Benchmark");
log.console("======================\n");
// Property Access
log.console("BENCHMARK: Property Access");
var propResults = benchPropertyAccess();
log.console(" Read time: " + propResults.readTime.toFixed(3) + "s => " +
(iterations.simple / propResults.readTime).toFixed(1) + " reads/sec [" +
(propResults.readTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
log.console(" Write time: " + propResults.writeTime.toFixed(3) + "s => " +
(iterations.simple / propResults.writeTime).toFixed(1) + " writes/sec [" +
(propResults.writeTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
log.console("");
// Function Calls
log.console("BENCHMARK: Function Calls");
var funcResults = benchFunctionCalls();
log.console(" Simple calls: " + funcResults.simpleCallTime.toFixed(3) + "s => " +
(iterations.simple / funcResults.simpleCallTime).toFixed(1) + " calls/sec [" +
(funcResults.simpleCallTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
log.console(" Method calls: " + funcResults.methodCallTime.toFixed(3) + "s => " +
(iterations.simple / funcResults.methodCallTime).toFixed(1) + " calls/sec [" +
(funcResults.methodCallTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
log.console(" Complex calls: " + funcResults.complexCallTime.toFixed(3) + "s => " +
(iterations.medium / funcResults.complexCallTime).toFixed(1) + " calls/sec [" +
(funcResults.complexCallTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console("");
// Array Operations
log.console("BENCHMARK: Array Operations");
var arrayResults = benchArrayOps();
log.console(" Push: " + arrayResults.pushTime.toFixed(3) + "s => " +
(iterations.medium / arrayResults.pushTime).toFixed(1) + " pushes/sec [" +
(arrayResults.pushTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console(" Access: " + arrayResults.accessTime.toFixed(3) + "s => " +
(iterations.medium / arrayResults.accessTime).toFixed(1) + " accesses/sec [" +
(arrayResults.accessTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console(" Iterate: " + arrayResults.iterateTime.toFixed(3) + "s => " +
(1000 / arrayResults.iterateTime).toFixed(1) + " full iterations/sec");
log.console("");
// Object Creation
log.console("BENCHMARK: Object Creation");
var objResults = benchObjectCreation();
log.console(" Literal: " + objResults.literalTime.toFixed(3) + "s => " +
(iterations.medium / objResults.literalTime).toFixed(1) + " creates/sec [" +
(objResults.literalTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console(" Constructor: " + objResults.constructorTime.toFixed(3) + "s => " +
(iterations.medium / objResults.constructorTime).toFixed(1) + " creates/sec [" +
(objResults.constructorTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console(" Prototype: " + objResults.prototypeTime.toFixed(3) + "s => " +
(iterations.medium / objResults.prototypeTime).toFixed(1) + " creates/sec [" +
(objResults.prototypeTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console("");
// String Operations
log.console("BENCHMARK: String Operations");
var strResults = benchStringOps();
log.console(" Concat: " + strResults.concatTime.toFixed(3) + "s => " +
(iterations.complex / strResults.concatTime).toFixed(1) + " concats/sec [" +
(strResults.concatTime / iterations.complex * 1e9).toFixed(1) + " ns/op]");
log.console(" Join: " + strResults.joinTime.toFixed(3) + "s => " +
(iterations.complex / strResults.joinTime).toFixed(1) + " joins/sec [" +
(strResults.joinTime / iterations.complex * 1e9).toFixed(1) + " ns/op]");
log.console(" Split: " + strResults.splitTime.toFixed(3) + "s => " +
(iterations.medium / strResults.splitTime).toFixed(1) + " splits/sec [" +
(strResults.splitTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console("");
// Arithmetic Operations
log.console("BENCHMARK: Arithmetic Operations");
var mathResults = benchArithmetic();
log.console(" Integer math: " + mathResults.intMathTime.toFixed(3) + "s => " +
(iterations.simple / mathResults.intMathTime).toFixed(1) + " ops/sec [" +
(mathResults.intMathTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
log.console(" Float math: " + mathResults.floatMathTime.toFixed(3) + "s => " +
(iterations.simple / mathResults.floatMathTime).toFixed(1) + " ops/sec [" +
(mathResults.floatMathTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
log.console(" Bitwise: " + mathResults.bitwiseTime.toFixed(3) + "s => " +
(iterations.simple / mathResults.bitwiseTime).toFixed(1) + " ops/sec [" +
(mathResults.bitwiseTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
log.console("");
// Closures
log.console("BENCHMARK: Closures");
var closureResults = benchClosures();
log.console(" Create: " + closureResults.closureCreateTime.toFixed(3) + "s => " +
(iterations.medium / closureResults.closureCreateTime).toFixed(1) + " creates/sec [" +
(closureResults.closureCreateTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console(" Call: " + closureResults.closureCallTime.toFixed(3) + "s => " +
(iterations.medium / closureResults.closureCallTime).toFixed(1) + " calls/sec [" +
(closureResults.closureCallTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console("");
log.console("---------------------------------------------------------");
log.console("Benchmark complete.\n");
$_.stop()

40
benchmarks/mandelbrot.ce Normal file
View File

@@ -0,0 +1,40 @@
var blob = use('blob')
var iter = 50, limit = 2.0;
var zr, zi, cr, ci, tr, ti;
var h = Number(arg[0]) || 500
var w = h
log.console(`P4\n${w} ${h}`);
for (let y = 0; y < h; ++y) {
// Create a blob for the row - we need w bits
var row = new blob(w);
for (let x = 0; x < w; ++x) {
zr = zi = tr = ti = 0;
cr = 2 * x / w - 1.5;
ci = 2 * y / h - 1;
for (let i = 0; i < iter && (tr + ti <= limit * limit); ++i) {
zi = 2 * zr * zi + ci;
zr = tr - ti + cr;
tr = zr * zr;
ti = zi * zi;
}
// Write a 1 bit if inside the set, 0 if outside
if (tr + ti <= limit * limit)
row.write_bit(1);
else
row.write_bit(0);
}
// Convert the blob to stone (immutable) to prepare for output
stone(row)
// Output the blob data as raw bytes
log.console(text(row, 'b'));
}
$_.stop()

11
benchmarks/montecarlo.ce Normal file
View File

@@ -0,0 +1,11 @@
var N = 1000000;
var num = 0;
for (var i = 0; i < N; i ++) {
var x = 2 * $_.random();
var y = $_.random();
if (y < Math.sin(x * x))
num++;
}
log.console(2 * num / N);
$_.stop()

161
benchmarks/nbody.ce Normal file
View File

@@ -0,0 +1,161 @@
var PI = Math.PI;
var SOLAR_MASS = 4 * PI * PI;
var DAYS_PER_YEAR = 365.24;
function Body(x, y, z, vx, vy, vz, mass) {
this.x = x;
this.y = y;
this.z = z;
this.vx = vx;
this.vy = vy;
this.vz = vz;
this.mass = mass;
}
function Jupiter() {
return new Body(
4.84143144246472090e+00,
-1.16032004402742839e+00,
-1.03622044471123109e-01,
1.66007664274403694e-03 * DAYS_PER_YEAR,
7.69901118419740425e-03 * DAYS_PER_YEAR,
-6.90460016972063023e-05 * DAYS_PER_YEAR,
9.54791938424326609e-04 * SOLAR_MASS
);
}
function Saturn() {
return new Body(
8.34336671824457987e+00,
4.12479856412430479e+00,
-4.03523417114321381e-01,
-2.76742510726862411e-03 * DAYS_PER_YEAR,
4.99852801234917238e-03 * DAYS_PER_YEAR,
2.30417297573763929e-05 * DAYS_PER_YEAR,
2.85885980666130812e-04 * SOLAR_MASS
);
}
function Uranus() {
return new Body(
1.28943695621391310e+01,
-1.51111514016986312e+01,
-2.23307578892655734e-01,
2.96460137564761618e-03 * DAYS_PER_YEAR,
2.37847173959480950e-03 * DAYS_PER_YEAR,
-2.96589568540237556e-05 * DAYS_PER_YEAR,
4.36624404335156298e-05 * SOLAR_MASS
);
}
function Neptune() {
return new Body(
1.53796971148509165e+01,
-2.59193146099879641e+01,
1.79258772950371181e-01,
2.68067772490389322e-03 * DAYS_PER_YEAR,
1.62824170038242295e-03 * DAYS_PER_YEAR,
-9.51592254519715870e-05 * DAYS_PER_YEAR,
5.15138902046611451e-05 * SOLAR_MASS
);
}
function Sun() {
return new Body(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, SOLAR_MASS);
}
var bodies = Array(Sun(), Jupiter(), Saturn(), Uranus(), Neptune());
function offsetMomentum() {
let px = 0;
let py = 0;
let pz = 0;
var size = bodies.length;
for (let i = 0; i < size; i++) {
var body = bodies[i];
var mass = body.mass;
px += body.vx * mass;
py += body.vy * mass;
pz += body.vz * mass;
}
var body = bodies[0];
body.vx = -px / SOLAR_MASS;
body.vy = -py / SOLAR_MASS;
body.vz = -pz / SOLAR_MASS;
}
function advance(dt) {
var size = bodies.length;
for (let i = 0; i < size; i++) {
var bodyi = bodies[i];
let vxi = bodyi.vx;
let vyi = bodyi.vy;
let vzi = bodyi.vz;
for (let j = i + 1; j < size; j++) {
var bodyj = bodies[j];
var dx = bodyi.x - bodyj.x;
var dy = bodyi.y - bodyj.y;
var dz = bodyi.z - bodyj.z;
var d2 = dx * dx + dy * dy + dz * dz;
var mag = dt / (d2 * Math.sqrt(d2));
var massj = bodyj.mass;
vxi -= dx * massj * mag;
vyi -= dy * massj * mag;
vzi -= dz * massj * mag;
var massi = bodyi.mass;
bodyj.vx += dx * massi * mag;
bodyj.vy += dy * massi * mag;
bodyj.vz += dz * massi * mag;
}
bodyi.vx = vxi;
bodyi.vy = vyi;
bodyi.vz = vzi;
}
for (let i = 0; i < size; i++) {
var body = bodies[i];
body.x += dt * body.vx;
body.y += dt * body.vy;
body.z += dt * body.vz;
}
}
function energy() {
let e = 0;
var size = bodies.length;
for (let i = 0; i < size; i++) {
var bodyi = bodies[i];
e += 0.5 * bodyi.mass * ( bodyi.vx * bodyi.vx +
bodyi.vy * bodyi.vy + bodyi.vz * bodyi.vz );
for (let j = i + 1; j < size; j++) {
var bodyj = bodies[j];
var dx = bodyi.x - bodyj.x;
var dy = bodyi.y - bodyj.y;
var dz = bodyi.z - bodyj.z;
var distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
e -= (bodyi.mass * bodyj.mass) / distance;
}
}
return e;
}
var n = arg[0] || 1000000
offsetMomentum();
log.console(`n = ${n}`)
log.console(energy().toFixed(9))
for (let i = 0; i < n; i++)
advance(0.01);
log.console(energy().toFixed(9))
$_.stop()

76
benchmarks/nota.ce Normal file
View File

@@ -0,0 +1,76 @@
var nota = use('nota')
var os = use('os')
var io = use('io')
var ll = io.slurp('benchmarks/nota.json')
var newarr = []
var accstr = ""
for (var i = 0; i < 10000; i++) {
accstr += i;
newarr.push(i.toString())
}
// Arrays to store timing results
var jsonDecodeTimes = [];
var jsonEncodeTimes = [];
var notaEncodeTimes = [];
var notaDecodeTimes = [];
var notaSizes = [];
// Run 100 tests
for (let i = 0; i < 100; i++) {
// JSON Decode test
let start = os.now();
var jll = json.decode(ll);
jsonDecodeTimes.push((os.now() - start) * 1000);
// JSON Encode test
start = os.now();
let jsonStr = JSON.stringify(jll);
jsonEncodeTimes.push((os.now() - start) * 1000);
// NOTA Encode test
start = os.now();
var nll = nota.encode(jll);
notaEncodeTimes.push((os.now() - start) * 1000);
// NOTA Decode test
start = os.now();
var oll = nota.decode(nll);
notaDecodeTimes.push((os.now() - start) * 1000);
}
// Calculate statistics
function getStats(arr) {
def avg = arr.reduce((a, b) => a + b) / arr.length;
def min = Math.min(...arr);
def max = Math.max(...arr);
return { avg, min, max };
}
// Pretty print results
log.console("\n== Performance Test Results (100 iterations) ==");
log.console("\nJSON Decoding (ms):");
def jsonDecStats = getStats(jsonDecodeTimes);
log.console(`Average: ${jsonDecStats.avg.toFixed(2)} ms`);
log.console(`Min: ${jsonDecStats.min.toFixed(2)} ms`);
log.console(`Max: ${jsonDecStats.max.toFixed(2)} ms`);
log.console("\nJSON Encoding (ms):");
def jsonEncStats = getStats(jsonEncodeTimes);
log.console(`Average: ${jsonEncStats.avg.toFixed(2)} ms`);
log.console(`Min: ${jsonEncStats.min.toFixed(2)} ms`);
log.console(`Max: ${jsonEncStats.max.toFixed(2)} ms`);
log.console("\nNOTA Encoding (ms):");
def notaEncStats = getStats(notaEncodeTimes);
log.console(`Average: ${notaEncStats.avg.toFixed(2)} ms`);
log.console(`Min: ${notaEncStats.min.toFixed(2)} ms`);
log.console(`Max: ${notaEncStats.max.toFixed(2)} ms`);
log.console("\nNOTA Decoding (ms):");
def notaDecStats = getStats(notaDecodeTimes);
log.console(`Average: ${notaDecStats.avg.toFixed(2)} ms`);
log.console(`Min: ${notaDecStats.min.toFixed(2)} ms`);
log.console(`Max: ${notaDecStats.max.toFixed(2)} ms`);

2132
benchmarks/nota.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,50 @@
function A(i,j) {
return 1/((i+j)*(i+j+1)/2+i+1);
}
function Au(u,v) {
for (var i=0; i<u.length; ++i) {
var t = 0;
for (var j=0; j<u.length; ++j)
t += A(i,j) * u[j];
v[i] = t;
}
}
function Atu(u,v) {
for (var i=0; i<u.length; ++i) {
var t = 0;
for (var j=0; j<u.length; ++j)
t += A(j,i) * u[j];
v[i] = t;
}
}
function AtAu(u,v,w) {
Au(u,w);
Atu(w,v);
}
function spectralnorm(n) {
var i, u=[], v=[], w=[], vv=0, vBv=0;
for (i=0; i<n; ++i)
u[i] = 1; v[i] = w[i] = 0;
for (i=0; i<10; ++i) {
AtAu(u,v,w);
AtAu(v,u,w);
}
for (i=0; i<n; ++i) {
vBv += u[i]*v[i];
vv += v[i]*v[i];
}
return Math.sqrt(vBv/vv);
}
log.console(spectralnorm(arg[0]).toFixed(9));
$_.stop()

106
benchmarks/wota.ce Normal file
View File

@@ -0,0 +1,106 @@
//
// wota_benchmark.js
//
// Usage in QuickJS:
// qjs wota_benchmark.js
//
// Prerequisite:
var wota = use('wota');
var os = use('os');
// or otherwise ensure `wota` and `os` are available.
// Make sure wota_benchmark.js is loaded after wota.js or combined with it.
//
// Helper to run a function repeatedly and measure total time in seconds.
// Returns elapsed time in seconds.
function measureTime(fn, iterations) {
let t1 = os.now();
for (let i = 0; i < iterations; i++) {
fn();
}
let t2 = os.now();
return t2 - t1;
}
// We'll define a function that does `encode -> decode` for a given value:
function roundTripWota(value) {
let encoded = wota.encode(value);
let decoded = wota.decode(encoded);
// Not doing a deep compare here, just measuring performance.
// (We trust the test suite to verify correctness.)
}
// A small suite of data we want to benchmark. Each entry includes:
// name: label for printing
// data: the test value(s) to encode/decode
// iterations: how many times to loop
//
// You can tweak these as you like for heavier or lighter tests.
def benchmarks = [
{
name: "Small Integers",
data: [0, 42, -1, 2023],
iterations: 100000
},
{
name: "Strings (short, emoji)",
data: ["Hello, Wota!", "short", "Emoji: \u{1f600}\u{1f64f}"],
iterations: 100000
},
{
name: "Small Objects",
data: [
{ a:1, b:2.2, c:"3", d:false },
{ x:42, y:null, z:"test" }
],
iterations: 50000
},
{
name: "Nested Arrays",
data: [ [ [ [1,2], [3,4] ] ], [[[]]], [1, [2, [3, [4]]]] ],
iterations: 50000
},
{
name: "Large Array (1k numbers)",
// A thousand random numbers
data: [ Array.from({length:1000}, (_, i) => i * 0.5) ],
iterations: 1000
},
{
name: "Large Binary Blob (256KB)",
// A 256KB ArrayBuffer
data: [ new Uint8Array(256 * 1024).buffer ],
iterations: 200
}
];
// Print a header
log.console("Wota Encode/Decode Benchmark");
log.console("===================\n");
// We'll run each benchmark scenario in turn.
for (let bench of benchmarks) {
// We'll measure how long it takes to do 'iterations' *for each test value*
// in bench.data. The total loop count is `bench.iterations * bench.data.length`.
// Then we compute an overall encode+decode throughput (ops/s).
let totalIterations = bench.iterations * bench.data.length;
// We'll define a function that does a roundTrip for *each* data item in bench.data
// to measure in one loop iteration. Then we multiply by bench.iterations.
function runAllData() {
for (let val of bench.data) {
roundTripWota(val);
}
}
let elapsedSec = measureTime(runAllData, bench.iterations);
let opsPerSec = (totalIterations / elapsedSec).toFixed(1);
log.console(`${bench.name}:`);
log.console(` Iterations: ${bench.iterations} × ${bench.data.length} data items = ${totalIterations}`);
log.console(` Elapsed: ${elapsedSec.toFixed(3)} s`);
log.console(` Throughput: ${opsPerSec} encode+decode ops/sec\n`);
}
// All done
log.console("Benchmark completed.\n");

View File

@@ -0,0 +1,204 @@
//
// benchmark_wota_nota_json.js
//
// Usage in QuickJS:
// qjs benchmark_wota_nota_json.js <LibraryName> <ScenarioName>
//
// Ensure wota, nota, json, and os are all available, e.g.:
var wota = use('wota');
var nota = use('nota');
var json = use('json');
var jswota = use('jswota')
var os = use('os');
//
// Parse command line arguments
if (arg.length != 2) {
log.console('Usage: cell benchmark_wota_nota_json.ce <LibraryName> <ScenarioName>');
$_.stop()
}
var lib_name = arg[0];
var scenario_name = arg[1];
////////////////////////////////////////////////////////////////////////////////
// 1. Setup "libraries" array to easily switch among wota, nota, and json
////////////////////////////////////////////////////////////////////////////////
def libraries = [
{
name: "wota",
encode: wota.encode,
decode: wota.decode,
// wota produces an ArrayBuffer. We'll count `buffer.byteLength` as size.
getSize(encoded) {
return encoded.length;
}
},
{
name: "nota",
encode: nota.encode,
decode: nota.decode,
// nota also produces an ArrayBuffer:
getSize(encoded) {
return encoded.length;
}
},
{
name: "json",
encode: json.encode,
decode: json.decode,
// json produces a JS string. We'll measure its UTF-16 code unit length
// as a rough "size". Alternatively, you could convert to UTF-8 for
// a more accurate byte size. Here we just use `string.length`.
getSize(encodedStr) {
return encodedStr.length;
}
}
];
////////////////////////////////////////////////////////////////////////////////
// 2. Test data sets (similar to wota benchmarks).
// Each scenario has { name, data, iterations }
////////////////////////////////////////////////////////////////////////////////
def benchmarks = [
{
name: "empty",
data: [{}, {}, {}, {}],
iterations: 10000
},
{
name: "integers",
data: [0, 42, -1, 2023],
iterations: 100000
},
{
name: "floats",
data: [0.1, 1e-50, 3.14159265359],
iterations: 100000
},
{
name: "strings",
data: ["Hello, wota!", "short", "Emoji: \u{1f600}\u{1f64f}"],
iterations: 100000
},
{
name: "objects",
data: [
{ a:1, b:2.2, c:"3", d:false },
{ x:42, y:null, z:"test" }
],
iterations: 50000
},
{
name: "nested",
data: [ [ [ [1,2], [3,4] ] ], [[[]]], [1, [2, [3, [4]]]] ],
iterations: 50000
},
{
name: "large_array",
data: [ Array.from({length:1000}, (_, i) => i) ],
iterations: 1000
},
];
////////////////////////////////////////////////////////////////////////////////
// 3. Utility: measureTime(fn) => how long fn() takes in seconds.
////////////////////////////////////////////////////////////////////////////////
function measureTime(fn) {
let start = os.now();
fn();
let end = os.now();
return (end - start); // in seconds
}
////////////////////////////////////////////////////////////////////////////////
// 4. For each library, we run each benchmark scenario and measure:
// - Encoding time (seconds)
// - Decoding time (seconds)
// - Total encoded size (bytes or code units for json)
//
////////////////////////////////////////////////////////////////////////////////
function runBenchmarkForLibrary(lib, bench) {
// We'll encode and decode each item in `bench.data`.
// We do 'bench.iterations' times. Then sum up total time.
// Pre-store the encoded results for all items so we can measure decode time
// in a separate pass. Also measure total size once.
let encodedList = [];
let totalSize = 0;
// 1) Measure ENCODING
let encodeTime = measureTime(() => {
for (let i = 0; i < bench.iterations; i++) {
// For each data item, encode it
for (let j = 0; j < bench.data.length; j++) {
let e = lib.encode(bench.data[j]);
// store only in the very first iteration, so we can decode them later
// but do not store them every iteration or we blow up memory.
if (i == 0) {
encodedList.push(e);
totalSize += lib.getSize(e);
}
}
}
});
// 2) Measure DECODING
let decodeTime = measureTime(() => {
for (let i = 0; i < bench.iterations; i++) {
// decode everything we stored during the first iteration
for (let e of encodedList) {
let decoded = lib.decode(e);
// not verifying correctness here, just measuring speed
}
}
});
return { encodeTime, decodeTime, totalSize };
}
////////////////////////////////////////////////////////////////////////////////
// 5. Main driver: run only the specified library and scenario
////////////////////////////////////////////////////////////////////////////////
// Find the requested library and scenario
var lib = libraries.find(l => l.name == lib_name);
var bench = benchmarks.find(b => b.name == scenario_name);
if (!lib) {
log.console('Unknown library:', lib_name);
log.console('Available libraries:', libraries.map(l => l.name).join(', '));
$_.stop()
}
if (!bench) {
log.console('Unknown scenario:', scenario_name);
log.console('Available scenarios:', benchmarks.map(b => b.name).join(', '));
$_.stop()
}
// Run the benchmark for this library/scenario combination
var { encodeTime, decodeTime, totalSize } = runBenchmarkForLibrary(lib, bench);
// Output json for easy parsing by hyperfine or other tools
var totalOps = bench.iterations * bench.data.length;
var result = {
lib: lib_name,
scenario: scenario_name,
encodeTime: encodeTime,
decodeTime: decodeTime,
totalSize: totalSize,
totalOps: totalOps,
encodeOpsPerSec: totalOps / encodeTime,
decodeOpsPerSec: totalOps / decodeTime,
encodeNsPerOp: (encodeTime / totalOps) * 1e9,
decodeNsPerOp: (decodeTime / totalOps) * 1e9
};
log.console(json.encode(result));
$_.stop()

128
cell.md Normal file
View File

@@ -0,0 +1,128 @@
JAVASCRIPT VISION
I see objects as being a sort of combination of a lisp cell and a record: symbols, which are used internally, and are private and non iterable, and record string values, which are iterable, readable, and writable; of course everything becomes locked in when stone.
This lets the runtime have its own set of symbols, accessible via "cell.+", "cell.-", etc.
but then you can also have modules, imported via use, which expose their own symbols: use('math') can give you a set of symbols like "torads", which you can then define on your objects to specify how they should react to "torads".
CELLSCRIPT
Javascript to its core. Objects. What does the language need? It can be quite small, I think. The key is, ANYTHING that we want to be fast and JIT'd, must be present. So, record lookups. These are actually quicker in a jit'd language that have them as a feature. Most things should be libraries. Blobs need to be in the runtime.
The purpose of this is to be a great language for passing messages. So it should be fast at creating records first and foremost, and finding items on them. So it needs first class, jitt'd records.
Finally, it needs to use less memory. Deleting a bunch of this stuff should make that simpler.
What is present?
Objects, prototypes, numbers, arrays, strings, true, false, null.
Things to do:
merge typeof and instanceof. Misty has array? stone? number? etc; it needs to be generic. 5 is number returns true.
No new operator. It's the same idea though: simply instead of 'var guy = new sprite({x,y})' you would say 'var guy = sprite({x,y})', and sprite would simply be a function written to return a sprite object.
One number type. Dec64. Numeric stack can be added in later: a bigint library, for example, built inside cell.
Simplify the property attributes stuff. It is simple: objects have text keys and whatever values. Objects can also have objects as values. These work like symbols. You can share them, if desired. No well known symbols exist to eliminate that much misdirection. Obejcts basically work like private keys. If you serialize an object, objects that are keys are not serialized; only textual keys are. You can do something about it with a json() method that is invoked, if you desire. You cannot retrieve
var works like let; use var instead of let
no const
Function closures and _ => all work the same and close over the 'this' variable
Totally delete modules, coroutines, generators, proxy .. this deletes a lot of the big switch statement
Add the 'go' statement for tail calls
Add the 'do' statement
Implementation detail: separate out arrays and objects. They are not the same. Objects no longer need to track if they're fast arrays or not. They're not. Arrays are. Always.
Add the functional proxy idea. Log will be implemented through that.
Remove ===; it's just == now, and !=.
Remove 'continue'; now, break handles both. For a do statement, label it, and break to that label; so
var x = 0
do loop {
x++
if (x < 5) break loop // goes back to loop
break // exits loop
}
rename instanceof to 'is'
remove undefined; all are 'null' now
remove 'delete'; to remove a field, assign it to null
remove with
Remove Object. New records have a prototype of nothing. There are no more 'type prototypes' at all.
Arrays are their own type
Remove property descriptors. Properties are always settable, unless the object as a whole is stone. Stone is an object property instead of a shape property.
Syntax stuff .. would like to invoke functions without (). This can effectively simulate a "getter". Make ? and all other characters usable for names. No reserve words, which are endlessly irritating.
----
This will all actually come about gradually. Add a few things at a time, fix up code that did not adhere. For a lot of this, no new functions will even need to be written; it's a matter of not calling certain functions that are no longer relevant, or calling different functions when required.
## Benchmarks to implement
### general speed
binarytrees
coro-prime-sieve
edigits
fannkuch-redux
fasta
http-server
json serialize/deserialize
knucleotide
lru
mandelbrot
merkletrees
nbody
nsieve
pidigits
regex-redux
secp256k1
spectral-norm
### function calling and recursion stress - test goto
naive recursive fibonacci [fib(35) or fib(40)]
tak
ackermann
### numeric
sieve of eratosthenes [10^7 bits]
spectral norm [5500 x 5500 matrix]
n-body sim [50 000 - 100 000 steps]
mandelbrot [1600x1200 image, max iter = 50]
### memory & gc torture
binary trees [depth 18 (~500 000 nodes)]
richards task scheduler
fannkuch redux [n=11 or 12]
### dynamic object & property access
deltablue constraint solver
splay tree [256k nodes]
json, wota, nota decode->encode [use 2MB example]
### string / regex kernels
regex-DNA
fasta
word-frequency
### concurrency/message passing
ping-pong [two actors exhange a small record N times, 1M messages end to end]
chameneos [mating color swap game w/ randezvous]
For all, track memory and time.

View File

@@ -47,7 +47,7 @@ Certain functions are intrinsic to the program and cannot be overridden. They
- **Example**: - **Example**:
```js ```js
this.delay(_ => { this.delay(_ => {
console.log("3 seconds later!") log.console("3 seconds later!")
}, 3) }, 3)
``` ```

View File

@@ -0,0 +1,46 @@
# camera
### list() <sub>function</sub>
Return an array of available camera device IDs.
**Returns**: An array of camera IDs, or undefined if no cameras are available.
### open(id) <sub>function</sub>
Open a camera device with the given ID.
**id**: The camera ID to open.
**Returns**: A camera object on success, or throws an error if the camera cannot be opened.
### name(id) <sub>function</sub>
Return the name of the camera with the given ID.
**id**: The camera ID to query.
**Returns**: A string with the camera's name, or throws an error if the name cannot be retrieved.
### position(id) <sub>function</sub>
Return the physical position of the camera with the given ID.
**id**: The camera ID to query.
**Returns**: A string indicating the camera position ("unknown", "front", or "back").

76
docs/api/modules/debug.md Normal file
View File

@@ -0,0 +1,76 @@
# debug
### stack_depth() <sub>function</sub>
Return the current stack depth.
**Returns**: A number representing the stack depth.
### build_backtrace() <sub>function</sub>
Build and return a backtrace of the current call stack.
**Returns**: An object representing the call stack backtrace.
### closure_vars(fn) <sub>function</sub>
Return the closure variables for a given function.
**fn**: The function object to inspect.
**Returns**: An object containing the closure variables.
### local_vars(depth) <sub>function</sub>
Return the local variables for a specific stack frame.
**depth**: The stack frame depth to inspect.
**Returns**: An object containing the local variables at the specified depth.
### fn_info(fn) <sub>function</sub>
Return metadata about a given function.
**fn**: The function object to inspect.
**Returns**: An object with metadata about the function.
### backtrace_fns() <sub>function</sub>
Return an array of functions in the current backtrace.
**Returns**: An array of function objects from the call stack.
### dump_obj(obj) <sub>function</sub>
Return a string representation of a given object.
**obj**: The object to dump.
**Returns**: A string describing the object's contents.

39
docs/api/modules/dmon.md Normal file
View File

@@ -0,0 +1,39 @@
# dmon
### watch() <sub>function</sub>
Start watching the root directory, recursively.
This function begins monitoring the specified directory and its subdirectories recursively for events such as file creation, deletion, modification, or movement. Events are queued and can be retrieved by calling poll.
:throws: An error if dmon is already watching.
**Returns**: None
### unwatch() <sub>function</sub>
Stop watching the currently monitored directory.
This function halts filesystem monitoring for the directory previously set by watch. It clears the watch state, allowing a new watch to be started.
:throws: An error if no directory is currently being watched.
**Returns**: None
### poll(callback) <sub>function</sub>
Retrieve and process queued filesystem events.
This function dequeues all pending filesystem events and invokes the provided callback for each one. The callback receives an event object with properties: 'action' (string: "create", "delete", "modify", or "move"), 'root' (string: watched directory), 'file' (string: affected file path), and 'old' (string: previous file path for move events, empty if not applicable).
**callback**: A function to call for each event, receiving an event object as its argument.
**Returns**: None

View File

@@ -3,11 +3,11 @@
Provides a consistent way to create documentation for prosperon elements. Objects are documented by adding docstrings directly to object-like things (functions, objects, ...), or to an object's own "doc object". Provides a consistent way to create documentation for prosperon elements. Objects are documented by adding docstrings directly to object-like things (functions, objects, ...), or to an object's own "doc object".
Docstrings are set to the symbol `prosperon.DOC` Docstrings are set to the symbol `cell.DOC`
```js ```js
// Suppose we have a module that returns a function // Suppose we have a module that returns a function
function greet(name) { console.log("Hello, " + name) } function greet(name) { log.console("Hello, " + name) }
// We can attach a docstring // We can attach a docstring
greet.doc = ` greet.doc = `
@@ -21,12 +21,12 @@ return greet
```js ```js
// Another way is to add a docstring object to an object // Another way is to add a docstring object to an object
var greet = { var greet = {
hello() { console.log('hello!') } hello() { log.console('hello!') }
} }
greet[prosperon.DOC] = {} greet[cell.DOC] = {}
greet[prosperon.DOC][prosperon.DOC] = 'An object full of different greeter functions' greet[cell.DOC][cell.DOC] = 'An object full of different greeter functions'
greet[prosperon.DOC].hello = 'A greeter that says, "hello!"' greet[cell.DOC].hello = 'A greeter that says, "hello!"'
``` ```

45
docs/api/modules/enet.md Normal file
View File

@@ -0,0 +1,45 @@
# enet
### initialize() <sub>function</sub>
Initialize the ENet library. Must be called before using any ENet functionality.
Throws an error if initialization fails.
**Returns**: None
### deinitialize() <sub>function</sub>
Deinitialize the ENet library, cleaning up all resources. Call this when you no longer
need any ENet functionality.
**Returns**: None
### create_host(address) <sub>function</sub>
Create an ENet host for either a client-like unbound host or a server bound to a specific
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.
omit to create an unbound client-like host.
**address**: (optional) A string in 'ip:port' format to bind the host (server), or
**Returns**: An ENetHost object.

File diff suppressed because it is too large Load Diff

View File

@@ -66,23 +66,3 @@ and booleans for pressed buttons (left, middle, right, x1, x2).
**Returns**: Object { x, y, left, middle, right, x1, x2 } **Returns**: Object { x, y, left, middle, right, x1, x2 }
### mouse <sub>object</sub>
### keyboard <sub>object</sub>
### print_pawn_kbm(pawn) <sub>function</sub>
### procdown() <sub>function</sub>
### print_md_kbm(pawn) <sub>function</sub>
### has_bind(pawn, bind) <sub>function</sub>
### action <sub>object</sub>
### tabcomplete(val, list) <sub>function</sub>
### do_uncontrol(pawn) <sub>function</sub>
### player <sub>object</sub>

View File

@@ -174,12 +174,17 @@ Return the application's base directory (where the executable is located).
**Returns**: A string with the base directory path. **Returns**: A string with the base directory path.
### userdir() <sub>function</sub> ### prefdir(org, app) <sub>function</sub>
Return the user's directory, often used for saving data. Get the user-and-app-specific path where files can be written.
**org**: The name of your organization.
**app**: The name of your application.
**Returns**: A string with the user's directory path. **Returns**: A string with the user's directory path.

30
docs/api/modules/nota.md Normal file
View File

@@ -0,0 +1,30 @@
# nota
### encode(value) <sub>function</sub>
Convert a JavaScript value into a NOTA-encoded ArrayBuffer.
This function serializes JavaScript values (such as numbers, strings, booleans, arrays, objects, or ArrayBuffers) into the NOTA binary format. The resulting ArrayBuffer can be stored or transmitted and later decoded back into a JavaScript value.
:throws: An error if no argument is provided.
**value**: The JavaScript value to encode (e.g., number, string, boolean, array, object, or ArrayBuffer).
**Returns**: An ArrayBuffer containing the NOTA-encoded data.
### decode(buffer) <sub>function</sub>
Decode a NOTA-encoded ArrayBuffer into a JavaScript value.
This function deserializes a NOTA-formatted ArrayBuffer into its corresponding JavaScript representation, such as a number, string, boolean, array, object, or ArrayBuffer. If the input is invalid or empty, it returns undefined.
**buffer**: An ArrayBuffer containing NOTA-encoded data to decode.
**Returns**: The decoded JavaScript value (e.g., number, string, boolean, array, object, or ArrayBuffer), or undefined if no argument is provided.

View File

@@ -33,71 +33,3 @@
### imgui(...args) <sub>function</sub> ### imgui(...args) <sub>function</sub>
### app(...args) <sub>function</sub> ### app(...args) <sub>function</sub>
### date <sub>string</sub>
### camera <sub>object</sub>
### debug <sub>boolean</sub>
### semver <sub>object</sub>
### title <sub>string</sub>
### width <sub>number</sub>
### height <sub>number</sub>
### size <sub>object</sub>
### icon <sub>object</sub>
### high_dpi <sub>number</sub>
### alpha <sub>number</sub>
### fullscreen <sub>number</sub>
### sample_count <sub>number</sub>
### enable_clipboard <sub>boolean</sub>
### enable_dragndrop <sub>boolean</sub>
### max_dropped_files <sub>number</sub>
### swap_interval <sub>number</sub>
### name <sub>string</sub>
### identifier <sub>string</sub>
### creator <sub>string</sub>
### copyright <sub>string</sub>
### type <sub>string</sub>
### url <sub>string</sub>
### postvals <sub>object</sub>
### hudcam <sub>object</sub>
### appcam <sub>object</sub>
### screencolor <sub>object</sub>
### window <sub>object</sub>
An application window, created via prosperon.engine_start or SDL calls. Freed on GC.
### font <sub>object</sub>
### gpu <sub>object</sub>
A handle for low-level GPU operations via SDL GPU. Freed on GC.
### exit() <sub>function</sub>

View File

@@ -0,0 +1,66 @@
# enet_host
### service(callback, timeout) <sub>function</sub>
Poll for and process any available network events (connect, receive, disconnect, or none)
from this host, calling the provided callback for each event. This function loops until
no more events are available in the current timeframe.
Event object properties:
- type: String, one of "connect", "receive", "disconnect", or "none".
- peer: (present if type = "connect") The ENetPeer object for the new connection.
- channelID: (present if type = "receive") The channel on which the data was received.
- data: (present if type = "receive") The received data as a *plain JavaScript object*.
If the JSON parse fails or the data isn't an object, a JavaScript error is thrown.
object as its single argument.
**callback**: A function called once for each available event, receiving an event
**timeout**: (optional) Timeout in milliseconds. Defaults to 0 (non-blocking).
**Returns**: None
### connect(host, port) <sub>function</sub>
Initiate a connection from this host to a remote server. Throws an error if the
connection cannot be started.
**host**: The hostname or IP address of the remote server (e.g. "example.com" or "127.0.0.1").
**port**: The port number to connect to.
**Returns**: An ENetPeer object representing the connection.
### flush() <sub>function</sub>
Flush all pending outgoing packets for this host immediately.
**Returns**: None
### broadcast(data) <sub>function</sub>
Broadcast a JavaScript object to all connected peers on channel 0. The object is
serialized to JSON, and the packet is sent reliably. Throws an error if serialization fails.
**data**: A JavaScript object to broadcast to all peers.
**Returns**: None

102
docs/api/types/enet_peer.md Normal file
View File

@@ -0,0 +1,102 @@
# enet_peer
### send(data) <sub>function</sub>
Send a JavaScript object to this peer on channel 0. The object is serialized to JSON and
sent reliably. Throws an error if serialization fails.
**data**: A JavaScript object to send.
**Returns**: None
### disconnect() <sub>function</sub>
Request a graceful disconnection from this peer. The connection will close after
pending data is sent.
**Returns**: None
### disconnect_now() <sub>function</sub>
Immediately terminate the connection to this peer, discarding any pending data.
**Returns**: None
### disconnect_later() <sub>function</sub>
Request a disconnection from this peer after all queued packets are sent.
**Returns**: None
### reset() <sub>function</sub>
Reset this peer's connection, immediately dropping it and clearing its internal state.
**Returns**: None
### ping() <sub>function</sub>
Send a ping request to this peer to measure latency.
**Returns**: None
### throttle_configure(interval, acceleration, deceleration) <sub>function</sub>
Configure the throttling behavior for this peer, controlling how ENet adjusts its sending
rate based on packet loss or congestion.
**interval**: The interval (ms) between throttle adjustments.
**acceleration**: The factor to increase sending speed when conditions improve.
**deceleration**: The factor to decrease sending speed when conditions worsen.
**Returns**: None
### timeout(timeout_limit, timeout_min, timeout_max) <sub>function</sub>
Set timeout parameters for this peer, determining how long ENet waits before considering
the connection lost.
**timeout_limit**: The total time (ms) before the peer is disconnected.
**timeout_min**: The minimum timeout (ms) used for each timeout attempt.
**timeout_max**: The maximum timeout (ms) used for each timeout attempt.
**Returns**: None

661
docs/dull/Array.md Normal file
View File

@@ -0,0 +1,661 @@
# Array
### length <sub>number</sub>
### at(index) <sub>function</sub>
Return the item at index 'index', supporting negative indices to count from
the end. If 'index' is out of range, returns undefined.
**index**: The index of the element to return (can be negative).
**Returns**: The element at the given index, or undefined.
### with(index, value) <sub>function</sub>
Return a shallow copy of the array, but with the element at 'index' replaced
by 'value'. If 'index' is negative, it counts from the end. Throws if out of range.
**index**: The zero-based index (can be negative) to replace.
**value**: The new value for the specified position.
**Returns**: A new array with the updated element.
### concat(items) <sub>function</sub>
Return a new array that is the result of concatenating this array with
any additional arrays or values provided.
**items**: One or more arrays or values to concatenate.
**Returns**: A new array with the items appended.
### every(callback, thisArg) <sub>function</sub>
Return true if the provided callback function returns a truthy value for
every element in the array; otherwise false.
**callback**: A function(element, index, array) => boolean.
**thisArg**: Optional. A value to use as 'this' within the callback.
**Returns**: True if all elements pass the test, otherwise false.
### some(callback, thisArg) <sub>function</sub>
Return true if the provided callback function returns a truthy value for at
least one element in the array; otherwise false.
**callback**: A function(element, index, array) => boolean.
**thisArg**: Optional. A value to use as 'this' within the callback.
**Returns**: True if at least one element passes the test, otherwise false.
### forEach(callback, thisArg) <sub>function</sub>
Call the provided callback function once for each element in the array.
Does not produce a return value.
**callback**: A function(element, index, array).
**thisArg**: Optional. A value to use as 'this' within the callback.
**Returns**: None
### map(callback, thisArg) <sub>function</sub>
Create a new array with the results of calling a provided callback function
on every element in this array.
**callback**: A function(element, index, array) => newElement.
**thisArg**: Optional. A value to use as 'this' within the callback.
**Returns**: A new array of transformed elements.
### filter(callback, thisArg) <sub>function</sub>
Create a new array containing all elements for which the provided callback
function returns a truthy value.
**callback**: A function(element, index, array) => boolean.
**thisArg**: Optional. A value to use as 'this' within the callback.
**Returns**: A new array of elements that passed the test.
### reduce(callback, initialValue) <sub>function</sub>
Apply a callback function against an accumulator and each element in the
array (from left to right) to reduce it to a single value.
**callback**: A function(accumulator, element, index, array) => newAccumulator.
**initialValue**: Optional. The initial value for the accumulator.
**Returns**: The single resulting value.
### reduceRight(callback, initialValue) <sub>function</sub>
Similar to reduce(), except it processes elements from right to left.
**callback**: A function(accumulator, element, index, array) => newAccumulator.
**initialValue**: Optional. The initial value for the accumulator.
**Returns**: The single resulting value.
### fill(value, start, end) <sub>function</sub>
Fill the array with a static value from 'start' index up to (but not including)
'end' index. Modifies the original array.
**value**: The value to fill.
**start**: The starting index (default 0).
**end**: The ending index (default array.length).
**Returns**: The modified array (with filled values).
### find(callback, thisArg) <sub>function</sub>
Return the first element in the array that satisfies the provided callback
function. If none is found, return undefined.
**callback**: A function(element, index, array) => boolean.
**thisArg**: Optional. A value to use as 'this' within the callback.
**Returns**: The first matching element, or undefined if not found.
### findIndex(callback, thisArg) <sub>function</sub>
Return the index of the first element in the array that satisfies the
provided callback function. If none is found, return -1.
**callback**: A function(element, index, array) => boolean.
**thisArg**: Optional. A value to use as 'this' within the callback.
**Returns**: The index of the first matching element, or -1 if not found.
### findLast(callback, thisArg) <sub>function</sub>
Return the last element in the array that satisfies the provided callback
function, searching from right to left. If none is found, return undefined.
**callback**: A function(element, index, array) => boolean.
**thisArg**: Optional. A value to use as 'this' within the callback.
**Returns**: The last matching element, or undefined if not found.
### findLastIndex(callback, thisArg) <sub>function</sub>
Return the index of the last element in the array that satisfies the
provided callback function, searching from right to left. If none is found,
return -1.
**callback**: A function(element, index, array) => boolean.
**thisArg**: Optional. A value to use as 'this' within the callback.
**Returns**: The index of the last matching element, or -1 if not found.
### indexOf(searchElement, fromIndex) <sub>function</sub>
Return the first index at which a given element can be found. If not present,
return -1.
**searchElement**: The item to locate in the array.
**fromIndex**: The index at which to start searching (default 0).
**Returns**: The index of the found element, or -1 if not found.
### lastIndexOf(searchElement, fromIndex) <sub>function</sub>
Return the last index at which a given element can be found, or -1 if not
present. Searches backward from 'fromIndex'.
**searchElement**: The item to locate in the array.
**fromIndex**: The index at which to start searching backward (default array.length - 1).
**Returns**: The last index of the found element, or -1 if not found.
### includes(searchElement, fromIndex) <sub>function</sub>
Return a boolean indicating whether the array contains the given element,
comparing elements using the SameValueZero algorithm.
**searchElement**: The value to search for.
**fromIndex**: The position in the array to start searching (default 0).
**Returns**: True if the element is found, otherwise false.
### join(separator) <sub>function</sub>
Join all elements of the array into a string, separated by 'separator'.
**separator**: The delimiter string to separate elements (default ',').
**Returns**: A string of array elements joined by the separator.
### toString() <sub>function</sub>
Return a string representing the elements of the array, separated by commas.
Overrides Object.prototype.toString.
**Returns**: A comma-separated string of array elements.
### toLocaleString() <sub>function</sub>
Return a localized string representing the array and its elements, calling
each element's toLocaleString if available.
**Returns**: A locale-sensitive, comma-separated string of array elements.
### pop() <sub>function</sub>
Remove the last element from the array and return it. This changes the length
of the array.
**Returns**: The removed element, or undefined if the array is empty.
### push(items) <sub>function</sub>
Append one or more elements to the end of the array and return the new length
of the array.
**items**: One or more items to append.
**Returns**: The new length of the array.
### shift() <sub>function</sub>
Remove the first element from the array and return it. This changes the length
of the array.
**Returns**: The removed element, or undefined if the array is empty.
### unshift(items) <sub>function</sub>
Insert one or more elements at the start of the array and return the new
length of the array.
**items**: One or more elements to insert at the start.
**Returns**: The new length of the array.
### reverse() <sub>function</sub>
Reverse the elements of the array in place and return the modified array.
**Returns**: The reversed array.
### toReversed() <sub>function</sub>
Return a shallow copy of the array in reverse order, without modifying the original array.
**Returns**: A new reversed array.
### sort(compareFunction) <sub>function</sub>
Sort the array in place, returning it. By default, sorts elements as strings in ascending order. An optional compareFunction may be used for custom sorting.
**compareFunction**: A function(a, b) => number, defining sort order.
**Returns**: The sorted array.
### toSorted(compareFunction) <sub>function</sub>
Return a shallow copy of the array, sorted according to the optional compare
function, without modifying the original array.
**compareFunction**: A function(a, b) => number, defining sort order.
**Returns**: A new sorted array.
### slice(start, end) <sub>function</sub>
Return a shallow copy of a portion of the array into a new array object, selected from 'start' to 'end' (end not included).
**start**: The beginning index (0-based). Negative values count from the end.
**end**: The end index (exclusive). Negative values count from the end.
**Returns**: A new array containing the extracted elements.
### splice(start, deleteCount, items) <sub>function</sub>
Change the contents of the array by removing or replacing existing elements and/or adding new elements in place. Returns an array of removed elements.
**start**: The index at which to start changing the array.
**deleteCount**: Number of elements to remove.
**items**: Elements to add in place of the removed elements.
**Returns**: An array containing the removed elements (if any).
### toSpliced(start, deleteCount, items) <sub>function</sub>
Return a shallow copy of the array, with a splice-like operation applied at 'start' removing 'deleteCount' elements and adding 'items'. Does not mutate the original array.
**start**: The index at which to start the splice operation.
**deleteCount**: Number of elements to remove.
**items**: Elements to add in place of the removed elements.
**Returns**: A new array with the splice applied.
### copyWithin(target, start, end) <sub>function</sub>
Copy a sequence of array elements within the array, overwriting existing values. This operation is performed in place and returns the modified array.
**target**: The index at which to copy the sequence to.
**start**: The beginning index of the sequence to copy.
**end**: The end index (exclusive) of the sequence to copy (default array.length).
**Returns**: The modified array.
### flatMap(callback, thisArg) <sub>function</sub>
Return a new array formed by applying a callback function to each element and then flattening the result by one level.
**callback**: A function(element, index, array) => array or value.
**thisArg**: Optional. A value to use as 'this' within the callback.
**Returns**: A new array with the flattened results.
### flat(depth) <sub>function</sub>
Return a new array with all sub-array elements concatenated into it recursively up to the specified depth.
**depth**: The maximum depth to flatten (default 1).
**Returns**: A new flattened array.
### values() <sub>function</sub>
Return a new Array Iterator object that contains the values for each index in the array.
**Returns**: An iterator over the array's elements.
### keys() <sub>function</sub>
Return a new Array Iterator object that contains the keys (indexes) for each index in the array.
**Returns**: An iterator over the array's indices.
### entries() <sub>function</sub>
Return a new Array Iterator object that contains key/value pairs for each index in the array. Each entry is [index, value].
**Returns**: An iterator over [index, value] pairs.
### add(other) <sub>function</sub>
Non-standard. Add corresponding elements of the current array and 'other' element-wise, returning a new array. Behavior depends on data types.
**other**: Another array or scalar to add to each element.
**Returns**: A new array with element-wise sum results.
### sub(other) <sub>function</sub>
Non-standard. Subtract corresponding elements of 'other' from the current array element-wise, returning a new array. Behavior depends on data types.
**other**: Another array or scalar to subtract from each element.
**Returns**: A new array with element-wise difference results.
### div(other) <sub>function</sub>
Non-standard. Divide each element of the current array by the corresponding element of 'other' (or a scalar) element-wise, returning a new array. Behavior depends on data types.
**other**: Another array or scalar for the division.
**Returns**: A new array with element-wise division results.
### scale() <sub>function</sub>
### lerp() <sub>function</sub>
### x <sub>accessor</sub>
### y <sub>accessor</sub>
### xy <sub>accessor</sub>
### r <sub>accessor</sub>
### g <sub>accessor</sub>
### b <sub>accessor</sub>
### a <sub>accessor</sub>
### rgb <sub>accessor</sub>
### rgba <sub>accessor</sub>
### filter!(fn) <sub>function</sub>
Perform an in-place filter of this array using the provided callback 'fn'.
Any element for which 'fn' returns a falsy value is removed. The array is modified
and then returned.
**fn**: A callback function(element, index, array) => boolean.
**Returns**: The filtered (modified) array.
### delete(item) <sub>function</sub>
Remove the first occurrence of 'item' from the array, if it exists.
Returns undefined.
**item**: The item to remove.
**Returns**: undefined
### copy() <sub>function</sub>
Return a deep copy of this array by applying 'deep_copy' to each element.
The resulting array is entirely new.
**Returns**: A new array that is a deep copy of the original.
### equal(b) <sub>function</sub>
Check if this array and array 'b' have the same elements in the same order.
If they are of different lengths, return false. Otherwise compare them via JSON.
**b**: Another array to compare against.
**Returns**: True if they match, false otherwise.
### last() <sub>function</sub>
Return the last element of this array. If the array is empty, returns undefined.
**Returns**: The last element of the array, or undefined if empty.
### wrapped(x) <sub>function</sub>
Return a copy of the array with the first 'x' elements appended to the end.
Does not modify the original array.
**x**: The number of leading elements to re-append.
**Returns**: A new array with the leading elements wrapped to the end.
### wrap_idx(x) <sub>function</sub>
Wrap the integer 'x' around this array's length, ensuring the resulting index
lies within [0, this.length - 1].
**x**: The index to wrap.
**Returns**: A wrapped index within this array's bounds.
### mirrored(x) <sub>function</sub>
Return a new array that appends a reversed copy (excluding the last element)
of itself to the end. For example, [1,2,3] -> [1,2,3,2,1]. If the array has length
<= 1, a copy of it is returned directly.
**Returns**: A new "mirrored" array.

View File

@@ -49,7 +49,7 @@ return {
This will cause prosperon to launch a 500x500 window with the title 'Hello World'. In your ```main.js```, write the following: This will cause prosperon to launch a 500x500 window with the title 'Hello World'. In your ```main.js```, write the following:
``` ```
console.log("Hello world") log.console("Hello world")
this.delay(_ => { this.delay(_ => {
this.kill(); this.kill();
@@ -62,6 +62,6 @@ this.delay(_ => {
The global object called `prosperon` has a variety of engine specific settings on it that can be set to influence how the engine behaves. For example, `prosperon.argv` contains a list of the command line arguments given to prosperon; `prosperon.PATH` is an array of paths to resolve resources such as modules and images. `prosperon` is fully documented in the API section. The global object called `prosperon` has a variety of engine specific settings on it that can be set to influence how the engine behaves. For example, `prosperon.argv` contains a list of the command line arguments given to prosperon; `prosperon.PATH` is an array of paths to resolve resources such as modules and images. `prosperon` is fully documented in the API section.
## Getting help ## Getting help
The `prosperon` global has a 'doc' function, which can be invoked on any engine object to see a description of it and its members. For example, to learn about `prosperon`, try printing out `prosperon.doc(prosperon)` in your `main.js`. The `prosperon` global has a 'doc' function, which can be invoked on any engine object to see a description of it and its members. For example, to learn about `prosperon`, try printing out `cell.DOC(prosperon)` in your `main.js`.
Writing documentation for your own modules and game components will be explored in the chapter on actors & modules. Writing documentation for your own modules and game components will be explored in the chapter on actors & modules.

View File

@@ -3,9 +3,20 @@ c = 'emcc'
cpp = 'em++' cpp = 'em++'
ar = 'emar' ar = 'emar'
strip = 'emstrip' strip = 'emstrip'
pkg-config = 'pkg-config'
exe_wrapper = 'node'
[host_machine] [host_machine]
system = 'emscripten' system = 'emscripten'
cpu_family = 'wasm64' cpu_family = 'wasm32'
cpu = 'wasm64' cpu = 'wasm32'
endian = 'little' endian = 'little'
[built-in options]
pkg_config_path = '$EMSDK/upstream/emscripten/cache/sysroot/lib/pkgconfig'
cmake_prefix_path = '$EMSDK/upstream/emscripten/cache/sysroot'
[properties]
needs_exe_wrapper = true
cmake_system_name = 'Emscripten'
sys_root = '@env:EMSDK@/upstream/emscripten/cache/sysroot'

View File

@@ -0,0 +1,234 @@
// HTTP Download Actor
// Handles download requests and progress queries
var http = use('http');
var os = use('os');
// Actor state
var state = {
downloading: false,
current_url: null,
total_bytes: 0,
downloaded_bytes: 0,
start_time: 0,
error: null,
connection: null,
download_msg: null,
chunks: []
};
// Helper to calculate progress percentage
function get_progress() {
if (state.total_bytes == 0) {
return 0;
}
return Math.round((state.downloaded_bytes / state.total_bytes) * 100);
}
// Helper to format status response
function get_status() {
if (!state.downloading) {
return {
status: 'idle',
error: state.error
};
}
var elapsed = os.now() - state.start_time;
var bytes_per_sec = elapsed > 0 ? state.downloaded_bytes / elapsed : 0;
return {
status: 'downloading',
url: state.current_url,
progress: get_progress(),
downloaded_bytes: state.downloaded_bytes,
total_bytes: state.total_bytes,
elapsed_seconds: elapsed,
bytes_per_second: Math.round(bytes_per_sec)
};
}
// Main message receiver
$_.receiver(function(msg) {
switch (msg.type) {
case 'download':
if (state.downloading) {
send(msg, {
type: 'error',
error: 'Already downloading',
current_url: state.current_url
});
return;
}
if (!msg.url) {
send(msg, {
type: 'error',
error: 'No URL provided'
});
return;
}
// Start download
state.downloading = true;
state.current_url = msg.url;
state.total_bytes = 0;
state.downloaded_bytes = 0;
state.start_time = os.now();
state.error = null;
state.download_msg = msg;
state.chunks = [];
try {
// Start the connection
state.connection = http.fetch_start(msg.url, msg.options || {});
if (!state.connection) {
throw new Error('Failed to start download');
}
// Schedule the first chunk read
$_.delay(read_next_chunk, 0);
} catch (e) {
state.error = e.toString();
state.downloading = false;
send(msg, {
type: 'error',
error: state.error,
url: msg.url
});
}
break;
case 'status':
log.console(`got status request. current is ${json.encode(get_status())}`)
send(msg, {
type: 'status_response',
...get_status()
});
break;
case 'cancel':
if (state.downloading) {
// Cancel the download
if (state.connection) {
http.fetch_close(state.connection);
state.connection = null;
}
state.downloading = false;
state.current_url = null;
state.download_msg = null;
state.chunks = [];
send(msg, {
type: 'cancelled',
message: 'Download cancelled',
url: state.current_url
});
} else {
send(msg, {
type: 'error',
error: 'No download in progress'
});
}
break;
default:
send(msg, {
type: 'error',
error: 'Unknown message type: ' + msg.type
});
}
});
// Non-blocking chunk reader
function read_next_chunk() {
if (!state.downloading || !state.connection) {
return;
}
try {
var chunk = http.fetch_read_chunk(state.connection);
if (chunk == null) {
// Download complete
finish_download();
return;
}
// Store chunk
state.chunks.push(chunk);
// Update progress
var info = http.fetch_info(state.connection);
state.downloaded_bytes = info.bytes_read;
if (info.headers_complete && info.content_length > 0) {
state.total_bytes = info.content_length;
}
// Schedule next chunk read
$_.delay(read_next_chunk, 0);
} catch (e) {
// Error during download
state.error = e.toString();
if (state.connection) {
http.fetch_close(state.connection);
}
if (state.download_msg) {
send(state.download_msg, {
type: 'error',
error: state.error,
url: state.current_url
});
}
// Reset state
state.downloading = false;
state.connection = null;
state.download_msg = null;
state.chunks = [];
}
}
// Complete the download and send result
function finish_download() {
if (state.connection) {
http.fetch_close(state.connection);
}
// Combine all chunks into single ArrayBuffer
var total_size = 0;
for (var i = 0; i < state.chunks.length; i++) {
total_size += state.chunks[i].byteLength;
}
var result = new ArrayBuffer(total_size);
var view = new Uint8Array(result);
var offset = 0;
for (var i = 0; i < state.chunks.length; i++) {
var chunk_view = new Uint8Array(state.chunks[i]);
view.set(chunk_view, offset);
offset += state.chunks[i].byteLength;
}
// Send complete message
if (state.download_msg) {
send(state.download_msg, {
type: 'complete',
url: state.current_url,
data: result,
size: result.byteLength,
duration: os.now() - state.start_time
});
}
// Reset state
state.downloading = false;
state.connection = null;
state.current_url = null;
state.download_msg = null;
state.chunks = [];
}

29
examples/nat.ce Normal file
View File

@@ -0,0 +1,29 @@
// NAT Punchthrough Server
// This server helps two chess clients find each other through NAT
// The server coordinates the punchthrough by having both clients
// connect to each other simultaneously
var json = use('json');
var waiting_client = null;
var match_id = 0;
$_.portal(e => {
log.console("NAT server: received connection request");
if (!is_actor(e.actor))
send(e, {reason: "Must provide the actor you want to connect."});
if (waiting_client) {
log.console(`sending out messages! to ${json.encode(e.actor)} and ${json.encode(waiting_client.actor)}`)
send(waiting_client, e.actor)
send(e, waiting_client.actor)
waiting_client = null
return
}
waiting_client = e
log.console(`actor ${json.encode(e.actor)} is waiting ...`)
}, 4000);

22
examples/nat_client.ce Normal file
View File

@@ -0,0 +1,22 @@
log.console(`nat client starting`)
$_.contact((actor, reason) => {
if (actor) {
log.console(`trying to message ${json.encode(actor)}`)
send(actor, {type:"greet"})
} else {
log.console(json.encode(reason))
}
}, {
address: "108.210.60.32", // NAT server's public IP
port: 4000,
actor: $_
})
$_.receiver(e => {
switch(e.type) {
case 'greet':
log.console(`hello!`)
break
}
})

View File

@@ -1,51 +1,110 @@
project('prosperon', ['c', 'cpp'], default_options : [ 'cpp_std=c++11']) project('cell', ['c', 'cpp'],
version: '0.9.3',
meson_version: '>=1.4',
default_options : [ 'cpp_std=c++11'])
libtype = get_option('default_library') libtype = get_option('default_library')
link = [] link = []
src = [] src = []
if not get_option('editor') fs = import('fs')
add_project_arguments('-DNEDITOR', language:'c')
endif add_project_arguments('-pedantic', language: ['c'])
git_tag_cmd = run_command('git', 'describe', '--tags', '--abbrev=0', check: false) git_tag_cmd = run_command('git', 'describe', '--tags', '--abbrev=0', check: false)
prosperon_version = 'unknown' cell_version = 'unknown'
if git_tag_cmd.returncode() == 0 if git_tag_cmd.returncode() == 0
prosperon_version = git_tag_cmd.stdout().strip() cell_version = git_tag_cmd.stdout().strip()
endif endif
git_commit_cmd = run_command('git', 'rev-parse', '--short', 'HEAD', check: false) git_commit_cmd = run_command('git', 'rev-parse', '--short', 'HEAD', check: false)
prosperon_commit = 'unknown' cell_commit = 'unknown'
if git_commit_cmd.returncode() == 0 if git_commit_cmd.returncode() == 0
prosperon_commit = git_commit_cmd.stdout().strip() cell_commit = git_commit_cmd.stdout().strip()
endif endif
# Important: pass the definitions without double-escaping quotes # Important: pass the definitions without double-escaping quotes
add_project_arguments( add_project_arguments(
'-DPROSPERON_VERSION="' + prosperon_version + '"', '-DCELL_VERSION="' + cell_version + '"',
'-DPROSPERON_COMMIT="' + prosperon_commit + '"', '-DCELL_COMMIT="' + cell_commit + '"',
language : 'c' language : 'c'
) )
add_project_arguments('-Wno-incompatible-pointer-types', language: 'c') add_project_arguments('-Wno-incompatible-pointer-types', language: 'c')
add_project_arguments('-Wno-narrowing', language: 'cpp') add_project_arguments('-Wno-narrowing', language: 'cpp')
add_project_arguments('-Wno-missing-braces', language:'c') add_project_arguments('-Wno-missing-braces', language:'c')
add_project_arguments('-Wl,--disable-new-dtags', language:'cpp') add_project_arguments('-Wno-strict-prototypes', language:'c')
add_project_arguments('-Wl,--disable-new-dtags', language:'c') add_project_arguments('-Wno-unused-command-line-argument', language: 'c')
add_project_arguments('-Wno-unused-command-line-argument', language: 'cpp')
deps = [] deps = []
if host_machine.system() == 'darwin' if host_machine.system() == 'darwin'
add_project_arguments('-x', 'objective-c', language: 'c') add_project_arguments('-x', 'objective-c', language: 'c')
fworks = ['foundation', 'metal', 'audiotoolbox', 'metalkit', 'avfoundation', 'quartzcore', 'cocoa'] fworks = [
'foundation',
'metal',
'audiotoolbox',
'metalkit',
'avfoundation',
'quartzcore',
'cocoa',
'coreaudio',
'coremedia',
'gamecontroller',
'forcefeedback',
'iokit',
'corefoundation',
'corehaptics',
'carbon',
'uniformtypeidentifiers'
]
foreach fkit : fworks foreach fkit : fworks
deps += dependency('appleframeworks', modules: fkit) deps += dependency('appleframeworks', modules: fkit)
endforeach endforeach
endif endif
cmake = import('cmake')
# Try to find system-installed mbedtls first
mbedtls_dep = dependency('mbedtls', static: true, required: false)
mbedx509_dep = dependency('mbedx509', static: true, required: false)
mbedcrypto_dep = dependency('mbedcrypto', static: true, required: false)
if not mbedtls_dep.found() or not mbedx509_dep.found() or not mbedcrypto_dep.found()
message('⚙ System mbedtls not found, building subproject...')
mbedtls_opts = cmake.subproject_options()
mbedtls_opts.add_cmake_defines({
'ENABLE_PROGRAMS': 'OFF', # Disable Mbed TLS programs
'ENABLE_TESTING': 'OFF', # Disable Mbed TLS tests
'CMAKE_BUILD_TYPE': 'Release', # Optimize for release
'MBEDTLS_FATAL_WARNINGS': 'ON', # Treat warnings as errors
'USE_STATIC_MBEDTLS_LIBRARY': 'ON',# Build static libraries
'USE_SHARED_MBEDTLS_LIBRARY': 'OFF'# Disable shared libraries
})
mbedtls_proj = cmake.subproject('mbedtls', options: mbedtls_opts)
deps += [
mbedtls_proj.dependency('mbedtls'),
mbedtls_proj.dependency('mbedx509'),
mbedtls_proj.dependency('mbedcrypto')
]
else
deps += [mbedtls_dep, mbedx509_dep, mbedcrypto_dep]
endif
sdl3_opts = cmake.subproject_options()
sdl3_opts.add_cmake_defines({
'SDL_STATIC': 'ON',
'SDL_SHARED': 'OFF',
'SDL_TEST': 'OFF',
'CMAKE_BUILD_TYPE': 'Release',
'SDL_THREADS': 'ON',
'SDL_PIPEWIRE': 'ON',
'SDL_PULSEAUDIO': 'ON',
})
cc = meson.get_compiler('c') cc = meson.get_compiler('c')
deps += dependency('sdl3', required:true)
if host_machine.system() == 'darwin' if host_machine.system() == 'darwin'
deps += dependency('appleframeworks', modules: 'accelerate') deps += dependency('appleframeworks', modules: 'accelerate')
@@ -56,157 +115,232 @@ endif
if host_machine.system() == 'linux' if host_machine.system() == 'linux'
deps += cc.find_library('asound', required:true) deps += cc.find_library('asound', required:true)
deps += [dependency('x11'), dependency('xi'), dependency('xcursor'), dependency('egl'), dependency('gl')] deps += [dependency('x11'), dependency('xi'), dependency('xcursor'), dependency('egl'), dependency('gl')]
# link += '-fuse-ld=mold' # use mold, which is very fast, for debug builds deps += cc.find_library('blas', required:true)
deps += cc.find_library('lapack', required:true)
endif endif
if host_machine.system() == 'windows' if host_machine.system() == 'windows'
deps += cc.find_library('d3d11') deps += cc.find_library('d3d11')
# these are for tracy
deps += cc.find_library('ws2_32', required:true) deps += cc.find_library('ws2_32', required:true)
# For Windows, you may need to install OpenBLAS or Intel MKL
# and adjust these library names accordingly
deps += cc.find_library('openblas', required:false)
if not cc.find_library('openblas', required:false).found()
deps += cc.find_library('blas', required:false)
deps += cc.find_library('lapack', required:false)
endif
deps += cc.find_library('dbghelp') deps += cc.find_library('dbghelp')
#end deps += cc.find_library('winmm')
link += '-static' # Required to pack in mingw dlls on cross compilation deps += cc.find_library('setupapi')
deps += cc.find_library('imm32')
deps += cc.find_library('version')
deps += cc.find_library('cfgmgr32')
deps += cc.find_library('bcrypt')
sdl3_opts.add_cmake_defines({'HAVE_ISINF': '1'}) # Hack for MSYS2
sdl3_opts.add_cmake_defines({'HAVE_ISNAN': '1'})
link += '-static'
endif endif
if host_machine.system() == 'emscripten' if host_machine.system() == 'emscripten'
link += '-sUSE_WEBGPU' link += '-sUSE_WEBGPU'
# Use the pre-installed copy
deps += dependency('sdl3',
static : true,
method : 'pkg-config', # or 'cmake' if you prefer
required : true)
else
# Try to find system-installed SDL3 first
sdl3_dep = dependency('sdl3', static: true, required: false)
if not sdl3_dep.found()
message('⚙ System SDL3 not found, building subproject...')
sdl3_proj = cmake.subproject('sdl3', options : sdl3_opts)
deps += sdl3_proj.dependency('SDL3-static')
else
deps += sdl3_dep
endif
endif endif
tracy_opts = ['fibers=true', 'on_demand=true'] miniz_dep = dependency('miniz', static: true, required: false)
quickjs_opts = [] if not miniz_dep.found()
message('⚙ System miniz not found, building subproject...')
src += 'qjs_tracy.c' deps += dependency('miniz', static:true)
add_project_arguments('-DTRACY_ENABLE', language:['c','cpp']) else
deps += dependency('tracy', static:true, default_options:tracy_opts) deps += miniz_dep
quickjs_opts += 'default_library=static'
deps += dependency('quickjs', static:true, default_options:quickjs_opts)
storefront = get_option('storefront')
if storefront == 'steam'
deps += dependency('qjs-steam',static:false)
endif endif
deps += dependency('qjs-layout',static:true) # Try to find system-installed physfs first
deps += dependency('qjs-nota',static:true) physfs_dep = dependency('physfs', static: true, required: false)
deps += dependency('qjs-miniz',static:true) if not physfs_dep.found()
deps += dependency('qjs-soloud',static:true) message('⚙ System physfs not found, building subproject...')
deps += dependency('physfs', static:true) deps += dependency('physfs', static:true)
else
#deps += dependency('opencv4') deps += physfs_dep
#deps += cc.find_library('opencv') endif
deps += dependency('threads') deps += dependency('threads')
deps += dependency('mimalloc')
deps += dependency('chipmunk') # Try to find system-installed chipmunk first
chipmunk_dep = dependency('chipmunk', static: true, required: false)
if get_option('chipmunk') if not chipmunk_dep.found()
# deps += dependency('qjs-chipmunk', static:false) message('⚙ System chipmunk not found, building subproject...')
deps += dependency('chipmunk', static:true)
else
deps += chipmunk_dep
endif endif
if get_option('enet') if host_machine.system() != 'emscripten'
#deps += dependency('qjs-enet', static:false) # Try to find system-installed enet first
enet_dep = dependency('enet', static: true, required: false)
if not enet_dep.found()
message('⚙ System enet not found, building subproject...')
deps += dependency('enet', static:true)
else
deps += enet_dep
endif
src += 'qjs_enet.c'
tracy_opts = ['fibers=true', 'no_exit=true', 'libunwind_backtrace=true', 'on_demand=true']
add_project_arguments('-DTRACY_ENABLE', language:['c','cpp'])
# Try to find system-installed tracy first
tracy_dep = dependency('tracy', static: true, required: false)
if not tracy_dep.found()
message('⚙ System tracy not found, building subproject...')
deps += dependency('tracy', static:true, default_options:tracy_opts)
else
deps += tracy_dep
endif
src += 'qjs_dmon.c'
endif endif
# Try to find system-installed soloud first
soloud_dep = dependency('soloud', static: true, required: false)
if not soloud_dep.found()
message('⚙ System soloud not found, building subproject...')
deps += dependency('soloud', static:true)
else
deps += soloud_dep
endif
# Try to find system-installed qrencode first
qr_dep = dependency('qrencode', static: true, required: false)
if not qr_dep.found()
message('⚙ System qrencode not found, building subproject...')
deps += dependency('libqrencode', static:true)
else
deps += qr_dep
endif
# Storefront SDK support
storefront = get_option('storefront')
if storefront == 'steam'
steam_sdk_path = meson.current_source_dir() / 'sdk'
if host_machine.system() == 'darwin'
steam_lib_path = steam_sdk_path / 'redistributable_bin' / 'osx' / 'libsteam_api.dylib'
elif host_machine.system() == 'linux'
steam_lib_path = steam_sdk_path / 'redistributable_bin' / 'linux64' / 'libsteam_api.so'
elif host_machine.system() == 'windows'
steam_lib_path = steam_sdk_path / 'redistributable_bin' / 'win64' / 'steam_api64.lib'
else
steam_lib_path = ''
endif
if fs.exists(steam_lib_path)
steam_dep = declare_dependency(
include_directories: include_directories('sdk/public'),
link_args: [steam_lib_path]
)
deps += steam_dep
src += 'qjs_steam.cpp'
message('Steam SDK enabled')
else
error('Steam SDK required but not found at: ' + steam_lib_path)
endif
else
add_project_arguments('-DNSTEAM', language: ['c', 'cpp'])
message('Storefront: ' + storefront)
endif
link_args = link
sources = [] sources = []
src += ['anim.c', 'config.c', 'datastream.c','font.c','gameobject.c','HandmadeMath.c','jsffi.c','model.c','render.c','script.c','simplex.c','spline.c', 'timer.c', 'transform.c','prosperon.c', 'wildmatch.c', 'sprite.c', 'quadtree.c', 'aabb.c', 'rtree.c'] src += [
'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c',
'render.c','simplex.c','spline.c', 'transform.c','cell.c', 'wildmatch.c',
'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_input.c', 'qjs_sdl_video.c', 'qjs_sdl_surface.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_fd.c', 'qjs_os.c', 'qjs_actor.c',
'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c', 'qjs_num.c', 'timer.c', 'qjs_socket.c', 'qjs_kim.c', 'qjs_utf8.c', 'qjs_fit.c', 'qjs_text.c'
]
# quirc src
src += [
'thirdparty/quirc/quirc.c', 'thirdparty/quirc/decode.c',
'thirdparty/quirc/identify.c', 'thirdparty/quirc/version_db.c'
]
imsrc = ['GraphEditor.cpp','ImCurveEdit.cpp','ImGradient.cpp','imgui_draw.cpp','imgui_tables.cpp','imgui_widgets.cpp','imgui.cpp','ImGuizmo.cpp','imnodes.cpp','implot_items.cpp','implot.cpp', 'imgui_impl_sdlrenderer3.cpp', 'imgui_impl_sdl3.cpp', 'imgui_impl_sdlgpu3.cpp'] # quickjs src
src += ['quickjs.c', 'libregexp.c', 'libunicode.c', 'cutils.c', 'dtoa.c']
imsrc = [
'GraphEditor.cpp','ImCurveEdit.cpp','ImGradient.cpp','imgui_draw.cpp',
'imgui_tables.cpp','imgui_widgets.cpp','imgui.cpp','ImGuizmo.cpp','imnodes.cpp',
'implot_items.cpp','implot.cpp','imgui_impl_sdlrenderer3.cpp','imgui_impl_sdl3.cpp',
'imgui_impl_sdlgpu3.cpp'
]
srceng = 'source' srceng = 'source'
tp = srceng / 'thirdparty' tp = srceng / 'thirdparty'
includes = [
includes = [srceng,tp / 'cgltf',tp / 'imgui',tp / 'par',tp / 'stb',tp,tp / 'pl_mpeg/include'] srceng, tp / 'cgltf', tp / 'imgui', tp / 'par', tp / 'stb',
tp, tp / 'pl_mpeg/include', tp / 'quirc'
]
foreach file : src foreach file : src
full_path = join_paths('source', file) full_path = join_paths('source', file)
sources += files(full_path) sources += files(full_path)
endforeach endforeach
if get_option('editor')
sources += 'source/qjs_imgui.cpp'
foreach imgui : imsrc
sources += tp / 'imgui' / imgui
endforeach
# sub_dmon = subproject('qjs-dmon')
# dmon_dep = sub_dmon.get_variable('qjs_dmon_dep')
endif
includers = [] includers = []
foreach inc : includes foreach inc : includes
includers += include_directories(inc) includers += include_directories(inc)
endforeach endforeach
zip_folders = ['scripts', 'fonts', 'icons', 'shaders']
zip_paths = []
foreach folder: zip_folders
zip_paths += meson.project_source_root() / folder
endforeach
# Now use the hash file as a dependency so that any change in the files causes a rebuild.
core = custom_target('core.zip',
output : 'core.zip',
command : ['sh', '-c',
'cd ' + meson.project_source_root() +
' && echo "Rebuilding core.zip" && rm -f ' + meson.current_build_dir() + '/core.zip && ' +
'zip -r ' + meson.current_build_dir() + '/core.zip scripts fonts icons shaders'
],
build_always: true,
build_by_default: true
)
prosperon_raw = executable('prosperon_raw', sources,
dependencies: deps,
include_directories: includers,
link_args: link,
build_rpath: '$ORIGIN',
install:false
)
if host_machine.system() == 'windows' if host_machine.system() == 'windows'
exe_ext = '.exe' exe_ext = '.exe'
else else
exe_ext = '' exe_ext = ''
endif endif
prosperon = custom_target('prosperon', strip_enabled = ['release', 'minsize'].contains(get_option('buildtype'))
output: 'prosperon' + exe_ext,
input: [prosperon_raw, core], if strip_enabled
command: [ add_project_link_arguments('-s', language: ['c', 'cpp'])
'sh', '-c', endif
'cat "$1" "$2" > "$3" && chmod +x "$3" >/dev/null 2>&1',
'concat', cell = executable('cell', sources,
'@INPUT0@', dependencies: deps,
'@INPUT1@', include_directories: includers,
'@OUTPUT@' link_args: link,
], build_rpath: '$ORIGIN',
build_always: true, install: true
build_by_default: true
) )
prosperon_dep = declare_dependency( cell_dep = declare_dependency(
link_with:prosperon link_with: cell
)
copy_tests = custom_target(
'copy_tests',
output: 'tests',
command: [
'cp', '-rf',
join_paths(meson.source_root(), 'tests'),
meson.build_root()
],
build_always: true,
build_by_default: true
) )
tests = [ tests = [
'spawn_actor', 'spawn_actor',
'empty' 'empty',
'nota',
'wota',
'portalspawner',
'overling',
'send',
'delay'
] ]
foreach file : tests foreach file : tests
test(file, prosperon_raw, args:['tests/' + file + '.js'], depends:copy_tests) test(file, cell, args:['tests/' + file])
endforeach endforeach

View File

@@ -1,4 +1,3 @@
option('editor', type:'boolean', value:true) option('editor', type:'boolean', value:true)
option('chipmunk', type:'boolean', value:true) option('chipmunk', type:'boolean', value:true)
option('enet', type:'boolean', value:true)
option('storefront', type:'combo', choices:['none','steam', 'gog', 'egs'], value:'none') option('storefront', type:'combo', choices:['none','steam', 'gog', 'egs'], value:'none')

View File

@@ -1,9 +1,9 @@
site_name: Prosperon Documentation site_name: Prosperon Documentation
edit_uri: edit/master/doc/docs
plugins: plugins:
- search - search
- awesome-pages - awesome-pages
- mike
extra_css: extra_css:
- style.css - style.css
@@ -32,6 +32,9 @@ extra:
analytics: analytics:
provider: google provider: google
property: G-85ECSFGCBV property: G-85ECSFGCBV
version:
default: latest
provider: mike
markdown_extensions: markdown_extensions:
- admonition - admonition

View File

@@ -1,7 +1,8 @@
var graphics = use('graphics') var graphics = use('graphics')
var color = use('color')
var sprite = { var sprite = {
image: undefined, image: null,
set color(x) { this._sprite.color = x; }, set color(x) { this._sprite.color = x; },
get color() { return this._sprite.color; }, get color() { return this._sprite.color; },
anim_speed: 1, anim_speed: 1,
@@ -11,7 +12,7 @@ var sprite = {
return; return;
} }
if (typeof str === 'string') { if (typeof str == 'string') {
if (!this.animset[str]) { if (!this.animset[str]) {
fn?.(); fn?.();
return; return;
@@ -25,8 +26,8 @@ var sprite = {
this.del_anim?.(); this.del_anim?.();
this.del_anim = () => { this.del_anim = () => {
this.del_anim = undefined; this.del_anim = null;
advance = undefined; advance = null;
stop?.(); stop?.();
}; };
@@ -37,10 +38,10 @@ var sprite = {
var done = false; var done = false;
if (reverse) { if (reverse) {
f = (((f - 1) % playing.frames.length) + playing.frames.length) % playing.frames.length; f = (((f - 1) % playing.frames.length) + playing.frames.length) % playing.frames.length;
if (f === playing.frames.length - 1) done = true; if (f == playing.frames.length - 1) done = true;
} else { } else {
f = (f + 1) % playing.frames.length; f = (f + 1) % playing.frames.length;
if (f === 0) done = true; if (f == 0) done = true;
} }
this.image = playing.frames[f]; this.image = playing.frames[f];
@@ -65,7 +66,7 @@ var sprite = {
set path(p) { set path(p) {
var image = graphics.texture(p); var image = graphics.texture(p);
if (!image) { if (!image) {
console.warn(`Could not find image ${p}.`); log.warn(`Could not find image ${p}.`);
return; return;
} }
@@ -101,14 +102,14 @@ var sprite = {
}, },
garbage: function() { garbage: function() {
this.del_anim?.(); this.del_anim?.();
this.anim = undefined; this.anim = null;
tree.delete(this._sprite) tree.delete(this._sprite)
this.transform.parent = undefined this.transform.parent = null
for (var t of this.transform.children()) for (var t of this.transform.children())
t.parent = undefined t.parent = null
delete this.transform.sprite delete this.transform.sprite
delete this._sprite delete this._sprite
// console.log("CLEARED SPRITE") // log.console("CLEARED SPRITE")
}, },
anchor: [0, 0], anchor: [0, 0],
set layer(v) { this._sprite.layer = v; }, set layer(v) { this._sprite.layer = v; },
@@ -184,7 +185,9 @@ sprite.inputs.kp1 = function () {
this.setanchor("ul"); this.setanchor("ul");
}; };
var tree = graphics.make_rtree() var rtree = use('rtree')
var tree = new rtree
sprite.tree = tree; sprite.tree = tree;
var IN = Symbol() var IN = Symbol()
@@ -213,7 +216,7 @@ sprite.to_queue = function(ysort = false)
}; };
var culled = sprite.tree.query(camrect) var culled = sprite.tree.query(camrect)
if (culled.length == 0) return []; if (culled.length == 0) return [];
var cmd = graphics.make_sprite_queue(culled, prosperon.camera, undefined, 1); var cmd = graphics.make_sprite_queue(culled, prosperon.camera, null, 1);
return cmd; return cmd;
} }
@@ -221,18 +224,18 @@ return sprite;
--- ---
var Color = use('color') var color = use('color')
var os = use('os') var transform = use('transform')
var graphics = use('graphics') var sprite = use('sprite')
this.transform = os.make_transform(); this.transform = new transform;
if (this.overling.transform) if (this.overling.transform)
this.transform.parent = this.overling.transform; this.transform.parent = this.overling.transform;
this.transform.change_hook = $.t_hook; this.transform.change_hook = $.t_hook;
var msp = graphics.make_sprite(); var msp = new sprite
this._sprite = msp; this._sprite = msp;
msp.color = Color.white; msp.color = color.white;
this.transform.sprite = this this.transform.sprite = this

View File

@@ -1,6 +1,6 @@
var cam = {} var cam = {}
var os = use('os') var transform = use('transform')
var basecam = {} var basecam = {}
basecam.draw_rect = function(size) basecam.draw_rect = function(size)
@@ -88,7 +88,7 @@ function mode_rect(src,dst,mode = "stretch")
cam.make = function() cam.make = function()
{ {
var c = Object.create(basecam) var c = Object.create(basecam)
c.transform = os.make_transform() c.transform = new transform
c.transform.unit() c.transform.unit()
c.zoom = 1 c.zoom = 1
c.size = [640,360] c.size = [640,360]

View File

@@ -11,18 +11,18 @@ var input = use('input')
var lay_ctx = layout.make_context(); var lay_ctx = layout.make_context();
var clay_base = { var clay_base = {
font: undefined, font: null,
background_image: undefined, background_image: null,
slice: 0, slice: 0,
font: 'smalle.16', font: 'smalle.16',
font_size: undefined, font_size: null,
color: [1,1,1,1], color: [1,1,1,1],
spacing:0, spacing:0,
padding:0, padding:0,
margin:0, margin:0,
offset:[0,0], offset:[0,0],
size:undefined, size:null,
background_color: undefined background_color: null
}; };
var root_item; var root_item;
@@ -125,8 +125,8 @@ function add_item(config)
// Adjust for child_gap // Adjust for child_gap
if (root_config._childIndex > 0) { if (root_config._childIndex > 0) {
var parentContain = root_config.contain || 0; var parentContain = root_config.contain || 0;
var isVStack = (parentContain & layout.contain.column) !== 0; var isVStack = (parentContain & layout.contain.column) != 0;
var isHStack = (parentContain & layout.contain.row) !== 0; var isHStack = (parentContain & layout.contain.row) != 0;
if (isVStack) { if (isVStack) {
margin.t += childGap; margin.t += childGap;
@@ -162,7 +162,7 @@ function add_item(config)
function rectify_configs(config_array) function rectify_configs(config_array)
{ {
if (config_array.length === 0) if (config_array.length == 0)
config_array = [{}]; config_array = [{}];
for (var i = config_array.length-1; i > 0; i--) for (var i = config_array.length-1; i > 0; i--)
@@ -216,8 +216,8 @@ clay.button = function button(str, action, config = {})
config.action = action; config.action = action;
} }
var hovered = undefined; var hovered = null;
clay.newframe = function() { hovered = undefined; } clay.newframe = function() { hovered = null; }
// mousepos given in hud coordinates // mousepos given in hud coordinates
clay.draw_commands = function draw_commands(cmds, pos = [0,0], mousepos = prosperon.camera.screen2hud(input.mouse.screenpos())) clay.draw_commands = function draw_commands(cmds, pos = [0,0], mousepos = prosperon.camera.screen2hud(input.mouse.screenpos()))

216
prosperon/color.cm Normal file
View File

@@ -0,0 +1,216 @@
function tohex(n) {
var s = Math.floor(n).toString(16);
if (s.length == 1) s = "0" + s;
return s.toUpperCase();
};
var Color = {
white: [1, 1, 1],
black: [0, 0, 0],
blue: [0, 0, 1],
green: [0, 1, 0],
yellow: [1, 1, 0],
red: [1, 0, 0],
gray: [0.71, 0.71, 0.71],
cyan: [0, 1, 1],
purple: [0.635, 0.365, 0.89],
orange: [1, 0.565, 0.251],
magenta: [1, 0, 1],
};
Color.editor = {};
Color.editor.ur = Color.green;
Color.tohtml = function (v) {
var html = v.map(function (n) {
return tohex(n * 255);
});
return "#" + html.join("");
};
var esc = {};
esc.reset = "\x1b[0";
esc.color = function (v) {
var c = v.map(function (n) {
return Math.floor(n * 255);
});
var truecolor = "\x1b[38;2;" + c.join(";") + ";";
return truecolor;
};
esc.doc = "Functions and constants for ANSI escape sequences.";
Color.Arkanoid = {
orange: [1, 0.561, 0],
teal: [0, 1, 1],
green: [0, 1, 0],
red: [1, 0, 0],
blue: [0, 0.439, 1],
purple: [1, 0, 1],
yellow: [1, 1, 0],
silver: [0.616, 0.616, 0.616],
gold: [0.737, 0.682, 0],
};
Color.Arkanoid.Powerups = {
red: [0.682, 0, 0] /* laser */,
blue: [0, 0, 0.682] /* enlarge */,
green: [0, 0.682, 0] /* catch */,
orange: [0.878, 0.561, 0] /* slow */,
purple: [0.824, 0, 0.824] /* break */,
cyan: [0, 0.682, 1] /* disruption */,
gray: [0.561, 0.561, 0.561] /* 1up */,
};
Color.Gameboy = {
darkest: [0.898, 0.42, 0.102],
dark: [0.898, 0.741, 0.102],
light: [0.741, 0.898, 0.102],
lightest: [0.42, 0.898, 0.102],
};
Color.Apple = {
green: [0.369, 0.741, 0.243],
yellow: [1, 0.725, 0],
orange: [0.969, 0.51, 0],
red: [0.886, 0.22, 0.22],
purple: [0.592, 0.224, 0.6],
blue: [0, 0.612, 0.875],
};
Color.Debug = {
boundingbox: Color.white,
names: [0.329, 0.431, 1],
};
Color.Editor = {
grid: [0.388, 1, 0.502],
select: [1, 1, 0.216],
newgroup: [0.471, 1, 0.039],
};
/* Detects the format of all colors and munges them into a floating point format */
Color.normalize = function (c) {
var add_a = function (a) {
var n = this.slice();
n[3] = a;
return n;
};
for (var p of Object.keys(c)) {
if (typeof c[p] != "object") continue;
if (!Array.isArray(c[p])) {
Color.normalize(c[p]);
continue;
}
// Add alpha channel if not present
if (c[p].length == 3) {
c[p][3] = 1;
}
// Check if any values are > 1 (meaning they're in 0-255 format)
var needs_conversion = false;
for (var color of c[p]) {
if (color > 1) {
needs_conversion = true;
break;
}
}
// Convert from 0-255 to 0-1 if needed
if (needs_conversion) {
c[p] = c[p].map(function (x) {
return x / 255;
});
}
c[p].alpha = add_a;
}
};
Color.normalize(Color);
var ColorMap = {};
ColorMap.makemap = function (map) {
var newmap = Object.create(ColorMap);
Object.assign(newmap, map);
return newmap;
};
ColorMap.Jet = ColorMap.makemap({
0: [0, 0, 0.514],
0.125: [0, 0.235, 0.667],
0.375: [0.02, 1, 1],
0.625: [1, 1, 0],
0.875: [0.98, 0, 0],
1: [0.502, 0, 0],
});
ColorMap.BlueRed = ColorMap.makemap({
0: [0, 0, 1],
1: [1, 0, 0],
});
ColorMap.Inferno = ColorMap.makemap({
0: [0, 0, 0.016],
0.13: [0.122, 0.047, 0.282],
0.25: [0.333, 0.059, 0.427],
0.38: [0.533, 0.133, 0.416],
0.5: [0.729, 0.212, 0.333],
0.63: [0.89, 0.349, 0.2],
0.75: [0.976, 0.549, 0.039],
0.88: [0.976, 0.788, 0.196],
1: [0.988, 1, 0.643],
});
ColorMap.Bathymetry = ColorMap.makemap({
0: [0.157, 0.102, 0.173],
0.13: [0.233, 0.192, 0.353],
0.25: [0.251, 0.298, 0.545],
0.38: [0.247, 0.431, 0.592],
0.5: [0.282, 0.557, 0.62],
0.63: [0.333, 0.682, 0.639],
0.75: [0.471, 0.808, 0.639],
0.88: [0.733, 0.902, 0.675],
1: [0.992, 0.996, 0.8],
});
ColorMap.Viridis = ColorMap.makemap({
0: [0.267, 0.004, 0.329],
0.13: [0.278, 0.173, 0.478],
0.25: [0.231, 0.318, 0.545],
0.38: [0.173, 0.443, 0.557],
0.5: [0.129, 0.565, 0.553],
0.63: [0.153, 0.678, 0.506],
0.75: [0.361, 0.784, 0.388],
0.88: [0.667, 0.863, 0.196],
1: [0.992, 0.906, 0.145],
});
Color.normalize(ColorMap);
ColorMap.sample = function (t, map = this) {
if (t < 0) return map[0];
if (t > 1) return map[1];
var lastkey = 0;
for (var key of Object.keys(map).sort()) {
if (t < key) {
var b = map[key];
var a = map[lastkey];
var tt = (key - lastkey) * t;
return a.lerp(b, tt);
}
lastkey = key;
}
return map[1];
};
ColorMap.doc = {
sample: "Sample a given colormap at the given percentage (0 to 1).",
};
Color.maps = ColorMap
Color.utils = esc
return Color

View File

@@ -1,5 +1,4 @@
var input = use('input') var input = use('input')
var util = use('util')
var downkeys = {}; var downkeys = {};
@@ -108,9 +107,9 @@ input.mouse.normal.doc = "Set the mouse to show again after hiding.";
input.keyboard = {}; input.keyboard = {};
input.keyboard.down = function (code) { input.keyboard.down = function (code) {
if (typeof code === "number") return downkeys[code]; if (typeof code == "number") return downkeys[code];
if (typeof code === "string") return downkeys[code.toUpperCase().charCodeAt()] || downkeys[code.toLowerCase().charCodeAt()]; if (typeof code == "string") return downkeys[code.toUpperCase().charCodeAt()] || downkeys[code.toLowerCase().charCodeAt()];
return undefined; return null;
}; };
input.print_pawn_kbm = function (pawn) { input.print_pawn_kbm = function (pawn) {
@@ -158,7 +157,7 @@ input.print_md_kbm = function print_md_kbm(pawn) {
}; };
input.has_bind = function (pawn, bind) { input.has_bind = function (pawn, bind) {
return typeof pawn.inputs?.[bind] === "function"; return typeof pawn.inputs?.[bind] == "function";
}; };
input.action = { input.action = {
@@ -177,17 +176,17 @@ input.tabcomplete = function tabcomplete(val, list) {
if (!val) return val; if (!val) return val;
list = filter(x => x.startsWith(val)) list = filter(x => x.startsWith(val))
if (list.length === 1) { if (list.length == 1) {
return list[0]; return list[0];
} }
var ret = undefined; var ret = null;
var i = val.length; var i = val.length;
while (!ret && list.length !== 0) { while (!ret && list.length != 0) {
var char = list[0][i]; var char = list[0][i];
if ( if (
!list.every(function (x) { !list.every(function (x) {
return x[i] === char; return x[i] == char;
}) })
) )
ret = list[0].slice(0, i); ret = list[0].slice(0, i);
@@ -214,7 +213,7 @@ var Player = {
mouse_input(type, ...args) { mouse_input(type, ...args) {
for (var pawn of [...this.pawns].reverse()) { for (var pawn of [...this.pawns].reverse()) {
if (typeof pawn.inputs?.mouse?.[type] === "function") { if (typeof pawn.inputs?.mouse?.[type] == "function") {
pawn.inputs.mouse[type].call(pawn, ...args); pawn.inputs.mouse[type].call(pawn, ...args);
pawn.inputs.post?.call(pawn); pawn.inputs.post?.call(pawn);
if (!pawn.inputs.fallthru) return; if (!pawn.inputs.fallthru) return;
@@ -224,7 +223,7 @@ var Player = {
char_input(c) { char_input(c) {
for (var pawn of [...this.pawns].reverse()) { for (var pawn of [...this.pawns].reverse()) {
if (typeof pawn.inputs?.char === "function") { if (typeof pawn.inputs?.char == "function") {
pawn.inputs.char.call(pawn, c); pawn.inputs.char.call(pawn, c);
pawn.inputs.post?.call(pawn); pawn.inputs.post?.call(pawn);
if (!pawn.inputs.fallthru) return; if (!pawn.inputs.fallthru) return;
@@ -271,16 +270,16 @@ var Player = {
fn = inputs[cmd].released; fn = inputs[cmd].released;
break; break;
case "down": case "down":
if (typeof inputs[cmd].down === "function") fn = inputs[cmd].down; if (typeof inputs[cmd].down == "function") fn = inputs[cmd].down;
else if (inputs[cmd].down) fn = inputs[cmd]; else if (inputs[cmd].down) fn = inputs[cmd];
} }
var consumed = false; var consumed = false;
if (typeof fn === "function") { if (typeof fn == "function") {
fn.call(pawn, ...args); fn.call(pawn, ...args);
consumed = true; consumed = true;
} }
if (state === "released") inputs.release_post?.call(pawn); if (state == "released") inputs.release_post?.call(pawn);
if (inputs.block) return; if (inputs.block) return;
if (consumed) return; if (consumed) return;
} }
@@ -295,7 +294,7 @@ var Player = {
}, },
print_pawns() { print_pawns() {
[...this.pawns].reverse().forEach(x => console.log(x)) [...this.pawns].reverse().forEach(x => log.console(x))
}, },
create() { create() {

35
prosperon/device.cm Normal file
View File

@@ -0,0 +1,35 @@
// helpful render devices. width and height in pixels; diagonal in inches.
return {
pc: { width: 1920, height: 1080 },
macbook_m2: { width: 2560, height: 1664, diagonal: 13.6 },
ds_top: { width: 400, height: 240, diagonal: 3.53 },
ds_bottom: { width: 320, height: 240, diagonal: 3.02 },
playdate: { width: 400, height: 240, diagonal: 2.7 },
switch: { width: 1280, height: 720, diagonal: 6.2 },
switch_lite: { width: 1280, height: 720, diagonal: 5.5 },
switch_oled: { width: 1280, height: 720, diagonal: 7 },
dsi: { width: 256, height: 192, diagonal: 3.268 },
ds: { width: 256, height: 192, diagonal: 3 },
dsixl: { width: 256, height: 192, diagonal: 4.2 },
ipad_air_m2: { width: 2360, height: 1640, diagonal: 11.97 },
iphone_se: { width: 1334, height: 750, diagonal: 4.7 },
iphone_12_pro: { width: 2532, height: 1170, diagonal: 6.06 },
iphone_15: { width: 2556, height: 1179, diagonal: 6.1 },
gba: { width: 240, height: 160, diagonal: 2.9 },
gameboy: { width: 160, height: 144, diagonal: 2.48 },
gbc: { width: 160, height: 144, diagonal: 2.28 },
steamdeck: { width: 1280, height: 800, diagonal: 7 },
vita: { width: 960, height: 544, diagonal: 5 },
psp: { width: 480, height: 272, diagonal: 4.3 },
imac_m3: { width: 4480, height: 2520, diagonal: 23.5 },
macbook_pro_m3: { width: 3024, height: 1964, diagonal: 14.2 },
ps1: { width: 320, height: 240, diagonal: 5 },
ps2: { width: 640, height: 480 },
snes: { width: 256, height: 224 },
gamecube: { width: 640, height: 480 },
n64: { width: 320, height: 240 },
c64: { width: 320, height: 200 },
macintosh: { width: 512, height: 342 },
gamegear: { width: 160, height: 144, diagonal: 3.2 }
};

172
prosperon/draw2d.cm Normal file
View File

@@ -0,0 +1,172 @@
var math = use('math')
var color = use('color')
var draw = {}
draw[cell.DOC] = `
A collection of 2D drawing functions that create drawing command lists.
These are pure functions that return plain JavaScript objects representing
drawing operations. No rendering or actor communication happens here.
`
var current_list = []
// Clear current list
draw.clear = function() {
current_list.length = 0
}
// Get commands from current list
draw.get_commands = function() {
return current_list
}
// Helper to add a command
function add_command(type, data) {
data.cmd = type
current_list.push(data)
}
// Default geometry definitions
var ellipse_def = {
start: 0,
end: 1,
mode: 'fill',
thickness: 1,
}
var line_def = {
thickness: 1,
cap:"butt",
}
var rect_def = {
thickness:1,
radius: 0
}
var slice9_info = {
tile_top:true,
tile_bottom:true,
tile_left:true,
tile_right:true,
tile_center_x:true,
tile_center_right:true,
}
var image_info = {
tile_x: false,
tile_y: false,
flip_x: false,
flip_y: false,
mode: 'linear'
}
var circle_def = {
inner_radius:1, // percentage: 1 means filled circle
start:0,
end: 1,
}
// Drawing functions
draw.point = function(pos, size, opt = {}, material) {
add_command("draw_point", {
pos: pos,
size: size,
opt: opt,
material: material
})
}
draw.ellipse = function(pos, radii, defl, material) {
var opt = defl ? {...ellipse_def, ...defl} : ellipse_def
if (opt.thickness <= 0) opt.thickness = Math.max(radii[0], radii[1])
add_command("draw_ellipse", {
pos: pos,
radii: radii,
opt: opt,
material: material
})
}
draw.line = function(points, defl, material)
{
var opt = defl ? {...line_def, ...defl} : line_def
add_command("draw_line", {
points: points,
opt: opt,
material: material
})
}
draw.cross = function render_cross(pos, size, defl, material) {
var a = [pos.add([0, size]), pos.add([0, -size])]
var b = [pos.add([size, 0]), pos.add([-size, 0])]
draw.line(a, defl, material)
draw.line(b, defl, material)
}
draw.arrow = function render_arrow(start, end, wingspan = 4, wingangle = 10, defl, material) {
var dir = math.norm(end.sub(start))
var wing1 = [math.rotate(dir, wingangle).scale(wingspan).add(end), end]
var wing2 = [math.rotate(dir, -wingangle).scale(wingspan).add(end), end]
draw.line([start, end], defl, material)
draw.line(wing1, defl, material)
draw.line(wing2, defl, material)
}
draw.rectangle = function render_rectangle(rect, defl, material) {
var opt = defl ? {...rect_def, ...defl} : rect_def
add_command("draw_rect", {
rect: rect,
opt: opt,
material: material
})
}
draw.slice9 = function slice9(image, rect = [0,0], slice = 0, info = slice9_info, material) {
if (!image) throw Error('Need an image to render.')
add_command("draw_slice9", {
image: image,
rect: rect,
slice: slice,
info: info,
material: material
})
}
draw.image = function image(image, rect, rotation, anchor, shear, info, material) {
if (!rect) throw Error('Need rectangle to render image.')
if (!image) throw Error('Need an image to render.')
if (!('x' in rect && 'y' in rect)) throw Error('Must provide X and Y for image.')
add_command("draw_image", {
image,
rect,
rotation,
anchor,
shear,
info,
material,
})
}
draw.circle = function render_circle(pos, radius, defl, material) {
draw.ellipse(pos, [radius,radius], defl, material)
}
draw.text = function text(text, pos, font = 'fonts/c64.ttf', size = 8, color = color.white, wrap = 0) {
add_command("draw_text", {
text,
pos,
font,
size,
wrap,
material: {color}
})
}
return draw

View File

@@ -1,7 +1,6 @@
var Color = use('color') var color = use('color')
var os = use('os')
var graphics = use('graphics') var graphics = use('graphics')
//var render = use('render') var transform = use('transform')
var ex = {} var ex = {}
@@ -15,7 +14,7 @@ ex.garbage = function()
ex.update = function(dt) ex.update = function(dt)
{ {
for (var e of ex.emitters) for (var e of ex.emitters)
try { e.step(dt) } catch(e) { console.error(e) } try { e.step(dt) } catch(e) { log.error(e) }
} }
ex.step_hook = function(p) ex.step_hook = function(p)
@@ -77,7 +76,7 @@ ex.spawn = function(t)
} }
par = { par = {
transform: os.make_transform(), transform: new transform,
life: this.life, life: this.life,
time: 0, time: 0,
color: this.color, color: this.color,
@@ -104,15 +103,15 @@ ex.scale = 1
ex.grow_for = 0 ex.grow_for = 0
ex.spawn_timer = 0 ex.spawn_timer = 0
ex.pps = 0 ex.pps = 0
ex.color = Color.white ex.color = color.white
ex.draw = function() ex.draw = function()
{ {
/* var diff = graphics.texture(this.diffuse) /* var diff = graphics.texture(this.diffuse)
if (!diff) throw new Error("emitter does not have a proper diffuse texture") if (!diff) throw new Error("emitter does not have a proper diffuse texture")
var mesh = render._main.make_sprite_mesh(this.particles) var mesh = graphics.make_sprite_mesh(this.particles)
if (mesh.num_indices === 0) return if (mesh.num_indices == 0) return
render.queue({ render.queue({
type:'geometry', type:'geometry',
mesh, mesh,

View File

Before

Width:  |  Height:  |  Size: 449 B

After

Width:  |  Height:  |  Size: 449 B

View File

@@ -5,6 +5,7 @@ var sprite = use('sprite')
var geom = use('geometry') var geom = use('geometry')
var input = use('controller') var input = use('controller')
var config = use('config') var config = use('config')
var color = use('color')
var bunnyTex = graphics.texture("bunny") var bunnyTex = graphics.texture("bunny")
@@ -65,5 +66,5 @@ this.hud = function() {
draw.images(bunnyTex, bunnies) draw.images(bunnyTex, bunnies)
var msg = 'FPS: ' + fpsAvg.toFixed(2) + ' Bunnies: ' + bunnies.length var msg = 'FPS: ' + fpsAvg.toFixed(2) + ' Bunnies: ' + bunnies.length
draw.text(msg, {x:0, y:0, width:config.width, height:40}, undefined, 0, Color.white, 0) draw.text(msg, {x:0, y:0, width:config.width, height:40}, null, 0, color.white, 0)
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 B

View File

@@ -0,0 +1,395 @@
/* main.js runs the demo with your prototype-based grid */
var json = use('json')
var draw2d = use('prosperon/draw2d')
var blob = use('blob')
/*──── 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)
}
})

View File

@@ -0,0 +1,9 @@
// Chess game configuration for Moth framework
return {
title: "Chess",
resolution: { width: 480, height: 480 },
internal_resolution: { width: 480, height: 480 },
fps: 60,
clearColor: [22/255, 120/255, 194/255, 1],
mode: 'stretch' // No letterboxing for chess
};

View File

@@ -0,0 +1,69 @@
function grid(w, h) {
this.width = w;
this.height = h;
// create a height×width array of empty lists
this.cells = new Array(h);
for (let y = 0; y < h; y++) {
this.cells[y] = new Array(w);
for (let x = 0; x < w; x++) {
this.cells[y][x] = []; // each cell holds its own list
}
}
}
grid.prototype = {
// return the array at (x,y)
cell(x, y) {
return this.cells[y][x];
},
// alias for cell
at(pos) {
return this.cell(pos.x, pos.y);
},
// add an entity into a cell
add(entity, pos) {
this.cell(pos.x, pos.y).push(entity);
entity.coord = pos.slice();
},
// remove an entity from a cell
remove(entity, pos) {
const c = this.cell(pos.x, pos.y);
const i = c.indexOf(entity);
if (i !== -1) c.splice(i, 1);
},
// bounds check
inBounds(pos) {
return (
pos.x >= 0 && pos.x < this.width &&
pos.y >= 0 && pos.y < this.height
);
},
// call fn(entity, coord) for every entity in every cell
each(fn) {
for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
const list = this.cells[y][x];
for (let entity of list) {
fn(entity, entity.coord);
}
}
}
},
// printable representation
toString() {
let out = `grid [${this.width}×${this.height}]\n`;
for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
out += this.cells[y][x].length;
}
if (y !== this.height - 1) out += "\n";
}
return out;
}
};

View File

@@ -0,0 +1,32 @@
var MovementSystem = function(grid, rules) {
this.grid = grid;
this.rules = rules || {}; // expects { canMove: fn }
this.turn = 'white';
}
MovementSystem.prototype.tryMove = function (piece, to) {
if (piece.colour != this.turn) return false;
// normalise to into our hybrid coord
var dest = [to.x ?? t[0],
to.y ?? to[1]];
if (!this.grid.inBounds(dest)) return false;
if (!this.rules.canMove(piece, piece.coord, dest, this.grid)) return false;
var victims = this.grid.at(dest);
if (victims.length && victims[0].colour == piece.colour) return false;
if (victims.length) victims[0].captured = true;
this.grid.remove(piece, piece.coord);
this.grid.add (piece, dest);
// grid.add() re-creates coord; re-add .x/.y fields:
piece.coord.x = dest.x;
piece.coord.y = dest.y;
this.turn = (this.turn == 'white') ? 'black' : 'white';
return true;
};
return { MovementSystem: MovementSystem };

View File

@@ -0,0 +1,29 @@
/* pieces.js simple data holders + starting layout */
function Piece(kind, colour) {
this.kind = kind; // "pawn" etc.
this.colour = colour; // "white"/"black"
this.sprite = colour + '_' + kind; // for draw2d.image
this.captured = false;
this.coord = [0,0];
}
Piece.prototype.toString = function () {
return this.colour.charAt(0) + this.kind.charAt(0).toUpperCase();
};
function startingPosition(grid) {
var W = 'white', B = 'black', x;
// pawns
for (x = 0; x < 8; x++) {
grid.add(new Piece('pawn', W), [x, 6]);
grid.add(new Piece('pawn', B), [x, 1]);
}
// major pieces
var back = ['rook','knight','bishop','queen','king','bishop','knight','rook'];
for (x = 0; x < 8; x++) {
grid.add(new Piece(back[x], W), [x, 7]);
grid.add(new Piece(back[x], B), [x, 0]);
}
}
return { Piece, startingPosition };

Binary file not shown.

View File

@@ -0,0 +1,45 @@
/* helper robust coord access */
function cx(c) { return c.x ?? c[0] }
function cy(c) { return c.y ?? c[1] }
/* simple move-shape checks */
var deltas = {
pawn: function (pc, dx, dy, grid, to) {
var dir = (pc.colour == 'white') ? -1 : 1;
var base = (pc.colour == 'white') ? 6 : 1;
var one = (dy == dir && dx == 0 && grid.at(to).length == 0);
var two = (dy == 2 * dir && dx == 0 && cy(pc.coord) == base &&
grid.at({ x: cx(pc.coord), y: cy(pc.coord)+dir }).length == 0 &&
grid.at(to).length == 0);
var cap = (dy == dir && Math.abs(dx) == 1 && grid.at(to).length);
return one || two || cap;
},
rook : function (pc, dx, dy) { return (dx == 0 || dy == 0); },
bishop: function (pc, dx, dy) { return Math.abs(dx) == Math.abs(dy); },
queen : function (pc, dx, dy) { return (dx == 0 || dy == 0 || Math.abs(dx) == Math.abs(dy)); },
knight: function (pc, dx, dy) { return (Math.abs(dx) == 1 && Math.abs(dy) == 2) ||
(Math.abs(dx) == 2 && Math.abs(dy) == 1); },
king : function (pc, dx, dy) { return Math.max(Math.abs(dx), Math.abs(dy)) == 1; }
};
function clearLine(from, to, grid) {
var dx = Math.sign(cx(to) - cx(from));
var dy = Math.sign(cy(to) - cy(from));
var x = cx(from) + dx, y = cy(from) + dy;
while (x != cx(to) || y != cy(to)) {
if (grid.at({ x: x, y: y }).length) return false;
x += dx; y += dy;
}
return true;
}
function canMove(piece, from, to, grid) {
var dx = cx(to) - cx(from);
var dy = cy(to) - cy(from);
var f = deltas[piece.kind];
if (!f || !f(piece, dx, dy, grid, to)) return false;
if (piece.kind == 'knight') return true;
return clearLine(from, to, grid);
}
return { canMove };

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 B

View File

@@ -2,6 +2,7 @@
var draw = use('draw2d') var draw = use('draw2d')
var input = use('controller') var input = use('controller')
var config = use('config') var config = use('config')
var color = use('color')
prosperon.camera.transform.pos = [0,0] prosperon.camera.transform.pos = [0,0]
@@ -73,13 +74,13 @@ this.hud = function() {
draw.rectangle({x:0, y:0, width:config.width, height:config.height}, [0,0,0,1]) draw.rectangle({x:0, y:0, width:config.width, height:config.height}, [0,0,0,1])
// Draw paddles // Draw paddles
draw.rectangle({x:p1.x - paddleW*0.5, y:p1.y - paddleH*0.5, width:paddleW, height:paddleH}, Color.white) draw.rectangle({x:p1.x - paddleW*0.5, y:p1.y - paddleH*0.5, width:paddleW, height:paddleH}, color.white)
draw.rectangle({x:p2.x - paddleW*0.5, y:p2.y - paddleH*0.5, width:paddleW, height:paddleH}, Color.white) draw.rectangle({x:p2.x - paddleW*0.5, y:p2.y - paddleH*0.5, width:paddleW, height:paddleH}, color.white)
// Draw ball // Draw ball
draw.rectangle({x:ball.x - ball.size*0.5, y:ball.y - ball.size*0.5, width:ball.size, height:ball.size}, Color.white) draw.rectangle({x:ball.x - ball.size*0.5, y:ball.y - ball.size*0.5, width:ball.size, height:ball.size}, color.white)
// Simple score display // Simple score display
var msg = score1 + " " + score2 var msg = score1 + " " + score2
draw.text(msg, {x:0, y:10, width:config.width, height:40}, undefined, 0, Color.white, 0) draw.text(msg, {x:0, y:10, width:config.width, height:40}, null, 0, color.white, 0)
} }

View File

@@ -4,6 +4,7 @@ var render = use('render')
var graphics = use('graphics') var graphics = use('graphics')
var input = use('input') var input = use('input')
var config = use('config') var config = use('config')
var color = use('color')
prosperon.camera.transform.pos = [0,0] prosperon.camera.transform.pos = [0,0]
@@ -35,7 +36,7 @@ function spawnApple() {
apple = {x:Math.floor(Math.random()*gridW), y:Math.floor(Math.random()*gridH)} apple = {x:Math.floor(Math.random()*gridW), y:Math.floor(Math.random()*gridH)}
// Re-spawn if apple lands on snake // Re-spawn if apple lands on snake
for (var i=0; i<snake.length; i++) for (var i=0; i<snake.length; i++)
if (snake[i].x === apple.x && snake[i].y === apple.y) { spawnApple(); return } if (snake[i].x == apple.x && snake[i].y == apple.y) { spawnApple(); return }
} }
function wrap(pos) { function wrap(pos) {
@@ -48,7 +49,7 @@ function wrap(pos) {
resetGame() resetGame()
this.update = function(dt) { this.update = function(dt) {
if (gameState !== "playing") return if (gameState != "playing") return
moveTimer += dt moveTimer += dt
if (moveTimer < moveInterval) return if (moveTimer < moveInterval) return
moveTimer -= moveInterval moveTimer -= moveInterval
@@ -62,7 +63,7 @@ this.update = function(dt) {
// Check collision with body // Check collision with body
for (var i=0; i<snake.length; i++) { for (var i=0; i<snake.length; i++) {
if (snake[i].x === head.x && snake[i].y === head.y) { if (snake[i].x == head.x && snake[i].y == head.y) {
gameState = "gameover" gameState = "gameover"
return return
} }
@@ -72,7 +73,7 @@ this.update = function(dt) {
snake.unshift(head) snake.unshift(head)
// Eat apple? // Eat apple?
if (head.x === apple.x && head.y === apple.y) spawnApple() if (head.x == apple.x && head.y == apple.y) spawnApple()
else snake.pop() else snake.pop()
} }
@@ -83,15 +84,15 @@ this.hud = function() {
// Draw snake // Draw snake
for (var i=0; i<snake.length; i++) { for (var i=0; i<snake.length; i++) {
var s = snake[i] var s = snake[i]
draw.rectangle({x:s.x*cellSize, y:s.y*cellSize, width:cellSize, height:cellSize}, Color.green) draw.rectangle({x:s.x*cellSize, y:s.y*cellSize, width:cellSize, height:cellSize}, color.green)
} }
// Draw apple // Draw apple
draw.rectangle({x:apple.x*cellSize, y:apple.y*cellSize, width:cellSize, height:cellSize}, Color.red) draw.rectangle({x:apple.x*cellSize, y:apple.y*cellSize, width:cellSize, height:cellSize}, color.red)
if (gameState === "gameover") { if (gameState == "gameover") {
var msg = "GAME OVER! Press SPACE to restart." var msg = "GAME OVER! Press SPACE to restart."
draw.text(msg, {x:0, y:config.height*0.5-10, width:config.width, height:20}, undefined, 0, Color.white) draw.text(msg, {x:0, y:config.height*0.5-10, width:config.width, height:20}, null, 0, color.white)
} }
} }
@@ -99,19 +100,19 @@ this.hud = function() {
// "Up" means y=1, so going physically up on screen // "Up" means y=1, so going physically up on screen
this.inputs = { this.inputs = {
up: function() { up: function() {
if (direction.y !== -1) nextDirection = {x:0,y:1} if (direction.y != -1) nextDirection = {x:0,y:1}
}, },
down: function() { down: function() {
if (direction.y !== 1) nextDirection = {x:0,y:-1} if (direction.y != 1) nextDirection = {x:0,y:-1}
}, },
left: function() { left: function() {
if (direction.x !== 1) nextDirection = {x:-1,y:0} if (direction.x != 1) nextDirection = {x:-1,y:0}
}, },
right: function() { right: function() {
if (direction.x !== -1) nextDirection = {x:1,y:0} if (direction.x != -1) nextDirection = {x:1,y:0}
}, },
space: function() { space: function() {
if (gameState==="gameover") resetGame() if (gameState=="gameover") resetGame()
} }
} }

View File

@@ -0,0 +1,187 @@
// Steam Integration Example
// This example shows how to use Steam achievements and stats
var steam = use("steam");
// Achievement names (these should match your Steam app configuration)
var ACHIEVEMENTS = {
FIRST_WIN: "ACH_FIRST_WIN",
PLAY_10_GAMES: "ACH_PLAY_10_GAMES",
HIGH_SCORE: "ACH_HIGH_SCORE_1000"
};
// Stat names
var STATS = {
GAMES_PLAYED: "stat_games_played",
TOTAL_SCORE: "stat_total_score",
PLAY_TIME: "stat_play_time"
};
var steam_available = false;
var stats_loaded = false;
// Initialize Steam
function init_steam() {
if (!steam) {
log.console("Steam module not available");
return false;
}
log.console("Initializing Steam...");
steam_available = steam.steam_init();
if (steam_available) {
log.console("Steam initialized successfully");
// Request current stats/achievements
if (steam.stats.stats_request()) {
log.console("Stats requested");
stats_loaded = true;
}
} else {
log.console("Failed to initialize Steam");
}
return steam_available;
}
// Update Steam (call this regularly, e.g., once per frame)
function update_steam() {
if (steam_available) {
steam.steam_run_callbacks();
}
}
// Unlock an achievement
function unlock_achievement(achievement_name) {
if (!steam_available || !stats_loaded) return false;
// Check if already unlocked
var unlocked = steam.achievement.achievement_get(achievement_name);
if (unlocked) {
log.console("Achievement already unlocked:", achievement_name);
return true;
}
// Unlock it
if (steam.achievement.achievement_set(achievement_name)) {
log.console("Achievement unlocked:", achievement_name);
// Store stats to make it permanent
steam.stats.stats_store();
return true;
}
return false;
}
// Update a stat
function update_stat(stat_name, value, is_float) {
if (!steam_available || !stats_loaded) return false;
var success;
if (is_float) {
success = steam.stats.stats_set_float(stat_name, value);
} else {
success = steam.stats.stats_set_int(stat_name, value);
}
if (success) {
log.console("Stat updated:", stat_name, "=", value);
steam.stats.stats_store();
}
return success;
}
// Get a stat value
function get_stat(stat_name, is_float) {
if (!steam_available || !stats_loaded) return 0;
if (is_float) {
return steam.stats.stats_get_float(stat_name) || 0;
} else {
return steam.stats.stats_get_int(stat_name) || 0;
}
}
// Example game logic
var games_played = 0;
var total_score = 0;
var current_score = 0;
function start_game() {
games_played = get_stat(STATS.GAMES_PLAYED, false);
total_score = get_stat(STATS.TOTAL_SCORE, false);
current_score = 0;
log.console("Starting game #" + (games_played + 1));
}
function end_game(score) {
current_score = score;
games_played++;
total_score += score;
// Update stats
update_stat(STATS.GAMES_PLAYED, games_played, false);
update_stat(STATS.TOTAL_SCORE, total_score, false);
// Check for achievements
if (games_played == 1) {
unlock_achievement(ACHIEVEMENTS.FIRST_WIN);
}
if (games_played >= 10) {
unlock_achievement(ACHIEVEMENTS.PLAY_10_GAMES);
}
if (score >= 1000) {
unlock_achievement(ACHIEVEMENTS.HIGH_SCORE);
}
}
// Cloud save example
function save_to_cloud(save_data) {
if (!steam_available) return false;
var json_data = JSON.stringify(save_data);
return steam.cloud.cloud_write("savegame.json", json_data);
}
function load_from_cloud() {
if (!steam_available) return null;
var data = steam.cloud.cloud_read("savegame.json");
if (data) {
// Convert ArrayBuffer to string
var decoder = new TextDecoder();
var json_str = decoder.decode(data);
return JSON.parse(json_str);
}
return null;
}
// Cleanup
function cleanup_steam() {
if (steam_available) {
steam.steam_shutdown();
log.console("Steam shut down");
}
}
// Export the API
module.exports = {
init: init_steam,
update: update_steam,
cleanup: cleanup_steam,
unlock_achievement: unlock_achievement,
update_stat: update_stat,
get_stat: get_stat,
start_game: start_game,
end_game: end_game,
save_to_cloud: save_to_cloud,
load_from_cloud: load_from_cloud,
is_available: function() { return steam_available; }
};

View File

@@ -1,6 +1,7 @@
var draw = use('draw2d') var draw = use('draw2d')
var input = use('input') var input = use('input')
var config = use('config') var config = use('config')
var color = use('color')
prosperon.camera.transform.pos = [0,0] prosperon.camera.transform.pos = [0,0]
@@ -127,10 +128,10 @@ function clearLines() {
} }
} }
// Score // Score
if (lines===1) score += 100 if (lines==1) score += 100
else if (lines===2) score += 300 else if (lines==2) score += 300
else if (lines===3) score += 500 else if (lines==3) score += 500
else if (lines===4) score += 800 else if (lines==4) score += 800
linesCleared += lines linesCleared += lines
level = Math.floor(linesCleared/10) level = Math.floor(linesCleared/10)
} }
@@ -152,7 +153,7 @@ spawnPiece()
this.update = function(dt) { this.update = function(dt) {
if (gameOver) return if (gameOver) return
// ========== Horizontal Movement Gate ========== // ======= Horizontal Movement Gate =======
var leftPressed = input.keyboard.down('a') var leftPressed = input.keyboard.down('a')
var rightPressed = input.keyboard.down('d') var rightPressed = input.keyboard.down('d')
var horizontalMove = 0 var horizontalMove = 0
@@ -190,7 +191,7 @@ this.update = function(dt) {
hMoveTimer -= dt hMoveTimer -= dt
prevLeft = leftPressed prevLeft = leftPressed
prevRight = rightPressed prevRight = rightPressed
// ========== End Horizontal Movement Gate ========== // ======= End Horizontal Movement Gate =======
// Rotate with W (once per press, no spinning) // Rotate with W (once per press, no spinning)
if (input.keyboard.down('w')) { if (input.keyboard.down('w')) {
@@ -248,7 +249,7 @@ this.hud = function() {
} }
// Next piece window // Next piece window
draw.text("Next", {x:70, y:5, width:50, height:10}, undefined, 0, Color.white) draw.text("Next", {x:70, y:5, width:50, height:10}, null, 0, color.white)
if (nextPiece) { if (nextPiece) {
for (var i=0; i<nextPiece.blocks.length; i++) { for (var i=0; i<nextPiece.blocks.length; i++) {
var nx = nextPiece.blocks[i][0] var nx = nextPiece.blocks[i][0]
@@ -261,10 +262,10 @@ this.hud = function() {
// Score & Level // Score & Level
var info = "Score: " + score + "\nLines: " + linesCleared + "\nLevel: " + level var info = "Score: " + score + "\nLines: " + linesCleared + "\nLevel: " + level
draw.text(info, {x:70, y:30, width:90, height:50}, undefined, 0, Color.white) draw.text(info, {x:70, y:30, width:90, height:50}, null, 0, color.white)
if (gameOver) { if (gameOver) {
draw.text("GAME OVER", {x:10, y:config.height*0.5-5, width:config.width-20, height:20}, undefined, 0, Color.red) draw.text("GAME OVER", {x:10, y:config.height*0.5-5, width:config.width-20, height:20}, null, 0, color.red)
} }
} }

View File

@@ -1,5 +1,5 @@
var geometry = this var geometry = this
geometry[prosperon.DOC] = ` geometry[cell.DOC] = `
A collection of geometry-related functions for circles, spheres, boxes, polygons, A collection of geometry-related functions for circles, spheres, boxes, polygons,
and rectangle utilities. Some functionality is implemented in C and exposed here. and rectangle utilities. Some functionality is implemented in C and exposed here.
` `
@@ -7,7 +7,7 @@ and rectangle utilities. Some functionality is implemented in C and exposed here
var math = use('math') var math = use('math')
geometry.box = {} geometry.box = {}
geometry.box[prosperon.DOC] = ` geometry.box[cell.DOC] = `
An object for box-related operations. Overridden later by a function definition, so An object for box-related operations. Overridden later by a function definition, so
its direct usage is overshadowed. Contains: its direct usage is overshadowed. Contains:
- points(ll, ur): Return an array of four 2D points for a box from ll (lower-left) to ur (upper-right). - points(ll, ur): Return an array of four 2D points for a box from ll (lower-left) to ur (upper-right).
@@ -16,7 +16,7 @@ its direct usage is overshadowed. Contains:
geometry.box.points = function (ll, ur) { geometry.box.points = function (ll, ur) {
return [ll, ll.add([ur.x - ll.x, 0]), ur, ll.add([0, ur.y - ll.y])] return [ll, ll.add([ur.x - ll.x, 0]), ur, ll.add([0, ur.y - ll.y])]
} }
geometry.box.points[prosperon.DOC] = ` geometry.box.points[cell.DOC] = `
:param ll: Lower-left coordinate as a 2D vector (x,y). :param ll: Lower-left coordinate as a 2D vector (x,y).
:param ur: Upper-right coordinate as a 2D vector (x,y). :param ur: Upper-right coordinate as a 2D vector (x,y).
:return: An array of four points forming the corners of the box in order [ll, lower-right, ur, upper-left]. :return: An array of four points forming the corners of the box in order [ll, lower-right, ur, upper-left].
@@ -24,14 +24,14 @@ Compute the four corners of a box given lower-left and upper-right corners.
` `
geometry.sphere = {} geometry.sphere = {}
geometry.sphere[prosperon.DOC] = ` geometry.sphere[cell.DOC] = `
Sphere-related geometry functions: Sphere-related geometry functions:
- volume(r): Return the volume of a sphere with radius r. - volume(r): Return the volume of a sphere with radius r.
- random(r, theta, phi): Return a random point on or inside a sphere. - random(r, theta, phi): Return a random point on or inside a sphere.
` `
geometry.circle = {} geometry.circle = {}
geometry.circle[prosperon.DOC] = ` geometry.circle[cell.DOC] = `
Circle-related geometry functions: Circle-related geometry functions:
- area(r): Return the area of a circle with radius r. - area(r): Return the area of a circle with radius r.
- random(r, theta): Return a random 2D point on a circle; uses sphere.random internally and extracts x,z. - random(r, theta): Return a random 2D point on a circle; uses sphere.random internally and extracts x,z.
@@ -40,22 +40,22 @@ Circle-related geometry functions:
geometry.sphere.volume = function (r) { geometry.sphere.volume = function (r) {
return (Math.pi * r * r * r * 4) / 3 return (Math.pi * r * r * r * 4) / 3
} }
geometry.sphere.volume[prosperon.DOC] = ` geometry.sphere.volume[cell.DOC] = `
:param r: The sphere radius. :param r: The sphere radius.
:return: The volume of the sphere, calculated as (4/3) * pi * r^3. :return: The volume of the sphere, calculated as (4/3) * pi * r^3.
` `
geometry.sphere.random = function (r, theta = [0, 1], phi = [-0.5, 0.5]) { geometry.sphere.random = function (r, theta = [0, 1], phi = [-0.5, 0.5]) {
if (typeof r === "number") r = [r, r] if (typeof r == "number") r = [r, r]
if (typeof theta === "number") theta = [theta, theta] if (typeof theta == "number") theta = [theta, theta]
if (typeof phi === "number") phi = [phi, phi] if (typeof phi == "number") phi = [phi, phi]
var ra = Math.random_range(r[0], r[1]) var ra = Math.random_range(r[0], r[1])
var ta = Math.turn2rad(Math.random_range(theta[0], theta[1])) var ta = Math.turn2rad(Math.random_range(theta[0], theta[1]))
var pa = Math.turn2rad(Math.random_range(phi[0], phi[1])) var pa = Math.turn2rad(Math.random_range(phi[0], phi[1]))
return [ra * Math.sin(ta) * Math.cos(pa), ra * Math.sin(ta) * Math.sin(pa), ra * Math.cos(ta)] return [ra * Math.sin(ta) * Math.cos(pa), ra * Math.sin(ta) * Math.sin(pa), ra * Math.cos(ta)]
} }
geometry.sphere.random[prosperon.DOC] = ` geometry.sphere.random[cell.DOC] = `
:param r: A single number (radius) or a 2-element array [minRadius, maxRadius]. :param r: A single number (radius) or a 2-element array [minRadius, maxRadius].
:param theta: A single number or 2-element array defining the range in turns for the theta angle, default [0,1]. :param theta: A single number or 2-element array defining the range in turns for the theta angle, default [0,1].
:param phi: A single number or 2-element array defining the range in turns for the phi angle, default [-0.5,0.5]. :param phi: A single number or 2-element array defining the range in turns for the phi angle, default [-0.5,0.5].
@@ -66,7 +66,7 @@ Generate a random point inside a sphere of variable radius, distributing angles
geometry.circle.area = function (r) { geometry.circle.area = function (r) {
return Math.pi * r * r return Math.pi * r * r
} }
geometry.circle.area[prosperon.DOC] = ` geometry.circle.area[cell.DOC] = `
:param r: Radius of the circle. :param r: Radius of the circle.
:return: The area, pi * r^2. :return: The area, pi * r^2.
` `
@@ -74,7 +74,7 @@ geometry.circle.area[prosperon.DOC] = `
geometry.circle.random = function (r, theta) { geometry.circle.random = function (r, theta) {
return geometry.sphere.random(r, theta).xz return geometry.sphere.random(r, theta).xz
} }
geometry.circle.random[prosperon.DOC] = ` geometry.circle.random[cell.DOC] = `
:param r: A radius or [minRadius, maxRadius]. :param r: A radius or [minRadius, maxRadius].
:param theta: Angle range in turns (single number or [min,max]). :param theta: Angle range in turns (single number or [min,max]).
:return: A 2D point (x,z) in the circle, using the sphere random generator and ignoring y. :return: A 2D point (x,z) in the circle, using the sphere random generator and ignoring y.
@@ -91,7 +91,7 @@ geometry.box = function (w, h) {
] ]
return points return points
} }
geometry.box[prosperon.DOC] = ` geometry.box[cell.DOC] = `
:param w: The width of the box. :param w: The width of the box.
:param h: The height of the box. :param h: The height of the box.
:return: An array of four 2D points representing the corners of a rectangle centered at [0,0]. :return: An array of four 2D points representing the corners of a rectangle centered at [0,0].
@@ -101,7 +101,7 @@ Construct a box centered at the origin with the given width and height. This ove
geometry.ngon = function (radius, n) { geometry.ngon = function (radius, n) {
return geometry.arc(radius, 360, n) return geometry.arc(radius, 360, n)
} }
geometry.ngon[prosperon.DOC] = ` geometry.ngon[cell.DOC] = `
:param radius: The radius of the n-gon from center to each vertex. :param radius: The radius of the n-gon from center to each vertex.
:param n: Number of sides/vertices. :param n: Number of sides/vertices.
:return: An array of 2D points forming a regular n-gon. :return: An array of 2D points forming a regular n-gon.
@@ -118,7 +118,7 @@ geometry.arc = function (radius, angle, n, start = 0) {
for (var i = 0; i < n; i++) points.push(math.rotate([radius, 0], start + arclen * i)) for (var i = 0; i < n; i++) points.push(math.rotate([radius, 0], start + arclen * i))
return points return points
} }
geometry.arc[prosperon.DOC] = ` geometry.arc[cell.DOC] = `
:param radius: The distance from center to the arc points. :param radius: The distance from center to the arc points.
:param angle: The total angle (in degrees) over which points are generated, capped at 360. :param angle: The total angle (in degrees) over which points are generated, capped at 360.
:param n: Number of segments (if <=1, empty array is returned). :param n: Number of segments (if <=1, empty array is returned).
@@ -131,7 +131,7 @@ geometry.circle.points = function (radius, n) {
if (n <= 1) return [] if (n <= 1) return []
return geometry.arc(radius, 360, n) return geometry.arc(radius, 360, n)
} }
geometry.circle.points[prosperon.DOC] = ` geometry.circle.points[cell.DOC] = `
:param radius: The circle's radius. :param radius: The circle's radius.
:param n: Number of points around the circle. :param n: Number of points around the circle.
:return: An array of 2D points equally spaced around a full 360-degree circle. :return: An array of 2D points equally spaced around a full 360-degree circle.
@@ -141,7 +141,7 @@ Shortcut for geometry.arc(radius, 360, n).
geometry.corners2points = function (ll, ur) { geometry.corners2points = function (ll, ur) {
return [ll, ll.add([ur.x, 0]), ur, ll.add([0, ur.y])] return [ll, ll.add([ur.x, 0]), ur, ll.add([0, ur.y])]
} }
geometry.corners2points[prosperon.DOC] = ` geometry.corners2points[cell.DOC] = `
:param ll: Lower-left 2D coordinate. :param ll: Lower-left 2D coordinate.
:param ur: Upper-right 2D coordinate (relative offset in x,y). :param ur: Upper-right 2D coordinate (relative offset in x,y).
:return: A four-point array of corners [ll, lower-right, upper-right, upper-left]. :return: A four-point array of corners [ll, lower-right, upper-right, upper-left].
@@ -158,7 +158,7 @@ geometry.sortpointsccw = function (points) {
}) })
return ccw.map(function (x) { return x.add(cm) }) return ccw.map(function (x) { return x.add(cm) })
} }
geometry.sortpointsccw[prosperon.DOC] = ` geometry.sortpointsccw[cell.DOC] = `
:param points: An array of 2D points. :param points: An array of 2D points.
:return: A new array of the same points, sorted counterclockwise around their centroid. :return: A new array of the same points, sorted counterclockwise around their centroid.
Sort an array of points in CCW order based on their angles from the centroid. Sort an array of points in CCW order based on their angles from the centroid.
@@ -185,61 +185,61 @@ geometry.points2cm = function(points) {
}) })
return [x / n, y / n] return [x / n, y / n]
} }
geometry.points2cm[prosperon.DOC] = ` geometry.points2cm[cell.DOC] = `
:param points: An array of 2D points. :param points: An array of 2D points.
:return: The centroid (average x,y) of the given points. :return: The centroid (average x,y) of the given points.
` `
geometry.rect_intersection[prosperon.DOC] = ` geometry.rect_intersection[cell.DOC] = `
:param a: The first rectangle as {x, y, w, h}. :param a: The first rectangle as {x, y, w, h}.
:param b: The second rectangle as {x, y, w, h}. :param b: The second rectangle as {x, y, w, h}.
:return: A rectangle that is the intersection of the two. May have zero width/height if no overlap. :return: A rectangle that is the intersection of the two. May have zero width/height if no overlap.
Return the intersection of two rectangles. The result may be empty if no intersection. Return the intersection of two rectangles. The result may be empty if no intersection.
` `
geometry.rect_intersects[prosperon.DOC] = ` geometry.rect_intersects[cell.DOC] = `
:param a: Rectangle {x,y,w,h}. :param a: Rectangle {x,y,w,h}.
:param b: Rectangle {x,y,w,h}. :param b: Rectangle {x,y,w,h}.
:return: A boolean indicating whether the two rectangles overlap. :return: A boolean indicating whether the two rectangles overlap.
` `
geometry.rect_expand[prosperon.DOC] = ` geometry.rect_expand[cell.DOC] = `
:param a: Rectangle {x,y,w,h}. :param a: Rectangle {x,y,w,h}.
:param b: Rectangle {x,y,w,h}. :param b: Rectangle {x,y,w,h}.
:return: A new rectangle that covers the bounds of both input rectangles. :return: A new rectangle that covers the bounds of both input rectangles.
Merge or combine two rectangles, returning their bounding rectangle. Merge or combine two rectangles, returning their bounding rectangle.
` `
geometry.rect_inside[prosperon.DOC] = ` geometry.rect_inside[cell.DOC] = `
:param inner: A rectangle to test. :param inner: A rectangle to test.
:param outer: A rectangle that may contain 'inner'. :param outer: A rectangle that may contain 'inner'.
:return: True if 'inner' is completely inside 'outer', otherwise false. :return: True if 'inner' is completely inside 'outer', otherwise false.
` `
geometry.rect_random[prosperon.DOC] = ` geometry.rect_random[cell.DOC] = `
:param rect: A rectangle {x,y,w,h}. :param rect: A rectangle {x,y,w,h}.
:return: A random point within the rectangle (uniform distribution). :return: A random point within the rectangle (uniform distribution).
` `
geometry.cwh2rect[prosperon.DOC] = ` geometry.cwh2rect[cell.DOC] = `
:param center: A 2D point [cx, cy]. :param center: A 2D point [cx, cy].
:param wh: A 2D size [width, height]. :param wh: A 2D size [width, height].
:return: A rectangle {x, y, w, h} with x,y set to center and w,h set to the given size. :return: A rectangle {x, y, w, h} with x,y set to center and w,h set to the given size.
Helper: convert a center point and width/height vector to a rect object. Helper: convert a center point and width/height vector to a rect object.
` `
geometry.rect_point_inside[prosperon.DOC] = ` geometry.rect_point_inside[cell.DOC] = `
:param rect: A rectangle {x,y,w,h}. :param rect: A rectangle {x,y,w,h}.
:param point: A 2D point [px, py]. :param point: A 2D point [px, py].
:return: True if the point lies inside the rectangle, otherwise false. :return: True if the point lies inside the rectangle, otherwise false.
` `
geometry.rect_pos[prosperon.DOC] = ` geometry.rect_pos[cell.DOC] = `
:param rect: A rectangle {x,y,w,h}. :param rect: A rectangle {x,y,w,h}.
:return: A 2D vector [x,y] giving the rectangle's position. :return: A 2D vector [x,y] giving the rectangle's position.
` `
geometry.rect_move[prosperon.DOC] = ` geometry.rect_move[cell.DOC] = `
:param rect: A rectangle {x,y,w,h}. :param rect: A rectangle {x,y,w,h}.
:param offset: A 2D vector to add to the rectangle's position. :param offset: A 2D vector to add to the rectangle's position.
:return: A new rectangle with updated x,y offset. :return: A new rectangle with updated x,y offset.

View File

@@ -1,62 +1,197 @@
var graphics = this var graphics = this
graphics[prosperon.DOC] = `
graphics[cell.DOC] = `
Provides functionality for loading and managing images, fonts, textures, and sprite meshes. Provides functionality for loading and managing images, fonts, textures, and sprite meshes.
Includes both JavaScript and C-implemented routines for creating geometry buffers, performing Includes both JavaScript and C-implemented routines for creating geometry buffers, performing
rectangle packing, etc. rectangle packing, etc.
` `
var renderer_actor = arg[0]
var io = use('io') var io = use('io')
var time = use('time')
var res = use('resources') var res = use('resources')
var json = use('json')
var GPU = Symbol()
var CPU = Symbol()
var LASTUSE = Symbol()
var LOADING = Symbol()
var cache = {}
// Image constructor function
graphics.Image = function(surfaceData) {
// Initialize private properties
this[CPU] = surfaceData || null;
this[GPU] = null;
this[LOADING] = false;
this[LASTUSE] = time.number();
this.rect = {x:0, y:0, width:surfaceData.width, height:surfaceData.height};
}
// Define getters and methods on the prototype
Object.defineProperties(graphics.Image.prototype, {
gpu: {
get: function() {
if (!this[GPU] && !this[LOADING]) {
this[LOADING] = true;
var self = this;
// Send message to load texture
send(renderer_actor, {
kind: "renderer",
op: "loadTexture",
data: this[CPU]
}, function(response) {
if (response.error) {
log.error("Failed to load texture:")
log.error(response.error)
self[LOADING] = false;
} else {
self[GPU] = response;
decorate_rect_px(self);
self[LOADING] = false;
}
});
}
return this[GPU]
}
},
texture: {
get: function() { return this.gpu }
},
cpu: {
get: function() {
return this[CPU]
}
},
surface: {
get: function() { return this.cpu }
},
width: {
get: function() {
return this[CPU].width
}
},
height: {
get: function() {
return this[CPU].height
}
}
});
// Add methods to prototype
graphics.Image.prototype.unload_gpu = function() {
this[GPU] = null
}
graphics.Image.prototype.unload_cpu = function() {
this[CPU] = null
}
function calc_image_size(img) { function calc_image_size(img) {
if (!img.texture || !img.rect) return if (!img.texture || !img.rect) return
return [img.texture.width * img.rect.width, img.texture.height * img.rect.height] return [img.texture.width * img.rect.width, img.texture.height * img.rect.height]
} }
/** function decorate_rect_px(img) {
Internally loads image data from disk and prepares a GPU texture. Used by graphics.texture(). // needs a GPU texture to measure
Not intended for direct user calls. if (!img || !img.texture) return
*/
function create_image(path) { // default UV rect is the whole image if none supplied
var data = io.slurpbytes(path) img.rect ??= {x:0, y:0, width:1, height:1} // [u0,v0,uw,vh] in 0-1
var newimg
switch (path.ext()) { // store pixel-space version: [x, y, w, h] in texels
case 'gif': img.rect_px = {
newimg = graphics.make_gif(data) x:Math.round(img.rect.x * img.texture.width),
if (newimg.surface) y:Math.round(img.rect.y * img.texture.height),
newimg.texture = prosperon.gpu.load_texture(newimg.surface) width:Math.round(img.rect.width * img.texture.width),
else height:Math.round(img.rect.height * img.texture.height)
for (var frame of newimg.frames) }
frame.texture = prosperon.gpu.load_texture(frame.surface) }
break
case 'ase': function make_handle(obj)
case 'aseprite': {
newimg = graphics.make_aseprite(data) return new graphics.Image(obj);
if (newimg.surface) }
newimg.texture = prosperon.gpu.load_texture(newimg.surface)
else { function wrapSurface(surf, maybeRect){
for (var anim in newimg) { def h = make_handle(surf);
var a = newimg[anim] if(maybeRect) h.rect = maybeRect; /* honour frame sub-rect */
for (var frame of a.frames) return h;
frame.texture = prosperon.gpu.load_texture(frame.surface) }
} function wrapFrames(arr){ /* [{surface,time,rect}, …] → [{image,time}] */
} return arr.map(f => ({
break image : wrapSurface(f.surface || f), /* accept bare surface too */
default: time: f.time,
newimg = { rect: f.rect /* keep for reference */
surface: graphics.make_texture(data) }));
} }
newimg.texture = prosperon.gpu.load_texture(newimg.surface) function makeAnim(frames, loop=true){
break return { frames, loop }
}
function decode_image(bytes, ext)
{
switch(ext) {
case 'gif': return graphics.make_gif(bytes)
case 'ase':
case 'aseprite': return graphics.make_aseprite(bytes)
default: return {surface:graphics.make_texture(bytes)}
}
}
function create_image(path){
try{
def bytes = io.slurpbytes(path);
let raw = decode_image(bytes, path.ext());
/* ── Case A: static image ─────────────────────────────────── */
if(raw.surface) {
var gg = new graphics.Image(raw.surface)
return gg
}
/* ── Case B: GIF helpers returned array [surf, …] ─────────── */
if(Array.isArray(raw))
return makeAnim( wrapFrames(raw), true );
/* ── Case C: GIF helpers returned {frames,loop} ───────────── */
if(raw.frames && Array.isArray(raw.frames))
return makeAnim( wrapFrames(raw.frames), !!raw.loop );
/* ── Case D: ASE helpers returned { animName:{frames,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) /* ase with flat surface */
anims[name] = makeAnim(
[{image:make_handle(anim.surface),time:0}], true );
}
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;
} }
return newimg
} }
var image = {} var image = {}
image.dimensions = function() { image.dimensions = function() {
return [this.texture.width, this.texture.height].scale([this.rect[2], this.rect[3]]) return [this.texture.width, this.texture.height].scale([this.rect[2], this.rect[3]])
} }
image.dimensions[prosperon.DOC] = ` image.dimensions[cell.DOC] = `
:return: A 2D array [width, height] that is the scaled size of this image (texture size * rect size). :return: A 2D array [width, height] that is the scaled size of this image (texture size * rect size).
` `
@@ -77,22 +212,55 @@ function pack_into_sheet(images) {
graphics.is_image = function(obj) { graphics.is_image = function(obj) {
if (obj.texture && obj.rect) return true if (obj.texture && obj.rect) return true
} }
graphics.is_image[prosperon.DOC] = ` graphics.is_image[cell.DOC] = `
:param obj: An object to check. :param obj: An object to check.
:return: True if 'obj' has a .texture and a .rect property, indicating it's an image object. :return: True if 'obj' has a .texture and a .rect property, indicating it's an image object.
` `
graphics.texture = function texture(path) { graphics.texture_from_data = function(data)
if (typeof path !== 'string') { {
return path // fallback if already an image object if (!(data instanceof ArrayBuffer)) return null
throw new Error('need a string for graphics.texture')
} var image = graphics.make_texture(data);
var parts = path.split(':') var img = make_handle(image)
var ipath = res.find_image(parts[0])
graphics.texture.cache[ipath] ??= create_image(ipath) img.gpu;
return graphics.texture.cache[ipath]
return img;
} }
graphics.texture[prosperon.DOC] = `
graphics.from_surface = function(id, 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 id = path //.split(':')[0]
if (cache[id]) return cache[id]
var ipath = res.find_image(id)
if (!ipath)
throw new Error(`unknown image ${id}`)
var image = create_image(ipath)
cache[id] = image
return image
}
graphics.texture[cell.DOC] = `
:param path: A string path to an image file or an already-loaded image object. :param path: A string path to an image file or an already-loaded image object.
:return: An image object with {surface, texture, frames?, etc.} depending on the format. :return: An image object with {surface, texture, frames?, etc.} depending on the format.
Load or retrieve a cached image, converting it into a GPU texture. If 'path' is already an object, its returned directly. Load or retrieve a cached image, converting it into a GPU texture. If 'path' is already an object, its returned directly.
@@ -106,7 +274,7 @@ graphics.texture.total_size = function() {
// Not yet implemented, presumably sum of (texture.width * texture.height * 4) for images in RAM // Not yet implemented, presumably sum of (texture.width * texture.height * 4) for images in RAM
return size return size
} }
graphics.texture.total_size[prosperon.DOC] = ` graphics.texture.total_size[cell.DOC] = `
:return: The total estimated memory size of all cached textures in RAM, in bytes. (Not yet implemented.) :return: The total estimated memory size of all cached textures in RAM, in bytes. (Not yet implemented.)
` `
@@ -115,23 +283,23 @@ graphics.texture.total_vram = function() {
// Not yet implemented, presumably sum of GPU memory usage // Not yet implemented, presumably sum of GPU memory usage
return vram return vram
} }
graphics.texture.total_vram[prosperon.DOC] = ` graphics.texture.total_vram[cell.DOC] = `
:return: The total estimated GPU memory usage of all cached textures, in bytes. (Not yet implemented.) :return: The total estimated GPU memory usage of all cached textures, in bytes. (Not yet implemented.)
` `
graphics.tex_hotreload = function tex_hotreload(file) { graphics.tex_hotreload = function tex_hotreload(file) {
console.log(`hot reloading ${file}`) log.console(`hot reloading ${file}`)
if (!(file in graphics.texture.cache)) return if (!(file in graphics.texture.cache)) return
console.log('really doing it') log.console('really doing it')
var img = create_image(file) var img = create_image(file)
var oldimg = graphics.texture.cache[file] var oldimg = graphics.texture.cache[file]
console.log(`new image:${json.encode(img)}`) log.console(`new image:${json.encode(img)}`)
console.log(`old image: ${json.encode(oldimg)}`) log.console(`old image: ${json.encode(oldimg)}`)
merge_objects(oldimg, img, ['surface', 'texture', 'loop', 'time']) merge_objects(oldimg, img, ['surface', 'texture', 'loop', 'time'])
} }
graphics.tex_hotreload[prosperon.DOC] = ` graphics.tex_hotreload[cell.DOC] = `
:param file: The file path that was changed on disk. :param file: The file path that was changed on disk.
:return: None :return: None
Reload the image for the given file, updating the cached copy in memory and GPU. Reload the image for the given file, updating the cached copy in memory and GPU.
@@ -170,11 +338,26 @@ graphics.get_font = function get_font(path, size) {
if (fontcache[fontstr]) return fontcache[fontstr] if (fontcache[fontstr]) return fontcache[fontstr]
var data = io.slurpbytes(fullpath) var data = io.slurpbytes(fullpath)
fontcache[fontstr] = graphics.make_font(data, size) var font = graphics.make_font(data,size)
fontcache[fontstr].texture = prosperon.gpu.load_texture(fontcache[fontstr].surface)
return fontcache[fontstr] // Load font texture via renderer actor (async)
send(renderer_actor, {
kind: "renderer",
op: "loadTexture",
data: font.surface
}, function(response) {
if (response.error) {
log.error("Failed to load font texture:", response.error);
} else {
font.texture = response;
}
});
fontcache[fontstr] = font
return font
} }
graphics.get_font[prosperon.DOC] = ` graphics.get_font[cell.DOC] = `
:param path: A string path to a font file, optionally with ".size" appended. :param path: A string path to a font file, optionally with ".size" appended.
:param size: Pixel size of the font, if not included in 'path'. :param size: Pixel size of the font, if not included in 'path'.
:return: A font object with .surface and .texture for rendering text. :return: A font object with .surface and .texture for rendering text.
@@ -182,8 +365,8 @@ Load a font from file if not cached, or retrieve from cache if already loaded.
` `
graphics.queue_sprite_mesh = function(queue) { graphics.queue_sprite_mesh = function(queue) {
var sprites = queue.filter(x => x.type === 'sprite') var sprites = queue.filter(x => x.type == 'sprite')
if (sprites.length === 0) return [] if (sprites.length == 0) return []
var mesh = graphics.make_sprite_mesh(sprites) var mesh = graphics.make_sprite_mesh(sprites)
for (var i = 0; i < sprites.length; i++) { for (var i = 0; i < sprites.length; i++) {
sprites[i].mesh = mesh sprites[i].mesh = mesh
@@ -192,31 +375,14 @@ graphics.queue_sprite_mesh = function(queue) {
} }
return [mesh.pos, mesh.uv, mesh.color, mesh.indices] return [mesh.pos, mesh.uv, mesh.color, mesh.indices]
} }
graphics.queue_sprite_mesh[prosperon.DOC] = ` graphics.queue_sprite_mesh[cell.DOC] = `
:param queue: An array of draw commands, some of which are {type:'sprite'} objects. :param queue: An array of draw commands, some of which are {type:'sprite'} objects.
:return: An array of references to GPU buffers [pos,uv,color,indices]. :return: An array of references to GPU buffers [pos,uv,color,indices].
Builds a single geometry mesh for all sprite-type commands in the queue, storing first_index/num_indices Builds a single geometry mesh for all sprite-type commands in the queue, storing first_index/num_indices
so they can be rendered in one draw call. so they can be rendered in one draw call.
` `
graphics.make_sprite_mesh[prosperon.DOC] = ` graphics.make_text_buffer[cell.DOC] = `
:param sprites: An array of sprite objects, each containing .rect (or transform), .src (UV region), .color, etc.
:param oldMesh (optional): An existing mesh object to reuse/resize if possible.
:return: A GPU mesh object with pos, uv, color, and indices buffers for all sprites.
Given an array of sprites, build a single geometry mesh for rendering them.
`
graphics.make_sprite_queue[prosperon.DOC] = `
:param sprites: An array of sprite objects.
:param camera: (unused in the C code example) Typically a camera or transform for sorting?
:param pipeline: A pipeline object for rendering.
:param sort: An integer or boolean for whether to sort sprites; if truthy, sorts by layer & texture.
:return: An array of pipeline commands: geometry with mesh references, grouped by image.
Given an array of sprites, optionally sort them, then build a queue of pipeline commands.
Each group with a shared image becomes one command.
`
graphics.make_text_buffer[prosperon.DOC] = `
:param text: The string to render. :param text: The string to render.
:param rect: A rectangle specifying position and possibly wrapping. :param rect: A rectangle specifying position and possibly wrapping.
:param angle: Rotation angle (unused or optional). :param angle: Rotation angle (unused or optional).
@@ -227,7 +393,7 @@ graphics.make_text_buffer[prosperon.DOC] = `
Generate a GPU buffer mesh of text quads for rendering with a font, etc. Generate a GPU buffer mesh of text quads for rendering with a font, etc.
` `
graphics.rectpack[prosperon.DOC] = ` graphics.rectpack[cell.DOC] = `
:param width: The width of the area to pack into. :param width: The width of the area to pack into.
:param height: The height of the area to pack into. :param height: The height of the area to pack into.
:param sizes: An array of [w,h] pairs for the rectangles to pack. :param sizes: An array of [w,h] pairs for the rectangles to pack.
@@ -235,67 +401,39 @@ graphics.rectpack[prosperon.DOC] = `
Perform a rectangle packing using the stbrp library. Return positions for each rect. Perform a rectangle packing using the stbrp library. Return positions for each rect.
` `
graphics.make_rtree[prosperon.DOC] = ` graphics.make_texture[cell.DOC] = `
:return: An R-Tree object for quickly querying many rectangles or sprite bounds.
Create a new R-Tree for geometry queries.
`
graphics.make_texture[prosperon.DOC] = `
:param data: Raw image bytes (PNG, JPG, etc.) as an ArrayBuffer. :param data: Raw image bytes (PNG, JPG, etc.) as an ArrayBuffer.
:return: An SDL_Surface object representing the decoded image in RAM, for use with GPU or software rendering. :return: An SDL_Surface object representing the decoded image in RAM, for use with GPU or software rendering.
Convert raw image bytes into an SDL_Surface object. Convert raw image bytes into an SDL_Surface object.
` `
graphics.make_gif[prosperon.DOC] = ` graphics.make_gif[cell.DOC] = `
:param data: An ArrayBuffer containing GIF data. :param data: An ArrayBuffer containing GIF data.
:return: An object with frames[], each frame having its own .surface. Some also have a .texture for GPU use. :return: An object with frames[], each frame having its own .surface. Some also have a .texture for GPU use.
Load a GIF, returning its frames. If it's a single-frame GIF, the result may have .surface only. Load a GIF, returning its frames. If it's a single-frame GIF, the result may have .surface only.
` `
graphics.make_aseprite[prosperon.DOC] = ` graphics.make_aseprite[cell.DOC] = `
:param data: An ArrayBuffer containing Aseprite (ASE) file data. :param data: An ArrayBuffer containing Aseprite (ASE) file data.
:return: An object containing frames or animations, each with .surface. May also have top-level .surface for a single-layer case. :return: An object containing frames or animations, each with .surface. May also have top-level .surface for a single-layer case.
Load an Aseprite/ASE file from an array of bytes, returning frames or animations. Load an Aseprite/ASE file from an array of bytes, returning frames or animations.
` `
graphics.cull_sprites[prosperon.DOC] = ` graphics.cull_sprites[cell.DOC] = `
:param sprites: An array of sprite objects (each has rect or transform). :param sprites: An array of sprite objects (each has rect or transform).
:param camera: A camera or bounding rectangle defining the view area. :param camera: A camera or bounding rectangle defining the view area.
:return: A new array of sprites that are visible in the camera's view. :return: A new array of sprites that are visible in the camera's view.
Filter an array of sprites to only those visible in the provided cameras view. Filter an array of sprites to only those visible in the provided cameras view.
` `
graphics.rects_to_sprites[prosperon.DOC] = ` graphics.make_font[cell.DOC] = `
:param rects: An array of rect coords or objects.
:param image: An image object (with .texture).
:return: An array of sprite objects referencing the 'image' and each rect for UV or position.
Convert an array of rect coords into sprite objects referencing a single image.
`
graphics.make_surface[prosperon.DOC] = `
:param dimensions: The size object {width, height}, or an array [w,h].
:return: A blank RGBA surface with the given dimensions, typically for software rendering or icons.
Create a blank surface in RAM.
`
graphics.make_cursor[prosperon.DOC] = `
:param opts: An object with {surface, hotx, hoty} or similar.
:return: An SDL_Cursor object referencing the given surface for a custom mouse cursor.
`
graphics.make_font[prosperon.DOC] = `
:param data: TTF/OTF file data as an ArrayBuffer. :param data: TTF/OTF file data as an ArrayBuffer.
:param size: Pixel size for rendering glyphs. :param size: Pixel size for rendering glyphs.
:return: A font object with surface, texture, and glyph data, for text rendering with make_text_buffer. :return: A font object with surface, texture, and glyph data, for text rendering with make_text_buffer.
Load a font from TTF/OTF data at the given size. Load a font from TTF/OTF data at the given size.
` `
graphics.make_sprite[prosperon.DOC] = ` graphics.make_line_prim[cell.DOC] = `
:return: A new sprite object, which typically has .rect, .color, .layer, .image, etc.
Create a new sprite object, storing default properties.
`
graphics.make_line_prim[prosperon.DOC] = `
:param points: An array of [x,y] points forming the line. :param points: An array of [x,y] points forming the line.
:param thickness: The thickness (width) of the polyline. :param thickness: The thickness (width) of the polyline.
:param startCap: (Unused) Possibly the type of cap for the start. :param startCap: (Unused) Possibly the type of cap for the start.

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 867 B

After

Width:  |  Height:  |  Size: 867 B

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Some files were not shown because too many files have changed in this diff Show More