战魂小筑

讨论群:309800774 知乎关注:http://zhihu.com/people/sunicdavy 开源项目:https://github.com/davyxu

   :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  257 随笔 :: 0 文章 :: 506 评论 :: 0 Trackbacks

魔兽的UI插件结构

1.使用lua+XML作为配置

    分析:XML虽然人机交互很好,但其实没有几个UI是真正用纯XML写的,大多还是用编辑器比较方便。速度很慢,但尚不清楚魔兽代码里是否进行优化

2. Interface\Addons为插件目录,文件夹可以堆叠

3. 每个插件组,需要一个toc文件来做文件读取列表描述,类似于:

# Libraries
embeds.xml

AceGUIWidget-DragLink.lua
Core.lua

# Localization
Locale-enUS.lua
Locale-zhCN.lua
Locale-zhTW.lua

AutoBarDB.lua
AutoBarOptions.lua
AutoBarSearch.lua

4.一个插件组里可以拥有多个lua文件,都共享一个独立的全局空间

5.WTF\Account\账号名\服务器名\角色名\AddOns.txt文件描述哪些插件需要读取

根据分析:每次魔兽启动时,都会扫描一次插件目录,并更新这个列表,但是原有的插件读取状态仍然保留,类似于:

Combuctor: enabled
Combuctor_Config: enabled
Parrot: disabled
BattleInfo: disabled
BigWigs: disabled
BigWigs_Extras: disabled
BigWigs_BlackTemple: disabled
leafZone: enabled
InFlight: disabled

6. WTF\下的很多SavedVariables目录都是用于保存插件状态的,没有对lua的扩展库进行研究(ACE2/3等等),但是这是一种很好的保存插件数据的方法

OZ_Config = {
    {
        ["bottomCol"] = {
            ["a"] = 1,
            ["r"] = 0,
            ["g"] = 0,
            ["b"] = 0.6,
        },
        ["maxBars"] = 40,
        ["barHeight"] = 16,
        ["titleHeight"] = 20,
        ["sort2"] = 0,
        ["fadeAlpha"] = 0.3999999761581421,
        ["textSize"] = 10,
        ["colour"] = 2,
        ["minBars"] = 1,
        ["heading"] = {
            3, -- [1]
            0, -- [2]
            0, -- [3]

7.暂时没有找到魔兽UI的核心API是否用纯脚本提供的证据,但是可以推断,按照暴雪的实力,应该是全lua api写成。

 

 

构建安全的lua沙箱

所谓沙箱,就是每个插件拥有独立的_G全局环境,即便用户误将print修改,其他的插件也不会受到影响. 同时,考虑到沙箱的安全性和权限,需要对沙箱函数访问进行订制.以下是本人摸索出的一种方案:

先看下我的UI环境及lua嵌入架构:

1. C++层将必要的API注册到lua层.但都是基于id的全局函数(考虑到效率及便捷),但是实际使用时再在lua层进行OO封装,这和WINDOWS API及MFC的原理类似

2. C++层只提供4种原生控件: Button,Label, EditBox,MultiLineEditBox。其他的控件都是由这些组成。

3. 可以将整个系统分为内核模式和用户模式。

    内核模式:可以使用完整的API访问及权限。

    用户模式:被沙箱保护,无法访问一些危险的API,例如io

 

这里,我们使用lua_setfenv进行沙箱构建,首先我们必须将创建每个沙箱对应的table

// 放入沙箱名称
lua_pushstring( mLua, "mysandbox" );
// 将一个table压入栈
dotlua::table ts( mLua , false );
// 调用之前载入好的一个订制沙箱环境的函数
gt.call<void>("SetupSandBox", ts ); 
// 将沙箱以mysandbox的key保存在注册表中
lua_settable( mLua, LUA_REGISTRYINDEX );

之后使用lua_loadfile载入需要放进沙箱的代码

 

// 这里将本沙箱对应的环境取出来
lua_pushstring( mLua, "mysandbox" );
lua_gettable( mLua, LUA_REGISTRYINDEX );
// 栈内的情形为
// -1  沙箱table
// -2 chunk function
// 这里必须调用chunk函数
lua_setfenv( mLua, -2 );
调用pcall执行代码
这里的chunk函数,来源于lua_loadfile或者lua_loadbuffer,这2个函数将代码读入,但并不会执行,包括定义全局函数之类的操作。
沙箱订制函数必须提前载入,这里发一个做参考

function SetupSandBox( e )
    e._G = e
    -- system lib
    e.print         = print
    e.printf         = printf
    e.table         = table
    e.string         = string
    e.debug         = debug
    e.math             = math
    e.assert         = assert
    e.getmetatable     = getmetatable
    e.ipairs         = ipairs
    e.pairs         = pairs
    e.pcall         = pcall
    e.setmetatable     = setmetatable
    e.tostring         = tostring
    e.tonumber         = tonumber
    e.type             = type
    e.unpack         = unpack
    e.collectgarbage = collectgarbage
    -- class
    e.TREENODE = WIDGET_TREENODE
    e.SERIALIZE = WIDGET_SERIALIZE
    -- function
    e.CreateWidget = CreateWidget
    e._Inherit = _Inherit
end

只有被订制的函数,才能被调用
 
扩展:
 为了获得完整的内核模式开发,但又拥有独立的沙箱环境,可以使用setmetatable方式设置一个对全局table的引用,虽然速度慢了点。。
当然,可以支持一套可载入dll订制权限,并注册更多的api给自己的脚本用
posted on 2009-07-09 11:12 战魂小筑 阅读(2133) 评论(1)  编辑 收藏 引用 所属分类: 脚本技术

评论

# re: 构建类魔兽UI插件的lua安全沙箱 2009-07-09 11:20 Adrian
不错,希望接口可以再简洁一点,再快一点~  回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理