Domanda

Sono lo sviluppatore principale di Bitfighter e stiamo usando Lua come linguaggio di scripting per consentire ai giocatori di programmare il proprio possedere navi robot personalizzate.

In Lua, non è necessario dichiarare le variabili e tutte le variabili sono predefinite nell'ambito globale, se non diversamente indicato. Questo porta ad alcuni problemi. Prendi il seguente frammento, ad esempio:

loc = bot:getLoc()
items = bot:findItems(ShipType)     -- Find a Ship

minDist = 999999
found = false

for indx, item in ipairs(items) do           
   local d = loc:distSquared(item:getLoc())  

   if(d < minDist) then
      closestItem = item
      minDist = d
   end
end

if(closestItem != nil) then 
   firingAngle = getFiringSolution(closestItem) 
end

In questo frammento, se findItems () non restituisce candidati, allora latestItem si riferirà comunque a qualsiasi nave trovata l'ultima volta e, nel frattempo, quella nave avrebbe potuto essere uccisa. Se la nave viene uccisa, non esiste più e getFiringSolution () fallirà.

Hai riscontrato il problema? Bene, nemmeno i miei utenti. È sottile, ma con effetti drammatici.

Una soluzione sarebbe quella di richiedere che tutte le variabili vengano dichiarate e che tutte le variabili siano predefinite nell'ambito locale. Sebbene tale modifica non renderebbe impossibile per i programmatori fare riferimento a oggetti che non esistono più, renderebbe più difficile farlo inavvertitamente.

Esiste un modo per dire a Lua di impostare tutti i vars sull'ambito locale e / o richiedere che vengano dichiarati? So che alcune altre lingue (ad esempio Perl) hanno questa opzione disponibile.

Grazie!


Molte buone risposte qui, grazie!

Ho deciso di scegliere una versione leggermente modificata del modulo "rigoroso" di Lua. Questo sembra portarmi dove voglio andare e lo hackererò un po 'per migliorare i messaggi e renderli più appropriati per il mio contesto particolare.

È stato utile?

Soluzione

Non esiste alcuna opzione per impostare questo comportamento, ma esiste un modulo 'rigoroso' fornito con l'installazione standard, che fa esattamente questo (modificando le meta-tabelle). Uso: richiede "rigoroso"

Per informazioni più approfondite e altre soluzioni: http://lua-users.org/wiki / DetectingUndefinedVariables , ma raccomando "rigoroso".

Altri suggerimenti

Sorta.

In Lua, i globi vivono nozionalmente nella tabella dei globi _G (la realtà è un po 'più complessa, ma dal lato Lua non c'è modo di dirlo ad AFAIK). Come per tutte le altre tabelle Lua, è possibile associare un __newindex misurabile a _G che controlla come le variabili vengono aggiunte ad esso. Lascia che questo gestore __newindex faccia qualunque cosa tu voglia fare quando qualcuno crea un globale: lancia un errore, consentilo ma stampa un avviso, ecc.

Per immischiarsi con _G , è più semplice e pulito usare setfenv . Consulta la documentazione .

" Local per impostazione predefinita è errato " ;. Si prega di consultare

http://lua-users.org/wiki/LocalByDefault

http://lua-users.org/wiki/LuaScopingDiscussion

Devi usare un qualche tipo di protezione globale dell'ambiente. Ci sono alcuni strumenti statici per farlo (non troppo maturo), ma la soluzione più comune è usare la protezione runtime, basata su __index e __newindex in _G 's metatable.

Plug Shameles: questa pagina può anche essere utile:

http://code.google.com/p/lua-alchemy / wiki / LuaGlobalEnvironmentProtection

Si noti che mentre si discute di Lua incorporato in swf, la tecnica descritta (vedere fonti ) funzionano per Lua generico. Usiamo qualcosa del genere nel nostro codice di produzione al lavoro.

In realtà, la variabile globale extra con il riferimento stantio alla nave sarà sufficiente per impedire al GC di scartare l'oggetto. Quindi potrebbe essere rilevato in fase di esecuzione notando che la nave è ora "morta". e rifiutando di farci qualcosa. Non è ancora la nave giusta, ma almeno non ti schianterai.

Una cosa che puoi fare è conservare gli script degli utenti in un sandbox , probabilmente un sandbox per script. Con la corretta manipolazione della tabella di ambiente del sandbox o del suo metatable, puoi organizzare di scartare tutte o la maggior parte delle variabili globali dal sandbox prima (o subito dopo) di chiamare il codice dell'utente.

Pulire la sandbox dopo le chiamate avrebbe il vantaggio di scartare riferimenti extra a cose che non dovrebbero rimanere in giro. Questo potrebbe essere fatto mantenendo una whitelist di campi che possono rimanere nell'ambiente ed eliminando tutto il resto.

Ad esempio, quanto segue implementa una chiamata in modalità sandbox a una funzione fornita dall'utente con un ambiente contenente solo nomi in white list dietro una nuova tabella scratch fornita per ogni chiamata.

-- table of globals that will available to user scripts
local user_G = {
        print=_G.print,
        math=_G.math,
        -- ...
    }
-- metatable for user sandbox
local env_mt = { __index=user_G }


-- call the function in a sandbox with an environment in which new global 
-- variables can be created and modified but they will be discarded when the 
-- user code completes.
function doUserCode(user_code, ...)
    local env = setmetatable({}, env_mt) -- create a fresh user environment with RO globals
    setfenv(user_code, env)        -- hang it on the user code
    local results = {pcall(user_code, ...)}
    setfenv(user_code,{})
    return unpack(results)
end

Questo potrebbe essere esteso per rendere la tabella globale di sola lettura spingendola indietro dietro un altro accesso misurabile se lo si desidera.

Nota che una soluzione sandbox completa considererebbe anche cosa fare del codice utente che esegue accidentalmente (o maliziosamente) un ciclo infinito (o semplicemente molto lungo) o altre operazioni. Soluzioni generali per questo sono un argomento di discussione occasionale sulla Elenco Lua , ma buone soluzioni sono difficili.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top