你的情况听起来很适合 Lua。
- 你需要沙箱:这是Lua 很容易做 http://lua-users.org/wiki/SandBoxes。您只需通过覆盖或删除来初始化用户环境
os.execute
例如,命令,用户无法再访问该功能。
- 您想要快速:查看一些Lua 与其他语言的基准测试 http://benchmarksgame.alioth.debian.org/u64q/which-programs-are-fastest.html.
- 假设您需要与另一种语言进行互操作。至少 Lua 很容易(在我看来)嵌入到 C 或 C++ 中。我没用过Lua接口 http://luaforge.net/projects/luainterface/,但这就是 C# 绑定。
- Lua 具有一阶函数,因此应该很容易即时交换函数。
- Lua 在一定程度上支持OOP http://www.lua.org/pil/28.3.html与元表。
- Lua的主要数据结构是table http://lua-users.org/wiki/TablesTutorial(关联数组)非常适合稀疏数据结构,例如与世界地图集成。
- Lua 有非常规则的语法。分号或缩进没有什么有趣的技巧,所以当你的用户学习你的语言时,他们就少了一件需要学习的事情——更不用说,使用一种记录良好的语言会带走你必须做的一些工作。自己记录的条款。
此外,正如 @elviejo 在评论中指出的那样,Lua 已经在许多游戏中用作脚本语言。如果不出意外的话,按照您所描述的方式使用 Lua 肯定有一些先例。而且,正如 @gmonc 提到的,您的用户有可能已经在另一款游戏中使用过 Lua。
As far as
how to integrate with Lua: generally, your users should simply need to upload a Lua script file. To grossly oversimplify, you might provide the users with available functions such as
TurnLeft
,
TurnRight
,
Go
, and
Stop
. Then, the users would upload a script like
Actions = {} -- empty table, but you might want to provide default functions
function Actions.Cone()
TurnLeft()
end
function Actions.Wall()
Stop()
TurnRight()
TurnRight()
Go()
end
然后在服务器端,您可能会用Go()
。然后,当他们的车到达锥体时,你打电话给他们Actions.Cone()
功能;一堵墙通向Actions.Wall()
函数等。此时,您(希望)已经对 Lua 环境进行沙箱化,因此您可以简单地执行他们的脚本,甚至无需考虑错误检查 - 如果他们的脚本导致错误,您没有理由不能将错误直接传递给用户。如果有aren't任何错误,lua_State
服务器代码中应包含汽车的最终状态。
更好的例子
这是一个独立的 C 文件,它从 stdin 获取 Lua 脚本并像我上面解释的那样运行它。游戏中你会遇到地面、栅栏或树枝,你必须分别奔跑、跳跃或躲避才能通过。您通过 stdin 输入 Lua 脚本来决定如何反应。源代码有点长,但希望它很容易理解(除了 Lua API 需要一段时间才能习惯)。这是我过去30分钟的原创作品,希望对你有帮助:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#define FAIL 0
#define SUCCESS 1
/* Possible states for the player */
enum STATE {
RUNNING,
JUMPING,
DUCKING
};
/* Possible obstacles */
enum OBSTACLE {
GROUND,
FENCE,
BRANCH
};
/* Using global vars here for brevity */
enum STATE playerstate = RUNNING;
enum OBSTACLE currentobstacle = GROUND;
/* Functions to be bound to Lua */
int Duck(lua_State *L)
{
playerstate = DUCKING;
return 0; /* no return values to Lua */
}
int Run(lua_State *L)
{
playerstate = RUNNING;
return 0;
}
int Jump(lua_State *L)
{
playerstate = JUMPING;
return 0;
}
/* Check if player can pass obstacle, offer feedback */
int CanPassObstacle()
{
if ( (playerstate == RUNNING && currentobstacle == GROUND) )
{
printf("Successful run!\n");
return SUCCESS;
}
if (playerstate == JUMPING && currentobstacle == FENCE)
{
printf("Successful jump!\n");
return SUCCESS;
}
if (playerstate == DUCKING && currentobstacle == BRANCH)
{
printf("Successful duck!\n");
return SUCCESS;
}
printf("Wrong move!\n");
return FAIL;
}
/* Pick a random obstacle */
enum OBSTACLE GetNewObstacle()
{
int i = rand() % 3;
if (i == 0) { return GROUND; }
if (i == 1) { return FENCE; }
else { return BRANCH; }
}
/* Execute appropriate function defined in Lua for the next obstacle */
int HandleObstacle(lua_State *L)
{
/* Get the table named Actions */
lua_getglobal(L, "Actions");
if (!lua_istable(L, -1)) {return FAIL;}
currentobstacle = GetNewObstacle();
/* Decide which user function to call */
if (currentobstacle == GROUND)
{
lua_getfield(L, -1, "Ground");
}
else if (currentobstacle == FENCE)
{
lua_getfield(L, -1, "Fence");
}
else if (currentobstacle == BRANCH)
{
lua_getfield(L, -1, "Branch");
}
if (lua_isfunction(L, -1))
{
lua_call(L, 0, 0); /* 0 args, 0 results */
return CanPassObstacle();
}
return FAIL;
}
int main()
{
int i, res;
srand(time(NULL));
lua_State *L = lua_open();
/* Bind the C functions to Lua functions */
lua_pushcfunction(L, &Duck);
lua_setglobal(L, "Duck");
lua_pushcfunction(L, &Run);
lua_setglobal(L, "Run");
lua_pushcfunction(L, &Jump);
lua_setglobal(L, "Jump");
/* execute script from stdin */
res = luaL_dofile(L, NULL);
if (res)
{
printf("Lua script error: %s\n", lua_tostring(L, -1));
return 1;
}
for (i = 0 ; i < 5 ; i++)
{
if (HandleObstacle(L) == FAIL)
{
printf("You failed!\n");
return 0;
}
}
printf("You passed!\n");
return 0;
}
在 GCC 上构建上述内容gcc runner.c -o runner -llua5.1 -I/usr/include/lua5.1
.
几乎唯一每次都会成功通过的 Lua 脚本是:
Actions = {}
function Actions.Ground() Run() end
function Actions.Fence() Jump() end
function Actions.Branch() Duck() end
也可以写成
Actions = {}
Actions.Ground = Run
Actions.Fence = Jump
Actions.Branch = Duck
使用好的脚本,您将看到如下输出:
Successful duck!
Successful run!
Successful jump!
Successful jump!
Successful duck!
You passed!
如果用户尝试恶意操作,程序将简单地提供错误:
$ echo "Actions = {} function Actions.Ground() os.execute('rm -rf /') end" | ./runner
PANIC: unprotected error in call to Lua API (stdin:1: attempt to index global 'os' (a nil value))
如果移动脚本不正确,用户将看到他执行了错误的移动:
$ echo "Actions = {} Actions.Ground = Jump; Actions.Fence = Duck; Actions.Branch = Run" | ./runner
Wrong move!
You failed!