Adding Lua
Adding Lua to Source is fairly straightforward, and you can set up multiple instances quite easily.
Contents
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");
}
}