Ищете хороший серверный язык, который позволит игрокам загружать исполняемый код

У меня была идея программы, которую я хочу написать, но какой язык будет лучше, это моя проблема.

Если у меня есть гоночная игра, и я хочу, чтобы пользователи могли отправлять код для новых интерактивных трехмерных гоночных трасс (подумайте о трассах, таких как в фильме «Скоростной гонщик»), транспортных средств и их автономных транспортных средств, то они должны создать ИИ. для их автомобиля, что позволит автомобилю определить, как справляться с опасностями.

Итак, мне нужен язык, который будет работать быстро и как часть карты мира, имеющейся на сервере, со всеми возможными доступными расами и их различными состояниями.

Мне любопытно, будет ли это хорошей причиной, чтобы взглянуть на создание DSL, например, в Scala?

Я не хочу перезапускать приложение для загрузки новых DLL или JAR-файлов, так что многие скомпилированные языки будут проблемой.

Я открыт для Linux или Windows, а также для языков, большинства языков сценариев, F #, Scala, Erlang или большинства ООП, на которых я могу программировать.

Пользователь сможет следить за тем, как ведет себя его автомобиль, и, если для этого автомобиля загружено более одного ИИ, когда он столкнется с определенными препятствиями, он сможет заменить одну программу ИИ на другую по требованию.

Обновление: Пока что решения — это javascript, использующие V8 и Lua.

Мне любопытно, может ли это быть хорошим использованием DSL, на самом деле 3 отдельных. 1 для создания гоночной трассы, другой для управления гоночной машиной и третий для создания новых автомобилей.

Если да, то подойдут ли для этого Haskell, F# или Scala?

Обновление: имеет ли смысл переводить разные части на разные языки? Например, если бы Erlang использовался для управления автомобилем, а Lua — для самого автомобиля, а также для анимированной гоночной трассы?


person James Black    schedule 18.10.2009    source источник
comment
Кто-то должен это сказать: независимо от того, какой язык вы выберете (у меня, к сожалению, нет права давать советы в этой области), убедитесь, что вы чертовски изолированы от песочницы. Выполнение пользовательского кода всегда представляет собой серьезную угрозу безопасности.   -  person    schedule 18.10.2009
comment
Я тоже об этом думал. Вот почему я склоняюсь к DSL, но я никогда не создавал его, но это может быть самый безопасный подход.   -  person James Black    schedule 18.10.2009
comment
С точки зрения песочницы заманчиво предложить чисто функциональный язык, такой как Haskell. Вы можете создать свою собственную монаду состояния, которая разрешает только те операции, которые вы хотите разрешить. Пользователь отправляет функцию, которая возвращает экземпляр этой монады (просто следите за любыми вызовами unsafePerformIO в их коде!). С другой стороны, это было бы очень сложно для пользователей, которые плохо разбираются в программировании.   -  person David    schedule 18.10.2009
comment
@David - Вот почему я думаю, что DSL может быть полезен, так как я мог бы дать им простой язык для использования для каждой части, если я правильно его понимаю. Я мог бы выучить Haskell, но я экспериментировал со Scala, F# и начал экспериментировать с Erlang.   -  person James Black    schedule 18.10.2009
comment
Независимо от языка, мне любопытно увидеть эту игру в действии :)   -  person André Pena    schedule 25.10.2009
comment
Мне тоже интересно, на самом деле. Я думаю, что идея фреймворка, поддерживающего это, будет сложной, но очень интересной.   -  person James Black    schedule 25.10.2009


Ответы (5)


Ваша ситуация звучит как хороший кандидат на Lua.

Кроме того, как отмечает @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, который берет скрипт Lua из стандартного ввода и запускает его, как я объяснил выше. Игра заключается в том, что вы столкнетесь с землей, забором или веткой, и вам нужно соответственно бежать, прыгать или пригибаться, чтобы пройти. Вы вводите сценарий 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!
person Mark Rushakoff    schedule 20.10.2009
comment
Я думал, что Lua может получить упоминание. Я посмотрю на это более внимательно и посмотрю, как это работает. Спасибо. - person James Black; 20.10.2009
comment
Lua уже используется в качестве встроенного языка в нескольких видеоиграх. Вот список игр, использующих его: en.wikipedia.org/wiki/Category:Lua -scripted_video_games Так что, возможно, LUA не идеальное решение. Но точно не плохой. - person elviejo79; 26.10.2009
comment
Поскольку он действительно хорошо используется в других игровых платформах, у вас может быть преимущество знакомства пользователя с синтаксисом Lua, что я считаю не самой важной особенностью, но все же хорошим дополнением. - person GmonC; 26.10.2009
comment
При голосовании здесь мне нужно будет больше посмотреть, как будет использоваться Lua. Буду ли я использовать metalua, например? Я на самом деле удивлен результатами до сих пор. - person James Black; 26.10.2009
comment
Есть ли шанс, что вы можете помочь мне здесь? - person Stefan Falk; 12.12.2016

Почему не JavaScript или EcmaScript? V8 от Google — действительно хороший изолированный способ сделать это. Я помню, что это было действительно очень легко. Конечно, вам придется написать для него какие-то привязки.

person Daniel A. White    schedule 18.10.2009
comment
Огромный +1. JavaScript — это абсолютно правильный путь. Также есть вариант Rhino, если вы предпочитаете среду Java. - person Bob Aman; 20.10.2009
comment
Моя единственная проблема с javascript заключается в том, что может быть сложно создать язык для создания графики, например, написать код, который создает/управляет ипподромом. - person James Black; 20.10.2009
comment
Javascript был бы идеальным, например, для программирования гоночного автомобиля, что, скорее всего, будет сделано, поэтому теоретически может быть использовано более одного языка. - person James Black; 20.10.2009

Я бы рекомендовал Dot Net по нескольким причинам:

Игроки могут выбирать, на каком языке они реализуют свои решения: C#, IronPython, VB.NET, Boo и т. д., но вашей среде выполнения все равно — она просто динамически загружает сборки dot net в свою песочницу. Но это дает вашим игрокам возможность выбора своего любимого языка. Это побуждает игроков получать удовольствие от игры, а не некоторых игроков, которые решают не участвовать, потому что им просто не нравится выбранный вами язык. Ваша общая структура, вероятно, будет написана на C#, но код игроков может быть на любом языке Dot Net.

Песочница и динамическая загрузка очень развиты в Dot Net. Вы можете загружать сборки игроков в свои собственные изолированные домены приложений, которые работают с частичным доверием. Вам не нужно было бы перезапускать процесс контейнера, чтобы загружать и выгружать эти домены приложений игрока.

Игрокам рекомендуется «играть» в эту игру, потому что язык (какой бы язык Dot Net они ни выбрали) не только полезен для написания игровых сценариев, но и может привести к реальной карьере в отрасли. Выполнение поиска работы для "C#" дает намного больше совпадений, чем, например, для "Lua" или "Haskell". Таким образом, игра не только развлекает, но и, особенно для младших игроков, фактически помогает им освоить действительно полезные, востребованные на рынке навыки, которые впоследствии могут приносить им деньги. Это большой стимул для участия в этой игре.

Скорость выполнения зашкаливает. В отличие от некоторых альтернатив, таких как Lua, это скомпилированный код, хорошо известный своей отличной производительностью даже в играх в реальном времени. (См., например, Unity3d).

Игроки могут использовать MonoDevelop на Mac и Linux, или они могут бесплатно использовать Visual Studio Express от Microsoft, или они могут использовать старый добрый блокнот и командную строку. Отличие от альтернатив здесь в том, что зрелые, современные IDE доступны, если игроки захотят их использовать.

DSL не кажется хорошей идеей для части проблемы ИИ просто потому, что реализация ИИ для транспортных средств потребует от игроков большого творческого подхода к решению проблем. С DSL вы блокируете их только так, как вы определили проблему, когда думали об этом. Умные игроки с полной платформой, такой как Dot Net (или некоторые другие упомянутые варианты), могут иметь радикально новые и инновационные решения некоторых проблем ИИ, которые вы никогда не предвидели. С правильными инструментами эти игроки могли бы реализовать сумасшедшие обучающие программы, небольшие нейронные сети или кто знает что, чтобы реализовать свой ИИ. Но если вы замкнете их в упрощенный DSL, в реализациях ИИ разных игроков может не быть большого разнообразия (поскольку их набор доступных выражений идей намного меньше).

Для других частей проблемы, таких как определение дорожек, DSL может подойти. Опять же, я бы склонялся к одному из более простых языков Dot Net, такому как Boo, просто потому, что вы можете иметь унифицированный технический стек для всего вашего проекта.

person Clay Fowler    schedule 26.10.2009
comment
Моя единственная проблема с .NET заключается в том, что я не смогу динамически загружать и запускать без перезапуска приложения, и если вы просто меняете один алгоритм на другой, у меня есть проблема, что я должен закрыть приложение, чтобы сделать Это. Другая проблема заключается в том, что не все занимаются программированием .NET, но это незначительная проблема. - person James Black; 27.10.2009
comment
Вы определенно можете динамически загружать и выгружать без перезапуска приложения. - person Clay Fowler; 27.10.2009
comment
И да, не все используют .net (или что-то еще), но вы, вероятно, можете найти больше людей, которые используют один из языков с точечной сетью, чем любой другой фиксированный язык. По крайней мере, таким образом игроки могли выбрать один из понравившихся (или уже известных) языков. - person Clay Fowler; 27.10.2009

Я делал это раньше в MMO, вы знаете, сценарии ответа NPC использовали python, в то время как он находится в среде C ++, скажем, любое действие, связанное с NPC, вызовет запуск платформы для запуска сценария python (конечно, интерфейс C-python, а не вызов оболочки, такой как «python /script/run.py»). Сценарии заменяемы во время выполнения, хотя игроку или администратору игры требуется выполнить команду для обновления, но в любом случае перезапуск программы игрового сервера не требуется.

На самом деле я забыл, что требовалось ли «обновить, выполнив команду» для новой среды выполнения скрипта.. 2 года назад ... но я думаю, что это подходит для вас.

person Test    schedule 23.10.2009

Рассмотрим Эрланг:

  • Вам нужна песочница / DSL: вы можете написать «генераторы парсеров», чтобы очистить доступ к критическим / уязвимым системным вызовам. Стандартный компилятор может быть "легко" улучшен с помощью этой функциональности.

  • Вам необходимо детальное планирование: у вас есть некоторый контроль над этим, если вы запускайте каждого "пользователя" в отдельных эмуляторах. Может быть, вы можете сделать лучше, но мне придется копать больше. Помните, что планирование O (1): -)

  • Вам нужно разделение ресурсов между вашими «игроками» (думаю, если я правильно понял): Erlang не имеет общего состояния, поэтому это помогает с самого начала. Вы можете легко создать несколько супервайзеров, которые следят за потреблением ресурсов игроками и т. д. См. также ссылку выше (множество ручек для управления эмулятором).

  • Вам нужна горячая замена кода: Erlang был разработан для этого с самого начала.

  • Вам нужно масштабирование: Erlang хорошо масштабируется с помощью SMP, а поскольку он основан на передаче сообщений с бесшовной межмашинной связью, вы можете масштабировать горизонтально.

  • Вы можете оптимизировать критические пути с помощью драйверов C

  • Интегрированная функция «супервизора» для изящного перезапуска «пользователей».

Ульф Вигер о параллелизме

person jldupont    schedule 24.10.2009
comment
Я только недавно услышал о том, что Erlang поддерживает горячую замену, что интересно. - person James Black; 24.10.2009