我知道我想写的程序,但哪种语言最好是我的问题。
如果我有一个赛车游戏,我想让用户提交新的交互式3D赛道的代码(想想Speed Racer电影中的曲目),车辆和他们的自动驾驶汽车,那么,他们会为他们的汽车创建AI,使汽车能够确定如何处理危险。
所以,我需要一种能够快速运行的语言,并且作为服务器具有所有可用种族的世界地图的一部分,以及它们的各种状态。
我很好奇,例如,这是否是在Scala中创建DSL的一个很好的理由?
我不想重新启动一个应用程序来加载新的dll或jar文件,因此很多编译语言会出问题。
我对Linux或Windows开放,对于语言,大多数脚本语言,F#,Scala,Erlang或大多数OOP我都可以编程。
用户将能够监控他们的车辆如何运行,以及他们是否为该车上传了多个AI,当遇到某些障碍时,他们应该能够按需交换一个AI程序。 / p>
更新:到目前为止,解决方案是javascript,使用V8和Lua。
我很好奇这是否适用于DSL,实际上是3个独立的DSL。 1用于创建赛道,另一个用于控制赛车,第三个用于创建新车。
如果是这样,那么Haskell,F#或Scala会选择这个吗?
更新:让不同的部分以不同语言结尾是否有意义?例如,如果Erlang用于控制汽车,Lua用于汽车本身,还用于动画赛道?
答案 0 :(得分:31)
你的情况听起来像是Lua的好候选人。
os.execute
命令来初始化用户的环境,用户无法再访问该功能。另外,正如@elviejo在评论中指出的那样,Lua已经在许多游戏中被用作脚本语言。如果不出意外,以你所描述的方式使用Lua肯定有一些先例。并且,正如@gmonc所提到的,您的用户可能已经在另一个游戏中使用过Lua。
<小时/> 至于如何与Lua集成:通常,您的用户应该只需要上传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
然后在服务器端,您可以使用Go()
启动它们。然后,当他们的汽车到达一个圆锥时,你会调用他们的Actions.Cone()
功能;墙壁通向Actions.Wall()
函数等。此时,您(希望)已经沙箱化了Lua环境,因此您可以简单地执行其脚本而不必过多考虑错误检查 - 如果它们的脚本结果在一个错误中,没有理由你不能直接将错误传递给用户。如果没有任何错误,服务器代码中的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 runner.c -o runner -llua5.1 -I/usr/include/lua5.1
在GCC上构建上述内容。
几乎每次成功通过的唯一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!
答案 1 :(得分:10)
为什么不使用JavaScript或EcmaScript?谷歌的V8是一个非常好的沙盒方式。我记得它真的很容易。当然,你必须为它写一些绑定。
答案 2 :(得分:2)
我推荐Dot Net有几个原因:
玩家可以选择使用哪种语言来实现他们的解决方案:C#,IronPython,VB.NET,Boo等,但运行时并不关心 - 它只是动态地将点网组件加载到其沙箱中。但这会让您的玩家选择自己喜欢的语言。这鼓励玩家享受体验,而不是一些决定不参与的玩家,因为他们根本不喜欢你选择的单一语言。你的整体框架可能在C#中,但玩家的代码可能是任何Dot Net语言。
沙盒和动态加载在Dot Net中非常成熟。您可以将玩家的程序集加载到与Partial Trust一起运行的沙盒AppDomains中。您不必重新启动容器进程来加载和卸载这些播放器AppDomains。
鼓励玩家“玩”这个游戏,因为语言(他们选择的Dot Net语言)不仅对游戏脚本有用,而且可以带来真正的行业生涯。例如,搜索“C#”可以获得比“Lua”或“Haskell”更多的批次命中。因此,游戏不仅有趣,而且特别是对于年轻玩家来说,实际上是帮助他们学习真正有用的,适销对路的技能,以后可以赚钱。这是参与这场比赛的巨大鼓励。
执行速度很快。与Lua这样的替代品不同,这是一种编译代码,即使在实时游戏中也能以出色的性能而闻名。 (例如,参见Unity3d)。
玩家可以在Mac和Linux上使用MonoDevelop,也可以从Microsoft免费使用Visual Studio Express,或者他们可以使用好的记事本和命令行。与此处的替代方案的不同之处在于,如果玩家应该选择使用它们,则可以使用成熟的现代IDE。
对于问题的AI部分而言,DSL似乎不是一个好主意,因为为车辆实施AI将需要玩家的很多创造性问题解决。使用DSL,您只能将它们锁定在您考虑问题时定义问题的方式。拥有完整平台(如Dot Net(或其他一些选项))的智能播放器可能会为您从未预见到的一些AI问题提供全新的创新解决方案。使用正确的工具,这些玩家可以实施疯狂的学习计划或小型神经网络,或者谁知道为了实现他们的AI。但是如果你将它们锁定在一个简化的DSL中,那么不同玩家的AI实现可能没有多少变化(因为它们可用的思想表达式集合要小得多)。
对于问题的其他部分,例如定义轨道,DSL可能没问题。尽管如此,我还是倾向于像Boo这样简单的Dot Net语言之一,这样你就可以为整个项目建立一个统一的技术堆栈。
答案 3 :(得分:1)
我之前在MMO中做过,你知道,NPC响应脚本都在使用python,而它是在C ++框架中,比如任何与NPC相关的动作都会触发框架运行python脚本(C-python接口)当然,不是shell调用,如“python /script/run.py”)。脚本是可替换的运行时,虽然需要播放器或游戏管理员发出命令来进行刷新,但无论如何游戏服务器程序都不需要重新启动。
实际上我忘记了是否需要“通过发出命令进行刷新”是否需要新的脚本运行时... 2年前......但我觉得它适合你。
答案 4 :(得分:1)
考虑Erlang:
您需要沙盒/ DSL:您可以编写“解析器生成器”来擦除对关键/易受攻击的系统调用的访问。使用此功能可以“轻松”增强库存编译器。
您需要细粒度的调度:如果您在单独的模拟器中运行每个“用户”,则还需要some control。也许你可以做得更好,但我必须挖掘更多。记住安排是O(1): - )
您需要在“玩家”之间进行资源分区(我想如果我理解正确的话):Erlang没有共享状态,所以这有助于从开始。您可以轻松制作一些监视玩家资源消耗的主管等。另请参阅上面的链接(控制模拟器的许多旋钮)。
您需要代码热交换:Erlang是从开始设计的
你需要扩展:Erlang很好地扩展了SMP,因为它基于消息传递和无缝的机器间通信,你可以水平扩展
您可以使用C驱动程序优化关键路径
集成“管理员”功能,可以优雅地重新启动“用户”