Skip to content

Commit

Permalink
feat(extra-natives/five): CPortalTracker ScRT
Browse files Browse the repository at this point in the history
CPortalTracker probeLength adjustments via custom ScRT Native

Co-authored-by: nikez <[email protected]>
Co-authored-by: okqut <[email protected]>
  • Loading branch information
3 people committed Mar 14, 2024
1 parent 8b93ef2 commit 226cf72
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 0 deletions.
124 changes: 124 additions & 0 deletions code/components/extra-natives-five/src/InteriorExtraNatives.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
#include <atPool.h>
#include <DirectXMath.h>
#include <CrossBuildRuntime.h>
#include <GameInit.h>
#include <CoreConsole.h>
#include <MinHook.h>

#define DECLARE_ACCESSOR(x) \
decltype(impl.m2060.x)& x() \
Expand Down Expand Up @@ -303,8 +306,129 @@ static int GetInteriorRoomIdByHash(CMloModelInfo* arch, int searchHash)
return -1;
}

static float g_interiorProbeLengthOverride = 0.0;
static bool (*g_CPortalTracker__Probe)(Vector3* pos, CInteriorInst** ppInteriorInstance, int* roomId, Vector3* traceImpactPoint, float traceLength);
bool __fastcall CPortalTracker__Probe(Vector3* pos, CInteriorInst** ppInteriorInstance, int* roomId, Vector3* traceImpactPoint, float traceLength)
{
// game code has a lot of different special case handlings in CPortalTracker::vft0x8
// joaat('xs_arena_interior') seems to be the case with the longest traceLength override (150.f)
//
if (g_interiorProbeLengthOverride > 0.0f && traceLength < g_interiorProbeLengthOverride)
{
traceLength = g_interiorProbeLengthOverride;
}

return g_CPortalTracker__Probe(pos, ppInteriorInstance, roomId, traceImpactPoint, traceLength);
}

static uint64_t** g_pedFactory;

static void (*g_CPed__UpdatePortalTracker)(uint64_t thisptr);
void __fastcall CPed__UpdatePortalTracker(uint64_t thisptr)
{
// entity and portal tracker ownership check
//
if (!thisptr || *(uint8_t*)(thisptr + 0x28) != 4 || *(uint64_t*)(thisptr + 0xF0 + 0x8) != thisptr)
{
return g_CPed__UpdatePortalTracker(thisptr);
}

// local ped check
//
if (!*g_pedFactory || !((*g_pedFactory) + 1) || (*((*g_pedFactory) + 1) != thisptr))
{
return g_CPed__UpdatePortalTracker(thisptr);
}

uint64_t dynamicComponent = *(uint64_t*)(thisptr + 0x50);
if (!dynamicComponent)
{
return g_CPed__UpdatePortalTracker(thisptr);
}

uint64_t attachmentExtension = *(uint64_t*)(dynamicComponent + 0x48);
if (!attachmentExtension)
{
return g_CPed__UpdatePortalTracker(thisptr);
}

// dynamic object check
//
uint64_t attachmentParent = *(uint64_t*)(attachmentExtension);
if (!attachmentParent || *(uint8_t*)(attachmentParent + 0x28) < 2 || *(uint8_t*)(attachmentParent + 0x28) > 5)
{
return g_CPed__UpdatePortalTracker(thisptr);
}

// force a re-scan whilst attached to a physical object
//
*(uint32_t*)(thisptr + 0xF0 + 0x88) |= (1 << 3);

g_CPed__UpdatePortalTracker(thisptr);
}

static HookFunction initFunction([]()
{
{
static float* emitterAudioEntityProbeLengthData = (float*)hook::AllocateStubMemory(sizeof(float));
static ConVar<float> emitterAudioEntityProbeLength("game_emitterAudioEntityProbeLength", ConVar_Replicated, 150.0, emitterAudioEntityProbeLengthData);

emitterAudioEntityProbeLength.GetHelper()->SetConstraints(20.0f, 150.0f);

auto location = hook::get_pattern<uint32_t>("33 ED 39 A9 ? ? ? ? 0F 86 ? ? ? ? F3", 18);
hook::put<int32_t>(location, (intptr_t)emitterAudioEntityProbeLengthData - (intptr_t)location - 4);

OnKillNetworkDone.Connect([]()
{
se::ScopedPrincipal principalScopeInternal(se::Principal{ "system.internal" });
emitterAudioEntityProbeLength.GetHelper()->SetRawValue(150.0f);
});
}

{
g_pedFactory = hook::get_address<decltype(g_pedFactory)>(hook::get_pattern("E8 ? ? ? ? 48 8B 05 ? ? ? ? 48 8B 58 08 48 8B CB E8", 8));

auto location = hook ::get_pattern<void>("0F 28 81 ? ? ? ? 45 33 F6", -0x21);

MH_Initialize();
MH_CreateHook(location, CPed__UpdatePortalTracker, (void**)&g_CPed__UpdatePortalTracker);
MH_EnableHook(MH_ALL_HOOKS);

// CPortalTracker::fnCalledFromUpdate
//
location = hook::get_pattern<void>("E8 ? ? ? ? 40 8A F8 49 ? ? E8");
hook::set_call(&g_CPortalTracker__Probe, location);

hook::call(location, CPortalTracker__Probe);

// GET_INTERIOR_FROM_COLLISION
//
location = hook::get_pattern<void>("E8 ? ? ? ? 48 8B 54 24 ? 48 85 D2 74 1A");
hook::call(location, CPortalTracker__Probe);

// IS_COLLISION_MARKED_OUTSIDE
//
location = hook::get_pattern<void>("E8 ? ? ? ? 48 39 5C 24 ? 0F 94 C0");
hook::call(location, CPortalTracker__Probe);

fx::ScriptEngine::RegisterNativeHandler("SET_INTERIOR_PROBE_LENGTH", [=](fx::ScriptContext& context)
{
auto length = context.GetArgument<float>(0);
if (std::isnan(length) || std::isinf(length))
{
return false;
}

g_interiorProbeLengthOverride = std::clamp(length, 0.0f, 150.0f);
return true;
});

OnKillNetworkDone.Connect([]()
{
g_interiorProbeLengthOverride = 0.0f;
});
}

{
auto location = hook::get_pattern<char>("BA A1 85 94 52 41 B8 01", 0x34);

Expand Down
39 changes: 39 additions & 0 deletions ext/native-decls/SetInteriorProbeLength.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
ns: CFX
apiset: client
---
## SET_INTERIOR_PROBE_LENGTH

```c
void SET_INTERIOR_PROBE_LENGTH(float probeLength);
```
Overwrite the games default CPortalTracker interior detection range.
This fixes potentially unwanted behaviour in the base game and allows you to build custom interiors with larger ceiling heights without running into graphical glitches.
By default CPortalTracker will probe 4 units downward trying to reach collisions that are part of the interior the entity is in.
If no collision can be found 16 units are used in some circumstances.
There are 30+ hard coded special cases, only some of them exposed via script (for example `ENABLE_STADIUM_PROBES_THIS_FRAME`).
This native allows you to extend the probe range up to 150 units which is the same value the game uses for the `xs_arena_interior`
## Examples
```lua
RegisterCommand("setInteriorProbeLength", function(src, args, raw)
local probeLength = (tonumber(args[1]) + 0.0)
print("Extending interior detection probes to: ", probeLength)
SetInteriorProbeLength(probeLength)
end)
RegisterCommand("resetInteriorProbeLength", function()
print("Resetting interior detection probes to default settings")
SetInteriorProbeLength(0.0)
end)
```


## Parameters
* **probeLength**: The desired probe length (0.0 - 150.0)

0 comments on commit 226cf72

Please sign in to comment.