寻找良好的服务器端语言,允许玩家上传可以执行的代码 [英] Looking for good server-side language that will allow players to upload code that can be executed

查看:22
本文介绍了寻找良好的服务器端语言,允许玩家上传可以执行的代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个想要编写的程序的想法,但哪种语言最好是我的问题.

如果我有一个赛车游戏并且我希望允许用户提交新的交互式 3D 赛道(想想 Speed Racer 电影中的赛道)、车辆和他们的自动驾驶车辆的代码,那么,他们会为他们的汽车创建人工智能,使汽车能够确定如何处理危险.

所以,我需要一种运行速度很快的语言,并且作为服务器拥有的所有可用种族及其各种状态的世界地图的一部分.

我很好奇这是否是考虑在 Scala 中创建 DSL 的一个很好的理由?

我不想重新启动应用程序来加载新的 dll 或 jar 文件,因此许多编译语言会成为问题.

我对 Linux 或 Windows 持开放态度,对于大多数脚本语言、F#、Scala、Erlang 或大多数我可以编程的 OOP.

用户将能够监控他们的车辆的运行情况,如果他们为那辆车上传了多个 AI,当遇到某些障碍时,他们应该能够按需将一个 AI 程序替换为另一个.

更新:目前的解决方案是 javascript、使用 V8 和 Lua.

我很好奇这是否可以很好地用于 DSL,实际上是 3 个独立的 DSL.第一个用于创建赛道,另一个用于控制赛车,第三个用于创建新车.

如果是这样,那么 Haskell、F# 或 Scala 会是不错的选择吗?

更新:让不同的部分以不同的语言结束是否有意义?例如,如果Erlang用于控制汽车,Lua用于汽车本身,还用于动画赛道?

解决方案

你的情况听起来很适合 Lua.

  • 您需要沙箱:这是在 Lua 中很容易做到.例如,您只需通过覆盖或删除 os.execute 命令来初始化用户的环境,并且用户无法再访问该功能.
  • 你想要快速:查看一些 Lua 基准测试其他语言.
  • 假设您需要与另一种语言进行互操作.Lua 很容易 (IMO) 嵌入到 C 或 C++ 中,至少.我没有使用过 LuaInterface,但这是 C# 绑定.
  • Lua 具有一阶函数,因此应该很容易即时交换函数.
  • Lua 一定程度上支持 OOP 和元表.
  • Lua 的主要数据结构是 table(关联数组),它非常适合稀疏数据结构,例如与世界地图集成.
  • Lua 有一个非常规则的语法.分号或缩进并没有什么有趣的技巧,所以当你的用户学习你的语言时,这是少一件事——更不用说,使用一种有据可查的语言会带走你必须做的一些工作自己记录的条款.

此外,正如@elviejo 在评论中指出的那样,Lua 已经在许多游戏中用作脚本语言.如果不出意外,以您所描述的方式使用 Lua 肯定有一些先例.而且,正如@gmonc 所提到的,您的用户可能已经在另一款游戏中使用过 Lua.

<小时>至于如何与Lua集成:通常,您的用户只需要上传一个Lua脚本文件即可.为了过于简单,您可能会为用户提供可用的功能,例如 TurnLeftTurnRightGoStop.然后,用户将上传像

这样的脚本

Actions = {} -- 空表,但您可能希望提供默认功能函数 Actions.Cone()左转()结尾函数 Actions.Wall()停止()右转()右转()走()结尾

然后是服务器端,你可以用 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 失败 0#define 成功 1/* 玩家可能的状态 */枚举状态{跑步,跳跃,鸭子};/* 可能的障碍 */枚举障碍{地面,栅栏,分支};/* 为了简洁起见,这里使用全局变量 */枚举状态 playerstate = RUNNING;枚举障碍当前障碍=地面;/* 绑定到 Lua 的函数 */int Duck(lua_State *L){玩家状态 = DUCKING;返回0;/* 没有返回值给 Lua */}int 运行(lua_State *L){玩家状态 = 正在运行;返回0;}int 跳转(lua_State *L){玩家状态 = 跳跃;返回0;}/* 检查玩家是否可以通过障碍物,提供反馈 */int CanPassObstacle(){if ( (玩家状态 == RUNNING && currentobstacle == GROUND) ){printf("运行成功!\n");返回成功;}如果(玩家状态==跳跃&&当前障碍==围栏){printf("跳转成功!\n");返回成功;}如果(玩家状态 == DUCKING && currentobstacle == BRANCH){printf("鸭子成功!\n");返回成功;}printf("走错了!\n");返回失败;}/* 选择一个随机障碍物 */枚举障碍 GetNewObstacle(){int i = rand() % 3;如果(我 == 0){ 返回地面;}如果(我== 1){返回围栏;}否则 { 返回分支;}}/* 为下一个障碍执行 Lua 中定义的适当函数 */int HandleObstacle(lua_State *L){/* 获取名为 Actions 的表 */lua_getglobal(L, "Actions");if (!lua_istable(L, -1)) {返回失败;}当前障碍 = GetNewObstacle();/* 决定调用哪个用户函数 */如果(当前障碍==地面){lua_getfield(L, -1, "地面");}else if (currentobstacle == FENCE){lua_getfield(L, -1, "围栏");}else if (currentobstacle == BRANCH){lua_getfield(L, -1, "分支");}如果(lua_isfunction(L,-1)){lua_call(L, 0, 0);/* 0 个参数,0 个结果 */返回 CanPassObstacle();}返回失败;}int main(){国际我,资源;srand(时间(空));lua_State *L = lua_open();/* 将 C 函数绑定到 Lua 函数 */lua_pushcfunction(L, &Duck);lua_setglobal(L, "鸭子");lua_pushcfunction(L, &Run);lua_setglobal(L, "运行");lua_pushcfunction(L, &Jump);lua_setglobal(L, "跳转");/* 从标准输入执行脚本 */res = luaL_dofile(L, NULL);如果(资源){printf("Lua 脚本错误:%s\n", lua_tostring(L, -1));返回 1;}for (i = 0 ; i <5 ; i++){如果(处理障碍(L)==失败){printf("你失败了!\n");返回0;}}printf("你通过了!\n");返回0;}

使用 gcc runner.c -o runner -llua5.1 -I/usr/include/lua5.1 在 GCC 上构建上述内容.

而且几乎每次都能成功通过的唯一 Lua 脚本是:

Actions = {}函数 Actions.Ground() Run() 结束function Actions.Fence() Jump() end函数 Actions.Branch() Duck() 结束

也可以写成

Actions = {}Actions.Ground = 运行Actions.Fence = 跳跃Actions.Branch = 鸭子

使用好的脚本,您将看到如下输出:

<前>成功鸭!运行成功!跳跃成功!跳跃成功!成功鸭!你通过了!

如果用户尝试恶意操作,程序只会提供一个错误:

<前>$ echo "Actions = {} function Actions.Ground() os.execute('rm -rf/') end" |./跑步者PANIC:调用 Lua API 时出现未受保护的错误(标准输入:1:尝试索引全局os"(一个 nil 值))

如果移动脚本不正确,用户将看到他执行了错误的移动:

<前>$ echo "Actions = {} Actions.Ground = Jump; Actions.Fence = Duck; Actions.Branch = Run" |./跑步者错误的动作!你失败了!

I had an idea of a program I want to write, but which language would be best is my problem.

If I have a car racing game and I want to allow users to submit code for new interactive 3D race tracks (think of tracks such as found in the Speed Racer movie), vehicles and for their autonomous vehicles, so, they would create the AI for their car that will enable the car to determine how to handle hazards.

So, I need a language that will run fast, and as part of a world map that the server has of all the possible races available, and their various states.

I am curious if this would be a good reason to look at creating a DSL in Scala, for example?

I don't want to have to restart an application to load new dlls or jar files so many compiled languages would be a problem.

I am open to Linux or Windows, and for the languages, most scripting languages, F#, Scala, Erlang or most OOP I can program in.

The user will be able to monitor how their vehicle is doing, and if they have more than one AI uploaded for that car, when it gets to certain obstacles they should be able to swap one AI program for another on demand.

Update: So far the solutions are javascript, using V8, and Lua.

I am curious if this may be a good use for a DSL, actually 3 separate ones. 1 for creating a racetrack, another for controlling a racecar and the third for creating new cars.

If so, would Haskell, F# or Scala be good choices for this?

Update: Would it make sense to have different parts end up in different languages? For example, if Erlang was used for the controlling of the car and Lua for the car itself, and also for the animated racetrack?

解决方案

Your situation sounds like a good candidate for Lua.

  • You need sandboxing: This is easy to do in Lua. You simply initialize the users' environment by overwriting or deleting the os.execute command, for instance, and there is no way for the user to access that function anymore.
  • You want fast: Check out some of the Lua benchmarks against other languages.
  • Assumably you need to interoperate with another language. Lua is very easy (IMO) to embed in C or C++, at least. I haven't used LuaInterface, but that's the C# binding.
  • Lua has first-order functions, so it should be easy to swap functions on-the-fly.
  • Lua supports OOP to some extent with metatables.
  • Lua's primary data structure is the table (associative array) which is well-suited to sparse data structures like integrating with a world map.
  • Lua has a very regular syntax. There are no funny tricks with semicolons or indentation, so that's one less thing for your users to learn when they are picking up your language -- not to mention, using a well-documented language takes away some of the work you have to do in terms of documenting it yourself.

Also, as @elviejo points out in a comment, Lua is already used as a scripting language in many games. If nothing else, there's certainly some precedent for using Lua in the way you've described. And, as @gmonc mentions, there is a chance that your users have already used Lua in another game.


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

Then server-side, you would might start them off with a Go(). Then, when their car reaches a cone, you call their Actions.Cone() function; a wall leads to the Actions.Wall() function, etc. At this point, you've (hopefully) already sandboxed the Lua environment, so you can simply execute their script without even much regard for error checking -- if their script results in an error, no reason you can't pass the error on directly to the user. And if there aren't any errors, the lua_State in your server's code should contain the final state of their car.


Better example

Here's a standalone C file that takes a Lua script from stdin and runs it like I explained above. The game is that you'll encounter Ground, a Fence, or a Branch, and you have to respectively Run, Jump, or Duck to pass. You input a Lua script via stdin to decide how to react. The source is a little long, but hopefully it's easy to understand (besides the Lua API which takes a while to get used to). This is my original creation over the past 30 minutes, hope it helps:

#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;
}

Build the above on GCC with gcc runner.c -o runner -llua5.1 -I/usr/include/lua5.1.

And pretty much the only Lua script that will pass successfully every time is:

Actions = {}

function Actions.Ground() Run() end
function Actions.Fence() Jump() end
function Actions.Branch() Duck() end

which could also be written as

Actions = {}
Actions.Ground = Run
Actions.Fence = Jump
Actions.Branch = Duck

With the good script, you'll see output like:

Successful duck!
Successful run!
Successful jump!
Successful jump!
Successful duck!
You passed!

If the user tries something malicious, the program will simply provide an error:

$ 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))

With an incorrect move script, the user will see that he performed the wrong move:

$ echo "Actions = {} Actions.Ground = Jump; Actions.Fence = Duck; Actions.Branch = Run" | ./runner 
Wrong move!
You failed!

这篇关于寻找良好的服务器端语言,允许玩家上传可以执行的代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆