#if KV_EXIT_REGRESSION namespace game { // Boundary double for the actual QuitAct. Real UserService is verified by the Unity service host. public class PlayFabUserService : IUserService { internal int Revocations,Logouts; internal readonly TaskCompletionSource Observing=new(TaskCreationOptions.RunContinuationsAsynchronously); internal readonly TaskCompletionSource Observed=new(TaskCreationOptions.RunContinuationsAsynchronously); public void Logout() { Logouts++; } internal Task CloseKVForValidationAsync() { Revocations++; return Task.CompletedTask; } internal Task ObserveKVQuitAsync() { Observing.TrySetResult(true); return Observed.Task; } } } static class ControlledExitProbe { [System.Runtime.CompilerServices.ModuleInitializer] internal static void Run() { foreach(bool logout in new[]{true,false}) { GameCore.Timer.Reset(); var user=new game.PlayFabUserService(); var hub=new Hub(); var ads=new Ads(); var act=new game.QuitAct { userService=user,rtService=hub,config=new Config(),adsService=ads,soundMgr=new SoundMgr() }; game.QuitStage.Logout=logout; // Module initialization cannot block on code executing in the same module on another thread. // Complete the boundary doubles up front; the historical async Hub cases still run unchanged. hub.Gate.TrySetResult(true); user.Observed.TrySetResult(true); var task=act.StartAsync(); task.GetAwaiter().GetResult(); if(user.Revocations!=1 || user.Logouts!=(logout ? 1 : 0) || !user.Observing.Task.IsCompleted) throw new Exception("KV exit boundary was skipped."); if(!ads.Released || GameCore.Timer.Destroyed!=1) throw new Exception("Scene exit must continue after observation."); Console.WriteLine("PASS actual QuitAct controlled revoke / Hub / bounded-observer boundary, logout="+logout); } } } #endif