我对我想要编写的程序有所了解,但哪种语言最好是我的问题.
如果我有一个赛车游戏,我想让用户提交新的交互式3D赛道(想想Speed Racer电影中的曲目),车辆和自动驾驶汽车的代码,那么,他们会创建AI他们的汽车将使汽车能够确定如何处理危险.
所以,我需要一种能够快速运行的语言,并且作为服务器具有所有可用种族的世界地图的一部分,以及它们的各种状态.
我很好奇,例如,这是否是在Scala中创建DSL的一个很好的理由?
我不想重新启动一个应用程序来加载新的dll或jar文件这么多编译语言会有问题.
我对Linux或Windows开放,对于语言,大多数脚本语言,F#,Scala,Erlang或大多数OOP我可以编程.
用户将能够监控他们的车辆如何运行,并且如果他们为该车辆上载了多个AI,当遇到某些障碍时,他们应该能够根据需要将一个AI程序换成另一个AI程序.
更新:到目前为止,解决方案是javascript,使用V8和Lua.
我很好奇这是否适用于DSL,实际上是3个独立的DSL.1用于创建赛道,另一个用于控制赛车,第三个用于创建新车.
如果是这样,那么Haskell,F#或Scala会是一个很好的选择吗?
更新:让不同的部分以不同的语言结束是否有意义?例如,如果Erlang用于控制汽车,Lua用于汽车本身,还用于动画赛道?
你的情况听起来像是Lua的一个很好的候选人.
你需要沙盒:这在Lua很容易.例如,您只需通过覆盖或删除os.execute
命令来初始化用户的环境,用户就无法再访问该功能.
你想要快速:查看一些针对其他语言的Lua基准测试.
可能你需要与另一种语言互操作.Lua非常容易(IMO)嵌入C或C++,至少.我没有使用LuaInterface,但这是C#绑定.
Lua具有一阶函数,因此应该可以轻松地即时交换函数.
Lua 在某种程度上支持OOP与metatables.
Lua的主要数据结构是表(关联数组),它非常适合稀疏数据结构,例如与世界地图集成.
Lua有一个非常规则的语法.没有带有分号或缩进的有趣技巧,所以当用户拿起你的语言时,用户学习的东西就少了一些 - 更不用说,使用记录良好的语言会带走你必须完成的一些工作.自己记录的条款.
另外,正如@elviejo在评论中指出的那样,Lua已经在许多游戏中被用作脚本语言.如果不出意外,以你所描述的方式使用Lua肯定有一些先例.并且,正如@gmonc所提到的,您的用户可能已经在另一个游戏中使用过Lua.
TurnLeft
,TurnRight
,Go
,和Stop
.然后,用户将上传类似的脚本
Actions = {} -- empty table, but you might want to provide default functions function Actions.Cone() TurnLeft() end function Actions.Wall() Stop() TurnRight() TurnRight() Go() end
然后服务器端,你可能会用a启动它们Go()
.然后,当他们的汽车到达一个圆锥体时,你会调用它们的Actions.Cone()
功能; 墙壁通向Actions.Wall()
功能等.此时,您(希望)已经沙箱化了Lua环境,因此您可以简单地执行其脚本而不必过多考虑错误检查 - 如果它们的脚本导致错误,则不您无法直接将错误传递给用户的原因.如果没有任何错误,lua_State
服务器代码中的代码应包含其汽车的最终状态.
这是一个独立的C文件,它从stdin获取Lua脚本并像我上面解释的那样运行它.游戏是你会遇到地面,栅栏或树枝,你必须分别跑,跳或鸭子通过.您通过stdin输入Lua脚本来决定如何做出反应.源代码有点长,但希望它很容易理解(除了需要一段时间才能习惯的Lua API).这是我过去30分钟的原创作品,希望它有所帮助:
#include#include #include #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 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!
为什么不使用JavaScript或EcmaScript?谷歌的V8是一个非常好的沙盒方式来做到这一点.我记得它真的很容易.当然,你必须为它编写一些绑定.