Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,21 @@ Called when a gets off a ladder -> Direct bind to `CFuncLadder::PlayerGotOff`<br
Called when the movetype is about to change.<br>
If you call `Entity:SetMoveType` on the current entity inside this hook, it would have no effect.<br>

#### HolyLib:OnMapChange(string levelName, string landmarkName)
> [!NOTE]
> This currently works only `LINUX32`

Called right before a level transition occurs, this hook allows you to react to map changes initiated via changelevel.
- string levelName — The name of the level we are changing to.
- string levelLandmark — The name of the landmark (if any) otherwise, an empty string.
Example usage:
```lua
hook.Add("HolyLib:OnMapChange", "HelloThere", function(levelName, landmarkName)
print("Next level name: " .. levelName)
print("Using landmark: " .. landmarkName)
end)
```

## gameevent
This module contains additional functions for the gameevent library.<br>
With the Add/Get/RemoveClient* functions you can control the gameevents that are networked to a client which can be useful.<br>
Expand Down Expand Up @@ -4514,4 +4529,4 @@ PrintTable(fileTable)
## Usage with vphysics jolt
Currently there might be bugs when combining holylib with VPhysics-Jolt.<br>
This mainly affects the `physenv` module and most other modules should be completely fine.<br>
Only VPhysics-Jolt builds from https://github.com/RaphaelIT7/VPhysics-Jolt will be suppored for now due to holylib requiring extented functionality.<br>
Only VPhysics-Jolt builds from https://github.com/RaphaelIT7/VPhysics-Jolt will be suppored for now due to holylib requiring extented functionality.<br>
10 changes: 9 additions & 1 deletion gluatests/lua/autorun/gmod_tests_init.lua
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
include( "gmod_tests/sh_init.lua" )

hook.Add("HolyLib:OnMapChange", "HookOnMapChangeTest", function(levelName, levelLandmark)
local currentMap = game.GetMap()

if not file.Exists("HookOnMapChangeData.txt", "DATA") then
file.Write("HookOnMapChangeData.txt", currentMap .. "\n" .. levelName .. "\n" .. levelLandmark)
end
end)

-- We change the level once and run everything again as in rare cases a crash might only ocurr after a map change.
hook.Add("GLuaTest_Finished", "ChangeLevel", function()
if not file.Exists("waschanged.txt", "DATA") then
file.Write("waschanged.txt", "yes")

RunConsoleCommand("changelevel", game.GetMap())
end
end)
end)
22 changes: 22 additions & 0 deletions gluatests/lua/tests/modules/holylib/OnMapChange.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
return {
groupName = "HolyLib:OnMapChange",
cases = {
{
name = "Hook is called with correct map name",
when = file.Exists("HookOnMapChangeData.txt", "DATA"),
func = function(state)
local data = file.Read("HookOnMapChangeData.txt", "DATA")
expect(data).to.exist()

local lines = string.Explode("\n", data)
expect(#lines).to.beGreaterThan(1)

local oldLevel = lines[1]
local levelName = lines[2]

expect(levelName).to.beA("string")
expect(levelName).to.equal(oldLevel)
end,
},
}
}
44 changes: 44 additions & 0 deletions source/modules/holylib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "sourcesdk/baseclient.h"
#include "hl2/hl2_player.h"
#include "unordered_set"
#include "host_state.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
Expand All @@ -22,6 +23,7 @@ class CHolyLibModule : public IModule
virtual void LuaInit(GarrysMod::Lua::ILuaInterface* pLua, bool bServerInit) OVERRIDE;
virtual void LuaShutdown(GarrysMod::Lua::ILuaInterface* pLua) OVERRIDE;
virtual void InitDetour(bool bPreServer) OVERRIDE;
virtual void LevelShutdown() OVERRIDE;
virtual const char* Name() { return "holylib"; };
virtual int Compatibility() { return LINUX32 | LINUX64; };
virtual bool SupportsMultipleLuaStates() { return true; };
Expand Down Expand Up @@ -373,6 +375,42 @@ LUA_FUNCTION_STATIC(ReceiveClientMessage)
return 0;
}

static char pLevelName[256], pLandmarkName[256] = {0};
static Detouring::Hook detour_CHostState_State_ChangeLevelMP;
static void hook_CHostState_State_ChangeLevelMP(const char* levelName, const char* landmarkName)
{
if (levelName)
{
V_strncpy(pLevelName, levelName, sizeof(pLevelName));
}

if (landmarkName)
{
V_strncpy(pLandmarkName, landmarkName, sizeof(pLandmarkName));
} else {
pLandmarkName[0] = '\0';
}

detour_CHostState_State_ChangeLevelMP.GetTrampoline<Symbols::CHostState_State_ChangeLevelMP>()(levelName, landmarkName);
}

void CHolyLibModule::LevelShutdown()
{
if (*pLevelName == '\0') {
return;
}

if (Lua::PushHook("HolyLib:OnMapChange"))
{
g_Lua->PushString(pLevelName);
g_Lua->PushString(pLandmarkName);
g_Lua->CallFunctionProtected(3, 0, true);
}

pLevelName[0] = '\0';
pLandmarkName[0] = '\0';
}

void CHolyLibModule::LuaInit(GarrysMod::Lua::ILuaInterface* pLua, bool bServerInit)
{
if (!bServerInit)
Expand Down Expand Up @@ -455,6 +493,12 @@ void CHolyLibModule::InitDetour(bool bPreServer)
(void*)hook_GetGModServerTags, m_pID
);

Detour::Create(
&detour_CHostState_State_ChangeLevelMP, "CHostState_State_ChangeLevelMP",
engine_loader.GetModule(), Symbols::CHostState_State_ChangeLevelMPSym,
(void*)hook_CHostState_State_ChangeLevelMP, m_pID
);

func_CBaseAnimating_InvalidateBoneCache = (Symbols::CBaseAnimating_InvalidateBoneCache)Detour::GetFunction(server_loader.GetModule(), Symbols::CBaseAnimating_InvalidateBoneCacheSym);
Detour::CheckFunction((void*)func_CBaseAnimating_InvalidateBoneCache, "CBaseAnimating::InvalidateBoneCache");

Expand Down
7 changes: 7 additions & 0 deletions source/symbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,13 @@ namespace Symbols
NULL_SIGNATURE, // ToDo
};

const std::vector<Symbol> CHostState_State_ChangeLevelMPSym = {
Symbol::FromName("_Z23HostState_ChangeLevelMPPKcS0_"),
NULL_SIGNATURE,
NULL_SIGNATURE,
NULL_SIGNATURE,
};

//---------------------------------------------------------------------------------
// Purpose: gameevent Symbols
//---------------------------------------------------------------------------------
Expand Down
4 changes: 4 additions & 0 deletions source/symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ struct edict_t;
struct PackWork_t;
class CBaseViewModel;
class CBaseCombatCharacter;
class CHostState;

class CGameTrace;
typedef CGameTrace trace_t;
Expand Down Expand Up @@ -172,6 +173,9 @@ namespace Symbols
typedef void (GMCOMMON_CALLING_CONVENTION* CBaseEntity_SetMoveType)(void* ent, int, int);
extern const std::vector<Symbol> CBaseEntity_SetMoveTypeSym;

typedef void (GMCOMMON_CALLING_CONVENTION* CHostState_State_ChangeLevelMP)(const char* levelName, const char* LandmarkName);
extern const std::vector<Symbol> CHostState_State_ChangeLevelMPSym;

//---------------------------------------------------------------------------------
// Purpose: gameevent Symbols
//---------------------------------------------------------------------------------
Expand Down
Loading