Compare commits

...

251 Commits

Author SHA1 Message Date
75890b1a71 /apps/tools/twndel: thingies 2025-03-15 07:59:55 +03:00
73db3e57dc various small tweaks 2025-03-13 03:08:35 +03:00
2975aa2dfb comparing with whole number floats is okay. 2025-03-13 02:54:40 +03:00
6726faf719 twn_textures: lock cache when adding requests 2025-03-13 01:18:07 +03:00
183dfa6be5 twn_lines: combine differently colored lines into same batch 2025-03-12 12:00:53 +03:00
e974194af0 /docs/wiki: G1. Trigonometry starter 2025-03-12 01:36:42 +03:00
8607aa48ec /apps/tools/twndel: face hovering 2025-03-11 07:40:18 +03:00
f6600dfbda norm is needed after all. 2025-03-11 07:09:55 +03:00
bdabd04388 /apps/tools/twndel: reversable triangulation, fix to point finding with triangles 2025-03-11 07:02:52 +03:00
0e075ec334 /apps/tools/twndel: texture painting, face selection 2025-03-11 04:58:44 +03:00
b256fc903a /apps/tools/twndel: triangluation, triangle render 2025-03-11 02:41:03 +03:00
b52ecaeaa0 remove logging 2025-03-11 01:54:36 +03:00
37e46e9a7e /apps/tools/twndel: initial texture projection work 2025-03-11 01:53:45 +03:00
a472e6af52 /apps/demos/scenery: simpler normal_at(), clean one bit 2025-03-11 01:28:08 +03:00
66b2f04d9d fix missing texture sizing, fix bordered repeated texture upload, make quads default to repeating for now 2025-03-10 22:10:53 +03:00
90f4097070 /apps/templates/zig: depend on /src/*.zig sources for rebuilding, add .gitignore 2025-03-10 10:11:10 +03:00
829ff4780c zig template 2025-03-10 09:58:28 +03:00
8e15c9ec3c twn_util: dont add empty list 2025-03-10 07:05:45 +03:00
474ea84a77 /apps/tools/twndel: some data 2025-03-10 06:54:23 +03:00
7b8b9416ba twn_util: file_read() :images 2025-03-10 06:54:10 +03:00
8ed8158ae6 twn_util: file_read() with :exists 2025-03-10 05:34:29 +03:00
48e3a4c233 /share/twn_api.json: add new logging funcs 2025-03-10 05:29:56 +03:00
56530f9864 twn_util: final cleaning up, introducton of powerful file_read() 2025-03-10 05:19:58 +03:00
f86f3dd41a /apps/tools/twndel: use roundf for snapping 2025-03-09 08:12:29 +03:00
adae6be7e5 /apps/tools/twndel: camera translation 2025-03-09 07:38:32 +03:00
cd3033f9c4 /apps/tools/twndel: add reference lines 2025-03-09 06:53:54 +03:00
e11e63f273 /apps/tools/twndel: more plane awareness 2025-03-09 06:24:05 +03:00
75737b738f /apps/tools/twndel: plane intersection based point move 2025-03-09 00:30:20 +03:00
ce2c2513aa /apps/tool/twndel: grab.png 2025-03-08 21:57:53 +03:00
36c0af9953 hopefully more portable way of packaging binary embeds 2025-03-08 18:11:39 +03:00
826622cd58 twn_draw: proper ortho unproject 2025-03-08 04:38:56 +03:00
78b6a26de9 twndel: fix y axis point move 2025-03-08 04:10:14 +03:00
5f7b8bac6d undo, axis editing 2025-03-08 02:20:31 +03:00
6d6230c6a1 twn_audio: fix ogg vorbis sample reuse 2025-03-08 01:38:38 +03:00
c07e16490e model tool progress: initial selection 2025-03-08 00:50:47 +03:00
f5e55bb997 /apps/tools/twndel: point selection, sounds and other assets 2025-03-07 20:56:19 +03:00
1e6e323fe1 twn_draw: remove logging 2025-03-07 20:21:36 +03:00
dbf9599fe5 twn_api.json: add draw_camera_unproject() 2025-03-07 19:40:56 +03:00
923cd81571 twn_draw: draw_camera_unproject() 2025-03-07 19:31:46 +03:00
733a1786ab twn_draw: multiply zoom by 0.1 to match projections 2025-03-07 11:16:31 +03:00
a03e1d885d twn_camera: fix aspect of ortho projection 2025-03-07 11:07:05 +03:00
67feb5974a /apps/tools/twndel: start the thing 2025-03-07 07:10:44 +03:00
5be4ed4645 twn_lines: bail on empty batch 2025-03-07 07:05:20 +03:00
4a41f47a58 twn_textures: fix freeing of missed texture 2025-03-07 06:19:44 +03:00
35bb26705a twn_text: use full ascii range 2025-03-07 05:16:46 +03:00
13bc71a28d twn_text: embed default font 2025-03-07 03:35:35 +03:00
b97a155de4 /bin: make utilities quit on error 2025-03-07 02:56:12 +03:00
5df80addeb /apps/demos/scenery: no drifting :( 2025-03-07 00:51:07 +03:00
787977b747 /apps/demos/scenery: drift ! steer ! 2025-03-05 05:26:05 +03:00
f90b973d86 /apps/demos/scenery: more than almost 2025-03-04 09:45:49 +03:00
32675c012c /apps/demos/scenery: ...almost...🚬 2025-03-04 03:30:01 +03:00
a97515e948 twn_textures.c: fix atlas packing, allow out of order population 2025-03-03 00:57:19 +03:00
ed8e826b94 /apps/demos/scenery: something... 2025-03-02 23:19:27 +03:00
4e5ff9433c /apps/demos/scenery: box is almost coherent... 2025-03-02 03:37:02 +03:00
55829a1bef /apps/demos/scenery: fix world origin relation 2025-03-02 01:04:09 +03:00
119bd52c51 /apps/demos/scenery: some friction 2025-03-02 00:02:32 +03:00
5abd1ced1c /apps/demos/scenery: vehicle... 2025-03-01 16:42:14 +03:00
80db96672d stb_ds.h: fix implicit casts to int, resulting in bogus bitshift 2025-03-01 12:15:41 +03:00
2f6f7852be twn_api.json: add draw_line_3d() 2025-03-01 03:59:55 +03:00
307d5552f6 twn_lines.c: 3d case 2025-03-01 03:46:11 +03:00
5911cbd980 take care of warnings 2025-03-01 01:06:32 +03:00
e47b761a2c twn_lines.c: introduction with proper impl 2025-02-28 23:50:12 +03:00
844283c2fb /apps/examples/cirlce-raster: update 2025-02-28 17:35:58 +03:00
09eac707c3 draw: use GLint in circle element buffer 2025-02-28 17:27:01 +03:00
5e89710458 rename twn_engine_api.h to twn_api.h 2025-02-28 16:42:33 +03:00
4bc1feb826 /apps/demos/scenery: fix ramp bug, increase gravity 2025-02-26 23:19:22 +03:00
1c3973c6a2 /apps/demos/scenery: narrower cull, new assets used 2025-02-26 19:57:38 +03:00
da5bdb4fae /apps/demos/scenery: increase walking speed a bit 2025-02-26 17:22:02 +03:00
ed2afec5a7 /apps/demos/scenery: culling 2025-02-26 17:08:45 +03:00
6812c7c13d add trees to scenery, disable mipmapping by default, increase index buffer size again 2025-02-26 16:17:44 +03:00
8c0f43ec34 draw: draw_distance for 3d spaces, proper positioning of skybox according to it, scenery demo on circle rasters 2025-02-26 15:53:59 +03:00
23fbd45564 increase quad element buffer size 2025-02-26 13:29:28 +03:00
a36459397e draw: increase far Z, separate path for space quads, fix billboard batching 2025-02-26 13:27:09 +03:00
5f3920fdba /apps/demos/scenery: use quads 2025-02-26 11:28:59 +03:00
f57525cea6 /apps/demos/scenery: remove title scene 2025-02-26 11:26:38 +03:00
6b2901be28 /apps/demos/crawl: cleanup, document 2025-02-25 22:20:35 +03:00
9f0d15b9f6 /apps/twnlua: add ---@meta annotation 2025-02-23 22:07:32 +03:00
b46331e08d no c brain 2025-02-23 17:34:00 +03:00
d2938da8e2 /apps/demos/crawl: dont scale by 2 2025-02-23 17:28:45 +03:00
9134e51817 /apps/demos/crawl: add LICENSES 2025-02-23 17:19:33 +03:00
d66eda1894 add texture border to atlas residents, actually make use of mipmapping 2025-02-23 17:14:05 +03:00
a88392b9e9 /docs/wiki: T1.4 Developing 2025-02-22 21:21:42 +03:00
05f85062e8 fix command lists 2025-02-22 20:50:38 +03:00
d5aec5e6e1 /bin/twn: devcompl command to generate clangd completions in root 2025-02-22 20:29:28 +03:00
62866d33ae add notes explaining emscripten considerations 2025-02-22 16:19:22 +03:00
ce7240d423 dont generate source maps for web as it crashes lua build 2025-02-22 01:39:04 +03:00
7a38f7bcf3 /bin/twnbuild: fix cmake cache for web target 2025-02-22 01:32:55 +03:00
affaf7f557 cleanup templates 2025-02-22 01:14:20 +03:00
a223506a5f /bin/twn: support run command for --target=web 2025-02-22 00:39:24 +03:00
98d7d76a42 twn.png 2025-02-21 23:34:12 +03:00
814269ab0c textures working on web, separation of vertex and index buffers (actually matters) 2025-02-21 23:34:01 +03:00
d76ea06470 poll workers for non-threaded support, emscripten main loop, remove twn_model.c 2025-02-21 21:16:15 +03:00
dc6b298532 proper-er vector op flags 2025-02-21 19:37:22 +03:00
f25e27b102 changes to build system, emscipten progress (can render solid color, yippie!) 2025-02-21 19:06:19 +03:00
dd4fc45be3 attempt to build web version out of emscripten legacy gl wrapper 2025-02-21 18:07:04 +03:00
85e47dd677 api return restriction 2025-02-21 12:48:51 +03:00
a020b92824 /apps/demos/crawl: facing textures 2025-02-21 02:00:00 +03:00
b6347996f9 /apps/demos/crawl: new tile texture, solid walls 2025-02-21 01:36:46 +03:00
66c525a0d4 /apps/demos/crawl: visuals and stuff 2025-02-21 01:13:53 +03:00
70fab28158 /apps/twnlua: catch self-include stack busting 2025-02-21 00:18:22 +03:00
80a4ae3d0e progress on /apps/demos/crawl 2025-02-20 22:20:02 +03:00
bd3b090f6f add /apps/demos/crawl 2025-02-20 20:10:37 +03:00
6eb0730c52 mark identity parameter in log_ functions optional 2025-02-20 19:52:14 +03:00
a231d650f2 twn_util.c: add file_read() 2025-02-20 19:51:52 +03:00
e15975bfaa update to lua template 2025-02-20 17:25:11 +03:00
b67bc92857 remove optional by pointer texture_region parameters 2025-02-20 16:19:03 +03:00
991196f7c8 update to docs 2025-02-20 16:03:50 +03:00
0b89c90ad7 move /docs/interop.md to about-townengine.html 2025-02-20 15:42:05 +03:00
3d51c8c48f update about-townengine.html 2025-02-20 14:29:30 +03:00
21d8e2c5a5 actually set the interval 2025-02-20 14:06:18 +03:00
f805bf3f92 add interval from timer_elapse_seconds() result, add timers to twn_api.json 2025-02-20 14:04:04 +03:00
5228fa7e41 update platformer demo 2025-02-20 13:54:42 +03:00
f044a75ffe reorganization of twn_util.h, reletion of some seldom used procedures 2025-02-20 13:48:44 +03:00
723ccf1126 combine twn_texture_modes.h into twn_textures_c.h 2025-02-20 13:05:17 +03:00
6bd3afe9b2 move and combine option macro headers from public interface 2025-02-20 13:01:02 +03:00
d90bf4cbe2 twn_audio.c: fix freeing on unnamed channels 2025-02-19 21:17:13 +03:00
48f34f4623 twn_audio.c: fix unnamed channel audio 2025-02-19 21:13:21 +03:00
9c007f34df remove /docs/packaging.txt 2025-02-17 21:16:53 +03:00
a1f4599efd twn_text.c: dont segfault on font not found 2025-02-17 12:32:01 +03:00
4c1a8e087a /bin/twn: pass --debug switch to gdb command 2025-02-17 12:31:33 +03:00
a2b1f1820a /bin/twnbuild: more options 2025-02-17 12:30:17 +03:00
85ec8d3366 make unified build work on windows 2025-02-17 11:08:38 +03:00
7eebc7a2d7 minilua.h: revert changes, realized that lua_newstate is public 2025-02-17 10:57:31 +03:00
2b26fad983 twn_textures.c: fix error in deinit 2025-02-17 10:50:50 +03:00
47799deb8b /apps/twnlua: fix for windows, parametrize newstate for alloc func 2025-02-17 10:39:10 +03:00
1cd4bfa638 twn wiki on windows 2025-02-17 10:01:48 +03:00
9beef7686e bug suggest 2025-02-16 01:40:17 +03:00
cee344c7c1 /docs/wiki/packaging.html 2025-02-16 01:31:42 +03:00
18a76649b9 twn: fix symlinks but fr 2025-02-16 01:06:27 +03:00
88a4876d91 twn: fix symlinks 2025-02-16 00:46:15 +03:00
835edd737c Merge pull request 'twn: add twn to project on twn init' (#2) from yagi/townengine:twn-init-symlink-twn into main
Reviewed-on: http://tochie.space:3001/veclavtalica/townengine/pulls/2
2025-02-16 00:15:54 +03:00
9a486fa912 twn: add twn to project on twn init 2025-02-16 00:13:44 +03:00
d4ce6ab9ec add license file to tomlc99 dist 2025-02-16 00:11:18 +03:00
24b417c287 twn_loop.c: watch pack changes from pack dependencies 2025-02-15 23:37:57 +03:00
5a83381ae1 twn_draw: texture_region option for draw_billboard() 2025-02-15 22:42:14 +03:00
793bd850f6 /apps/twnlua: ctx.udata preservation in reload, no export in .so, ignore /data/scripts/twnapi.lua 2025-02-15 22:19:14 +03:00
29d163216c --no-sanity-timer option and its use in twn gdb 2025-02-15 15:09:01 +03:00
ffc3badc50 /apps/twnlua: output lua_loadbuffer errors 2025-02-14 21:52:06 +03:00
bedfe0cdfb fix twnlua :deadinside: 2025-02-14 21:07:59 +03:00
927f284fda Merge branch 'yagi-disable-luaserver-diag' 2025-02-14 20:45:29 +03:00
2616549f88 disable lua server diagnostics in twnapi.lua 2025-02-14 20:43:11 +03:00
cc4f7f7417 /apps/twnlua/docgen.py: remove Control legacy 2025-02-14 20:38:09 +03:00
f81c583319 push as is, half baked broken twn_models.c 2025-02-14 19:51:34 +03:00
0df0a9226f optimized twnlua building (10.5s -> 8.5s) 2025-02-10 18:29:35 +03:00
af1b9caedc twn_audio.c: use SDL2/SDL.h directly in hopes that its used precompiled 2025-02-10 18:23:05 +03:00
72d1941091 make libxm compiled as a single translation unit 2025-02-10 17:52:19 +03:00
8a58336d16 some flag play 🚬 2025-02-10 15:41:37 +03:00
1818532ec9 reuse draw_camera() in axial variant 2025-02-10 14:42:30 +03:00
e9f8dbebbf refactor toml reading a bit 2025-02-10 14:36:57 +03:00
c81f95e571 remove redundant for us opengl loading code from glad.c 2025-02-10 14:05:19 +03:00
5ba11dc584 remove texture upload profiling 2025-02-09 08:30:46 +03:00
f2aded9046 twn_dynamic_game_object.c: only reload on creation event 2025-02-09 08:20:39 +03:00
d6aaef3f68 assume constant dimensions for created textures, shaves a lot of time in uploading 2025-02-09 08:07:58 +03:00
322fbf6bbd twn_textures.c: don't zero fill intermediate surfaces 2025-02-09 07:51:21 +03:00
5a7d7433d1 wip multithreaded texture load 2025-02-09 07:35:27 +03:00
037548436d remove ; from styles in wiki 2025-02-09 07:32:53 +03:00
5e27845e55 ignore pre c11 compat 2025-02-09 07:32:33 +03:00
3bf8d7bedb /apps/twnlua: actually use 32 bit 2025-02-08 12:47:10 +03:00
85d7d54eed twn_loop.c: quit subsystems 2025-02-08 00:22:34 +03:00
8c248cb3fb twn_models.c: ignore deinit if it's not needed 2025-02-08 00:19:22 +03:00
145b040a0f /docs/wiki: add {twn} plaque 2025-02-07 19:29:27 +03:00
559ff9fedc proper state, handling of fans 2025-02-07 13:42:01 +03:00
990135105a fix missing texture over NULL 2025-02-07 12:47:40 +03:00
7040d6f218 wip model loading + workers 2025-02-07 10:19:36 +03:00
cb88b4bcc5 delay subsystems, detach opengl load thread, post background color on init 2025-02-05 03:31:07 +03:00
8110789b3a add --as-needed 2025-02-05 02:51:52 +03:00
0eadeb7e9d /bin/twn: propagate args to build instead of exe in run 2025-02-05 02:27:16 +03:00
3d10e1782a /apps/twnlua: fix dest folder for docgen 2025-02-05 01:25:30 +03:00
d9df3f9b04 twn_draw.c: draw_camera_2d()! 2025-02-05 00:54:38 +03:00
d9d7072c86 add control strings to wiki 2025-02-05 00:09:34 +03:00
3733b53cc5 twn_utils.c: fix profile command type 2025-02-04 09:07:31 +03:00
b6b436e1b7 /apps/twnlua: don't compile stb_ds.h 2025-02-04 09:05:29 +03:00
02b5ac4cc3 input system rework 2025-02-04 07:32:25 +03:00
4efe80bb5a /bin/twn: add init command to copy templates over 2025-02-04 06:01:41 +03:00
f3a2dc9063 stuffs to wiki 2025-02-04 01:13:38 +03:00
53c43a8f34 various SDL hints to try out ! 2025-02-04 01:04:34 +03:00
507bff6ed8 /docs/wiki: add clause of low latency 2025-02-04 00:31:15 +03:00
00636d65a9 fix resizing events, clean up the code 2025-02-04 00:24:31 +03:00
42253fc58a fix artefacts 2025-02-04 00:24:01 +03:00
c6cbf941a2 introduce audio_model = "push" to twn.toml 2025-02-04 00:21:30 +03:00
277d1b2e10 /bin/twn: add wiki command 2025-02-03 22:31:53 +03:00
46955a19c1 oopsie 2025-02-03 22:28:29 +03:00
9ab3e942cd /bin/twnbuild: shortening 2025-02-03 22:26:51 +03:00
c7bb317ead /docs/wiki: fix links, add 1.2 Wiki 2025-02-03 22:15:46 +03:00
241e72be1a /docs/wiki/style.css: add bottom padding to the page 2025-02-03 22:11:10 +03:00
f4fccc08c4 start of the /docs/wiki 2025-02-03 21:55:26 +03:00
2286cdefeb ignore docgen artifact in lua template 2025-02-02 23:11:11 +03:00
00ada15dbc remove unneeded cmake file from lua template 2025-02-02 23:10:47 +03:00
96b6b7e70b /bin/twnbuild: python based build solution 2025-02-02 23:08:02 +03:00
ccfdfd8a35 half pi. 2025-02-02 05:39:47 +03:00
7284bb726a make yaw = 0 result in (0, 0, 1) target vector 2025-02-02 05:32:18 +03:00
87b33c2e0c /apps/twnlua: replace dots to forward slashes in module lookup 2025-02-02 05:00:54 +03:00
732a3579b0 /apps/templates/lua: add .gitignore 2025-02-02 04:27:44 +03:00
2f629433aa catch null font, report it as unimplemented 2025-02-02 04:12:56 +03:00
6d58e964bc use ccalloc in input bindings 2025-02-02 03:15:05 +03:00
0014458dbb actuall no spam. i swear. 2025-02-02 03:13:58 +03:00
9112630330 don't report useless opengl messages 2025-02-02 02:17:37 +03:00
108810d68a /apps/twnlua: propagate errors in physfs_loader() 2025-02-02 02:10:05 +03:00
2c94efb796 /apps/twnlua: add error on attempt to import twnapi.lua 2025-02-02 01:59:27 +03:00
11ec35bc8a /apps/twnlua: add returns in docgen.py 2025-02-02 01:53:03 +03:00
2120f6876c camera reset and default state 2025-02-02 01:41:02 +03:00
dfd888a80a don't bypass x11 compositor 2025-02-02 00:50:26 +03:00
695784301d twn_filewatch.c: typo 2025-02-01 14:25:54 +03:00
98d19495a2 another ccaloc moment 2025-02-01 14:00:18 +03:00
0929aa9be4 move shell html to /src/ 2025-02-01 13:57:51 +03:00
898c11bbdf /share/twn_api.json: add draw_line 2025-02-01 13:47:35 +03:00
aeabb17f86 create /apps/temapltes/ structure, clean up cmake files to use directory name for binary name directly 2025-02-01 13:47:17 +03:00
24e8dc052d fixes 2025-01-31 05:11:10 +03:00
3f264ca0ad remove busted free 2025-01-31 02:52:33 +03:00
a7557fceb4 Merge remote-tracking branch 'origin/main' 2025-01-31 02:49:33 +03:00
bd89c4b938 fix mixing up of SDL and libc allocators, proper flushing of quad build buffers 2025-01-30 21:57:20 +03:00
7074e7499a ugh? 2025-01-30 05:10:38 +03:00
2e29cfcfe2 dmon license 2025-01-30 04:33:24 +03:00
74d7190c62 ilimination of system code, removal of x-watcher and replacement of it by dmon, fixes in audio code, dynamic asset reload 2025-01-30 04:30:20 +03:00
4b2a22bf3c twn_filewatch.c: file and directory change api, initial support for texture reload 2025-01-29 07:21:09 +03:00
630c6fb5d4 /apps/twnlua: automatically generate twnapi.lua file inside ~/data/scripts/ 2025-01-29 03:43:10 +03:00
d7c744a6ca /apps/twnlua: mark with CODEGEN 2025-01-29 03:36:15 +03:00
f8d7aa8a07 /apps/twnlua: propagate root project name for executable name 2025-01-29 03:22:04 +03:00
ea2bbf5de0 /apps/twnlua: support dynlib game=off 2025-01-29 03:14:08 +03:00
637343db5b /apps/twnlua: use minilua single source amalgam 2025-01-29 01:24:58 +03:00
fedf1b5ef3 remove twn_elf 2025-01-29 01:24:10 +03:00
732b61e207 twn_textures.c: remove hacky .rodata texture lookup method, it turns out to be not anyhow faster than the simplest solution. also fix path=NULL case 2025-01-29 00:56:05 +03:00
6cb166522e /share/twn_api.h: add profiling api 2025-01-29 00:55:12 +03:00
916e433753 /apps/twnlua: no warnings 2025-01-28 23:58:13 +03:00
458b44d0b0 /docs/interop.md: limit parameter count to 8, specify enums 2025-01-28 23:51:18 +03:00
8de4a1f09b /apps/twnlua: optimize default boolean and convertrer pops 2025-01-28 23:48:49 +03:00
ac93d114c9 add log over primitives to twn_api.h 2025-01-28 07:14:55 +03:00
37b0f4e3a6 /apps/twnlua: add docgen.py 2025-01-28 06:09:12 +03:00
fad11041bc fix uvs in billboards 2025-01-27 05:08:45 +03:00
bbd654a569 /apps/demos/scenery: update to new api 2025-01-27 05:08:35 +03:00
7c33107585 game.background_color option in twn.toml 2025-01-27 05:05:43 +03:00
f625dde8d1 some TODOs to twn_draw.h 2025-01-27 04:16:00 +03:00
166ae43981 make face culling optional 2025-01-27 04:15:46 +03:00
6ef3cf1a3a change near z for orthographic proj 2025-01-27 04:09:59 +03:00
d84e5f8610 normalize camera direction 2025-01-27 03:46:29 +03:00
6a87119c70 add zoom option to camera to work around the orthographic camera 2025-01-27 03:25:14 +03:00
791ab628ca orthographic projection for fov=0, rework of order and defaults for 3d camera api 2025-01-27 02:42:36 +03:00
20394eed6c disable elf texture lookup hack 2025-01-26 23:26:48 +03:00
a54657e7be twn_loop.c: limit minimum amount of delay 2025-01-26 18:34:34 +03:00
3bdee30e7b /docs/interop.md: add a line about parameter keywords 2025-01-26 18:08:49 +03:00
0e4abaae3c twn_api.c: change repeat parameter to loops, as to prevent name collisions 2025-01-26 17:53:01 +03:00
cf8227d7d3 twn_loop.c: place SDL_Delay() to not waste CPU time 2025-01-26 12:06:00 +03:00
f365cff590 twnlua: prepend /scripts/ in package loader 2025-01-26 11:35:34 +03:00
77ff5c7f25 add TODOs on streaming lua load 2025-01-26 11:10:05 +03:00
bf3eb50b55 twn_audio.c: only apply volume scaling on mixing of different streams 2025-01-26 11:08:13 +03:00
6a029b7e79 fix audio overrun 2025-01-26 09:39:31 +03:00
fad46137a0 set ctx.frame_duration 2025-01-26 09:09:21 +03:00
7a403c766e /apps/twnlua: report error on loading 2025-01-26 08:51:54 +03:00
b0e718876a show time limit in sanity timer 2025-01-26 02:56:00 +03:00
785b507330 twn_input.c: ignore input param 2025-01-26 02:45:50 +03:00
d884a9026f clean twn_timer 2025-01-26 02:45:01 +03:00
272 changed files with 40968 additions and 36604 deletions

4
.gitignore vendored
View File

@ -2,13 +2,17 @@
* *
!*.* !*.*
!*/ !*/
!bin/*
!Makefile !Makefile
!LICENSE
!COPYING
**/*.exe **/*.exe
**/*.html **/*.html
**/*.js **/*.js
**/*.wasm **/*.wasm
**/*.wasm.map **/*.wasm.map
**/*.data
**/*.so **/*.so
**/*.dll **/*.dll
**/*.7z **/*.7z

View File

@ -5,6 +5,8 @@ project(townengine LANGUAGES C)
set(CMAKE_MESSAGE_LOG_LEVEL "WARNING") set(CMAKE_MESSAGE_LOG_LEVEL "WARNING")
set(CMAKE_INSTALL_MESSAGE NEVER) set(CMAKE_INSTALL_MESSAGE NEVER)
# TODO: test whether webgl 1 is good enough.
# SDL dependencies # SDL dependencies
# for whatever reason Emscripten has SDL2 config, but not actual SDL2 port by default # for whatever reason Emscripten has SDL2 config, but not actual SDL2 port by default
if(NOT EMSCRIPTEN) if(NOT EMSCRIPTEN)
@ -26,19 +28,19 @@ set(TWN_TARGET townengine CACHE INTERNAL "")
set(TWN_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "") set(TWN_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "")
# feature configuration, set them with -DFEATURE=ON/OFF in cli # feature configuration, set them with -DFEATURE=ON/OFF in cli
option(TWN_FEATURE_DYNLIB_GAME "Enable dynamic library loading support" ON)
option(TWN_FEATURE_PUSH_AUDIO "Enable frame based audio push for easy realtime audio" ON)
option(TWN_USE_AMALGAM "Enable use of twn_amalgam.c as a single compilation unit" ON) option(TWN_USE_AMALGAM "Enable use of twn_amalgam.c as a single compilation unit" ON)
option(TWN_FEATURE_DYNLIB_GAME "Enable dynamic library loading support" ON)
set(TWN_OUT_DIR ${CMAKE_SOURCE_DIR} CACHE PATH "Artifact destination")
# todo: figure out how to compile for dynamic linking instead # todo: figure out how to compile for dynamic linking instead?
if(EMSCRIPTEN) if(HAIKU OR EMSCRIPTEN)
if(TWN_FEATURE_DYNLIB_GAME) if(TWN_FEATURE_DYNLIB_GAME)
message(WARNING "TWN_FEATURE_DYNLIB_GAME is set, but not supported - it is turned off") message(WARNING "TWN_FEATURE_DYNLIB_GAME is set, but not supported - it is turned off")
set(TWN_FEATURE_DYNLIB_GAME OFF CACHE INTERNAL "") set(TWN_FEATURE_DYNLIB_GAME OFF CACHE INTERNAL "")
endif() endif()
endif() endif()
if(HAIKU) if(HAIKU OR EMSCRIPTEN)
if(TWN_SANITIZE) if(TWN_SANITIZE)
message(WARNING "TWN_SANITIZE is set, but not supported - it is turned off") message(WARNING "TWN_SANITIZE is set, but not supported - it is turned off")
set(TWN_SANITIZE OFF CACHE INTERNAL "") set(TWN_SANITIZE OFF CACHE INTERNAL "")
@ -64,24 +66,7 @@ set(PHYSFS_ARCHIVE_7Z OFF CACHE INTERNAL "")
add_subdirectory(third-party/physfs ${CMAKE_CURRENT_BINARY_DIR}/third-party/physfs SYSTEM) add_subdirectory(third-party/physfs ${CMAKE_CURRENT_BINARY_DIR}/third-party/physfs SYSTEM)
add_subdirectory(third-party/libxm ${CMAKE_CURRENT_BINARY_DIR}/third-party/libxm SYSTEM) add_subdirectory(third-party/libxm ${CMAKE_CURRENT_BINARY_DIR}/third-party/libxm SYSTEM)
if(LINUX)
set(SYSTEM_SOURCE_FILES
src/system/linux/twn_elf.c
src/system/linux/twn_timer.c
$<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:src/game_object/twn_linux_game_object.c>)
elseif(WIN32)
set(SYSTEM_SOURCE_FILES
$<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:src/game_object/twn_win32_game_object.c>)
else()
set(SYSTEM_SOURCE_FILES)
endif()
if(EMSCRIPTEN)
set(TWN_RENDERING_API WEBGL1)
else()
set(TWN_RENDERING_API OPENGL_15) set(TWN_RENDERING_API OPENGL_15)
endif()
if(TWN_RENDERING_API MATCHES OPENGL_15) if(TWN_RENDERING_API MATCHES OPENGL_15)
set(SYSTEM_SOURCE_FILES ${SYSTEM_SOURCE_FILES} set(SYSTEM_SOURCE_FILES ${SYSTEM_SOURCE_FILES}
@ -97,14 +82,19 @@ set(TWN_THIRD_PARTY_SOURCE_FILES
set(TWN_NONOPT_SOURCE_FILES set(TWN_NONOPT_SOURCE_FILES
src/twn_stb.c src/twn_stb.c
src/twn_loop.c
src/twn_main.c
src/twn_context.c include/twn_context.h src/twn_context.c include/twn_context.h
src/twn_audio.c include/twn_audio.h src/twn_audio.c include/twn_audio.h
src/twn_util.c include/twn_util.h src/twn_util.c include/twn_util.h
src/twn_input.c include/twn_input.h src/twn_input.c include/twn_input.h
src/twn_loop.c src/twn_loop_c.h
src/twn_camera.c src/twn_camera_c.h src/twn_camera.c src/twn_camera_c.h
src/twn_textures.c src/twn_textures_c.h src/twn_textures.c src/twn_textures_c.h
src/twn_filewatch.c src/twn_filewatch_c.h
src/twn_filewatch.c src/twn_filewatch_c.h
src/twn_timer.c src/twn_timer_c.h
src/twn_workers.c src/twn_workers_c.h
src/rendering/twn_draw.c src/rendering/twn_draw_c.h src/rendering/twn_draw.c src/rendering/twn_draw_c.h
src/rendering/twn_quads.c src/rendering/twn_quads.c
@ -114,12 +104,16 @@ set(TWN_NONOPT_SOURCE_FILES
src/rendering/twn_triangles.c src/rendering/twn_triangles.c
src/rendering/twn_billboards.c src/rendering/twn_billboards.c
src/rendering/twn_circles.c src/rendering/twn_circles.c
src/rendering/twn_skybox.c) src/rendering/twn_skybox.c
src/rendering/twn_models.c
src/rendering/twn_lines.c
)
set(TWN_SOURCE_FILES set(TWN_SOURCE_FILES
$<IF:$<BOOL:${TWN_USE_AMALGAM}>,src/twn_amalgam.c src/twn_stb.c,${TWN_NONOPT_SOURCE_FILES}> $<IF:$<BOOL:${TWN_USE_AMALGAM}>,src/twn_amalgam.c src/twn_stb.c,${TWN_NONOPT_SOURCE_FILES}>
# for dynamic load based solution main is compiled in a separate target # for dynamic load based solution main is compiled in a separate target
$<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:src/game_object/twn_dynamic_game_object.c>
$<$<NOT:$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>>:src/twn_main.c $<$<NOT:$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>>:src/twn_main.c
src/game_object/twn_static_game_object.c> src/game_object/twn_static_game_object.c>
@ -142,12 +136,10 @@ set_target_properties(${TWN_TARGET} PROPERTIES
C_STANDARD_REQUIRED ON C_STANDARD_REQUIRED ON
C_EXTENSIONS ON) # extensions are required by stb_ds.h C_EXTENSIONS ON) # extensions are required by stb_ds.h
target_compile_definitions(${TWN_TARGET} PRIVATE $<$<BOOL:${TWN_FEATURE_PUSH_AUDIO}>:TWN_FEATURE_PUSH_AUDIO>)
# precompile commonly used not-so-small headers # precompile commonly used not-so-small headers
target_precompile_headers(${TWN_TARGET} PRIVATE target_precompile_headers(${TWN_TARGET} PRIVATE
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:third-party/glad/include/glad/glad.h> $<$<NOT:$<BOOL:${EMSCRIPTEN}>>:third-party/glad/include/glad/glad.h>
${SDL2_INCLUDE_DIR}/SDL.h $<$<NOT:$<BOOL:${EMSCRIPTEN}>>:${SDL2_INCLUDE_DIR}/SDL.h>
third-party/physfs/src/physfs.h) third-party/physfs/src/physfs.h)
@ -159,14 +151,14 @@ function(give_options_without_warnings target)
-fno-signed-zeros -fno-signed-zeros
-fno-trapping-math -fno-trapping-math
-freciprocal-math -freciprocal-math
# TODO: require packaging for web case
$<$<BOOL:${EMSCRIPTEN}>:-sUSE_SDL=2>) $<$<BOOL:${EMSCRIPTEN}>:-sUSE_SDL=2>)
set(BUILD_FLAGS_RELEASE set(BUILD_FLAGS_RELEASE
-O3 -O3
-flto=$<IF:$<STREQUAL:${CMAKE_C_COMPILER_ID},Clang>,thin,auto> -flto=$<IF:$<STREQUAL:${CMAKE_C_COMPILER_ID},Clang>,thin,auto>
-mavx -mavx2 $<$<STREQUAL:${CMAKE_SYSTEM_PROCESSOR},AMD64>:-sse2 -mavx -mavx2>
-fdata-sections $<$<BOOL:${EMSCRIPTEN}>:-msimd128 -mrelaxed-simd>
-ffunction-sections
-funroll-loops -funroll-loops
-fomit-frame-pointer -fomit-frame-pointer
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Gnu>:-s>) $<$<STREQUAL:${CMAKE_C_COMPILER_ID},Gnu>:-s>)
@ -176,8 +168,20 @@ function(give_options_without_warnings target)
-g3 -g3
-gdwarf -gdwarf
-fno-omit-frame-pointer -fno-omit-frame-pointer
$<$<BOOL:${TWN_SANITIZE}>:-fstack-protector-all -fsanitize=undefined -fsanitize=address> $<$<BOOL:${TWN_SANITIZE}>:-fstack-protector-all -fsanitize=undefined -fsanitize=address>)
$<$<BOOL:${EMSCRIPTEN}>:-gsource-map>)
set(LINK_FLAGS
-Bsymbolic-functions
$<$<BOOL:${EMSCRIPTEN}>:-sLEGACY_GL_EMULATION -sGL_FFP_ONLY -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2
-sENVIRONMENT=web -sDEFAULT_TO_CXX=0>
$<$<BOOL:${EMSCRIPTEN}>:--preload-file ${TWN_OUT_DIR}/data@data -sALLOW_MEMORY_GROWTH>
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:-Wl,--as-needed>
$<$<BOOL:${LINUX}>:-Wl,--hash-style=gnu>)
set(LINK_FLAGS_RELEASE
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Clang>:-Wl,--strip-all>
${BUILD_FLAGS_RELEASE})
if (CMAKE_C_COMPILER_LINKER_ID MATCHES GNU OR CMAKE_C_COMPILER_LINKER_ID MATCHES GNUgold) if (CMAKE_C_COMPILER_LINKER_ID MATCHES GNU OR CMAKE_C_COMPILER_LINKER_ID MATCHES GNUgold)
set(THINLTO_USAGE "-plugin-opt,") set(THINLTO_USAGE "-plugin-opt,")
@ -199,11 +203,9 @@ function(give_options_without_warnings target)
target_link_options(${target} PUBLIC target_link_options(${target} PUBLIC
${BUILD_FLAGS} ${BUILD_FLAGS}
# -Wl,--no-undefined # TODO: use later for implementing no-libc ${LINK_FLAGS}
$<$<CONFIG:Release>:${BUILD_FLAGS_RELEASE}> $<$<CONFIG:Release>:${LINK_FLAGS_RELEASE}>
$<$<CONFIG:Debug>:${BUILD_FLAGS_DEBUG}> $<$<CONFIG:Debug>:${BUILD_FLAGS_DEBUG}>)
-Bsymbolic-functions
$<$<BOOL:${LINUX}>:-Wl,--hash-style=gnu>)
get_target_property(target_type ${target} TYPE) get_target_property(target_type ${target} TYPE)
if (target_type MATCHES SHARED_LIBRARY) if (target_type MATCHES SHARED_LIBRARY)
@ -212,11 +214,14 @@ function(give_options_without_warnings target)
target_link_options(${target} PUBLIC target_link_options(${target} PUBLIC
$<$<CONFIG:Release>:${BUILD_SHARED_LIBRARY_FLAGS_RELEASE}>) $<$<CONFIG:Release>:${BUILD_SHARED_LIBRARY_FLAGS_RELEASE}>)
elseif(CMAKE_BUILD_TYPE MATCHES Release)
target_compile_options(${target} PUBLIC
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Clang>:-mllvm=--enable-gvn-hoist>)
endif() endif()
target_compile_definitions(${target} PRIVATE target_compile_definitions(${target} PRIVATE
$<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:TWN_FEATURE_DYNLIB_GAME> $<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:TWN_FEATURE_DYNLIB_GAME>
$<$<BOOL:${LINUX}>:_GNU_SOURCE>) _GNU_SOURCE)
endfunction() endfunction()
@ -229,7 +234,8 @@ function(give_options target)
-Wno-declaration-after-statement -Wno-declaration-after-statement
-Wno-unsafe-buffer-usage -Wno-unsafe-buffer-usage
-Wno-unused-command-line-argument -Wno-unused-command-line-argument
-Wno-covered-switch-default) -Wno-covered-switch-default
-Wno-disabled-macro-expansion)
set(WARNING_FLAGS set(WARNING_FLAGS
-Wall -Wall
@ -241,6 +247,7 @@ function(give_options target)
-Werror=vla -Werror=vla
-Wno-missing-field-initializers -Wno-missing-field-initializers
-Wunused-result -Wunused-result
-Wno-pre-c11-compat
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Gnu>:-Wcast-align=strict>) $<$<STREQUAL:${CMAKE_C_COMPILER_ID},Gnu>:-Wcast-align=strict>)
target_compile_options(${target} PRIVATE target_compile_options(${target} PRIVATE
@ -257,8 +264,9 @@ function(include_deps target)
third-party/physfs/extras third-party/physfs/extras
third-party/libxm/include third-party/libxm/include
third-party/stb third-party/stb
third-party/x-watcher third-party/dmon
third-party/tomlc99 third-party/tomlc99
third-party/fast_obj
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:third-party/glad/include>) $<$<NOT:$<BOOL:${EMSCRIPTEN}>>:third-party/glad/include>)
list(TRANSFORM THIRD_PARTY_INCLUDES PREPEND ${TWN_ROOT_DIR}/) list(TRANSFORM THIRD_PARTY_INCLUDES PREPEND ${TWN_ROOT_DIR}/)
@ -272,19 +280,40 @@ endfunction()
function(link_deps target) function(link_deps target)
target_link_libraries(${target} PUBLIC target_link_libraries(${target} PUBLIC
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:SDL2::SDL2> $<$<NOT:$<BOOL:${EMSCRIPTEN}>>:SDL2::SDL2>
$<$<NOT:$<OR:$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>,$<BOOL:${EMSCRIPTEN}>>>:SDL2::SDL2main>
physfs-static physfs-static
xms) xms)
target_include_directories(${target} PUBLIC ${SDL2_INCLUDE_DIRS}) target_include_directories(${target} PUBLIC ${SDL2_INCLUDE_DIRS})
endfunction() endfunction()
function(use_townengine target sources output_directory) function(put_townengine output_directory)
cmake_path(GET TWN_OUT_DIR STEM LAST_ONLY target)
add_executable(${target} ${TWN_ROOT_DIR}/src/twn_main.c)
target_link_options(${target} PRIVATE $<$<BOOL:${LINUX}>:-Wl,-rpath,$ORIGIN/>)
set_target_properties(${TWN_TARGET} PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${output_directory})
set_target_properties(${target} PROPERTIES
OUTPUT_NAME ${target}
LIBRARY_OUTPUT_DIRECTORY ${output_directory}
RUNTIME_OUTPUT_DIRECTORY ${output_directory})
give_options(${target})
include_deps(${target})
link_deps(${target})
target_link_libraries(${target} PUBLIC ${TWN_TARGET})
endfunction()
function(use_townengine sources output_directory)
cmake_path(GET TWN_OUT_DIR STEM LAST_ONLY target)
if(TWN_FEATURE_DYNLIB_GAME) if(TWN_FEATURE_DYNLIB_GAME)
# game shared library, for reloading # game shared library, for reloading
add_library(${target}_game SHARED ${sources}) add_library(${target}_game SHARED ${sources})
give_options(${target}_game) give_options(${target}_game)
include_deps(${target}_game) include_deps(${target}_game)
target_link_libraries(${target}_game PUBLIC $<$<NOT:$<BOOL:${EMSCRIPTEN}>>:SDL2::SDL2> ${TWN_TARGET}) target_link_libraries(${target}_game PUBLIC ${TWN_TARGET})
set_target_properties(${target}_game PROPERTIES set_target_properties(${target}_game PROPERTIES
OUTPUT_NAME game OUTPUT_NAME game
LIBRARY_OUTPUT_DIRECTORY ${output_directory} LIBRARY_OUTPUT_DIRECTORY ${output_directory}
@ -305,6 +334,7 @@ function(use_townengine target sources output_directory)
endif() endif()
set_target_properties(${target} PROPERTIES set_target_properties(${target} PROPERTIES
OUTPUT_NAME ${target}
LIBRARY_OUTPUT_DIRECTORY ${output_directory} LIBRARY_OUTPUT_DIRECTORY ${output_directory}
RUNTIME_OUTPUT_DIRECTORY ${output_directory}) RUNTIME_OUTPUT_DIRECTORY ${output_directory})
@ -313,7 +343,7 @@ function(use_townengine target sources output_directory)
endif() endif()
target_compile_options(${target} PRIVATE target_compile_options(${target} PRIVATE
$<$<BOOL:${EMSCRIPTEN}>:--shell-file ${TWN_ROOT_DIR}/shell_minimal.html>) $<$<BOOL:${EMSCRIPTEN}>:--shell-file ${TWN_ROOT_DIR}/src/shell_minimal.html>)
# system libraries # system libraries
find_library(MATH_LIBRARY m) find_library(MATH_LIBRARY m)
@ -342,5 +372,18 @@ link_deps(twn_third_parties)
give_options(${TWN_TARGET}) give_options(${TWN_TARGET})
include_deps(${TWN_TARGET}) include_deps(${TWN_TARGET})
link_deps(${TWN_TARGET}) link_deps(${TWN_TARGET})
target_link_libraries(${TWN_TARGET} PUBLIC twn_third_parties)
target_include_directories(${TWN_TARGET} PRIVATE ${TWN_ROOT_DIR}/src) target_include_directories(${TWN_TARGET} PRIVATE ${TWN_ROOT_DIR}/src)
target_link_libraries(${TWN_TARGET} PUBLIC
twn_third_parties
${CMAKE_CURRENT_BINARY_DIR}/font.o)
# embed resources
# TODO: think of a portable way to compress/decompress them
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/font.o
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${CMAKE_COMMAND} -E env CMAKE_CURRENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} sh bin/prep-embed.sh
DEPENDS share/assets/Dernyns256.ttf)
add_custom_target(asset-compilation ALL DEPENDS
${CMAKE_CURRENT_BINARY_DIR}/font.o)

View File

@ -6,11 +6,11 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug) set(CMAKE_BUILD_TYPE Debug)
endif() endif()
add_subdirectory($ENV{TWNROOT} $ENV{TWNROOT}/build) add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
set(SOURCE_FILES set(SOURCE_FILES
game.c game.c
state.h state.h
) )
use_townengine(${PROJECT_NAME} "${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR}) use_townengine("${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -1,3 +1,3 @@
[[deps]] [[deps]]
source = "../../../common-data" source = "../../../data"
name = "common-data" name = "common-data"

View File

@ -66,11 +66,11 @@ void game_tick(void)
{ // First tick, initalizing data { // First tick, initalizing data
// Allocating State struct to store data there // Allocating State struct to store data there
if (!ctx.udata) if (!ctx.udata)
ctx.udata = ccalloc(1, sizeof(State)); ctx.udata = calloc(1, sizeof(State));
} }
input_action("add_a_bit", CONTROL_LEFT_MOUSE); input_action("add_a_bit", "LCLICK");
input_action("add_a_lot", CONTROL_RIGHT_MOUSE); input_action("add_a_lot", "RCLICK");
State *state = ctx.udata; State *state = ctx.udata;

View File

@ -0,0 +1 @@
castledoors.png - https://opengameart.org/content/castle-door - CC-BY 3.0

BIN
apps/demos/crawl/data/assets/brick.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
apps/demos/crawl/data/assets/castledoors.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
apps/demos/crawl/data/assets/lever.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
apps/demos/crawl/data/assets/mossy_rock.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
apps/demos/crawl/data/assets/pebbles.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,37 @@
@map
-- Grid of symbols defined by @classes section.
#####
#.@.#
#...#
##.######
#...X...#
#./.#####
#####
@classes
-- Defines classes under symbols, which could have properties attached.
# stone_wall
wall_texture : /assets/brick.png
solid : 1
. stone_floor
tile_texture : /assets/mossy_rock.png
@ player_spawn
unique : 1
face : south
hold : torch
tile_texture : /assets/mossy_rock.png
X door
open_on_signal : sg_torch0
tile_texture : /assets/mossy_rock.png
face : horizon
face_texture : /assets/castledoors.png
/ lever
on_interact_emit : sg_torch0
tile_texture : /assets/mossy_rock.png
face : observer
face_texture : /assets/lever.png
@meta
-- Arbitrary sections could be defined with value pairs.
description : Test Level! Just two square rooms and a tunnel opened by lever.

View File

@ -0,0 +1,71 @@
require("level")
require("render")
function lerp(a, b, x)
return a + ((b - a) * x)
end
function qlerp(a, b, x)
return lerp(a, b, x * x)
end
function game_tick()
if ctx.udata == nil then
ctx.udata = {
level = load_level("levels/00.lvl")
}
ctx.udata.player = {
position = ctx.udata.level.classes.player_spawn.position,
position_lerp = ctx.udata.level.classes.player_spawn.position,
direction = { x = 1, y = 0, z = 0 },
direction_lerp = { x = 1, y = 0, z = 0 },
}
end
input_action { control = "A", name = "turn_left" }
input_action { control = "D", name = "turn_right" }
input_action { control = "W", name = "walk_forward" }
input_action { control = "S", name = "walk_backward" }
if input_action_just_released { name = "turn_left" } then
ctx.udata.player.direction = { x = ctx.udata.player.direction.z,
y = ctx.udata.player.direction.y,
z =-ctx.udata.player.direction.x }
end
if input_action_just_released { name = "turn_right" } then
ctx.udata.player.direction = { x =-ctx.udata.player.direction.z,
y = ctx.udata.player.direction.y,
z = ctx.udata.player.direction.x }
end
local move = { x = 0, y = 0 }
if input_action_just_released { name = "walk_forward" } then
move = { x = move.x + ctx.udata.player.direction.x, y = move.y + ctx.udata.player.direction.z }
end
if input_action_just_released { name = "walk_backward" } then
move = { x = move.x - ctx.udata.player.direction.x, y = move.y - ctx.udata.player.direction.z }
end
if ctx.udata.level.grid[ctx.udata.player.position.y + move.y][ctx.udata.player.position.x + move.x].solid ~= nil then
move = { x = 0, y = 0 }
end
ctx.udata.player.position = { x = ctx.udata.player.position.x + move.x, y = ctx.udata.player.position.y + move.y }
ctx.udata.player.position_lerp.x = qlerp(ctx.udata.player.position_lerp.x, ctx.udata.player.position.x, ctx.frame_duration * 30)
ctx.udata.player.position_lerp.y = qlerp(ctx.udata.player.position_lerp.y, ctx.udata.player.position.y, ctx.frame_duration * 30)
ctx.udata.player.direction_lerp.x = qlerp(ctx.udata.player.direction_lerp.x, ctx.udata.player.direction.x, ctx.frame_duration * 40)
ctx.udata.player.direction_lerp.z = qlerp(ctx.udata.player.direction_lerp.z, ctx.udata.player.direction.z, ctx.frame_duration * 40)
draw_camera {
position = {
x = ctx.udata.player.position_lerp.x + 0.5 - ctx.udata.player.direction.x / 2,
y = 0.5,
z = ctx.udata.player.position_lerp.y + 0.5 - ctx.udata.player.direction.z / 2,
},
direction = ctx.udata.player.direction_lerp,
}
render_dungeon(ctx.udata.level)
end

View File

@ -0,0 +1,91 @@
function load_level(file)
local f = file_read { file = file }
local result = {
-- templates to fill the grid with
classes = {
-- predefined empty tile
void = { },
},
-- symbol to class lookup table
glossary = {
[" "] = "void",
},
-- grid consists of expanded classes, of size dimensions
grid = {},
-- map consists of original rows of symbols that the grid is constructed from
map = {},
-- maximum extends of the map, unspecified tiles are filled with "void"
size = { x = 0, y = 0 },
}
-- iterate over lines
local section, subsection = "none", "none"
local from = 1
local start, limit = string.find(f, "\n", from)
while start do
local line = string.sub(f, from, start - 1)
-- skip over
if #line == 0 or line:find("^%-%-%s*") then
goto skip
-- start new section
elseif line:find("^@%g+") then
section = line:sub(2); subsection = "none"
-- decode map one line at a time
elseif section == "map" then
if result.size.x < #line then
result.size.x = #line
end
result.map[#result.map + 1] = line
-- templates to expand
elseif section == "classes" then
-- properties
if line:find("^ %g+") then
local _, _, property, value = line:find("^ (%g+)%s?:%s?(.*)")
result.classes[subsection][property] = value
goto skip
end
local symbol, classname = line:sub(1,1), line:sub(3)
result.classes[classname] = {
symbol = symbol,
}
result.glossary[symbol] = classname
subsection = classname
elseif section ~= "none" then
local _, _, property, value = line:find("^(%g+)%s?:%s?(.*)")
if result[section] == nil then
result[section] = {}
end
result[section][property] = value
end
::skip::
from = limit + 1
start, limit = string.find(f, "\n", from)
end
-- post process, expand map to grid
for y = 1, #result.map do
result.grid[y] = {}
for x = 1, result.size.x do
-- past defined for line
local symbol
if x > #result.map[y] then
symbol = " "
else
symbol = result.map[y]:sub(x,x)
end
local class = result.classes[result.glossary[symbol]]
if class["unique"] ~= nil then
class.position = { x = x, y = y }
end
result.grid[y][x] = class
::continue::
end
end
result.size.y = #result.map
print(result.meta.description)
return result
end

View File

@ -0,0 +1,88 @@
-- if this is too wasteful, one could check nerby tiles to see whether faces could be visible
-- more robust solution would be to travel the level from observer point of view
function render_dungeon(dungeon)
for y = 1, dungeon.size.y do
for x = 1, dungeon.size.x do
if dungeon.grid[y][x].wall_texture ~= nil then
draw_quad {
texture = dungeon.grid[y][x].wall_texture,
v3 = { x = x, y = 1, z = y },
v2 = { x = x, y = 0, z = y },
v1 = { x = x + 1, y = 0, z = y },
v0 = { x = x + 1, y = 1, z = y },
texture_region = { w = 128, h = 128 },
}
draw_quad {
texture = dungeon.grid[y][x].wall_texture,
v3 = { x = x + 1, y = 1, z = y },
v2 = { x = x + 1, y = 0, z = y },
v1 = { x = x + 1, y = 0, z = y + 1 },
v0 = { x = x + 1, y = 1, z = y + 1 },
texture_region = { w = 128, h = 128 },
}
draw_quad {
texture = dungeon.grid[y][x].wall_texture,
v3 = { x = x + 1, y = 1, z = y + 1 },
v2 = { x = x + 1, y = 0, z = y + 1 },
v1 = { x = x, y = 0, z = y + 1 },
v0 = { x = x, y = 1, z = y + 1 },
texture_region = { w = 128, h = 128 },
}
draw_quad {
texture = dungeon.grid[y][x].wall_texture,
v3 = { x = x, y = 1, z = y + 1 },
v2 = { x = x, y = 0, z = y + 1 },
v1 = { x = x, y = 0, z = y },
v0 = { x = x, y = 1, z = y },
texture_region = { w = 128, h = 128 },
}
elseif dungeon.grid[y][x].tile_texture ~= nil then
draw_quad {
texture = dungeon.grid[y][x].tile_texture,
v0 = { x = x + 1, y = 0, z = y },
v1 = { x = x, y = 0, z = y },
v2 = { x = x, y = 0, z = y + 1 },
v3 = { x = x + 1, y = 0, z = y + 1},
texture_region = { w = 128, h = 128 },
}
draw_quad {
texture = dungeon.grid[y][x].tile_texture,
v3 = { x = x + 1, y = 1, z = y },
v2 = { x = x, y = 1, z = y },
v1 = { x = x, y = 1, z = y + 1 },
v0 = { x = x + 1, y = 1, z = y + 1},
texture_region = { w = 128, h = 128 },
}
end
if dungeon.grid[y][x].face_texture ~= nil then
if dungeon.grid[y][x].face == "horizon" then
draw_quad {
texture = dungeon.grid[y][x].face_texture,
v3 = { x = x + 1, y = 1, z = y },
v2 = { x = x + 1, y = 0, z = y },
v1 = { x = x + 1, y = 0, z = y + 1 },
v0 = { x = x + 1, y = 1, z = y + 1 },
texture_region = { w = 64, h = 64 },
}
draw_quad {
texture = dungeon.grid[y][x].face_texture,
v3 = { x = x, y = 1, z = y + 1 },
v2 = { x = x, y = 0, z = y + 1 },
v1 = { x = x, y = 0, z = y },
v0 = { x = x, y = 1, z = y },
texture_region = { w = 64, h = 64 },
}
elseif dungeon.grid[y][x].face == "observer" then
draw_billboard {
texture = dungeon.grid[y][x].face_texture,
position = { x = x + 0.5, y = 0.5, z = y + 0.5 },
size = { x = 0.5, y = 0.5 },
}
end
end
end
end
end

View File

@ -14,6 +14,7 @@ dev_id = "you"
# Game runtime details # Game runtime details
[game] [game]
resolution = [ 640, 480 ] resolution = [ 640, 480 ]
interpreter = "$TWNROOT/apps/twnlua"
#debug = true #debug = true
# Engine tweaks. You probably don't need to change these # Engine tweaks. You probably don't need to change these

View File

@ -6,7 +6,7 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug) set(CMAKE_BUILD_TYPE Debug)
endif() endif()
add_subdirectory($ENV{TWNROOT} $ENV{TWNROOT}/build) add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
set(SOURCE_FILES set(SOURCE_FILES
game.c game.c
@ -20,4 +20,4 @@ set(SOURCE_FILES
scenes/ingame.c scenes/ingame.h scenes/ingame.c scenes/ingame.h
) )
use_townengine(${PROJECT_NAME} "${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR}) use_townengine("${SOURCE_FILES}" ${TWN_OUT_DIR})

View File

@ -1,3 +1,3 @@
[[deps]] [[deps]]
source = "../../../common-data" # where does it come from, might be an url source = "../../../data" # where does it come from, might be an url
name = "common-data" # should be globally unique name = "common-data" # should be globally unique

View File

@ -12,7 +12,7 @@
void game_tick(void) { void game_tick(void) {
if (ctx.initialization_needed) { if (ctx.initialization_needed) {
if (!ctx.udata) { if (!ctx.udata) {
ctx.udata = ccalloc(1, sizeof (State)); ctx.udata = calloc(1, sizeof (State));
State *state = ctx.udata; State *state = ctx.udata;
state->ctx = &ctx; state->ctx = &ctx;
@ -22,17 +22,13 @@ void game_tick(void) {
State *state = ctx.udata; State *state = ctx.udata;
input_action("debug_toggle", CONTROL_BACKSPACE); input_action("debug_toggle", "BACKSPACE");
input_action("debug_dump_atlases", CONTROL_HOME); input_action("debug_dump_atlases", "HOME");
if (input_action_just_pressed("debug_toggle")) { if (input_action_just_pressed("debug_toggle")) {
ctx.debug = !ctx.debug; ctx.debug = !ctx.debug;
} }
if (input_action_just_pressed("debug_dump_atlases")) {
textures_dump_atlases();
}
state->scene->tick(state); state->scene->tick(state);
/* there's a scene switch pending, we can do it now that the tick is done */ /* there's a scene switch pending, we can do it now that the tick is done */

View File

@ -11,9 +11,9 @@
static void update_timers(Player *player) { static void update_timers(Player *player) {
player->jump_air_timer = timer_tick_frames(player->jump_air_timer); player->jump_air_timer = player->jump_air_timer - 1 <= 0 ? 0 : player->jump_air_timer - 1;
player->jump_coyote_timer = timer_tick_frames(player->jump_coyote_timer); player->jump_coyote_timer = player->jump_coyote_timer - 1 <= 0 ? 0 : player->jump_coyote_timer - 1;
player->jump_buffer_timer = timer_tick_frames(player->jump_buffer_timer); player->jump_buffer_timer = player->jump_buffer_timer - 1 <= 0 ? 0 : player->jump_buffer_timer - 1;
} }
@ -203,7 +203,7 @@ static void calc_collisions_y(Player *player) {
Player *player_create(World *world) { Player *player_create(World *world) {
Player *player = cmalloc(sizeof *player); Player *player = malloc(sizeof *player);
*player = (Player) { *player = (Player) {
.world = world, .world = world,

View File

@ -11,12 +11,17 @@
static void ingame_tick(State *state) { static void ingame_tick(State *state) {
SceneIngame *scn = (SceneIngame *)state->scene; SceneIngame *scn = (SceneIngame *)state->scene;
input_action("player_left", CONTROL_A); input_action("player_left", "A");
input_action("player_right", CONTROL_D); input_action("player_right", "D");
input_action("player_forward", CONTROL_W); input_action("player_forward", "W");
input_action("player_backward", CONTROL_S); input_action("player_backward", "S");
input_action("player_jump", CONTROL_SPACE); input_action("player_jump", "SPACE");
input_action("player_run", CONTROL_LSHIFT); input_action("player_run", "LSHIFT");
draw_camera_2d((Vec2){ scn->player->rect.x + scn->player->rect.w / 2 - ctx.resolution.x / 2,
scn->player->rect.y + scn->player->rect.h / 2 - ctx.resolution.y / 2 },
0, 1
);
world_drawdef(scn->world); world_drawdef(scn->world);
player_calc(scn->player); player_calc(scn->player);
@ -34,7 +39,7 @@ static void ingame_end(State *state) {
Scene *ingame_scene(State *state) { Scene *ingame_scene(State *state) {
(void)state; (void)state;
SceneIngame *new_scene = ccalloc(1, sizeof *new_scene); SceneIngame *new_scene = calloc(1, sizeof *new_scene);
new_scene->base.tick = ingame_tick; new_scene->base.tick = ingame_tick;
new_scene->base.end = ingame_end; new_scene->base.end = ingame_end;

View File

@ -5,16 +5,15 @@
#include "twn_game_api.h" #include "twn_game_api.h"
#include <SDL2/SDL.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
static void title_tick(State *state) { static void title_tick(State *state) {
SceneTitle *scn = (SceneTitle *)state->scene; SceneTitle *scn = (SceneTitle *)state->scene;
(void)scn; (void)scn;
input_action("ui_accept", CONTROL_RETURN); input_action("ui_accept", "ENTER");
if (input_action_just_pressed("ui_accept")) { if (input_action_just_pressed("ui_accept")) {
switch_to(state, ingame_scene); switch_to(state, ingame_scene);
@ -26,7 +25,7 @@ static void title_tick(State *state) {
/* draw the tick count as an example of dynamic text */ /* draw the tick count as an example of dynamic text */
size_t text_str_len = snprintf(NULL, 0, "%llu", (unsigned long long)state->ctx->frame_number) + 1; size_t text_str_len = snprintf(NULL, 0, "%llu", (unsigned long long)state->ctx->frame_number) + 1;
char *text_str = cmalloc(text_str_len); char *text_str = malloc(text_str_len);
snprintf(text_str, text_str_len, "%llu", (unsigned long long)state->ctx->frame_number); snprintf(text_str, text_str_len, "%llu", (unsigned long long)state->ctx->frame_number);
const char *font = "fonts/kenney-pixel.ttf"; const char *font = "fonts/kenney-pixel.ttf";
@ -64,7 +63,7 @@ static void title_end(State *state) {
Scene *title_scene(State *state) { Scene *title_scene(State *state) {
(void)state; (void)state;
SceneTitle *new_scene = ccalloc(1, sizeof *new_scene); SceneTitle *new_scene = calloc(1, sizeof *new_scene);
new_scene->base.tick = title_tick; new_scene->base.tick = title_tick;
new_scene->base.end = title_end; new_scene->base.end = title_end;

View File

@ -45,7 +45,7 @@ static void drawdef_debug(struct World *world) {
struct World *world_create(void) { struct World *world_create(void) {
struct World *world = cmalloc(sizeof *world); struct World *world = malloc(sizeof *world);
*world = (struct World) { *world = (struct World) {
.tiles = NULL, .tiles = NULL,
@ -58,9 +58,9 @@ struct World *world_create(void) {
/* create the tilemap */ /* create the tilemap */
/* it simply stores what's in each tile as a 2d array */ /* it simply stores what's in each tile as a 2d array */
/* on its own, it's entirely unrelated to drawing or logic */ /* on its own, it's entirely unrelated to drawing or logic */
world->tilemap = cmalloc(sizeof *world->tilemap * world->tilemap_height); world->tilemap = malloc(sizeof *world->tilemap * world->tilemap_height);
for (size_t i = 0; i < world->tilemap_height; ++i) { for (size_t i = 0; i < world->tilemap_height; ++i) {
world->tilemap[i] = cmalloc(sizeof **world->tilemap * world->tilemap_width); world->tilemap[i] = malloc(sizeof **world->tilemap * world->tilemap_width);
for (size_t j = 0; j < world->tilemap_width; ++j) { for (size_t j = 0; j < world->tilemap_width; ++j) {
world->tilemap[i][j] = TILE_TYPE_VOID; world->tilemap[i][j] = TILE_TYPE_VOID;
@ -81,7 +81,7 @@ struct World *world_create(void) {
/* the tiles array contains data meant to be used by other logic */ /* the tiles array contains data meant to be used by other logic */
/* most importantly, it is used to draw the tiles */ /* most importantly, it is used to draw the tiles */
const size_t tile_count = world->tilemap_height * world->tilemap_width; const size_t tile_count = world->tilemap_height * world->tilemap_width;
world->tiles = cmalloc(sizeof *world->tiles * tile_count); world->tiles = malloc(sizeof *world->tiles * tile_count);
update_tiles(world); update_tiles(world);
return world; return world;

View File

@ -6,15 +6,14 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug) set(CMAKE_BUILD_TYPE Debug)
endif() endif()
add_subdirectory($ENV{TWNROOT} $ENV{TWNROOT}/build) add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
set(SOURCE_FILES set(SOURCE_FILES
game.c game.c
state.h state.h
scenes/scene.c scenes/scene.h scenes/scene.c scenes/scene.h
scenes/title.c scenes/title.h
scenes/ingame.c scenes/ingame.h scenes/ingame.c scenes/ingame.h
) )
use_townengine(${PROJECT_NAME} "${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR}) use_townengine("${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -1,3 +1,3 @@
[[deps]] [[deps]]
source = "../../../common-data" # where does it come from, might be an url source = "../../../data" # where does it come from, might be an url
name = "common-data" # should be globally unique name = "common-data" # should be globally unique

View File

@ -1,6 +1,5 @@
#include "state.h" #include "state.h"
#include "scenes/scene.h" #include "scenes/scene.h"
#include "scenes/title.h"
#include "scenes/ingame.h" #include "scenes/ingame.h"
#include "twn_game_api.h" #include "twn_game_api.h"
@ -14,7 +13,7 @@
void game_tick(void) { void game_tick(void) {
if (ctx.initialization_needed) { if (ctx.initialization_needed) {
if (!ctx.udata) { if (!ctx.udata) {
ctx.udata = ccalloc(1, sizeof (State)); ctx.udata = calloc(1, sizeof (State));
State *state = ctx.udata; State *state = ctx.udata;
state->ctx = &ctx; state->ctx = &ctx;
@ -24,17 +23,13 @@ void game_tick(void) {
State *state = ctx.udata; State *state = ctx.udata;
input_action("debug_toggle", CONTROL_BACKSPACE); input_action("debug_toggle", "BACKSPACE");
input_action("debug_dump_atlases", CONTROL_HOME); input_action("debug_dump_atlases", "HOME");
if (input_action_just_pressed("debug_toggle")) { if (input_action_just_pressed("debug_toggle")) {
ctx.debug = !ctx.debug; ctx.debug = !ctx.debug;
} }
if (input_action_just_pressed("debug_dump_atlases")) {
textures_dump_atlases();
}
state->scene->tick(state); state->scene->tick(state);
/* there's a scene switch pending, we can do it now that the tick is done */ /* there's a scene switch pending, we can do it now that the tick is done */

View File

@ -1,5 +1,4 @@
#include "ingame.h" #include "ingame.h"
#include "title.h"
#include "scene.h" #include "scene.h"
#include "twn_game_api.h" #include "twn_game_api.h"
@ -13,21 +12,298 @@
#define TERRAIN_FREQUENCY 0.15f #define TERRAIN_FREQUENCY 0.15f
#define TERRAIN_DISTANCE 64 #define TERRAIN_RADIUS 128
#define GRASS_RADIUS 16
#define TERRAIN_DISTANCE (TERRAIN_RADIUS * 2)
#define HALF_TERRAIN_DISTANCE ((float)TERRAIN_DISTANCE / 2) #define HALF_TERRAIN_DISTANCE ((float)TERRAIN_DISTANCE / 2)
#define PLAYER_HEIGHT 0.6f #define PLAYER_HEIGHT 0.6f
#define TREE_DENSITY 0.03f
#define G_CONST 10.0f
/* TODO: pregenerate grid of levels of detail */
static float heightmap[TERRAIN_DISTANCE][TERRAIN_DISTANCE]; static float heightmap[TERRAIN_DISTANCE][TERRAIN_DISTANCE];
/* vehicle sim ! */
/* https://www.youtube.com/watch?v=pwbwFdWBkU0 */
/* representation is a "jelly" box with spring configuration trying to make it coherent */
/* == springs == */
/* damped spring: F = -kx - cv */
/* x = length(p1-p0) - at_rest_length */
/* v = dot(v1 - v0, unit(p1-p0)) */
/* F = (-kx - cv) * unit(p1-p0) */
/* v += (F/m)*t */
/* one points gains positive F, other negative F, to come together */
/* == ground interaction == */
/* if point is under terrain, then apply this: */
/* x = y difference on point and ground */
/* -x(n) = x * normal */
/* -v(n) = dot(v, normal) */
/* -F = (-kx(n)-cv(n)) * normal */
/* == friction == */
/* v(o)/F(o) are perpendicular to slope (x - x(n)) */
/* if v(o) == 0, then */
/* -- at rest, static friction overcomes */
/* if F(o) <= f(s)*x(n), F(o) = 0 */
/* else, F(o) -= f(k) * x(n) */
/* else if length(v(o) + (F(o)/m)*t) <= (f(k)*x(n)*t), v(o) = 0 */
/* else, F = -unit(v(o)*f(k)*x(n)) */
#define VEHICLE_MASS 200.0f
#define VEHICLE_LENGTH 3.0f
#define VEHICLE_WIDTH 1.7f
#define VEHICLE_HEIGHT 1.3f
/* spring constant */
#define VEHICLE_SPRING_K 22000.0f
#define VEHICLE_SPRING_K_SHOCK 18000.0f
#define VEHICLE_SPRING_GK 70000.0f
/* damping constant */
#define VEHICLE_SPRING_C 800.0f
#define VEHICLE_SPRING_C_SHOCK 500.0f
#define VEHICLE_SPRING_GC 100.0f
#define VEHICLE_FRICTION_S 1000.0f
#define VEHICLE_FRICTION_K 110.0f
#define VEHICLE_FRICTION_V 4000.0f
/* TODO: shock springs, that are more loose, which are used to simulate the wheels */
/* initial, ideal corner positions */
static const Vec3 vbpi[8] = {
[0] = { 0, 0, 0 },
[1] = { VEHICLE_LENGTH, 0, 0 },
[2] = { VEHICLE_LENGTH, 0, VEHICLE_WIDTH },
[3] = { 0, 0, VEHICLE_WIDTH },
[4] = { 0, VEHICLE_HEIGHT, 0 },
[5] = { VEHICLE_LENGTH, VEHICLE_HEIGHT, 0 },
[6] = { VEHICLE_LENGTH, VEHICLE_HEIGHT, VEHICLE_WIDTH },
[7] = { 0, VEHICLE_HEIGHT, VEHICLE_WIDTH },
};
/* corner positions in simulation */
static Vec3 vbp[8] = { vbpi[0], vbpi[1], vbpi[2], vbpi[3],
vbpi[4], vbpi[5], vbpi[6], vbpi[7], };
/* corner velocities */
static Vec3 vbv[8];
/* springs */
static uint8_t vbs[28][2] = {
{0, 1}, {4, 5}, {0, 4},
{1, 2}, {5, 6}, {1, 5},
{2, 3}, {6, 7}, {2, 6},
{3, 0}, {7, 4}, {3, 7},
{0, 2}, {0, 5}, {0, 7},
{1, 3}, {1, 6}, {1, 4},
{4, 6}, {2, 7}, {2, 5},
{5, 7}, {3, 4}, {3, 6},
{0, 6}, {1, 7}, {2, 4}, {3, 5},
};
/* ackermann steering geometry */
static float vehicle_turning_extend;
static float vehicle_turning_speed = 0.12f;
static float vehicle_turning_extend_limit = VEHICLE_WIDTH * 1.75f;
static float height_at(SceneIngame *scn, Vec2 position);
static Vec3 normal_at(SceneIngame *scn, Vec2 position);
static inline float clampf(float f, float min, float max) {
const float t = f < min ? min : f;
return t > max ? max : t;
}
static void draw_vehicle(SceneIngame *scn) {
for (size_t i = 0; i < 12; ++i)
draw_line_3d(vbp[vbs[i][0]], vbp[vbs[i][1]], 1, (Color){255, 255, 255, 255});
for (size_t i = 12; i < 24; ++i)
draw_line_3d(vbp[vbs[i][0]], vbp[vbs[i][1]], 1, (Color){200, 200, 200, 255});
for (size_t i = 24; i < 28; ++i)
draw_line_3d(vbp[vbs[i][0]], vbp[vbs[i][1]], 1, (Color){255, 125, 125, 255});
}
static void process_vehicle(SceneIngame *scn) {
/* apply gravity */
Vec3 Facc[8] = {0};
/* steering */
bool steered = false;
if (input_action_pressed("player_left")) {
vehicle_turning_extend -= vehicle_turning_speed;
steered = true;
}
if (input_action_pressed("player_right")) {
vehicle_turning_extend += vehicle_turning_speed;
steered = true;
}
if (!steered)
vehicle_turning_extend -= copysignf(vehicle_turning_speed * 0.9f, vehicle_turning_extend);
vehicle_turning_extend = clampf(vehicle_turning_extend, -vehicle_turning_extend_limit, vehicle_turning_extend_limit);
if (fabsf(vehicle_turning_extend) <= 0.11f)
vehicle_turning_extend = 0;
Vec3 const Fg = { .y = -VEHICLE_MASS * G_CONST };
for (size_t i = 0; i < 8; ++i)
Facc[i] = vec3_add(Facc[i], Fg);
/* apply springs */
for (size_t i = 0; i < 28; ++i) {
Vec3 const p0 = vbp[vbs[i][0]];
Vec3 const p1 = vbp[vbs[i][1]];
Vec3 const v0 = vbv[vbs[i][0]];
Vec3 const v1 = vbv[vbs[i][1]];
Vec3 const pd = vec3_sub(p1, p0);
Vec3 const pn = vec3_norm(pd);
/* TODO: length at rest could be precalculated */
float const lar = vec3_length(vec3_sub(vbpi[vbs[i][1]], vbpi[vbs[i][0]]));
float const x = vec3_length(pd) - lar;
float const v = vec3_dot(vec3_sub(v1, v0), pn);
float const spring_k = i == 2 | i == 5 || i == 8 || i == 11 ? VEHICLE_SPRING_K_SHOCK : VEHICLE_SPRING_K;
float const spring_c = i == 2 | i == 5 || i == 8 || i == 11 ? VEHICLE_SPRING_C_SHOCK : VEHICLE_SPRING_C;
Vec3 const Fs = vec3_scale(pn, -spring_k * x - spring_c * v);
Facc[vbs[i][0]] = vec3_sub(Facc[vbs[i][0]], Fs);
Facc[vbs[i][1]] = vec3_add(Facc[vbs[i][1]], Fs);
}
/* spring and friction against the ground */
for (size_t i = 0; i < 8; ++i) {
Vec3 const p = vbp[i];
Vec3 const v = vbv[i];
float const h = height_at(scn, (Vec2){ p.x, p.z });
Vec3 const fwd = vec3_norm(vec3_sub(vbp[1], vbp[0]));
if (h >= p.y) {
/* back wheel processing: acceleration */
if (i == 0 || i == 3) {
float scale = 0;
if (scn->camera_mode == 2 && input_action_pressed("player_forward"))
scale += 1;
if (scn->camera_mode == 2 && input_action_pressed("player_backward"))
scale -= 1;
Facc[i] = vec3_add(Facc[i], vec3_scale(fwd, 6500 * scale));
}
/* normal force, for displacement */
Vec3 const n = normal_at(scn, (Vec2){ p.x, p.z });
float const xn = (h - p.y) * n.y;
float const vn = vec3_dot(v, n);
Vec3 const Fn = vec3_scale(n, -VEHICLE_SPRING_GK * xn - VEHICLE_SPRING_GC * vn);
Facc[i] = vec3_sub(Facc[i], Fn);
/* friction force, perpendicular to normal force */
/* TODO: is it right? aren't vn+vol should be = |v| */
Vec3 const von = vec3_norm(vec3_cross(n, vec3_cross(v, n)));
Vec3 const vo = vec3_scale(vec3_scale(von, vec3_length(v) - vn), -1);
float const vol = vec3_length(vo);
Vec3 const Fon = vec3_norm(vec3_cross(n, vec3_cross(Facc[i], n)));
Vec3 Fo = vec3_scale(vec3_scale(Fon, vec3_length(Facc[i]) - vec3_dot(Facc[i], n)), -1);
/* portion of total force along the surface */
float const Fol = vec3_length(Fo);
float const fkxn = VEHICLE_FRICTION_K * xn;
/* at rest, might want to start moving */
if (fabsf(0.0f - vol) <= 0.0001f) {
/* cannot overcome static friction, force along the surface is zeroed */
if (Fol <= VEHICLE_FRICTION_S * xn) { Fo = vec3_scale(Fo, -1);}
/* resist the force by friction, while starting to move */
else { Fo = vec3_sub(Fo, vec3_scale(von, fkxn));}
/* not at rest, stop accelerating along the surface */
} else if (vol + (Fol / VEHICLE_MASS) * ctx.frame_duration <= fkxn * ctx.frame_duration * 2) {
/* ugh ... */
vbv[i] = vec3_add(v, vo);
/* just apply friction */
} else {
Fo = vec3_scale(von, -fkxn * 400);
}
Facc[i] = vec3_add(Facc[i], Fo);
/* rear wheel friction */
if (i == 0 || i == 3) {
Vec3 const pn = vec3_cross(fwd, n);
Vec3 const Fp = vec3_scale(pn, vec3_dot(v, pn) * -VEHICLE_FRICTION_V);
Facc[i] = vec3_add(Facc[i], Fp);
}
/* front wheel processing */
if (i == 1 || i == 2) {
/* steering influences "center of turning", which is a point */
/* laying on line defined by rear axle */
/* front arms are rotated to be perpendicular to center of turning, */
/* which then are used to dissipate forces, thus providing control */
Vec3 const rear_bar = vec3_sub(vbp[0], vbp[3]);
Vec3 const rear_center = vec3_scale(vec3_add(vbp[0], vbp[3]), 0.5);
Vec3 a, b, r;
if (i == 1) {
a = vec3_sub(vbp[3], vbp[2]);
b = vec3_sub(rear_center, vbp[2]);
r = vbp[2];
} else {
a = vec3_sub(vbp[0], vbp[1]);
b = vec3_sub(rear_center, vbp[1]);
r = vbp[1];
}
float const arm_angle = vec3_angle(a, b);
Vec3 const turn_center = vec3_add(rear_center, vec3_scale(vec3_norm(rear_bar), vehicle_turning_extend));
Vec3 const arm = vec3_sub(r, turn_center);
Vec3 const n = vec3_norm(vec3_cross(a, b));
Vec3 const wheel = vec3_norm(vec3_rotate(arm, -arm_angle, n));
Vec3 const p = vec3_norm(vec3_cross(wheel, n));
draw_line_3d(r, vec3_add(r, p), 1, (Color){0,255,255,255});
Vec3 const Fp = vec3_scale(p, vec3_dot(v, p) * -VEHICLE_FRICTION_V);
Facc[i] = vec3_add(Facc[i], Fp);
}
}
Vec3 vd = vec3_scale(vec3_scale(Facc[i], (1.0f / VEHICLE_MASS)), ctx.frame_duration);
vbv[i] = vec3_add(vbv[i], vd);
vbp[i] = vec3_add(vbp[i], vec3_scale(vbv[i], ctx.frame_duration));
}
}
static void process_vehicle_mode(State *state) {
SceneIngame *scn = (SceneIngame *)state->scene;
Vec3 const top_center = vec3_sub(vbp[4], vec3_scale(vec3_sub(vbp[4], vbp[6]), 1.0f / 2.0f));
// Vec3 const front_center = vec3_add(vbp[4], vec3_scale(vec3_sub(vbp[4], vbp[7]), 1.0f / 2.0f));
// Vec3 const facing_direction = vec3_sub(top_center, front_center);
float yawc, yaws, pitchc, pitchs;
sincosf(scn->yaw + (float)M_PI_2, &yaws, &yawc);
sincosf(scn->pitch, &pitchs, &pitchc);
Vec3 const looking_direction = vec3_norm(((Vec3){
yawc * pitchc,
pitchs,
yaws * pitchc,
}));
Vec3 const orbit = vec3_sub(top_center, vec3_scale(looking_direction, 7.5));
draw_camera(orbit, looking_direction, (Vec3){0,1,0}, (float)M_PI_2 * 0.8f, 1, TERRAIN_RADIUS * sqrtf(3));
scn->looking_direction = looking_direction;
scn->pos = top_center;
}
static void process_fly_mode(State *state) { static void process_fly_mode(State *state) {
SceneIngame *scn = (SceneIngame *)state->scene; SceneIngame *scn = (SceneIngame *)state->scene;
DrawCameraFromPrincipalAxesResult dir_and_up = DrawCameraFromPrincipalAxesResult dir_and_up =
draw_camera_from_principal_axes(scn->pos, (float)M_PI_2 * 0.8f, scn->roll, scn->pitch, scn->yaw); draw_camera_from_principal_axes(scn->pos, scn->roll, scn->pitch, scn->yaw, (float)M_PI_2 * 0.8f, 1, TERRAIN_RADIUS * sqrtf(3));
scn->looking_direction = dir_and_up.direction;
const Vec3 right = m_vec_norm(m_vec_cross(dir_and_up.direction, dir_and_up.up)); const Vec3 right = m_vec_norm(m_vec_cross(dir_and_up.direction, dir_and_up.up));
const float speed = 0.04f; /* TODO: put this in a better place */ const float speed = 0.1f; /* TODO: put this in a better place */
if (input_action_pressed("player_left")) if (input_action_pressed("player_left"))
scn->pos = vec3_sub(scn->pos, m_vec_scale(right, speed)); scn->pos = vec3_sub(scn->pos, m_vec_scale(right, speed));
@ -47,33 +323,39 @@ static void process_fly_mode(State *state) {
scn->pos.y -= speed; scn->pos.y -= speed;
} }
/* TODO: could be baked in map format */
static Vec3 normal_at(SceneIngame *scn, Vec2 position) {
int const x = (int)(floorf(position.x - scn->world_center.x));
int const y = (int)(floorf(position.y - scn->world_center.y));
float const height0 = heightmap[x][y];
float const height1 = heightmap[x + 1][y];
float const height2 = heightmap[x][y + 1];
Vec3 const a = { .x = 1, .y = height0 - height1, .z = 0 };
Vec3 const b = { .x = 0, .y = height0 - height2, .z = -1 };
return vec3_norm(vec3_cross(a, b));
}
/* TODO: don't operate on triangles, instead interpolate on quads */
static float height_at(SceneIngame *scn, Vec2 position) { static float height_at(SceneIngame *scn, Vec2 position) {
float height0, height1, height2, weight0, weight1, weight2; int const x = (int)(floorf(position.x - scn->world_center.x));
int const y = (int)(floorf(position.y - scn->world_center.y));
int const x = (int)(HALF_TERRAIN_DISTANCE + (position.x - scn->pos.x)); float const height0 = heightmap[x][y];
int const y = (int)(HALF_TERRAIN_DISTANCE + (position.y - scn->pos.z)); float const height1 = heightmap[x + 1][y];
float const height2 = heightmap[x][y + 1];
height0 = heightmap[x][y]; float const height3 = heightmap[x + 1][y + 1];
height1 = heightmap[x + 1][y + 1];
Vec2 incell = { position.x - floorf(position.x), position.y - floorf(position.y) }; Vec2 incell = { position.x - floorf(position.x), position.y - floorf(position.y) };
/* who needs barycentric coordinates, am i right? */ float const weight0 = (1 - incell.x) * (1 - incell.y);
weight0 = 1 / sqrtf(powf(incell.x, 2) + powf(incell.y, 2)); float const weight1 = ( incell.x) * (1 - incell.y);
weight1 = 1 / sqrtf(powf(1 - incell.x, 2) + powf(1 - incell.y, 2)); float const weight2 = (1 - incell.x) * ( incell.y);
float const weight3 = ( incell.x) * ( incell.y);
/* find which triangle we're directly under */ return (height0 * weight0 + height1 * weight1 + height2 * weight2 + height3 * weight3) / (weight0 + weight1 + weight2 + weight3);
/* for this manhattan distance is sufficient */
if (incell.x + (1 - incell.y) < (1 - incell.x) + incell.y) {
height2 = heightmap[x][y + 1];
weight2 = 1 / sqrtf(powf(incell.x, 2) + powf(1 - incell.y, 2));
} else {
height2 = heightmap[x + 1][y];
weight2 = 1 / sqrtf(powf(1 - incell.x, 2) + powf(incell.y, 2));
}
return (height0 * weight0 + height1 * weight1 + height2 * weight2) / (weight0 + weight1 + weight2);
} }
@ -81,13 +363,15 @@ static void process_ground_mode(State *state) {
SceneIngame *scn = (SceneIngame *)state->scene; SceneIngame *scn = (SceneIngame *)state->scene;
DrawCameraFromPrincipalAxesResult dir_and_up = DrawCameraFromPrincipalAxesResult dir_and_up =
draw_camera_from_principal_axes(scn->pos, (float)M_PI_2 * 0.8f, scn->roll, scn->pitch, scn->yaw); draw_camera_from_principal_axes(scn->pos, scn->roll, scn->pitch, scn->yaw, (float)M_PI_2 * 0.8f, 1, TERRAIN_RADIUS * sqrtf(3));
scn->looking_direction = dir_and_up.direction;
dir_and_up.direction.y = 0; dir_and_up.direction.y = 0;
dir_and_up.direction = vec3_norm(dir_and_up.direction); dir_and_up.direction = vec3_norm(dir_and_up.direction);
const Vec3 right = m_vec_norm(m_vec_cross(dir_and_up.direction, dir_and_up.up)); const Vec3 right = m_vec_norm(m_vec_cross(dir_and_up.direction, dir_and_up.up));
const float speed = 0.18f; /* TODO: put this in a better place */ const float speed = 0.20f; /* TODO: put this in a better place */
Vec3 target = scn->pos; Vec3 target = scn->pos;
@ -96,7 +380,7 @@ static void process_ground_mode(State *state) {
float const height = height_at(scn, (Vec2){scn->pos.x, scn->pos.z}); float const height = height_at(scn, (Vec2){scn->pos.x, scn->pos.z});
if (target.y > height + PLAYER_HEIGHT) if (target.y > height + PLAYER_HEIGHT)
target.y = target.y - 0.4f; target.y = target.y - 0.6f;
if (target.y < height + PLAYER_HEIGHT) if (target.y < height + PLAYER_HEIGHT)
target.y = height + PLAYER_HEIGHT; target.y = height + PLAYER_HEIGHT;
@ -135,50 +419,100 @@ static void generate_terrain(SceneIngame *scn) {
float y = floorf(scn->pos.z - HALF_TERRAIN_DISTANCE + (float)ly); float y = floorf(scn->pos.z - HALF_TERRAIN_DISTANCE + (float)ly);
float height = stb_perlin_noise3((float)x * TERRAIN_FREQUENCY, (float)y * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 3 - 1; float height = stb_perlin_noise3((float)x * TERRAIN_FREQUENCY, (float)y * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 3 - 1;
height += stb_perlin_noise3((float)x * TERRAIN_FREQUENCY / 10, (float)y * TERRAIN_FREQUENCY / 10, 0, 0, 0, 0) * 10 - 1; height += stb_perlin_noise3((float)x * TERRAIN_FREQUENCY / 10, (float)y * TERRAIN_FREQUENCY / 10, 0, 0, 0, 0) * 20 - 1;
heightmap[lx][ly] = height; heightmap[lx][ly] = height;
} }
} }
scn->world_center = (Vec2){ floorf(scn->pos.x - HALF_TERRAIN_DISTANCE), floorf(scn->pos.z - HALF_TERRAIN_DISTANCE) };
}
static int32_t ceil_sqrt(int32_t const n) {
int32_t res = 1;
while(res * res < n)
res++;
return res;
}
static uint32_t adler32(const void *buf, size_t buflength) {
const uint8_t *buffer = (const uint8_t*)buf;
uint32_t s1 = 1;
uint32_t s2 = 0;
for (size_t n = 0; n < buflength; n++) {
s1 = (s1 + buffer[n]) % 65521;
s2 = (s2 + s1) % 65521;
}
return (s2 << 16) | s1;
} }
static void draw_terrain(SceneIngame *scn) { static void draw_terrain(SceneIngame *scn) {
for (int ly = TERRAIN_DISTANCE - 1; ly > 0; ly--) { /* used to cull invisible tiles over field of view (to horizon) */
for (int lx = 0; lx < TERRAIN_DISTANCE - 1; lx++) { Vec2 const d = vec2_norm((Vec2){ .x = scn->looking_direction.x, .y = scn->looking_direction.z });
int32_t x = (int32_t)(floorf(scn->pos.x - HALF_TERRAIN_DISTANCE + (float)lx)); float const c = cosf((float)M_PI_2 * 0.8f * 0.8f);
int32_t y = (int32_t)(floorf(scn->pos.z - HALF_TERRAIN_DISTANCE + (float)ly));
/* draw terrain in circle */
int32_t const rsi = (int32_t)TERRAIN_RADIUS * (int32_t)TERRAIN_RADIUS;
for (int32_t iy = -(int32_t)TERRAIN_RADIUS; iy <= (int32_t)TERRAIN_RADIUS - 1; ++iy) {
int32_t const dx = ceil_sqrt(rsi - (iy + (iy <= 0)) * (iy + (iy <= 0)));
for (int32_t ix = -dx; ix < dx - 1; ++ix) {
int32_t lx = ix + TERRAIN_RADIUS;
int32_t ly = iy + TERRAIN_RADIUS;
float x = (float)(floorf(scn->pos.x - HALF_TERRAIN_DISTANCE + (float)lx));
float y = (float)(floorf(scn->pos.z - HALF_TERRAIN_DISTANCE + (float)ly));
/* cull tiles outside of vision */
if (vec2_dot(vec2_norm((Vec2){x - scn->pos.x + d.x * 2, y - scn->pos.z + d.y * 2}), d) < c)
continue;
float d0 = heightmap[lx][ly]; float d0 = heightmap[lx][ly];
float d1 = heightmap[lx + 1][ly]; float d1 = heightmap[lx + 1][ly];
float d2 = heightmap[lx + 1][ly - 1]; float d2 = heightmap[lx + 1][ly - 1];
float d3 = heightmap[lx][ly - 1]; float d3 = heightmap[lx][ly - 1];
draw_triangle("/assets/grass.png", draw_quad("/assets/grass2.png",
(Vec3){ (float)x, d0, (float)y }, (Vec3){ (float)x, d0, (float)y },
(Vec3){ (float)x + 1, d1, (float)y },
(Vec3){ (float)x, d3, (float)y - 1 },
(Vec2){ 128, 128 },
(Vec2){ 128, 0 },
(Vec2){ 0, 128 },
(Color){255, 255, 255, 255},
(Color){255, 255, 255, 255},
(Color){255, 255, 255, 255});
draw_triangle("/assets/grass.png",
(Vec3){ (float)x + 1, d1, (float)y }, (Vec3){ (float)x + 1, d1, (float)y },
(Vec3){ (float)x + 1, d2, (float)y - 1 }, (Vec3){ (float)x + 1, d2, (float)y - 1 },
(Vec3){ (float)x, d3, (float)y - 1 }, (Vec3){ (float)x, d3, (float)y - 1 },
(Vec2){ 128, 0 }, (Rect){ .w = 128, .h = 128 },
(Vec2){ 0, 0 },
(Vec2){ 0, 128 },
(Color){255, 255, 255, 255},
(Color){255, 255, 255, 255},
(Color){255, 255, 255, 255}); (Color){255, 255, 255, 255});
draw_billboard("/assets/grasses/10.png", if (((float)(adler32(&((Vec2){x, y}), sizeof (Vec2)) % 100) / 100) <= TREE_DENSITY)
(Vec3){ (float)x, d0 + 0.15f, (float)y }, draw_billboard("/assets/trreez.png",
(Vec2){0.3f, 0.3f}, (Vec3){ (float)x, d0 + 1.95f, (float)y },
(Vec2){2.f, 2.f},
(Rect){0},
(Color){255, 255, 255, 255}, true);
}
}
int32_t const rsi_g = (int32_t)GRASS_RADIUS * (int32_t)GRASS_RADIUS;
for (int32_t iy = -(int32_t)GRASS_RADIUS; iy <= (int32_t)GRASS_RADIUS - 1; ++iy) {
int32_t const dx = ceil_sqrt(rsi_g - (iy + (iy <= 0)) * (iy + (iy <= 0)));
for (int32_t ix = -dx; ix < dx; ++ix) {
int32_t lx = ix + TERRAIN_RADIUS;
int32_t ly = iy + TERRAIN_RADIUS;
float x = (float)(floorf(scn->pos.x - HALF_TERRAIN_DISTANCE + (float)lx));
float y = (float)(floorf(scn->pos.z - HALF_TERRAIN_DISTANCE + (float)ly));
float d = heightmap[lx][ly];
draw_billboard("/assets/grasses/25.png",
(Vec3){
(float)x + (float)((adler32(&((Vec2){x, y}), sizeof (Vec2))) % 32) / 64.0f,
d + 0.2f,
(float)y + (float)((adler32(&((Vec2){y, x}), sizeof (Vec2))) % 32) / 64.0f
},
(Vec2){0.4f, 0.4f},
(Rect){0},
(Color){255, 255, 255, 255}, true); (Color){255, 255, 255, 255}, true);
} }
} }
@ -188,29 +522,31 @@ static void draw_terrain(SceneIngame *scn) {
static void ingame_tick(State *state) { static void ingame_tick(State *state) {
SceneIngame *scn = (SceneIngame *)state->scene; SceneIngame *scn = (SceneIngame *)state->scene;
input_action("player_left", CONTROL_A); input_action("player_left", "A");
input_action("player_right", CONTROL_D); input_action("player_right", "D");
input_action("player_forward", CONTROL_W); input_action("player_forward", "W");
input_action("player_backward", CONTROL_S); input_action("player_backward", "S");
input_action("player_jump", CONTROL_SPACE); input_action("player_jump", "SPACE");
input_action("player_run", CONTROL_LSHIFT); input_action("player_run", "LSHIFT");
input_action("mouse_capture_toggle", CONTROL_ESCAPE); input_action("mouse_capture_toggle", "ESCAPE");
input_action("toggle_camera_mode", CONTROL_C); input_action("toggle_camera_mode", "C");
if (scn->mouse_captured) { if (scn->mouse_captured) {
const float sensitivity = 0.4f * (float)DEG2RAD; /* TODO: put this in a better place */ const float sensitivity = 0.4f * (float)(M_PI / 180); /* TODO: put this in a better place */
scn->yaw += (float)ctx.mouse_movement.x * sensitivity; scn->yaw += (float)ctx.mouse_movement.x * sensitivity;
scn->pitch -= (float)ctx.mouse_movement.y * sensitivity; scn->pitch -= (float)ctx.mouse_movement.y * sensitivity;
scn->pitch = clampf(scn->pitch, (float)-M_PI * 0.49f, (float)M_PI * 0.49f); scn->pitch = clampf(scn->pitch, (float)-M_PI * 0.49f, (float)M_PI * 0.49f);
} }
if (input_action_just_pressed("toggle_camera_mode")) if (input_action_just_pressed("toggle_camera_mode"))
scn->flying_camera = !scn->flying_camera; scn->camera_mode = scn->camera_mode == 2 ? 0 : scn->camera_mode + 1;
if (scn->flying_camera) { if (scn->camera_mode == 1) {
process_fly_mode(state); process_fly_mode(state);
} else { } else if (scn->camera_mode == 0) {
process_ground_mode(state); process_ground_mode(state);
} else if (scn->camera_mode) {
process_vehicle_mode(state);
} }
/* toggle mouse capture with end key */ /* toggle mouse capture with end key */
@ -220,12 +556,15 @@ static void ingame_tick(State *state) {
ctx.mouse_capture = scn->mouse_captured; ctx.mouse_capture = scn->mouse_captured;
generate_terrain(scn); generate_terrain(scn);
process_vehicle(scn);
draw_terrain(scn); draw_terrain(scn);
draw_vehicle(scn);
draw_skybox("/assets/miramar/miramar_*.tga"); draw_skybox("/assets/miramar/miramar_*.tga");
ctx.fog_color = (Color){ 140, 147, 160, 255 }; ctx.fog_color = (Color){ 140, 147, 160, 255 };
ctx.fog_density = 0.03f; ctx.fog_density = 0.015f;
} }
@ -237,13 +576,13 @@ static void ingame_end(State *state) {
Scene *ingame_scene(State *state) { Scene *ingame_scene(State *state) {
(void)state; (void)state;
SceneIngame *new_scene = ccalloc(1, sizeof *new_scene); SceneIngame *new_scene = calloc(1, sizeof *new_scene);
new_scene->base.tick = ingame_tick; new_scene->base.tick = ingame_tick;
new_scene->base.end = ingame_end; new_scene->base.end = ingame_end;
new_scene->mouse_captured = true; new_scene->mouse_captured = true;
m_audio(m_set(path, "music/mod65.xm"), m_audio(m_set(path, "music/woah.ogg"),
m_opt(channel, "soundtrack"), m_opt(channel, "soundtrack"),
m_opt(repeat, true)); m_opt(repeat, true));

View File

@ -12,13 +12,16 @@
typedef struct SceneIngame { typedef struct SceneIngame {
Scene base; Scene base;
Vec3 looking_direction;
Vec2 world_center;
Vec3 pos; Vec3 pos;
float yaw; float yaw;
float pitch; float pitch;
float roll; float roll;
bool mouse_captured; bool mouse_captured;
bool flying_camera; int camera_mode;
} SceneIngame; } SceneIngame;

View File

@ -1,61 +0,0 @@
#include "title.h"
#include "ingame.h"
#include "twn_game_api.h"
#include <stdio.h>
#include <stdlib.h>
static void title_tick(State *state) {
SceneTitle *scn = (SceneTitle *)state->scene;
(void)scn;
input_action("ui_accept", CONTROL_RETURN);
if (input_action_just_pressed("ui_accept")) {
switch_to(state, ingame_scene);
return;
}
m_sprite("/assets/title.png", ((Rect) {
((float)ctx.resolution.x / 2) - ((float)320 / 2), 64, 320, 128 }));
/* draw the tick count as an example of dynamic text */
size_t text_str_len = snprintf(NULL, 0, "%llu", (unsigned long long)state->ctx->frame_number) + 1;
char *text_str = cmalloc(text_str_len);
snprintf(text_str, text_str_len, "%llu", (unsigned long long)state->ctx->frame_number);
const char *font = "/fonts/kenney-pixel.ttf";
float text_h = 32;
float text_w = draw_text_width(text_str, text_h, font);
draw_rectangle(
(Rect) {
.x = 0,
.y = 0,
.w = text_w,
.h = text_h,
},
(Color) { 0, 0, 0, 255 }
);
draw_text(text_str, (Vec2){ 0, 0 }, text_h, (Color) { 255, 255, 255, 255 }, font);
free(text_str);
}
static void title_end(State *state) {
free(state->scene);
}
Scene *title_scene(State *state) {
(void)state;
SceneTitle *new_scene = ccalloc(1, sizeof *new_scene);
new_scene->base.tick = title_tick;
new_scene->base.end = title_end;
return (Scene *)new_scene;
}

View File

@ -1,16 +0,0 @@
#ifndef TITLE_H
#define TITLE_H
#include "../state.h"
#include "scene.h"
typedef struct SceneTitle {
Scene base;
} SceneTitle;
Scene *title_scene(State *state);
#endif

View File

@ -6,11 +6,11 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug) set(CMAKE_BUILD_TYPE Debug)
endif() endif()
add_subdirectory($ENV{TWNROOT} $ENV{TWNROOT}/build) add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
set(SOURCE_FILES set(SOURCE_FILES
game.c game.c
state.h state.h
) )
use_townengine(${PROJECT_NAME} "${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR}) use_townengine("${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -103,7 +103,7 @@ static void benchmark(struct state *state) {
void game_tick(void) { void game_tick(void) {
if (ctx.initialization_needed) { if (ctx.initialization_needed) {
if (!ctx.udata) { if (!ctx.udata) {
ctx.udata = ccalloc(1, sizeof (struct state)); ctx.udata = calloc(1, sizeof (struct state));
struct state *state = ctx.udata; struct state *state = ctx.udata;
state->r = 24; state->r = 24;
} }
@ -113,14 +113,16 @@ void game_tick(void) {
Vec2 const mouse_snap = {floorf(ctx.mouse_position.x / 8) * 8, floorf(ctx.mouse_position.y / 8) * 8}; Vec2 const mouse_snap = {floorf(ctx.mouse_position.x / 8) * 8, floorf(ctx.mouse_position.y / 8) * 8};
input_action("up", CONTROL_LEFT_MOUSE); input_action("up", "LCLICK");
input_action("down", CONTROL_RIGHT_MOUSE); input_action("down", "RCLICK");
if (input_action_just_pressed("up")) if (input_action_pressed("up"))
state->r += 1; state->r += 1;
if (input_action_just_pressed("down")) if (input_action_pressed("down") && state->r > 2)
state->r -= 1; state->r -= 1;
draw_circle(mouse_snap, state->r * 8, (Color){125, 125, 125, 125});
int32_t const rsi = (int32_t)state->r * (int32_t)state->r; int32_t const rsi = (int32_t)state->r * (int32_t)state->r;
int32_t acc = 1; int32_t acc = 1;
for (int32_t iy = (int32_t)state->r - 1; iy >= 0; --iy) { for (int32_t iy = (int32_t)state->r - 1; iy >= 0; --iy) {
@ -133,9 +135,8 @@ void game_tick(void) {
} }
} }
draw_circle(mouse_snap, state->r * 8, (Color){125, 125, 125, 125}); /* uncomment to see performance difference between variants */
// benchmark(state);
benchmark(state);
} }

View File

@ -1,16 +0,0 @@
cmake_minimum_required(VERSION 3.21)
project(template LANGUAGES C)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif()
add_subdirectory($ENV{TWNROOT} $ENV{TWNROOT}/build)
set(SOURCE_FILES
game.c
state.h
)
use_townengine(${PROJECT_NAME} "${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.21)
project(twngame LANGUAGES C)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif()
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
set(SOURCE_FILES
game.c
state.h
)
cmake_path(GET CMAKE_SOURCE_DIR STEM LAST_ONLY GAME_PROJECT_NAME)
use_townengine("${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -0,0 +1,27 @@
# This file contains everything about the engine and your game that can be
# configured before it runs.
#
# Optional settings are commented out, with their default values shown.
# Invalid values in these settings will be ignored.
# Data about your game as an application
[about]
title = "Template"
developer = "You"
app_id = "template"
dev_id = "you"
# Game runtime details
[game]
resolution = [ 640, 480 ]
background_color = [ 255, 125, 0, 255 ]
#debug = true
# Engine tweaks. You probably don't need to change these
[engine]
#ticks_per_second = 60 # minimum of 8
#keybind_slots = 3 # minimum of 1
#texture_atlas_size = 2048 # minimum of 32
#font_texture_size = 2048 # minimum of 1024
#font_oversampling = 4 # minimum of 0
#font_filtering = "linear" # possible values: "nearest", "linear"

View File

@ -1,7 +1,7 @@
#include "twn_game_api.h" #include "twn_game_api.h"
#include "state.h" #include "state.h"
#include <malloc.h> #include <stdlib.h>
void game_tick(void) { void game_tick(void) {
@ -10,7 +10,7 @@ void game_tick(void) {
if (ctx.initialization_needed) { if (ctx.initialization_needed) {
/* application data could be stored in ctx.udata and retrieved anywhere */ /* application data could be stored in ctx.udata and retrieved anywhere */
if (!ctx.udata) if (!ctx.udata)
ctx.udata = ccalloc(1, sizeof (struct state)); ctx.udata = calloc(1, sizeof (struct state));
} }
/* a lot of data is accessible from `ctx`, look into `townengine/context.h` for more */ /* a lot of data is accessible from `ctx`, look into `townengine/context.h` for more */

17
apps/templates/lua/.gitignore vendored Normal file
View File

@ -0,0 +1,17 @@
# ignore executables
*
!*.*
!*/
**/*.so
**/*.dll
**/*.exe
**/*.trace
**/*.js
**/*.wasm
**/*.wasm.map
**/*.data
**/*.html
data/scripts/twnapi.lua
build/

View File

@ -0,0 +1,7 @@
-- called every frame, with constant delta time
function game_tick()
-- ctx.udata persists on code reload
if ctx.udata == nil then
ctx.udata = {}
end
end

View File

@ -0,0 +1,27 @@
# This file contains everything about the engine and your game that can be
# configured before it runs.
#
# Optional settings are commented out, with their default values shown.
# Invalid values in these settings will be ignored.
# Data about your game as an application
[about]
title = "Template"
developer = "You"
app_id = "template"
dev_id = "you"
# Game runtime details
[game]
resolution = [ 640, 480 ]
interpreter = "$TWNROOT/apps/twnlua"
#debug = true
# Engine tweaks. You probably don't need to change these
[engine]
#ticks_per_second = 60 # minimum of 8
#keybind_slots = 3 # minimum of 1
#texture_atlas_size = 2048 # minimum of 32
#font_texture_size = 2048 # minimum of 1024
#font_oversampling = 4 # minimum of 0
#font_filtering = "linear" # possible values: "nearest", "linear"

19
apps/templates/zig/.gitignore vendored Normal file
View File

@ -0,0 +1,19 @@
# ignore executables
*
!*.*
!*/
**/*.so
**/*.dll
**/*.exe
**/*.trace
**/*.js
**/*.wasm
**/*.wasm.map
**/*.data
**/*.html
data/scripts/twnapi.lua
build/
.zig-cache/
zig-out/

View File

@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.21)
project(twngame LANGUAGES C)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif()
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
cmake_path(GET CMAKE_SOURCE_DIR STEM LAST_ONLY GAME_PROJECT_NAME)
put_townengine(${CMAKE_CURRENT_SOURCE_DIR})
file(GLOB_RECURSE zig-sources ${CMAKE_CURRENT_SOURCE_DIR}/src/*.zig)
# TODO: support static build
# TODO: propagate release switches
add_custom_command(
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/libgame.so
COMMAND env zig build
DEPENDS ${TWN_TARGET} ${zig-sources}
)
add_custom_target(
zig-step ALL
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgame.so
)

View File

@ -0,0 +1,58 @@
const std = @import("std");
// Although this function looks imperative, note that its job is to
// declaratively construct a build graph that will be executed by an external
// runner.
pub fn build(b: *std.Build) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});
// Standard optimization options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
// set a preferred release mode, allowing the user to decide how to optimize.
const optimize = b.standardOptimizeOption(.{});
// This creates a "module", which represents a collection of source files alongside
// some compilation options, such as optimization mode and linked system libraries.
// Every executable or library we compile will be based on one or more modules.
const lib_mod = b.createModule(.{
// `root_source_file` is the Zig "entry point" of the module. If a module
// only contains e.g. external object files, you can make this `null`.
// In this case the main source file is merely a path, however, in more
// complicated build scripts, this could be a generated file.
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
});
lib_mod.addIncludePath(b.path("../../../"));
lib_mod.addIncludePath(b.path("../../../include/"));
lib_mod.addLibraryPath(b.path("./"));
// Now, we will create a static library based on the module we created above.
// This creates a `std.Build.Step.Compile`, which is the build step responsible
// for actually invoking the compiler.
const lib = b.addLibrary(.{
.linkage = .dynamic,
.name = "game",
.root_module = lib_mod,
});
lib.linkSystemLibrary("townengine");
// This declares intent for the library to be installed into the standard
// location when the user invokes the "install" step (the default step when
// running `zig build`).
const install_artifact = b.addInstallArtifact(lib, .{
.dest_dir = .{
.override = .{
.custom = "../",
},
},
});
b.getInstallStep().dependOn(&install_artifact.step);
}

View File

@ -0,0 +1,27 @@
# This file contains everything about the engine and your game that can be
# configured before it runs.
#
# Optional settings are commented out, with their default values shown.
# Invalid values in these settings will be ignored.
# Data about your game as an application
[about]
title = "Zig Awesomeness"
developer = "notwanp"
app_id = "yourzigthing"
dev_id = "definatelynotwanp"
# Game runtime details
[game]
resolution = [ 640, 480 ]
background_color = [ 255, 125, 0, 255 ]
#debug = true
# Engine tweaks. You probably don't need to change these
[engine]
#ticks_per_second = 60 # minimum of 8
#keybind_slots = 3 # minimum of 1
#texture_atlas_size = 2048 # minimum of 32
#font_texture_size = 2048 # minimum of 1024
#font_oversampling = 4 # minimum of 0
#font_filtering = "linear" # possible values: "nearest", "linear"

View File

@ -0,0 +1,24 @@
const std = @import("std");
const c = @cImport({
@cInclude("twn_game_api.h");
});
export fn game_tick() void {
tick() catch |err| {
std.log.err(@errorName(err));
};
}
export fn game_end() void {
end() catch |err| {
std.log.err(@errorName(err));
};
}
fn tick() !void {
if (c.ctx.initialization_needed) {
std.debug.print("lmao\n", .{});
}
}
fn end() !void {}

View File

@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.21)
project(twndel LANGUAGES C)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif()
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
set(SOURCE_FILES
tool.c
state.h
)
cmake_path(GET CMAKE_SOURCE_DIR STEM LAST_ONLY GAME_PROJECT_NAME)
use_townengine("${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})

BIN
apps/tools/twndel/data/bong.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
apps/tools/twndel/data/camera.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
apps/tools/twndel/data/center.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

BIN
apps/tools/twndel/data/drop.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
apps/tools/twndel/data/grab.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
apps/tools/twndel/data/point.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
apps/tools/twndel/data/selectin.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
apps/tools/twndel/data/selectout.ogg (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
apps/tools/twndel/data/switch.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,12 @@
[about]
title = "Townengine Modeling Tool"
developer = "twnteam"
app_id = "twndel"
dev_id = "twnteam"
# Game runtime details
[game]
resolution = [ 640, 480 ]
background_color = [ 255, 125, 0, 255 ]
[engine]

BIN
apps/tools/twndel/data/x.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
apps/tools/twndel/data/y.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
apps/tools/twndel/data/z.png (Stored with Git LFS) Normal file

Binary file not shown.

118
apps/tools/twndel/state.h Normal file
View File

@ -0,0 +1,118 @@
#ifndef STATE_H
#define STATE_H
#include "twn_game_api.h"
#define POINTS_PER_METER 128
#define UNDO_STACK_SIZE 32
#define CAMERA_FOV ((float)M_PI_2 * 0.75f)
#define POINT_LIMIT 65534
#define FACE_LIMIT 2048
#define OBJECT_LIMIT 16
#define TEXTURE_LIMIT 32
#define INVALID_POINT (POINT_LIMIT+1)
#define INVALID_FACE (FACE_LIMIT+1)
#define INVALID_OBJECT (OBJECT_LIMIT+1)
#define INVALID_TEXTURE (TEXTURE_LIMIT+1)
#define CAMERA_ROTATION_SPEED 0.04f
#define CAMERA_TRANSLATION_SPEED 0.04f
#define SELECTION_SPHERE_RADIUS 32
/* should be an odd number */
#define SNAP_LINES_SHOW 7
#define SNAP_LINES_WIDTH 1.0f
#define SNAP_LINES_COLOR ((Color){200,200,200,150})
typedef struct Operation {
enum {
OPERATION_MOVE_POINT,
OPERATION_SET_TEXTURE,
OPERATION_TRIANGULATE,
} kind;
union {
struct {
uint16_t point;
int16_t delta_x;
int16_t delta_y;
int16_t delta_z;
uint8_t object;
} move_point;
struct {
uint16_t face;
int16_t delta_texture;
uint8_t object;
} set_texture;
struct {
uint16_t old_face;
uint16_t new_face;
uint8_t object;
} triangulate;
} data;
bool chained;
} Operation;
typedef struct Point {
int16_t x, y, z;
} Point;
/* TODO: store topology in terms on edge connections? might be bad, as it's stateful */
/* triangles have p3 = INVALID_POINT */
/* lines have p2, p3 = INVALID_POINT */
/* billboards have p1, p2, p3 = INVALID_POINT */
typedef struct Face {
uint16_t p[4];
/* texture origin, as point on face plane, in absolute coordinates */
int16_t tex_x, tex_y;
uint8_t texture;
uint8_t tex_scale;
} Face;
typedef struct Object {
char *name;
bool is_invisible;
Point position;
char *textures[TEXTURE_LIMIT + 1];
uint8_t textures_sz;
Point rotation;
Face faces[FACE_LIMIT];
uint16_t faces_sz;
} Object;
typedef struct State {
Operation op_stack[UNDO_STACK_SIZE];
uint32_t op_stack_ptr;
bool op_active;
Vec3 active_center;
Vec3 camera_position;
Vec3 camera_direction;
float camera_zoom;
bool camera_is_orthographic;
/* defaults to wireframe */
bool solid_display_mode;
/* positions skipped */
uint8_t grid_snap_granularity;
Point points[POINT_LIMIT];
uint16_t points_sz;
Object objects[OBJECT_LIMIT];
uint8_t objects_sz;
/* which axes are blocked and which are not */
/* order: x, y, z */
bool axis_mask[3];
char *current_texture;
uint8_t current_hovered_obj;
uint16_t current_hovered_face;
} State;
#endif

972
apps/tools/twndel/tool.c Normal file
View File

@ -0,0 +1,972 @@
#include "twn_game_api.h"
#include "state.h"
#include "twn_vec.h"
#include <SDL.h>
/* planned features: */
/* grid-based, bounded space (65536 / POINTS_PER_METER meters), which allows for efficient storage */
/* 65534 point limit, 16 object limit, 2048 faces per object, 32 textures per object */
/* triangles and quads only */
/* support for billboards and flat two sided quads */
/* texture painting */
/* bones with mesh animations, snapping to grid, with no weights */
/* billboard render to specified angles */
/* 1 point light primitive lighting */
/* live edited textures are capped at 128x128 */
/* assumptions: */
/* up is always (0,1,0) */
/* preallocations everywhere */
static State state;
static bool init;
static uint8_t new_object(const char *name) {
if (state.objects_sz >= OBJECT_LIMIT)
return INVALID_OBJECT;
state.objects_sz++;
state.objects[state.objects_sz-1].name = SDL_strdup(name);
return state.objects_sz-1;
}
static uint16_t new_point(int16_t x, int16_t y, int16_t z) {
if (state.points_sz >= POINT_LIMIT)
return INVALID_POINT;
state.points_sz++;
state.points[state.points_sz-1] = (Point){x, y, z};
return state.points_sz-1;
}
static uint16_t push_face(uint8_t object,
uint16_t p0, uint16_t p1, uint16_t p2, uint16_t p3,
uint8_t texture, uint8_t tex_scale, int16_t tex_x, int16_t tex_y)
{
Object *o = &state.objects[object];
o->faces_sz++;
o->faces[o->faces_sz-1] = (Face) {
.p = {p0, p1, p2, p3},
.texture = texture,
.tex_scale = tex_scale,
.tex_x = tex_x,
.tex_y = tex_y,
};
return o->faces_sz-1;
}
static uint8_t push_texture(uint8_t object, char *texture) {
Object *o = &state.objects[object];
/* check whether it's already here */
for (uint8_t i = 0; i < o->textures_sz; ++i)
if (SDL_strcmp(o->textures[i], texture) == 0)
return i;
o->textures_sz++;
o->textures[o->textures_sz-1] = SDL_strdup(texture);
return o->textures_sz-1;
}
/* TODO: use tombstones instead? it would be easier to maintain, by a lot */
/* note: make sure nothing depends on none */
static void pop_face(uint8_t object, uint16_t face) {
Object *o = &state.objects[object];
if (face != o->faces_sz-1)
o->faces[face] = o->faces[o->faces_sz-1];
o->faces_sz--;
}
static void push_operation(Operation operation, bool active) {
state.op_stack_ptr++;
uint8_t op = state.op_stack_ptr % UNDO_STACK_SIZE;
state.op_stack[op] = operation;
state.op_active = active;
}
static void extend_operation(Operation operation) {
uint8_t op = state.op_stack_ptr % UNDO_STACK_SIZE;
Operation ext = state.op_stack[op];
ext.chained = true;
state.op_stack[op] = operation;
push_operation(ext, state.op_active);
}
static inline Vec3 point_to_vec3(uint8_t object, uint16_t point) {
Object *o = &state.objects[object];
return (Vec3){ (o->position.x + state.points[point].x) / (float)POINTS_PER_METER,
(o->position.y + state.points[point].y) / (float)POINTS_PER_METER,
(o->position.z + state.points[point].z) / (float)POINTS_PER_METER };
}
/* TODO: something is wrong, figure out how to introduce rotation to this. */
static inline Vec2 project_texture_coordinate(Vec2 origin, Vec3 plane, Vec3 point, float scale) {
Vec3 right = vec3_norm(vec3_cross(plane, fabsf(0.0f - plane.y) < 1e-9f ? (Vec3){0,1,0} : (Vec3){1,0,0}));
Vec3 up = vec3_norm(vec3_cross(right, plane));
Vec2 pp = { vec3_dot(point, right), vec3_dot(point, up) };
return vec2_scale(vec2_sub(origin, pp), scale);
}
static void render_object(uint8_t object) {
Object *o = &state.objects[object];
if (o->is_invisible)
return;
for (uint16_t fi = 0; fi < o->faces_sz; ++fi) {
Face *f = &o->faces[fi];
if (f->p[2] != INVALID_POINT) {
Vec3 p0 = point_to_vec3(object, f->p[0]);
Vec3 p1 = point_to_vec3(object, f->p[1]);
Vec3 p2 = point_to_vec3(object, f->p[2]);
if (state.solid_display_mode) {
Vec3 n = vec3_norm(vec3_cross(vec3_sub(p1, p0), vec3_sub(p2, p0)));
Vec2 to = { f->tex_x / (float)POINTS_PER_METER, f->tex_y / (float)POINTS_PER_METER, };
Vec2 tul = project_texture_coordinate(to, n, p0, (float)f->tex_scale);
Vec2 tdl = project_texture_coordinate(to, n, p1, (float)f->tex_scale);
Vec2 tdr = project_texture_coordinate(to, n, p2, (float)f->tex_scale);
draw_triangle(o->textures[f->texture],
p0, p1, p2,
tul, tdl, tdr,
(Color){255,255,255,255},
(Color){255,255,255,255},
(Color){255,255,255,255});
if (f->p[3] != INVALID_POINT) {
Vec3 p3 = point_to_vec3(object, f->p[3]);
Vec2 tur = project_texture_coordinate(to, n, p3, (float)f->tex_scale);
draw_triangle(o->textures[f->texture],
p2, p3, p0,
tdr, tur, tul,
(Color){255,255,255,255},
(Color){255,255,255,255},
(Color){255,255,255,255});
}
} else {
draw_line_3d(p0, p1, 1, (Color){255,255,255,255});
draw_line_3d(p1, p2, 1, (Color){255,255,255,255});
if (f->p[3] == INVALID_POINT)
draw_line_3d(p2, p0, 1, (Color){255,255,255,255});
else {
Vec3 p3 = point_to_vec3(object, f->p[3]);
draw_line_3d(p2, p3, 1, (Color){255,255,255,255});
draw_line_3d(p3, p0, 1, (Color){255,255,255,255});
}
}
} else
SDL_assert_always(false);
}
}
static uint8_t new_cube(Point pos, Point size) {
uint8_t object = new_object("cube");
uint16_t p0 = new_point(pos.x - size.x / 2, pos.y - size.y / 2, pos.z - size.z / 2);
uint16_t p1 = new_point(pos.x - size.x / 2, pos.y + size.y / 2, pos.z - size.z / 2);
uint16_t p2 = new_point(pos.x + size.x / 2, pos.y + size.y / 2, pos.z - size.z / 2);
uint16_t p3 = new_point(pos.x + size.x / 2, pos.y - size.y / 2, pos.z - size.z / 2);
uint16_t p4 = new_point(pos.x - size.x / 2, pos.y - size.y / 2, pos.z + size.z / 2);
uint16_t p5 = new_point(pos.x - size.x / 2, pos.y + size.y / 2, pos.z + size.z / 2);
uint16_t p6 = new_point(pos.x + size.x / 2, pos.y + size.y / 2, pos.z + size.z / 2);
uint16_t p7 = new_point(pos.x + size.x / 2, pos.y - size.y / 2, pos.z + size.z / 2);
uint8_t tex = push_texture(object, "/data/placeholder.png");
push_face(object, p2, p3, p0, p1, tex, 128, pos.x - size.x / 2, pos.y - size.y / 2);
push_face(object, p5, p4, p7, p6, tex, 128, pos.x - size.x / 2, pos.y - size.y / 2);
push_face(object, p1, p0, p4, p5, tex, 128, pos.x - size.x / 2, pos.y - size.y / 2);
push_face(object, p6, p7, p3, p2, tex, 128, pos.x - size.x / 2, pos.y - size.y / 2);
push_face(object, p2, p1, p5, p6, tex, 128, pos.x - size.x / 2, pos.y - size.y / 2);
push_face(object, p0, p3, p7, p4, tex, 128, pos.x - size.x / 2, pos.y - size.y / 2);
return object;
}
static void process_camera_rotation(void) {
float horizontal_rotation = 0;
float vertical_rotation = 0;
if (input_action_pressed("camera_rotate_left"))
horizontal_rotation -= CAMERA_ROTATION_SPEED;
if (input_action_pressed("camera_rotate_right"))
horizontal_rotation += CAMERA_ROTATION_SPEED;
if (input_action_pressed("camera_rotate_up"))
vertical_rotation -= CAMERA_ROTATION_SPEED;
if (input_action_pressed("camera_rotate_down"))
vertical_rotation += CAMERA_ROTATION_SPEED;
Vec3 front = vec3_cross(state.camera_direction, (Vec3){0,1,0});
Vec3 local_position = vec3_sub(state.active_center, state.camera_position);
Vec3 new_local_position = vec3_rotate(local_position, horizontal_rotation, (Vec3){0,1,0});
state.camera_direction = vec3_rotate(state.camera_direction, horizontal_rotation, (Vec3){0,1,0});
Vec3 new_rot = vec3_rotate(state.camera_direction, vertical_rotation, front);
/* only apply if it's in limits */
float d = vec3_dot(new_rot, (Vec3){0,-1,0});
if (fabsf(d) <= 0.999f) {
new_local_position = vec3_rotate(new_local_position, vertical_rotation, front);
state.camera_direction = new_rot;
}
state.camera_position = vec3_sub(state.active_center, new_local_position);
}
static void process_camera_translation(void) {
Vec3 right = vec3_norm(vec3_cross(state.camera_direction, (Vec3){0,1,0}));
Vec3 up = vec3_norm(vec3_cross(state.camera_direction, right));
Vec3 was = state.camera_position;
if (input_action_pressed("camera_rotate_left"))
state.camera_position = vec3_sub(state.camera_position, vec3_scale(right, CAMERA_TRANSLATION_SPEED));
if (input_action_pressed("camera_rotate_right"))
state.camera_position = vec3_add(state.camera_position, vec3_scale(right, CAMERA_TRANSLATION_SPEED));
if (input_action_pressed("camera_rotate_up"))
state.camera_position = vec3_sub(state.camera_position, vec3_scale(up, CAMERA_TRANSLATION_SPEED));
if (input_action_pressed("camera_rotate_down"))
state.camera_position = vec3_add(state.camera_position, vec3_scale(up, CAMERA_TRANSLATION_SPEED));
state.active_center = vec3_add(state.active_center, vec3_sub(state.camera_position, was));
draw_billboard("/data/camera.png",
vec3_add(state.camera_position, vec3_scale(state.camera_direction, vec3_length(state.camera_position))),
(Vec2){0.2f,0.2f},
(Rect){0},
(Color){255,255,255,255},
false);
/* show relation to origin */
draw_billboard("/data/center.png",
(Vec3){0},
(Vec2){0.1f,0.1f},
(Rect){0},
(Color){255,255,255,255},
false);
draw_line_3d((Vec3){0}, state.active_center, 1, (Color){255,255,255,255});
}
static void process_camera_movement(void) {
input_action("camera_rotate_left", "A");
input_action("camera_rotate_right", "D");
input_action("camera_rotate_up", "W");
input_action("camera_rotate_down", "S");
input_action("camera_lock_rotation", "SPACE");
if (input_action_pressed("camera_lock_rotation")) {
process_camera_translation();
} else {
process_camera_rotation();
}
}
static inline DrawCameraUnprojectResult unproject_point(Vec2 point) {
return draw_camera_unproject(
point,
state.camera_position,
state.camera_direction,
(Vec3){0, 1, 0},
state.camera_is_orthographic ? 0 : CAMERA_FOV,
state.camera_zoom,
100 );
}
static bool find_closest_point(uint8_t* object_result, uint16_t *point_result) {
DrawCameraUnprojectResult pos_and_ray = unproject_point(ctx.mouse_position);
/* step over every selectable object and find points closest to the view ray */
/* by constructing triangles and finding their height, from perpendicular */
uint16_t closest_point = INVALID_POINT;
uint8_t closest_obj = INVALID_OBJECT;
float closest_distance = INFINITY;
for (uint8_t obj = 0; obj < state.objects_sz; ++obj) {
Object *o = &state.objects[obj];
if (o->is_invisible)
continue;
/* TODO: is it possible to skip repeated points? does it matter? */
/* as we limit the point could we could actually have bool array preallocated for this */
for (uint16_t fi = 0; fi < o->faces_sz; ++fi) {
Face *f = &o->faces[fi];
for (uint16_t pi = 0; pi < 4; ++pi) {
if (f->p[pi] == INVALID_POINT) break;
Vec3 p = point_to_vec3(obj, f->p[pi]);
Vec3 d = vec3_sub(pos_and_ray.position, p);
Vec3 b = vec3_cross(d, pos_and_ray.direction);
float ray_dist = vec3_length(b);
if (ray_dist > ((float)SELECTION_SPHERE_RADIUS / POINTS_PER_METER))
continue;
float dist = vec3_length(vec3_sub(pos_and_ray.position, vec3_add(p, b)));
if (dist < closest_distance) {
closest_distance = dist;
closest_obj = obj;
closest_point = f->p[pi];
}
}
}
}
if (closest_point == INVALID_POINT)
return false;
if (object_result)
*object_result = closest_obj;
if (point_result)
*point_result = closest_point;
return true;
}
/* o = vector origin */
/* v = vector direction, normalized */
/* p = any point on plane */
/* n = normal of a plane */
static bool vector_plane_intersection(Vec3 o, Vec3 v, Vec3 p, Vec3 n, Vec3 *out) {
float dot = vec3_dot(n, v);
if (fabsf(dot) > FLT_EPSILON) {
Vec3 w = vec3_sub(o, p);
float fac = -vec3_dot(n, w) / dot;
*out = vec3_add(o, vec3_scale(v, fac));
return true;
}
/* vector and plane are perpendicular, assume that it lies exactly on it */
return false;
}
static bool find_closest_face(uint8_t* object_result, uint16_t *face_result) {
DrawCameraUnprojectResult cam = unproject_point(ctx.mouse_position);
uint8_t closest_obj = INVALID_OBJECT;
uint16_t closest_face = INVALID_FACE;
float closest_distance = INFINITY;
for (uint8_t oi = 0; oi < state.objects_sz; ++oi) {
Object *o = &state.objects[oi];
for (uint16_t fi = 0; fi < o->faces_sz; ++fi) {
Face *f = &o->faces[fi];
if (f->p[1] == INVALID_POINT) continue;
Vec3 p0 = point_to_vec3(oi, f->p[0]);
Vec3 p1 = point_to_vec3(oi, f->p[1]);
Vec3 p2 = point_to_vec3(oi, f->p[2]);
Vec3 n = vec3_norm(vec3_cross(vec3_sub(p1, p0), vec3_sub(p2, p0)));
/* culling */
if (vec3_dot(state.camera_direction, n) >= 0) continue;
Vec3 i;
if (!vector_plane_intersection(cam.position, cam.direction, p0, n, &i))
continue;
float dist = vec3_length(vec3_sub(p0, i));
if (dist >= closest_distance) continue;
/* left normals are used to determine whether point lies to the left for all forming lines */
Vec3 ln0 = vec3_norm(vec3_cross(n, vec3_sub(p1, p0)));
if (vec3_dot(ln0, vec3_sub(p0, i)) >= 0) continue;
Vec3 ln1 = vec3_norm(vec3_cross(n, vec3_sub(p2, p1)));
if (vec3_dot(ln1, vec3_sub(p1, i)) >= 0) continue;
if (f->p[3] == INVALID_POINT) {
/* triangle */
Vec3 ln2 = vec3_norm(vec3_cross(n, vec3_sub(p0, p2)));
if (vec3_dot(ln2, vec3_sub(p2, i)) >= 0) continue;
} else {
/* quad */
Vec3 p3 = point_to_vec3(oi, f->p[3]);
Vec3 ln2 = vec3_norm(vec3_cross(n, vec3_sub(p3, p2)));
if (vec3_dot(ln2, vec3_sub(p2, i)) >= 0) continue;
Vec3 ln3 = vec3_norm(vec3_cross(n, vec3_sub(p0, p3)));
if (vec3_dot(ln3, vec3_sub(p3, i)) >= 0) continue;
}
closest_distance = dist;
closest_face = fi;
closest_obj = oi;
}
}
if (closest_face == INVALID_FACE)
return false;
if (object_result)
*object_result = closest_obj;
if (face_result)
*face_result = closest_face;
return true;
}
static void show_snap_lines(Vec3 p) {
float step = 1.0f / ((float)POINTS_PER_METER / state.grid_snap_granularity);
int const lines_per_side = (SNAP_LINES_SHOW - 1) / 2;
for (int l = -lines_per_side; l <= lines_per_side; ++l) {
if (!state.axis_mask[0]) {
Vec3 c = vec3_add(p, vec3_scale((Vec3){1,0,0}, step * (float)l));
draw_line_3d(
vec3_add(c, vec3_scale((Vec3){0,0,1}, SNAP_LINES_WIDTH)),
vec3_add(c, vec3_scale((Vec3){0,0,1}, -SNAP_LINES_WIDTH)),
1,
SNAP_LINES_COLOR);
}
if (!state.axis_mask[1]) {
Vec3 axis = fabsf(vec3_dot(state.camera_direction, (Vec3){0,0,1})) >= 0.5f ? (Vec3){1,0,0} : (Vec3){0,0,1};
Vec3 c = vec3_add(p, vec3_scale((Vec3){0,1,0}, step * (float)l));
draw_line_3d(
vec3_add(c, vec3_scale(axis, SNAP_LINES_WIDTH)),
vec3_add(c, vec3_scale(axis, -SNAP_LINES_WIDTH)),
1,
SNAP_LINES_COLOR);
}
if (!state.axis_mask[2]) {
Vec3 c = vec3_add(p, vec3_scale((Vec3){0,0,1}, step * (float)l));
draw_line_3d(
vec3_add(c, vec3_scale((Vec3){1,0,0}, SNAP_LINES_WIDTH)),
vec3_add(c, vec3_scale((Vec3){1,0,0}, -SNAP_LINES_WIDTH)),
1,
SNAP_LINES_COLOR);
}
}
}
static bool process_operation_move_point(Operation *op) {
/* finish dragging around */
/* TODO: dont keep empty ops on stack? */
if (input_action_just_released("select")) {
state.op_active = false;
return false;
}
float size = sinf(ctx.frame_number / 10) * 0.05f + 0.05f;
draw_billboard("/data/grab.png",
point_to_vec3(op->data.move_point.object, op->data.move_point.point),
(Vec2){size, size},
(Rect){0},
(Color){255,255,255,255},
false);
DrawCameraUnprojectResult cam = unproject_point(ctx.mouse_position);
Vec3 p = point_to_vec3(op->data.move_point.object, op->data.move_point.point);
bool point_moved = false;
/* figure out which planes are angled acutely against the viewing direction */
bool y_closer_than_horizon = fabsf(vec3_dot(state.camera_direction, (Vec3){0,1,0})) >= 0.5f;
Vec3 plane_normal_x = y_closer_than_horizon ? (Vec3){0,1,0} : (Vec3){0,0,1};
Vec3 plane_normal_z = y_closer_than_horizon ? (Vec3){0,1,0} : (Vec3){1,0,0};
/* show snapping in lines */
show_snap_lines(p);
Vec3 s;
if (!state.axis_mask[0]) {
if (vector_plane_intersection(cam.position, cam.direction, p, plane_normal_x, &s)) {
int16_t xch = (int16_t)(roundf(vec3_dot(vec3_sub(s, p), (Vec3){1,0,0}) * (float)POINTS_PER_METER));
xch -= xch % state.grid_snap_granularity;
state.points[op->data.move_point.point].x += xch;
op->data.move_point.delta_x += xch;
if (xch != 0) point_moved = true;
}
}
if (!state.axis_mask[1]) {
if (vector_plane_intersection(cam.position, cam.direction, p, (Vec3){0,0,1}, &s)) {
int16_t ych = (int16_t)(roundf(vec3_dot(vec3_sub(s, p), (Vec3){0,1,0}) * (float)POINTS_PER_METER));
ych -= ych % state.grid_snap_granularity;
state.points[op->data.move_point.point].y += ych;
op->data.move_point.delta_y += ych;
if (ych != 0) point_moved = true;
}
}
if (!state.axis_mask[2]) {
if (vector_plane_intersection(cam.position, cam.direction, p, plane_normal_z, &s)) {
int16_t zch = (int16_t)(roundf(vec3_dot(vec3_sub(s, p), (Vec3){0,0,1}) * (float)POINTS_PER_METER));
zch -= zch % state.grid_snap_granularity;
state.points[op->data.move_point.point].z += zch;
op->data.move_point.delta_z += zch;
if (zch != 0) point_moved = true;
}
}
if (point_moved) {
audio_play("/data/bong.ogg", NULL, false, 0.12f, 0.0f);
return true;
}
return false;
}
static void reverse_operation_move_point(Operation *op) {
SDL_assert(op->kind == OPERATION_MOVE_POINT);
state.points[op->data.move_point.point].x -= op->data.move_point.delta_x;
state.points[op->data.move_point.point].y -= op->data.move_point.delta_y;
state.points[op->data.move_point.point].z -= op->data.move_point.delta_z;
audio_play("/data/drop.ogg", NULL, false, 0.4f, 0.0f);
}
static void reverse_operation_set_texture(Operation *op) {
SDL_assert(op->kind == OPERATION_SET_TEXTURE);
Object *o = &state.objects[op->data.set_texture.object];
o->faces[op->data.set_texture.face].texture += op->data.set_texture.delta_texture;
audio_play("/data/drop.ogg", NULL, false, 0.4f, 0.0f);
}
static void reverse_triangulation(Operation *op) {
SDL_assert(op->kind == OPERATION_TRIANGULATE);
Object *o = &state.objects[op->data.set_texture.object];
Face *fn = &o->faces[op->data.triangulate.new_face];
Face *fo = &o->faces[op->data.triangulate.old_face];
fo->p[3] = fo->p[2];
fo->p[2] = fn->p[1];
pop_face(op->data.set_texture.object, op->data.triangulate.new_face);
}
/* TODO: reverse of this */
static void try_subdividing_from_moving(uint8_t object, uint16_t point) {
Object *o = &state.objects[object];
bool not_first = false;
for (uint16_t fi = 0; fi < o->faces_sz; ++fi) {
Face *f = &o->faces[fi];
if (f->p[3] == INVALID_POINT) continue;
for (uint16_t pi = 0; pi < 4; ++pi) {
if (f->p[pi] == point) {
Face new0 = *f;
new0.p[0] = f->p[pi];
new0.p[1] = f->p[(pi + 1) % 4];
new0.p[2] = f->p[(pi + 3) % 4];
new0.p[3] = INVALID_POINT;
uint16_t newf = push_face(
object,
f->p[(pi + 1) % 4],
f->p[(pi + 2) % 4],
f->p[(pi + 3) % 4],
INVALID_POINT,
f->texture,
f->tex_scale,
f->tex_x,
f->tex_y);
*f = new0;
extend_operation((Operation){
.kind = OPERATION_TRIANGULATE,
.data = { .triangulate = { .new_face = newf, .old_face = fi, .object = object} },
.chained = not_first,
});
not_first = true;
}
}
}
}
static void process_undo(void) {
if (state.op_active)
state.op_active = false;
/* TODO: checks and defined limit */
Operation *op = &state.op_stack[state.op_stack_ptr % UNDO_STACK_SIZE];
state.op_stack_ptr--;
switch (op->kind) {
case OPERATION_MOVE_POINT: {
reverse_operation_move_point(op);
break;
}
case OPERATION_SET_TEXTURE: {
reverse_operation_set_texture(op);
break;
}
case OPERATION_TRIANGULATE: {
reverse_triangulation(op);
break;
}
default:
(void)0;
}
/* pop another if they're chained together */
if (op->chained) process_undo();
}
static void process_operations(void) {
if (input_action_just_pressed("undo"))
process_undo();
if (!state.op_active) {
/* point dragging */
if (!state.current_texture) {
uint16_t point_select; uint8_t obj_select;
if (find_closest_point(&obj_select, &point_select)) {
draw_billboard("/data/point.png",
point_to_vec3(obj_select, point_select),
(Vec2){0.05f, 0.05f},
(Rect){0},
(Color){255,255,255,255},
false);
if (input_action_just_pressed("select"))
push_operation((Operation){
.kind = OPERATION_MOVE_POINT,
.data = { .move_point = { .point = point_select, .object = obj_select } },
}, true );
}
/* texture setting */
} else {
uint8_t obj_select; uint16_t face_select;
if (find_closest_face(&obj_select, &face_select)) {
state.current_hovered_face = face_select;
state.current_hovered_obj = obj_select;
if (input_action_just_pressed("rotate")) {
Face *f = &state.objects[obj_select].faces[face_select];
int16_t tex_x = f->tex_x;
int16_t tex_y = f->tex_y;
f->tex_x = -tex_y;
f->tex_y = tex_x;
}
if (input_action_pressed("select") && state.current_texture) {
uint8_t new_tex = push_texture(obj_select, state.current_texture);
uint8_t cur_tex = state.objects[obj_select].faces[face_select].texture;
if (new_tex != cur_tex) {
state.objects[obj_select].faces[face_select].texture = new_tex;
audio_play("/data/bong.ogg", NULL, false, 0.5f, 0.0f);
push_operation((Operation){
.kind = OPERATION_SET_TEXTURE,
.data = { .set_texture = {
.face = face_select,
.object = obj_select,
.delta_texture = cur_tex - new_tex,
}},
}, false );
}
}
}
}
}
if (state.op_active) {
Operation *op = &state.op_stack[state.op_stack_ptr % UNDO_STACK_SIZE];
switch (op->kind) {
case OPERATION_MOVE_POINT: {
bool update = process_operation_move_point(op);
if (update)
try_subdividing_from_moving(op->data.move_point.object, op->data.move_point.point);
break;
}
case OPERATION_SET_TEXTURE:
case OPERATION_TRIANGULATE:
default:
(void)0;
}
}
}
static void draw_axes(void) {
/* axis helpers */
/* idea: double selection of axes for diagonal edits */
draw_line_3d(state.active_center, (Vec3){256,0,0}, 1, state.axis_mask[0] ? (Color){0,0,0,75} : (Color){255,0,0,125});
draw_billboard("/data/x.png",
vec3_add(state.active_center, (Vec3){2,0,0}),
(Vec2){0.1f, 0.1f},
(Rect){0},
state.axis_mask[0] ? (Color){0,0,0,255} : (Color){255,0,0,255},
false);
draw_line_3d(state.active_center, (Vec3){0,256,0}, 1, state.axis_mask[1] ? (Color){0,0,0,75} : (Color){75,125,25,125});
draw_billboard("/data/y.png",
vec3_add(state.active_center, (Vec3){0,1.5f,0}),
(Vec2){0.1f, 0.1f},
(Rect){0},
state.axis_mask[1] ? (Color){0,0,0,255} : (Color){75,125,25,255},
false);
draw_line_3d(state.active_center, (Vec3){0,0,256}, 1, state.axis_mask[2] ? (Color){0,0,0,75} : (Color){0,0,255,125});
draw_billboard("/data/z.png",
vec3_add(state.active_center, (Vec3){0,0,2}),
(Vec2){0.1f, 0.1f},
(Rect){0},
state.axis_mask[2] ? (Color){0,0,0,255} : (Color){0,0,255,255},
false);
}
static void draw_hovered_face_border(void) {
if (state.current_hovered_obj == INVALID_OBJECT || state.current_hovered_face == INVALID_FACE)
return;
Object *o = &state.objects[state.current_hovered_obj];
Face *f = &o->faces[state.current_hovered_face];
if (f->p[3] != INVALID_POINT) {
Vec3 p0 = point_to_vec3(state.current_hovered_obj, f->p[0]);
Vec3 p1 = point_to_vec3(state.current_hovered_obj, f->p[1]);
Vec3 p2 = point_to_vec3(state.current_hovered_obj, f->p[2]);
Vec3 p3 = point_to_vec3(state.current_hovered_obj, f->p[3]);
Vec3 center = vec3_scale(vec3_add(p2, p0), 0.5);
float size = sinf(ctx.frame_number / 10) * 0.05f + 0.1f;
Vec3 c0 = vec3_add(p0, vec3_scale(vec3_sub(p0, center), size));
Vec3 c1 = vec3_add(p1, vec3_scale(vec3_sub(p1, center), size));
Vec3 c2 = vec3_add(p2, vec3_scale(vec3_sub(p2, center), size));
Vec3 c3 = vec3_add(p3, vec3_scale(vec3_sub(p3, center), size));
draw_line_3d(c0, c1, 1, (Color){255,255,255,255});
draw_line_3d(c1, c2, 1, (Color){255,255,255,255});
draw_line_3d(c2, c3, 1, (Color){255,255,255,255});
draw_line_3d(c3, c0, 1, (Color){255,255,255,255});
} else {
Vec3 p0 = point_to_vec3(state.current_hovered_obj, f->p[0]);
Vec3 p1 = point_to_vec3(state.current_hovered_obj, f->p[1]);
Vec3 p2 = point_to_vec3(state.current_hovered_obj, f->p[2]);
Vec3 center = vec3_scale(vec3_add(p0, vec3_add(p1, p2)), 0.33f);
float size = sinf(ctx.frame_number / 10) * 0.05f + 0.1f;
Vec3 c0 = vec3_add(p0, vec3_scale(vec3_sub(p0, center), size));
Vec3 c1 = vec3_add(p1, vec3_scale(vec3_sub(p1, center), size));
Vec3 c2 = vec3_add(p2, vec3_scale(vec3_sub(p2, center), size));
draw_line_3d(c0, c1, 1, (Color){255,255,255,255});
draw_line_3d(c1, c2, 1, (Color){255,255,255,255});
draw_line_3d(c2, c0, 1, (Color){255,255,255,255});
}
}
static void display_texture_selection(void) {
String list = file_read("/data/assets/", ":images");
if (!list.data)
return;
draw_rectangle((Rect){0, 480 - 50, 640, 480}, (Color){0,0,0,126});
char *selected = NULL;
char *saveptr = NULL;
int count = 0;
char const *part = SDL_strtokr(list.data, "\n", &saveptr);
do {
Rect box = (Rect){(float)count * 50, 480 - 48, 48, 48};
draw_sprite(part,
box,
(Rect){0,0,64,64},
(Color){255,255,255,255},
0, false, false, true);
count++;
if (state.current_texture && SDL_strcmp(part, state.current_texture) == 0)
draw_box(box, 1, (Color){255,255,255,255});
if (rect_intersects(box, (Rect){ctx.mouse_position.x, ctx.mouse_position.y, 1, 1}))
selected = SDL_strdup(part);
} while ((part = SDL_strtokr(NULL, "\n", &saveptr)));
if (selected) {
draw_text(selected, (Vec2){0, 480 - 48 - 24}, 24, (Color){255,255,255,255}, NULL);
if (input_action_just_pressed("select")) {
if (state.current_texture) SDL_free(state.current_texture);
state.current_texture = selected;
} else
SDL_free(selected);
}
}
static void process_camera_inputs(void) {
if (input_action_just_pressed("toggle_display_mode")) {
audio_play("/data/click.wav", NULL, false, 0.7f, 0.0f);
state.solid_display_mode = !state.solid_display_mode;
if (state.current_texture) {
SDL_free(state.current_texture);
state.current_texture = NULL;
}
}
if (input_action_just_pressed("toggle_projection")) {
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
state.camera_is_orthographic = !state.camera_is_orthographic;
}
if (input_action_just_pressed("toggle_x_axis")) {
state.axis_mask[0] = 0; state.axis_mask[1] = 1; state.axis_mask[2] = 1;
}
if (input_action_just_pressed("toggle_y_axis")) {
state.axis_mask[0] = 1; state.axis_mask[1] = 0; state.axis_mask[2] = 1;
}
if (input_action_just_pressed("toggle_z_axis")) {
state.axis_mask[0] = 1; state.axis_mask[1] = 1; state.axis_mask[2] = 0;
}
/* TODO: determine bounding box for this? */
/* TODO: no idea whether it's all correct in terms of directions. */
if (input_action_just_pressed("camera_front")) {
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
state.camera_is_orthographic = true;
state.camera_direction = (Vec3){0,0,-1};
state.camera_position = vec3_add(state.active_center, (Vec3){0,0,2});
}
if (input_action_just_pressed("camera_back")) {
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
state.camera_is_orthographic = true;
state.camera_direction = (Vec3){0,0,1};
state.camera_position = vec3_add(state.active_center, (Vec3){0,0,-2});
}
if (input_action_just_pressed("camera_left")) {
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
state.camera_is_orthographic = true;
state.camera_direction = (Vec3){1,0,0};
state.camera_position = vec3_add(state.active_center, (Vec3){-2,0,0});
}
if (input_action_just_pressed("camera_right")) {
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
state.camera_is_orthographic = true;
state.camera_direction = (Vec3){-1,0,0};
state.camera_position = vec3_add(state.active_center, (Vec3){2,0,0});
}
/* TODO: broken */
if (input_action_just_pressed("camera_above")) {
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
state.camera_is_orthographic = true;
state.camera_direction = (Vec3){0,-1,0};
state.camera_position = vec3_add(state.active_center, (Vec3){0,2,0});
}
/* TODO: broken */
if (input_action_just_pressed("camera_below")) {
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
state.camera_is_orthographic = true;
state.camera_direction = (Vec3){0,1,0};
state.camera_position = vec3_add(state.active_center, (Vec3){0,-2,0});
}
}
void game_tick(void) {
if (!init) {
/* default state */
new_cube((Point){0,0,0}, (Point){POINTS_PER_METER,POINTS_PER_METER,POINTS_PER_METER});
state.camera_position = (Vec3){2,1,2};
state.camera_direction = vec3_norm(((Vec3){-2,-1,-2}));
state.camera_zoom = 0.5f;
state.grid_snap_granularity = 16;
state.axis_mask[1] = 1; state.axis_mask[2] = 1;
init = true;
}
state.current_hovered_face = INVALID_FACE;
state.current_hovered_obj = INVALID_OBJECT;
input_action("toggle_display_mode", "Q");
input_action("toggle_projection", "TAB");
input_action("toggle_x_axis", "Z");
input_action("toggle_y_axis", "X");
input_action("toggle_z_axis", "C");
input_action("select", "LCLICK");
input_action("rotate", "R");
input_action("undo", "F");
input_action("camera_front", "KP0");
input_action("camera_back", "KP1");
input_action("camera_left", "KP2");
input_action("camera_right", "KP3");
input_action("camera_above", "KP4");
input_action("camera_below", "KP5");
process_camera_inputs();
process_camera_movement();
process_operations();
/* helpres */
draw_axes();
draw_hovered_face_border();
for (uint8_t obj = 0; obj < state.objects_sz; ++obj)
render_object(obj);
if (state.solid_display_mode)
display_texture_selection();
draw_text("twndel\x03", (Vec2){0, 2}, 32, (Color){255,255,255,200}, NULL);
draw_camera(
state.camera_position,
state.camera_direction,
(Vec3){0, 1, 0},
state.camera_is_orthographic ? 0 : CAMERA_FOV,
state.camera_zoom,
100
);
}
void game_end(void) {
for (uint8_t obj = 0; obj < state.objects_sz; ++obj) {
Object *o = &state.objects[obj];
for (uint8_t t = 0; t < o->textures_sz; ++t)
SDL_free(o->textures[t]);
SDL_free(o->name);
}
if (state.current_texture) {
SDL_free(state.current_texture);
state.current_texture = NULL;
}
}

View File

@ -1 +1,2 @@
luabind.c luabind.c
data/scripts/twnapi.lua

View File

@ -1,86 +1,36 @@
cmake_minimum_required(VERSION 3.21) cmake_minimum_required(VERSION 3.30)
project(twnlua LANGUAGES C) project(twnlua LANGUAGES C)
find_package(Python3 COMPONENTS Interpreter)
if(NOT CMAKE_BUILD_TYPE) if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug) set(CMAKE_BUILD_TYPE Debug)
endif() endif()
add_subdirectory($ENV{TWNROOT} $ENV{TWNROOT}/build) add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/luabind.c OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/game.c
COMMAND ${PYTHON3} ${CMAKE_CURRENT_SOURCE_DIR}/bindgen.py $ENV{TWNROOT}/share/twn_api.json > ${CMAKE_CURRENT_SOURCE_DIR}/luabind.c COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/bindgen.py $ENV{TWNROOT}/share/twn_api.json ${FLAGS} > ${CMAKE_CURRENT_SOURCE_DIR}/luabind.c
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bindgen.py $ENV{TWNROOT}/share/twn_api.json DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bindgen.py $ENV{TWNROOT}/share/twn_api.json
) )
add_custom_command(
OUTPUT ${TWN_OUT_DIR}/data/scripts/twnapi.lua
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/docgen.py $ENV{TWNROOT}/share/twn_api.json > ${TWN_OUT_DIR}/data/scripts/twnapi.lua
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/docgen.py $ENV{TWNROOT}/share/twn_api.json
)
add_custom_target(
twnlua_docgen ALL
DEPENDS ${TWN_OUT_DIR}/data/scripts/twnapi.lua
)
add_compile_definitions(LUA_32BITS=1)
set(SOURCE_FILES set(SOURCE_FILES
game.c game.c
state.h minilua.c
${CMAKE_CURRENT_SOURCE_DIR}/luabind.c
lua/src/lapi.c
lua/src/lapi.h
lua/src/lauxlib.c
lua/src/lauxlib.h
lua/src/lbaselib.c
lua/src/lcode.c
lua/src/lcode.h
lua/src/lcorolib.c
lua/src/lctype.c
lua/src/lctype.h
lua/src/ldblib.c
lua/src/ldebug.c
lua/src/ldebug.h
lua/src/ldo.c
lua/src/ldo.h
lua/src/ldump.c
lua/src/lfunc.c
lua/src/lfunc.h
lua/src/lgc.c
lua/src/lgc.h
lua/src/linit.c
lua/src/liolib.c
lua/src/ljumptab.h
lua/src/llex.c
lua/src/llex.h
lua/src/llimits.h
lua/src/lmathlib.c
lua/src/lmem.c
lua/src/lmem.h
lua/src/loadlib.c
lua/src/lobject.c
lua/src/lobject.h
lua/src/lopcodes.c
lua/src/lopcodes.h
lua/src/lopnames.h
lua/src/loslib.c
lua/src/lparser.c
lua/src/lparser.h
lua/src/lprefix.h
lua/src/lstate.c
lua/src/lstate.h
lua/src/lstring.c
lua/src/lstring.h
lua/src/lstrlib.c
lua/src/ltable.c
lua/src/ltable.h
lua/src/ltablib.c
lua/src/ltm.c
lua/src/ltm.h
lua/src/lua.h
lua/src/luaconf.h
lua/src/lualib.h
lua/src/lundump.c
lua/src/lundump.h
lua/src/lutf8lib.c
lua/src/lvm.c
lua/src/lvm.h
lua/src/lzio.c
lua/src/lzio.h
) )
use_townengine("${SOURCE_FILES}" ${TWN_OUT_DIR})
use_townengine(${PROJECT_NAME} "${SOURCE_FILES}" ${CMAKE_SOURCE_DIR})

62
apps/twnlua/README.md Normal file
View File

@ -0,0 +1,62 @@
# MiniLua
This is Lua contained in a single header to be bundled in C/C++ applications with ease.
[Lua](https://www.lua.org/) is a powerful, efficient, lightweight, embeddable scripting language.
## Example Usage
```c
#define LUA_IMPL
#include "minilua.h"
int main() {
lua_State *L = luaL_newstate();
if(L == NULL)
return -1;
luaL_openlibs(L);
luaL_loadstring(L, "print 'hello world'");
lua_call(L, 0, 0);
lua_close(L);
return 0;
}
```
## Usage
Copy `minilua.h` into your C or C++ project, include it anywhere you want to use Lua API.
Then do the following in *one* C file to implement Lua:
```c
#define LUA_IMPL
#include "minilua.h"
```
By default it detects the system platform to use, however you can explicitly define one.
Note that almost no modification was made in the Lua implementation code,
thus there are some C variable names that may collide with your code,
therefore it is best to declare the Lua implementation in dedicated C file.
Optionally provide the following defines:
- `LUA_MAKE_LUA` - implement the Lua command line REPL
## Documentation
For documentation on how to use Lua read its [official manual](https://www.lua.org/manual/).
## Updates
- **25-Jul-2024**: Updated to Lua 5.4.7.
- **13-Nov-2023**: Updated to Lua 5.4.6.
- **28-Jan-2022**: Updated to Lua 5.4.4.
- **31-Mar-2021**: Updated to Lua 5.4.3.
- **03-Dec-2020**: Updated to Lua 5.4.2.
- **27-Nov-2020**: Library created, using Lua 5.4.2-rc1.
## Notes
This library tries to keep up with latest official Lua release.
The header is generated using the bash script `gen.sh` all modifications done is there.
## License
Same license as Lua, the MIT license, see LICENSE.txt for information.

View File

@ -11,7 +11,8 @@ api = json.loads(api_source)
def default(parameter): def default(parameter):
basetype = parameter["type"].rsplit(' *', 1)[0] basetype = parameter["type"].rsplit(' *', 1)[0]
if parameter["type"] == "float": if parameter["type"] == "float":
return parameter["default"] s = str(parameter["default"])
return s + 'f' if '.' in s else s + '.f'
elif parameter["type"] == "bool": elif parameter["type"] == "bool":
return "true" if parameter["default"] else "false" return "true" if parameter["default"] else "false"
elif parameter["type"] == "char *": elif parameter["type"] == "char *":
@ -35,7 +36,7 @@ def to_table(typedesc, variable, indent = 0):
binding = ' ' * indent + "lua_createtable(L, 0, %i);\n" % len(typedesc["fields"]) binding = ' ' * indent + "lua_createtable(L, 0, %i);\n" % len(typedesc["fields"])
for field in typedesc["fields"]: for field in typedesc["fields"]:
if field["type"] == "float" or field["type"] == "uint8_t": if field["type"] == "float" or field["type"] == "uint8_t":
binding += ' ' * indent + "lua_pushnumber(L, (double)(%s));\n" % (variable + ".%s" % field["name"]) binding += ' ' * indent + "lua_pushnumber(L, (float)(%s));\n" % (variable + ".%s" % field["name"])
elif field["type"] == "bool": elif field["type"] == "bool":
binding += ' ' * indent + "lua_pushboolean(L, (%s));\n" % (variable + ".%s" % field["name"]) binding += ' ' * indent + "lua_pushboolean(L, (%s));\n" % (variable + ".%s" % field["name"])
elif field["type"] in api["types"]: elif field["type"] in api["types"]:
@ -51,7 +52,7 @@ def from_table(typedesc, variable, indent = 0):
for field in typedesc["fields"]: for field in typedesc["fields"]:
binding += ' ' * indent + "lua_getfield(L, -1, \"%s\");\n" % field["name"] binding += ' ' * indent + "lua_getfield(L, -1, \"%s\");\n" % field["name"]
if field["type"] == "float" or field["type"] == "uint8_t": if field["type"] == "float" or field["type"] == "uint8_t":
binding += ' ' * indent + "%s = (%s)lua_tonumber(L, -1);\n" % (variable + ".%s" % field["name"], field["type"]) binding += ' ' * indent + "%s = (%s)(lua_tonumber(L, -1));\n" % (variable + ".%s" % field["name"], field["type"])
elif field["type"] == "bool": elif field["type"] == "bool":
binding += ' ' * indent + "%s = lua_toboolean(L, -1);\n" % (variable + ".%s" % field["name"]) binding += ' ' * indent + "%s = lua_toboolean(L, -1);\n" % (variable + ".%s" % field["name"])
elif field["type"] in api["types"]: elif field["type"] in api["types"]:
@ -63,12 +64,7 @@ def from_table(typedesc, variable, indent = 0):
print('#include "twn_game_api.h"\n') print('#include "twn_game_api.h"\n')
# TODO: reuse implementation from the engine, this also breaks with statically compiled build print('/* assumed to be included from game.c, where minilua.h is already present */\n')
print('#define STB_DS_IMPLEMENTATION')
print('#include <stb_ds.h>')
print('#include <lua.h>')
print('#include <lualib.h>')
print('#include <lauxlib.h>\n')
bindings, used_converters = [], {} bindings, used_converters = [], {}
for procedure, procedure_desc in api["procedures"].items(): for procedure, procedure_desc in api["procedures"].items():
@ -84,7 +80,7 @@ for procedure, procedure_desc in api["procedures"].items():
binding += " %s %s;\n" % (parameter["type"] if not parameter["type"].endswith("*") else 'const ' + parameter["type"], parameter["name"]) binding += " %s %s;\n" % (parameter["type"] if not parameter["type"].endswith("*") else 'const ' + parameter["type"], parameter["name"])
binding += " lua_getfield(L, 1, \"%s\");\n" % parameter["name"] binding += " lua_getfield(L, 1, \"%s\");\n" % parameter["name"]
if "default" in parameter and parameter["type"] != "float": if "default" in parameter and not parameter["type"] in ("float", "bool"):
binding += " if (lua_isnoneornil(L, -1))\n" binding += " if (lua_isnoneornil(L, -1))\n"
binding += " %s = %s;\n" % (parameter["name"], default(parameter)) binding += " %s = %s;\n" % (parameter["name"], default(parameter))
binding += " else\n " binding += " else\n "
@ -92,10 +88,10 @@ for procedure, procedure_desc in api["procedures"].items():
if parameter["type"] == "float": if parameter["type"] == "float":
if "default" in parameter: if "default" in parameter:
binding += " int is_%s_num;\n" % parameter["name"] binding += " int is_%s_num;\n" % parameter["name"]
binding += " %s = (float)lua_tonumberx(L, -1, &is_%s_num);\n" % (parameter["name"], parameter["name"]); binding += " %s = (float)(lua_tonumberx(L, -1, &is_%s_num));\n" % (parameter["name"], parameter["name"]);
binding += " if (!is_%s_num) %s = %s;\n" % (parameter["name"], parameter["name"], default(parameter)) binding += " if (!is_%s_num) %s = %s;\n" % (parameter["name"], parameter["name"], default(parameter))
else: else:
binding += " %s = (float)lua_tonumber(L, -1);\n" % (parameter["name"]); binding += " %s = (float)(lua_tonumber(L, -1));\n" % (parameter["name"]);
elif parameter["type"] == "bool": elif parameter["type"] == "bool":
binding += " %s = lua_toboolean(L, -1);\n" % (parameter["name"]); binding += " %s = lua_toboolean(L, -1);\n" % (parameter["name"]);
elif parameter["type"] == "char *": elif parameter["type"] == "char *":
@ -113,11 +109,13 @@ for procedure, procedure_desc in api["procedures"].items():
if procedure_desc["return"] == "bool": if procedure_desc["return"] == "bool":
binding += " lua_pushboolean(L, (int)(%s(%s)));\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"])) binding += " lua_pushboolean(L, (int)(%s(%s)));\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
elif procedure_desc["return"] == "float": elif procedure_desc["return"] == "float":
binding += " lua_pushnumber(L, (double)(%s(%s)));\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"])) binding += " lua_pushnumber(L, (float)(%s(%s)));\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
elif procedure_desc["return"] == "char *": elif procedure_desc["return"] == "char *":
binding += " lua_pushstring(L, %s(%s));\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"])) binding += " lua_pushstring(L, %s(%s));\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
elif procedure_desc["return"] == "String":
binding += " String result = %s(%s);\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
binding += " lua_pushlstring(L, result.data, (int)result.length);\n"
elif type(procedure_desc["return"]) is dict or procedure_desc["return"] in api["types"]: elif type(procedure_desc["return"]) is dict or procedure_desc["return"] in api["types"]:
# TODO: handle enums
type_desc = procedure_desc["return"] if type(procedure_desc["return"]) is dict else api["types"][procedure_desc["return"]] type_desc = procedure_desc["return"] if type(procedure_desc["return"]) is dict else api["types"][procedure_desc["return"]]
binding += " %s result = %s(%s);\n" % (type_desc["c_type"], procedure, ", ".join(param["name"] for param in procedure_desc["params"])) binding += " %s result = %s(%s);\n" % (type_desc["c_type"], procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
binding += to_table(type_desc, "result", 4) binding += to_table(type_desc, "result", 4)
@ -141,45 +139,27 @@ for typename, typedesc in used_converters.items():
converter += " %s %s;\n" % (typename, typename.lower()); converter += " %s %s;\n" % (typename, typename.lower());
if "fields" in typedesc: if "fields" in typedesc:
for field in typedesc["fields"]: for idx, field in enumerate(typedesc["fields"]):
converter += " lua_getfield(L, -1, \"%s\");\n" % (field["name"]); converter += " lua_getfield(L, -%i, \"%s\");\n" % (idx + 1, field["name"]);
if field["type"] == "float": if field["type"] == "float":
converter += " %s.%s = (float)lua_tonumber(L, -1);\n" % (typename.lower(), field["name"]); converter += " %s.%s = (float)lua_tonumber(L, -1);\n" % (typename.lower(), field["name"]);
elif field["type"] == "uint8_t": elif field["type"] == "uint8_t":
converter += " %s.%s = (uint8_t)lua_tointeger(L, -1);\n" % (typename.lower(), field["name"]); converter += " %s.%s = (uint8_t)lua_tointeger(L, -1);\n" % (typename.lower(), field["name"]);
else: else:
raise BaseException("Unhandled converter field type '%s'" % (field["type"])) raise BaseException("Unhandled converter field type '%s'" % (field["type"]))
converter += " lua_pop(L, 1);\n"; converter += " lua_pop(L, %i);\n" % len(typedesc["fields"]);
# TODO: wild idea: use compile time built hash table
elif "enums" in typedesc:
storages += ["struct %sHashItem { char *key; %s value; };\nstatic struct %sHashItem *%s_map = NULL;\n" % (typename, typename, typename, typename.lower())]
# TODO: use arena
for enum in typedesc["enums"]:
initializer = " shput(%s_map, \"%s\", %s);" % (typename.lower(), enum, typedesc["enums"][enum])
initializers += [initializer]
deinitializers += [" shfree(%s_map);" % typename.lower()]
converter += " char const *value = lua_tostring(L, -1);\n";
converter += " %s = shget(%s_map, value);\n" % (typename.lower(), typename.lower())
converter += " lua_pop(L, 1);\n";
converter += " return %s;\n}\n" % (typename.lower()) converter += " return %s;\n}\n" % (typename.lower())
converters += [converter] converters += [converter]
print('\n'.join(storages)) print('\n'.join(storages))
print("extern void bindgen_init(void);\n")
print("void bindgen_init(void) {\n" + '\n'.join(initializers) + "\n}\n")
print('\n'.join(converters)) print('\n'.join(converters))
print('\n'.join(bindings)) print('\n'.join(bindings))
loader = "extern void bindgen_load_%s(lua_State *L);\n" % api["name"] loader = "extern void bindgen_load_%s(lua_State *L);\n" % api["name"]
loader += "void bindgen_load_%s(lua_State *L) {\n" % api["name"] loader += "void bindgen_load_%s(lua_State *L) {\n" % api["name"]
loader += " bindgen_init();\n"
for procedure, procedure_desc in api["procedures"].items(): for procedure, procedure_desc in api["procedures"].items():
loader += " lua_pushcfunction(L, binding_%s);\n" % procedure loader += " lua_pushcfunction(L, binding_%s);\n" % procedure
loader += " lua_setglobal(L, \"%s\");\n" % procedure loader += " lua_setglobal(L, \"%s\");\n" % procedure

View File

@ -1,3 +1,3 @@
[[deps]] [[deps]]
source = "../../common-data" # where does it come from, might be an url source = "../../data" # where does it come from, might be an url
name = "common-data" # should be globally unique name = "common-data" # should be globally unique

View File

@ -4,16 +4,39 @@ offset = { x = 0, y = 0 }
angle = 0 angle = 0
function game_tick() function game_tick()
if ctx.udata == nil then
ctx.udata = {
frame_count = 0,
nest = {
frame_count = 0,
},
arr = { [0] = 0 },
}
end
draw_text {
string = tostring(ctx.udata.frame_count),
position = { x = 0, y = 0 },
font = "/fonts/kenney-pixel.ttf",
}
draw_text {
string = tostring(ctx.udata.nest.frame_count),
position = { x = 0, y = 14 },
font = "/fonts/kenney-pixel.ttf",
}
draw_text {
string = tostring(ctx.udata.arr[0]),
position = { x = 0, y = 28 },
font = "/fonts/kenney-pixel.ttf",
}
input_action { input_action {
name = "press", name = "press",
control = "A" control = "A"
} }
draw_rectangle {
rect = { x = 0, y = 0, w = 640, h = 360 },
color = { r = 127, g = 0, b = 127, a = 255 },
}
draw_sprite { draw_sprite {
texture = "/assets/title.png", texture = "/assets/title.png",
rect = { rect = {
@ -32,6 +55,10 @@ function game_tick()
} }
end end
ctx.udata.frame_count = ctx.udata.frame_count + 1
ctx.udata.nest.frame_count = ctx.udata.nest.frame_count + 1
ctx.udata.arr[0] = ctx.udata.arr[0] + 1
offset.x = ORIGIN.x + (math.cos(angle) * RADIUS) offset.x = ORIGIN.x + (math.cos(angle) * RADIUS)
offset.y = ORIGIN.y + (math.sin(angle) * RADIUS) offset.y = ORIGIN.y + (math.sin(angle) * RADIUS)
angle = angle + 0.1 angle = angle + 0.1

View File

@ -6,5 +6,6 @@ dev_id = "somebody"
[game] [game]
resolution = [ 640, 360 ] resolution = [ 640, 360 ]
background_color = [ 127, 0, 127, 255 ]
[engine] [engine]

55
apps/twnlua/docgen.py Executable file
View File

@ -0,0 +1,55 @@
#!/bin/env python3
import sys, json
with open(sys.argv[1], 'r') if sys.argv[1] != "-" else sys.stdin as f:
api_source = f.read()
api = json.loads(api_source)
def to_lua_type_annot(typedesc):
if type(typedesc) is dict:
return r'{ %s }' % ','.join('%s: %s' % (f["name"], to_lua_type_annot(f["type"])) for f in typedesc["fields"])
basetype = typedesc.rsplit(' *', 1)[0]
if typedesc == "char *":
return "string"
elif basetype == "float":
return "number"
elif basetype == "bool":
return "boolean"
elif basetype == "Vec2":
return r"{ x: number, y: number }"
elif basetype == "Vec3":
return r"{ x: number, y: number, z: number }"
elif basetype == "Color":
return r"{ r: number, g: number, b: number, a: number }"
elif basetype == "Rect":
return r"{ x: number, y: number, w: number, h: number }"
else:
return "unknown"
# raise BaseException("Unhandled type for annotation: %s" % typedesc)
print("---@meta twn")
print("---@diagnostic disable")
type_annotations = {}
type_annotations["ctx"] = r"{ %s, udata: table }" % \
', '.join("%s: %s" % (f["name"], to_lua_type_annot(f["type"])) for f in api["types"]["Context"]["fields"])
for annot in type_annotations:
print("---@type " + type_annotations[annot])
print(r"%s = nil" % annot)
procedure_annotations = {}
for procedure, procedure_desc in api["procedures"].items():
procedure_annotations[procedure] = {}
procedure_annotations[procedure]["params"] = r"{ %s }" % \
', '.join("%s: %s" % (p["name"], to_lua_type_annot(p["type"]) + '?' * ("default" in p)) for p in procedure_desc["params"])
if "return" in procedure_desc:
procedure_annotations[procedure]["return"] = to_lua_type_annot(procedure_desc["return"])
for annot in procedure_annotations:
print("---@param args " + procedure_annotations[annot]["params"])
if "return" in procedure_annotations[annot]:
print("---@return " + procedure_annotations[annot]["return"])
print("function %s(args) end" % annot)

View File

@ -1,13 +1,14 @@
#include "twn_game_api.h" #include "twn_game_api.h"
/* TODO: actually move it back it its own file, it doesn't give any compilation benefits */
#include "minilua.h"
#include "state.h" #include "state.h"
#include "luabind.c"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <lua.h> #define UDATA_NESTING_LIMIT 128
#include <lualib.h>
#include <lauxlib.h>
#include <malloc.h>
/* generated by bindgen.py */ /* generated by bindgen.py */
@ -18,29 +19,49 @@ void bindgen_upload_context(lua_State *L);
/* require will go through physicsfs exclusively so that scripts can be in the data dir */ /* require will go through physicsfs exclusively so that scripts can be in the data dir */
/* TODO: allow for bytecode files */
/* TODO: support .lua suffixes files? */
static int physfs_loader(lua_State *L) { static int physfs_loader(lua_State *L) {
const char *name = luaL_checkstring(L, 1); const char *name = luaL_checkstring(L, 1);
char *final_path = NULL;
SDL_asprintf(&final_path, "%s.lua", name);
if (!file_exists(final_path)) { static const char *name_breaker = NULL;
if (name_breaker && SDL_strcmp(name, name_breaker) == 0) {
log_string(name_breaker, "Recursive load on itself from lua module");
return 0;
} name_breaker = name;
/* replace dots with path slashes */
char *path_copy = SDL_strdup(name);
char *ch = NULL;
while ((ch = SDL_strchr(path_copy, '.')))
*ch = '/';
char *final_path = NULL;
SDL_asprintf(&final_path, "/scripts/%s.lua", path_copy);
SDL_free(path_copy);
if (SDL_strcmp(file_read(final_path, ":exists").data, "yes") == 0) {
char *error_message = NULL; char *error_message = NULL;
SDL_asprintf(&error_message, "could not find module %s in filesystem", name); SDL_asprintf(&error_message, "could not find module %s in filesystem", name);
lua_pushstring(L, error_message); lua_pushstring(L, error_message);
free(error_message); SDL_free(error_message);
free(final_path); SDL_free(final_path);
return 1; return 1;
} }
unsigned char *buf = NULL; char *final_path_binary = NULL;
int64_t buf_size = file_to_bytes(final_path, &buf); SDL_asprintf(&final_path_binary, "%s%s", final_path, ":binary");
free(final_path);
luaL_loadbuffer(L, (char *)buf, buf_size, name); String file = file_read(final_path, ":binary");
free(buf); SDL_free(final_path);
return 1; /* TODO: use reader interface for streaming instead */
int const result = luaL_loadbuffer(L, file.data, (size_t)file.length, name);
if (result != LUA_OK)
log_string(lua_tostring(L, -1), NULL);
return result == LUA_OK;
} }
@ -87,21 +108,76 @@ static void *custom_alloc(void *ud, void *ptr, size_t osize, size_t nsize) {
} }
static void exchange_lua_states(lua_State *from, lua_State *to, int level, int index) {
if (level >= UDATA_NESTING_LIMIT) {
log_string("ctx.udata nesting limit is reached", NULL);
return;
}
/* TODO: use arrays for optimized paths */
/* TODO: preallocate table records */
switch (lua_type(from, index)) {
case LUA_TTABLE:
lua_newtable(to);
lua_pushnil(from); /* first key */
while (lua_next(from, index - 1) != 0) {
/* 'key' at index -2 and 'value' at index -1 */
exchange_lua_states(from, to, level + 1, -2);
exchange_lua_states(from, to, level + 1, -1);
lua_settable(to, index - 2);
/* removes 'value'; keeps 'key' for next iteration */
lua_pop(from, 1);
}
break;
case LUA_TNUMBER:
lua_pushnumber(to, lua_tonumber(from, index));
break;
case LUA_TBOOLEAN:
lua_pushboolean(to, lua_toboolean(from, index));
break;
case LUA_TSTRING:
lua_pushstring(to, lua_tostring(from, index));
break;
case LUA_TNIL:
lua_pushnil(to);
break;
default:
/* TODO: provide a path and type of it for better diagnostic */
log_string("Unserializable udata found and is ignored", NULL);
break;
}
}
void game_tick(void) { void game_tick(void) {
if (ctx.initialization_needed) { if (ctx.initialization_needed) {
if (!ctx.udata) if (!ctx.udata)
ctx.udata = ccalloc(1, sizeof (State)); ctx.udata = SDL_calloc(1, sizeof (State));
State *state = ctx.udata; State *state = ctx.udata;
/* let's init lua */ /* let's init lua */
/* state existed already */ lua_State *new_state = lua_newstate(custom_alloc, NULL);
lua_setallocf(new_state, custom_alloc, NULL);
/* state existed already, copy its udata over */
if (state->L != NULL) { if (state->L != NULL) {
lua_getglobal(state->L, "ctx");
lua_getfield(state->L, -1, "udata");
SDL_assert(!lua_isnoneornil(state->L, -1));
SDL_assert(!lua_isnoneornil(state->L, -2));
if (!lua_isnoneornil(state->L, -1)) {
lua_newtable(new_state);
exchange_lua_states(state->L, new_state, 0, -1);
lua_setfield(new_state, -2, "udata");
lua_setglobal(new_state, "ctx");
}
/* bye :) */
lua_close(state->L); lua_close(state->L);
} }
state->L = luaL_newstate();
lua_setallocf(state->L, custom_alloc, NULL); state->L = new_state;
/* fakey version of luaL_openlibs() that excludes file i/o and os stuff */ /* fakey version of luaL_openlibs() that excludes file i/o and os stuff */
{ {
@ -136,27 +212,30 @@ void game_tick(void) {
lua_pop(state->L, 2); lua_pop(state->L, 2);
/* binding */ /* binding */
// lua_register(state->L, "sprite", b_sprite);
// lua_register(state->L, "rectangle", b_rectangle);
// lua_register(state->L, "text", b_text);
bindgen_load_twn(state->L); bindgen_load_twn(state->L);
/* now finally get to running the code */ /* now finally get to running the code */
unsigned char *game_buf = NULL; String file = file_read("/scripts/game.lua", ":binary");
size_t game_buf_size = file_to_bytes("/scripts/game.lua", &game_buf); /* TODO: use reader interface for streaming instead */
if (luaL_loadbuffer(state->L, (char *)game_buf, game_buf_size, "game.lua") == LUA_OK) { if (luaL_loadbuffer(state->L, file.data, (size_t)file.length, "game.lua") == LUA_OK) {
if (lua_pcall(state->L, 0, 0, 0) != LUA_OK) { if (lua_pcall(state->L, 0, 0, 0) != LUA_OK) {
log_critical("%s", lua_tostring(state->L, -1)); log_string(luaL_tolstring(state->L, -1, NULL), "Error executing /scripts/game.lua entry");
lua_pop(state->L, 1);
} else
state->loaded_successfully = true;
} else {
/* got some sort of error, it should be pushed on top of the stack */
SDL_assert(lua_isstring(state->L, -1));
log_string(luaL_tolstring(state->L, -1, NULL), "Error loading /scripts/game.lua entry");
lua_pop(state->L, 1); lua_pop(state->L, 1);
} }
}
free(game_buf);
/* from this point we have access to everything defined in lua */ /* from this point we have access to everything defined in lua */
} }
State *state = ctx.udata; State *state = ctx.udata;
if (state->loaded_successfully) {
bindgen_build_context(state->L); bindgen_build_context(state->L);
lua_getglobal(state->L, "ctx"); lua_getglobal(state->L, "ctx");
if (!lua_isnoneornil(state->L, -1)) { if (!lua_isnoneornil(state->L, -1)) {
@ -168,18 +247,19 @@ void game_tick(void) {
lua_getglobal(state->L, "game_tick"); lua_getglobal(state->L, "game_tick");
if (lua_pcall(state->L, 0, 0, 0) != LUA_OK) { if (lua_pcall(state->L, 0, 0, 0) != LUA_OK) {
log_critical("%s", lua_tostring(state->L, -1)); log_string(luaL_tolstring(state->L, -1, NULL), "Error executing game_tick()");
lua_pop(state->L, 1); lua_pop(state->L, 1);
} }
lua_getglobal(state->L, "ctx"); lua_getglobal(state->L, "ctx");
bindgen_upload_context(state->L); bindgen_upload_context(state->L);
} }
}
void game_end(void) { void game_end(void) {
State *state = ctx.udata; State *state = ctx.udata;
bindgen_unload_twn(state->L); bindgen_unload_twn(state->L);
lua_close(state->L); lua_close(state->L);
free(state); SDL_free(state);
} }

View File

@ -1,106 +0,0 @@
# Makefile for installing Lua
# See doc/readme.html for installation and customization instructions.
# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================
# Your platform. See PLATS for possible values.
PLAT= guess
# Where to install. The installation starts in the src and doc directories,
# so take care if INSTALL_TOP is not an absolute path. See the local target.
# You may want to make INSTALL_LMOD and INSTALL_CMOD consistent with
# LUA_ROOT, LUA_LDIR, and LUA_CDIR in luaconf.h.
INSTALL_TOP= /usr/local
INSTALL_BIN= $(INSTALL_TOP)/bin
INSTALL_INC= $(INSTALL_TOP)/include
INSTALL_LIB= $(INSTALL_TOP)/lib
INSTALL_MAN= $(INSTALL_TOP)/man/man1
INSTALL_LMOD= $(INSTALL_TOP)/share/lua/$V
INSTALL_CMOD= $(INSTALL_TOP)/lib/lua/$V
# How to install. If your install program does not support "-p", then
# you may have to run ranlib on the installed liblua.a.
INSTALL= install -p
INSTALL_EXEC= $(INSTALL) -m 0755
INSTALL_DATA= $(INSTALL) -m 0644
#
# If you don't have "install" you can use "cp" instead.
# INSTALL= cp -p
# INSTALL_EXEC= $(INSTALL)
# INSTALL_DATA= $(INSTALL)
# Other utilities.
MKDIR= mkdir -p
RM= rm -f
# == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE =======
# Convenience platforms targets.
PLATS= guess aix bsd c89 freebsd generic ios linux linux-readline macosx mingw posix solaris
# What to install.
TO_BIN= lua luac
TO_INC= lua.h luaconf.h lualib.h lauxlib.h lua.hpp
TO_LIB= liblua.a
TO_MAN= lua.1 luac.1
# Lua version and release.
V= 5.4
R= $V.7
# Targets start here.
all: $(PLAT)
$(PLATS) help test clean:
@cd src && $(MAKE) $@
install: dummy
cd src && $(MKDIR) $(INSTALL_BIN) $(INSTALL_INC) $(INSTALL_LIB) $(INSTALL_MAN) $(INSTALL_LMOD) $(INSTALL_CMOD)
cd src && $(INSTALL_EXEC) $(TO_BIN) $(INSTALL_BIN)
cd src && $(INSTALL_DATA) $(TO_INC) $(INSTALL_INC)
cd src && $(INSTALL_DATA) $(TO_LIB) $(INSTALL_LIB)
cd doc && $(INSTALL_DATA) $(TO_MAN) $(INSTALL_MAN)
uninstall:
cd src && cd $(INSTALL_BIN) && $(RM) $(TO_BIN)
cd src && cd $(INSTALL_INC) && $(RM) $(TO_INC)
cd src && cd $(INSTALL_LIB) && $(RM) $(TO_LIB)
cd doc && cd $(INSTALL_MAN) && $(RM) $(TO_MAN)
local:
$(MAKE) install INSTALL_TOP=../install
# make may get confused with install/ if it does not support .PHONY.
dummy:
# Echo config parameters.
echo:
@cd src && $(MAKE) -s echo
@echo "PLAT= $(PLAT)"
@echo "V= $V"
@echo "R= $R"
@echo "TO_BIN= $(TO_BIN)"
@echo "TO_INC= $(TO_INC)"
@echo "TO_LIB= $(TO_LIB)"
@echo "TO_MAN= $(TO_MAN)"
@echo "INSTALL_TOP= $(INSTALL_TOP)"
@echo "INSTALL_BIN= $(INSTALL_BIN)"
@echo "INSTALL_INC= $(INSTALL_INC)"
@echo "INSTALL_LIB= $(INSTALL_LIB)"
@echo "INSTALL_MAN= $(INSTALL_MAN)"
@echo "INSTALL_LMOD= $(INSTALL_LMOD)"
@echo "INSTALL_CMOD= $(INSTALL_CMOD)"
@echo "INSTALL_EXEC= $(INSTALL_EXEC)"
@echo "INSTALL_DATA= $(INSTALL_DATA)"
# Echo pkg-config data.
pc:
@echo "version=$R"
@echo "prefix=$(INSTALL_TOP)"
@echo "libdir=$(INSTALL_LIB)"
@echo "includedir=$(INSTALL_INC)"
# Targets that do not create files (not all makes understand .PHONY).
.PHONY: all $(PLATS) help test clean install uninstall local dummy echo pc
# (end of Makefile)

View File

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0f6cc3aaa68597034efda8bb6bfa214fc2fde99bd1e67d0bb32d7fe75a5dc777
size 12127

View File

@ -1,21 +0,0 @@
ul {
list-style-type: none ;
}
ul.contents {
padding: 0 ;
}
table {
border: none ;
border-spacing: 0 ;
border-collapse: collapse ;
}
td {
vertical-align: top ;
padding: 0 ;
text-align: left ;
line-height: 1.25 ;
width: 15% ;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

View File

@ -1,155 +0,0 @@
.\" $Id: lua.man,v 1.14 2024/05/08 18:48:27 lhf Exp $
.TH LUA 1 "$Date: 2024/05/08 18:48:27 $"
.SH NAME
lua \- Lua interpreter
.SH SYNOPSIS
.B lua
[
.I options
]
[
.I script
[
.I args
]
]
.SH DESCRIPTION
.B lua
is the standalone Lua interpreter.
It loads and executes Lua programs,
either in textual source form or
in precompiled binary form.
(Precompiled binaries are output by
.BR luac ,
the Lua compiler.)
.B lua
can be used as a batch interpreter and also interactively.
.LP
After handling the
.IR options ,
the Lua program in file
.I script
is loaded and executed.
The
.I args
are available to
.I script
as strings in a global table named
.B arg
and also as arguments to its main function.
When called without arguments,
.B lua
behaves as
.B "lua \-v \-i"
if the standard input is a terminal,
and as
.B "lua \-"
otherwise.
.LP
In interactive mode,
.B lua
prompts the user,
reads lines from the standard input,
and executes them as they are read.
If the line contains an expression,
then the line is evaluated and the result is printed.
If a line does not contain a complete statement,
then a secondary prompt is displayed and
lines are read until a complete statement is formed or
a syntax error is found.
.LP
Before handling command line options and scripts,
.B lua
checks the contents of the environment variables
.B LUA_INIT_5_4
and
.BR LUA_INIT ,
in that order.
If the contents are of the form
.RI '@ filename ',
then
.I filename
is executed.
Otherwise, the contents are assumed to be a Lua statement and is executed.
When
.B LUA_INIT_5_4
is defined,
.B LUA_INIT
is ignored.
.SH OPTIONS
.TP
.BI \-e " stat"
execute statement
.IR stat .
.TP
.B \-i
enter interactive mode after executing
.IR script .
.TP
.BI \-l " mod"
require library
.I mod
into global
.IR mod .
.TP
.BI \-l " g=mod"
require library
.I mod
into global
.IR g .
.TP
.B \-v
show version information.
.TP
.B \-E
ignore environment variables.
.TP
.B \-W
turn warnings on.
.TP
.B \-\-
stop handling options.
.TP
.B \-
stop handling options and execute the standard input as a file.
.SH ENVIRONMENT VARIABLES
The following environment variables affect the execution of
.BR lua .
When defined,
the version-specific variants take priority
and the version-neutral variants are ignored.
.TP
.B LUA_INIT, LUA_INIT_5_4
Code to be executed before command line options and scripts.
.TP
.B LUA_PATH, LUA_PATH_5_4
Initial value of package.path,
the path used by require to search for Lua loaders.
.TP
.B LUA_CPATH, LUA_CPATH_5_4
Initial value of package.cpath,
the path used by require to search for C loaders.
.SH EXIT STATUS
If a script calls os.exit,
then
.B lua
exits with the given exit status.
Otherwise,
.B lua
exits
with EXIT_SUCCESS (0 on POSIX systems) if there were no errors
and
with EXIT_FAILURE (1 on POSIX systems) if there were errors.
Errors raised in interactive mode do not cause exits.
.SH DIAGNOSTICS
Error messages should be self explanatory.
.SH "SEE ALSO"
.BR luac (1)
.br
The documentation at lua.org,
especially section 7 of the reference manual.
.SH AUTHORS
R. Ierusalimschy,
L. H. de Figueiredo,
W. Celes
.\" EOF

View File

@ -1,162 +0,0 @@
html {
background-color: #F8F8F8 ;
}
body {
background-color: #FFFFFF ;
color: #000000 ;
font-family: Helvetica, Arial, sans-serif ;
text-align: justify ;
line-height: 1.25 ;
margin: 16px auto ;
padding: 32px ;
border: solid #ccc 1px ;
border-radius: 20px ;
max-width: 70em ;
width: 90% ;
}
h1, h2, h3, h4 {
color: #000080 ;
font-family: Verdana, Geneva, sans-serif ;
font-weight: normal ;
font-style: normal ;
text-align: left ;
}
h1 {
font-size: 28pt ;
}
h1 img {
vertical-align: text-bottom ;
}
h2:before {
content: "\2756" ;
padding-right: 0.5em ;
}
a {
text-decoration: none ;
}
a:link {
color: #000080 ;
}
a:link:hover, a:visited:hover {
background-color: #D0D0FF ;
color: #000080 ;
border-radius: 4px ;
}
a:link:active, a:visited:active {
color: #FF0000 ;
}
div.menubar {
padding-bottom: 0.5em ;
}
p.menubar {
margin-left: 2.5em ;
}
.menubar a:hover {
margin: -3px -3px -3px -3px ;
padding: 3px 3px 3px 3px ;
border-radius: 4px ;
}
:target {
background-color: #F0F0F0 ;
margin: -8px ;
padding: 8px ;
border-radius: 8px ;
outline: none ;
}
hr {
display: none ;
}
table hr {
background-color: #a0a0a0 ;
color: #a0a0a0 ;
border: 0 ;
height: 1px ;
display: block ;
}
.footer {
color: gray ;
font-size: x-small ;
text-transform: lowercase ;
}
input[type=text] {
border: solid #a0a0a0 2px ;
border-radius: 2em ;
background-image: url('images/search.png') ;
background-repeat: no-repeat ;
background-position: 4px center ;
padding-left: 20px ;
height: 2em ;
}
pre.session {
background-color: #F8F8F8 ;
padding: 1em ;
border-radius: 8px ;
}
table {
border: none ;
border-spacing: 0 ;
border-collapse: collapse ;
}
td {
padding: 0 ;
margin: 0 ;
}
td.gutter {
width: 4% ;
}
table.columns td {
vertical-align: top ;
padding-bottom: 1em ;
text-align: justify ;
line-height: 1.25 ;
}
table.book td {
vertical-align: top ;
}
table.book td.cover {
padding-right: 1em ;
}
table.book img {
border: solid #000080 1px ;
border-radius: 2px ;
}
table.book span {
font-size: small ;
text-align: left ;
display: block ;
margin-top: 0.25em ;
}
p.logos a:link:hover, p.logos a:visited:hover {
background-color: inherit ;
}
img {
background-color: white ;
}

View File

@ -1,118 +0,0 @@
.\" $Id: luac.man,v 1.29 2011/11/16 13:53:40 lhf Exp $
.TH LUAC 1 "$Date: 2011/11/16 13:53:40 $"
.SH NAME
luac \- Lua compiler
.SH SYNOPSIS
.B luac
[
.I options
] [
.I filenames
]
.SH DESCRIPTION
.B luac
is the Lua compiler.
It translates programs written in the Lua programming language
into binary files containing precompiled chunks
that can be later loaded and executed.
.LP
The main advantages of precompiling chunks are:
faster loading,
protecting source code from accidental user changes,
and
off-line syntax checking.
Precompiling does not imply faster execution
because in Lua chunks are always compiled into bytecodes before being executed.
.B luac
simply allows those bytecodes to be saved in a file for later execution.
Precompiled chunks are not necessarily smaller than the corresponding source.
The main goal in precompiling is faster loading.
.LP
In the command line,
you can mix
text files containing Lua source and
binary files containing precompiled chunks.
.B luac
produces a single output file containing the combined bytecodes
for all files given.
Executing the combined file is equivalent to executing the given files.
By default,
the output file is named
.BR luac.out ,
but you can change this with the
.B \-o
option.
.LP
Precompiled chunks are
.I not
portable across different architectures.
Moreover,
the internal format of precompiled chunks
is likely to change when a new version of Lua is released.
Make sure you save the source files of all Lua programs that you precompile.
.LP
.SH OPTIONS
.TP
.B \-l
produce a listing of the compiled bytecode for Lua's virtual machine.
Listing bytecodes is useful to learn about Lua's virtual machine.
If no files are given, then
.B luac
loads
.B luac.out
and lists its contents.
Use
.B \-l \-l
for a full listing.
.TP
.BI \-o " file"
output to
.IR file ,
instead of the default
.BR luac.out .
(You can use
.B "'\-'"
for standard output,
but not on platforms that open standard output in text mode.)
The output file may be one of the given files because
all files are loaded before the output file is written.
Be careful not to overwrite precious files.
.TP
.B \-p
load files but do not generate any output file.
Used mainly for syntax checking and for testing precompiled chunks:
corrupted files will probably generate errors when loaded.
If no files are given, then
.B luac
loads
.B luac.out
and tests its contents.
No messages are displayed if the file loads without errors.
.TP
.B \-s
strip debug information before writing the output file.
This saves some space in very large chunks,
but if errors occur when running a stripped chunk,
then the error messages may not contain the full information they usually do.
In particular,
line numbers and names of local variables are lost.
.TP
.B \-v
show version information.
.TP
.B \-\-
stop handling options.
.TP
.B \-
stop handling options and process standard input.
.SH "SEE ALSO"
.BR lua (1)
.br
The documentation at lua.org.
.SH DIAGNOSTICS
Error messages should be self explanatory.
.SH AUTHORS
R. Ierusalimschy,
L. H. de Figueiredo,
W. Celes
.\" EOF

View File

@ -1,21 +0,0 @@
h3 code {
font-family: inherit ;
font-size: inherit ;
}
pre, code {
font-size: 12pt ;
}
span.apii {
color: gray ;
float: right ;
font-family: inherit ;
font-style: normal ;
font-size: small ;
}
h2:before {
content: "" ;
padding-right: 0em ;
}

View File

@ -1,225 +0,0 @@
# Makefile for building Lua
# See ../doc/readme.html for installation and customization instructions.
# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================
# Your platform. See PLATS for possible values.
PLAT= guess
CC= gcc -std=gnu99
CFLAGS= -O2 -Wall -Wextra -DLUA_COMPAT_5_3 $(SYSCFLAGS) $(MYCFLAGS)
LDFLAGS= $(SYSLDFLAGS) $(MYLDFLAGS)
LIBS= -lm $(SYSLIBS) $(MYLIBS)
AR= ar rcu
RANLIB= ranlib
RM= rm -f
UNAME= uname
SYSCFLAGS=
SYSLDFLAGS=
SYSLIBS=
MYCFLAGS=
MYLDFLAGS=
MYLIBS=
MYOBJS=
# Special flags for compiler modules; -Os reduces code size.
CMCFLAGS=
# == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE =======
PLATS= guess aix bsd c89 freebsd generic ios linux linux-readline macosx mingw posix solaris
LUA_A= liblua.a
CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o
LIB_O= lauxlib.o lbaselib.o lcorolib.o ldblib.o liolib.o lmathlib.o loadlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o linit.o
BASE_O= $(CORE_O) $(LIB_O) $(MYOBJS)
LUA_T= lua
LUA_O= lua.o
LUAC_T= luac
LUAC_O= luac.o
ALL_O= $(BASE_O) $(LUA_O) $(LUAC_O)
ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T)
ALL_A= $(LUA_A)
# Targets start here.
default: $(PLAT)
all: $(ALL_T)
o: $(ALL_O)
a: $(ALL_A)
$(LUA_A): $(BASE_O)
$(AR) $@ $(BASE_O)
$(RANLIB) $@
$(LUA_T): $(LUA_O) $(LUA_A)
$(CC) -o $@ $(LDFLAGS) $(LUA_O) $(LUA_A) $(LIBS)
$(LUAC_T): $(LUAC_O) $(LUA_A)
$(CC) -o $@ $(LDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS)
test:
./$(LUA_T) -v
clean:
$(RM) $(ALL_T) $(ALL_O)
depend:
@$(CC) $(CFLAGS) -MM l*.c
echo:
@echo "PLAT= $(PLAT)"
@echo "CC= $(CC)"
@echo "CFLAGS= $(CFLAGS)"
@echo "LDFLAGS= $(LDFLAGS)"
@echo "LIBS= $(LIBS)"
@echo "AR= $(AR)"
@echo "RANLIB= $(RANLIB)"
@echo "RM= $(RM)"
@echo "UNAME= $(UNAME)"
# Convenience targets for popular platforms.
ALL= all
help:
@echo "Do 'make PLATFORM' where PLATFORM is one of these:"
@echo " $(PLATS)"
@echo "See doc/readme.html for complete instructions."
guess:
@echo Guessing `$(UNAME)`
@$(MAKE) `$(UNAME)`
AIX aix:
$(MAKE) $(ALL) CC="xlc" CFLAGS="-O2 -DLUA_USE_POSIX -DLUA_USE_DLOPEN" SYSLIBS="-ldl" SYSLDFLAGS="-brtl -bexpall"
bsd:
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" SYSLIBS="-Wl,-E"
c89:
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_C89" CC="gcc -std=c89"
@echo ''
@echo '*** C89 does not guarantee 64-bit integers for Lua.'
@echo '*** Make sure to compile all external Lua libraries'
@echo '*** with LUA_USE_C89 to ensure consistency'
@echo ''
FreeBSD NetBSD OpenBSD freebsd:
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -DLUA_USE_READLINE -I/usr/include/edit" SYSLIBS="-Wl,-E -ledit" CC="cc"
generic: $(ALL)
ios:
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_IOS"
Linux linux: linux-noreadline
linux-noreadline:
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX" SYSLIBS="-Wl,-E -ldl"
linux-readline:
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -DLUA_USE_READLINE" SYSLIBS="-Wl,-E -ldl -lreadline"
Darwin macos macosx:
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_MACOSX -DLUA_USE_READLINE" SYSLIBS="-lreadline"
mingw:
$(MAKE) "LUA_A=lua54.dll" "LUA_T=lua.exe" \
"AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \
"SYSCFLAGS=-DLUA_BUILD_AS_DLL" "SYSLIBS=" "SYSLDFLAGS=-s" lua.exe
$(MAKE) "LUAC_T=luac.exe" luac.exe
posix:
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX"
SunOS solaris:
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN -D_REENTRANT" SYSLIBS="-ldl"
# Targets that do not create files (not all makes understand .PHONY).
.PHONY: all $(PLATS) help test clean default o a depend echo
# Compiler modules may use special flags.
llex.o:
$(CC) $(CFLAGS) $(CMCFLAGS) -c llex.c
lparser.o:
$(CC) $(CFLAGS) $(CMCFLAGS) -c lparser.c
lcode.o:
$(CC) $(CFLAGS) $(CMCFLAGS) -c lcode.c
# DO NOT DELETE
lapi.o: lapi.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lstring.h \
ltable.h lundump.h lvm.h
lauxlib.o: lauxlib.c lprefix.h lua.h luaconf.h lauxlib.h
lbaselib.o: lbaselib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
lcode.o: lcode.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \
llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \
ldo.h lgc.h lstring.h ltable.h lvm.h
lcorolib.o: lcorolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
lctype.o: lctype.c lprefix.h lctype.h lua.h luaconf.h llimits.h
ldblib.o: ldblib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
ldebug.o: ldebug.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
lobject.h ltm.h lzio.h lmem.h lcode.h llex.h lopcodes.h lparser.h \
ldebug.h ldo.h lfunc.h lstring.h lgc.h ltable.h lvm.h
ldo.o: ldo.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lopcodes.h \
lparser.h lstring.h ltable.h lundump.h lvm.h
ldump.o: ldump.c lprefix.h lua.h luaconf.h lobject.h llimits.h lstate.h \
ltm.h lzio.h lmem.h lundump.h
lfunc.o: lfunc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h
lgc.o: lgc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h
linit.o: linit.c lprefix.h lua.h luaconf.h lualib.h lauxlib.h
liolib.o: liolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
llex.o: llex.c lprefix.h lua.h luaconf.h lctype.h llimits.h ldebug.h \
lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lgc.h llex.h lparser.h \
lstring.h ltable.h
lmathlib.o: lmathlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
lmem.o: lmem.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h
loadlib.o: loadlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
lobject.o: lobject.c lprefix.h lua.h luaconf.h lctype.h llimits.h \
ldebug.h lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h \
lvm.h
lopcodes.o: lopcodes.c lprefix.h lopcodes.h llimits.h lua.h luaconf.h
loslib.o: loslib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
lparser.o: lparser.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \
llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \
ldo.h lfunc.h lstring.h lgc.h ltable.h
lstate.o: lstate.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h llex.h \
lstring.h ltable.h
lstring.o: lstring.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \
lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h
lstrlib.o: lstrlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
ltable.o: ltable.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h
ltablib.o: ltablib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
ltm.o: ltm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h
lua.o: lua.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
luac.o: luac.c lprefix.h lua.h luaconf.h lauxlib.h ldebug.h lstate.h \
lobject.h llimits.h ltm.h lzio.h lmem.h lopcodes.h lopnames.h lundump.h
lundump.o: lundump.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \
lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h \
lundump.h
lutf8lib.o: lutf8lib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
lvm.o: lvm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h \
ltable.h lvm.h ljumptab.h
lzio.o: lzio.c lprefix.h lua.h luaconf.h llimits.h lmem.h lstate.h \
lobject.h ltm.h lzio.h
# (end of Makefile)

File diff suppressed because it is too large Load Diff

View File

@ -1,52 +0,0 @@
/*
** $Id: lapi.h $
** Auxiliary functions from Lua API
** See Copyright Notice in lua.h
*/
#ifndef lapi_h
#define lapi_h
#include "llimits.h"
#include "lstate.h"
/* Increments 'L->top.p', checking for stack overflows */
#define api_incr_top(L) {L->top.p++; \
api_check(L, L->top.p <= L->ci->top.p, \
"stack overflow");}
/*
** If a call returns too many multiple returns, the callee may not have
** stack space to accommodate all results. In this case, this macro
** increases its stack space ('L->ci->top.p').
*/
#define adjustresults(L,nres) \
{ if ((nres) <= LUA_MULTRET && L->ci->top.p < L->top.p) \
L->ci->top.p = L->top.p; }
/* Ensure the stack has at least 'n' elements */
#define api_checknelems(L,n) \
api_check(L, (n) < (L->top.p - L->ci->func.p), \
"not enough elements in the stack")
/*
** To reduce the overhead of returning from C functions, the presence of
** to-be-closed variables in these functions is coded in the CallInfo's
** field 'nresults', in a way that functions with no to-be-closed variables
** with zero, one, or "all" wanted results have no overhead. Functions
** with other number of wanted results, as well as functions with
** variables to be closed, have an extra check.
*/
#define hastocloseCfunc(n) ((n) < LUA_MULTRET)
/* Map [-1, inf) (range of 'nresults') into (-inf, -2] */
#define codeNresults(n) (-(n) - 3)
#define decodeNresults(n) (-(n) - 3)
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,301 +0,0 @@
/*
** $Id: lauxlib.h $
** Auxiliary functions for building Lua libraries
** See Copyright Notice in lua.h
*/
#ifndef lauxlib_h
#define lauxlib_h
#include <stddef.h>
#include <stdio.h>
#include "luaconf.h"
#include "lua.h"
/* global table */
#define LUA_GNAME "_G"
typedef struct luaL_Buffer luaL_Buffer;
/* extra error code for 'luaL_loadfilex' */
#define LUA_ERRFILE (LUA_ERRERR+1)
/* key, in the registry, for table of loaded modules */
#define LUA_LOADED_TABLE "_LOADED"
/* key, in the registry, for table of preloaded loaders */
#define LUA_PRELOAD_TABLE "_PRELOAD"
typedef struct luaL_Reg {
const char *name;
lua_CFunction func;
} luaL_Reg;
#define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number))
LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz);
#define luaL_checkversion(L) \
luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES)
LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len);
LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg);
LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname);
LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg,
size_t *l);
LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg,
const char *def, size_t *l);
LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg);
LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def);
LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg);
LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg,
lua_Integer def);
LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t);
LUALIB_API void (luaL_checkany) (lua_State *L, int arg);
LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname);
LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
LUALIB_API void (luaL_where) (lua_State *L, int lvl);
LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def,
const char *const lst[]);
LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname);
LUALIB_API int (luaL_execresult) (lua_State *L, int stat);
/* predefined references */
#define LUA_NOREF (-2)
#define LUA_REFNIL (-1)
LUALIB_API int (luaL_ref) (lua_State *L, int t);
LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename,
const char *mode);
#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL)
LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz,
const char *name, const char *mode);
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
LUALIB_API lua_State *(luaL_newstate) (void);
LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);
LUALIB_API void (luaL_addgsub) (luaL_Buffer *b, const char *s,
const char *p, const char *r);
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s,
const char *p, const char *r);
LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);
LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname);
LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1,
const char *msg, int level);
LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
lua_CFunction openf, int glb);
/*
** ===============================================================
** some useful macros
** ===============================================================
*/
#define luaL_newlibtable(L,l) \
lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
#define luaL_newlib(L,l) \
(luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
#define luaL_argcheck(L, cond,arg,extramsg) \
((void)(luai_likely(cond) || luaL_argerror(L, (arg), (extramsg))))
#define luaL_argexpected(L,cond,arg,tname) \
((void)(luai_likely(cond) || luaL_typeerror(L, (arg), (tname))))
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
#define luaL_dofile(L, fn) \
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
#define luaL_dostring(L, s) \
(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)
/*
** Perform arithmetic operations on lua_Integer values with wrap-around
** semantics, as the Lua core does.
*/
#define luaL_intop(op,v1,v2) \
((lua_Integer)((lua_Unsigned)(v1) op (lua_Unsigned)(v2)))
/* push the value used to represent failure/error */
#define luaL_pushfail(L) lua_pushnil(L)
/*
** Internal assertions for in-house debugging
*/
#if !defined(lua_assert)
#if defined LUAI_ASSERT
#include <assert.h>
#define lua_assert(c) assert(c)
#else
#define lua_assert(c) ((void)0)
#endif
#endif
/*
** {======================================================
** Generic Buffer manipulation
** =======================================================
*/
struct luaL_Buffer {
char *b; /* buffer address */
size_t size; /* buffer size */
size_t n; /* number of characters in buffer */
lua_State *L;
union {
LUAI_MAXALIGN; /* ensure maximum alignment for buffer */
char b[LUAL_BUFFERSIZE]; /* initial buffer */
} init;
};
#define luaL_bufflen(bf) ((bf)->n)
#define luaL_buffaddr(bf) ((bf)->b)
#define luaL_addchar(B,c) \
((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \
((B)->b[(B)->n++] = (c)))
#define luaL_addsize(B,s) ((B)->n += (s))
#define luaL_buffsub(B,s) ((B)->n -= (s))
LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz);
LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz);
LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz);
#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
/* }====================================================== */
/*
** {======================================================
** File handles for IO library
** =======================================================
*/
/*
** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and
** initial structure 'luaL_Stream' (it may contain other fields
** after that initial structure).
*/
#define LUA_FILEHANDLE "FILE*"
typedef struct luaL_Stream {
FILE *f; /* stream (NULL for incompletely created streams) */
lua_CFunction closef; /* to close stream (NULL for closed streams) */
} luaL_Stream;
/* }====================================================== */
/*
** {==================================================================
** "Abstraction Layer" for basic report of messages and errors
** ===================================================================
*/
/* print a string */
#if !defined(lua_writestring)
#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout)
#endif
/* print a newline and flush the output */
#if !defined(lua_writeline)
#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout))
#endif
/* print an error message */
#if !defined(lua_writestringerror)
#define lua_writestringerror(s,p) \
(fprintf(stderr, (s), (p)), fflush(stderr))
#endif
/* }================================================================== */
/*
** {============================================================
** Compatibility with deprecated conversions
** =============================================================
*/
#if defined(LUA_COMPAT_APIINTCASTS)
#define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a))
#define luaL_optunsigned(L,a,d) \
((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d)))
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
#endif
/* }============================================================ */
#endif

View File

@ -1,549 +0,0 @@
/*
** $Id: lbaselib.c $
** Basic library
** See Copyright Notice in lua.h
*/
#define lbaselib_c
#define LUA_LIB
#include "lprefix.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
static int luaB_print (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
int i;
for (i = 1; i <= n; i++) { /* for each argument */
size_t l;
const char *s = luaL_tolstring(L, i, &l); /* convert it to string */
if (i > 1) /* not the first element? */
lua_writestring("\t", 1); /* add a tab before it */
lua_writestring(s, l); /* print it */
lua_pop(L, 1); /* pop result */
}
lua_writeline();
return 0;
}
/*
** Creates a warning with all given arguments.
** Check first for errors; otherwise an error may interrupt
** the composition of a warning, leaving it unfinished.
*/
static int luaB_warn (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
int i;
luaL_checkstring(L, 1); /* at least one argument */
for (i = 2; i <= n; i++)
luaL_checkstring(L, i); /* make sure all arguments are strings */
for (i = 1; i < n; i++) /* compose warning */
lua_warning(L, lua_tostring(L, i), 1);
lua_warning(L, lua_tostring(L, n), 0); /* close warning */
return 0;
}
#define SPACECHARS " \f\n\r\t\v"
static const char *b_str2int (const char *s, int base, lua_Integer *pn) {
lua_Unsigned n = 0;
int neg = 0;
s += strspn(s, SPACECHARS); /* skip initial spaces */
if (*s == '-') { s++; neg = 1; } /* handle sign */
else if (*s == '+') s++;
if (!isalnum((unsigned char)*s)) /* no digit? */
return NULL;
do {
int digit = (isdigit((unsigned char)*s)) ? *s - '0'
: (toupper((unsigned char)*s) - 'A') + 10;
if (digit >= base) return NULL; /* invalid numeral */
n = n * base + digit;
s++;
} while (isalnum((unsigned char)*s));
s += strspn(s, SPACECHARS); /* skip trailing spaces */
*pn = (lua_Integer)((neg) ? (0u - n) : n);
return s;
}
static int luaB_tonumber (lua_State *L) {
if (lua_isnoneornil(L, 2)) { /* standard conversion? */
if (lua_type(L, 1) == LUA_TNUMBER) { /* already a number? */
lua_settop(L, 1); /* yes; return it */
return 1;
}
else {
size_t l;
const char *s = lua_tolstring(L, 1, &l);
if (s != NULL && lua_stringtonumber(L, s) == l + 1)
return 1; /* successful conversion to number */
/* else not a number */
luaL_checkany(L, 1); /* (but there must be some parameter) */
}
}
else {
size_t l;
const char *s;
lua_Integer n = 0; /* to avoid warnings */
lua_Integer base = luaL_checkinteger(L, 2);
luaL_checktype(L, 1, LUA_TSTRING); /* no numbers as strings */
s = lua_tolstring(L, 1, &l);
luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range");
if (b_str2int(s, (int)base, &n) == s + l) {
lua_pushinteger(L, n);
return 1;
} /* else not a number */
} /* else not a number */
luaL_pushfail(L); /* not a number */
return 1;
}
static int luaB_error (lua_State *L) {
int level = (int)luaL_optinteger(L, 2, 1);
lua_settop(L, 1);
if (lua_type(L, 1) == LUA_TSTRING && level > 0) {
luaL_where(L, level); /* add extra information */
lua_pushvalue(L, 1);
lua_concat(L, 2);
}
return lua_error(L);
}
static int luaB_getmetatable (lua_State *L) {
luaL_checkany(L, 1);
if (!lua_getmetatable(L, 1)) {
lua_pushnil(L);
return 1; /* no metatable */
}
luaL_getmetafield(L, 1, "__metatable");
return 1; /* returns either __metatable field (if present) or metatable */
}
static int luaB_setmetatable (lua_State *L) {
int t = lua_type(L, 2);
luaL_checktype(L, 1, LUA_TTABLE);
luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
if (l_unlikely(luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL))
return luaL_error(L, "cannot change a protected metatable");
lua_settop(L, 2);
lua_setmetatable(L, 1);
return 1;
}
static int luaB_rawequal (lua_State *L) {
luaL_checkany(L, 1);
luaL_checkany(L, 2);
lua_pushboolean(L, lua_rawequal(L, 1, 2));
return 1;
}
static int luaB_rawlen (lua_State *L) {
int t = lua_type(L, 1);
luaL_argexpected(L, t == LUA_TTABLE || t == LUA_TSTRING, 1,
"table or string");
lua_pushinteger(L, lua_rawlen(L, 1));
return 1;
}
static int luaB_rawget (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
luaL_checkany(L, 2);
lua_settop(L, 2);
lua_rawget(L, 1);
return 1;
}
static int luaB_rawset (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
luaL_checkany(L, 2);
luaL_checkany(L, 3);
lua_settop(L, 3);
lua_rawset(L, 1);
return 1;
}
static int pushmode (lua_State *L, int oldmode) {
if (oldmode == -1)
luaL_pushfail(L); /* invalid call to 'lua_gc' */
else
lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental"
: "generational");
return 1;
}
/*
** check whether call to 'lua_gc' was valid (not inside a finalizer)
*/
#define checkvalres(res) { if (res == -1) break; }
static int luaB_collectgarbage (lua_State *L) {
static const char *const opts[] = {"stop", "restart", "collect",
"count", "step", "setpause", "setstepmul",
"isrunning", "generational", "incremental", NULL};
static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,
LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL,
LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC};
int o = optsnum[luaL_checkoption(L, 1, "collect", opts)];
switch (o) {
case LUA_GCCOUNT: {
int k = lua_gc(L, o);
int b = lua_gc(L, LUA_GCCOUNTB);
checkvalres(k);
lua_pushnumber(L, (lua_Number)k + ((lua_Number)b/1024));
return 1;
}
case LUA_GCSTEP: {
int step = (int)luaL_optinteger(L, 2, 0);
int res = lua_gc(L, o, step);
checkvalres(res);
lua_pushboolean(L, res);
return 1;
}
case LUA_GCSETPAUSE:
case LUA_GCSETSTEPMUL: {
int p = (int)luaL_optinteger(L, 2, 0);
int previous = lua_gc(L, o, p);
checkvalres(previous);
lua_pushinteger(L, previous);
return 1;
}
case LUA_GCISRUNNING: {
int res = lua_gc(L, o);
checkvalres(res);
lua_pushboolean(L, res);
return 1;
}
case LUA_GCGEN: {
int minormul = (int)luaL_optinteger(L, 2, 0);
int majormul = (int)luaL_optinteger(L, 3, 0);
return pushmode(L, lua_gc(L, o, minormul, majormul));
}
case LUA_GCINC: {
int pause = (int)luaL_optinteger(L, 2, 0);
int stepmul = (int)luaL_optinteger(L, 3, 0);
int stepsize = (int)luaL_optinteger(L, 4, 0);
return pushmode(L, lua_gc(L, o, pause, stepmul, stepsize));
}
default: {
int res = lua_gc(L, o);
checkvalres(res);
lua_pushinteger(L, res);
return 1;
}
}
luaL_pushfail(L); /* invalid call (inside a finalizer) */
return 1;
}
static int luaB_type (lua_State *L) {
int t = lua_type(L, 1);
luaL_argcheck(L, t != LUA_TNONE, 1, "value expected");
lua_pushstring(L, lua_typename(L, t));
return 1;
}
static int luaB_next (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
lua_settop(L, 2); /* create a 2nd argument if there isn't one */
if (lua_next(L, 1))
return 2;
else {
lua_pushnil(L);
return 1;
}
}
static int pairscont (lua_State *L, int status, lua_KContext k) {
(void)L; (void)status; (void)k; /* unused */
return 3;
}
static int luaB_pairs (lua_State *L) {
luaL_checkany(L, 1);
if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */
lua_pushcfunction(L, luaB_next); /* will return generator, */
lua_pushvalue(L, 1); /* state, */
lua_pushnil(L); /* and initial value */
}
else {
lua_pushvalue(L, 1); /* argument 'self' to metamethod */
lua_callk(L, 1, 3, 0, pairscont); /* get 3 values from metamethod */
}
return 3;
}
/*
** Traversal function for 'ipairs'
*/
static int ipairsaux (lua_State *L) {
lua_Integer i = luaL_checkinteger(L, 2);
i = luaL_intop(+, i, 1);
lua_pushinteger(L, i);
return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2;
}
/*
** 'ipairs' function. Returns 'ipairsaux', given "table", 0.
** (The given "table" may not be a table.)
*/
static int luaB_ipairs (lua_State *L) {
luaL_checkany(L, 1);
lua_pushcfunction(L, ipairsaux); /* iteration function */
lua_pushvalue(L, 1); /* state */
lua_pushinteger(L, 0); /* initial value */
return 3;
}
static int load_aux (lua_State *L, int status, int envidx) {
if (l_likely(status == LUA_OK)) {
if (envidx != 0) { /* 'env' parameter? */
lua_pushvalue(L, envidx); /* environment for loaded function */
if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */
lua_pop(L, 1); /* remove 'env' if not used by previous call */
}
return 1;
}
else { /* error (message is on top of the stack) */
luaL_pushfail(L);
lua_insert(L, -2); /* put before error message */
return 2; /* return fail plus error message */
}
}
static int luaB_loadfile (lua_State *L) {
const char *fname = luaL_optstring(L, 1, NULL);
const char *mode = luaL_optstring(L, 2, NULL);
int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */
int status = luaL_loadfilex(L, fname, mode);
return load_aux(L, status, env);
}
/*
** {======================================================
** Generic Read function
** =======================================================
*/
/*
** reserved slot, above all arguments, to hold a copy of the returned
** string to avoid it being collected while parsed. 'load' has four
** optional arguments (chunk, source name, mode, and environment).
*/
#define RESERVEDSLOT 5
/*
** Reader for generic 'load' function: 'lua_load' uses the
** stack for internal stuff, so the reader cannot change the
** stack top. Instead, it keeps its resulting string in a
** reserved slot inside the stack.
*/
static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
(void)(ud); /* not used */
luaL_checkstack(L, 2, "too many nested functions");
lua_pushvalue(L, 1); /* get function */
lua_call(L, 0, 1); /* call it */
if (lua_isnil(L, -1)) {
lua_pop(L, 1); /* pop result */
*size = 0;
return NULL;
}
else if (l_unlikely(!lua_isstring(L, -1)))
luaL_error(L, "reader function must return a string");
lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */
return lua_tolstring(L, RESERVEDSLOT, size);
}
static int luaB_load (lua_State *L) {
int status;
size_t l;
const char *s = lua_tolstring(L, 1, &l);
const char *mode = luaL_optstring(L, 3, "bt");
int env = (!lua_isnone(L, 4) ? 4 : 0); /* 'env' index or 0 if no 'env' */
if (s != NULL) { /* loading a string? */
const char *chunkname = luaL_optstring(L, 2, s);
status = luaL_loadbufferx(L, s, l, chunkname, mode);
}
else { /* loading from a reader function */
const char *chunkname = luaL_optstring(L, 2, "=(load)");
luaL_checktype(L, 1, LUA_TFUNCTION);
lua_settop(L, RESERVEDSLOT); /* create reserved slot */
status = lua_load(L, generic_reader, NULL, chunkname, mode);
}
return load_aux(L, status, env);
}
/* }====================================================== */
static int dofilecont (lua_State *L, int d1, lua_KContext d2) {
(void)d1; (void)d2; /* only to match 'lua_Kfunction' prototype */
return lua_gettop(L) - 1;
}
static int luaB_dofile (lua_State *L) {
const char *fname = luaL_optstring(L, 1, NULL);
lua_settop(L, 1);
if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK))
return lua_error(L);
lua_callk(L, 0, LUA_MULTRET, 0, dofilecont);
return dofilecont(L, 0, 0);
}
static int luaB_assert (lua_State *L) {
if (l_likely(lua_toboolean(L, 1))) /* condition is true? */
return lua_gettop(L); /* return all arguments */
else { /* error */
luaL_checkany(L, 1); /* there must be a condition */
lua_remove(L, 1); /* remove it */
lua_pushliteral(L, "assertion failed!"); /* default message */
lua_settop(L, 1); /* leave only message (default if no other one) */
return luaB_error(L); /* call 'error' */
}
}
static int luaB_select (lua_State *L) {
int n = lua_gettop(L);
if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') {
lua_pushinteger(L, n-1);
return 1;
}
else {
lua_Integer i = luaL_checkinteger(L, 1);
if (i < 0) i = n + i;
else if (i > n) i = n;
luaL_argcheck(L, 1 <= i, 1, "index out of range");
return n - (int)i;
}
}
/*
** Continuation function for 'pcall' and 'xpcall'. Both functions
** already pushed a 'true' before doing the call, so in case of success
** 'finishpcall' only has to return everything in the stack minus
** 'extra' values (where 'extra' is exactly the number of items to be
** ignored).
*/
static int finishpcall (lua_State *L, int status, lua_KContext extra) {
if (l_unlikely(status != LUA_OK && status != LUA_YIELD)) { /* error? */
lua_pushboolean(L, 0); /* first result (false) */
lua_pushvalue(L, -2); /* error message */
return 2; /* return false, msg */
}
else
return lua_gettop(L) - (int)extra; /* return all results */
}
static int luaB_pcall (lua_State *L) {
int status;
luaL_checkany(L, 1);
lua_pushboolean(L, 1); /* first result if no errors */
lua_insert(L, 1); /* put it in place */
status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, finishpcall);
return finishpcall(L, status, 0);
}
/*
** Do a protected call with error handling. After 'lua_rotate', the
** stack will have <f, err, true, f, [args...]>; so, the function passes
** 2 to 'finishpcall' to skip the 2 first values when returning results.
*/
static int luaB_xpcall (lua_State *L) {
int status;
int n = lua_gettop(L);
luaL_checktype(L, 2, LUA_TFUNCTION); /* check error function */
lua_pushboolean(L, 1); /* first result */
lua_pushvalue(L, 1); /* function */
lua_rotate(L, 3, 2); /* move them below function's arguments */
status = lua_pcallk(L, n - 2, LUA_MULTRET, 2, 2, finishpcall);
return finishpcall(L, status, 2);
}
static int luaB_tostring (lua_State *L) {
luaL_checkany(L, 1);
luaL_tolstring(L, 1, NULL);
return 1;
}
static const luaL_Reg base_funcs[] = {
{"assert", luaB_assert},
{"collectgarbage", luaB_collectgarbage},
{"dofile", luaB_dofile},
{"error", luaB_error},
{"getmetatable", luaB_getmetatable},
{"ipairs", luaB_ipairs},
{"loadfile", luaB_loadfile},
{"load", luaB_load},
{"next", luaB_next},
{"pairs", luaB_pairs},
{"pcall", luaB_pcall},
{"print", luaB_print},
{"warn", luaB_warn},
{"rawequal", luaB_rawequal},
{"rawlen", luaB_rawlen},
{"rawget", luaB_rawget},
{"rawset", luaB_rawset},
{"select", luaB_select},
{"setmetatable", luaB_setmetatable},
{"tonumber", luaB_tonumber},
{"tostring", luaB_tostring},
{"type", luaB_type},
{"xpcall", luaB_xpcall},
/* placeholders */
{LUA_GNAME, NULL},
{"_VERSION", NULL},
{NULL, NULL}
};
LUAMOD_API int luaopen_base (lua_State *L) {
/* open lib into global table */
lua_pushglobaltable(L);
luaL_setfuncs(L, base_funcs, 0);
/* set global _G */
lua_pushvalue(L, -1);
lua_setfield(L, -2, LUA_GNAME);
/* set global _VERSION */
lua_pushliteral(L, LUA_VERSION);
lua_setfield(L, -2, "_VERSION");
return 1;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,101 +0,0 @@
/*
** $Id: lcode.h $
** Code generator for Lua
** See Copyright Notice in lua.h
*/
#ifndef lcode_h
#define lcode_h
#include "llex.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lparser.h"
/*
** Marks the end of a patch list. It is an invalid value both as an absolute
** address, and as a list link (would link an element to itself).
*/
#define NO_JUMP (-1)
/*
** grep "ORDER OPR" if you change these enums (ORDER OP)
*/
typedef enum BinOpr {
/* arithmetic operators */
OPR_ADD, OPR_SUB, OPR_MUL, OPR_MOD, OPR_POW,
OPR_DIV, OPR_IDIV,
/* bitwise operators */
OPR_BAND, OPR_BOR, OPR_BXOR,
OPR_SHL, OPR_SHR,
/* string operator */
OPR_CONCAT,
/* comparison operators */
OPR_EQ, OPR_LT, OPR_LE,
OPR_NE, OPR_GT, OPR_GE,
/* logical operators */
OPR_AND, OPR_OR,
OPR_NOBINOPR
} BinOpr;
/* true if operation is foldable (that is, it is arithmetic or bitwise) */
#define foldbinop(op) ((op) <= OPR_SHR)
#define luaK_codeABC(fs,o,a,b,c) luaK_codeABCk(fs,o,a,b,c,0)
typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
/* get (pointer to) instruction of given 'expdesc' */
#define getinstruction(fs,e) ((fs)->f->code[(e)->u.info])
#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET)
#define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t)
LUAI_FUNC int luaK_code (FuncState *fs, Instruction i);
LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx);
LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A,
int B, int C, int k);
LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v);
LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
LUAI_FUNC void luaK_checkstack (FuncState *fs, int n);
LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n);
LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e);
LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key);
LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k);
LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_goiffalse (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e);
LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults);
LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);
LUAI_FUNC int luaK_jump (FuncState *fs);
LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret);
LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target);
LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list);
LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2);
LUAI_FUNC int luaK_getlabel (FuncState *fs);
LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line);
LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v);
LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1,
expdesc *v2, int line);
LUAI_FUNC void luaK_settablesize (FuncState *fs, int pc,
int ra, int asize, int hsize);
LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore);
LUAI_FUNC void luaK_finish (FuncState *fs);
LUAI_FUNC l_noret luaK_semerror (LexState *ls, const char *msg);
#endif

View File

@ -1,210 +0,0 @@
/*
** $Id: lcorolib.c $
** Coroutine Library
** See Copyright Notice in lua.h
*/
#define lcorolib_c
#define LUA_LIB
#include "lprefix.h"
#include <stdlib.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
static lua_State *getco (lua_State *L) {
lua_State *co = lua_tothread(L, 1);
luaL_argexpected(L, co, 1, "thread");
return co;
}
/*
** Resumes a coroutine. Returns the number of results for non-error
** cases or -1 for errors.
*/
static int auxresume (lua_State *L, lua_State *co, int narg) {
int status, nres;
if (l_unlikely(!lua_checkstack(co, narg))) {
lua_pushliteral(L, "too many arguments to resume");
return -1; /* error flag */
}
lua_xmove(L, co, narg);
status = lua_resume(co, L, narg, &nres);
if (l_likely(status == LUA_OK || status == LUA_YIELD)) {
if (l_unlikely(!lua_checkstack(L, nres + 1))) {
lua_pop(co, nres); /* remove results anyway */
lua_pushliteral(L, "too many results to resume");
return -1; /* error flag */
}
lua_xmove(co, L, nres); /* move yielded values */
return nres;
}
else {
lua_xmove(co, L, 1); /* move error message */
return -1; /* error flag */
}
}
static int luaB_coresume (lua_State *L) {
lua_State *co = getco(L);
int r;
r = auxresume(L, co, lua_gettop(L) - 1);
if (l_unlikely(r < 0)) {
lua_pushboolean(L, 0);
lua_insert(L, -2);
return 2; /* return false + error message */
}
else {
lua_pushboolean(L, 1);
lua_insert(L, -(r + 1));
return r + 1; /* return true + 'resume' returns */
}
}
static int luaB_auxwrap (lua_State *L) {
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
int r = auxresume(L, co, lua_gettop(L));
if (l_unlikely(r < 0)) { /* error? */
int stat = lua_status(co);
if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */
stat = lua_closethread(co, L); /* close its tbc variables */
lua_assert(stat != LUA_OK);
lua_xmove(co, L, 1); /* move error message to the caller */
}
if (stat != LUA_ERRMEM && /* not a memory error and ... */
lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
luaL_where(L, 1); /* add extra info, if available */
lua_insert(L, -2);
lua_concat(L, 2);
}
return lua_error(L); /* propagate error */
}
return r;
}
static int luaB_cocreate (lua_State *L) {
lua_State *NL;
luaL_checktype(L, 1, LUA_TFUNCTION);
NL = lua_newthread(L);
lua_pushvalue(L, 1); /* move function to top */
lua_xmove(L, NL, 1); /* move function from L to NL */
return 1;
}
static int luaB_cowrap (lua_State *L) {
luaB_cocreate(L);
lua_pushcclosure(L, luaB_auxwrap, 1);
return 1;
}
static int luaB_yield (lua_State *L) {
return lua_yield(L, lua_gettop(L));
}
#define COS_RUN 0
#define COS_DEAD 1
#define COS_YIELD 2
#define COS_NORM 3
static const char *const statname[] =
{"running", "dead", "suspended", "normal"};
static int auxstatus (lua_State *L, lua_State *co) {
if (L == co) return COS_RUN;
else {
switch (lua_status(co)) {
case LUA_YIELD:
return COS_YIELD;
case LUA_OK: {
lua_Debug ar;
if (lua_getstack(co, 0, &ar)) /* does it have frames? */
return COS_NORM; /* it is running */
else if (lua_gettop(co) == 0)
return COS_DEAD;
else
return COS_YIELD; /* initial state */
}
default: /* some error occurred */
return COS_DEAD;
}
}
}
static int luaB_costatus (lua_State *L) {
lua_State *co = getco(L);
lua_pushstring(L, statname[auxstatus(L, co)]);
return 1;
}
static int luaB_yieldable (lua_State *L) {
lua_State *co = lua_isnone(L, 1) ? L : getco(L);
lua_pushboolean(L, lua_isyieldable(co));
return 1;
}
static int luaB_corunning (lua_State *L) {
int ismain = lua_pushthread(L);
lua_pushboolean(L, ismain);
return 2;
}
static int luaB_close (lua_State *L) {
lua_State *co = getco(L);
int status = auxstatus(L, co);
switch (status) {
case COS_DEAD: case COS_YIELD: {
status = lua_closethread(co, L);
if (status == LUA_OK) {
lua_pushboolean(L, 1);
return 1;
}
else {
lua_pushboolean(L, 0);
lua_xmove(co, L, 1); /* move error message */
return 2;
}
}
default: /* normal or running coroutine */
return luaL_error(L, "cannot close a %s coroutine", statname[status]);
}
}
static const luaL_Reg co_funcs[] = {
{"create", luaB_cocreate},
{"resume", luaB_coresume},
{"running", luaB_corunning},
{"status", luaB_costatus},
{"wrap", luaB_cowrap},
{"yield", luaB_yield},
{"isyieldable", luaB_yieldable},
{"close", luaB_close},
{NULL, NULL}
};
LUAMOD_API int luaopen_coroutine (lua_State *L) {
luaL_newlib(L, co_funcs);
return 1;
}

View File

@ -1,64 +0,0 @@
/*
** $Id: lctype.c $
** 'ctype' functions for Lua
** See Copyright Notice in lua.h
*/
#define lctype_c
#define LUA_CORE
#include "lprefix.h"
#include "lctype.h"
#if !LUA_USE_CTYPE /* { */
#include <limits.h>
#if defined (LUA_UCID) /* accept UniCode IDentifiers? */
/* consider all non-ascii codepoints to be alphabetic */
#define NONA 0x01
#else
#define NONA 0x00 /* default */
#endif
LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = {
0x00, /* EOZ */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */
0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */
0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05,
0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00,
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 8. */
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 9. */
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* a. */
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* b. */
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
0x00, 0x00, NONA, NONA, NONA, NONA, NONA, NONA, /* c. */
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* d. */
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* e. */
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
NONA, NONA, NONA, NONA, NONA, 0x00, 0x00, 0x00, /* f. */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
#endif /* } */

View File

@ -1,101 +0,0 @@
/*
** $Id: lctype.h $
** 'ctype' functions for Lua
** See Copyright Notice in lua.h
*/
#ifndef lctype_h
#define lctype_h
#include "lua.h"
/*
** WARNING: the functions defined here do not necessarily correspond
** to the similar functions in the standard C ctype.h. They are
** optimized for the specific needs of Lua.
*/
#if !defined(LUA_USE_CTYPE)
#if 'A' == 65 && '0' == 48
/* ASCII case: can use its own tables; faster and fixed */
#define LUA_USE_CTYPE 0
#else
/* must use standard C ctype */
#define LUA_USE_CTYPE 1
#endif
#endif
#if !LUA_USE_CTYPE /* { */
#include <limits.h>
#include "llimits.h"
#define ALPHABIT 0
#define DIGITBIT 1
#define PRINTBIT 2
#define SPACEBIT 3
#define XDIGITBIT 4
#define MASK(B) (1 << (B))
/*
** add 1 to char to allow index -1 (EOZ)
*/
#define testprop(c,p) (luai_ctype_[(c)+1] & (p))
/*
** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_'
*/
#define lislalpha(c) testprop(c, MASK(ALPHABIT))
#define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT)))
#define lisdigit(c) testprop(c, MASK(DIGITBIT))
#define lisspace(c) testprop(c, MASK(SPACEBIT))
#define lisprint(c) testprop(c, MASK(PRINTBIT))
#define lisxdigit(c) testprop(c, MASK(XDIGITBIT))
/*
** In ASCII, this 'ltolower' is correct for alphabetic characters and
** for '.'. That is enough for Lua needs. ('check_exp' ensures that
** the character either is an upper-case letter or is unchanged by
** the transformation, which holds for lower-case letters and '.'.)
*/
#define ltolower(c) \
check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \
(c) | ('A' ^ 'a'))
/* one entry for each character and for -1 (EOZ) */
LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];)
#else /* }{ */
/*
** use standard C ctypes
*/
#include <ctype.h>
#define lislalpha(c) (isalpha(c) || (c) == '_')
#define lislalnum(c) (isalnum(c) || (c) == '_')
#define lisdigit(c) (isdigit(c))
#define lisspace(c) (isspace(c))
#define lisprint(c) (isprint(c))
#define lisxdigit(c) (isxdigit(c))
#define ltolower(c) (tolower(c))
#endif /* } */
#endif

View File

@ -1,483 +0,0 @@
/*
** $Id: ldblib.c $
** Interface from Lua to its debug API
** See Copyright Notice in lua.h
*/
#define ldblib_c
#define LUA_LIB
#include "lprefix.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
/*
** The hook table at registry[HOOKKEY] maps threads to their current
** hook function.
*/
static const char *const HOOKKEY = "_HOOKKEY";
/*
** If L1 != L, L1 can be in any state, and therefore there are no
** guarantees about its stack space; any push in L1 must be
** checked.
*/
static void checkstack (lua_State *L, lua_State *L1, int n) {
if (l_unlikely(L != L1 && !lua_checkstack(L1, n)))
luaL_error(L, "stack overflow");
}
static int db_getregistry (lua_State *L) {
lua_pushvalue(L, LUA_REGISTRYINDEX);
return 1;
}
static int db_getmetatable (lua_State *L) {
luaL_checkany(L, 1);
if (!lua_getmetatable(L, 1)) {
lua_pushnil(L); /* no metatable */
}
return 1;
}
static int db_setmetatable (lua_State *L) {
int t = lua_type(L, 2);
luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
lua_settop(L, 2);
lua_setmetatable(L, 1);
return 1; /* return 1st argument */
}
static int db_getuservalue (lua_State *L) {
int n = (int)luaL_optinteger(L, 2, 1);
if (lua_type(L, 1) != LUA_TUSERDATA)
luaL_pushfail(L);
else if (lua_getiuservalue(L, 1, n) != LUA_TNONE) {
lua_pushboolean(L, 1);
return 2;
}
return 1;
}
static int db_setuservalue (lua_State *L) {
int n = (int)luaL_optinteger(L, 3, 1);
luaL_checktype(L, 1, LUA_TUSERDATA);
luaL_checkany(L, 2);
lua_settop(L, 2);
if (!lua_setiuservalue(L, 1, n))
luaL_pushfail(L);
return 1;
}
/*
** Auxiliary function used by several library functions: check for
** an optional thread as function's first argument and set 'arg' with
** 1 if this argument is present (so that functions can skip it to
** access their other arguments)
*/
static lua_State *getthread (lua_State *L, int *arg) {
if (lua_isthread(L, 1)) {
*arg = 1;
return lua_tothread(L, 1);
}
else {
*arg = 0;
return L; /* function will operate over current thread */
}
}
/*
** Variations of 'lua_settable', used by 'db_getinfo' to put results
** from 'lua_getinfo' into result table. Key is always a string;
** value can be a string, an int, or a boolean.
*/
static void settabss (lua_State *L, const char *k, const char *v) {
lua_pushstring(L, v);
lua_setfield(L, -2, k);
}
static void settabsi (lua_State *L, const char *k, int v) {
lua_pushinteger(L, v);
lua_setfield(L, -2, k);
}
static void settabsb (lua_State *L, const char *k, int v) {
lua_pushboolean(L, v);
lua_setfield(L, -2, k);
}
/*
** In function 'db_getinfo', the call to 'lua_getinfo' may push
** results on the stack; later it creates the result table to put
** these objects. Function 'treatstackoption' puts the result from
** 'lua_getinfo' on top of the result table so that it can call
** 'lua_setfield'.
*/
static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) {
if (L == L1)
lua_rotate(L, -2, 1); /* exchange object and table */
else
lua_xmove(L1, L, 1); /* move object to the "main" stack */
lua_setfield(L, -2, fname); /* put object into table */
}
/*
** Calls 'lua_getinfo' and collects all results in a new table.
** L1 needs stack space for an optional input (function) plus
** two optional outputs (function and line table) from function
** 'lua_getinfo'.
*/
static int db_getinfo (lua_State *L) {
lua_Debug ar;
int arg;
lua_State *L1 = getthread(L, &arg);
const char *options = luaL_optstring(L, arg+2, "flnSrtu");
checkstack(L, L1, 3);
luaL_argcheck(L, options[0] != '>', arg + 2, "invalid option '>'");
if (lua_isfunction(L, arg + 1)) { /* info about a function? */
options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */
lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */
lua_xmove(L, L1, 1);
}
else { /* stack level */
if (!lua_getstack(L1, (int)luaL_checkinteger(L, arg + 1), &ar)) {
luaL_pushfail(L); /* level out of range */
return 1;
}
}
if (!lua_getinfo(L1, options, &ar))
return luaL_argerror(L, arg+2, "invalid option");
lua_newtable(L); /* table to collect results */
if (strchr(options, 'S')) {
lua_pushlstring(L, ar.source, ar.srclen);
lua_setfield(L, -2, "source");
settabss(L, "short_src", ar.short_src);
settabsi(L, "linedefined", ar.linedefined);
settabsi(L, "lastlinedefined", ar.lastlinedefined);
settabss(L, "what", ar.what);
}
if (strchr(options, 'l'))
settabsi(L, "currentline", ar.currentline);
if (strchr(options, 'u')) {
settabsi(L, "nups", ar.nups);
settabsi(L, "nparams", ar.nparams);
settabsb(L, "isvararg", ar.isvararg);
}
if (strchr(options, 'n')) {
settabss(L, "name", ar.name);
settabss(L, "namewhat", ar.namewhat);
}
if (strchr(options, 'r')) {
settabsi(L, "ftransfer", ar.ftransfer);
settabsi(L, "ntransfer", ar.ntransfer);
}
if (strchr(options, 't'))
settabsb(L, "istailcall", ar.istailcall);
if (strchr(options, 'L'))
treatstackoption(L, L1, "activelines");
if (strchr(options, 'f'))
treatstackoption(L, L1, "func");
return 1; /* return table */
}
static int db_getlocal (lua_State *L) {
int arg;
lua_State *L1 = getthread(L, &arg);
int nvar = (int)luaL_checkinteger(L, arg + 2); /* local-variable index */
if (lua_isfunction(L, arg + 1)) { /* function argument? */
lua_pushvalue(L, arg + 1); /* push function */
lua_pushstring(L, lua_getlocal(L, NULL, nvar)); /* push local name */
return 1; /* return only name (there is no value) */
}
else { /* stack-level argument */
lua_Debug ar;
const char *name;
int level = (int)luaL_checkinteger(L, arg + 1);
if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */
return luaL_argerror(L, arg+1, "level out of range");
checkstack(L, L1, 1);
name = lua_getlocal(L1, &ar, nvar);
if (name) {
lua_xmove(L1, L, 1); /* move local value */
lua_pushstring(L, name); /* push name */
lua_rotate(L, -2, 1); /* re-order */
return 2;
}
else {
luaL_pushfail(L); /* no name (nor value) */
return 1;
}
}
}
static int db_setlocal (lua_State *L) {
int arg;
const char *name;
lua_State *L1 = getthread(L, &arg);
lua_Debug ar;
int level = (int)luaL_checkinteger(L, arg + 1);
int nvar = (int)luaL_checkinteger(L, arg + 2);
if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */
return luaL_argerror(L, arg+1, "level out of range");
luaL_checkany(L, arg+3);
lua_settop(L, arg+3);
checkstack(L, L1, 1);
lua_xmove(L, L1, 1);
name = lua_setlocal(L1, &ar, nvar);
if (name == NULL)
lua_pop(L1, 1); /* pop value (if not popped by 'lua_setlocal') */
lua_pushstring(L, name);
return 1;
}
/*
** get (if 'get' is true) or set an upvalue from a closure
*/
static int auxupvalue (lua_State *L, int get) {
const char *name;
int n = (int)luaL_checkinteger(L, 2); /* upvalue index */
luaL_checktype(L, 1, LUA_TFUNCTION); /* closure */
name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
if (name == NULL) return 0;
lua_pushstring(L, name);
lua_insert(L, -(get+1)); /* no-op if get is false */
return get + 1;
}
static int db_getupvalue (lua_State *L) {
return auxupvalue(L, 1);
}
static int db_setupvalue (lua_State *L) {
luaL_checkany(L, 3);
return auxupvalue(L, 0);
}
/*
** Check whether a given upvalue from a given closure exists and
** returns its index
*/
static void *checkupval (lua_State *L, int argf, int argnup, int *pnup) {
void *id;
int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */
luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */
id = lua_upvalueid(L, argf, nup);
if (pnup) {
luaL_argcheck(L, id != NULL, argnup, "invalid upvalue index");
*pnup = nup;
}
return id;
}
static int db_upvalueid (lua_State *L) {
void *id = checkupval(L, 1, 2, NULL);
if (id != NULL)
lua_pushlightuserdata(L, id);
else
luaL_pushfail(L);
return 1;
}
static int db_upvaluejoin (lua_State *L) {
int n1, n2;
checkupval(L, 1, 2, &n1);
checkupval(L, 3, 4, &n2);
luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected");
luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected");
lua_upvaluejoin(L, 1, n1, 3, n2);
return 0;
}
/*
** Call hook function registered at hook table for the current
** thread (if there is one)
*/
static void hookf (lua_State *L, lua_Debug *ar) {
static const char *const hooknames[] =
{"call", "return", "line", "count", "tail call"};
lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY);
lua_pushthread(L);
if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */
lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */
if (ar->currentline >= 0)
lua_pushinteger(L, ar->currentline); /* push current line */
else lua_pushnil(L);
lua_assert(lua_getinfo(L, "lS", ar));
lua_call(L, 2, 0); /* call hook function */
}
}
/*
** Convert a string mask (for 'sethook') into a bit mask
*/
static int makemask (const char *smask, int count) {
int mask = 0;
if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
if (strchr(smask, 'r')) mask |= LUA_MASKRET;
if (strchr(smask, 'l')) mask |= LUA_MASKLINE;
if (count > 0) mask |= LUA_MASKCOUNT;
return mask;
}
/*
** Convert a bit mask (for 'gethook') into a string mask
*/
static char *unmakemask (int mask, char *smask) {
int i = 0;
if (mask & LUA_MASKCALL) smask[i++] = 'c';
if (mask & LUA_MASKRET) smask[i++] = 'r';
if (mask & LUA_MASKLINE) smask[i++] = 'l';
smask[i] = '\0';
return smask;
}
static int db_sethook (lua_State *L) {
int arg, mask, count;
lua_Hook func;
lua_State *L1 = getthread(L, &arg);
if (lua_isnoneornil(L, arg+1)) { /* no hook? */
lua_settop(L, arg+1);
func = NULL; mask = 0; count = 0; /* turn off hooks */
}
else {
const char *smask = luaL_checkstring(L, arg+2);
luaL_checktype(L, arg+1, LUA_TFUNCTION);
count = (int)luaL_optinteger(L, arg + 3, 0);
func = hookf; mask = makemask(smask, count);
}
if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) {
/* table just created; initialize it */
lua_pushliteral(L, "k");
lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
lua_pushvalue(L, -1);
lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */
}
checkstack(L, L1, 1);
lua_pushthread(L1); lua_xmove(L1, L, 1); /* key (thread) */
lua_pushvalue(L, arg + 1); /* value (hook function) */
lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */
lua_sethook(L1, func, mask, count);
return 0;
}
static int db_gethook (lua_State *L) {
int arg;
lua_State *L1 = getthread(L, &arg);
char buff[5];
int mask = lua_gethookmask(L1);
lua_Hook hook = lua_gethook(L1);
if (hook == NULL) { /* no hook? */
luaL_pushfail(L);
return 1;
}
else if (hook != hookf) /* external hook? */
lua_pushliteral(L, "external hook");
else { /* hook table must exist */
lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY);
checkstack(L, L1, 1);
lua_pushthread(L1); lua_xmove(L1, L, 1);
lua_rawget(L, -2); /* 1st result = hooktable[L1] */
lua_remove(L, -2); /* remove hook table */
}
lua_pushstring(L, unmakemask(mask, buff)); /* 2nd result = mask */
lua_pushinteger(L, lua_gethookcount(L1)); /* 3rd result = count */
return 3;
}
static int db_debug (lua_State *L) {
for (;;) {
char buffer[250];
lua_writestringerror("%s", "lua_debug> ");
if (fgets(buffer, sizeof(buffer), stdin) == NULL ||
strcmp(buffer, "cont\n") == 0)
return 0;
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
lua_pcall(L, 0, 0, 0))
lua_writestringerror("%s\n", luaL_tolstring(L, -1, NULL));
lua_settop(L, 0); /* remove eventual returns */
}
}
static int db_traceback (lua_State *L) {
int arg;
lua_State *L1 = getthread(L, &arg);
const char *msg = lua_tostring(L, arg + 1);
if (msg == NULL && !lua_isnoneornil(L, arg + 1)) /* non-string 'msg'? */
lua_pushvalue(L, arg + 1); /* return it untouched */
else {
int level = (int)luaL_optinteger(L, arg + 2, (L == L1) ? 1 : 0);
luaL_traceback(L, L1, msg, level);
}
return 1;
}
static int db_setcstacklimit (lua_State *L) {
int limit = (int)luaL_checkinteger(L, 1);
int res = lua_setcstacklimit(L, limit);
lua_pushinteger(L, res);
return 1;
}
static const luaL_Reg dblib[] = {
{"debug", db_debug},
{"getuservalue", db_getuservalue},
{"gethook", db_gethook},
{"getinfo", db_getinfo},
{"getlocal", db_getlocal},
{"getregistry", db_getregistry},
{"getmetatable", db_getmetatable},
{"getupvalue", db_getupvalue},
{"upvaluejoin", db_upvaluejoin},
{"upvalueid", db_upvalueid},
{"setuservalue", db_setuservalue},
{"sethook", db_sethook},
{"setlocal", db_setlocal},
{"setmetatable", db_setmetatable},
{"setupvalue", db_setupvalue},
{"traceback", db_traceback},
{"setcstacklimit", db_setcstacklimit},
{NULL, NULL}
};
LUAMOD_API int luaopen_debug (lua_State *L) {
luaL_newlib(L, dblib);
return 1;
}

View File

@ -1,962 +0,0 @@
/*
** $Id: ldebug.c $
** Debug Interface
** See Copyright Notice in lua.h
*/
#define ldebug_c
#define LUA_CORE
#include "lprefix.h"
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#include "lua.h"
#include "lapi.h"
#include "lcode.h"
#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#include "lvm.h"
#define LuaClosure(f) ((f) != NULL && (f)->c.tt == LUA_VLCL)
static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
const char **name);
static int currentpc (CallInfo *ci) {
lua_assert(isLua(ci));
return pcRel(ci->u.l.savedpc, ci_func(ci)->p);
}
/*
** Get a "base line" to find the line corresponding to an instruction.
** Base lines are regularly placed at MAXIWTHABS intervals, so usually
** an integer division gets the right place. When the source file has
** large sequences of empty/comment lines, it may need extra entries,
** so the original estimate needs a correction.
** If the original estimate is -1, the initial 'if' ensures that the
** 'while' will run at least once.
** The assertion that the estimate is a lower bound for the correct base
** is valid as long as the debug info has been generated with the same
** value for MAXIWTHABS or smaller. (Previous releases use a little
** smaller value.)
*/
static int getbaseline (const Proto *f, int pc, int *basepc) {
if (f->sizeabslineinfo == 0 || pc < f->abslineinfo[0].pc) {
*basepc = -1; /* start from the beginning */
return f->linedefined;
}
else {
int i = cast_uint(pc) / MAXIWTHABS - 1; /* get an estimate */
/* estimate must be a lower bound of the correct base */
lua_assert(i < 0 ||
(i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc));
while (i + 1 < f->sizeabslineinfo && pc >= f->abslineinfo[i + 1].pc)
i++; /* low estimate; adjust it */
*basepc = f->abslineinfo[i].pc;
return f->abslineinfo[i].line;
}
}
/*
** Get the line corresponding to instruction 'pc' in function 'f';
** first gets a base line and from there does the increments until
** the desired instruction.
*/
int luaG_getfuncline (const Proto *f, int pc) {
if (f->lineinfo == NULL) /* no debug information? */
return -1;
else {
int basepc;
int baseline = getbaseline(f, pc, &basepc);
while (basepc++ < pc) { /* walk until given instruction */
lua_assert(f->lineinfo[basepc] != ABSLINEINFO);
baseline += f->lineinfo[basepc]; /* correct line */
}
return baseline;
}
}
static int getcurrentline (CallInfo *ci) {
return luaG_getfuncline(ci_func(ci)->p, currentpc(ci));
}
/*
** Set 'trap' for all active Lua frames.
** This function can be called during a signal, under "reasonable"
** assumptions. A new 'ci' is completely linked in the list before it
** becomes part of the "active" list, and we assume that pointers are
** atomic; see comment in next function.
** (A compiler doing interprocedural optimizations could, theoretically,
** reorder memory writes in such a way that the list could be
** temporarily broken while inserting a new element. We simply assume it
** has no good reasons to do that.)
*/
static void settraps (CallInfo *ci) {
for (; ci != NULL; ci = ci->previous)
if (isLua(ci))
ci->u.l.trap = 1;
}
/*
** This function can be called during a signal, under "reasonable"
** assumptions.
** Fields 'basehookcount' and 'hookcount' (set by 'resethookcount')
** are for debug only, and it is no problem if they get arbitrary
** values (causes at most one wrong hook call). 'hookmask' is an atomic
** value. We assume that pointers are atomic too (e.g., gcc ensures that
** for all platforms where it runs). Moreover, 'hook' is always checked
** before being called (see 'luaD_hook').
*/
LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
if (func == NULL || mask == 0) { /* turn off hooks? */
mask = 0;
func = NULL;
}
L->hook = func;
L->basehookcount = count;
resethookcount(L);
L->hookmask = cast_byte(mask);
if (mask)
settraps(L->ci); /* to trace inside 'luaV_execute' */
}
LUA_API lua_Hook lua_gethook (lua_State *L) {
return L->hook;
}
LUA_API int lua_gethookmask (lua_State *L) {
return L->hookmask;
}
LUA_API int lua_gethookcount (lua_State *L) {
return L->basehookcount;
}
LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) {
int status;
CallInfo *ci;
if (level < 0) return 0; /* invalid (negative) level */
lua_lock(L);
for (ci = L->ci; level > 0 && ci != &L->base_ci; ci = ci->previous)
level--;
if (level == 0 && ci != &L->base_ci) { /* level found? */
status = 1;
ar->i_ci = ci;
}
else status = 0; /* no such level */
lua_unlock(L);
return status;
}
static const char *upvalname (const Proto *p, int uv) {
TString *s = check_exp(uv < p->sizeupvalues, p->upvalues[uv].name);
if (s == NULL) return "?";
else return getstr(s);
}
static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
if (clLvalue(s2v(ci->func.p))->p->is_vararg) {
int nextra = ci->u.l.nextraargs;
if (n >= -nextra) { /* 'n' is negative */
*pos = ci->func.p - nextra - (n + 1);
return "(vararg)"; /* generic name for any vararg */
}
}
return NULL; /* no such vararg */
}
const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
StkId base = ci->func.p + 1;
const char *name = NULL;
if (isLua(ci)) {
if (n < 0) /* access to vararg values? */
return findvararg(ci, n, pos);
else
name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
}
if (name == NULL) { /* no 'standard' name? */
StkId limit = (ci == L->ci) ? L->top.p : ci->next->func.p;
if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */
/* generic name for any valid slot */
name = isLua(ci) ? "(temporary)" : "(C temporary)";
}
else
return NULL; /* no name */
}
if (pos)
*pos = base + (n - 1);
return name;
}
LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
const char *name;
lua_lock(L);
if (ar == NULL) { /* information about non-active function? */
if (!isLfunction(s2v(L->top.p - 1))) /* not a Lua function? */
name = NULL;
else /* consider live variables at function start (parameters) */
name = luaF_getlocalname(clLvalue(s2v(L->top.p - 1))->p, n, 0);
}
else { /* active function; get information through 'ar' */
StkId pos = NULL; /* to avoid warnings */
name = luaG_findlocal(L, ar->i_ci, n, &pos);
if (name) {
setobjs2s(L, L->top.p, pos);
api_incr_top(L);
}
}
lua_unlock(L);
return name;
}
LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
StkId pos = NULL; /* to avoid warnings */
const char *name;
lua_lock(L);
name = luaG_findlocal(L, ar->i_ci, n, &pos);
if (name) {
setobjs2s(L, pos, L->top.p - 1);
L->top.p--; /* pop value */
}
lua_unlock(L);
return name;
}
static void funcinfo (lua_Debug *ar, Closure *cl) {
if (!LuaClosure(cl)) {
ar->source = "=[C]";
ar->srclen = LL("=[C]");
ar->linedefined = -1;
ar->lastlinedefined = -1;
ar->what = "C";
}
else {
const Proto *p = cl->l.p;
if (p->source) {
ar->source = getstr(p->source);
ar->srclen = tsslen(p->source);
}
else {
ar->source = "=?";
ar->srclen = LL("=?");
}
ar->linedefined = p->linedefined;
ar->lastlinedefined = p->lastlinedefined;
ar->what = (ar->linedefined == 0) ? "main" : "Lua";
}
luaO_chunkid(ar->short_src, ar->source, ar->srclen);
}
static int nextline (const Proto *p, int currentline, int pc) {
if (p->lineinfo[pc] != ABSLINEINFO)
return currentline + p->lineinfo[pc];
else
return luaG_getfuncline(p, pc);
}
static void collectvalidlines (lua_State *L, Closure *f) {
if (!LuaClosure(f)) {
setnilvalue(s2v(L->top.p));
api_incr_top(L);
}
else {
const Proto *p = f->l.p;
int currentline = p->linedefined;
Table *t = luaH_new(L); /* new table to store active lines */
sethvalue2s(L, L->top.p, t); /* push it on stack */
api_incr_top(L);
if (p->lineinfo != NULL) { /* proto with debug information? */
int i;
TValue v;
setbtvalue(&v); /* boolean 'true' to be the value of all indices */
if (!p->is_vararg) /* regular function? */
i = 0; /* consider all instructions */
else { /* vararg function */
lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP);
currentline = nextline(p, currentline, 0);
i = 1; /* skip first instruction (OP_VARARGPREP) */
}
for (; i < p->sizelineinfo; i++) { /* for each instruction */
currentline = nextline(p, currentline, i); /* get its line */
luaH_setint(L, t, currentline, &v); /* table[line] = true */
}
}
}
}
static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {
/* calling function is a known function? */
if (ci != NULL && !(ci->callstatus & CIST_TAIL))
return funcnamefromcall(L, ci->previous, name);
else return NULL; /* no way to find a name */
}
static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
Closure *f, CallInfo *ci) {
int status = 1;
for (; *what; what++) {
switch (*what) {
case 'S': {
funcinfo(ar, f);
break;
}
case 'l': {
ar->currentline = (ci && isLua(ci)) ? getcurrentline(ci) : -1;
break;
}
case 'u': {
ar->nups = (f == NULL) ? 0 : f->c.nupvalues;
if (!LuaClosure(f)) {
ar->isvararg = 1;
ar->nparams = 0;
}
else {
ar->isvararg = f->l.p->is_vararg;
ar->nparams = f->l.p->numparams;
}
break;
}
case 't': {
ar->istailcall = (ci) ? ci->callstatus & CIST_TAIL : 0;
break;
}
case 'n': {
ar->namewhat = getfuncname(L, ci, &ar->name);
if (ar->namewhat == NULL) {
ar->namewhat = ""; /* not found */
ar->name = NULL;
}
break;
}
case 'r': {
if (ci == NULL || !(ci->callstatus & CIST_TRAN))
ar->ftransfer = ar->ntransfer = 0;
else {
ar->ftransfer = ci->u2.transferinfo.ftransfer;
ar->ntransfer = ci->u2.transferinfo.ntransfer;
}
break;
}
case 'L':
case 'f': /* handled by lua_getinfo */
break;
default: status = 0; /* invalid option */
}
}
return status;
}
LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
int status;
Closure *cl;
CallInfo *ci;
TValue *func;
lua_lock(L);
if (*what == '>') {
ci = NULL;
func = s2v(L->top.p - 1);
api_check(L, ttisfunction(func), "function expected");
what++; /* skip the '>' */
L->top.p--; /* pop function */
}
else {
ci = ar->i_ci;
func = s2v(ci->func.p);
lua_assert(ttisfunction(func));
}
cl = ttisclosure(func) ? clvalue(func) : NULL;
status = auxgetinfo(L, what, ar, cl, ci);
if (strchr(what, 'f')) {
setobj2s(L, L->top.p, func);
api_incr_top(L);
}
if (strchr(what, 'L'))
collectvalidlines(L, cl);
lua_unlock(L);
return status;
}
/*
** {======================================================
** Symbolic Execution
** =======================================================
*/
static int filterpc (int pc, int jmptarget) {
if (pc < jmptarget) /* is code conditional (inside a jump)? */
return -1; /* cannot know who sets that register */
else return pc; /* current position sets that register */
}
/*
** Try to find last instruction before 'lastpc' that modified register 'reg'.
*/
static int findsetreg (const Proto *p, int lastpc, int reg) {
int pc;
int setreg = -1; /* keep last instruction that changed 'reg' */
int jmptarget = 0; /* any code before this address is conditional */
if (testMMMode(GET_OPCODE(p->code[lastpc])))
lastpc--; /* previous instruction was not actually executed */
for (pc = 0; pc < lastpc; pc++) {
Instruction i = p->code[pc];
OpCode op = GET_OPCODE(i);
int a = GETARG_A(i);
int change; /* true if current instruction changed 'reg' */
switch (op) {
case OP_LOADNIL: { /* set registers from 'a' to 'a+b' */
int b = GETARG_B(i);
change = (a <= reg && reg <= a + b);
break;
}
case OP_TFORCALL: { /* affect all regs above its base */
change = (reg >= a + 2);
break;
}
case OP_CALL:
case OP_TAILCALL: { /* affect all registers above base */
change = (reg >= a);
break;
}
case OP_JMP: { /* doesn't change registers, but changes 'jmptarget' */
int b = GETARG_sJ(i);
int dest = pc + 1 + b;
/* jump does not skip 'lastpc' and is larger than current one? */
if (dest <= lastpc && dest > jmptarget)
jmptarget = dest; /* update 'jmptarget' */
change = 0;
break;
}
default: /* any instruction that sets A */
change = (testAMode(op) && reg == a);
break;
}
if (change)
setreg = filterpc(pc, jmptarget);
}
return setreg;
}
/*
** Find a "name" for the constant 'c'.
*/
static const char *kname (const Proto *p, int index, const char **name) {
TValue *kvalue = &p->k[index];
if (ttisstring(kvalue)) {
*name = getstr(tsvalue(kvalue));
return "constant";
}
else {
*name = "?";
return NULL;
}
}
static const char *basicgetobjname (const Proto *p, int *ppc, int reg,
const char **name) {
int pc = *ppc;
*name = luaF_getlocalname(p, reg + 1, pc);
if (*name) /* is a local? */
return "local";
/* else try symbolic execution */
*ppc = pc = findsetreg(p, pc, reg);
if (pc != -1) { /* could find instruction? */
Instruction i = p->code[pc];
OpCode op = GET_OPCODE(i);
switch (op) {
case OP_MOVE: {
int b = GETARG_B(i); /* move from 'b' to 'a' */
if (b < GETARG_A(i))
return basicgetobjname(p, ppc, b, name); /* get name for 'b' */
break;
}
case OP_GETUPVAL: {
*name = upvalname(p, GETARG_B(i));
return "upvalue";
}
case OP_LOADK: return kname(p, GETARG_Bx(i), name);
case OP_LOADKX: return kname(p, GETARG_Ax(p->code[pc + 1]), name);
default: break;
}
}
return NULL; /* could not find reasonable name */
}
/*
** Find a "name" for the register 'c'.
*/
static void rname (const Proto *p, int pc, int c, const char **name) {
const char *what = basicgetobjname(p, &pc, c, name); /* search for 'c' */
if (!(what && *what == 'c')) /* did not find a constant name? */
*name = "?";
}
/*
** Find a "name" for a 'C' value in an RK instruction.
*/
static void rkname (const Proto *p, int pc, Instruction i, const char **name) {
int c = GETARG_C(i); /* key index */
if (GETARG_k(i)) /* is 'c' a constant? */
kname(p, c, name);
else /* 'c' is a register */
rname(p, pc, c, name);
}
/*
** Check whether table being indexed by instruction 'i' is the
** environment '_ENV'
*/
static const char *isEnv (const Proto *p, int pc, Instruction i, int isup) {
int t = GETARG_B(i); /* table index */
const char *name; /* name of indexed variable */
if (isup) /* is 't' an upvalue? */
name = upvalname(p, t);
else /* 't' is a register */
basicgetobjname(p, &pc, t, &name);
return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field";
}
/*
** Extend 'basicgetobjname' to handle table accesses
*/
static const char *getobjname (const Proto *p, int lastpc, int reg,
const char **name) {
const char *kind = basicgetobjname(p, &lastpc, reg, name);
if (kind != NULL)
return kind;
else if (lastpc != -1) { /* could find instruction? */
Instruction i = p->code[lastpc];
OpCode op = GET_OPCODE(i);
switch (op) {
case OP_GETTABUP: {
int k = GETARG_C(i); /* key index */
kname(p, k, name);
return isEnv(p, lastpc, i, 1);
}
case OP_GETTABLE: {
int k = GETARG_C(i); /* key index */
rname(p, lastpc, k, name);
return isEnv(p, lastpc, i, 0);
}
case OP_GETI: {
*name = "integer index";
return "field";
}
case OP_GETFIELD: {
int k = GETARG_C(i); /* key index */
kname(p, k, name);
return isEnv(p, lastpc, i, 0);
}
case OP_SELF: {
rkname(p, lastpc, i, name);
return "method";
}
default: break; /* go through to return NULL */
}
}
return NULL; /* could not find reasonable name */
}
/*
** Try to find a name for a function based on the code that called it.
** (Only works when function was called by a Lua function.)
** Returns what the name is (e.g., "for iterator", "method",
** "metamethod") and sets '*name' to point to the name.
*/
static const char *funcnamefromcode (lua_State *L, const Proto *p,
int pc, const char **name) {
TMS tm = (TMS)0; /* (initial value avoids warnings) */
Instruction i = p->code[pc]; /* calling instruction */
switch (GET_OPCODE(i)) {
case OP_CALL:
case OP_TAILCALL:
return getobjname(p, pc, GETARG_A(i), name); /* get function name */
case OP_TFORCALL: { /* for iterator */
*name = "for iterator";
return "for iterator";
}
/* other instructions can do calls through metamethods */
case OP_SELF: case OP_GETTABUP: case OP_GETTABLE:
case OP_GETI: case OP_GETFIELD:
tm = TM_INDEX;
break;
case OP_SETTABUP: case OP_SETTABLE: case OP_SETI: case OP_SETFIELD:
tm = TM_NEWINDEX;
break;
case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: {
tm = cast(TMS, GETARG_C(i));
break;
}
case OP_UNM: tm = TM_UNM; break;
case OP_BNOT: tm = TM_BNOT; break;
case OP_LEN: tm = TM_LEN; break;
case OP_CONCAT: tm = TM_CONCAT; break;
case OP_EQ: tm = TM_EQ; break;
/* no cases for OP_EQI and OP_EQK, as they don't call metamethods */
case OP_LT: case OP_LTI: case OP_GTI: tm = TM_LT; break;
case OP_LE: case OP_LEI: case OP_GEI: tm = TM_LE; break;
case OP_CLOSE: case OP_RETURN: tm = TM_CLOSE; break;
default:
return NULL; /* cannot find a reasonable name */
}
*name = getshrstr(G(L)->tmname[tm]) + 2;
return "metamethod";
}
/*
** Try to find a name for a function based on how it was called.
*/
static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
const char **name) {
if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */
*name = "?";
return "hook";
}
else if (ci->callstatus & CIST_FIN) { /* was it called as a finalizer? */
*name = "__gc";
return "metamethod"; /* report it as such */
}
else if (isLua(ci))
return funcnamefromcode(L, ci_func(ci)->p, currentpc(ci), name);
else
return NULL;
}
/* }====================================================== */
/*
** Check whether pointer 'o' points to some value in the stack frame of
** the current function and, if so, returns its index. Because 'o' may
** not point to a value in this stack, we cannot compare it with the
** region boundaries (undefined behavior in ISO C).
*/
static int instack (CallInfo *ci, const TValue *o) {
int pos;
StkId base = ci->func.p + 1;
for (pos = 0; base + pos < ci->top.p; pos++) {
if (o == s2v(base + pos))
return pos;
}
return -1; /* not found */
}
/*
** Checks whether value 'o' came from an upvalue. (That can only happen
** with instructions OP_GETTABUP/OP_SETTABUP, which operate directly on
** upvalues.)
*/
static const char *getupvalname (CallInfo *ci, const TValue *o,
const char **name) {
LClosure *c = ci_func(ci);
int i;
for (i = 0; i < c->nupvalues; i++) {
if (c->upvals[i]->v.p == o) {
*name = upvalname(c->p, i);
return "upvalue";
}
}
return NULL;
}
static const char *formatvarinfo (lua_State *L, const char *kind,
const char *name) {
if (kind == NULL)
return ""; /* no information */
else
return luaO_pushfstring(L, " (%s '%s')", kind, name);
}
/*
** Build a string with a "description" for the value 'o', such as
** "variable 'x'" or "upvalue 'y'".
*/
static const char *varinfo (lua_State *L, const TValue *o) {
CallInfo *ci = L->ci;
const char *name = NULL; /* to avoid warnings */
const char *kind = NULL;
if (isLua(ci)) {
kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */
if (!kind) { /* not an upvalue? */
int reg = instack(ci, o); /* try a register */
if (reg >= 0) /* is 'o' a register? */
kind = getobjname(ci_func(ci)->p, currentpc(ci), reg, &name);
}
}
return formatvarinfo(L, kind, name);
}
/*
** Raise a type error
*/
static l_noret typeerror (lua_State *L, const TValue *o, const char *op,
const char *extra) {
const char *t = luaT_objtypename(L, o);
luaG_runerror(L, "attempt to %s a %s value%s", op, t, extra);
}
/*
** Raise a type error with "standard" information about the faulty
** object 'o' (using 'varinfo').
*/
l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
typeerror(L, o, op, varinfo(L, o));
}
/*
** Raise an error for calling a non-callable object. Try to find a name
** for the object based on how it was called ('funcnamefromcall'); if it
** cannot get a name there, try 'varinfo'.
*/
l_noret luaG_callerror (lua_State *L, const TValue *o) {
CallInfo *ci = L->ci;
const char *name = NULL; /* to avoid warnings */
const char *kind = funcnamefromcall(L, ci, &name);
const char *extra = kind ? formatvarinfo(L, kind, name) : varinfo(L, o);
typeerror(L, o, "call", extra);
}
l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what) {
luaG_runerror(L, "bad 'for' %s (number expected, got %s)",
what, luaT_objtypename(L, o));
}
l_noret luaG_concaterror (lua_State *L, const TValue *p1, const TValue *p2) {
if (ttisstring(p1) || cvt2str(p1)) p1 = p2;
luaG_typeerror(L, p1, "concatenate");
}
l_noret luaG_opinterror (lua_State *L, const TValue *p1,
const TValue *p2, const char *msg) {
if (!ttisnumber(p1)) /* first operand is wrong? */
p2 = p1; /* now second is wrong */
luaG_typeerror(L, p2, msg);
}
/*
** Error when both values are convertible to numbers, but not to integers
*/
l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) {
lua_Integer temp;
if (!luaV_tointegerns(p1, &temp, LUA_FLOORN2I))
p2 = p1;
luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2));
}
l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) {
const char *t1 = luaT_objtypename(L, p1);
const char *t2 = luaT_objtypename(L, p2);
if (strcmp(t1, t2) == 0)
luaG_runerror(L, "attempt to compare two %s values", t1);
else
luaG_runerror(L, "attempt to compare %s with %s", t1, t2);
}
/* add src:line information to 'msg' */
const char *luaG_addinfo (lua_State *L, const char *msg, TString *src,
int line) {
char buff[LUA_IDSIZE];
if (src)
luaO_chunkid(buff, getstr(src), tsslen(src));
else { /* no source available; use "?" instead */
buff[0] = '?'; buff[1] = '\0';
}
return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg);
}
l_noret luaG_errormsg (lua_State *L) {
if (L->errfunc != 0) { /* is there an error handling function? */
StkId errfunc = restorestack(L, L->errfunc);
lua_assert(ttisfunction(s2v(errfunc)));
setobjs2s(L, L->top.p, L->top.p - 1); /* move argument */
setobjs2s(L, L->top.p - 1, errfunc); /* push function */
L->top.p++; /* assume EXTRA_STACK */
luaD_callnoyield(L, L->top.p - 2, 1); /* call it */
}
luaD_throw(L, LUA_ERRRUN);
}
l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
CallInfo *ci = L->ci;
const char *msg;
va_list argp;
luaC_checkGC(L); /* error message uses memory */
va_start(argp, fmt);
msg = luaO_pushvfstring(L, fmt, argp); /* format message */
va_end(argp);
if (isLua(ci)) { /* if Lua function, add source:line information */
luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci));
setobjs2s(L, L->top.p - 2, L->top.p - 1); /* remove 'msg' */
L->top.p--;
}
luaG_errormsg(L);
}
/*
** Check whether new instruction 'newpc' is in a different line from
** previous instruction 'oldpc'. More often than not, 'newpc' is only
** one or a few instructions after 'oldpc' (it must be after, see
** caller), so try to avoid calling 'luaG_getfuncline'. If they are
** too far apart, there is a good chance of a ABSLINEINFO in the way,
** so it goes directly to 'luaG_getfuncline'.
*/
static int changedline (const Proto *p, int oldpc, int newpc) {
if (p->lineinfo == NULL) /* no debug information? */
return 0;
if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */
int delta = 0; /* line difference */
int pc = oldpc;
for (;;) {
int lineinfo = p->lineinfo[++pc];
if (lineinfo == ABSLINEINFO)
break; /* cannot compute delta; fall through */
delta += lineinfo;
if (pc == newpc)
return (delta != 0); /* delta computed successfully */
}
}
/* either instructions are too far apart or there is an absolute line
info in the way; compute line difference explicitly */
return (luaG_getfuncline(p, oldpc) != luaG_getfuncline(p, newpc));
}
/*
** Traces Lua calls. If code is running the first instruction of a function,
** and function is not vararg, and it is not coming from an yield,
** calls 'luaD_hookcall'. (Vararg functions will call 'luaD_hookcall'
** after adjusting its variable arguments; otherwise, they could call
** a line/count hook before the call hook. Functions coming from
** an yield already called 'luaD_hookcall' before yielding.)
*/
int luaG_tracecall (lua_State *L) {
CallInfo *ci = L->ci;
Proto *p = ci_func(ci)->p;
ci->u.l.trap = 1; /* ensure hooks will be checked */
if (ci->u.l.savedpc == p->code) { /* first instruction (not resuming)? */
if (p->is_vararg)
return 0; /* hooks will start at VARARGPREP instruction */
else if (!(ci->callstatus & CIST_HOOKYIELD)) /* not yieded? */
luaD_hookcall(L, ci); /* check 'call' hook */
}
return 1; /* keep 'trap' on */
}
/*
** Traces the execution of a Lua function. Called before the execution
** of each opcode, when debug is on. 'L->oldpc' stores the last
** instruction traced, to detect line changes. When entering a new
** function, 'npci' will be zero and will test as a new line whatever
** the value of 'oldpc'. Some exceptional conditions may return to
** a function without setting 'oldpc'. In that case, 'oldpc' may be
** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc'
** at most causes an extra call to a line hook.)
** This function is not "Protected" when called, so it should correct
** 'L->top.p' before calling anything that can run the GC.
*/
int luaG_traceexec (lua_State *L, const Instruction *pc) {
CallInfo *ci = L->ci;
lu_byte mask = L->hookmask;
const Proto *p = ci_func(ci)->p;
int counthook;
if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */
ci->u.l.trap = 0; /* don't need to stop again */
return 0; /* turn off 'trap' */
}
pc++; /* reference is always next instruction */
ci->u.l.savedpc = pc; /* save 'pc' */
counthook = (mask & LUA_MASKCOUNT) && (--L->hookcount == 0);
if (counthook)
resethookcount(L); /* reset count */
else if (!(mask & LUA_MASKLINE))
return 1; /* no line hook and count != 0; nothing to be done now */
if (ci->callstatus & CIST_HOOKYIELD) { /* hook yielded last time? */
ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
return 1; /* do not call hook again (VM yielded, so it did not move) */
}
if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */
L->top.p = ci->top.p; /* correct top */
if (counthook)
luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
if (mask & LUA_MASKLINE) {
/* 'L->oldpc' may be invalid; use zero in this case */
int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0;
int npci = pcRel(pc, p);
if (npci <= oldpc || /* call hook when jump back (loop), */
changedline(p, oldpc, npci)) { /* or when enter new line */
int newline = luaG_getfuncline(p, npci);
luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */
}
L->oldpc = npci; /* 'pc' of last call to line hook */
}
if (L->status == LUA_YIELD) { /* did hook yield? */
if (counthook)
L->hookcount = 1; /* undo decrement to zero */
ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */
luaD_throw(L, LUA_YIELD);
}
return 1; /* keep 'trap' on */
}

View File

@ -1,64 +0,0 @@
/*
** $Id: ldebug.h $
** Auxiliary functions from Debug Interface module
** See Copyright Notice in lua.h
*/
#ifndef ldebug_h
#define ldebug_h
#include "lstate.h"
#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1)
/* Active Lua function (given call info) */
#define ci_func(ci) (clLvalue(s2v((ci)->func.p)))
#define resethookcount(L) (L->hookcount = L->basehookcount)
/*
** mark for entries in 'lineinfo' array that has absolute information in
** 'abslineinfo' array
*/
#define ABSLINEINFO (-0x80)
/*
** MAXimum number of successive Instructions WiTHout ABSolute line
** information. (A power of two allows fast divisions.)
*/
#if !defined(MAXIWTHABS)
#define MAXIWTHABS 128
#endif
LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc);
LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n,
StkId *pos);
LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o,
const char *opname);
LUAI_FUNC l_noret luaG_callerror (lua_State *L, const TValue *o);
LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o,
const char *what);
LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1,
const TValue *p2);
LUAI_FUNC l_noret luaG_opinterror (lua_State *L, const TValue *p1,
const TValue *p2,
const char *msg);
LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1,
const TValue *p2);
LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1,
const TValue *p2);
LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...);
LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg,
TString *src, int line);
LUAI_FUNC l_noret luaG_errormsg (lua_State *L);
LUAI_FUNC int luaG_traceexec (lua_State *L, const Instruction *pc);
LUAI_FUNC int luaG_tracecall (lua_State *L);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,87 +0,0 @@
/*
** $Id: ldo.h $
** Stack and Call structure of Lua
** See Copyright Notice in lua.h
*/
#ifndef ldo_h
#define ldo_h
#include "llimits.h"
#include "lobject.h"
#include "lstate.h"
#include "lzio.h"
/*
** Macro to check stack size and grow stack if needed. Parameters
** 'pre'/'pos' allow the macro to preserve a pointer into the
** stack across reallocations, doing the work only when needed.
** It also allows the running of one GC step when the stack is
** reallocated.
** 'condmovestack' is used in heavy tests to force a stack reallocation
** at every check.
*/
#define luaD_checkstackaux(L,n,pre,pos) \
if (l_unlikely(L->stack_last.p - L->top.p <= (n))) \
{ pre; luaD_growstack(L, n, 1); pos; } \
else { condmovestack(L,pre,pos); }
/* In general, 'pre'/'pos' are empty (nothing to save) */
#define luaD_checkstack(L,n) luaD_checkstackaux(L,n,(void)0,(void)0)
#define savestack(L,pt) (cast_charp(pt) - cast_charp(L->stack.p))
#define restorestack(L,n) cast(StkId, cast_charp(L->stack.p) + (n))
/* macro to check stack size, preserving 'p' */
#define checkstackp(L,n,p) \
luaD_checkstackaux(L, n, \
ptrdiff_t t__ = savestack(L, p), /* save 'p' */ \
p = restorestack(L, t__)) /* 'pos' part: restore 'p' */
/* macro to check stack size and GC, preserving 'p' */
#define checkstackGCp(L,n,p) \
luaD_checkstackaux(L, n, \
ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \
luaC_checkGC(L), /* stack grow uses memory */ \
p = restorestack(L, t__)) /* 'pos' part: restore 'p' */
/* macro to check stack size and GC */
#define checkstackGC(L,fsize) \
luaD_checkstackaux(L, (fsize), luaC_checkGC(L), (void)0)
/* type of protected functions, to be ran by 'runprotected' */
typedef void (*Pfunc) (lua_State *L, void *ud);
LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop);
LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
const char *mode);
LUAI_FUNC void luaD_hook (lua_State *L, int event, int line,
int fTransfer, int nTransfer);
LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci);
LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
int narg1, int delta);
LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
LUAI_FUNC int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status);
LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
ptrdiff_t oldtop, ptrdiff_t ef);
LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres);
LUAI_FUNC int luaD_reallocstack (lua_State *L, int newsize, int raiseerror);
LUAI_FUNC int luaD_growstack (lua_State *L, int n, int raiseerror);
LUAI_FUNC void luaD_shrinkstack (lua_State *L);
LUAI_FUNC void luaD_inctop (lua_State *L);
LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode);
LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);
#endif

View File

@ -1,230 +0,0 @@
/*
** $Id: ldump.c $
** save precompiled Lua chunks
** See Copyright Notice in lua.h
*/
#define ldump_c
#define LUA_CORE
#include "lprefix.h"
#include <limits.h>
#include <stddef.h>
#include "lua.h"
#include "lobject.h"
#include "lstate.h"
#include "lundump.h"
typedef struct {
lua_State *L;
lua_Writer writer;
void *data;
int strip;
int status;
} DumpState;
/*
** All high-level dumps go through dumpVector; you can change it to
** change the endianness of the result
*/
#define dumpVector(D,v,n) dumpBlock(D,v,(n)*sizeof((v)[0]))
#define dumpLiteral(D, s) dumpBlock(D,s,sizeof(s) - sizeof(char))
static void dumpBlock (DumpState *D, const void *b, size_t size) {
if (D->status == 0 && size > 0) {
lua_unlock(D->L);
D->status = (*D->writer)(D->L, b, size, D->data);
lua_lock(D->L);
}
}
#define dumpVar(D,x) dumpVector(D,&x,1)
static void dumpByte (DumpState *D, int y) {
lu_byte x = (lu_byte)y;
dumpVar(D, x);
}
/*
** 'dumpSize' buffer size: each byte can store up to 7 bits. (The "+6"
** rounds up the division.)
*/
#define DIBS ((sizeof(size_t) * CHAR_BIT + 6) / 7)
static void dumpSize (DumpState *D, size_t x) {
lu_byte buff[DIBS];
int n = 0;
do {
buff[DIBS - (++n)] = x & 0x7f; /* fill buffer in reverse order */
x >>= 7;
} while (x != 0);
buff[DIBS - 1] |= 0x80; /* mark last byte */
dumpVector(D, buff + DIBS - n, n);
}
static void dumpInt (DumpState *D, int x) {
dumpSize(D, x);
}
static void dumpNumber (DumpState *D, lua_Number x) {
dumpVar(D, x);
}
static void dumpInteger (DumpState *D, lua_Integer x) {
dumpVar(D, x);
}
static void dumpString (DumpState *D, const TString *s) {
if (s == NULL)
dumpSize(D, 0);
else {
size_t size = tsslen(s);
const char *str = getstr(s);
dumpSize(D, size + 1);
dumpVector(D, str, size);
}
}
static void dumpCode (DumpState *D, const Proto *f) {
dumpInt(D, f->sizecode);
dumpVector(D, f->code, f->sizecode);
}
static void dumpFunction(DumpState *D, const Proto *f, TString *psource);
static void dumpConstants (DumpState *D, const Proto *f) {
int i;
int n = f->sizek;
dumpInt(D, n);
for (i = 0; i < n; i++) {
const TValue *o = &f->k[i];
int tt = ttypetag(o);
dumpByte(D, tt);
switch (tt) {
case LUA_VNUMFLT:
dumpNumber(D, fltvalue(o));
break;
case LUA_VNUMINT:
dumpInteger(D, ivalue(o));
break;
case LUA_VSHRSTR:
case LUA_VLNGSTR:
dumpString(D, tsvalue(o));
break;
default:
lua_assert(tt == LUA_VNIL || tt == LUA_VFALSE || tt == LUA_VTRUE);
}
}
}
static void dumpProtos (DumpState *D, const Proto *f) {
int i;
int n = f->sizep;
dumpInt(D, n);
for (i = 0; i < n; i++)
dumpFunction(D, f->p[i], f->source);
}
static void dumpUpvalues (DumpState *D, const Proto *f) {
int i, n = f->sizeupvalues;
dumpInt(D, n);
for (i = 0; i < n; i++) {
dumpByte(D, f->upvalues[i].instack);
dumpByte(D, f->upvalues[i].idx);
dumpByte(D, f->upvalues[i].kind);
}
}
static void dumpDebug (DumpState *D, const Proto *f) {
int i, n;
n = (D->strip) ? 0 : f->sizelineinfo;
dumpInt(D, n);
dumpVector(D, f->lineinfo, n);
n = (D->strip) ? 0 : f->sizeabslineinfo;
dumpInt(D, n);
for (i = 0; i < n; i++) {
dumpInt(D, f->abslineinfo[i].pc);
dumpInt(D, f->abslineinfo[i].line);
}
n = (D->strip) ? 0 : f->sizelocvars;
dumpInt(D, n);
for (i = 0; i < n; i++) {
dumpString(D, f->locvars[i].varname);
dumpInt(D, f->locvars[i].startpc);
dumpInt(D, f->locvars[i].endpc);
}
n = (D->strip) ? 0 : f->sizeupvalues;
dumpInt(D, n);
for (i = 0; i < n; i++)
dumpString(D, f->upvalues[i].name);
}
static void dumpFunction (DumpState *D, const Proto *f, TString *psource) {
if (D->strip || f->source == psource)
dumpString(D, NULL); /* no debug info or same source as its parent */
else
dumpString(D, f->source);
dumpInt(D, f->linedefined);
dumpInt(D, f->lastlinedefined);
dumpByte(D, f->numparams);
dumpByte(D, f->is_vararg);
dumpByte(D, f->maxstacksize);
dumpCode(D, f);
dumpConstants(D, f);
dumpUpvalues(D, f);
dumpProtos(D, f);
dumpDebug(D, f);
}
static void dumpHeader (DumpState *D) {
dumpLiteral(D, LUA_SIGNATURE);
dumpByte(D, LUAC_VERSION);
dumpByte(D, LUAC_FORMAT);
dumpLiteral(D, LUAC_DATA);
dumpByte(D, sizeof(Instruction));
dumpByte(D, sizeof(lua_Integer));
dumpByte(D, sizeof(lua_Number));
dumpInteger(D, LUAC_INT);
dumpNumber(D, LUAC_NUM);
}
/*
** dump Lua function as precompiled chunk
*/
int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data,
int strip) {
DumpState D;
D.L = L;
D.writer = w;
D.data = data;
D.strip = strip;
D.status = 0;
dumpHeader(&D);
dumpByte(&D, f->sizeupvalues);
dumpFunction(&D, f, NULL);
return D.status;
}

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