Add live spatial weight tuning
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
@@ -211,6 +212,7 @@ constexpr float kGpsSpatialRoadMultiplier = 1.0f;
|
||||
constexpr float kGpsSpatialGpsOnlyMultiplier = 1.20f;
|
||||
constexpr float kGpsSpatialPavementMultiplier = 1.35f;
|
||||
constexpr float kGpsSpatialUnknownMultiplier = 1.05f;
|
||||
constexpr uint64_t kGpsSpatialWeightsReloadIntervalMs = 500;
|
||||
|
||||
struct GpsProviderClassOverride
|
||||
{
|
||||
@@ -530,17 +532,38 @@ GpsProviderFilterFunc gOriginalGpsFilteredProviderFilter = nullptr;
|
||||
GpsProviderFilterFunc gOriginalGpsBaseProviderFilter = nullptr;
|
||||
GpsMultiplierFunc gOriginalGpsAuxMultiplier = nullptr;
|
||||
GpsMultiplierFunc gOriginalGpsNodeMultiplier = nullptr;
|
||||
std::array<std::atomic<float>, 5> gGpsSpatialMultipliers = {
|
||||
kGpsSpatialHighwayMultiplier,
|
||||
kGpsSpatialRoadMultiplier,
|
||||
kGpsSpatialGpsOnlyMultiplier,
|
||||
kGpsSpatialPavementMultiplier,
|
||||
kGpsSpatialUnknownMultiplier,
|
||||
};
|
||||
std::mutex gGpsSpatialWeightsMutex;
|
||||
std::atomic<uint64_t> gGpsSpatialWeightsNextReloadCheckMs = 0;
|
||||
std::filesystem::file_time_type gGpsSpatialWeightsLastWriteTime{};
|
||||
bool gGpsSpatialWeightsFileKnown = false;
|
||||
|
||||
std::filesystem::path GetLogPath()
|
||||
std::filesystem::path GetPluginDirectory()
|
||||
{
|
||||
wchar_t modulePath[MAX_PATH]{};
|
||||
if (gModule && GetModuleFileNameW(gModule, modulePath, MAX_PATH) > 0)
|
||||
{
|
||||
auto path = std::filesystem::path(modulePath);
|
||||
return path.parent_path() / L"EdgeWeightGPS.log";
|
||||
return path.parent_path();
|
||||
}
|
||||
|
||||
return std::filesystem::path(L"red4ext") / L"plugins" / L"EdgeWeightGPS" / L"EdgeWeightGPS.log";
|
||||
return std::filesystem::path(L"red4ext") / L"plugins" / L"EdgeWeightGPS";
|
||||
}
|
||||
|
||||
std::filesystem::path GetLogPath()
|
||||
{
|
||||
return GetPluginDirectory() / L"EdgeWeightGPS.log";
|
||||
}
|
||||
|
||||
std::filesystem::path GetSpatialWeightsPath()
|
||||
{
|
||||
return GetPluginDirectory() / L"spatial_weights.bin";
|
||||
}
|
||||
|
||||
uint64_t GetElapsedMs()
|
||||
@@ -587,6 +610,100 @@ void LogRed4ext(std::string_view aMessage)
|
||||
}
|
||||
}
|
||||
|
||||
bool IsUsableSpatialMultiplier(float aValue)
|
||||
{
|
||||
return std::isfinite(aValue) && aValue > 0.0f && aValue < 20.0f;
|
||||
}
|
||||
|
||||
void StoreGpsSpatialMultipliers(const std::array<float, 5>& aValues)
|
||||
{
|
||||
for (size_t index = 0; index < aValues.size(); ++index)
|
||||
{
|
||||
gGpsSpatialMultipliers[index].store(aValues[index], std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
void LogGpsSpatialMultipliers(std::string_view aPrefix, const std::array<float, 5>& aValues)
|
||||
{
|
||||
std::ostringstream line;
|
||||
line << aPrefix << " highway=" << aValues[0] << " road=" << aValues[1] << " gpsonly=" << aValues[2]
|
||||
<< " pavement=" << aValues[3] << " unknown=" << aValues[4];
|
||||
LogRed4ext(line.str());
|
||||
}
|
||||
|
||||
void MaybeReloadGpsSpatialWeights()
|
||||
{
|
||||
if (!kEnableGpsSpatialEdgeWeightPatch)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto nowMs = GetElapsedMs();
|
||||
if (nowMs < gGpsSpatialWeightsNextReloadCheckMs.load(std::memory_order_relaxed))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lock(gGpsSpatialWeightsMutex);
|
||||
if (nowMs < gGpsSpatialWeightsNextReloadCheckMs.load(std::memory_order_relaxed))
|
||||
{
|
||||
return;
|
||||
}
|
||||
gGpsSpatialWeightsNextReloadCheckMs.store(nowMs + kGpsSpatialWeightsReloadIntervalMs, std::memory_order_relaxed);
|
||||
|
||||
const auto path = GetSpatialWeightsPath();
|
||||
std::error_code error;
|
||||
const auto writeTime = std::filesystem::last_write_time(path, error);
|
||||
if (error)
|
||||
{
|
||||
if (gGpsSpatialWeightsFileKnown)
|
||||
{
|
||||
gGpsSpatialWeightsFileKnown = false;
|
||||
std::ostringstream line;
|
||||
line << "spatial weights file unavailable path=" << path.string() << " using current/default values";
|
||||
LogRed4ext(line.str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (gGpsSpatialWeightsFileKnown && writeTime == gGpsSpatialWeightsLastWriteTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<float, 5> values{};
|
||||
std::ifstream input(path, std::ios::binary);
|
||||
input.read(reinterpret_cast<char*>(values.data()), static_cast<std::streamsize>(values.size() * sizeof(float)));
|
||||
if (input.gcount() != static_cast<std::streamsize>(values.size() * sizeof(float)))
|
||||
{
|
||||
std::ostringstream line;
|
||||
line << "spatial weights reload skipped path=" << path.string() << " bytes=" << input.gcount()
|
||||
<< " expected=" << values.size() * sizeof(float);
|
||||
LogRed4ext(line.str());
|
||||
gGpsSpatialWeightsFileKnown = true;
|
||||
gGpsSpatialWeightsLastWriteTime = writeTime;
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto value : values)
|
||||
{
|
||||
if (!IsUsableSpatialMultiplier(value))
|
||||
{
|
||||
std::ostringstream line;
|
||||
line << "spatial weights reload skipped path=" << path.string() << " invalidValue=" << value;
|
||||
LogRed4ext(line.str());
|
||||
gGpsSpatialWeightsFileKnown = true;
|
||||
gGpsSpatialWeightsLastWriteTime = writeTime;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
StoreGpsSpatialMultipliers(values);
|
||||
gGpsSpatialWeightsFileKnown = true;
|
||||
gGpsSpatialWeightsLastWriteTime = writeTime;
|
||||
LogGpsSpatialMultipliers("spatial weights reloaded", values);
|
||||
}
|
||||
|
||||
uintptr_t GetImageBase()
|
||||
{
|
||||
return reinterpret_cast<uintptr_t>(GetModuleHandleW(nullptr));
|
||||
@@ -1035,15 +1152,15 @@ float GpsSpatialRoadClassMultiplier(char aCode)
|
||||
switch (aCode)
|
||||
{
|
||||
case 'H':
|
||||
return kGpsSpatialHighwayMultiplier;
|
||||
return gGpsSpatialMultipliers[0].load(std::memory_order_relaxed);
|
||||
case 'R':
|
||||
return kGpsSpatialRoadMultiplier;
|
||||
return gGpsSpatialMultipliers[1].load(std::memory_order_relaxed);
|
||||
case 'G':
|
||||
return kGpsSpatialGpsOnlyMultiplier;
|
||||
return gGpsSpatialMultipliers[2].load(std::memory_order_relaxed);
|
||||
case 'P':
|
||||
return kGpsSpatialPavementMultiplier;
|
||||
return gGpsSpatialMultipliers[3].load(std::memory_order_relaxed);
|
||||
default:
|
||||
return kGpsSpatialUnknownMultiplier;
|
||||
return gGpsSpatialMultipliers[4].load(std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4213,6 +4330,8 @@ int32_t DetourGpsRouteProducer(void* aGraph, void* aQuery, void* aOutResult)
|
||||
LogRed4ext(line.str());
|
||||
}
|
||||
|
||||
MaybeReloadGpsSpatialWeights();
|
||||
|
||||
GpsEdgeCostStats stats{};
|
||||
auto* previousStats = gActiveGpsEdgeCostStats;
|
||||
const auto previousRouteProducerCall = gActiveGpsRouteProducerCall;
|
||||
@@ -4905,6 +5024,21 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e
|
||||
const auto added = aSdk->gameStates->Add(aHandle, kGameStateRunning, &gRunningState);
|
||||
LogRed4ext(added ? "registered Running game-state callbacks" : "failed to register Running game-state callbacks");
|
||||
|
||||
if (kEnableGpsSpatialEdgeWeightPatch)
|
||||
{
|
||||
std::array<float, 5> values = {
|
||||
gGpsSpatialMultipliers[0].load(std::memory_order_relaxed),
|
||||
gGpsSpatialMultipliers[1].load(std::memory_order_relaxed),
|
||||
gGpsSpatialMultipliers[2].load(std::memory_order_relaxed),
|
||||
gGpsSpatialMultipliers[3].load(std::memory_order_relaxed),
|
||||
gGpsSpatialMultipliers[4].load(std::memory_order_relaxed),
|
||||
};
|
||||
std::ostringstream line;
|
||||
line << "spatial weights path=" << GetSpatialWeightsPath().string();
|
||||
LogRed4ext(line.str());
|
||||
LogGpsSpatialMultipliers("spatial weights defaults", values);
|
||||
}
|
||||
|
||||
if (kEnableGpsQueryLifecycleHooks)
|
||||
{
|
||||
AttachProbeHook("RunGPSQuery body 0x29bd128", kRunGpsQueryBodyRva,
|
||||
|
||||
Reference in New Issue
Block a user