Keep launcher alive through Skate startup; trim per-frame client work

Launcher
- "Close the launcher when Skate starts" now hides the launcher and exits
  when Skate.exe does, instead of exiting as the splash opens. Outside a
  Steam library, Skate's protection closed the game (TerminateProcess 0x32)
  when the launcher exited during startup; players confirmed turning the
  setting off avoided it.
- STATUS tile warns when Steam isn't open or signed in that PLAY starts
  offline as Unknown Player (hidden when Offline mode is chosen or while
  launching).
- Setting description updated to match.
Startup interventions (20260929)
- exit_return 0x87755f2 -> 0x87fe7fb: the last LEA RSP,[RSP]; JMP stub
  before the PUSHFQ / LOCK CMPXCHG routine, as 0x87367cf was in 20260908,
  and the return address player logs show. Only enforced once a frame site
  is armed; this build's protector has no equivalent rep stosb frame clear.
Client frame cost (profiled 2026-10-02)
- Transform interpolate is inline plain arithmetic (~1% of an MP frame).
- Script gameplay-setting and native-setting gets are served from small
  per-thread caches keyed on asset/key address, name prefix and a new
  profile::Store::changes() counter, instead of a store lookup each call.
- Hot-path reads in client tick, native settings and population controls
  use memory::peek instead of ReadProcessMemory.
- Script error logging probes each argument pointer once.
This commit is contained in:
AyeZeeBB committed 2026-10-02 18:56:57 -05:00
1 parent ca1d82f636
commit b7932535e5
17 files changed
+177 -60

No files matched your search

+5 -1
View File
@@ -14,7 +14,7 @@ void script_error(std::uintptr_t, std::uintptr_t*, std::uintptr_t* arguments) {
PreserveError preserve;
// Authored graphs raise the same error every frame, from the same constant text: a text
// pointer already looked at is not read again (each character was a system call).
static std::mutex mutex; static std::set<std::string> seen; static std::set<std::uintptr_t> looked_at;
static std::mutex mutex; static std::set<std::string> seen; static std::set<std::uintptr_t> looked_at, arguments_seen;
std::lock_guard lock(mutex);
try {
// The message position is not known from the binding alone; take the
@@ -35,6 +35,10 @@ void script_error(std::uintptr_t, std::uintptr_t*, std::uintptr_t* arguments) {
for (std::size_t index = 0; arguments && index < 4 && message.empty(); ++index) {
std::uintptr_t argument{}, inner{};
if (!memory::peek(reinterpret_cast<std::uintptr_t>(arguments) + index * 8, argument) || argument < 0x10000) break;
// The same arguments come back every frame: each is probed once (read, not peeked:
// it is often not a pointer, and a failed peek costs an exception).
if (arguments_seen.contains(argument)) continue;
if (arguments_seen.size() < 4096) arguments_seen.insert(argument);
if (read(argument, inner) && inner >= 0x10000) message = printable(inner);
if (message.empty()) message = printable(argument);
}