From 0458c097e63b55ac4e3d5898d62d13f403df746f Mon Sep 17 00:00:00 2001 From: Mikhail Babynichev Date: Tue, 14 Jul 2026 14:26:58 +0300 Subject: [PATCH] fix: behaviour when lazer starts before tosu and fails on memory --- packages/tosu/src/instances/lazerInstance.ts | 12 +++++++++++ packages/tosu/src/memory/lazer.ts | 2 +- packages/tsprocess/lib/functions.cc | 12 +++++++++++ packages/tsprocess/lib/memory/memory.h | 1 + packages/tsprocess/lib/memory/memory_linux.cc | 12 +++++++++++ .../tsprocess/lib/memory/memory_windows.cc | 21 +++++++++++++++++++ packages/tsprocess/src/process.ts | 4 ++++ 7 files changed, 63 insertions(+), 1 deletion(-) diff --git a/packages/tosu/src/instances/lazerInstance.ts b/packages/tosu/src/instances/lazerInstance.ts index d5cb8229..4fddd081 100644 --- a/packages/tosu/src/instances/lazerInstance.ts +++ b/packages/tosu/src/instances/lazerInstance.ts @@ -28,6 +28,18 @@ export class LazerInstance extends AbstractInstance { override async initiate() { try { + const startTime = this.process.getStartTime(); + const age = startTime > 0 ? Date.now() - startTime : 0; + const waitMs = Math.max(0, 15 * 1000 - age); + + if (waitMs > 0) { + wLogger.info( + `%${ClientType[this.client]}%`, + `Waiting %${(waitMs / 1000).toFixed(1)}s% for lazer startup before reading memory...` + ); + await sleep(waitMs); + } + const cacheFolder = getCachePath(); if (!fs.existsSync(cacheFolder)) await fsp.mkdir(cacheFolder, { recursive: true }); diff --git a/packages/tosu/src/memory/lazer.ts b/packages/tosu/src/memory/lazer.ts index b75e6220..b81e6de9 100644 --- a/packages/tosu/src/memory/lazer.ts +++ b/packages/tosu/src/memory/lazer.ts @@ -436,7 +436,7 @@ export class LazerMemory extends AbstractMemory { private scanPatterns: ScanPatterns = { scalingContainerTargetDrawSize: { - pattern: '00 00 80 44 00 00 40 44', + pattern: '01 01 00 00 00 00 80 44 00 00 40 44', offset: 0, nonZeroMask: false } diff --git a/packages/tsprocess/lib/functions.cc b/packages/tsprocess/lib/functions.cc index ff10ba04..3ce1d6fd 100644 --- a/packages/tsprocess/lib/functions.cc +++ b/packages/tsprocess/lib/functions.cc @@ -397,6 +397,17 @@ Napi::Value is_process_64bit(const Napi::CallbackInfo &args) { return Napi::Boolean::New(env, memory::is_process_64bit(process_id)); } +Napi::Value get_process_start_time(const Napi::CallbackInfo &args) { + Napi::Env env = args.Env(); + if (args.Length() < 1) { + Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException(); + return env.Null(); + } + + auto handle = reinterpret_cast(args[0].As().Int64Value()); + return Napi::Number::New(env, static_cast(memory::get_process_start_time(handle))); +} + Napi::Value get_process_path(const Napi::CallbackInfo &args) { Napi::Env env = args.Env(); if (args.Length() < 1) { @@ -560,6 +571,7 @@ Napi::Object init(Napi::Env env, Napi::Object exports) { exports["closeHandle"] = Napi::Function::New(env, close_handle); exports["findProcesses"] = Napi::Function::New(env, find_processes); exports["isProcessExist"] = Napi::Function::New(env, is_process_exist); + exports["getProcessStartTime"] = Napi::Function::New(env, get_process_start_time); exports["isProcess64bit"] = Napi::Function::New(env, is_process_64bit); exports["getProcessPath"] = Napi::Function::New(env, get_process_path); exports["getProcessCommandLine"] = Napi::Function::New(env, get_process_command_line); diff --git a/packages/tsprocess/lib/memory/memory.h b/packages/tsprocess/lib/memory/memory.h index d4eeafeb..9f68f5d4 100644 --- a/packages/tsprocess/lib/memory/memory.h +++ b/packages/tsprocess/lib/memory/memory.h @@ -36,6 +36,7 @@ void *open_process(uint32_t id); void close_handle(void *handle); bool is_process_64bit(uint32_t id); bool is_process_exist(void *process); +uint64_t get_process_start_time(void *process); std::string get_process_path(void *process); std::string get_process_command_line(void *process); std::string get_process_cwd(void *process); diff --git a/packages/tsprocess/lib/memory/memory_linux.cc b/packages/tsprocess/lib/memory/memory_linux.cc index 552645c4..ac5bd6c1 100644 --- a/packages/tsprocess/lib/memory/memory_linux.cc +++ b/packages/tsprocess/lib/memory/memory_linux.cc @@ -72,6 +72,18 @@ bool memory::is_process_exist(void *process) { return true; } +// Returns the process creation time as unix epoch milliseconds, or 0 on failure. +// /proc/ is created when the process starts, so its ctime approximates it. +uint64_t memory::get_process_start_time(void *process) { + const auto pid = reinterpret_cast(process); + struct stat sts; + const auto proc_path = "/proc/" + std::to_string(pid); + if (stat(proc_path.c_str(), &sts) != 0) { + return 0; + } + return static_cast(sts.st_ctime) * 1000ULL; +} + bool memory::is_process_64bit(uint32_t id) { const auto exe_path = "/proc/" + std::to_string(id) + "/exe"; diff --git a/packages/tsprocess/lib/memory/memory_windows.cc b/packages/tsprocess/lib/memory/memory_windows.cc index e9577168..9a407f73 100644 --- a/packages/tsprocess/lib/memory/memory_windows.cc +++ b/packages/tsprocess/lib/memory/memory_windows.cc @@ -73,6 +73,27 @@ bool memory::is_process_exist(void *handle) { return false; } +// Returns the process creation time as unix epoch milliseconds, or 0 on failure. +uint64_t memory::get_process_start_time(void *handle) { + FILETIME creation{}, exit{}, kernel{}, user{}; + if (!GetProcessTimes(handle, &creation, &exit, &kernel, &user)) { + return 0; + } + + ULARGE_INTEGER ticks; + ticks.LowPart = creation.dwLowDateTime; + ticks.HighPart = creation.dwHighDateTime; + + // FILETIME counts 100ns intervals since 1601-01-01. 116444736000000000 is the + // interval count between 1601 and the unix epoch (1970-01-01). + constexpr uint64_t epochDelta = 116444736000000000ULL; + if (ticks.QuadPart < epochDelta) { + return 0; + } + + return (ticks.QuadPart - epochDelta) / 10000ULL; +} + bool memory::is_process_64bit(uint32_t id) { HANDLE process_handle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, id); BOOL is_wow64 = FALSE; diff --git a/packages/tsprocess/src/process.ts b/packages/tsprocess/src/process.ts index f12ab008..a0ba9186 100644 --- a/packages/tsprocess/src/process.ts +++ b/packages/tsprocess/src/process.ts @@ -56,6 +56,10 @@ export class Process { return ProcessUtils.isProcessExist(handle); } + getStartTime(): number { + return ProcessUtils.getProcessStartTime(this.handle); + } + static isProcess64bit(pid: number): boolean { return ProcessUtils.isProcess64bit(pid); }