Source SDK

Source SDK

Not enough ratings
Lua Source C++
By Effluvia
Это руководство покажет основы встраивания Lua кода в движок Source.
Проверено на Source SDK 2009 | При использовании Visual Studio 2008
   
Award
Favorite
Favorited
Unfavorite
Предисловие
Информация расписанная снизу принадлежит Valve
Моя задача была - перевод на русский язык | адаптация к Steam
Что такое Lua?
Lua сочетает в себе простой процедурный синтаксис с мощными конструкциями описания данных на основе ассоциативных массивов и расширяемой семантики. Lua динамически типизируется, выполняется путем интерпретации байт-кода для виртуальной машины на основе регистра и имеет автоматическое управление памятью с инкрементной сборкой мусора, что делает его идеальным для конфигурации, сценариев и быстрого прототипирования.
Вы можете узнать больше о Lua на официальном сайте.[www.lua.org]
Обзор
Lua в настоящее время является ведущим[www.satori.org] скриптовым языком в играх, а у пользователей Steam ассоциируется с Garry's Mod. К сожалению, исходный код игры Garry's Mod не распространяется, и, как результат, мы не можем использовать реализацию языка Lua из этой игры для своего мода. Тем не менее, мой проект Half-Life 2: Sandbox[code.google.com] в настоящее время является ведущим проектом с открытым исходным кодом, предназначенным для обеспечения сообщества разработчиков методом внедрения Lua в их мод практически без проблем, и в этой статье я объясню, как это сделать.

В Half-Life 2: Sandbox используется чистый Lua, отличающийся от реализации языка в моде Гарри, который имеет ограничения на многие библиотеки и модифицированный синтаксис для комментариев в стиле C++. Цель Sandbox - обеспечить нейтральную, чистую, стандартизированную и хорошо документированную базу кода для взаимодействия с Lua, в отличие от статей, увиденных в сообществе разработчиков Valve, таких как учебник LuaBind[www.rasterbar.com] по языку Lua или просто статья о добавлении Lua.
Добавление LUA
Надо добавить два файла в ваш проект:
LuaManager_CPP
LuaManager_H

Инициализация
Все изменения будут выполняться в проекте Server.
Сначала добавьте include в файл gameinterface.cpp.

#ifdef GE_LUA #include "ge_luamanager.h" #endif

В gameinterface.cpp добавьте этот вызов в функцию DLLInit перед "return true".
#ifdef GE_LUA // Start LUA GELua()->InitDll(); #endif

И добавляем вызов в конец функции DLLShutdown:
#ifdef GE_LUA // Shutdown LUA, close all open gameplays GELua()->ShutdownDll(); #endif

Создаём Lua Обработчик
Это самая интересная часть. Вам необходимо создать класс, который наследуется от LuaHandle и имеет функции Init, Shutdown, RegFunctions и RegGlobals. Это изначальный вид класса (g_LuaHandle и GetLuaHandle() нам понадобятся в будущем):
class MyLuaHandle : LuaHandle { MyLuaHandle(); ~MyLuaHandle(); void Init(); void Shutdown(); void RegFunctions(); void RegGlobals(); }; MyLuaHandle* g_LuaHandle = NULL; MyLuaHandle *GetLuaHandle();

Init
Init вызывается после инициализации Lua, а это означает, что это лучшее место для загрузки скрипта.
void MyLuaHandle::Init() { const char* luaFile = "myLuaFile.lua"; //Load into buffer FileHandle_t f = filesystem->Open( luaFile, "rb", "MOD" ); if (!f) return; // load file into a null-terminated buffer int fileSize = filesystem->Size(f); unsigned bufSize = ((IFileSystem *)filesystem)->GetOptimalReadSize( f, fileSize + 1 ); char *buffer = (char*)((IFileSystem *)filesystem)->AllocOptimalReadBuffer( f, bufSize ); Assert(buffer); ((IFileSystem *)filesystem)->ReadEx( buffer, bufSize, fileSize, f ); // read into local buffer buffer[fileSize] = '\0'; // null terminate file as EOF filesystem->Close( f ); // close file after reading int error = luaL_loadbuffer( GetLua(), buffer, fileSize, luaFile ); if (error) { Warning("[LUA-ERR] %s\n", lua_tostring(GetLua(), -1)); lua_pop(GetLua(), 1); /* pop error message from the stack */ Warning("[LUA-ERR] One or more errors occured while loading lua script!\n"); return; } CallLUA(GetLua(), 0, LUA_MULTRET, 0, luaFile ); m_bLuaLoaded = true; }

Shutdown
В функции Shutdown обрабатывается завершение работы Lua.

RegGlobals
RegGlobals регистрирует глобальные переменные lua.
void MyLuaHandle::RegGlobals() { LG_DEFINE_INT("FOR_ALL_PLAYERS", -1); LG_DEFINE_INT("INVALID_ENTITY", -1); LG_DEFINE_INT("NULL", 0); LG_DEFINE_INT("GE_MAX_HEALTH", MAX_HEALTH); LG_DEFINE_INT("GE_MAX_ARMOR", MAX_ARMOR); LG_DEFINE_INT("MAX_PLAYERS", gpGlobals->maxClients); //Team Indices LG_DEFINE_INT("TEAM_NONE",TEAM_UNASSIGNED); LG_DEFINE_INT("TEAM_SPECTATOR",TEAM_SPECTATOR); LG_DEFINE_INT("TEAM_MI6",TEAM_MI6); LG_DEFINE_INT("TEAM_JANUS",TEAM_JANUS); //ClientPrintAll Types LG_DEFINE_INT("HUD_PRINTNOTIFY",HUD_PRINTNOTIFY); LG_DEFINE_INT("HUD_PRINTCONSOLE",HUD_PRINTCONSOLE); LG_DEFINE_INT("HUD_PRINTTALK",HUD_PRINTTALK); LG_DEFINE_INT("HUD_PRINTCENTER",HUD_PRINTCENTER); }
Данные макросы также могут регистрировать и другие типы данных (LG_DEFINE_INT для чисел,LG_DEFINE_STRING для строк и LG_DEFINE_BOOL для булева)

RegFunctions
В этой функции вы регистрируете функции для Lua.
void MyLuaHandle::RegFunctions() { REG_FUNCTION( Msg ); REG_FUNCTION( ConMsg ); REG_FUNCTION( ClientPrintAll ); REG_FUNCTION( GetTime ); }

Также вам необходимо определить эти функции в другом файле. Необходимо объявлять с приставкой lua. Например: REG_FUNCTION( Msg ) тогда объявление будет int luaMsg(lua_State *L).
extern "C" { #include <lua.h> #include <lauxlib.h> #include <lualib.h> } int luaClientPrintAll(lua_State *L) { int n = lua_gettop(L); /* number of arguments */ switch(n) { case 2: UTIL_ClientPrintAll( lua_tointeger(L,1), lua_tostring(L,2)); break; case 3: UTIL_ClientPrintAll( lua_tointeger(L,1), lua_tostring(L,2),lua_tostring(L,3)); break; case 4: UTIL_ClientPrintAll( lua_tointeger(L,1), lua_tostring(L,2),lua_tostring(L,3),lua_tostring(L,4)); break; case 5: UTIL_ClientPrintAll( lua_tointeger(L,1), lua_tostring(L,2),lua_tostring(L,3),lua_tostring(L,4),lua_tostring(L,5)); break; case 6: UTIL_ClientPrintAll( lua_tointeger(L,1), lua_tostring(L,2),lua_tostring(L,3),lua_tostring(L,4),lua_tostring(L,5),lua_tostring(L,6)); break; } return 0; } int luaMsg(lua_State *L) { Msg("%s\n",lua_tostring(L,1)); return 0; } int luaConMsg(lua_State *L) { return luaMsg(L); } int luaGetTime(lua_State *L) { lua_pushnumber( L, gpGlobals->curtime ); return 1; } GetLuaHandle

Эта функция говорит о том, инициализирован обработчик или нет.
MyLuaHandle *GetLuaHandle() { return g_LuaHandle; }

Использование Обработчика
В конструктор вашего обработчика нужно добавить регистрацию обработчика и установить переменную инициализации :
MyLuaHandle::MyLuaHandle() : LuaHandle() { g_LuaHandle = this; Register(); }
И наконец в файле правил игры нужно добавить конструктор нашего обработчика:

CGameRules::CGameRules() { //other stuff here if (!GetLuaHandle()) new MyLuaHandle(); }
Не забудьте добавить include с вашим обработчиком.

Под конец
В вашем проекте "Server" нужно добавить новое определение препроцессора (Свойства -> C++ -> Препроцессор -> Определения препроцессора). Также вы должны иметь скомпилированную библиотеку lua (в моём случае lua5.1.lib) и добавить её в зависимости компоновщика (Свойства -> Компоновщик -> Ввод -> Дополнительные зависимости )
Вы должны сделать это и для Debug и Release конфигурации.
Также поместите вашу lua библиотеку в папку "src/lib/public/".

Привязка вызовов к Lua
Это посложнее чем обработчик.
void CGELUAGamePlay::PostRoundBegin() { if (m_bLuaLoaded) { lua_getglobal(GetLua(), "RoundStart"); CallLUA(GetLua(), 0, 0, 0, "RoundStart"); } }
Внедрение Lua
Внедрение
Файлы
luamanager.cpp
luamanager.h
lua51.dll

Установка
Сценарии менеджера Lua, найденные в исходном коде Half-Life 2: Sandbox, были написаны обобщённо, чтобы их можно было добавить в любой мод.
Если вы работаете с исходным кодом мода, а не с плагином, указанные выше файлы должны быть помещены в src/game/shared
В файлах вашего проекта поместите и luamanager.cpp, и luamanager.h в Source Files filter
Скомпилируйте ваши двоичные файлы
Просто добавьте заранее скомпилированный файл lua51.dll в папку bin вашего мода, и теперь ваш мод интегрирован с Lua!
Обновление: Half-Life 2: Sandbox прошел несколько ревизий, значительно расширив его Lua API, включая множество классов и библиотек для его возможного использования с Source. В результате этих расширений вы заметите, что для использования этих классов вам необходимо добавить отдельные файлы классов .cpp и .h, которые перечислены в luamanager.cpp заголовками, соответствующими .cpp файлам.