Files
ReSkate/Extension/World/local_population_controls.cpp
AyeZeeBB b763631b77 Trim per-frame multiplayer client work; size mesh pool to the GPU
Mesh streaming pool (fixes loading crashes on older GPUs)
- 1.0.0 forced MeshStreaming.PoolSize to 3.5 GiB on every card. An RX 580
  crashed while loading with DXGI_ERROR_DEVICE_RESET, and the oversized pool
  is the likely cause. The pool is now a quarter of the largest hardware
  adapter's dedicated video memory, capped at 3.5 GiB; cards of 6 GB or less
  keep the game's own 520 MB. The card, its memory and the chosen size are
  logged once at startup.

Remote players (busy servers)
- Transform interpolate is inline plain arithmetic: 22 -> 5.8 ns per joint.
  It was 4% of a busy server's client frame, since every player within
  200 m has the whole skeleton interpolated every frame.
- Players out of the camera's view and more than 8 m away are sampled at
  20 Hz, like far players. Their position still updates every frame for
  collision, sound and nametags, and a player coming into view is sampled
  that frame.

Expression hooks (run for every expression the game executes)
- No profiler zones in the expression hooks: taking a sample switched them
  on, inflating the very samples that measured the hooks.
- The throwdown lab pump does one sorted lookup of the running graph and
  skips everything else for graphs it doesn't watch (graph_throttle gets
  throttled_graph for this).

Settings and memory reads
- Script gameplay-setting and native-setting gets are served from small
  per-thread caches, keyed on the asset/key address, its first 8 name
  bytes and a new profile::Store::changes() counter, instead of a store
  lookup each call.
- Per-frame reads in the client tick, native settings and population
  controls use memory::peek instead of ReadProcessMemory.
- Script error logging probes each argument pointer once.
2026-10-02 21:07:43 -05:00

122 lines
6.6 KiB
C++

#include "Engine/Core/Log/logging.h"
#include "Extension/Profile/runtime_internal.h"
#include "local_population_controls.h"
#include "local_world_controls.h"
#include "Engine/Game/Build/20260929/population.h"
namespace dingosdk::profile_runtime {
PopulationRuntime& population_runtime() { static auto* r = new PopulationRuntime; return *r; }
bool population_identity(std::uintptr_t manager, int realm) {
std::uintptr_t vt{}, global{}; int actual{};
// Every population tick: the game's own manager global first, then that live object, peeked.
return realm >= 0 && realm <= 1 && manager &&
memory::peek(local_runtime().base + addr::population::managers + realm * 8, global) && global == manager &&
memory::peek(manager, vt) && vt == local_runtime().base + addr::population::manager_vtable &&
memory::peek(manager + 8, actual) && actual == realm;
}
bool population_config_identity(std::uintptr_t config) {
std::uintptr_t type{}; std::string name;
// The validated manager supplies a TrafficSystemConfig, but the asset name
// varies by map (BAM uses Gameplay/AI/BAM_TrafficSystemConfig). The exact
// native class identifies the field layout; the default asset name does not.
return memory::peek(config + 8, type) && type == local_runtime().base + addr::population::traffic_system_config_type &&
identifier(reinterpret_cast<void*>(config + 0x18), name);
}
// Every population tick, on the validated live config: peeked.
bool population_value(std::uintptr_t config, unsigned i, float& out) {
if (i == 0 || i == 3) return memory::peek(config + population_offsets[i], out) && std::isfinite(out) && out >= 0 && out <= 100;
std::uint32_t v{}; if (!memory::peek(config + population_offsets[i], v) || v > 1000) return false; out = static_cast<float>(v); return true;
}
bool population_write(std::uintptr_t config, unsigned i, float value) {
const auto integer = static_cast<std::uint32_t>(value);
const void* data = i == 0 || i == 3 ? static_cast<const void*>(&value) : &integer;
SIZE_T written{};
return WriteProcessMemory(GetCurrentProcess(), reinterpret_cast<void*>(config + population_offsets[i]), data, 4, &written) && written == 4;
}
void population_release_config(std::uintptr_t manager) {
auto& p = population_runtime();
const auto it = p.nodes.find(manager); if (it == p.nodes.end()) return;
const auto config = it->second.config; it->second.config = 0;
if (!config || std::any_of(p.nodes.begin(), p.nodes.end(), [&](const auto& n) { return n.second.config == config; })) return;
const auto saved = p.configs.find(config);
if (saved != p.configs.end() && population_config_identity(config)) {
for (unsigned i = 0; i < population_offsets.size(); ++i) {
float current{}; const auto& f = saved->second.fields[i];
if (f.owned && population_value(config, i, current) && current == f.applied) population_write(config, i, f.original);
}
}
p.configs.erase(config);
}
std::uintptr_t population_construct(std::uintptr_t manager, int realm, std::uintptr_t allocator) {
auto& p = population_runtime(); const auto result = p.construct(manager, realm, allocator);
PreserveError preserve;
try {
std::lock_guard lock(world_control_runtime().mutex);
if (population_identity(manager, realm)) p.nodes[manager] = PopulationNode{0,realm};
} catch (...) { dingosdk::logging::event(dingosdk::logging::Channel::world, "{\"event\":\"world_population_capture_failed\"}"); }
return result;
}
std::uintptr_t population_destroy(std::uintptr_t manager, unsigned flags) {
auto& p = population_runtime();
{ PreserveError preserve; std::lock_guard lock(world_control_runtime().mutex);
population_release_config(manager); p.nodes.erase(manager); }
return p.destroy(manager, flags);
}
int population_choice(unsigned kind) {
const auto& r = world_control_runtime();
if (r.map == WorldMap::none || !r.population_active.load(std::memory_order_acquire)) return -1;
return kind == 0 ? r.model.choices.traffic : r.model.choices.pedestrians;
}
void update_population_controls() {
auto& r = world_control_runtime(); auto& p = population_runtime(); std::lock_guard lock(r.mutex);
if (!r.population_active.load(std::memory_order_acquire)) return;
for (auto& [manager, node] : p.nodes) {
if (!population_identity(manager, node.realm)) continue;
for (unsigned kind = 0; kind < 2; ++kind) {
const auto choice = population_choice(kind);
if (choice < 0 && !node.owned[kind]) continue;
std::uint8_t current{};
if (!read(manager + 0x150 + kind, current) || current > 1) continue;
if (!node.owned[kind]) node.original[kind] = current != 0;
const bool desired = choice < 0 ? node.original[kind] : node.realm == 1 || choice != 0;
p.enable[kind](desired, node.realm);
node.owned[kind] = choice >= 0;
}
}
if (r.diagnostic_due && GetTickCount64() >= r.diagnostic_due) {
r.diagnostic_due = 0;
dingosdk::Json managers = dingosdk::Json::array();
for (const auto& [manager, node] : p.nodes) {
if (!population_identity(manager, node.realm)) continue;
std::array<std::uint8_t,3> flags{}; std::uintptr_t config{};
read(manager + 0x150, flags); read(manager + 0x108, config);
config &= ~std::uintptr_t{4};
std::string name;
if (population_config_identity(config)) identifier(reinterpret_cast<void*>(config + 0x18), name);
managers.push_back({{"realm",node.realm},{"flags",flags},{"config",config},{"asset",name}});
}
auto fields = dingosdk::Json::array();
for (unsigned i=0;i<atmosphere_controls.size();++i) {
const auto& control=atmosphere_controls[i];
const auto& reading=r.model.atmosphere[i];
const auto choice=r.model.choices.atmosphere.find(control.key);
if (choice==r.model.choices.atmosphere.end()) continue;
fields.push_back({{"control",control.key},{"requested",atmosphere_value_text(control,choice->second)},
{"observed",reading.value ? atmosphere_value_text(control,*reading.value) : "unavailable"},
{"varies",reading.varies},{"applied",reading.applied}});
}
dingosdk::logging::event(dingosdk::logging::Channel::world, dingosdk::Json{{"event","world_controls_observed"},{"environments",r.model.environments},{"fields",fields},
{"components",r.nodes.size()},{"population_ready",r.model.population_ready},
{"population",r.model.population},{"managers",managers},{"status",r.model.status}}.dump().c_str());
}
}
}