diff --git a/polymer/eduke32/source/lunatic/control.lua b/polymer/eduke32/source/lunatic/control.lua index a77f2b150..1d3fb2585 100644 --- a/polymer/eduke32/source/lunatic/control.lua +++ b/polymer/eduke32/source/lunatic/control.lua @@ -995,6 +995,9 @@ end function _sound(aci, sndidx) check_sprite_idx(aci) + -- A_PlaySound() returns early if the sound index is oob, but IMO it's good + -- style to throw an error in instead of silently failing. + check_sound_idx(sndidx) ffiC.A_PlaySound(sndidx, aci) end @@ -1010,19 +1013,22 @@ local function S_StopSound(sndidx) ffiC.S_StopEnvSound(sndidx, -1) end -function _stopsound(aci, sndidx) +function _soundplaying(aci, sndidx) check_sprite_idx(aci) check_sound_idx(sndidx) + return (ffiC.S_CheckSoundPlaying(aci, sndidx) ~= 0) +end + +function _stopsound(aci, sndidx) -- XXX: This is weird: the checking is done wrt a sprite, but the sound not. -- NOTE: S_StopSound() stops sound that started playing most recently. - if (ffiC.S_CheckSoundPlaying(aci, sndidx) ~= 0) then + if (_soundplaying(aci, sndidx)) then S_StopSound(sndidx) end end function _soundonce(aci, sndidx) - check_sound_idx(sndidx) - if (ffiC.S_CheckSoundPlaying(aci, sndidx) == 0) then + if (not _soundplaying(aci, sndidx)) then _sound(aci, sndidx) end end diff --git a/polymer/eduke32/source/lunatic/defs.ilua b/polymer/eduke32/source/lunatic/defs.ilua index 297596e96..558f223e8 100644 --- a/polymer/eduke32/source/lunatic/defs.ilua +++ b/polymer/eduke32/source/lunatic/defs.ilua @@ -23,15 +23,12 @@ local tonumber = tonumber local tostring = tostring local type = type - -- The "gv" global will provide access to C global *scalars* and safe functions. --- XXX: still exposes C library functions etc. contained in ffi.C, problem? -local gv_ = { - -- All non-scalars need to be explicitly listed here and access to them is - -- redirected to the dummy empty table... -} - -local dummy_empty_table = {} +-- NOTE: This exposes C library functions from e.g. the global C namespace, but +-- without their declarations, they will be sitting there like a stone. +local gv_ = {} +-- [key]=true forbids, [key]= overrides +local gv_access = {} -- This is for declarations of arrays or pointers which should not be -- accessible through the "gv" global. The "defs_common" module will @@ -44,8 +41,10 @@ function decl(str) -- NOTE that the regexp also catches non-array/non-function identifiers -- like "user_defs ud;" for varname in string.gmatch(str, "([%a_][%w_]*)[[(;]") do --- print("DUMMY "..varname) - gv_[varname] = dummy_empty_table + if (ffiC._DEBUG_LUNATIC ~= 0) then + print("FORBID "..varname) + end + gv_access[varname] = true end ffi.cdef(str) @@ -464,6 +463,7 @@ const char *g_sizes_of_what[]; int32_t g_sizes_of[]; int32_t g_elCallDepth; actor_t actor[MAXSPRITES]; +camera_t g_camera; user_defs ud; playerdata_t g_player[MAXPLAYERS]; weapondata_t g_playerWeapon[MAXPLAYERS][MAX_WEAPONS]; @@ -513,7 +513,7 @@ int32_t g_scriptModulesNum; const char *G_ConFile(void); void G_DoGameStartup(const int32_t *params); -int32_t C_DefineSound(int32_t sndidx, const char *fn, int32_t args[5]); +int32_t C_DefineSound(int32_t sndidx, const char *fn, int32_t args [5]); void C_DefineMusic(int32_t vol, int32_t lev, const char *fn); void C_DefineQuote(int32_t qnum, const char *qstr); void C_DefineVolumeName(int32_t vol, const char *name); @@ -548,6 +548,7 @@ end -- Add game-side metamethods to "spritetype" and register it with "metatype" defs_c.spritetype_mt.__index.isenemy = function(s) + -- CODEDUP control.lua: isenemytile return (bit.band(ffiC.g_tile[s.picnum].flags, ffiC.SFLAG_BADGUY)~=0) end ffi.metatype("spritetype", defs_c.spritetype_mt) @@ -772,6 +773,7 @@ local function get_weapondata_accessor(pli, weap) __newindex = function(_, member, val) if (string.match(member, "sound")) then + -- TODO: set to 0 if oob? (e.g. CrackDown) check_sound_idx(val) elseif (member=="workslike") then check_weapon_idx(val) @@ -871,15 +873,24 @@ local player_mt = { pals.f = f pals.r, pals.g, pals.b = r, g, b end, - - -- XXX: is this so useful without z offset? - _cansee = function(p, otherpos, othersect) - return cansee(p.pos, sprite[p.i].sectnum, otherpos, othersect) - end, }, } ffi.metatype("DukePlayer_t", player_mt) +local camera_mt = { + -- TODO: "set position" method, which also updates the sectnum + __index = ffiC.g_camera, + __newindex = function(_, key, val) + if (key=="sect") then + defs_c.check_sector_idx(val) + end + ffiC.g_camera[key] = val + end, + __metatable = true +} + +gv_access.cam = setmetatable({}, camera_mt) + -- Declare all con_lang.labels constants in the global FFI namespace. for i=1,#con_lang.labels do local strbuf = {"enum {"} @@ -1253,16 +1264,19 @@ end -- error(..., 2) is to blame the caller and get its line numbers -local tmpmt = { - __index = function() error("dummy variable: read access forbidden", 2) end, - __newindex = function() error("dummy variable: write access forbidden", 2) end, -} -setmtonce(dummy_empty_table, tmpmt) - gv = gv_ local tmpmt = { - __index = ffiC, - __newindex = function() error("cannot create new or write into existing fields of 'gv'", 2) end, + __index = function(gv, key) + if (gv_access[key] == nil) then + return ffiC[key] + end + if (type(gv_access[key])=="table") then + -- overridden... + return gv_access[key] + end + error("access forbidden", 2) + end, + __newindex = function() error("write access forbidden", 2) end, } setmtonce(gv, tmpmt) diff --git a/polymer/eduke32/source/lunatic/defs_common.lua b/polymer/eduke32/source/lunatic/defs_common.lua index f924c7f75..0025babf0 100644 --- a/polymer/eduke32/source/lunatic/defs_common.lua +++ b/polymer/eduke32/source/lunatic/defs_common.lua @@ -7,6 +7,8 @@ local ffi = require("ffi") local ffiC = ffi.C +ffi.cdef "enum { _DEBUG_LUNATIC=1 }" + local bit = require("bit") local string = require("string") @@ -133,7 +135,7 @@ typedef struct { local vec3_ct = ffi.typeof("vec3_t") local hitdata_ct = ffi.typeof("hitdata_t") -ffi.cdef[[const int32_t engine_main_arrays_are_static, engine_v8;]] +decl[[const int32_t engine_main_arrays_are_static, engine_v8;]] --== Engine data and functions ==-- @@ -196,7 +198,7 @@ enum { ffi.cdef[[ const int16_t numsectors, numwalls; -const int32_t numyaxbunches; +const int32_t numyaxbunches; // XXX const int32_t totalclock; const int32_t xdim, ydim; ]] @@ -384,10 +386,18 @@ function creategtab(ctab, maxidx, name) return setmtonce(tab, tmpmt) end +function check_sector_idx(sectnum) + if (sectnum >= ffiC.numsectors+0ULL) then + error("passed out-of-bounds sector number "..sectnum, 3) + end +end + local vars_to_ignore = {} for varname,_ in pairs(getfenv(1)) do --- print("IGNORE "..varname) + if (ffiC._DEBUG_LUNATIC ~= 0) then + print("IGNORE "..varname) + end vars_to_ignore[varname] = true end @@ -406,12 +416,6 @@ nextspritestat = creategtab(ffiC.nextspritestat, ffiC.MAXSPRITES, 'nextspritesta prevspritesect = creategtab(ffiC.prevspritesect, ffiC.MAXSPRITES, 'prevspritesect[]') prevspritestat = creategtab(ffiC.prevspritestat, ffiC.MAXSPRITES, 'prevspritestat[]') -function check_sector_idx(sectnum) - if (sectnum >= ffiC.numsectors+0ULL) then - error("passed out-of-bounds sector number "..sectnum, 3) - end -end - local function iter_wallsofsec(endwall, w) w = w+1 if (w < endwall) then @@ -579,7 +583,9 @@ function create_globals(_G_their) for varname,obj in pairs(_G_our) do if (not vars_to_ignore[varname]) then --- print("EXPORT "..varname) + if (ffiC._DEBUG_LUNATIC ~= 0) then + print("EXPORT "..varname) + end _G_their[varname] = obj end end diff --git a/polymer/eduke32/source/lunatic/dynsymlist b/polymer/eduke32/source/lunatic/dynsymlist index 959b88f49..a70aa3091 100644 --- a/polymer/eduke32/source/lunatic/dynsymlist +++ b/polymer/eduke32/source/lunatic/dynsymlist @@ -76,6 +76,7 @@ C_DefineSkillName; C_DefineLevelName; actor; +g_camera; ud; g_player; g_playerWeapon; diff --git a/polymer/eduke32/source/lunatic/lunacon.lua b/polymer/eduke32/source/lunatic/lunacon.lua index 9d7ddb321..e67dda80a 100644 --- a/polymer/eduke32/source/lunatic/lunacon.lua +++ b/polymer/eduke32/source/lunatic/lunacon.lua @@ -91,7 +91,7 @@ local g_warn = { ["not-redefined"]=true, ["bad-identifier"]=true, ["number-conversion"]=true, } -- Code generation options. -local g_cgopt = { ["no"]=false, } +local g_cgopt = { ["no"]=false, ["_incomplete"]=false, } -- How many 'if' statements are following immediately each other, -- needed to cope with CONs dangling-else resolution @@ -128,7 +128,7 @@ local function new_initial_codetab() return { "local _con, _bit, _math = require'con', require'bit', require'math';", "local sector, sprite, actor, player = sector, sprite, actor, player;", - "local gameactor, gameevent = gameactor, gameevent;" + "local gameactor, gameevent, _gv = gameactor, gameevent, gv;" } end @@ -136,10 +136,41 @@ end -- KEEPINSYNC gamevars.c: Gv_AddSystemVars() local function new_initial_gvartab() local wmembers = conl.wdata_members - local gamevar = {} - local MAX_WEAPONS = ffiC and ffiC.MAX_WEAPONS or 12 + local RW = function(varname) + return { name=varname, flags=GVFLAG.SYSTEM } + end + + local RO = function(varname) + return { name=varname, flags=GVFLAG.SYSTEM+GVFLAG.READONLY } + end + + local gamevar = { + -- XXX: TODO: THISACTOR can mean different things in some contexts, + -- e.g. sector[THISACTOR] is sector[sprite[].sectnum] + THISACTOR = RO "_aci", + + -- TODO + RETURN = RW "_RETURN_", + + xdim = RO "_gv.xdim", + ydim = RO "_gv.ydim", + + numsectors = RO "_gv.numsectors", + NUMSECTORS = RO "_gv.numsectors", + NUMWALLS = RO "_gv.numwalls", + + camerax = RW "_gv.cam.pos.x", + cameray = RW "_gv.cam.pos.y", + cameraz = RW "_gv.cam.pos.z", + cameraang = RW "_gv.cam.ang", + camerahoriz = RW "_gv.cam.horiz", + camerasect = RW "_gv.cam.sect", + cameradist = RW "_gv.cam.dist", + cameraclock = RW "_gv.cam.clock", + } + for w=0,MAX_WEAPONS-1 do for i=1,#wmembers do local member = wmembers[i]:gsub(".* ","") -- strip e.g. "int32_t " @@ -308,8 +339,18 @@ local function reset_labels() -- NO is also a valid `move', `ai' or `action', but they are handled -- separately in lookup_composite(). - g_labeldef = { NO=0 } - g_labeltype = { NO=LABEL.NUMBER } + g_labeldef = { + NO = 0, + -- NOTE: these are read-only gamevars in C-CON + CLIPMASK0 = 65536+1, -- blocking + CLIPMASK1 = (256*65536)+64, -- hittable + } + + g_labeltype = { + NO = LABEL.NUMBER, + CLIPMASK0 = LABEL.NUMBER, + CLIPMASK1 = LABEL.NUMBER, + } -- Initialize default defines. for i=1,#conl.labels do @@ -1208,7 +1249,7 @@ local Cinner = { ezshoot = cmd(R,D), ezshootvar = cmd(R,R), shoot = cmd(D) - / "_con._A_Shoot(_aci, %1)", + / "_con._A_Shoot(_aci,%1)", zshoot = cmd(R,D), zshootvar = cmd(R,R), @@ -1419,31 +1460,47 @@ local Cif = { ifangdiffl = cmd(D) / format("_con._angdiffabs(%s,%s)<=%%1", PLS".ang", SPS".ang"), ifsound = cmd(D) - / "", + / "_con._soundplaying(_aci,%1)", -- vvv TODO: this is not correct for GET_ACCESS or GET_SHIELD. ifpinventory = cmd(D,D) / format("_con._getinventory(%s,%%1,_aci)~=%%2", PLS""), - ifvarl = cmd(R,D), - ifvarg = cmd(R,D), + ifvarl = cmd(R,D) + / "%1<%2", + ifvarg = cmd(R,D) + / "%1>%2", ifvare = cmd(R,D) / "%1==%2", - ifvarn = cmd(R,D), - ifvarand = cmd(R,D), - ifvaror = cmd(R,D), - ifvarxor = cmd(R,D), - ifvareither = cmd(R,D), + ifvarn = cmd(R,D) + / "%1~=%2", + ifvarand = cmd(R,D) + / "_bit.band(%1,%2)~=0", + ifvaror = cmd(R,D) + / "_bit.bor(%1,%2)~=0", + ifvarxor = cmd(R,D) + / "_bit.bxor(%1,%2)~=0", + ifvareither = cmd(R,D) + / "%1~=0 or %2~=0", - ifvarvarg = cmd(R,R), - ifvarvarl = cmd(R,R), - ifvarvare = cmd(R,R), - ifvarvarn = cmd(R,R), - ifvarvarand = cmd(R,R), - ifvarvaror = cmd(R,R), - ifvarvarxor = cmd(R,R), - ifvarvareither = cmd(R,R), + ifvarvarl = cmd(R,R) + / "%1<%2", + ifvarvarg = cmd(R,R) + / "%1>%2", + ifvarvare = cmd(R,R) + / "%1==%2", + ifvarvarn = cmd(R,R) + / "%1~=%2", + ifvarvarand = cmd(R,R) + / "_bit.band(%1,%2)~=0", + ifvarvaror = cmd(R,R) + / "_bit.bor(%1,%2)~=0", + ifvarvarxor = cmd(R,R) + / "_bit.bxor(%1,%2)~=0", + ifvarvareither = cmd(R,R) + / "%1~=0 or %2~=0", - ifactorsound = cmd(R,R), + ifactorsound = cmd(R,R) + / "_con._soundplaying(%1,%2)", ifp = (sp1 * tok.define)^1 / function (...) return format("_con._ifp(%d,_pli,_aci)", bit.bor(...)) end, @@ -1609,15 +1666,15 @@ local function after_if_cmd_Cmt(subj, pos, ...) if (g_numerrors == inf) then return nil end ----[[ - if (capts[1] ~= nil) then + + if (not g_cgopt["_incomplete"] and capts[1] ~= nil) then assert(#capts <= 3) for i=1,#capts do assert(type(capts[i]=="string")) end return true, unpack(capts, 1, #capts) end ---]] + return true end @@ -2017,6 +2074,11 @@ local function handle_cmdline_arg(str) elseif (str:sub(2)=="fno") then -- Disable printing code. g_cgopt["no"] = true + ok = true + elseif (str:sub(2)=="f_incomplete") then + -- TEMP + g_cgopt["_incomplete"] = true + ok = true end if (not ok) then diff --git a/polymer/eduke32/source/lunatic/test.elua b/polymer/eduke32/source/lunatic/test.elua index 66892946d..843db986a 100644 --- a/polymer/eduke32/source/lunatic/test.elua +++ b/polymer/eduke32/source/lunatic/test.elua @@ -77,12 +77,9 @@ if (vol==1 and lev==1) then -- E1L1 spriteext[i].roll = 256; for spr in spritesofsect(307) do -- some fence sprites in E1L1 - print('spr', spr) + printf('spr %d', spr) sprite[spr].pal = 6 end - for spr in spritesofsect(236) do - print('#spr', spr) - end --this is a problem --actor = {} @@ -110,30 +107,31 @@ local unsafe = pcall(function() string.UNSAFE=true; end) --DBG_.printkv('_G in test.elua', _G) -- direct gv array access forbidden -checkfail('gv.sprite[0].yrepeat = 100', "dummy variable: read access forbidden") +checkfail('gv.sprite[0].yrepeat = 100', "access forbidden") -- indexing struct array with non-numeric type checkfail('local i = sprite["qwe"]') -- this will produce an error inside the sprite metatable checkfail('print(sprite[100000].ceilingpal)', "out-of-bounds sprite[] struct read access") --- NOTE: gv.sprite doesn't fail, but we can't use it -checkfail('print(gv.sprite[0])', "dummy variable: read access forbidden") +checkfail('print(gv.sprite[0])', "access forbidden") -- set metatable forbidden checkfail('setmetatable(sprite, {})', "attempt to read undeclared variable 'setmetatable'") --- oob write access -checkfail('sector[-1].ceilingpal = 4', "out-of-bounds sector[] read access") -- note the strange err msg +-- OOB write access. +-- Note that indexing ("reading") sector fails, even if the user wants to +-- assign to a sector member. Potentially confusing error message. +checkfail('sector[-1].ceilingpal = 4', "out-of-bounds sector[] read access") -- wallnum member is read-only checkfail('sector[0].wallnum = 0', "attempt to write to constant location") -- this comes from LJ/FFI -- gv.numsectors is read-only -checkfail('gv.numsectors = 4', "cannot create new or write into existing fields of 'gv'") +checkfail('gv.numsectors = 4', "write access forbidden") -- cannot create new fields in 'gv' -checkfail('gv.QWE = 4', "cannot create new or write into existing fields of 'gv'") +checkfail('gv.QWE = 4', "write access forbidden") -- direct sector write access forbidden checkfail('sector[4] = sector[6]', "cannot write directly to sector[] struct") @@ -154,7 +152,7 @@ checkfail('wall[4].QWE = 123', "has no member named 'QWE'") checkfail("new_global = 345", "attempt to write to undeclared variable 'new_global'") -- can't redefine constants in 'gv' -checkfail('gv.CEILING = 3', "cannot create new or write into existing fields of 'gv'") +checkfail('gv.CEILING = 3', "write access forbidden") -- string.dump is unavailable checkfail('local s=require[[string]]; local tmp=s.dump(gameevent)', @@ -174,8 +172,8 @@ print('') checkfail('gv.luaJIT_setmode(nil, 0, 0)', "missing declaration for symbol 'luaJIT_setmode'") checkfail('gv.luaJIT_BC_con_lang', "attempt to call a nil value") -checkfail('gv.yax_getbunch(0,0)', "attempt to call field 'yax_getbunch' (a table value)") -checkfail('gv.gethitickms = nil', "cannot create new or write into existing fields of 'gv'") +checkfail('gv.yax_getbunch(0,0)', "access forbidden") +checkfail('gv.gethitickms = nil', "write access forbidden") -- we don't have arrays in Lua-accessible structs now checkfail('local i = actor[0].t_data[15]', "has no member named 't_data'") @@ -188,8 +186,10 @@ checkfail('gameactor(1680, 0)', "invalid last argument to gameactor: must be a f checkfail("do local bt=require'bittest'; bt.QWE=1; end", "modifying module table forbidden") -- the cdata returned by player[] can't be made into a pointer! checkfail("do local pl=player[0]; i=pl[1]; end") -checkfail("do local ud=gv.ud.camera; end", "dummy variable: read access forbidden") -- test for proper decl() +checkfail("do local ud=gv.ud.camerasprite; end", "access forbidden") -- test for proper decl() checkfail("sprite[0]:set_picnum(-10)", "attempt to set invalid picnum") +checkfail("gv.g_sizes_of=nil; print(gv.g_sizes_of[0])", "write access forbidden") +checkfail("gv.cam.sect=-1", "passed out-of-bounds sector number") printf('ceilingbunch of sector 0: %d', getbunch(0, gv.CEILING)) @@ -298,6 +298,13 @@ gameactor(1680, TROOPSTRENGTH, "TROOPSTAND", -- LIZTROOP end ) +gameevent("DISPLAYROOMS", + function() + local cam = gv.cam + cam.pos.z = cam.pos.z + 64*16*math.sin(gv.totalclock/30) + end + ) + printf("EVENT_INIT = %d", gv.EVENT_INIT) -- tests default defines local bittest = require "bittest" @@ -308,9 +315,11 @@ require("test/test_rotspr") print('---=== END TEST SCRIPT ===---') +--[[ function check_sector_idx() error("bla") end +--]] spritesofsect(0) --DBG_.oom()