Adding Lua: Difference between revisions
m (linked the german translation) |
m (Nesciuse moved page Adding Lua/en to Adding Lua without leaving a redirect: Move en subpage to basepage) |
||
(14 intermediate revisions by 10 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: | 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> | <source lang=cpp> | ||
Line 22: | Line 43: | ||
</source> | </source> | ||
And in the DLLShutdown | And in the CServerGameDLL::DLLShutdown method, add this: | ||
<source lang=cpp> | <source lang=cpp>#ifdef GE_LUA | ||
#ifdef GE_LUA | |||
// Shutdown LUA, close all open gameplays | // Shutdown LUA, close all open gameplays | ||
GELua()->ShutdownDll(); | GELua()->ShutdownDll(); | ||
Line 31: | Line 51: | ||
</source> | </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> | <source lang=cpp> | ||
class MyLuaHandle : LuaHandle { | |||
public: | |||
MyLuaHandle(); | |||
~MyLuaHandle(); | |||
void Init(); | |||
void Shutdown(); | |||
void RegFunctions(); | |||
void RegGlobals(); | |||
} | |||
</source> | </source> | ||
=== Init === | === Init === | ||
Init | Init is called after Lua gets initialized, and this provides the best place to load your script. | ||
<source lang=cpp> | <source lang=cpp> | ||
Line 83: | Line 106: | ||
=== Shutdown === | === Shutdown === | ||
Shutdown handles the shutting down of Lua and should do things | 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> | <source lang=cpp> | ||
Line 113: | Line 143: | ||
</source> | </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> | <source lang=cpp> | ||
Line 128: | Line 158: | ||
} | } | ||
</source> | </source> | ||
<source lang=cpp> | <source lang=cpp> | ||
Line 181: | Line 209: | ||
</source> | </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> | <source lang=cpp> | ||
LuaHandle* g_LuaHandle = NULL; | |||
LuaHandle* GetLuaHandle() | |||
{ | { | ||
return g_LuaHandle; | return g_LuaHandle; | ||
Line 195: | Line 223: | ||
</source> | </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> | <source lang=cpp> | ||
MyLuaHandle::MyLuaHandle() : LuaHandle() | MyLuaHandle::MyLuaHandle() : LuaHandle() | ||
Line 204: | Line 232: | ||
</source> | </source> | ||
Note | {{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> | <source lang=cpp> | ||
CGameRules::CGameRules() | CGameRules::CGameRules() | ||
Line 219: | Line 247: | ||
</source> | </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> | <source lang=cpp> | ||
void | void PostRoundBegin() | ||
{ | { | ||
if (m_bLuaLoaded) | LuaHandle* lh = GetLuaHandle(); | ||
if (lh && lh->m_bLuaLoaded) | |||
{ | { | ||
lua_getglobal(GetLua(), "RoundStart"); | lua_getglobal(lh->GetLua(), "RoundStart"); | ||
CallLUA(GetLua(), 0, 0, 0, "RoundStart"); | CallLUA(lh->GetLua(), 0, 0, 0, "RoundStart"); | ||
} | } | ||
} | } | ||
</source> | </source> | ||
[[Category:Source]] | |||
[[Category:C++]] | |||
[[Category:Scripting]] | |||
[[Category:Free source code]] | [[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");
}
}