Skip to content
Open
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
56 changes: 56 additions & 0 deletions addons/sourcemod/gamedata/l4d_consistent_escaperoute.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
}
}

Expand Down Expand Up @@ -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 */
}
}
}

Expand Down Expand Up @@ -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 */
}
}
}
}
87 changes: 84 additions & 3 deletions addons/sourcemod/scripting/l4d_consistent_escaperoute.sp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <sourcemod>
#include <dhooks>
#include <sourcescramble>
#include <sdktools>
#include <left4dhooks>

#if DEBUG
#include <sdktools_functions>
Expand All @@ -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);
Expand Down Expand Up @@ -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 {}
Expand Down Expand Up @@ -98,6 +128,7 @@ methodmap TerrorNavArea < Address
#endif

MemoryPatch g_patch_SkipSpawnPosIdx;
Handle g_call_Checkpoint_GetLargestArea;

public void OnPluginStart()
{
Expand All @@ -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;
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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;
}
Expand Down