Skip to content

Commit 60a229f

Browse files
authored
OnMapChangeHook (#66)
* holylib: OnMapChangeHook added * MapChangeHook: fixed issues * readme: added OnMapChange hook entry * OnMapChange: fixed formatting * OnMapHook: fixed more formatting issues darn * OnMapChange: using V_strncpy instead of strncpy
1 parent 61089fe commit 60a229f

File tree

6 files changed

+102
-2
lines changed

6 files changed

+102
-2
lines changed

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,21 @@ Called when a gets off a ladder -> Direct bind to `CFuncLadder::PlayerGotOff`<br
376376
Called when the movetype is about to change.<br>
377377
If you call `Entity:SetMoveType` on the current entity inside this hook, it would have no effect.<br>
378378

379+
#### HolyLib:OnMapChange(string levelName, string landmarkName)
380+
> [!NOTE]
381+
> This currently works only `LINUX32`
382+
383+
Called right before a level transition occurs, this hook allows you to react to map changes initiated via changelevel.
384+
- string levelName — The name of the level we are changing to.
385+
- string levelLandmark — The name of the landmark (if any) otherwise, an empty string.
386+
Example usage:
387+
```lua
388+
hook.Add("HolyLib:OnMapChange", "HelloThere", function(levelName, landmarkName)
389+
print("Next level name: " .. levelName)
390+
print("Using landmark: " .. landmarkName)
391+
end)
392+
```
393+
379394
## gameevent
380395
This module contains additional functions for the gameevent library.<br>
381396
With the Add/Get/RemoveClient* functions you can control the gameevents that are networked to a client which can be useful.<br>
@@ -4522,4 +4537,4 @@ PrintTable(fileTable)
45224537
## Usage with vphysics jolt
45234538
Currently there might be bugs when combining holylib with VPhysics-Jolt.<br>
45244539
This mainly affects the `physenv` module and most other modules should be completely fine.<br>
4525-
Only VPhysics-Jolt builds from https://github.com/RaphaelIT7/VPhysics-Jolt will be suppored for now due to holylib requiring extented functionality.<br>
4540+
Only VPhysics-Jolt builds from https://github.com/RaphaelIT7/VPhysics-Jolt will be suppored for now due to holylib requiring extented functionality.<br>
Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
include( "gmod_tests/sh_init.lua" )
22

3+
hook.Add("HolyLib:OnMapChange", "HookOnMapChangeTest", function(levelName, levelLandmark)
4+
local currentMap = game.GetMap()
5+
6+
if not file.Exists("HookOnMapChangeData.txt", "DATA") then
7+
file.Write("HookOnMapChangeData.txt", currentMap .. "\n" .. levelName .. "\n" .. levelLandmark)
8+
end
9+
end)
10+
311
-- We change the level once and run everything again as in rare cases a crash might only ocurr after a map change.
412
hook.Add("GLuaTest_Finished", "ChangeLevel", function()
513
if not file.Exists("waschanged.txt", "DATA") then
614
file.Write("waschanged.txt", "yes")
715

816
RunConsoleCommand("changelevel", game.GetMap())
917
end
10-
end)
18+
end)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
return {
2+
groupName = "HolyLib:OnMapChange",
3+
cases = {
4+
{
5+
name = "Hook is called with correct map name",
6+
when = file.Exists("HookOnMapChangeData.txt", "DATA"),
7+
func = function(state)
8+
local data = file.Read("HookOnMapChangeData.txt", "DATA")
9+
expect(data).to.exist()
10+
11+
local lines = string.Explode("\n", data)
12+
expect(#lines).to.beGreaterThan(1)
13+
14+
local oldLevel = lines[1]
15+
local levelName = lines[2]
16+
17+
expect(levelName).to.beA("string")
18+
expect(levelName).to.equal(oldLevel)
19+
end,
20+
},
21+
}
22+
}

source/modules/holylib.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "sourcesdk/baseclient.h"
1313
#include "hl2/hl2_player.h"
1414
#include "unordered_set"
15+
#include "host_state.h"
1516

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

378+
static char pLevelName[256], pLandmarkName[256] = {0};
379+
static Detouring::Hook detour_CHostState_State_ChangeLevelMP;
380+
static void hook_CHostState_State_ChangeLevelMP(const char* levelName, const char* landmarkName)
381+
{
382+
if (levelName)
383+
{
384+
V_strncpy(pLevelName, levelName, sizeof(pLevelName));
385+
}
386+
387+
if (landmarkName)
388+
{
389+
V_strncpy(pLandmarkName, landmarkName, sizeof(pLandmarkName));
390+
} else {
391+
pLandmarkName[0] = '\0';
392+
}
393+
394+
detour_CHostState_State_ChangeLevelMP.GetTrampoline<Symbols::CHostState_State_ChangeLevelMP>()(levelName, landmarkName);
395+
}
396+
397+
void CHolyLibModule::LevelShutdown()
398+
{
399+
if (*pLevelName == '\0') {
400+
return;
401+
}
402+
403+
if (Lua::PushHook("HolyLib:OnMapChange"))
404+
{
405+
g_Lua->PushString(pLevelName);
406+
g_Lua->PushString(pLandmarkName);
407+
g_Lua->CallFunctionProtected(3, 0, true);
408+
}
409+
410+
pLevelName[0] = '\0';
411+
pLandmarkName[0] = '\0';
412+
}
413+
376414
void CHolyLibModule::LuaInit(GarrysMod::Lua::ILuaInterface* pLua, bool bServerInit)
377415
{
378416
if (!bServerInit)
@@ -455,6 +493,12 @@ void CHolyLibModule::InitDetour(bool bPreServer)
455493
(void*)hook_GetGModServerTags, m_pID
456494
);
457495

496+
Detour::Create(
497+
&detour_CHostState_State_ChangeLevelMP, "CHostState_State_ChangeLevelMP",
498+
engine_loader.GetModule(), Symbols::CHostState_State_ChangeLevelMPSym,
499+
(void*)hook_CHostState_State_ChangeLevelMP, m_pID
500+
);
501+
458502
func_CBaseAnimating_InvalidateBoneCache = (Symbols::CBaseAnimating_InvalidateBoneCache)Detour::GetFunction(server_loader.GetModule(), Symbols::CBaseAnimating_InvalidateBoneCacheSym);
459503
Detour::CheckFunction((void*)func_CBaseAnimating_InvalidateBoneCache, "CBaseAnimating::InvalidateBoneCache");
460504

source/symbols.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,13 @@ namespace Symbols
131131
NULL_SIGNATURE, // ToDo
132132
};
133133

134+
const std::vector<Symbol> CHostState_State_ChangeLevelMPSym = {
135+
Symbol::FromName("_Z23HostState_ChangeLevelMPPKcS0_"),
136+
NULL_SIGNATURE,
137+
NULL_SIGNATURE,
138+
NULL_SIGNATURE,
139+
};
140+
134141
//---------------------------------------------------------------------------------
135142
// Purpose: gameevent Symbols
136143
//---------------------------------------------------------------------------------

source/symbols.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ struct edict_t;
6363
struct PackWork_t;
6464
class CBaseViewModel;
6565
class CBaseCombatCharacter;
66+
class CHostState;
6667

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

176+
typedef void (GMCOMMON_CALLING_CONVENTION* CHostState_State_ChangeLevelMP)(const char* levelName, const char* LandmarkName);
177+
extern const std::vector<Symbol> CHostState_State_ChangeLevelMPSym;
178+
175179
//---------------------------------------------------------------------------------
176180
// Purpose: gameevent Symbols
177181
//---------------------------------------------------------------------------------

0 commit comments

Comments
 (0)