声明变量和Lua作用域问题
我是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()没有返回任何候选项,closestItem仍将引用上次发现的任何飞船,而在此期间,该飞船可能已被击败。如果船被摧毁,它将不再存在,getFiringSolution()将失败。
你发现了问题吗?那么我的用户也不会注意到。这是微妙的,但却有巨大的影响。
一个解决办法是要求所有变量都声明,并且所有变量默认为局部作用域。虽然这种变化不会使编程人员无法引用不再存在的对象,但会使无意间这样做变得更加困难。
有没有办法告诉Lua将所有变量默认为局部作用域,并/或要求声明它们?我知道一些其他语言(如Perl)可以使用此选项。
谢谢你!
这里有很多好的答案,谢谢!
我决定采用稍微修改过的Lua 'strict'模块。这似乎可以让我达到想要的效果,并且我将稍加修改以改进消息并使其更适合我的特定上下文。
原文链接 https://stackoverflow.com/questions/1014757
没有选项可以设置这种行为,但是标准安装提供了一个名为“strict”的模块,可以通过修改元表来实现此功能。
使用方法:require 'strict'
有关更详细的信息和其他解决方案,请参阅http://lua-users.org/wiki/DetectingUndefinedVariables,但我推荐使用“strict”。
实际上,具有过时引用到飞船的额外全局变量足以防止 GC 丢弃对象。因此,可以通过注意到该飞船现在已经“死亡”并拒绝对其执行任何操作来在运行时检测到它。它仍然不是正确的飞船,但至少不会崩溃。
你可以做的一件事是保持用户脚本处于 沙盒 中,可能是每个脚本一个沙盒。通过正确处理沙盒的环境表或元表,您可以在调用用户代码之前或之后排列在沙盒中丢弃所有或大多数全局变量。
在调用后清理沙盒的好处是丢弃不应该挂起的额外引用。这可以通过保留白名单中允许在环境中保留的字段并删除所有其他字段来完成。
例如,以下实现了使用仅包含白名单名称的环境与为每个调用提供的新刮擦表的用户提供的函数的沙盒调用。
-- 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
如果您希望,可以通过将全局表推回到一个元表访问之后再将其设为只读来扩展此功能。
请注意,完整的沙盒解决方案还将考虑对无意或有意执行无限(或仅仅很长)循环或其他操作的用户代码的处理方法。这方面的一般解决方案是 Lua 列表 上的讨论主题,但好的解决方案比较难。
默认情况下局部变量是错误的。请参见:
http://lua-users.org/wiki/LocalByDefault
http://lua-users.org/wiki/LuaScopingDiscussion
你需要使用某种全局环境保护。有一些静态工具可以做到这一点(不是很成熟),但最常见的解决方案是使用基于 _G
的元表中的 __index
和 __newindex
的运行时保护。
不要忘了这个页面可能也很有用:
http://code.google.com/p/lua-alchemy/wiki/LuaGlobalEnvironmentProtection
请注意,虽然它讨论的是嵌入到swf中的Lua,但所描述的技术(参见源代码)也适用于通用的Lua。我们在工作中的生产代码中采用了类似的技术。
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
- 如何编写 Lua 模式将字符串(嵌套数组)转换为真正的数组?
在 Lua 中,全局变量名义上存在于全局表
_G
中(实际上更为复杂,但从 Lua 方面来说,我认为没有办法区分)。和其他 Lua 表一样,可以将__newindex
元表附加到_G
上,以控制变量被添加到其中的方式。让这个__newindex
处理程序在创建全局变量时执行您想要执行的任何操作:抛出错误,允许但打印警告等等。为了干涉
_G
,最简单、最清晰的方法是使用setfenv
。请参阅文档。