Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of CustomClipboardImages v1.0.1
BepInEx/plugins/TutorialClipboardPages/TutorialClipboardPages.dll
Decompiled 20 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.Video; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("ScrapVisbility")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ScrapVisbility")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("8a6853bd-bdc9-4741-95c7-5aa2c8c6a6f9")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace YourFurnace.TutorialClipboardPages; [BepInPlugin("YourFurnace.TutorialClipboardPages", "TutorialClipboardPages", "1.0.25")] public class Plugin : BaseUnityPlugin { internal enum PageMediaType { Image, Video } internal class PageMedia { public int PageNumber; public string FilePath; public PageMediaType MediaType; public Texture Texture; } public const string PluginGuid = "YourFurnace.TutorialClipboardPages"; public const string PluginName = "TutorialClipboardPages"; public const string PluginVersion = "1.0.25"; internal static ManualLogSource Log; internal static Harmony HarmonyInstance; internal static ConfigEntry<string> FolderName; internal static ConfigEntry<bool> PreferDllFolder; internal static ConfigEntry<bool> PreferMp4OverPng; internal static ConfigEntry<int> MaxPagesToScan; internal static ConfigEntry<bool> StopAtFirstMissingPage; internal static ConfigEntry<bool> EnableExtendedPages; internal static ConfigEntry<bool> QERightIsForward; internal static ConfigEntry<int> ExtendedPreviousRawSlot; internal static ConfigEntry<int> ExtendedCurrentRawSlot; internal static ConfigEntry<int> ExtendedNextRawSlot; internal static ConfigEntry<bool> HidePreviousAndNextPageParts; internal static ConfigEntry<bool> EnableNetworkSync; internal static ConfigEntry<bool> AcceptClientPageSync; internal static ConfigEntry<bool> CloneMaterials; internal static ConfigEntry<float> RescanIntervalSeconds; internal static ConfigEntry<int> VideoTextureWidth; internal static ConfigEntry<int> VideoTextureHeight; internal static ConfigEntry<string> VideoAspectMode; internal static ConfigEntry<bool> LoopMp4Pages; internal static ConfigEntry<bool> KeepHiddenMp4VisualsPlaying; internal static ConfigEntry<bool> DebugLogging; internal static readonly Dictionary<int, PageMedia> Pages = new Dictionary<int, PageMedia>(); internal static string ActiveFolder = string.Empty; private const string PageTurnRequestMessageName = "YourFurnace.TutorialClipboardPages.PageTurnRequest.v1"; private const string PageStateMessageName = "YourFurnace.TutorialClipboardPages.PageState.v1"; private const string PageStateRequestMessageName = "YourFurnace.TutorialClipboardPages.PageStateRequest.v1"; private static readonly Dictionary<ulong, int> PendingNetworkPages = new Dictionary<ulong, int>(); private static readonly Dictionary<ulong, int> ServerAuthoritativePages = new Dictionary<ulong, int>(); private static Type clipboardItemType; private static Type grabbableObjectType; private static FieldInfo currentPageField; private static float nextScanTime; private static bool networkMessageRegistered; private static NetworkManager registeredNetworkManager; private static NetworkManager registeredCallbackNetworkManager; private static float nextServerStateHeartbeatTime; private static float nextClientStateRequestTime; private static float nextLateJoinTargetedReplayTime; private static readonly Dictionary<ulong, float> LateJoinPageReplayUntil = new Dictionary<ulong, float>(); private static int lastKnownAuthoritativePage = 1; private static bool hasKnownAuthoritativePage; private static float lastClientTurnRequestTime; private static int lastClientTurnRequestDelta; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; FolderName = ((BaseUnityPlugin)this).Config.Bind<string>("Images", "Image folder name", "TutorialClipboardPages", "Folder that contains page1.png, page2.mp4, page3.png, etc. This keeps the same folder name as older TutorialClipboardPages versions."); PreferDllFolder = ((BaseUnityPlugin)this).Config.Bind<bool>("Images", "Prefer folder beside DLL", true, "If true, the first checked path is a folder named TutorialClipboardPages beside this DLL."); PreferMp4OverPng = ((BaseUnityPlugin)this).Config.Bind<bool>("Images", "Prefer mp4 over png when both exist", true, "If true, page1.mp4 wins over page1.png when both exist for the same page number."); MaxPagesToScan = ((BaseUnityPlugin)this).Config.Bind<int>("Images", "Max pages to scan", 999, "Highest page number the mod will check."); StopAtFirstMissingPage = ((BaseUnityPlugin)this).Config.Bind<bool>("Images", "Stop at first missing page", true, "If true, the page list stops when it reaches the first missing page after page1. Keep page numbers unbroken."); EnableExtendedPages = ((BaseUnityPlugin)this).Config.Bind<bool>("Pages", "Enable more than four pages", true, "If true, page5+ works by managing an extended page number and only rendering the current page plane outside the vanilla 1-4 range."); QERightIsForward = ((BaseUnityPlugin)this).Config.Bind<bool>("Pages", "QE right is forward", true, "Used for left/right page flip direction. If Q/E turns backwards, set this false."); ExtendedPreviousRawSlot = ((BaseUnityPlugin)this).Config.Bind<int>("Pages", "Extended previous raw slot", 2, "Which vanilla Manual slot should show the previous curled-up page when on page5+. Updated default is 2."); ExtendedCurrentRawSlot = ((BaseUnityPlugin)this).Config.Bind<int>("Pages", "Extended current raw slot", 4, "Which vanilla Manual slot is the readable current page when on page5+. Default 4 based on testing."); ExtendedNextRawSlot = ((BaseUnityPlugin)this).Config.Bind<int>("Pages", "Extended next raw slot", 3, "Which vanilla Manual slot should show the next page under the current page when on page5+. Updated default is 3."); HidePreviousAndNextPageParts = ((BaseUnityPlugin)this).Config.Bind<bool>("Pages", "Hide previous and next page parts", true, "If true, only the current readable page plane is shown. Previous curled page and next under-page visuals are hidden for both vanilla pages 1-4 and extended page5+."); EnableNetworkSync = ((BaseUnityPlugin)this).Config.Bind<bool>("Network", "Sync extended pages over network", true, "If true, page changes are synced with host and clients using a small custom Netcode message. Required for page5+ multiplayer sync."); AcceptClientPageSync = ((BaseUnityPlugin)this).Config.Bind<bool>("Network", "Accept page turns from clients", true, "If true, clients can tell the host which clipboard page they flipped to. Keep true unless you only want the host to control clipboard pages."); CloneMaterials = ((BaseUnityPlugin)this).Config.Bind<bool>("Replacement", "Clone clipboard page materials", true, "If true, clone Manual1-Manual4 materials before replacing textures."); RescanIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "Rescan interval seconds", 0.2f, "How often the mod refreshes clipboard page textures."); VideoTextureWidth = ((BaseUnityPlugin)this).Config.Bind<int>("Video", "MP4 render texture width", 900, "Render texture width used for MP4 pages. Vanilla manual textures are 900x1260."); VideoTextureHeight = ((BaseUnityPlugin)this).Config.Bind<int>("Video", "MP4 render texture height", 1260, "Render texture height used for MP4 pages. Vanilla manual textures are 900x1260."); VideoAspectMode = ((BaseUnityPlugin)this).Config.Bind<string>("Video", "MP4 aspect mode", "FitInside", "VideoPlayer aspect ratio mode. Good values: FitInside, FitVertically, FitHorizontally, Stretch."); LoopMp4Pages = ((BaseUnityPlugin)this).Config.Bind<bool>("Video", "Loop mp4 pages", true, "If true, MP4 pages loop visually."); KeepHiddenMp4VisualsPlaying = ((BaseUnityPlugin)this).Config.Bind<bool>("Video", "Keep hidden MP4 visuals playing", true, "If true, MP4 pages keep advancing visually even while not displayed. Audio is always disabled."); DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Debug logging", false, "Logs page turns, material remapping, hidden renderer state, and video state."); ResolveTypes(); LoadPages(); PatchPageTurnMethods(); TryRegisterNetworkMessages(); SceneManager.sceneLoaded += OnSceneLoaded; nextScanTime = 0f; Log.LogInfo((object)string.Format("{0} {1} loaded. Loaded {2} custom clipboard page file(s). Server-authoritative page turns enabled. MP4 audio disabled.", "TutorialClipboardPages", "1.0.25", Pages.Count)); } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; try { Harmony harmonyInstance = HarmonyInstance; if (harmonyInstance != null) { harmonyInstance.UnpatchSelf(); } } catch { } try { if ((Object)(object)registeredCallbackNetworkManager != (Object)null) { registeredCallbackNetworkManager.OnClientConnectedCallback -= OnClientConnectedForPageSync; } } catch { } CleanupLoadedImages(); } private void Update() { TryRegisterNetworkMessages(); BroadcastCurrentPageStateForLateJoiners(); ReplayPageStateToRecentlyJoinedClients(); RequestCurrentPageStateFromServerHeartbeat(); if (!(Time.realtimeSinceStartup < nextScanTime)) { nextScanTime = Time.realtimeSinceStartup + Mathf.Max(0.05f, RescanIntervalSeconds.Value); ApplyToAllClipboards(); } } private static void OnSceneLoaded(Scene scene, LoadSceneMode mode) { nextScanTime = 0f; nextClientStateRequestTime = 0f; ResolveTypes(); LoadPages(); } private static void ResolveTypes() { try { clipboardItemType = FindTypeByName("ClipboardItem"); grabbableObjectType = FindTypeByName("GrabbableObject"); currentPageField = ((clipboardItemType != null) ? clipboardItemType.GetField("currentPage", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) : null); if (DebugLogging != null && DebugLogging.Value) { Log.LogInfo((object)("Clipboard type: " + ((clipboardItemType != null) ? clipboardItemType.FullName : "null"))); Log.LogInfo((object)("GrabbableObject type: " + ((grabbableObjectType != null) ? grabbableObjectType.FullName : "null"))); Log.LogInfo((object)("currentPage field: " + (currentPageField != null))); } } catch (Exception ex) { if (DebugLogging != null && DebugLogging.Value) { Log.LogWarning((object)("ResolveTypes failed: " + ex.Message)); } } } private static Type FindTypeByName(string typeName) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { Type type = assembly.GetTypes().FirstOrDefault((Type t) => t.Name == typeName || t.FullName == typeName); if (type != null) { return type; } } catch { } } return null; } private static void PatchPageTurnMethods() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown try { HarmonyInstance = new Harmony("YourFurnace.TutorialClipboardPages"); if (!(grabbableObjectType == null)) { PatchGrabbableMethod("ItemActivate", new Type[2] { typeof(bool), typeof(bool) }, "ItemActivatePrefix", "ItemActivatePostfix"); PatchGrabbableMethod("ItemInteractLeftRight", new Type[1] { typeof(bool) }, "ItemInteractLeftRightPrefix", "ItemInteractLeftRightPostfix"); PatchGrabbableMethod("ItemInteractLeftRightOnClient", new Type[1] { typeof(bool) }, "ItemInteractLeftRightPrefix", "ItemInteractLeftRightPostfix"); if (DebugLogging.Value) { Log.LogInfo((object)"Clipboard page-turn patches installed."); } } } catch (Exception ex) { Log.LogWarning((object)("Failed to patch page turn methods. Extended pages may not turn correctly: " + ex.Message)); } } private static void PatchGrabbableMethod(string methodName, Type[] args, string prefixName, string postfixName) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_0063: Expected O, but got Unknown try { MethodInfo method = grabbableObjectType.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, args, null); if (!(method == null)) { MethodInfo method2 = typeof(Plugin).GetMethod(prefixName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo method3 = typeof(Plugin).GetMethod(postfixName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); HarmonyInstance.Patch((MethodBase)method, new HarmonyMethod(method2), new HarmonyMethod(method3), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); if (DebugLogging.Value) { Log.LogInfo((object)("Patched " + method.DeclaringType.FullName + "." + method.Name)); } } } catch (Exception ex) { if (DebugLogging.Value) { Log.LogWarning((object)("Could not patch " + methodName + ": " + ex.Message)); } } } public static bool ItemActivatePrefix(object __instance, bool used, bool buttonDown) { if (!buttonDown) { return true; } return HandlePageTurn(__instance, 1); } public static void ItemActivatePostfix(object __instance, bool used, bool buttonDown) { if (buttonDown && !IsNetworkAuthoritativeActive()) { SyncStateFromVanillaRaw(__instance); } } public static bool ItemInteractLeftRightPrefix(object __instance, bool right) { int delta = ((right == QERightIsForward.Value) ? 1 : (-1)); return HandlePageTurn(__instance, delta); } public static void ItemInteractLeftRightPostfix(object __instance, bool right) { if (!IsNetworkAuthoritativeActive()) { SyncStateFromVanillaRaw(__instance); } } private static bool HandlePageTurn(object instance, int delta) { try { if (!IsClipboardInstance(instance)) { return true; } Component val = (Component)((instance is Component) ? instance : null); if ((Object)(object)val == (Object)null) { return true; } int maxPageNumber = GetMaxPageNumber(); if (maxPageNumber <= 0) { return true; } if (IsNetworkAuthoritativeActive()) { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton != (Object)null && singleton.IsServer) { ApplyServerAuthoritativeTurn(val, instance, delta, broadcast: true, 0uL); } else { SendClientTurnRequest(val.gameObject, delta); } return false; } if (!EnableExtendedPages.Value || Pages.Count <= 4) { return true; } ClipboardPageState state = GetState(val); state.SyncFromRaw(instance, respectSuppress: false); int num = Mathf.Clamp(state.ExtendedCurrentPage, 1, maxPageNumber); int num2 = Mathf.Clamp(num + delta, 1, maxPageNumber); if (num2 == num) { return false; } bool flag = num <= 4 && num2 > 4; bool flag2 = num > 4 && num2 <= 4; bool flag3 = num > 4; if (flag || flag2 || flag3) { state.ExtendedCurrentPage = num2; state.LastRawPage = ((num2 > 4) ? Mathf.Clamp(ExtendedCurrentRawSlot.Value, 1, 4) : Mathf.Clamp(num2, 1, 4)); state.SuppressRawSyncUntil = Time.realtimeSinceStartup + 0.35f; WriteCurrentClipboardPage(instance, state.LastRawPage); ClipboardPageVideoController component = val.gameObject.GetComponent<ClipboardPageVideoController>(); if ((Object)(object)component != (Object)null) { component.ForceRefreshSoon(); } if (DebugLogging.Value) { Log.LogInfo((object)$"LOCAL EXT page turn {num} -> {num2}; forced raw slot={state.LastRawPage}; delta={delta}"); } TryPlayExtendedFlipSound(val); return false; } } catch (Exception ex) { if (DebugLogging.Value) { Log.LogWarning((object)("HandlePageTurn failed: " + ex.Message)); } } return true; } private static void SyncStateFromVanillaRaw(object instance) { try { if (!IsClipboardInstance(instance)) { return; } Component val = (Component)((instance is Component) ? instance : null); if (!((Object)(object)val == (Object)null)) { ClipboardPageState state = GetState(val); state.SyncFromRaw(instance, respectSuppress: true); ClipboardPageVideoController component = val.gameObject.GetComponent<ClipboardPageVideoController>(); if ((Object)(object)component != (Object)null) { component.ForceRefreshSoon(); } } } catch { } } private static bool IsClipboardInstance(object instance) { try { if (instance == null || clipboardItemType == null) { return false; } return clipboardItemType.IsInstanceOfType(instance); } catch { return false; } } private static ClipboardPageState GetState(Component component) { ClipboardPageState clipboardPageState = component.gameObject.GetComponent<ClipboardPageState>(); if ((Object)(object)clipboardPageState == (Object)null) { clipboardPageState = component.gameObject.AddComponent<ClipboardPageState>(); } if (clipboardPageState.ExtendedCurrentPage <= 0) { int num = ReadCurrentClipboardPage(component); clipboardPageState.ExtendedCurrentPage = Mathf.Clamp(num, 1, Mathf.Max(1, GetMaxPageNumber())); clipboardPageState.LastRawPage = Mathf.Clamp(num, 1, 4); } return clipboardPageState; } internal static int ReadCurrentClipboardPage(object clipboardItem) { try { if (clipboardItem == null || currentPageField == null) { return 1; } object value = currentPageField.GetValue(clipboardItem); if (value is int num) { return Mathf.Clamp(num, 1, 4); } if (value != null && int.TryParse(value.ToString(), out var result)) { return Mathf.Clamp(result, 1, 4); } } catch { } return 1; } private static void WriteCurrentClipboardPage(object clipboardItem, int rawPage) { try { if (clipboardItem != null && !(currentPageField == null)) { currentPageField.SetValue(clipboardItem, Mathf.Clamp(rawPage, 1, 4)); } } catch { } } internal static int ToVanillaRawPage(int extendedPage) { return (Mathf.Max(1, extendedPage) - 1) % 4 + 1; } internal static int GetMaxPageNumber() { if (Pages.Count == 0) { return 0; } return Pages.Keys.Max(); } private static void TryPlayExtendedFlipSound(Component clipboardComponent) { if ((Object)(object)clipboardComponent == (Object)null) { return; } try { AudioSource[] componentsInChildren = clipboardComponent.GetComponentsInChildren<AudioSource>(true); if (componentsInChildren == null || componentsInChildren.Length == 0) { return; } AudioSource val = null; AudioSource[] array = componentsInChildren; foreach (AudioSource val2 in array) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.clip == (Object)null) && !val2.loop) { string text = ((((Object)val2.clip).name != null) ? ((Object)val2.clip).name.ToLowerInvariant() : string.Empty); if (text.Contains("page") || text.Contains("paper") || text.Contains("flip") || text.Contains("manual")) { val = val2; break; } if ((Object)(object)val == (Object)null && val2.clip.length > 0f && val2.clip.length <= 2f) { val = val2; } } } if ((Object)(object)val != (Object)null && (Object)(object)val.clip != (Object)null) { val.PlayOneShot(val.clip); } } catch (Exception ex) { if (DebugLogging.Value) { Log.LogWarning((object)("TryPlayExtendedFlipSound failed: " + ex.Message)); } } } private static bool IsNetworkAuthoritativeActive() { try { if (EnableNetworkSync == null || !EnableNetworkSync.Value) { return false; } NetworkManager singleton = NetworkManager.Singleton; return (Object)(object)singleton != (Object)null && singleton.IsListening && singleton.CustomMessagingManager != null; } catch { return false; } } private static void TryRegisterNetworkMessages() { //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Expected O, but got Unknown //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Expected O, but got Unknown try { if (EnableNetworkSync == null || !EnableNetworkSync.Value) { return; } NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.CustomMessagingManager != null && (!networkMessageRegistered || !((Object)(object)registeredNetworkManager == (Object)(object)singleton))) { try { singleton.CustomMessagingManager.UnregisterNamedMessageHandler("YourFurnace.TutorialClipboardPages.PageTurnRequest.v1"); } catch { } try { singleton.CustomMessagingManager.UnregisterNamedMessageHandler("YourFurnace.TutorialClipboardPages.PageState.v1"); } catch { } try { singleton.CustomMessagingManager.UnregisterNamedMessageHandler("YourFurnace.TutorialClipboardPages.PageStateRequest.v1"); } catch { } singleton.CustomMessagingManager.RegisterNamedMessageHandler("YourFurnace.TutorialClipboardPages.PageTurnRequest.v1", new HandleNamedMessageDelegate(OnReceiveTurnRequestMessage)); singleton.CustomMessagingManager.RegisterNamedMessageHandler("YourFurnace.TutorialClipboardPages.PageState.v1", new HandleNamedMessageDelegate(OnReceivePageStateMessage)); singleton.CustomMessagingManager.RegisterNamedMessageHandler("YourFurnace.TutorialClipboardPages.PageStateRequest.v1", new HandleNamedMessageDelegate(OnReceivePageStateRequestMessage)); registeredNetworkManager = singleton; networkMessageRegistered = true; RegisterConnectionCallback(singleton); if (DebugLogging != null && DebugLogging.Value) { Log.LogInfo((object)"Registered TutorialClipboardPages server-authoritative network messages."); } } } catch (Exception ex) { networkMessageRegistered = false; registeredNetworkManager = null; if (DebugLogging != null && DebugLogging.Value) { Log.LogWarning((object)("TryRegisterNetworkMessages failed: " + ex.Message)); } } } private static void RegisterConnectionCallback(NetworkManager manager) { try { if ((Object)(object)manager == (Object)null || (Object)(object)registeredCallbackNetworkManager == (Object)(object)manager) { return; } if ((Object)(object)registeredCallbackNetworkManager != (Object)null) { try { registeredCallbackNetworkManager.OnClientConnectedCallback -= OnClientConnectedForPageSync; } catch { } } try { manager.OnClientConnectedCallback -= OnClientConnectedForPageSync; } catch { } manager.OnClientConnectedCallback += OnClientConnectedForPageSync; registeredCallbackNetworkManager = manager; } catch (Exception ex) { if (DebugLogging != null && DebugLogging.Value) { Log.LogWarning((object)("RegisterConnectionCallback failed: " + ex.Message)); } } } private static void OnClientConnectedForPageSync(ulong clientId) { try { if (!IsNetworkAuthoritativeActive()) { return; } NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsServer) { LateJoinPageReplayUntil[clientId] = Time.realtimeSinceStartup + 12f; nextLateJoinTargetedReplayTime = 0f; ulong num = 0uL; int serverAuthoritativePage = GetServerAuthoritativePage(num); SendAuthoritativePageStateToClient(clientId, num, serverAuthoritativePage); if (DebugLogging != null && DebugLogging.Value) { Log.LogInfo((object)$"Queued repeated clipboard page state sync for connected client {clientId}. clipboard={num}, page={serverAuthoritativePage}"); } } } catch (Exception ex) { if (DebugLogging != null && DebugLogging.Value) { Log.LogWarning((object)("OnClientConnectedForPageSync failed: " + ex.Message)); } } } private static int GetServerAuthoritativePage(ulong clipboardId) { int num = Mathf.Max(1, GetMaxPageNumber()); try { if (ServerAuthoritativePages.TryGetValue(clipboardId, out var value)) { return Mathf.Clamp(value, 1, num); } if (hasKnownAuthoritativePage) { return Mathf.Clamp(lastKnownAuthoritativePage, 1, num); } GameObject val = FindClipboardByNetworkId(clipboardId); if ((Object)(object)val != (Object)null) { ClipboardPageState component = val.GetComponent<ClipboardPageState>(); if ((Object)(object)component != (Object)null && component.ExtendedCurrentPage > 0) { int num2 = Mathf.Clamp(component.ExtendedCurrentPage, 1, num); RememberAuthoritativePage(clipboardId, num2); return num2; } object clipboardItem = null; if (clipboardItemType != null) { clipboardItem = val.GetComponent(clipboardItemType); } int num3 = Mathf.Clamp(ReadCurrentClipboardPage(clipboardItem), 1, Mathf.Min(4, num)); RememberAuthoritativePage(clipboardId, num3); return num3; } } catch { } return Mathf.Clamp(lastKnownAuthoritativePage, 1, num); } private static void RememberAuthoritativePage(ulong clipboardId, int page) { int num = Mathf.Max(1, GetMaxPageNumber()); page = Mathf.Clamp(page, 1, num); ServerAuthoritativePages[clipboardId] = page; lastKnownAuthoritativePage = page; hasKnownAuthoritativePage = true; } private static void BroadcastCurrentPageStateForLateJoiners() { try { if (IsNetworkAuthoritativeActive()) { NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsServer && !(Time.realtimeSinceStartup < nextServerStateHeartbeatTime)) { nextServerStateHeartbeatTime = Time.realtimeSinceStartup + 2f; ulong clipboardId = 0uL; int serverAuthoritativePage = GetServerAuthoritativePage(clipboardId); SendAuthoritativePageState(clipboardId, serverAuthoritativePage); } } } catch (Exception ex) { if (DebugLogging != null && DebugLogging.Value) { Log.LogWarning((object)("BroadcastCurrentPageStateForLateJoiners failed: " + ex.Message)); } } } private static void ReplayPageStateToRecentlyJoinedClients() { try { if (!IsNetworkAuthoritativeActive()) { return; } NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsServer || singleton.CustomMessagingManager == null || LateJoinPageReplayUntil.Count == 0 || Time.realtimeSinceStartup < nextLateJoinTargetedReplayTime) { return; } nextLateJoinTargetedReplayTime = Time.realtimeSinceStartup + 0.75f; ulong clipboardId = 0uL; int serverAuthoritativePage = GetServerAuthoritativePage(clipboardId); float realtimeSinceStartup = Time.realtimeSinceStartup; List<ulong> list = new List<ulong>(); foreach (KeyValuePair<ulong, float> item in LateJoinPageReplayUntil) { ulong key = item.Key; if (realtimeSinceStartup > item.Value) { list.Add(key); } else if (key != singleton.LocalClientId) { bool flag = true; try { flag = singleton.ConnectedClientsIds.Contains(key); } catch { } if (!flag) { list.Add(key); } else { SendAuthoritativePageStateToClient(key, clipboardId, serverAuthoritativePage); } } } foreach (ulong item2 in list) { LateJoinPageReplayUntil.Remove(item2); } } catch (Exception ex) { if (DebugLogging != null && DebugLogging.Value) { Log.LogWarning((object)("ReplayPageStateToRecentlyJoinedClients failed: " + ex.Message)); } } } private static void RequestCurrentPageStateFromServerHeartbeat() { try { if (IsNetworkAuthoritativeActive()) { NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && !singleton.IsServer && singleton.CustomMessagingManager != null && !(Time.realtimeSinceStartup < nextClientStateRequestTime)) { nextClientStateRequestTime = Time.realtimeSinceStartup + 1.25f; SendClientPageStateRequest(0uL); } } } catch (Exception ex) { if (DebugLogging != null && DebugLogging.Value) { Log.LogWarning((object)("RequestCurrentPageStateFromServerHeartbeat failed: " + ex.Message)); } } } private static void SendClientPageStateRequest(ulong clipboardId) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) try { if (!IsNetworkAuthoritativeActive()) { return; } NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && !singleton.IsServer && singleton.CustomMessagingManager != null) { FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(16, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref clipboardId, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessage("YourFurnace.TutorialClipboardPages.PageStateRequest.v1", 0uL, val, (NetworkDelivery)3); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } if (DebugLogging != null && DebugLogging.Value) { Log.LogInfo((object)$"Client requested authoritative clipboard page. clipboard={clipboardId}"); } } } catch (Exception ex) { if (DebugLogging != null && DebugLogging.Value) { Log.LogWarning((object)("SendClientPageStateRequest failed: " + ex.Message)); } } } private static void OnReceiveTurnRequestMessage(ulong senderClientId, FastBufferReader reader) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) try { if (!IsNetworkAuthoritativeActive()) { return; } NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsServer || (!AcceptClientPageSync.Value && senderClientId != singleton.LocalClientId)) { return; } ulong num = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives)); int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num2, default(ForPrimitives)); num2 = ((num2 >= 0) ? 1 : (-1)); GameObject val = FindClipboardByNetworkId(num); if (!((Object)(object)val == (Object)null)) { object obj = null; if (clipboardItemType != null) { obj = val.GetComponent(clipboardItemType); } Component val2 = (Component)((obj is Component) ? obj : null); if ((Object)(object)val2 == (Object)null) { val2 = val.GetComponent<Component>(); } ApplyServerAuthoritativeTurn(val2, obj, num2, broadcast: true, num); if (DebugLogging.Value) { Log.LogInfo((object)$"Server accepted client {senderClientId} page turn request. clipboard={num}, delta={num2}"); } } } catch (Exception ex) { if (DebugLogging != null && DebugLogging.Value) { Log.LogWarning((object)("OnReceiveTurnRequestMessage failed: " + ex.Message)); } } } private static void OnReceivePageStateRequestMessage(ulong senderClientId, FastBufferReader reader) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) try { if (!IsNetworkAuthoritativeActive()) { return; } NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsServer) { ulong num = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives)); num = 0uL; int serverAuthoritativePage = GetServerAuthoritativePage(num); SendAuthoritativePageStateToClient(senderClientId, num, serverAuthoritativePage); if (DebugLogging != null && DebugLogging.Value) { Log.LogInfo((object)$"Server answered clipboard page request from client {senderClientId}. clipboard={num}, page={serverAuthoritativePage}"); } } } catch (Exception ex) { if (DebugLogging != null && DebugLogging.Value) { Log.LogWarning((object)("OnReceivePageStateRequestMessage failed: " + ex.Message)); } } } private static void OnReceivePageStateMessage(ulong senderClientId, FastBufferReader reader) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) try { if (IsNetworkAuthoritativeActive()) { ulong num = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives)); int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num2, default(ForPrimitives)); num = 0uL; int num3 = Mathf.Max(1, GetMaxPageNumber()); num2 = Mathf.Clamp(num2, 1, num3); RememberAuthoritativePage(num, num2); PendingNetworkPages[num] = num2; PendingNetworkPages[0uL] = num2; ApplyNetworkPage(num, num2); nextScanTime = 0f; if (DebugLogging.Value) { Log.LogInfo((object)$"Received authoritative page state from {senderClientId}: clipboard={num}, page={num2}"); } } } catch (Exception ex) { if (DebugLogging != null && DebugLogging.Value) { Log.LogWarning((object)("OnReceivePageStateMessage failed: " + ex.Message)); } } } private static void SendClientTurnRequest(GameObject clipboardObject, int delta) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) try { if (!IsNetworkAuthoritativeActive()) { return; } NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.IsServer) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (!(realtimeSinceStartup - lastClientTurnRequestTime < 0.08f) || delta != lastClientTurnRequestDelta) { lastClientTurnRequestTime = realtimeSinceStartup; lastClientTurnRequestDelta = delta; TryRegisterNetworkMessages(); ulong num = 0uL; FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(32, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref num, default(ForPrimitives)); int num2 = ((delta >= 0) ? 1 : (-1)); ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num2, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessage("YourFurnace.TutorialClipboardPages.PageTurnRequest.v1", 0uL, val, (NetworkDelivery)3); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } if (DebugLogging.Value) { Log.LogInfo((object)$"Client sent page turn request to server. clipboard={num}, delta={delta}"); } } } catch (Exception ex) { if (DebugLogging != null && DebugLogging.Value) { Log.LogWarning((object)("SendClientTurnRequest failed: " + ex.Message)); } } } private static void ApplyServerAuthoritativeTurn(Component clipboardComponent, object clipboardItem, int delta, bool broadcast, ulong forcedClipboardId = 0uL) { try { if ((Object)(object)clipboardComponent == (Object)null) { return; } int num = Mathf.Max(1, GetMaxPageNumber()); if (num > 0) { ClipboardPageState state = GetState(clipboardComponent); if (!ServerAuthoritativePages.TryGetValue(forcedClipboardId, out var value)) { value = Mathf.Clamp((state.ExtendedCurrentPage <= 0) ? 1 : state.ExtendedCurrentPage, 1, num); } int num2 = Mathf.Clamp(value + ((delta >= 0) ? 1 : (-1)), 1, num); RememberAuthoritativePage(forcedClipboardId, num2); ApplyNetworkPage(forcedClipboardId, num2); if (DebugLogging.Value) { Log.LogInfo((object)$"SERVER AUTH page turn {value} -> {num2}; clipboard={forcedClipboardId}; delta={delta}"); } TryPlayExtendedFlipSound(clipboardComponent); if (broadcast) { SendAuthoritativePageState(forcedClipboardId, num2); } } } catch (Exception ex) { if (DebugLogging != null && DebugLogging.Value) { Log.LogWarning((object)("ApplyServerAuthoritativeTurn failed: " + ex.Message)); } } } private static void SendAuthoritativePageState(ulong clipboardId, int page) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) try { if (!IsNetworkAuthoritativeActive()) { return; } NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.CustomMessagingManager != null && singleton.IsServer) { int num = Mathf.Max(1, GetMaxPageNumber()); page = Mathf.Clamp(page, 1, num); RememberAuthoritativePage(clipboardId, page); FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(32, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref clipboardId, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref page, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessageToAll("YourFurnace.TutorialClipboardPages.PageState.v1", val, (NetworkDelivery)3); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } if (DebugLogging.Value) { Log.LogInfo((object)$"Server broadcast authoritative page state. clipboard={clipboardId}, page={page}"); } } } catch (Exception ex) { if (DebugLogging != null && DebugLogging.Value) { Log.LogWarning((object)("SendAuthoritativePageState failed: " + ex.Message)); } } } private static ulong GetClipboardNetworkId(GameObject clipboardObject) { return 0uL; } private static bool IsSceneInstance(Component component) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)component == (Object)null || (Object)(object)component.gameObject == (Object)null) { return false; } Scene scene = component.gameObject.scene; return ((Scene)(ref scene)).IsValid() && ((Scene)(ref scene)).isLoaded; } catch { return false; } } private static GameObject FindClipboardByNetworkId(ulong clipboardId) { //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) GameObject val = null; try { if (clipboardItemType != null) { Object[] array = Object.FindObjectsOfType(clipboardItemType); Object[] array2 = array; foreach (Object val2 in array2) { Component val3 = (Component)(object)((val2 is Component) ? val2 : null); if (IsSceneInstance(val3)) { if ((Object)(object)val == (Object)null) { val = val3.gameObject; } if (clipboardId == 0L || GetClipboardNetworkId(val3.gameObject) == clipboardId) { return val3.gameObject; } } } Object[] array3 = Resources.FindObjectsOfTypeAll(clipboardItemType); Object[] array4 = array3; foreach (Object val4 in array4) { Component val5 = (Component)(object)((val4 is Component) ? val4 : null); if (IsSceneInstance(val5)) { if ((Object)(object)val == (Object)null) { val = val5.gameObject; } if (clipboardId == 0L || GetClipboardNetworkId(val5.gameObject) == clipboardId) { return val5.gameObject; } } } } Scene activeScene = SceneManager.GetActiveScene(); GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects(); foreach (GameObject val6 in rootGameObjects) { Transform[] componentsInChildren = val6.GetComponentsInChildren<Transform>(true); foreach (Transform val7 in componentsInChildren) { if (!((Object)(object)val7 == (Object)null) && string.Equals(((Object)val7).name, "ClipboardManual", StringComparison.OrdinalIgnoreCase)) { if ((Object)(object)val == (Object)null) { val = ((Component)val7).gameObject; } if (clipboardId == 0L || GetClipboardNetworkId(((Component)val7).gameObject) == clipboardId) { return ((Component)val7).gameObject; } } } } } catch { } return val; } private static void AddUniqueClipboardObject(List<GameObject> results, HashSet<int> seen, GameObject obj) { if (results != null && seen != null && !((Object)(object)obj == (Object)null)) { int instanceID = ((Object)obj).GetInstanceID(); if (seen.Add(instanceID)) { results.Add(obj); } } } private static List<GameObject> FindAllClipboardObjects(ulong clipboardId) { //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) List<GameObject> list = new List<GameObject>(); HashSet<int> seen = new HashSet<int>(); try { if (clipboardItemType != null) { Object[] array = Object.FindObjectsOfType(clipboardItemType); foreach (Object val in array) { Component val2 = (Component)(object)((val is Component) ? val : null); if (IsSceneInstance(val2) && (clipboardId == 0L || GetClipboardNetworkId(val2.gameObject) == clipboardId)) { AddUniqueClipboardObject(list, seen, val2.gameObject); } } Object[] array2 = Resources.FindObjectsOfTypeAll(clipboardItemType); foreach (Object val3 in array2) { Component val4 = (Component)(object)((val3 is Component) ? val3 : null); if (IsSceneInstance(val4) && (clipboardId == 0L || GetClipboardNetworkId(val4.gameObject) == clipboardId)) { AddUniqueClipboardObject(list, seen, val4.gameObject); } } } Scene activeScene = SceneManager.GetActiveScene(); GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects(); foreach (GameObject val5 in rootGameObjects) { Transform[] componentsInChildren = val5.GetComponentsInChildren<Transform>(true); foreach (Transform val6 in componentsInChildren) { if (!((Object)(object)val6 == (Object)null) && string.Equals(((Object)val6).name, "ClipboardManual", StringComparison.OrdinalIgnoreCase) && (clipboardId == 0L || GetClipboardNetworkId(((Component)val6).gameObject) == clipboardId)) { AddUniqueClipboardObject(list, seen, ((Component)val6).gameObject); } } } } catch { } return list; } private static void SendAuthoritativePageStateToClient(ulong targetClientId, ulong clipboardId, int page) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) try { if (!IsNetworkAuthoritativeActive()) { return; } NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null || !singleton.IsServer) { return; } int num = Mathf.Max(1, GetMaxPageNumber()); page = Mathf.Clamp(page, 1, num); RememberAuthoritativePage(clipboardId, page); FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(32, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref clipboardId, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref page, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessage("YourFurnace.TutorialClipboardPages.PageState.v1", targetClientId, val, (NetworkDelivery)3); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } catch (Exception ex) { if (DebugLogging != null && DebugLogging.Value) { Log.LogWarning((object)("SendAuthoritativePageStateToClient failed: " + ex.Message)); } } } private static void ApplyNetworkPage(ulong clipboardId, int page) { try { int num = Mathf.Max(1, GetMaxPageNumber()); page = Mathf.Clamp(page, 1, num); RememberAuthoritativePage(clipboardId, page); PendingNetworkPages[clipboardId] = page; PendingNetworkPages[0uL] = page; int num2 = 0; foreach (GameObject item in FindAllClipboardObjects(clipboardId)) { if (!((Object)(object)item == (Object)null)) { object clipboardItem = null; if (clipboardItemType != null) { clipboardItem = item.GetComponent(clipboardItemType); } ClipboardPageState clipboardPageState = item.GetComponent<ClipboardPageState>(); if ((Object)(object)clipboardPageState == (Object)null) { clipboardPageState = item.AddComponent<ClipboardPageState>(); } ApplyAuthoritativePageToState(item, clipboardPageState, clipboardItem, page); num2++; } } if (num2 == 0 && DebugLogging != null && DebugLogging.Value) { Log.LogInfo((object)$"Stored authoritative clipboard page for later. clipboard={clipboardId}, page={page}"); } nextScanTime = 0f; } catch (Exception ex) { if (DebugLogging != null && DebugLogging.Value) { Log.LogWarning((object)("ApplyNetworkPage failed: " + ex.Message)); } } } private static bool ApplyPendingNetworkPageIfNeeded(GameObject clipboardObject, ClipboardPageState state, object clipboardItem) { try { if ((Object)(object)clipboardObject == (Object)null || (Object)(object)state == (Object)null) { return false; } ulong clipboardNetworkId = GetClipboardNetworkId(clipboardObject); if (!TryGetRememberedAuthoritativePage(clipboardNetworkId, out var page)) { return false; } ApplyAuthoritativePageToState(clipboardObject, state, clipboardItem, page); return true; } catch { return false; } } private static bool TryGetRememberedAuthoritativePage(ulong clipboardId, out int page) { page = 1; try { if (PendingNetworkPages.TryGetValue(clipboardId, out page) || PendingNetworkPages.TryGetValue(0uL, out page)) { page = Mathf.Clamp(page, 1, Mathf.Max(1, GetMaxPageNumber())); return true; } if (ServerAuthoritativePages.TryGetValue(clipboardId, out page) || ServerAuthoritativePages.TryGetValue(0uL, out page)) { page = Mathf.Clamp(page, 1, Mathf.Max(1, GetMaxPageNumber())); return true; } if (hasKnownAuthoritativePage) { page = Mathf.Clamp(lastKnownAuthoritativePage, 1, Mathf.Max(1, GetMaxPageNumber())); return true; } } catch { } page = 1; return false; } private static void ApplyAuthoritativePageToState(GameObject clipboardObject, ClipboardPageState state, object clipboardItem, int page) { if ((Object)(object)state == (Object)null) { return; } int num = Mathf.Max(1, GetMaxPageNumber()); page = Mathf.Clamp(page, 1, num); ulong clipboardId = (((Object)(object)clipboardObject != (Object)null) ? GetClipboardNetworkId(clipboardObject) : 0); RememberAuthoritativePage(clipboardId, page); state.ExtendedCurrentPage = page; int rawPage = (state.LastRawPage = ((page > 4) ? Mathf.Clamp(ExtendedCurrentRawSlot.Value, 1, 4) : ToVanillaRawPage(page))); state.SuppressRawSyncUntil = Time.realtimeSinceStartup + 2.5f; if (clipboardItem != null) { WriteCurrentClipboardPage(clipboardItem, rawPage); } if ((Object)(object)clipboardObject != (Object)null) { ClipboardPageVideoController component = clipboardObject.GetComponent<ClipboardPageVideoController>(); if ((Object)(object)component != (Object)null) { component.ForceRefreshSoon(); } } } private static void LoadPages() { CleanupLoadedImages(); Pages.Clear(); ActiveFolder = string.Empty; foreach (string candidateFolder in GetCandidateFolders()) { if (Directory.Exists(candidateFolder)) { int num = LoadPagesFromFolder(candidateFolder); if (num > 0) { ActiveFolder = candidateFolder; Log.LogInfo((object)$"Loaded {num} clipboard page file(s) from: {candidateFolder}"); return; } } } string text = GetCandidateFolders().FirstOrDefault(); if (!string.IsNullOrEmpty(text)) { try { Directory.CreateDirectory(text); } catch { } Log.LogWarning((object)("No page files found. Put page1.png/page1.mp4, page2.png/page2.mp4, etc. in: " + text)); } } private static List<string> GetCandidateFolders() { string path = SanitizeFolderName((FolderName != null) ? FolderName.Value : "TutorialClipboardPages"); List<string> list = new List<string>(); try { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if ((PreferDllFolder == null || PreferDllFolder.Value) && !string.IsNullOrEmpty(directoryName)) { list.Add(Path.Combine(directoryName, path)); } } catch { } list.Add(Path.Combine(Paths.PluginPath, path)); list.Add(Path.Combine(Paths.ConfigPath, path)); list.Add(Path.Combine(Paths.BepInExRootPath, path)); return list.Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList(); } private static string SanitizeFolderName(string name) { if (string.IsNullOrWhiteSpace(name)) { name = "TutorialClipboardPages"; } name = name.Trim().Replace("/", "_").Replace("\\", "_"); char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { name = name.Replace(oldChar, '_'); } return name; } private static int LoadPagesFromFolder(string folder) { int num = 0; int num2 = Mathf.Clamp(MaxPagesToScan.Value, 1, 9999); for (int i = 1; i <= num2; i++) { string path = Path.Combine(folder, $"page{i}.png"); string text = Path.Combine(folder, $"page{i}.mp4"); bool flag = File.Exists(path); bool flag2 = File.Exists(text); if (!flag && !flag2) { if (StopAtFirstMissingPage.Value && num > 0) { break; } } else if (flag2 && (PreferMp4OverPng == null || PreferMp4OverPng.Value || !flag)) { Pages[i] = new PageMedia { PageNumber = i, FilePath = text, MediaType = PageMediaType.Video }; num++; } else if (flag) { PageMedia pageMedia = CreateImagePage(i, path); if (pageMedia != null) { Pages[i] = pageMedia; num++; } } } return num; } private static PageMedia CreateImagePage(int page, string path) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown try { byte[] array = File.ReadAllBytes(path); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); ((Object)val).name = $"TutorialClipboardPages_page{page}"; ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)0; ((Texture)val).anisoLevel = 0; if (!ImageConversion.LoadImage(val, array, false)) { Object.Destroy((Object)(object)val); Log.LogWarning((object)("Failed to load image: " + path)); return null; } return new PageMedia { PageNumber = page, FilePath = path, MediaType = PageMediaType.Image, Texture = (Texture)(object)val }; } catch (Exception ex) { Log.LogWarning((object)("Failed loading " + path + ": " + ex.Message)); return null; } } private static void CleanupLoadedImages() { foreach (PageMedia value in Pages.Values) { try { if (value.MediaType == PageMediaType.Image && (Object)(object)value.Texture != (Object)null) { Object.Destroy((Object)(object)value.Texture); } } catch { } } } private static void ApplyToAllClipboards() { //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) if (Pages.Count == 0) { return; } try { HashSet<int> hashSet = new HashSet<int>(); if (clipboardItemType != null) { Object[] array = Object.FindObjectsOfType(clipboardItemType); Object[] array2 = array; foreach (Object val in array2) { Component val2 = (Component)(object)((val is Component) ? val : null); if (IsSceneInstance(val2)) { int instanceID = ((Object)val2.gameObject).GetInstanceID(); if (hashSet.Add(instanceID)) { ApplyToClipboard(val2.gameObject, val); } } } Object[] array3 = Resources.FindObjectsOfTypeAll(clipboardItemType); Object[] array4 = array3; foreach (Object val3 in array4) { Component val4 = (Component)(object)((val3 is Component) ? val3 : null); if (IsSceneInstance(val4)) { int instanceID2 = ((Object)val4.gameObject).GetInstanceID(); if (hashSet.Add(instanceID2)) { ApplyToClipboard(val4.gameObject, val3); } } } } Scene activeScene = SceneManager.GetActiveScene(); GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects(); foreach (GameObject val5 in rootGameObjects) { Transform[] componentsInChildren = val5.GetComponentsInChildren<Transform>(true); foreach (Transform val6 in componentsInChildren) { if ((Object)(object)val6 == (Object)null || !string.Equals(((Object)val6).name, "ClipboardManual", StringComparison.OrdinalIgnoreCase)) { continue; } int instanceID3 = ((Object)((Component)val6).gameObject).GetInstanceID(); if (hashSet.Add(instanceID3)) { object clipboardItem = null; if (clipboardItemType != null) { clipboardItem = ((Component)val6).GetComponent(clipboardItemType); } ApplyToClipboard(((Component)val6).gameObject, clipboardItem); } } } } catch (Exception ex) { if (DebugLogging.Value) { Log.LogWarning((object)("ApplyToAllClipboards failed: " + ex.Message)); } } } private static void ApplyToClipboard(GameObject clipboardObject, object clipboardItem) { if ((Object)(object)clipboardObject == (Object)null) { return; } ClipboardPageState clipboardPageState = clipboardObject.GetComponent<ClipboardPageState>(); if ((Object)(object)clipboardPageState == (Object)null) { clipboardPageState = clipboardObject.AddComponent<ClipboardPageState>(); } bool flag = false; if (IsNetworkAuthoritativeActive()) { flag = ApplyPendingNetworkPageIfNeeded(clipboardObject, clipboardPageState, clipboardItem); } if (!flag) { clipboardPageState.SyncFromRaw(clipboardItem, respectSuppress: true); ApplyPendingNetworkPageIfNeeded(clipboardObject, clipboardPageState, clipboardItem); } RequestPageStateWhenClipboardIsSeenOnClient(clipboardObject); if (EnableExtendedPages.Value && clipboardPageState.ExtendedCurrentPage > 4 && clipboardItem != null) { int num = Mathf.Clamp(ExtendedCurrentRawSlot.Value, 1, 4); WriteCurrentClipboardPage(clipboardItem, num); clipboardPageState.LastRawPage = num; } ClipboardPageVideoController clipboardPageVideoController = clipboardObject.GetComponent<ClipboardPageVideoController>(); if ((Object)(object)clipboardPageVideoController == (Object)null) { clipboardPageVideoController = clipboardObject.AddComponent<ClipboardPageVideoController>(); } clipboardPageVideoController.ClipboardItem = clipboardItem; clipboardPageVideoController.State = clipboardPageState; clipboardPageVideoController.EnsurePageVideos(); bool flag2 = EnableExtendedPages.Value && clipboardPageState.ExtendedCurrentPage > 4; bool flag3 = HidePreviousAndNextPageParts != null && HidePreviousAndNextPageParts.Value; bool flag4 = ShouldShowClipboardPageRenderers(clipboardObject, clipboardItem); int num2 = (flag2 ? Mathf.Clamp(ExtendedCurrentRawSlot.Value, 1, 4) : ToVanillaRawPage(clipboardPageState.ExtendedCurrentPage)); int num3 = Mathf.Clamp(ExtendedPreviousRawSlot.Value, 1, 4); int num4 = Mathf.Clamp(ExtendedNextRawSlot.Value, 1, 4); Renderer[] componentsInChildren = clipboardObject.GetComponentsInChildren<Renderer>(true); Renderer[] array = componentsInChildren; foreach (Renderer val in array) { if ((Object)(object)val == (Object)null) { continue; } Material[] array2 = (CloneMaterials.Value ? val.materials : val.sharedMaterials); if (array2 == null || array2.Length == 0) { continue; } bool flag5 = false; bool flag6 = false; bool flag7 = flag4 && !flag2 && !flag3; foreach (Material val2 in array2) { if ((Object)(object)val2 == (Object)null) { continue; } int manualSlotForMaterial = GetManualSlotForMaterial(val2); if (manualSlotForMaterial < 1 || manualSlotForMaterial > 4) { continue; } flag6 = true; int num5 = -1; if (flag3) { if (manualSlotForMaterial != num2) { continue; } num5 = (flag2 ? clipboardPageState.ExtendedCurrentPage : manualSlotForMaterial); flag7 = flag4; } else if (flag2) { int maxPageNumber = GetMaxPageNumber(); if (manualSlotForMaterial == num2) { num5 = clipboardPageState.ExtendedCurrentPage; flag7 = flag4; } else if (manualSlotForMaterial == num3 && clipboardPageState.ExtendedCurrentPage > 1) { num5 = clipboardPageState.ExtendedCurrentPage - 1; flag7 = flag4; } else { if (manualSlotForMaterial != num4 || clipboardPageState.ExtendedCurrentPage >= maxPageNumber) { continue; } num5 = clipboardPageState.ExtendedCurrentPage + 1; flag7 = flag4; } } else { num5 = manualSlotForMaterial; flag7 = flag4; } Texture textureForPage = GetTextureForPage(clipboardPageVideoController, num5); if (!((Object)(object)textureForPage == (Object)null)) { ApplyTextureToMaterial(val2, textureForPage); flag5 = true; if (DebugLogging.Value) { string text = ((!flag3) ? (flag2 ? "EXT-TRIPLE" : "VANILLA") : (flag2 ? "EXT-CURRENT-ONLY" : "VANILLA-CURRENT-ONLY")); Log.LogInfo((object)$"{text}: Manual{manualSlotForMaterial} -> page{num5}; renderer={((Object)val).name}; material={CleanMaterialName(((Object)val2).name)}; ext={clipboardPageState.ExtendedCurrentPage}; prevSlot={num3}; currentSlot={num2}; nextSlot={num4}"); } } } if (flag5) { if (CloneMaterials.Value) { val.materials = array2; } else { val.sharedMaterials = array2; } } if (flag6) { try { val.enabled = flag4 && flag7; } catch { } } } clipboardPageVideoController.UpdatePlaybackState(); } private static void RequestPageStateWhenClipboardIsSeenOnClient(GameObject clipboardObject) { try { if (!((Object)(object)clipboardObject == (Object)null) && IsNetworkAuthoritativeActive()) { NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && !singleton.IsServer && Time.realtimeSinceStartup >= nextClientStateRequestTime) { nextClientStateRequestTime = Time.realtimeSinceStartup + 0.75f; SendClientPageStateRequest(GetClipboardNetworkId(clipboardObject)); } } } catch { } } private static bool ShouldShowClipboardPageRenderers(GameObject clipboardObject, object clipboardItem) { try { object obj = clipboardItem; if (obj == null && (Object)(object)clipboardObject != (Object)null && grabbableObjectType != null) { obj = clipboardObject.GetComponent(grabbableObjectType); } if (obj == null) { return true; } if (TryReadBoolMember(obj, "isPocketed", out var value) && value) { return false; } return true; } catch { return true; } } private static bool TryReadBoolMember(object instance, string memberName, out bool value) { value = false; try { if (instance == null || string.IsNullOrEmpty(memberName)) { return false; } Type type = instance.GetType(); FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.FieldType == typeof(bool)) { value = (bool)field.GetValue(instance); return true; } PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.PropertyType == typeof(bool) && property.GetIndexParameters().Length == 0) { value = (bool)property.GetValue(instance, null); return true; } } catch { } return false; } private static Texture GetTextureForPage(ClipboardPageVideoController controller, int pageNumber) { if (!Pages.TryGetValue(pageNumber, out var value) || value == null) { return null; } if (value.MediaType == PageMediaType.Image) { return value.Texture; } return (Texture)(object)(((Object)(object)controller != (Object)null) ? controller.GetRenderTextureForPage(pageNumber) : null); } private static int GetManualSlotForMaterial(Material mat) { string text = CleanMaterialName(((Object)mat).name); if (text.Equals("Manual1", StringComparison.OrdinalIgnoreCase)) { return 1; } if (text.Equals("Manual2", StringComparison.OrdinalIgnoreCase)) { return 2; } if (text.Equals("Manual3", StringComparison.OrdinalIgnoreCase)) { return 3; } if (text.Equals("Manual4", StringComparison.OrdinalIgnoreCase)) { return 4; } return -1; } private static void ApplyTextureToMaterial(Material mat, Texture texture) { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)mat == (Object)null || (Object)(object)texture == (Object)null) { return; } try { mat.mainTexture = texture; } catch { } try { if (mat.HasProperty("_MainTex")) { mat.SetTexture("_MainTex", texture); } if (mat.HasProperty("_BaseMap")) { mat.SetTexture("_BaseMap", texture); } if (mat.HasProperty("_BaseColorMap")) { mat.SetTexture("_BaseColorMap", texture); } if (mat.HasProperty("_EmissiveColorMap")) { mat.SetTexture("_EmissiveColorMap", texture); } if (mat.HasProperty("_Color")) { mat.SetColor("_Color", Color.white); } if (mat.HasProperty("_BaseColor")) { mat.SetColor("_BaseColor", Color.white); } } catch { } } private static string CleanMaterialName(string name) { if (string.IsNullOrEmpty(name)) { return string.Empty; } name = name.Replace(" (Instance)", string.Empty); return name.Trim(); } internal static VideoAspectRatio ParseVideoAspect(string value) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) string text = (value ?? "FitInside").Trim().ToLowerInvariant(); if (text == "stretch" || text == "stretchtofill") { return (VideoAspectRatio)5; } if (text == "fitvertically" || text == "vertical") { return (VideoAspectRatio)1; } if (text == "fithorizontally" || text == "horizontal") { return (VideoAspectRatio)2; } if (text == "no-scaling" || text == "noscaling" || text == "none") { return (VideoAspectRatio)0; } return (VideoAspectRatio)3; } internal static string ToVideoUrl(string path) { try { return new Uri(path).AbsoluteUri; } catch { return path; } } } public class ClipboardPageState : MonoBehaviour { public int ExtendedCurrentPage = 1; public int LastRawPage = 1; public float SuppressRawSyncUntil = 0f; public void SyncFromRaw(object clipboardItem, bool respectSuppress) { if (!respectSuppress || !(Time.realtimeSinceStartup < SuppressRawSyncUntil)) { int num = Plugin.ReadCurrentClipboardPage(clipboardItem); num = Mathf.Clamp(num, 1, 4); int num2 = Mathf.Max(1, Plugin.GetMaxPageNumber()); if (ExtendedCurrentPage <= 0) { ExtendedCurrentPage = Mathf.Clamp(num, 1, num2); } if (LastRawPage <= 0) { LastRawPage = num; } else if (ExtendedCurrentPage <= 4 && num != LastRawPage) { ExtendedCurrentPage = Mathf.Clamp(num, 1, Mathf.Min(4, num2)); LastRawPage = num; } else if (ExtendedCurrentPage > 4) { LastRawPage = Mathf.Clamp(Plugin.ExtendedCurrentRawSlot.Value, 1, 4); } else { LastRawPage = num; } } } } public class ClipboardPageVideoController : MonoBehaviour { private class VideoSlot { public GameObject Host; public VideoPlayer VideoPlayer; public AudioSource MutedSink; public RenderTexture RenderTexture; } public object ClipboardItem; public ClipboardPageState State; private readonly Dictionary<int, VideoSlot> slots = new Dictionary<int, VideoSlot>(); private float nextVideoStateUpdate; public void ForceRefreshSoon() { nextVideoStateUpdate = 0f; } public void EnsurePageVideos() { foreach (KeyValuePair<int, Plugin.PageMedia> page in Plugin.Pages) { if (page.Value != null && page.Value.MediaType == Plugin.PageMediaType.Video && !slots.ContainsKey(page.Key)) { slots[page.Key] = CreateVideoSlot(page.Key, page.Value.FilePath); } } } public RenderTexture GetRenderTextureForPage(int pageNumber) { if (!slots.TryGetValue(pageNumber, out var value) || value == null) { return null; } return value.RenderTexture; } public void UpdatePlaybackState() { if (Time.realtimeSinceStartup < nextVideoStateUpdate) { return; } nextVideoStateUpdate = Time.realtimeSinceStartup + 0.15f; int num = (((Object)(object)State != (Object)null) ? State.ExtendedCurrentPage : Plugin.ReadCurrentClipboardPage(ClipboardItem)); foreach (KeyValuePair<int, VideoSlot> slot in slots) { VideoSlot value = slot.Value; if (value == null || (Object)(object)value.VideoPlayer == (Object)null) { continue; } bool flag = slot.Key == num; bool flag2 = Plugin.KeepHiddenMp4VisualsPlaying.Value || flag; try { ForceVideoAudioIntoMutedSink(value); if (flag2) { if (!value.VideoPlayer.isPlaying) { value.VideoPlayer.Play(); } } else if (value.VideoPlayer.isPlaying) { value.VideoPlayer.Pause(); } } catch { } } } private VideoSlot CreateVideoSlot(int pageNumber, string path) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Expected O, but got Unknown //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Expected O, but got Unknown //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Expected O, but got Unknown VideoSlot videoSlot = new VideoSlot(); try { GameObject val = new GameObject($"TutorialClipboardPages_VideoPage{pageNumber}_VisualOnly"); val.transform.SetParent(((Component)this).transform, false); val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.identity; val.transform.localScale = Vector3.one; VideoPlayer val2 = val.AddComponent<VideoPlayer>(); AudioSource mutedSink = val.AddComponent<AudioSource>(); ConfigureMutedSink(mutedSink); int num = Mathf.Clamp(Plugin.VideoTextureWidth.Value, 64, 4096); int num2 = Mathf.Clamp(Plugin.VideoTextureHeight.Value, 64, 4096); RenderTexture val3 = new RenderTexture(num, num2, 0, (RenderTextureFormat)0); ((Object)val3).name = $"TutorialClipboardPages_page{pageNumber}_RT"; ((Texture)val3).wrapMode = (TextureWrapMode)1; ((Texture)val3).filterMode = (FilterMode)1; val3.Create(); val2.source = (VideoSource)1; val2.url = Plugin.ToVideoUrl(path); val2.renderMode = (VideoRenderMode)2; val2.targetTexture = val3; val2.isLooping = Plugin.LoopMp4Pages.Value; val2.playOnAwake = false; val2.waitForFirstFrame = false; val2.skipOnDrop = true; val2.aspectRatio = Plugin.ParseVideoAspect(Plugin.VideoAspectMode.Value); val2.audioOutputMode = (VideoAudioOutputMode)1; val2.controlledAudioTrackCount = 16; for (ushort num3 = 0; num3 < 16; num3++) { try { val2.SetTargetAudioSource(num3, mutedSink); } catch { } try { val2.EnableAudioTrack(num3, false); } catch { } try { val2.SetDirectAudioMute(num3, true); } catch { } try { val2.SetDirectAudioVolume(num3, 0f); } catch { } } val2.prepareCompleted += (EventHandler)delegate(VideoPlayer source) { try { ushort num4 = source.audioTrackCount; if (num4 < 1) { num4 = 1; } source.controlledAudioTrackCount = num4; for (ushort num5 = 0; num5 < num4; num5++) { try { source.SetTargetAudioSource(num5, mutedSink); } catch { } try { source.EnableAudioTrack(num5, false); } catch { } try { source.SetDirectAudioMute(num5, true); } catch { } try { source.SetDirectAudioVolume(num5, 0f); } catch { } } } catch { } }; val2.errorReceived += (ErrorEventHandler)delegate(VideoPlayer source, string message) { if (Plugin.DebugLogging.Value) { Plugin.Log.LogWarning((object)$"Video page{pageNumber} error: {message}"); } }; try { val2.Prepare(); val2.Play(); } catch (Exception ex) { if (Plugin.DebugLogging.Value) { Plugin.Log.LogWarning((object)$"Video page{pageNumber} prepare/play failed: {ex.Message}"); } } videoSlot.Host = val; videoSlot.VideoPlayer = val2; videoSlot.MutedSink = mutedSink; videoSlot.RenderTexture = val3; } catch (Exception ex2) { if (Plugin.DebugLogging.Value) { Plugin.Log.LogWarning((object)$"CreateVideoSlot page{pageNumber} failed: {ex2.Message}"); } } return videoSlot; } private void OnDestroy() { foreach (VideoSlot value in slots.Values) { try { if ((Object)(object)value.VideoPlayer != (Object)null) { value.VideoPlayer.Stop(); } if ((Object)(object)value.MutedSink != (Object)null) { value.MutedSink.Stop(); } if ((Object)(object)value.RenderTexture != (Object)null) { value.RenderTexture.Release(); Object.Destroy((Object)(object)value.RenderTexture); } if ((Object)(object)value.Host != (Object)null) { Object.Destroy((Object)(object)value.Host); } } catch { } } slots.Clear(); } private static void ConfigureMutedSink(AudioSource source) { if (!((Object)(object)source == (Object)null)) { source.playOnAwake = false; source.mute = true; source.volume = 0f; source.spatialBlend = 1f; source.dopplerLevel = 0f; source.loop = false; source.panStereo = 0f; source.bypassEffects = true; source.bypassListenerEffects = true; source.bypassReverbZones = true; source.ignoreListenerPause = false; source.ignoreListenerVolume = false; } } private static void ForceVideoAudioIntoMutedSink(VideoSlot slot) { if (slot == null || (Object)(object)slot.VideoPlayer == (Object)null) { return; } if ((Object)(object)slot.MutedSink != (Object)null) { ConfigureMutedSink(slot.MutedSink); } try { slot.VideoPlayer.audioOutputMode = (VideoAudioOutputMode)1; } catch { } ushort num = 1; try { num = slot.VideoPlayer.audioTrackCount; if (num < 1) { num = 1; } } catch { num = 1; } try { slot.VideoPlayer.controlledAudioTrackCount = num; } catch { } for (ushort num2 = 0; num2 < num; num2++) { try { if ((Object)(object)slot.MutedSink != (Object)null) { slot.VideoPlayer.SetTargetAudioSource(num2, slot.MutedSink); } } catch { } try { slot.VideoPlayer.EnableAudioTrack(num2, false); } catch { } try { slot.VideoPlayer.SetDirectAudioMute(num2, true); } catch { } try { slot.VideoPlayer.SetDirectAudioVolume(num2, 0f); } catch { } } } }