From 2de4a41bec2c245c0ea153b4331e5ccef83a1f15 Mon Sep 17 00:00:00 2001 From: ohaiibuzzle <23693150+ohaiibuzzle@users.noreply.github.com> Date: Thu, 29 Jan 2026 19:04:57 +0700 Subject: [PATCH 1/2] test: insomnia hack --- PlayTools/PlayLoader.m | 58 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/PlayTools/PlayLoader.m b/PlayTools/PlayLoader.m index 73e18e1a..89ec126a 100644 --- a/PlayTools/PlayLoader.m +++ b/PlayTools/PlayLoader.m @@ -3,6 +3,7 @@ // PlayTools // +#include #include #include @@ -230,11 +231,60 @@ static int pt_unlink(char const* path) { return unlink(ue_fix_filename(path)); } +static NSMutableDictionary *thread_sleep_counters = nil; +static NSMutableDictionary *last_sleep_attempts = nil; +static dispatch_once_t thread_sleep_once; +static NSLock *thread_sleep_lock = nil; + +static int pt_usleep(useconds_t time) { + dispatch_once(&thread_sleep_once, ^{ + thread_sleep_counters = [NSMutableDictionary dictionary]; + last_sleep_attempts = [NSMutableDictionary dictionary]; + thread_sleep_lock = [[NSLock alloc] init]; + [thread_sleep_lock lock]; + }); + + int thread_id = pthread_mach_thread_np(pthread_self()); + NSNumber *threadKey = @(thread_id); + + int thread_sleep_counter = [thread_sleep_counters[threadKey] intValue]; + int last_sleep_attempt = [last_sleep_attempts[threadKey] intValue]; + + if (time == 100000) { + int timestamp = (int)[[NSDate date] timeIntervalSince1970]; + // If it sleeps too fast, increase counter + if (timestamp - last_sleep_attempt < 2) { + thread_sleep_counter++; + } else { + thread_sleep_counter = 1; + } + last_sleep_attempt = timestamp; + thread_sleep_counters[threadKey] = @(thread_sleep_counter); + last_sleep_attempts[threadKey] = @(last_sleep_attempt); + + } + + if (thread_sleep_counter > 100) { + // Stop this thread from spamming usleep calls + NSLog(@"[PC] Thread %i exceeded usleep limit. Seem sus, stopping this " + @"thread FOREVER", + thread_id); + + [thread_sleep_lock lock]; + [thread_sleep_lock unlock]; + + return 0; + } + + return usleep(time); +} + DYLD_INTERPOSE(pt_open, open) DYLD_INTERPOSE(pt_stat, stat) DYLD_INTERPOSE(pt_access, access) DYLD_INTERPOSE(pt_rename, rename) DYLD_INTERPOSE(pt_unlink, unlink) +DYLD_INTERPOSE(pt_usleep, usleep) @implementation PlayLoader @@ -250,6 +300,14 @@ static void __attribute__((constructor)) initialize(void) { if (ue_status == 2) { [PlayKeychain debugLogger: [NSString stringWithFormat:@"UnrealEngine Hooked"]]; } + + // Add an observer so we can unlock threads on app termination + [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification * _Nonnull note) { + [thread_sleep_lock unlock]; + }]; } @end From 9cd0480a1e9df4adbf38615287501e36458c9a0f Mon Sep 17 00:00:00 2001 From: ohaiibuzzle <23693150+ohaiibuzzle@users.noreply.github.com> Date: Fri, 30 Jan 2026 12:02:53 +0700 Subject: [PATCH 2/2] fix: allow sleep spamming block to be togglable from PlayCover --- PlayTools/PlayLoader.m | 74 +++++++++++++++++++----------------- PlayTools/PlaySettings.swift | 3 ++ 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/PlayTools/PlayLoader.m b/PlayTools/PlayLoader.m index 89ec126a..348ab022 100644 --- a/PlayTools/PlayLoader.m +++ b/PlayTools/PlayLoader.m @@ -244,36 +244,38 @@ static int pt_usleep(useconds_t time) { [thread_sleep_lock lock]; }); - int thread_id = pthread_mach_thread_np(pthread_self()); - NSNumber *threadKey = @(thread_id); - - int thread_sleep_counter = [thread_sleep_counters[threadKey] intValue]; - int last_sleep_attempt = [last_sleep_attempts[threadKey] intValue]; - - if (time == 100000) { - int timestamp = (int)[[NSDate date] timeIntervalSince1970]; - // If it sleeps too fast, increase counter - if (timestamp - last_sleep_attempt < 2) { - thread_sleep_counter++; - } else { - thread_sleep_counter = 1; - } - last_sleep_attempt = timestamp; - thread_sleep_counters[threadKey] = @(thread_sleep_counter); - last_sleep_attempts[threadKey] = @(last_sleep_attempt); + if ([[PlaySettings shared] blockSleepSpamming]) { + int thread_id = pthread_mach_thread_np(pthread_self()); + NSNumber *threadKey = @(thread_id); - } - - if (thread_sleep_counter > 100) { - // Stop this thread from spamming usleep calls - NSLog(@"[PC] Thread %i exceeded usleep limit. Seem sus, stopping this " - @"thread FOREVER", - thread_id); - - [thread_sleep_lock lock]; - [thread_sleep_lock unlock]; + int thread_sleep_counter = [thread_sleep_counters[threadKey] intValue]; + int last_sleep_attempt = [last_sleep_attempts[threadKey] intValue]; - return 0; + if (time == 100000) { + int timestamp = (int)[[NSDate date] timeIntervalSince1970]; + // If it sleeps too fast, increase counter + if (timestamp - last_sleep_attempt < 2) { + thread_sleep_counter++; + } else { + thread_sleep_counter = 1; + } + last_sleep_attempt = timestamp; + thread_sleep_counters[threadKey] = @(thread_sleep_counter); + last_sleep_attempts[threadKey] = @(last_sleep_attempt); + + } + + if (thread_sleep_counter > 100) { + // Stop this thread from spamming usleep calls + NSLog(@"[PC] Thread %i exceeded usleep limit. Seem sus, stopping this " + @"thread FOREVER", + thread_id); + + [thread_sleep_lock lock]; + [thread_sleep_lock unlock]; + + return 0; + } } return usleep(time); @@ -301,13 +303,15 @@ static void __attribute__((constructor)) initialize(void) { [PlayKeychain debugLogger: [NSString stringWithFormat:@"UnrealEngine Hooked"]]; } - // Add an observer so we can unlock threads on app termination - [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification - object:nil - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification * _Nonnull note) { - [thread_sleep_lock unlock]; - }]; + if ([[PlaySettings shared] blockSleepSpamming]) { + // Add an observer so we can unlock threads on app termination + [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification * _Nonnull note) { + [thread_sleep_lock unlock]; + }]; + } } @end diff --git a/PlayTools/PlaySettings.swift b/PlayTools/PlaySettings.swift index bd570b91..c20d6d32 100644 --- a/PlayTools/PlaySettings.swift +++ b/PlayTools/PlaySettings.swift @@ -93,6 +93,8 @@ let settings = PlaySettings.shared @objc lazy var limitMotionUpdateFrequency = settingsData.limitMotionUpdateFrequency @objc lazy var disableBuiltinMouse = settingsData.disableBuiltinMouse + + @objc lazy var blockSleepSpamming = settingsData.blockSleepSpamming } struct AppSettingsData: Codable { @@ -125,4 +127,5 @@ struct AppSettingsData: Codable { var resizableAspectRatioType = 0 var resizableAspectRatioWidth = 0 var resizableAspectRatioHeight = 0 + var blockSleepSpamming = false }