Add disabled GPS momentum penalty prototype
This commit is contained in:
@@ -2,6 +2,40 @@
|
|||||||
|
|
||||||
Current date/time context: 2026-06-27, local timezone America/Chicago.
|
Current date/time context: 2026-06-27, local timezone America/Chicago.
|
||||||
|
|
||||||
|
## 2026-06-27 Disabled Momentum Penalty Prototype
|
||||||
|
|
||||||
|
- Static disassembly of the full async solver relaxation cluster is updated.
|
||||||
|
`0x40ba58` has exactly four direct callers:
|
||||||
|
`0x40b175`, `0x40b4ac`, `0x40b6d5`, and `0x40bca7`.
|
||||||
|
- The ordinary adjacency walkers are still the clean first patch surface:
|
||||||
|
`0x40b304` reaches relax at `0x40b4ac`, and `0x40b540` reaches relax at
|
||||||
|
`0x40b6d5`. Both compute vanilla tentative `g` in `xmm6`, copy it to
|
||||||
|
`xmm3`, write `f = g + h` at `[rsp+0x20]`, and then call `0x40ba58`.
|
||||||
|
- Patch starts are `0x40b49a` for list A and `0x40b6c3` for list B. Each
|
||||||
|
replaces 23 bytes ending at the vanilla `0x40ba58` call and jumps to a
|
||||||
|
private executable cave. The cave reproduces the overwritten vanilla math,
|
||||||
|
adds a fixed nonnegative cost to `xmm6`, recomputes `f` from the penalized
|
||||||
|
`g`, calls vanilla `0x40ba58`, and jumps back after the overwritten block.
|
||||||
|
- Added a disabled-by-default RED4ext prototype in
|
||||||
|
`red4ext/EdgeWeightGPS/src/Main.cpp`:
|
||||||
|
`kEnableGpsMomentumPenaltyInlinePatch = false`,
|
||||||
|
`kGpsMomentumFixedEdgePenalty = 8.0f`.
|
||||||
|
- This first prototype is only a short-segment/churn lever. It adds a fixed
|
||||||
|
per-edge penalty on ordinary neighbor expansion, so routes that stair-step
|
||||||
|
through many small pieces become more expensive without making highways
|
||||||
|
cheaper. It does not yet compute turn angle, ramp transition cost, or
|
||||||
|
predecessor/current/neighbor geometry.
|
||||||
|
- The first prototype intentionally leaves the two non-ordinary relax paths
|
||||||
|
vanilla:
|
||||||
|
`0x40b175` appears to be a target/terminal candidate path, and `0x40bca7`
|
||||||
|
appears to be a special continuation/end-candidate path from `0x40bbbc`.
|
||||||
|
Patch them only after the ordinary expansion test proves stable.
|
||||||
|
- Build verification passed in toolbox:
|
||||||
|
`toolbox run -c 2077 ./tools/build_red4ext_shim.sh`.
|
||||||
|
- The prototype was not installed into the live game directory. To test later,
|
||||||
|
flip `kEnableGpsMomentumPenaltyInlinePatch` to `true`, rebuild, install only
|
||||||
|
after the game is cold, and compare against the fixed El Coyote route set.
|
||||||
|
|
||||||
## 2026-06-27 Routing Literature And Engine Research
|
## 2026-06-27 Routing Literature And Engine Research
|
||||||
|
|
||||||
- Added `docs/gps-routing-research.md` as the durable summary of the GPS
|
- Added `docs/gps-routing-research.md` as the durable summary of the GPS
|
||||||
|
|||||||
@@ -403,18 +403,33 @@ Add tests targeted at the new model:
|
|||||||
|
|
||||||
## Practical Next Step
|
## Practical Next Step
|
||||||
|
|
||||||
Before coding the full patch, do one static pass to finish the field map around
|
The static pass around `0x40b304`, `0x40b540`, and `0x40ba58` is complete
|
||||||
`0x40b304`, `0x40b540`, and `0x40ba58`:
|
enough for the first prototype:
|
||||||
|
|
||||||
|
- `0x40ba58` has four direct callers: `0x40b175`, `0x40b4ac`, `0x40b6d5`,
|
||||||
|
and `0x40bca7`.
|
||||||
|
- The ordinary adjacency walkers are the two clean first patch sites:
|
||||||
|
`0x40b4ac` from `0x40b304`, and `0x40b6d5` from `0x40b540`.
|
||||||
|
- At both ordinary sites, the live context contains solver, current state,
|
||||||
|
neighbor state, current node, neighbor node, current progress, neighbor
|
||||||
|
progress, vanilla `g`, and vanilla `f`.
|
||||||
|
- A disabled-by-default RED4ext prototype now patches the two ordinary sites by
|
||||||
|
adding a fixed nonnegative per-edge penalty before calling vanilla
|
||||||
|
`0x40ba58`.
|
||||||
|
|
||||||
|
Remaining static work before the full momentum model:
|
||||||
|
|
||||||
- Confirm candidate vector layout at `0x40b8f0`/state allocation.
|
- Confirm candidate vector layout at `0x40b8f0`/state allocation.
|
||||||
- Confirm predecessor state pointer/index path from `state+0x1e`.
|
|
||||||
- Confirm route-mode values at `solver+0xc4` for manual driving vs AutoDrive.
|
- Confirm route-mode values at `solver+0xc4` for manual driving vs AutoDrive.
|
||||||
- Identify a cheap way to detect road-class transition from current/neighbor
|
- Identify a cheap way to detect road-class transition from current/neighbor
|
||||||
node flags.
|
node flags.
|
||||||
- Decide whether the first prototype patches only the two main relax callsites
|
- Decode predecessor geometry from `state+0x1e` well enough to compute
|
||||||
(`0x40b4ac`, `0x40b6d5`) or also the special/helper relax paths
|
predecessor/current/neighbor turn angle.
|
||||||
(`0x40b175`, `0x40bca7`).
|
- Decide whether the later turn/ramp model should also patch the special/helper
|
||||||
|
relax paths (`0x40b175`, `0x40bca7`) or keep them vanilla.
|
||||||
|
|
||||||
After that, implement the smallest live-tunable prototype with additive
|
Next implementation step is to enable and test the smallest additive prototype
|
||||||
penalties only. Keep highway multiplier support available as a diagnostic knob,
|
with route-result comparisons. If the fixed per-edge penalty moves paths in the
|
||||||
but do not use it as the primary default behavior.
|
right direction and remains stable, extend it into a live-tunable cost file and
|
||||||
|
then add turn/continuity penalties. Keep highway multiplier support available as
|
||||||
|
a diagnostic knob, but do not use it as the primary default behavior.
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ constexpr bool kEnableGpsMultiplierHooks = false;
|
|||||||
constexpr bool kEnableGpsAuxMultiplierHook = false;
|
constexpr bool kEnableGpsAuxMultiplierHook = false;
|
||||||
constexpr bool kEnableGpsMultiplierSignatureTrace = false;
|
constexpr bool kEnableGpsMultiplierSignatureTrace = false;
|
||||||
constexpr bool kEnableGpsNodeMultiplierInlinePatch = true;
|
constexpr bool kEnableGpsNodeMultiplierInlinePatch = true;
|
||||||
|
constexpr bool kEnableGpsMomentumPenaltyInlinePatch = false;
|
||||||
constexpr bool kEnableGpsCostTablePatch = false;
|
constexpr bool kEnableGpsCostTablePatch = false;
|
||||||
constexpr bool kEnableGpsProviderClassPatch = false;
|
constexpr bool kEnableGpsProviderClassPatch = false;
|
||||||
constexpr bool kEnableGpsProviderClassPatchTrace = false;
|
constexpr bool kEnableGpsProviderClassPatchTrace = false;
|
||||||
@@ -197,6 +198,9 @@ constexpr uintptr_t kGpsFilteredCostProviderVtableRva = 0x2AE60F8;
|
|||||||
constexpr uintptr_t kGpsAuxMultiplierRva = 0x040BB00;
|
constexpr uintptr_t kGpsAuxMultiplierRva = 0x040BB00;
|
||||||
constexpr uintptr_t kGpsNodeMultiplierRva = 0x040BB40;
|
constexpr uintptr_t kGpsNodeMultiplierRva = 0x040BB40;
|
||||||
constexpr uintptr_t kGpsNodeMultiplierRoadTailRva = 0x040BB98;
|
constexpr uintptr_t kGpsNodeMultiplierRoadTailRva = 0x040BB98;
|
||||||
|
constexpr uintptr_t kGpsRelaxUpdateRva = 0x040BA58;
|
||||||
|
constexpr uintptr_t kGpsExpandListARelaxPatchRva = 0x040B49A;
|
||||||
|
constexpr uintptr_t kGpsExpandListBRelaxPatchRva = 0x040B6C3;
|
||||||
constexpr uintptr_t kGpsCostMultiplierTableRva = 0x3154D28;
|
constexpr uintptr_t kGpsCostMultiplierTableRva = 0x3154D28;
|
||||||
constexpr uint64_t kGpsResolveTraceWindowMs = 1200;
|
constexpr uint64_t kGpsResolveTraceWindowMs = 1200;
|
||||||
constexpr uint32_t kGpsResolveSelectedRetLogLimit = 24;
|
constexpr uint32_t kGpsResolveSelectedRetLogLimit = 24;
|
||||||
@@ -217,6 +221,9 @@ constexpr float kGpsSolverHighwayMultiplierDefault = 0.80f;
|
|||||||
constexpr float kGpsSolverGpsOnlyMultiplier = 1.10f;
|
constexpr float kGpsSolverGpsOnlyMultiplier = 1.10f;
|
||||||
constexpr float kGpsSolverPavementMultiplier = 1.25f;
|
constexpr float kGpsSolverPavementMultiplier = 1.25f;
|
||||||
constexpr size_t kGpsNodeMultiplierRoadTailPatchSize = 14;
|
constexpr size_t kGpsNodeMultiplierRoadTailPatchSize = 14;
|
||||||
|
constexpr size_t kGpsMomentumRelaxPatchSize = 23;
|
||||||
|
constexpr size_t kGpsMomentumPenaltyCaveSize = 96;
|
||||||
|
constexpr float kGpsMomentumFixedEdgePenalty = 8.0f;
|
||||||
constexpr std::array<float, 8> kGpsSolverNonRoadMultipliers = {
|
constexpr std::array<float, 8> kGpsSolverNonRoadMultipliers = {
|
||||||
10.0f, 6.0f, 2.0f, 0.0f, 4.0f, 2.5f, 1.5f, 0.0f,
|
10.0f, 6.0f, 2.0f, 0.0f, 4.0f, 2.5f, 1.5f, 0.0f,
|
||||||
};
|
};
|
||||||
@@ -245,6 +252,16 @@ constexpr std::array<GpsProviderClassOverride, 5> kGpsProviderClassOverrides = {
|
|||||||
|
|
||||||
constexpr std::array<size_t, 6> kGpsProviderClassProbeIds = {1, 3, 4, 5, 14, 15};
|
constexpr std::array<size_t, 6> kGpsProviderClassProbeIds = {1, 3, 4, 5, 14, 15};
|
||||||
|
|
||||||
|
struct GpsMomentumPenaltyPatchSite
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
uintptr_t targetRva;
|
||||||
|
std::array<uint8_t, kGpsMomentumRelaxPatchSize> original{};
|
||||||
|
void* cave = nullptr;
|
||||||
|
uint8_t* penaltyConstant = nullptr;
|
||||||
|
bool applied = false;
|
||||||
|
};
|
||||||
|
|
||||||
HMODULE gModule = nullptr;
|
HMODULE gModule = nullptr;
|
||||||
std::mutex gLogMutex;
|
std::mutex gLogMutex;
|
||||||
RED4ext::v1::PluginHandle gHandle = nullptr;
|
RED4ext::v1::PluginHandle gHandle = nullptr;
|
||||||
@@ -330,6 +347,10 @@ std::array<uint8_t, kGpsNodeMultiplierRoadTailPatchSize> gGpsNodeMultiplierRoadT
|
|||||||
void* gGpsNodeMultiplierRoadTailCave = nullptr;
|
void* gGpsNodeMultiplierRoadTailCave = nullptr;
|
||||||
uint8_t* gGpsNodeMultiplierRoadTailHighwayConstant = nullptr;
|
uint8_t* gGpsNodeMultiplierRoadTailHighwayConstant = nullptr;
|
||||||
bool gGpsNodeMultiplierRoadTailPatchApplied = false;
|
bool gGpsNodeMultiplierRoadTailPatchApplied = false;
|
||||||
|
std::array<GpsMomentumPenaltyPatchSite, 2> gGpsMomentumPenaltyPatchSites = {{
|
||||||
|
{"expand-list-a", kGpsExpandListARelaxPatchRva},
|
||||||
|
{"expand-list-b", kGpsExpandListBRelaxPatchRva},
|
||||||
|
}};
|
||||||
std::mutex gGpsCostLogMutex;
|
std::mutex gGpsCostLogMutex;
|
||||||
std::mutex gGpsAsyncLogMutex;
|
std::mutex gGpsAsyncLogMutex;
|
||||||
std::mutex gGpsCostTablePatchMutex;
|
std::mutex gGpsCostTablePatchMutex;
|
||||||
@@ -687,6 +708,43 @@ void LogGpsSolverHighwayMultiplier(std::string_view aPrefix, float aValue)
|
|||||||
LogRed4ext(line.str());
|
LogRed4ext(line.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmitAbsoluteRaxCall(uint8_t* aCave, size_t& aOffset, uintptr_t aTarget)
|
||||||
|
{
|
||||||
|
aCave[aOffset++] = 0x48; // mov rax, imm64
|
||||||
|
aCave[aOffset++] = 0xB8;
|
||||||
|
std::memcpy(aCave + aOffset, &aTarget, sizeof(aTarget));
|
||||||
|
aOffset += sizeof(aTarget);
|
||||||
|
aCave[aOffset++] = 0xFF; // call rax
|
||||||
|
aCave[aOffset++] = 0xD0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitAbsoluteRaxJump(uint8_t* aCave, size_t& aOffset, uintptr_t aTarget)
|
||||||
|
{
|
||||||
|
aCave[aOffset++] = 0x48; // mov rax, imm64
|
||||||
|
aCave[aOffset++] = 0xB8;
|
||||||
|
std::memcpy(aCave + aOffset, &aTarget, sizeof(aTarget));
|
||||||
|
aOffset += sizeof(aTarget);
|
||||||
|
aCave[aOffset++] = 0xFF; // jmp rax
|
||||||
|
aCave[aOffset++] = 0xE0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildAbsoluteJumpPatch(uint8_t* aPatch, size_t aPatchSize, uintptr_t aTarget)
|
||||||
|
{
|
||||||
|
size_t offset = 0;
|
||||||
|
aPatch[offset++] = 0xFF; // jmp qword ptr [rip+0]
|
||||||
|
aPatch[offset++] = 0x25;
|
||||||
|
aPatch[offset++] = 0x00;
|
||||||
|
aPatch[offset++] = 0x00;
|
||||||
|
aPatch[offset++] = 0x00;
|
||||||
|
aPatch[offset++] = 0x00;
|
||||||
|
std::memcpy(aPatch + offset, &aTarget, sizeof(aTarget));
|
||||||
|
offset += sizeof(aTarget);
|
||||||
|
while (offset < aPatchSize)
|
||||||
|
{
|
||||||
|
aPatch[offset++] = 0x90;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MaybeReloadGpsSolverWeights(bool aForce = false)
|
void MaybeReloadGpsSolverWeights(bool aForce = false)
|
||||||
{
|
{
|
||||||
if (!kEnableGpsNodeMultiplierInlinePatch)
|
if (!kEnableGpsNodeMultiplierInlinePatch)
|
||||||
@@ -761,6 +819,159 @@ void MaybeReloadGpsSolverWeights(bool aForce = false)
|
|||||||
LogRed4ext(line.str());
|
LogRed4ext(line.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RemoveGpsMomentumPenaltyInlinePatch();
|
||||||
|
|
||||||
|
bool InstallGpsMomentumPenaltyPatchSite(GpsMomentumPenaltyPatchSite& aSite,
|
||||||
|
std::initializer_list<uint8_t> aAddCurrentCostInstruction)
|
||||||
|
{
|
||||||
|
if (aSite.applied)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* cave = static_cast<uint8_t*>(
|
||||||
|
VirtualAlloc(nullptr, kGpsMomentumPenaltyCaveSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
|
||||||
|
if (!cave)
|
||||||
|
{
|
||||||
|
std::ostringstream line;
|
||||||
|
line << "gps-momentum-penalty-inline-patch skipped site=" << aSite.name << " reason=alloc-failed";
|
||||||
|
LogRed4ext(line.str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t offset = 0;
|
||||||
|
const auto emit = [&](std::initializer_list<uint8_t> aBytes) {
|
||||||
|
for (auto byte : aBytes)
|
||||||
|
{
|
||||||
|
cave[offset++] = byte;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Replaces the final g/f assembly before 0x40ba58. The overwritten
|
||||||
|
// vanilla block differs only in which XMM register stores current.g.
|
||||||
|
emit(aAddCurrentCostInstruction);
|
||||||
|
|
||||||
|
const auto addPenaltyOffset = offset;
|
||||||
|
emit({0xF3, 0x0F, 0x58, 0x35, 0x00, 0x00, 0x00, 0x00}); // addss xmm6, [rip+disp32]
|
||||||
|
|
||||||
|
emit({0xF3, 0x0F, 0x58, 0xC6}); // addss xmm0, xmm6
|
||||||
|
emit({0x0F, 0x28, 0xDE}); // movaps xmm3, xmm6
|
||||||
|
emit({0xF3, 0x0F, 0x11, 0x44, 0x24, 0x20}); // movss [rsp+0x20], xmm0
|
||||||
|
EmitAbsoluteRaxCall(cave, offset, GetImageBase() + kGpsRelaxUpdateRva);
|
||||||
|
EmitAbsoluteRaxJump(cave, offset, GetImageBase() + aSite.targetRva + kGpsMomentumRelaxPatchSize);
|
||||||
|
|
||||||
|
while ((offset % alignof(float)) != 0)
|
||||||
|
{
|
||||||
|
cave[offset++] = 0x90;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto penaltyConstantOffset = offset;
|
||||||
|
WriteFloat(cave + offset, kGpsMomentumFixedEdgePenalty);
|
||||||
|
offset += sizeof(float);
|
||||||
|
|
||||||
|
const auto caveAddress = reinterpret_cast<uintptr_t>(cave);
|
||||||
|
WriteInt32(cave + addPenaltyOffset + 4,
|
||||||
|
static_cast<int32_t>((caveAddress + penaltyConstantOffset) - (caveAddress + addPenaltyOffset + 8)));
|
||||||
|
|
||||||
|
auto* target = reinterpret_cast<uint8_t*>(GetImageBase() + aSite.targetRva);
|
||||||
|
std::memcpy(aSite.original.data(), target, aSite.original.size());
|
||||||
|
|
||||||
|
std::array<uint8_t, kGpsMomentumRelaxPatchSize> patch{};
|
||||||
|
BuildAbsoluteJumpPatch(patch.data(), patch.size(), caveAddress);
|
||||||
|
|
||||||
|
DWORD oldProtect = 0;
|
||||||
|
if (!VirtualProtect(target, patch.size(), PAGE_EXECUTE_READWRITE, &oldProtect))
|
||||||
|
{
|
||||||
|
VirtualFree(cave, 0, MEM_RELEASE);
|
||||||
|
std::ostringstream line;
|
||||||
|
line << "gps-momentum-penalty-inline-patch skipped site=" << aSite.name << " reason=protect-failed";
|
||||||
|
LogRed4ext(line.str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memcpy(target, patch.data(), patch.size());
|
||||||
|
FlushInstructionCache(GetCurrentProcess(), target, patch.size());
|
||||||
|
|
||||||
|
DWORD ignoredProtect = 0;
|
||||||
|
VirtualProtect(target, patch.size(), oldProtect, &ignoredProtect);
|
||||||
|
|
||||||
|
aSite.cave = cave;
|
||||||
|
aSite.penaltyConstant = cave + penaltyConstantOffset;
|
||||||
|
aSite.applied = true;
|
||||||
|
|
||||||
|
std::ostringstream line;
|
||||||
|
line << "gps-momentum-penalty-inline-patch applied site=" << aSite.name << " target_rva=0x" << std::hex
|
||||||
|
<< aSite.targetRva << " cave=" << static_cast<void*>(cave) << std::dec
|
||||||
|
<< " fixedEdgePenalty=" << kGpsMomentumFixedEdgePenalty;
|
||||||
|
LogRed4ext(line.str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstallGpsMomentumPenaltyInlinePatch()
|
||||||
|
{
|
||||||
|
if (!kEnableGpsMomentumPenaltyInlinePatch)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool firstOk =
|
||||||
|
InstallGpsMomentumPenaltyPatchSite(gGpsMomentumPenaltyPatchSites[0],
|
||||||
|
{0xF3, 0x41, 0x0F, 0x58, 0xF6}); // addss xmm6, xmm14
|
||||||
|
const bool secondOk =
|
||||||
|
InstallGpsMomentumPenaltyPatchSite(gGpsMomentumPenaltyPatchSites[1],
|
||||||
|
{0xF3, 0x41, 0x0F, 0x58, 0xF4}); // addss xmm6, xmm12
|
||||||
|
|
||||||
|
if (!firstOk || !secondOk)
|
||||||
|
{
|
||||||
|
RemoveGpsMomentumPenaltyInlinePatch();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveGpsMomentumPenaltyInlinePatch()
|
||||||
|
{
|
||||||
|
for (auto& site : gGpsMomentumPenaltyPatchSites)
|
||||||
|
{
|
||||||
|
if (!site.applied)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* target = reinterpret_cast<uint8_t*>(GetImageBase() + site.targetRva);
|
||||||
|
DWORD oldProtect = 0;
|
||||||
|
if (VirtualProtect(target, site.original.size(), PAGE_EXECUTE_READWRITE, &oldProtect))
|
||||||
|
{
|
||||||
|
std::memcpy(target, site.original.data(), site.original.size());
|
||||||
|
FlushInstructionCache(GetCurrentProcess(), target, site.original.size());
|
||||||
|
|
||||||
|
DWORD ignoredProtect = 0;
|
||||||
|
VirtualProtect(target, site.original.size(), oldProtect, &ignoredProtect);
|
||||||
|
|
||||||
|
std::ostringstream line;
|
||||||
|
line << "gps-momentum-penalty-inline-patch restored site=" << site.name;
|
||||||
|
LogRed4ext(line.str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::ostringstream line;
|
||||||
|
line << "gps-momentum-penalty-inline-patch restore-failed site=" << site.name
|
||||||
|
<< " reason=protect-failed";
|
||||||
|
LogRed4ext(line.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (site.cave)
|
||||||
|
{
|
||||||
|
VirtualFree(site.cave, 0, MEM_RELEASE);
|
||||||
|
site.cave = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
site.penaltyConstant = nullptr;
|
||||||
|
site.applied = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool InstallGpsNodeMultiplierInlinePatch()
|
bool InstallGpsNodeMultiplierInlinePatch()
|
||||||
{
|
{
|
||||||
if (!kEnableGpsNodeMultiplierInlinePatch || gGpsNodeMultiplierRoadTailPatchApplied)
|
if (!kEnableGpsNodeMultiplierInlinePatch || gGpsNodeMultiplierRoadTailPatchApplied)
|
||||||
@@ -5550,7 +5761,16 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e
|
|||||||
gGpsSolverHighwayMultiplier.load(std::memory_order_relaxed));
|
gGpsSolverHighwayMultiplier.load(std::memory_order_relaxed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (kEnableGpsMomentumPenaltyInlinePatch)
|
||||||
|
{
|
||||||
|
std::ostringstream line;
|
||||||
|
line << "momentum fixed-edge penalty active value=" << kGpsMomentumFixedEdgePenalty
|
||||||
|
<< " sites=ordinary-expansion";
|
||||||
|
LogRed4ext(line.str());
|
||||||
|
}
|
||||||
|
|
||||||
InstallGpsNodeMultiplierInlinePatch();
|
InstallGpsNodeMultiplierInlinePatch();
|
||||||
|
InstallGpsMomentumPenaltyInlinePatch();
|
||||||
|
|
||||||
if (kEnableGpsQueryLifecycleHooks)
|
if (kEnableGpsQueryLifecycleHooks)
|
||||||
{
|
{
|
||||||
@@ -5781,6 +6001,7 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e
|
|||||||
if (aReason == RED4ext::v1::EMainReason::Unload)
|
if (aReason == RED4ext::v1::EMainReason::Unload)
|
||||||
{
|
{
|
||||||
RemoveGpsNodeMultiplierInlinePatch();
|
RemoveGpsNodeMultiplierInlinePatch();
|
||||||
|
RemoveGpsMomentumPenaltyInlinePatch();
|
||||||
|
|
||||||
if (kEnableGpsQueryLifecycleHooks)
|
if (kEnableGpsQueryLifecycleHooks)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user