Adding Lua: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
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:
{{otherlang2
{{LanguageBar}}
|de=Adding_Lua:de
}}
Adding [http://en.wikipedia.org/wiki/Lua_%28programming_language%29 Lua] to Source is quite easy, and setting up multiple instances is even easier.


== Source ==
 
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]]


*[[LuaManager_CPP]]
== Project Settings ==
*[[LuaManager_H]]
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)


== Init Lua  ==
{{Note|Don't forget to do these for both the Release and Debug configurations.}}


Once that is done you will need to make sure your game code calls the init and shutdown functions. In gameinterface.cpp add this into DLLInit before the return true statement (around line 557):
== 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 function add this:
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>


Also make sure you include the manager header file up the top:
== 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>
#ifdef GE_LUA
class MyLuaHandle : LuaHandle {
#include "ge_luamanager.h"
public:
#endif
MyLuaHandle();
~MyLuaHandle();
void Init();
void Shutdown();
void RegFunctions();
void RegGlobals();
}
</source>
</source>
== Making Your own Lua Instance ==
Now this is the fun part, making your own lua instance. You will need to make your own class that inherits from LuaHandle and provides functionality for Init, Shutdown, RegFunctions and RegGlobals.


=== Init ===
=== Init ===


Init gets called after Lua gets init and thus provides the best place to load your script.
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 in here like call shutdown functions in your script.
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 lua.
RegGlobals allows us to register global variables in Lua.


<source lang=cpp>
<source lang=cpp>
Line 113: Line 143:
</source>
</source>


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 ===


Like Globals, RegFunctions is where you put all the c functions you want to expose to Lua.
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>
Again this has been shorten using a macro. And thus you will need to define this functions in another file.


<source lang=cpp>
<source lang=cpp>
Line 181: Line 209:
</source>
</source>


Note: The function name must be the same as the one used in the macro with lua infront.
{{Note|All functions that are registered in "RegFunctions" must have their ''actual function definition names'' prefixed with "lua".}}


== Creating The Instance ==
== Creating The Instance ==


On the top of your new LuaHandle class you need to add a function allowing global access to your Lua instance. Add something like this:
You'll need to add a function allowing global access to your Lua instance. Add something like this:


<source lang=cpp>
<source lang=cpp>
MyLuaHandle* g_LuaHandle = NULL;
LuaHandle* g_LuaHandle = NULL;
MyLuaHandle *GetLuaHandle()
LuaHandle* GetLuaHandle()
{
{
return g_LuaHandle;
return g_LuaHandle;
Line 195: Line 223:
</source>
</source>


And in your constructor of your LuaHandle add a call to set the instance when you make a new one:
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: Its important to have Register here other wise it wont work!
{{Note|Ensure that the Register function is called or else it will not work!}}


And finally in your game rules constructor make a new LuaHandle:
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>


== Project Settings ==
{{Example|CHalfLife2::CHalfLife2() in src/game/shared/hl2/hl2_gamerules.cpp}}
 
In the server project settings you need to add GE_LUA to the preprocesser options (Configure properties -> c++ -> Preprocesser -> Preprocesser defines).
Also you will need to add the lua lib (should be the same name as the lib file, mines lua5.1.lib) to the linker settings (Configure properties -> Linker -> Input -> Additional Dependencies )


Note: Make sure you do this for both release and debug.
== Calling Lua functions from C ==


Now place the lua lib into the src/lib folder. And you should be able to compile and now have a working Lua.
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:


== Creating Calls from c into Lua ==
Lua:
 
<source lang=lua>
This is a bit harder and you will need to consult the lua documents for this but here is an example on how ges does it:
function RoundStart()
Msg("The round has started!")
end
</source>


C:
<source lang=cpp>
<source lang=cpp>
void CGELUAGamePlay::PostRoundBegin()
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:Programming]]
[[Category:Free source code]]
[[Category:Free source code]]
[[Category:Scripting]]

Latest revision as of 03:59, 12 July 2024

English (en)Deutsch (de)Русский (ru)中文 (zh)Translate (Translate)


Adding Lua Lua to Source Source is fairly straightforward, and you can set up multiple instances quite easily.

Note.pngNote: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_LUA to your preprocessor definitions. (C/C++ -> Preprocessor -> Preprocessor Definitions)
Note.pngNote: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.pngNote: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.pngNote: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.pngNote: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();
}
PlacementTip.pngExample:CHalfLife2::CHalfLife2() in src/game/shared/hl2/hl2_gamerules.cpp

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");
	}
}