我是Bitfighter的首席开发者,我们使用Lua作为脚本语言,允许玩家编写自己的定制机器人船。
在Lua中,除非另有声明,否则不需要声明变量,并且所有变量都默认为全局范围。这导致一些问题。请使用以下代码段:
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
在这个片段中,如果findItems()没有返回候选者,那么nearestItem仍将引用它最后一次发现的任何船只,并且在此期间,该船可能已被杀死。如果船被杀死,它将不再存在,并且getFiringSolution()将失败。
你发现了这个问题吗?好吧,我的用户也不会。它很微妙,但具有戏剧性的效果。
一种解决方案是要求声明所有变量,并且所有变量都要默认为本地范围。虽然这种改变不会使程序员无法引用不再存在的对象,但这会使得无意中更难以这样做。
有没有办法告诉Lua默认所有变量到本地范围,和/或要求它们被声明?我知道其他一些语言(例如Perl)可以使用此选项。
谢谢!
这里有很多好的答案,谢谢!
我决定使用Lua'tright'模块的略微修改版本。这似乎让我想到了我想去的地方,我会稍微改进一下这些消息并使它们更适合我的特定背景。
答案 0 :(得分:3)
没有选项来设置此行为,但标准安装提供了一个模块'strict',它正是这样做的(通过修改元表)。 用法: 要求'严格'
有关更深入的信息和其他解决方案:http://lua-users.org/wiki/DetectingUndefinedVariables,但我建议'严格'。
答案 1 :(得分:2)
八九不离十。
在Lua中,全局变量存在于全局表_G
中(现实情况稍微复杂一些,但从Lua方面来说,没有办法告诉AFAIK)。与所有其他Lua表一样,可以将__newindex
metatable附加到_G
,以控制如何向其添加变量。当有人创建全局时,让这个__newindex
处理程序做你想做的事情:抛出错误,允许它但是打印警告等等。
要干涉_G
,使用setfenv
最简单,最干净。请参阅documentation。
答案 2 :(得分:2)
“默认为”本地错误“。请参阅
http://lua-users.org/wiki/LocalByDefault
http://lua-users.org/wiki/LuaScopingDiscussion
您需要使用某种全球环境保护。有一些静态工具可以做到这一点(不太成熟),但最常见的解决方案是使用运行时保护,基于__index
和__newindex
的{{1}}的metatable。
Shameles插件:此页面也可能有用:
http://code.google.com/p/lua-alchemy/wiki/LuaGlobalEnvironmentProtection
请注意,虽然它讨论了嵌入到swf中的Lua,但所描述的技术(请参阅sources)确实适用于通用Lua。我们在工作中的生产代码中使用了这些内容。
答案 3 :(得分:0)
实际上,具有过时参考船舶的额外全局变量将足以防止GC丢弃该对象。所以它可以在运行时通过注意到船现在“死”并且拒绝对它做任何事情来检测。它仍然不是正确的船,但至少你不会崩溃。
您可以做的一件事是将用户脚本保存在sandbox中,可能是每个脚本的沙箱。通过正确操作沙箱的环境表或其元表,您可以安排在调用用户代码之前(或之后)从沙箱中丢弃所有或大多数全局变量。
调用后清理沙箱的好处是可以放弃对不应该挂起的东西的额外引用。这可以通过保留允许保留在环境中的字段的白名单来完成,并删除所有其余字段。
例如,以下内容实现了对用户提供的函数的沙盒调用,其中的环境仅包含为每个调用提供的全新临时表后面的白名单。
-- 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
如果你愿意的话,可以通过将全局表推回到一个更多的metatable访问后来扩展它以使其成为只读。
请注意,完整的沙盒解决方案还会考虑如何处理意外(或恶意)执行无限(或仅非常长)循环或其他操作的用户代码。针对此问题的一般解决方案偶尔会讨论Lua list,但很难找到好的解决方案。