Adding Lua: Difference between revisions
(New page: Adding Lua to Source SDK is quite easy and setting up multiple instances is even easier. First think you need to do is download the two source files and add them to your project: http://d...) |
m (Nesciuse moved page Adding Lua/en to Adding Lua without leaving a redirect: Move en subpage to basepage) |
||
(32 intermediate revisions by 16 users not shown) | |||
Line 1: | Line 1: | ||
{{LanguageBar}} | |||
Adding {{lua}} [http://en.wikipedia.org/wiki/Lua_%28programming_language%29 Lua] to {{Source|4}} is fairly straightforward, and you can set up multiple instances quite easily. | |||
{{Note|This tutorial is mainly aimed at people building Source SDK 2013 using Visual Studio 2013 on Windows. The steps may be quite different for Linux.}} | |||
== Prerequisites== | |||
< | |||
You'll need to download and add Lua to your SDK project: | |||
*Go to the [https://sourceforge.net/projects/luabinaries/files/ LuaBinaries Sourceforge] | |||
*Click the latest version > Windows Libraries > Static, and pick one that uses Win32. | |||
*Extract it, and add the header files that are located in it's "include" directory to your Server project. | |||
*Move the Lua Library (.lib) to somewhere in {{path|src/lib}} | |||
Add these two source files to your project: | |||
*[[LuaManager_CPP|ge_luamanager.cpp]] | |||
*[[LuaManager_H|ge_luamanager.h]] | |||
== Project Settings == | |||
Right click your Server project, and click Properties, then: | |||
*Add the Lua Library to linker dependencies. (Linker -> Input -> Additional Dependencies) | |||
*Add {{code|GE_LUA}} to your preprocessor definitions. (C/C++ -> Preprocessor -> Preprocessor Definitions) | |||
{{Note|Don't forget to do these for both the Release and Debug configurations.}} | |||
== Initializing Lua == | |||
In {{path|src/game/server/gameinterface.cpp}}, you'll need to include ge_luamanager.h: | |||
<source lang=cpp> | |||
#ifdef GE_LUA | |||
#include "ge_luamanager.h" | |||
#endif | |||
</source> | |||
Insert this into CServerGameDLL::DLLInit, right before the return true statement: | |||
<source lang=cpp> | |||
#ifdef GE_LUA | #ifdef GE_LUA | ||
// Start LUA | // Start LUA | ||
GELua()->InitDll(); | GELua()->InitDll(); | ||
#endif | #endif | ||
</ | </source> | ||
And in the DLLShutdown | And in the CServerGameDLL::DLLShutdown method, add this: | ||
< | |||
#ifdef GE_LUA | <source lang=cpp>#ifdef GE_LUA | ||
// Shutdown LUA, close all open gameplays | // Shutdown LUA, close all open gameplays | ||
GELua()->ShutdownDll(); | GELua()->ShutdownDll(); | ||
#endif | #endif | ||
</ | </source> | ||
== Writing Your Own Lua Instance == | |||
You will need to write your own class that inherits from LuaHandle and provides functionality for the methods: Init, Shutdown, RegFunctions and RegGlobals. | |||
<source lang=cpp> | |||
class MyLuaHandle : LuaHandle { | |||
public: | |||
MyLuaHandle(); | |||
~MyLuaHandle(); | |||
void Init(); | |||
void Shutdown(); | |||
void RegFunctions(); | |||
void RegGlobals(); | |||
} | |||
</source> | |||
=== Init === | |||
Init is called after Lua gets initialized, and this provides the best place to load your script. | |||
<source lang=cpp> | |||
< | void MyLuaHandle::Init() | ||
void MyLuaHandle:: | |||
{ | { | ||
const char* luaFile = "myLuaFile.lua"; | const char* luaFile = "myLuaFile.lua"; | ||
Line 61: | Line 94: | ||
if (error) | if (error) | ||
{ | { | ||
Warning("[LUA-ERR] %s\n", lua_tostring(GetLua(), -1)); | Warning("[LUA-ERR] %s\n", lua_tostring(GetLua(), -1)); | ||
lua_pop(GetLua(), 1); /* pop error message from the stack */ | lua_pop(GetLua(), 1); /* pop error message from the stack */ | ||
Warning("[LUA-ERR] One or more errors occured while loading | Warning("[LUA-ERR] One or more errors occured while loading lua script!\n"); | ||
return; | return; | ||
} | } | ||
CallLUA(GetLua(), 0, LUA_MULTRET, 0, | CallLUA(GetLua(), 0, LUA_MULTRET, 0, luaFile ); | ||
m_bLuaLoaded = true; | m_bLuaLoaded = true; | ||
} | } | ||
</ | </source> | ||
=== Shutdown === | === Shutdown === | ||
Shutdown handles the | Shutdown handles the shutting down of Lua, and should do things like call any shutdown functions in your script. | ||
<source lang=cpp> | |||
void MyLuaHandle::Shutdown() | |||
{ | |||
// Do stuff | |||
} | |||
</source> | |||
=== RegGlobals === | === RegGlobals === | ||
RegGlobals allows us to register global variables in | RegGlobals allows us to register global variables in Lua. | ||
< | |||
<source lang=cpp> | |||
void MyLuaHandle::RegGlobals() | void MyLuaHandle::RegGlobals() | ||
{ | { | ||
Line 101: | Line 141: | ||
LG_DEFINE_INT("HUD_PRINTCENTER",HUD_PRINTCENTER); | LG_DEFINE_INT("HUD_PRINTCENTER",HUD_PRINTCENTER); | ||
} | } | ||
</ | </source> | ||
{{Note|The macros provided allow for different types as well. LG_DEFINE_INT for int, LG_DEFINE_STRING for string and LG_DEFINE_BOOL for boolean.}} | |||
=== RegFunctions === | === RegFunctions === | ||
RegFunctions is where you put any and all C functions that you want to expose to Lua. | |||
< | |||
<source lang=cpp> | |||
void MyLuaHandle::RegFunctions() | void MyLuaHandle::RegFunctions() | ||
{ | { | ||
Line 115: | Line 157: | ||
REG_FUNCTION( GetTime ); | REG_FUNCTION( GetTime ); | ||
} | } | ||
</ | </source> | ||
< | <source lang=cpp> | ||
extern "C" | extern "C" | ||
{ | { | ||
Line 166: | Line 207: | ||
return 1; | return 1; | ||
} | } | ||
</ | </source> | ||
Note | {{Note|All functions that are registered in "RegFunctions" must have their ''actual function definition names'' prefixed with "lua".}} | ||
== Creating The Instance == | == Creating The Instance == | ||
You'll need to add a function allowing global access to your Lua instance. Add something like this: | |||
< | <source lang=cpp> | ||
LuaHandle* g_LuaHandle = NULL; | |||
LuaHandle* GetLuaHandle() | |||
{ | { | ||
return g_LuaHandle; | return g_LuaHandle; | ||
} | } | ||
</ | </source> | ||
In the constructor of your LuaHandle, add a call to set the global access pointer to your instance when you make a new one: | |||
< | <source lang=cpp> | ||
MyLuaHandle::MyLuaHandle() | MyLuaHandle::MyLuaHandle() : LuaHandle() | ||
{ | { | ||
g_LuaHandle = this; | |||
Register(); | Register(); | ||
} | } | ||
</ | </source> | ||
{{Note|Ensure that the Register function is called or else it will not work!}} | |||
Finally, in your game rules constructor, instantiate a new LuaHandle: | |||
<source lang=cpp> | |||
CGameRules::CGameRules() | |||
{ | |||
//other stuff here | |||
// Start our LUA GamePlay Engine | // Start our LUA GamePlay Engine | ||
if (!GetLuaHandle()) | if (!GetLuaHandle()) | ||
new MyLuaHandle(); | new MyLuaHandle(); | ||
</ | } | ||
</source> | |||
== | {{Example|CHalfLife2::CHalfLife2() in src/game/shared/hl2/hl2_gamerules.cpp}} | ||
== Calling Lua functions from C == | |||
You may need to consult the [https://www.lua.org/pil/25.2.html Lua documents] for this, but here is an example on how you might do something like it: | |||
Lua: | |||
<source lang=lua> | |||
function RoundStart() | |||
Msg("The round has started!") | |||
end | |||
</source> | |||
C: | |||
<source lang=cpp> | |||
void PostRoundBegin() | |||
{ | |||
LuaHandle* lh = GetLuaHandle(); | |||
if (lh && lh->m_bLuaLoaded) | |||
{ | |||
lua_getglobal(lh->GetLua(), "RoundStart"); | |||
CallLUA(lh->GetLua(), 0, 0, 0, "RoundStart"); | |||
} | |||
} | |||
</source> | |||
[[Category:Source]] | |||
[[Category:C++]] | |||
[[Category:Scripting]] | |||
[[Category:Free source code]] |
Latest revision as of 03:59, 12 July 2024
Adding Lua to
Source is fairly straightforward, and you can set up multiple instances quite easily.

Prerequisites
You'll need to download and add Lua to your SDK project:
- Go to the LuaBinaries Sourceforge
- Click the latest version > Windows Libraries > Static, and pick one that uses Win32.
- Extract it, and add the header files that are located in it's "include" directory to your Server project.
- Move the Lua Library (.lib) to somewhere in
src/lib
Add these two source files to your project:
Project Settings
Right click your Server project, and click Properties, then:
- Add the Lua Library to linker dependencies. (Linker -> Input -> Additional Dependencies)
- Add GE_LUA to your preprocessor definitions. (C/C++ -> Preprocessor -> Preprocessor Definitions)

Initializing Lua
In src/game/server/gameinterface.cpp
, you'll need to include ge_luamanager.h:
#ifdef GE_LUA
#include "ge_luamanager.h"
#endif
Insert this into CServerGameDLL::DLLInit, right before the return true statement:
#ifdef GE_LUA
// Start LUA
GELua()->InitDll();
#endif
And in the CServerGameDLL::DLLShutdown method, add this:
#ifdef GE_LUA
// Shutdown LUA, close all open gameplays
GELua()->ShutdownDll();
#endif
Writing Your Own Lua Instance
You will need to write your own class that inherits from LuaHandle and provides functionality for the methods: Init, Shutdown, RegFunctions and RegGlobals.
class MyLuaHandle : LuaHandle {
public:
MyLuaHandle();
~MyLuaHandle();
void Init();
void Shutdown();
void RegFunctions();
void RegGlobals();
}
Init
Init is called after Lua gets initialized, and this provides the best place to load your script.
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 handles the shutting down of Lua, and should do things like call any shutdown functions in your script.
void MyLuaHandle::Shutdown()
{
// Do stuff
}
RegGlobals
RegGlobals allows us to register global variables in 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);
}

RegFunctions
RegFunctions is where you put any and all C functions that you want to expose to Lua.
void MyLuaHandle::RegFunctions()
{
REG_FUNCTION( Msg );
REG_FUNCTION( ConMsg );
REG_FUNCTION( ClientPrintAll );
REG_FUNCTION( GetTime );
}
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;
}

Creating The Instance
You'll need to add a function allowing global access to your Lua instance. Add something like this:
LuaHandle* g_LuaHandle = NULL;
LuaHandle* GetLuaHandle()
{
return g_LuaHandle;
}
In the constructor of your LuaHandle, add a call to set the global access pointer to your instance when you make a new one:
MyLuaHandle::MyLuaHandle() : LuaHandle()
{
g_LuaHandle = this;
Register();
}

Finally, in your game rules constructor, instantiate a new LuaHandle:
CGameRules::CGameRules()
{
//other stuff here
// Start our LUA GamePlay Engine
if (!GetLuaHandle())
new MyLuaHandle();
}

Calling Lua functions from C
You may need to consult the Lua documents for this, but here is an example on how you might do something like it:
Lua:
function RoundStart()
Msg("The round has started!")
end
C:
void PostRoundBegin()
{
LuaHandle* lh = GetLuaHandle();
if (lh && lh->m_bLuaLoaded)
{
lua_getglobal(lh->GetLua(), "RoundStart");
CallLUA(lh->GetLua(), 0, 0, 0, "RoundStart");
}
}