diff --git a/addons/sourcemod/gamedata/l4d_consistent_escaperoute.txt b/addons/sourcemod/gamedata/l4d_consistent_escaperoute.txt index 123e28f90..49300eee9 100644 --- a/addons/sourcemod/gamedata/l4d_consistent_escaperoute.txt +++ b/addons/sourcemod/gamedata/l4d_consistent_escaperoute.txt @@ -38,6 +38,30 @@ } } } + + // Checkpoint::GetSpawnPosition(Vector *, QAngle *, TerrorNavArea **)const + "l4d_consistent_escaperoute::Checkpoint::GetSpawnPosition" + { + "signature" "Checkpoint::GetSpawnPosition" + "callconv" "thiscall" + "return" "bool" + "this" "address" + "arguments" + { + "Vector *" + { + "type" "vectorptr" + } + "QAngle *" + { + "type" "vectorptr" + } + "TerrorNavArea **" + { + "type" "objectptr" + } + } + } } } @@ -97,6 +121,22 @@ "windows" "\x83\xEC\x34\x8B\x0D\x2A\x2A\x2A\x2A\x53" /* 83 EC 34 8B 0D ? ? ? ? 53 */ } + + "Checkpoint::GetSpawnPosition" + { + "library" "server" + "linux" "@_ZNK10Checkpoint16GetSpawnPositionEP6VectorP6QAnglePP13TerrorNavArea" + "windows" "\x55\x8B\xEC\x83\xE4\xF0\x81\xEC\x14\x01\x00\x00\x8B\x45\x08\x53" + /* 55 8B EC 83 E4 F0 81 EC 14 01 00 00 8B 45 08 53 */ + } + + "Checkpoint::GetLargestArea" + { + "library" "server" + "linux" "@_ZNK10Checkpoint14GetLargestAreaEv" + "windows" "\x51\xD9\xEE\x8B\xC1" + /* 51 D9 EE 8B C1 */ + } } } @@ -156,6 +196,22 @@ "windows" "\x55\x8B\xEC\x83\xEC\x4C\x53\x33\xDB" /* 55 8B EC 83 EC 4C 53 33 DB */ } + + "Checkpoint::GetSpawnPosition" + { + "library" "server" + "linux" "@_ZNK10Checkpoint16GetSpawnPositionEP6VectorP6QAnglePP13TerrorNavArea" + "windows" "\x53\x8B\xDC\x83\xEC\x08\x83\xE4\xF0\x83\xC4\x04\x55\x8B\x6B\x04\x89\x6C\x24\x04\x8B\xEC\x81\xEC\x28\x01\x00\x00\xA1\x2A\x2A\x2A\x2A\x33\xC5\x89\x45\xFC\x8B\x43\x08\x56" + /* 53 8B DC 83 EC 08 83 E4 F0 83 C4 04 55 8B 6B 04 89 6C 24 04 8B EC 81 EC 28 01 00 00 A1 ? ? ? ? 33 C5 89 45 FC 8B 43 08 56 */ + } + + "Checkpoint::GetLargestArea" + { + "library" "server" + "linux" "@_ZNK10Checkpoint14GetLargestAreaEv" + "windows" "\x55\x8B\xEC\x51\x0F\x57\xC0\x8B\xD1" + /* 55 8B EC 51 0F 57 C0 8B D1 */ + } } } } diff --git a/addons/sourcemod/scripting/l4d_consistent_escaperoute.sp b/addons/sourcemod/scripting/l4d_consistent_escaperoute.sp index f5ac8cf5b..4ebf68866 100644 --- a/addons/sourcemod/scripting/l4d_consistent_escaperoute.sp +++ b/addons/sourcemod/scripting/l4d_consistent_escaperoute.sp @@ -2,12 +2,14 @@ #pragma newdecls required #define DEBUG 1 -#define PLUGIN_VERSION "2.0" +#define PLUGIN_VERSION "2.1" #define PLUGIN_TAG "l4d_consistent_escaperoute" #include #include #include +#include +#include #if DEBUG #include @@ -22,6 +24,13 @@ public Plugin myinfo = url = "https://github.com/Target5150/MoYu_Server_Stupid_Plugins" }; +enum struct SDKCallParamsWrapper { + SDKType type; + SDKPassMethod pass; + int decflags; + int encflags; +} + methodmap GameDataWrapper < GameData { public GameDataWrapper(const char[] file) { GameData gd = new GameData(file); @@ -60,6 +69,27 @@ methodmap GameDataWrapper < GameData { SetFailState("Failed to post-detour \"%s\"", name); return hSetup; } + public Handle CreateSDKCallOrFail( + SDKCallType type, + SDKFuncConfSource src, + const char[] name, + const SDKCallParamsWrapper[] params = {}, + int numParams = 0, + bool hasReturnValue = false, + const SDKCallParamsWrapper ret = {}) { + static const char k_sSDKFuncConfSource[SDKFuncConfSource][] = { "offset", "signature", "address" }; + Handle result; + StartPrepSDKCall(type); + if (!PrepSDKCall_SetFromConf(this, src, name)) + SetFailState("Missing %s \"%s\"", k_sSDKFuncConfSource[src], name); + for (int i = 0; i < numParams; ++i) + PrepSDKCall_AddParameter(params[i].type, params[i].pass, params[i].decflags, params[i].encflags); + if (hasReturnValue) + PrepSDKCall_SetReturnInfo(ret.type, ret.pass, ret.decflags, ret.encflags); + if (!(result = EndPrepSDKCall())) + SetFailState("Failed to prep sdkcall \"%s\"", name); + return result; + } } methodmap Address {} @@ -98,6 +128,7 @@ methodmap TerrorNavArea < Address #endif MemoryPatch g_patch_SkipSpawnPosIdx; +Handle g_call_Checkpoint_GetLargestArea; public void OnPluginStart() { @@ -110,6 +141,12 @@ public void OnPluginStart() g_patch_SkipSpawnPosIdx = gd.CreatePatchOrFail("skip_spawn_position_idx", false); + SDKCallParamsWrapper params[] = { + { SDKType_PlainOldData, SDKPass_Plain }, + }; + g_call_Checkpoint_GetLargestArea = gd.CreateSDKCallOrFail(SDKCall_Raw, SDKConf_Signature, "Checkpoint::GetLargestArea", _, 0, true, params[0]); + + delete gd.CreateDetourOrFail("l4d_consistent_escaperoute::Checkpoint::GetSpawnPosition", _, DTR_Checkpoint_GetSpawnPosition_Post); delete gd.CreateDetourOrFail("l4d_consistent_escaperoute::GetPlayerSpawnPosition", DTR_GetPlayerSpawnPosition, DTR_GetPlayerSpawnPosition_Post); delete gd.CreateDetourOrFail("l4d_consistent_escaperoute::TerrorNavMesh::ComputeFlowDistances", DTR_ComputeFlowDistances, DTR_ComputeFlowDistances_Post); delete gd; @@ -136,6 +173,7 @@ MRESReturn DTR_ComputeFlowDistances(DHookReturn hReturn) return MRES_Ignored; } +// GetPlayerSpawnPosition(SurvivorCharacterType,Vector *,QAngle *,TerrorNavArea **) MRESReturn DTR_GetPlayerSpawnPosition(DHookReturn hReturn, DHookParam hParams) { if (!g_bInComputeFlowDistances) @@ -159,6 +197,49 @@ MRESReturn DTR_GetPlayerSpawnPosition(DHookReturn hReturn, DHookParam hParams) return MRES_Ignored; } +// Checkpoint::GetSpawnPosition(Vector *, QAngle *, TerrorNavArea **)const +MRESReturn DTR_Checkpoint_GetSpawnPosition_Post(Address pThis, DHookReturn hReturn, DHookParam hParams) +{ + if (!g_bInComputeFlowDistances) + return MRES_Ignored; + +#if DEBUG + PrintToServer("[%s] Overriding Checkpoint::GetSpawnPosition", PLUGIN_TAG); +#endif + + // NOTE: + // GetSpawnPosition always computes a random position within start safe area. + // The following makes it always return the largest area instead. + TerrorNavArea area = SDKCall(g_call_Checkpoint_GetLargestArea, pThis); + if (area == Address_Null) + return MRES_Ignored; + +#if DEBUG + PrintToServer("[%s] Fixed player start area = %d", PLUGIN_TAG, area.GetID()); +#endif + + if (!hParams.IsNull(1)) + { + float pos[3]; + L4D_GetNavAreaCenter(area, pos); + hParams.SetVector(1, pos); + } + + if (!hParams.IsNull(2)) + { + hParams.SetVector(2, {0.0, 0.0, 0.0}); + } + + if (!hParams.IsNull(3)) + { + hParams.SetObjectVar(3, 0, ObjectValueType_Int, area); + } + + hReturn.Value = true; + return MRES_ChangedOverride; +} + +// GetPlayerSpawnPosition(SurvivorCharacterType,Vector *,QAngle *,TerrorNavArea **) MRESReturn DTR_GetPlayerSpawnPosition_Post(DHookReturn hReturn, DHookParam hParams) { if (!g_bInComputeFlowDistances) @@ -207,7 +288,7 @@ void NextFrame_ComputeFlowDistance() if (g_SavedEscapeRouteAreas.Length != TheEscapeRoute.m_nMainPathAreaCount) { - PrintToServer(" Count mismatches (was %d, now %d)", PLUGIN_TAG, g_SavedEscapeRouteAreas.Length, TheEscapeRoute.m_nMainPathAreaCount); + PrintToServer(" Count mismatches (was %d, now %d)", g_SavedEscapeRouteAreas.Length, TheEscapeRoute.m_nMainPathAreaCount); fullyMatched = false; } else @@ -217,7 +298,7 @@ void NextFrame_ComputeFlowDistance() TerrorNavArea area = TheEscapeRoute.GetMainPathArea(i); if (area.GetID() != g_SavedEscapeRouteAreas.Get(i)) { - PrintToServer(" Area @ %d mismatches (was #%d, now #%d)", PLUGIN_TAG, i, g_SavedEscapeRouteAreas.Get(i), area.GetID()); + PrintToServer(" Area @ %d mismatches (was #%d, now #%d)", i, g_SavedEscapeRouteAreas.Get(i), area.GetID()); fullyMatched = false; break; }