diff --git a/Engine/Core/Profiling/sampler.cpp b/Engine/Core/Profiling/sampler.cpp index 0f201cf6..0258ed18 100644 --- a/Engine/Core/Profiling/sampler.cpp +++ b/Engine/Core/Profiling/sampler.cpp @@ -202,14 +202,19 @@ std::string symbol_name(const Module& module, std::uintptr_t address) { info->MaxNameLen = 511; DWORD64 displacement{}; if (symbols_ready && SymFromAddr(symbol_session, address, &displacement, info) && info->Name[0]) { - // A release ReSkate.dll ships without its symbols, so all this finds in it is the nearest - // export, megabytes away: every function of ours then reads as one of three exports and - // a report cannot say which was slow. Unless the address is inside a symbol with a size - // (the real ones have one), give our own module's address instead: the release's symbols - // (build/release/ReSkate--symbols) name it afterwards. + // A release ReSkate.dll ships without its symbols (and a ReSkate.pdb left beside it from + // another build is not taken), so all this finds in it is the nearest export, megabytes + // away: every function of ours then reads as one of three exports and a report cannot + // say which was slow. An export is given a size too, up to the next one, so the size + // tells nothing: ask what the names came from. Without the module's own symbols, give + // its address instead: the release's symbols (build/release/ReSkate--symbols) + // name it afterwards. const auto here = reinterpret_cast(&waiting_marker); const bool own = module.base <= here && here < module.end; - if (!own || (info->Size && displacement < info->Size)) return module.name + "!" + info->Name; + IMAGEHLP_MODULEW64 loaded{}; + loaded.SizeOfStruct = sizeof(loaded); + const bool named = !own || (SymGetModuleInfoW64(symbol_session, module.base, &loaded) && loaded.SymType == SymPdb); + if (named) return module.name + "!" + info->Name; } return std::format("{}+0x{:x}", module.name, address - module.base); } diff --git a/Extension/Multiplayer/Steam/steam_transport.cpp b/Extension/Multiplayer/Steam/steam_transport.cpp index d7ba572f..81c5e140 100644 --- a/Extension/Multiplayer/Steam/steam_transport.cpp +++ b/Extension/Multiplayer/Steam/steam_transport.cpp @@ -656,8 +656,12 @@ bool SteamTransport::connect_peer(std::uint64_t id) { const auto connection = p.connect(p.sockets, &identity, virtual_port, static_cast(options.size()), options.data()); p.closed.erase(id); + // The link's name only labels it in diagnostics: the one already read, if any, and poll + // fills it in. Asking Steam here waited on the Steam client for ten milliseconds and more, + // a hitch each time a player came into reach. + const auto known = p.names.find(id); if (connection) - p.links.emplace(id, Impl::Link{connection, GetTickCount64(), false, name(id), + p.links.emplace(id, Impl::Link{connection, GetTickCount64(), false, known != p.names.end() ? known->second.text : std::string{}, p.lanes.setup(p.sockets, connection)}); p.publish_links(); return connection != 0; diff --git a/Extension/Objects/ParkEditor/park_editor_runtime.cpp b/Extension/Objects/ParkEditor/park_editor_runtime.cpp index b4b1d360..d204cc0d 100644 --- a/Extension/Objects/ParkEditor/park_editor_runtime.cpp +++ b/Extension/Objects/ParkEditor/park_editor_runtime.cpp @@ -10,6 +10,8 @@ #include "Extension/Objects/network_object_runtime.h" #include "park_document.h" #include "park_mods.h" +#include +#include namespace dingosdk::profile_runtime { namespace park_editor_detail { @@ -30,17 +32,53 @@ const profile::PlacedObject *find_object(const profile::ObjectLayout &objects, s return it == objects.end() ? nullptr : &*it; } std::atomic park_mod_list_visible{true}; -void refresh_park_mods() { - auto &e = editor_state(); - e.next_park_scan = GetTickCount64() + 5000; +// What is in the Mods folder: every mod's manifest is read to find the ones with parks, which +// with many mods takes ten milliseconds and more. +struct ParkScan { + std::vector mods; + std::vector legacy; +}; +ParkScan scan_parks(const std::filesystem::path &data_root, const std::filesystem::path &directory) { + ParkScan found; try { - e.park_mods = e.data_root.empty() ? std::vector{} : editor::list_park_mods(e.data_root); - e.legacy_parks = e.directory.empty() ? std::vector{} : editor::list_parks(e.directory); + if (!data_root.empty()) found.mods = editor::list_park_mods(data_root); + if (!directory.empty()) found.legacy = editor::list_parks(directory); } catch (...) { /* An unreadable Mods folder just lists nothing. */ } + return found; +} +void use_park_scan(ParkScan found) { + auto &e = editor_state(); + e.park_mods = std::move(found.mods); + e.legacy_parks = std::move(found.legacy); if (!e.project.empty() && std::none_of(e.park_mods.begin(), e.park_mods.end(), [&](const auto &mod) { return mod.folder == e.project; })) e.project.clear(); // The project was removed or renamed outside the game. } +// The look every few seconds runs on a thread of its own and is taken up when it is done +// (poll_park_mods): on the game's thread it was a hitch every five seconds. `park_scans` counts +// the looks asked for, so one that a later look has overtaken is dropped. +std::future park_scan; +std::uint64_t park_scans{}, park_scan_running{}; +// At once: after the player made, renamed or removed a park, when the list has to be right. +void refresh_park_mods() { + auto &e = editor_state(); + e.next_park_scan = GetTickCount64() + 5000; + ++park_scans; + use_park_scan(scan_parks(e.data_root, e.directory)); +} +void poll_park_mods() { + auto &e = editor_state(); + if (park_scan.valid()) { + if (park_scan.wait_for(std::chrono::seconds(0)) != std::future_status::ready) return; + auto found = park_scan.get(); + if (park_scan_running == park_scans) use_park_scan(std::move(found)); + return; + } + if (GetTickCount64() < e.next_park_scan) return; + e.next_park_scan = GetTickCount64() + 5000; + park_scan_running = ++park_scans; + park_scan = std::async(std::launch::async, [data_root = e.data_root, directory = e.directory] { return scan_parks(data_root, directory); }); +} // Multiplayer guests play on the host's objects; only the host swaps layouts. bool is_lobby_guest() { return placements_runtime().personal_document.has_value(); @@ -603,8 +641,7 @@ ParkEditorModel local_park_editor() { model.can_undo = idle() && !e.undo.empty(); model.can_redo = idle() && !e.redo.empty(); model.assets = e.assets; - if (park_mod_list_visible.load(std::memory_order_relaxed) && GetTickCount64() >= e.next_park_scan) - refresh_park_mods(); + if (park_mod_list_visible.load(std::memory_order_relaxed)) poll_park_mods(); for (const auto &mod : e.park_mods) model.park_mods.push_back({mod.folder, mod.details.title, mod.details.author, mod.details.version, mod.details.description, mod.enabled,