Adding Lua: Difference between revisions
| mNo edit summary | m (Nesciuse moved page Adding Lua/en to Adding Lua without leaving a redirect: Move en subpage to basepage) | ||
| (6 intermediate revisions by 5 users not shown) | |||
| Line 1: | Line 1: | ||
| {{ | {{LanguageBar}} | ||
| Adding {{lua}} [http://en.wikipedia.org/wiki/Lua_%28programming_language%29 Lua] to {{Source|4}} is  | 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 23: | 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 32: | 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 84: | 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 114: | 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 129: | Line 158: | ||
| } | } | ||
| </source> | </source> | ||
| <source lang=cpp> | <source lang=cpp> | ||
| Line 182: | 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 196: | 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 205: | 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 220: | 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]] | |||
Latest revision as of 03:59, 12 July 2024
Adding  Lua to
 Lua to  Source is fairly straightforward, and you can set up multiple instances quite easily.
 Source 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.
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 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_LUAto your preprocessor definitions. (C/C++ -> Preprocessor -> Preprocessor Definitions)
 Note:Don't forget to do these for both the Release and Debug configurations.
Note:Don't forget to do these for both the Release and Debug configurations.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);
}
 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.
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 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;
}
 Note:All functions that are registered in "RegFunctions" must have their actual function definition names prefixed with "lua".
Note:All functions that are registered in "RegFunctions" must have their actual function definition names prefixed with "lua".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();
}
 Note:Ensure that the Register function is called or else it will not work!
Note:Ensure that the Register function is called or else it will not work!Finally, in your game rules constructor, instantiate a new LuaHandle:
CGameRules::CGameRules()
{
	//other stuff here
	// Start our LUA GamePlay Engine
	if (!GetLuaHandle())
		new MyLuaHandle();
}
 Example:CHalfLife2::CHalfLife2() in src/game/shared/hl2/hl2_gamerules.cpp
Example:CHalfLife2::CHalfLife2() in src/game/shared/hl2/hl2_gamerules.cppCalling 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");
	}
}

























