using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Dawn; using Dusk; using GameNetcodeStuff; using HarmonyLib; using HatLib.NetcodePatcher; using HatLib.Patches; using HatLib.Tools; using Microsoft.CodeAnalysis; using Newtonsoft.Json.Linq; using Unity.Collections; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("HatLib")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("0.0.1.0")] [assembly: AssemblyInformationalVersion("0.0.1")] [assembly: AssemblyProduct("HatLib")] [assembly: AssemblyTitle("HatLib")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] [module: NetcodePatchedAssembly] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace HatLib { internal static class ConfigManager { internal static ConfigEntry? DebugLogs { get; private set; } internal static void Bind(ConfigFile config) { DebugLogs = config.Bind("Internal", "Debug Logs", false, "If true, allows debug logging"); } } internal static class Log { internal static void Debug(string message) { if (ConfigManager.DebugLogs.Value) { Write((LogLevel)32, message); } } internal static void Info(string message) { Write((LogLevel)16, message); } internal static void Warn(string message) { Write((LogLevel)4, message); } internal static void Error(string message) { Write((LogLevel)2, message); } internal static void Fatal(string message) { Write((LogLevel)1, message); } private static void Write(LogLevel logLevel, string message) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) Plugin.mls.Log(logLevel, (object)message); } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("MrHat.Lib", "HatLib", "0.0.1")] internal class Plugin : BaseUnityPlugin { internal const string modGUID = "MrHat.Lib"; internal const string modName = "HatLib"; internal const string modVersion = "0.0.1"; private const string DawnLibGUID = "com.github.teamxiaolan.dawnlib"; private const string DuskGUID = "com.github.teamxiaolan.dawnlib.dusk"; internal static Harmony _harmony; internal static ManualLogSource mls; internal static Plugin instance; private void Awake() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown instance = this; mls = Logger.CreateLogSource("MrHat.Lib"); _harmony = new Harmony("MrHat.Lib"); ConfigManager.Bind(((BaseUnityPlugin)this).Config); _harmony.PatchAll(typeof(GameNetworkManagerPatches)); _harmony.PatchAll(typeof(StartOfRoundPatches)); mls.LogInfo((object)"HatLib loaded"); } } public class RoundEvents { public static event Action? OnRoundStart; public static event Action? OnRoundEnd; public static event Action? OnShipLeft; public static event Action? OnShipReadyToLand; public static event Action? OnShipItemsLoaded; public static event Action? OnShipUnlockablesSynced; public static event Action? OnPlayersLoadedReset; public static event Action? OnLocalDisconnect; public static event Action? OnDestroy; public static event Action? OnGameSaved; private RoundEvents() { } internal static void RoundStart(StartOfRound startOfRound) { RoundEvents.OnRoundStart?.Invoke(startOfRound); } internal static void RoundEnd(StartOfRound startOfRound) { RoundEvents.OnRoundEnd?.Invoke(startOfRound); } internal static void ShipLeft(StartOfRound startOfRound) { RoundEvents.OnShipLeft?.Invoke(startOfRound); } internal static void ShipReadyToLand(StartOfRound startOfRound) { RoundEvents.OnShipReadyToLand?.Invoke(startOfRound); } internal static void ShipItemsLoaded(StartOfRound startOfRound) { RoundEvents.OnShipItemsLoaded?.Invoke(startOfRound); } internal static void ShipUnlockablesSynced(StartOfRound startOfRound) { RoundEvents.OnShipUnlockablesSynced?.Invoke(startOfRound); } internal static void PlayersLoadedReset(StartOfRound startOfRound) { RoundEvents.OnPlayersLoadedReset?.Invoke(startOfRound); } internal static void Disconnect(StartOfRound startOfRound) { RoundEvents.OnLocalDisconnect?.Invoke(startOfRound); } internal static void Destroy(StartOfRound startOfRound) { RoundEvents.OnDestroy?.Invoke(startOfRound); } internal static void GameSaved() { RoundEvents.OnGameSaved?.Invoke(); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "HatLib"; public const string PLUGIN_NAME = "HatLib"; public const string PLUGIN_VERSION = "0.0.1"; } } namespace HatLib.Tools { public static class AudioTools { public static bool PlayOneShot(AudioSource? source, AudioClip? clip) { if ((Object)(object)source == (Object)null || (Object)(object)clip == (Object)null) { return false; } source.PlayOneShot(clip); return true; } public static bool Load(AudioClip? clip) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 if ((Object)(object)clip == (Object)null || (int)clip.loadState > 0) { return false; } return clip.LoadAudioData(); } public static bool Unload(AudioClip? clip) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 if ((Object)(object)clip == (Object)null || (int)clip.loadState != 2) { return false; } return clip.UnloadAudioData(); } public static bool SwapClip(AudioSource? source, AudioClip? clip, bool play) { if ((Object)(object)source == (Object)null) { return false; } source.clip = clip; if (play && (Object)(object)clip != (Object)null) { source.Play(); } return true; } public static bool Stop(AudioSource? source, bool zeroVolume = false) { if ((Object)(object)source == (Object)null) { return false; } if (zeroVolume) { source.volume = 0f; } source.Stop(); return true; } public static int StopMany(bool zeroVolume = false, params AudioSource?[] sources) { if (sources == null) { return 0; } int num = 0; for (int i = 0; i < sources.Length; i++) { if (Stop(sources[i], zeroVolume)) { num++; } } return num; } } public static class CoroutineTools { public static bool Start(MonoBehaviour owner, ref Coroutine? handle, IEnumerator job) { if ((Object)(object)owner == (Object)null || job == null || handle != null) { return false; } handle = owner.StartCoroutine(job); return true; } public static bool Stop(MonoBehaviour owner, ref Coroutine? handle) { if ((Object)(object)owner == (Object)null || handle == null) { return false; } owner.StopCoroutine(handle); handle = null; return true; } public static bool Restart(MonoBehaviour owner, ref Coroutine? handle, IEnumerator job) { Stop(owner, ref handle); return Start(owner, ref handle, job); } public static Coroutine? NextFrame(MonoBehaviour owner, Action action) { if ((Object)(object)owner == (Object)null || action == null) { return null; } return owner.StartCoroutine(NextFrameWait(action)); } public static Coroutine? Delay(MonoBehaviour owner, float seconds, Action action) { if ((Object)(object)owner == (Object)null || action == null) { return null; } return owner.StartCoroutine(DelayWait(seconds, action)); } private static IEnumerator NextFrameWait(Action action) { yield return null; action(); } private static IEnumerator DelayWait(float seconds, Action action) { yield return (object)new WaitForSeconds(seconds); action(); } } public static class DawnContractData { public static bool LoadString(NamespacedKey key, out string? raw) { raw = string.Empty; PersistentDataContainer currentContract = DawnLib.GetCurrentContract(); if (currentContract == null) { Log.Debug($"Dawn contract string load skipped because no current contract exists for {key}"); return false; } if (!((DataContainer)currentContract).TryGet(key, ref raw)) { Log.Debug($"Dawn contract string was not found for {key}"); return false; } Log.Debug($"Dawn contract string loaded for {key}"); return true; } public static bool WriteString(NamespacedKey key, string raw) { return Write(key, raw); } public static bool Load(NamespacedKey key, out T? value) { value = default(T); PersistentDataContainer currentContract = DawnLib.GetCurrentContract(); if (currentContract == null) { Log.Debug($"Dawn contract value load skipped because no current contract exists for {key}"); return false; } if (!((DataContainer)currentContract).TryGet(key, ref value)) { Log.Debug($"Dawn contract value was not found for {key}"); return false; } Log.Debug($"Dawn contract value loaded for {key}"); return true; } public static bool Write(NamespacedKey key, T value) { if (value == null) { Log.Warn($"Dawn contract value write skipped because value was null for {key}"); return false; } if (!CanWrite()) { Log.Warn($"Dawn contract value write skipped because this client is not the server for {key}"); return false; } PersistentDataContainer currentContract = DawnLib.GetCurrentContract(); if (currentContract == null) { Log.Warn($"Dawn contract value write skipped because no current contract exists for {key}"); return false; } using (((DataContainer)currentContract).CreateEditContext()) { ((DataContainer)currentContract).Set(key, value); } Log.Debug($"Dawn contract value written for {key}"); return true; } public static bool LoadJObject(NamespacedKey parentKey, NamespacedKey childKey, out JObject? value) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown value = new JObject(); PersistentDataContainer currentContract = DawnLib.GetCurrentContract(); if (currentContract == null) { Log.Debug($"Dawn contract child object load skipped because no current contract exists for {childKey}"); return false; } ChildPersistentDataContainer val = default(ChildPersistentDataContainer); if (!((DataContainer)currentContract).TryGet(parentKey, ref val)) { Log.Debug($"Dawn contract child container was not found for {parentKey}"); return false; } if (!((DataContainer)val).TryGet(childKey, ref value)) { Log.Debug($"Dawn contract child object was not found for {childKey}"); return false; } Log.Debug($"Dawn contract child object loaded for {childKey}"); return true; } public static bool WriteJObject(NamespacedKey parentKey, NamespacedKey childKey, JObject value) { return Write(parentKey, childKey, value); } public static bool Load(NamespacedKey parentKey, NamespacedKey childKey, out T? value) { value = default(T); PersistentDataContainer currentContract = DawnLib.GetCurrentContract(); if (currentContract == null) { Log.Debug($"Dawn contract child value load skipped because no current contract exists for {childKey}"); return false; } ChildPersistentDataContainer val = default(ChildPersistentDataContainer); if (!((DataContainer)currentContract).TryGet(parentKey, ref val)) { Log.Debug($"Dawn contract child container was not found for {parentKey}"); return false; } if (!((DataContainer)val).TryGet(childKey, ref value)) { Log.Debug($"Dawn contract child value was not found for {childKey}"); return false; } Log.Debug($"Dawn contract child value loaded for {childKey}"); return true; } public static bool Write(NamespacedKey parentKey, NamespacedKey childKey, T value) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown if (value == null) { Log.Warn($"Dawn contract child value write skipped because value was null for {childKey}"); return false; } if (!CanWrite()) { Log.Warn($"Dawn contract child value write skipped because this client is not the server for {childKey}"); return false; } PersistentDataContainer currentContract = DawnLib.GetCurrentContract(); if (currentContract == null) { Log.Warn($"Dawn contract child value write skipped because no current contract exists for {childKey}"); return false; } using (((DataContainer)currentContract).CreateEditContext()) { ChildPersistentDataContainer val = default(ChildPersistentDataContainer); if (!((DataContainer)currentContract).TryGet(parentKey, ref val)) { val = new ChildPersistentDataContainer(currentContract); ((DataContainer)currentContract).Set(parentKey, val); Log.Debug($"Dawn contract child container created for {parentKey}"); } ((DataContainer)val).Set(childKey, value); } Log.Debug($"Dawn contract child value written for {childKey}"); return true; } public static bool Delete(NamespacedKey key) { if (!CanWrite()) { Log.Warn($"Dawn contract value delete skipped because this client is not the server for {key}"); return false; } PersistentDataContainer currentContract = DawnLib.GetCurrentContract(); if (currentContract == null) { Log.Warn($"Dawn contract value delete skipped because no current contract exists for {key}"); return false; } using (((DataContainer)currentContract).CreateEditContext()) { ((DataContainer)currentContract).Remove(key); } Log.Debug($"Dawn contract value deleted for {key}"); return true; } public static bool Delete(NamespacedKey parentKey, NamespacedKey childKey, bool deleteParentWhenEmpty = false) { if (!CanWrite()) { Log.Warn($"Dawn contract child value delete skipped because this client is not the server for {childKey}"); return false; } PersistentDataContainer currentContract = DawnLib.GetCurrentContract(); if (currentContract == null) { Log.Warn($"Dawn contract child value delete skipped because no current contract exists for {childKey}"); return false; } using (((DataContainer)currentContract).CreateEditContext()) { ChildPersistentDataContainer val = default(ChildPersistentDataContainer); if (!((DataContainer)currentContract).TryGet(parentKey, ref val)) { Log.Debug($"Dawn contract child delete skipped because container was not found for {parentKey}"); return false; } ((DataContainer)val).Remove(childKey); Log.Debug($"Dawn contract child value deleted for {childKey}"); if (deleteParentWhenEmpty && ((DataContainer)val).Count == 0) { ((DataContainer)currentContract).Remove(parentKey); Log.Debug($"Dawn contract child container deleted for {parentKey}"); } } return true; } private static bool CanWrite() { NetworkManager singleton = NetworkManager.Singleton; return singleton != null && singleton.IsServer; } } public class DawnContractSync { private const float DelaySeconds = 1.25f; private static readonly WaitForSeconds DelayWait = new WaitForSeconds(1.25f); private readonly NamespacedKey dataKey; private readonly NamespacedKey requestKey; private readonly Func writeData; private readonly Action readData; private readonly bool saveOnGameSaved; private Coroutine? hostCoroutine; private Coroutine? clientCoroutine; private MonoBehaviour? hostOwner; private MonoBehaviour? clientOwner; private string rawData = string.Empty; private bool dataReceived; private bool listening; private bool HostRestoreFinished { get; set; } = true; private bool ClientDelayFinished { get; set; } public static DawnContractSync Register(NamespacedKey dataKey, Func writeData, Action readData, bool saveOnGameSaved = true) { DawnContractSync dawnContractSync = new DawnContractSync(dataKey, writeData, readData, saveOnGameSaved); dawnContractSync.Enable(); return dawnContractSync; } public DawnContractSync(NamespacedKey dataKey, Func writeData, Action readData, bool saveOnGameSaved = true) { this.dataKey = dataKey ?? throw new ArgumentNullException("dataKey"); requestKey = RequestKey(dataKey); this.writeData = writeData ?? throw new ArgumentNullException("writeData"); this.readData = readData ?? throw new ArgumentNullException("readData"); this.saveOnGameSaved = saveOnGameSaved; } public void Enable() { if (listening) { Log.Debug($"Dawn contract sync enable skipped because it is already enabled for {dataKey}"); return; } listening = true; HatNet.Init(); HatNet.RegisterClient(dataKey, DataReceived); HatNet.RegisterServer(requestKey, RequestReceived); RoundEvents.OnShipItemsLoaded += ShipItemsLoaded; RoundEvents.OnShipUnlockablesSynced += ShipUnlockablesSynced; RoundEvents.OnLocalDisconnect += Clear; RoundEvents.OnDestroy += Clear; if (saveOnGameSaved) { RoundEvents.OnGameSaved += SaveGame; } Log.Debug($"Dawn contract sync enabled for {dataKey}"); } public void Disable() { if (!listening) { Log.Debug($"Dawn contract sync disable skipped because it is already disabled for {dataKey}"); return; } listening = false; HatNet.UnregisterClient(dataKey, DataReceived); HatNet.UnregisterServer(requestKey, RequestReceived); RoundEvents.OnShipItemsLoaded -= ShipItemsLoaded; RoundEvents.OnShipUnlockablesSynced -= ShipUnlockablesSynced; RoundEvents.OnLocalDisconnect -= Clear; RoundEvents.OnDestroy -= Clear; RoundEvents.OnGameSaved -= SaveGame; Clear(); Log.Debug($"Dawn contract sync disabled for {dataKey}"); } public bool Save() { return Save(writeData() ?? string.Empty); } public bool Save(string data) { if (!HostRestoreFinished) { Log.Debug($"Dawn contract sync save skipped because restore is still running for {dataKey}"); return false; } string raw = data ?? string.Empty; if (!DawnContractData.WriteString(dataKey, raw)) { Log.Warn($"Dawn contract sync data could not be saved for {dataKey}"); return false; } rawData = raw; dataReceived = true; Log.Debug($"Dawn contract sync data saved for {dataKey}"); return true; } public bool SaveAndPublish() { if (!Save()) { return false; } if (PublishRaw(rawData)) { Log.Debug($"Dawn contract sync data saved and published for {dataKey}"); return true; } Log.Warn($"Dawn contract sync data was saved but not published for {dataKey}"); return false; } public bool Delete() { if (!HostRestoreFinished) { Log.Debug($"Dawn contract sync delete skipped because restore is still running for {dataKey}"); return false; } if (!DawnContractData.Delete(dataKey)) { Log.Warn($"Dawn contract sync data could not be deleted for {dataKey}"); return false; } rawData = string.Empty; dataReceived = true; Log.Debug($"Dawn contract sync data deleted for {dataKey}"); return true; } public bool Publish() { if (HostRestoreFinished) { NetworkManager singleton = NetworkManager.Singleton; if (singleton != null && singleton.IsServer) { rawData = writeData() ?? string.Empty; dataReceived = true; if (PublishRaw(rawData)) { Log.Debug($"Dawn contract sync data published for {dataKey}"); return true; } Log.Warn($"Dawn contract sync data could not be published for {dataKey}"); return false; } } Log.Debug($"Dawn contract sync publish skipped for {dataKey}"); return false; } public bool Request() { NetworkManager singleton = NetworkManager.Singleton; if (singleton == null || !singleton.IsListening) { Log.Debug($"Dawn contract sync request skipped because networking is unavailable for {dataKey}"); return false; } if (HatNet.SendToServer(requestKey, string.Empty)) { Log.Debug($"Dawn contract sync data requested for {dataKey}"); return true; } Log.Warn($"Dawn contract sync data could not be requested for {dataKey}"); return false; } internal void Clear() { StopHostCoroutine(); StopClientCoroutine(); HostRestoreFinished = true; ClientDelayFinished = false; rawData = string.Empty; dataReceived = false; Log.Debug($"Dawn contract sync transient data cleared for {dataKey}"); } private static NamespacedKey RequestKey(NamespacedKey key) { return NamespacedKey.From(key.Namespace, key.Key + "_request"); } private void SaveGame() { Save(); } private void ShipItemsLoaded(StartOfRound startOfRound) { NetworkManager singleton = NetworkManager.Singleton; if (singleton != null && singleton.IsServer) { StopHostCoroutine(); HostRestoreFinished = false; hostOwner = (MonoBehaviour?)(object)startOfRound; hostCoroutine = ((MonoBehaviour)startOfRound).StartCoroutine(HostRestoreAfterDelay()); Log.Debug($"Dawn contract sync restore scheduled for {dataKey}"); } } private void ShipUnlockablesSynced(StartOfRound startOfRound) { NetworkManager singleton = NetworkManager.Singleton; if (singleton == null || !singleton.IsServer) { StopClientCoroutine(); ClientDelayFinished = false; clientOwner = (MonoBehaviour?)(object)startOfRound; clientCoroutine = ((MonoBehaviour)startOfRound).StartCoroutine(ClientRestoreAfterDelay()); Log.Debug($"Dawn contract sync client restore scheduled for {dataKey}"); } } private void Clear(StartOfRound startOfRound) { Clear(); } private void RequestReceived(ulong senderClientId, string data) { if (!HostRestoreFinished) { return; } NetworkManager singleton = NetworkManager.Singleton; if (singleton != null && singleton.IsServer) { rawData = writeData() ?? string.Empty; dataReceived = true; if (HatNet.SendToClient(dataKey, senderClientId, rawData)) { Log.Debug($"Dawn contract sync request answered for {dataKey}"); } } } private void DataReceived(ulong senderClientId, string data) { NetworkManager singleton = NetworkManager.Singleton; if (singleton == null || !singleton.IsServer) { ReceiveData(data); } } private void ReceiveData(string data) { rawData = data ?? string.Empty; dataReceived = true; Log.Debug($"Dawn contract sync data received for {dataKey}"); if (ClientDelayFinished) { NetworkManager singleton = NetworkManager.Singleton; if (singleton == null || !singleton.IsServer) { readData(rawData); } } } private IEnumerator HostRestoreAfterDelay() { yield return DelayWait; string savedData; bool loaded = DawnContractData.LoadString(dataKey, out savedData) && savedData != null; string contractData = (rawData = (loaded ? savedData : string.Empty)); dataReceived = true; readData(contractData); if (loaded) { Log.Debug($"Dawn contract sync data loaded for {dataKey}"); } else { Log.Debug($"Dawn contract sync data was missing for {dataKey}"); } rawData = writeData() ?? string.Empty; PublishRaw(rawData); HostRestoreFinished = true; hostCoroutine = null; hostOwner = null; Log.Debug($"Dawn contract sync restore finished for {dataKey}"); } private IEnumerator ClientRestoreAfterDelay() { yield return DelayWait; ClientDelayFinished = true; clientCoroutine = null; clientOwner = null; if (dataReceived) { readData(rawData); Log.Debug($"Dawn contract sync client data applied for {dataKey}"); } else { Request(); } } private bool PublishRaw(string data) { NetworkManager singleton = NetworkManager.Singleton; if (singleton == null || !singleton.IsServer) { return false; } return HatNet.SendToClients(dataKey, data ?? string.Empty); } private void StopHostCoroutine() { if (hostCoroutine != null && !((Object)(object)hostOwner == (Object)null)) { hostOwner.StopCoroutine(hostCoroutine); hostCoroutine = null; hostOwner = null; Log.Debug($"Dawn contract sync restore stopped for {dataKey}"); } } private void StopClientCoroutine() { if (clientCoroutine != null && !((Object)(object)clientOwner == (Object)null)) { clientOwner.StopCoroutine(clientCoroutine); clientCoroutine = null; clientOwner = null; Log.Debug($"Dawn contract sync client restore stopped for {dataKey}"); } } } public static class DawnFindResource { public static void Add(Registry registry, NamespacedKey key, Action apply) where TInfo : INamespaced { if (registry == null) { Log.Error($"Dawn resource registry was missing for {key}"); return; } if (apply == null) { Log.Error($"Dawn resource request was missing for {key}"); return; } DawnFindResource.OnFreeze(registry, (Action)delegate { if (DawnFindResource.Get(registry, key, out TInfo info)) { apply(info); Log.Debug($"Dawn resource request applied for {key}"); } }); Log.Debug($"Dawn resource request added for {key}"); } public static void Add(Registry registry, NamespacedKey key, Func read, Action apply) where TInfo : INamespaced where TValue : class { DawnFindResource.Add(registry, key, read, typeof(TValue).Name, apply); } public static void Add(Registry registry, NamespacedKey key, Func read, string name, Action apply) where TInfo : INamespaced where TValue : class { if (registry == null) { Log.Error("Dawn resource registry was missing for " + name); return; } if (read == null) { Log.Error("Dawn resource lookup was missing for " + name); return; } if (apply == null) { Log.Error("Dawn resource request was missing for " + name); return; } DawnFindResource.OnFreeze(registry, (Action)delegate { if (DawnFindResource.Get(registry, key, read, name, out TValue value)) { apply(value); Log.Debug("Dawn resource request applied for " + name); } }); Log.Debug("Dawn resource request added for " + name); } public static void OnFreeze(Registry registry, Action action) where TInfo : INamespaced { if (registry == null) { Log.Error("Dawn registry was missing for " + typeof(TInfo).Name); } else if (action == null) { Log.Error("Dawn registry freeze action was missing for " + typeof(TInfo).Name); } else if (registry.IsFrozen) { Log.Debug("Dawn registry was already frozen for " + typeof(TInfo).Name); action(); } else { registry.OnFreeze += action; Log.Debug("Dawn registry freeze action added for " + typeof(TInfo).Name); } } public static bool Get(Registry registry, NamespacedKey key, out TInfo info) where TInfo : INamespaced { info = default(TInfo); if (registry == null) { Log.Error($"Dawn resource registry was missing for {key}"); return false; } if (registry.TryGetValue(key, ref info)) { Log.Debug($"Dawn resource found for {key}"); return true; } info = default(TInfo); Log.Warn($"Dawn resource was not found for {key}"); return false; } public static bool Get(Registry registry, NamespacedKey key, Func read, out TValue value) where TInfo : INamespaced where TValue : class { return DawnFindResource.Get(registry, key, read, typeof(TValue).Name, out value); } public static bool Get(Registry registry, NamespacedKey key, Func read, string name, out TValue value) where TInfo : INamespaced where TValue : class { value = null; if (read == null) { Log.Error("Dawn resource lookup was missing for " + name); return false; } if (!DawnFindResource.Get(registry, key, out TInfo info)) { return false; } TValue val = read(info); if (Missing(val)) { Log.Warn("Dawn resource value was not found for " + name); return false; } value = val; Log.Debug("Dawn resource value found for " + name); return true; } private static bool Missing([NotNullWhen(false)] TValue? value) where TValue : class { if (value == null) { return true; } Object val = (Object)(object)((value is Object) ? value : null); if (val != null && val == (Object)null) { return true; } return false; } } public class DuskConfigInjector(string dawnModGUID) { public readonly string dawnModGUID = dawnModGUID; public static T Get(string dawnModGUID, string settingName) { if (string.IsNullOrWhiteSpace(dawnModGUID)) { Log.Error("Dusk config read failed because the Dawn mod GUID was empty"); return default(T); } if (string.IsNullOrWhiteSpace(settingName)) { Log.Error("Dusk config read failed because the setting name was empty"); return default(T); } DuskMod val = ((IEnumerable)DuskMod.AllMods).FirstOrDefault((Func)((DuskMod duskMod) => duskMod.Plugin.GUID == dawnModGUID)); if (val == null) { Log.Warn("Dusk config read failed because Dawn mod GUID '" + dawnModGUID + "' was not found"); return default(T); } ConfigEntryBase val2 = ((IEnumerable)val.ConfigEntries).FirstOrDefault((Func)((ConfigEntryBase configEntry) => configEntry.Definition.Key == settingName)); if (val2 == null) { Log.Warn("Dusk config setting '" + settingName + "' was not found"); return default(T); } if (!(val2 is ConfigEntry val3)) { Log.Error("Dusk config setting '" + settingName + "' was not a " + typeof(T).Name); return default(T); } return val3.Value; } public ConfigEntry? Bind(string section, string settingName, T defaultValue, string description) { if (string.IsNullOrWhiteSpace(dawnModGUID)) { Log.Error("DuskConfigInjector was created with an empty dawnModGUID"); return null; } if (string.IsNullOrWhiteSpace(section)) { Log.Error("Dusk config section was empty"); return null; } if (string.IsNullOrWhiteSpace(settingName)) { Log.Error("Dusk config key was empty"); return null; } DuskMod val = ((IEnumerable)DuskMod.AllMods).FirstOrDefault((Func)((DuskMod duskMod) => duskMod.Plugin.GUID == dawnModGUID)); if (val == null) { Log.Warn("Dusk GUID was not found for '" + dawnModGUID + "'"); return null; } ConfigContext val2 = val.ConfigManager.CreateConfigSection(section); try { return val2.Bind(settingName, description, defaultValue); } finally { ((IDisposable)val2)?.Dispose(); } } } public static class FindItems { public static NamespacedKey? DawnKey(GrabbableObject item) { if ((Object)(object)item == (Object)null) { Log.Warn("Dawn item key read skipped because item was missing"); return null; } if ((Object)(object)item.itemProperties == (Object)null) { Log.Warn("Dawn item key read skipped because item properties were missing"); return null; } return ((DawnBaseInfo)(object)ItemExtensions.GetDawnInfo(item.itemProperties))?.TypedKey; } public static bool MatchesDawnKey(GrabbableObject item, NamespacedKey itemKey) { if (itemKey == null) { Log.Warn("Dawn item match skipped because item key was missing"); return false; } return ((object)DawnKey(item))?.Equals((object?)itemKey) ?? false; } public static List Active(Func? itemFilter = null) { GrabbableObject[] array = Object.FindObjectsByType((FindObjectsInactive)0, (FindObjectsSortMode)0); List list = new List(); foreach (GrabbableObject val in array) { if (itemFilter == null || itemFilter(val)) { list.Add(val); } } Log.Debug($"{list.Count} items found"); return list; } public static List Active(NamespacedKey itemKey, Func? itemFilter = null) { List list = Active((Func?)((GrabbableObject item) => MatchesDawnKey(item, itemKey) && (itemFilter == null || itemFilter(item)))); Log.Debug($"{list.Count} Dawn items found"); return list; } public static GrabbableObject? ClosestTo(Vector3 position, float radius, Func? itemFilter = null) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) GrabbableObject[] items = Object.FindObjectsByType((FindObjectsInactive)0, (FindObjectsSortMode)0); GrabbableObject val = ClosestTo(position, radius, items, itemFilter); Log.Debug(((Object)(object)val == (Object)null) ? "Closest item was not found" : "Closest item found"); return val; } public static GrabbableObject? ClosestTo(NamespacedKey itemKey, Vector3 position, float radius, Func? itemFilter = null) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) GrabbableObject[] items = Object.FindObjectsByType((FindObjectsInactive)0, (FindObjectsSortMode)0); GrabbableObject val = ClosestTo(itemKey, position, radius, items, itemFilter); Log.Debug(((Object)(object)val == (Object)null) ? "Closest Dawn item was not found" : "Closest Dawn item found"); return val; } public static GrabbableObject? ClosestTo(Vector3 position, float radius, IReadOnlyList items, Func? itemFilter = null) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) float num = radius * radius; GrabbableObject val = null; for (int i = 0; i < items.Count; i++) { GrabbableObject val2 = items[i]; if (!((Object)(object)val2 == (Object)null) && (itemFilter == null || itemFilter(val2))) { Vector3 val3 = ((Component)val2).transform.position - position; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (!(sqrMagnitude >= num)) { num = sqrMagnitude; val = val2; } } } Log.Debug(((Object)(object)val == (Object)null) ? "Closest item was not found" : "Closest item found"); return val; } public static GrabbableObject? ClosestTo(NamespacedKey itemKey, Vector3 position, float radius, IReadOnlyList items, Func? itemFilter = null) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) GrabbableObject val = ClosestTo(position, radius, items, (GrabbableObject item) => MatchesDawnKey(item, itemKey) && (itemFilter == null || itemFilter(item))); Log.Debug(((Object)(object)val == (Object)null) ? "Closest Dawn item was not found" : "Closest Dawn item found"); return val; } } public delegate void HatNetMessageHandler(ulong senderClientId, string data); public static class HatNet { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__ServerMessageReceived; public static HandleNamedMessageDelegate <1>__ClientMessageReceived; } private const string ServerMessageName = "MrHat.Lib.HatNet.Server"; private const string ClientMessageName = "MrHat.Lib.HatNet.Client"; private static readonly Dictionary> ServerHandlers = new Dictionary>(StringComparer.Ordinal); private static readonly Dictionary> ClientHandlers = new Dictionary>(StringComparer.Ordinal); private static NetworkManager? registeredManager; private static bool registeredMessages; internal static void Init() { //IL_0067: 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) //IL_0072: Expected O, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; if (((singleton != null) ? singleton.CustomMessagingManager : null) != null && (!registeredMessages || !((Object)(object)registeredManager == (Object)(object)singleton))) { Shutdown(); registeredManager = singleton; registeredMessages = true; CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager; object obj = <>O.<0>__ServerMessageReceived; if (obj == null) { HandleNamedMessageDelegate val = ServerMessageReceived; <>O.<0>__ServerMessageReceived = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("MrHat.Lib.HatNet.Server", (HandleNamedMessageDelegate)obj); CustomMessagingManager customMessagingManager2 = singleton.CustomMessagingManager; object obj2 = <>O.<1>__ClientMessageReceived; if (obj2 == null) { HandleNamedMessageDelegate val2 = ClientMessageReceived; <>O.<1>__ClientMessageReceived = val2; obj2 = (object)val2; } customMessagingManager2.RegisterNamedMessageHandler("MrHat.Lib.HatNet.Client", (HandleNamedMessageDelegate)obj2); Log.Info("HatNet messages registered"); } } internal static void Shutdown() { if (registeredMessages) { NetworkManager? obj = registeredManager; if (((obj != null) ? obj.CustomMessagingManager : null) != null) { registeredManager.CustomMessagingManager.UnregisterNamedMessageHandler("MrHat.Lib.HatNet.Server"); registeredManager.CustomMessagingManager.UnregisterNamedMessageHandler("MrHat.Lib.HatNet.Client"); registeredManager = null; registeredMessages = false; Log.Info("HatNet messages unregistered"); return; } } registeredManager = null; registeredMessages = false; } public static void RegisterServer(NamespacedKey key, HatNetMessageHandler handler) { if (Register(ServerHandlers, key, handler)) { Log.Debug($"HatNet server registered for {key}"); } } public static void RegisterClient(NamespacedKey key, HatNetMessageHandler handler) { if (Register(ClientHandlers, key, handler)) { Log.Debug($"HatNet client registered for {key}"); } } public static void UnregisterServer(NamespacedKey key, HatNetMessageHandler handler) { if (Unregister(ServerHandlers, key, handler)) { Log.Debug($"HatNet server unregistered for {key}"); } } public static void UnregisterClient(NamespacedKey key, HatNetMessageHandler handler) { if (Unregister(ClientHandlers, key, handler)) { Log.Debug($"HatNet client unregistered for {key}"); } } public unsafe static bool SendToServer(NamespacedKey key, string data) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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) Init(); NetworkManager val = registeredManager ?? NetworkManager.Singleton; if (val == null || !val.IsListening || val.CustomMessagingManager == null) { Log.Debug("HatNet server message was not sent because networking is unavailable"); return false; } FastBufferWriter val2 = CreateWriter(key, data); try { val.CustomMessagingManager.SendNamedMessage("MrHat.Lib.HatNet.Server", 0uL, val2, (NetworkDelivery)4); return true; } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } public unsafe static bool SendToClients(NamespacedKey key, string data) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) Init(); NetworkManager val = registeredManager ?? NetworkManager.Singleton; if (val == null || !val.IsListening || !val.IsServer || val.CustomMessagingManager == null) { Log.Debug("HatNet client message was not sent because the server is unavailable"); return false; } FastBufferWriter val2 = CreateWriter(key, data); try { val.CustomMessagingManager.SendNamedMessageToAll("MrHat.Lib.HatNet.Client", val2, (NetworkDelivery)4); return true; } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } public unsafe static bool SendToClient(NamespacedKey key, ulong clientId, string data) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) Init(); NetworkManager val = registeredManager ?? NetworkManager.Singleton; if (val == null || !val.IsListening || !val.IsServer || val.CustomMessagingManager == null) { Log.Debug("HatNet client message was not sent because the server is unavailable"); return false; } FastBufferWriter val2 = CreateWriter(key, data); try { val.CustomMessagingManager.SendNamedMessage("MrHat.Lib.HatNet.Client", clientId, val2, (NetworkDelivery)4); return true; } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } private static bool Register(Dictionary> handlers, NamespacedKey key, HatNetMessageHandler handler) { if (key == null) { throw new ArgumentNullException("key"); } if (handler == null) { throw new ArgumentNullException("handler"); } string key2 = ((object)key).ToString(); if (!handlers.TryGetValue(key2, out List value)) { value = (handlers[key2] = new List()); } if (value.Contains(handler)) { return false; } value.Add(handler); return true; } private static bool Unregister(Dictionary> handlers, NamespacedKey key, HatNetMessageHandler handler) { if (key == null) { throw new ArgumentNullException("key"); } if (handler == null) { throw new ArgumentNullException("handler"); } string key2 = ((object)key).ToString(); if (!handlers.TryGetValue(key2, out List value)) { return false; } if (!value.Remove(handler)) { return false; } if (value.Count == 0) { handlers.Remove(key2); } return true; } private static FastBufferWriter CreateWriter(NamespacedKey key, string data) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) string text = ((object)key).ToString(); string text2 = data ?? string.Empty; int num = Math.Max(64, text.Length * 4 + text2.Length * 4 + 64); FastBufferWriter result = default(FastBufferWriter); ((FastBufferWriter)(ref result))..ctor(num, (Allocator)2, -1); ((FastBufferWriter)(ref result)).WriteValueSafe(text, false); ((FastBufferWriter)(ref result)).WriteValueSafe(text2, false); return result; } private static void ServerMessageReceived(ulong senderClientId, FastBufferReader reader) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) MessageReceived(senderClientId, reader, ServerHandlers); } private static void ClientMessageReceived(ulong senderClientId, FastBufferReader reader) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) MessageReceived(senderClientId, reader, ClientHandlers); } private static void MessageReceived(ulong senderClientId, FastBufferReader reader, Dictionary> handlers) { string text = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false); string data = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref data, false); if (!handlers.TryGetValue(text, out List value)) { Log.Debug("HatNet message ignored because no handler is registered for " + text); return; } HatNetMessageHandler[] array = value.ToArray(); for (int i = 0; i < array.Length; i++) { array[i](senderClientId, data); } } } public static class LocalPlayerCollider { public static Collider? Current() { GameNetworkManager instance = GameNetworkManager.Instance; if ((Object)(object)instance == (Object)null) { return null; } PlayerControllerB localPlayerController = instance.localPlayerController; if ((Object)(object)localPlayerController == (Object)null) { return null; } return localPlayerController.playerCollider; } public static bool Matches(Collider other) { if ((Object)(object)other == (Object)null) { return false; } return (Object)(object)other == (Object)(object)Current(); } } [DisallowMultipleComponent] public class LocalPlayerTrigger : MonoBehaviour { private Collider? playerCollider; private bool playerInside; public Collider? PlayerCollider => playerCollider ?? (playerCollider = LocalPlayerCollider.Current()); public bool PlayerInside => playerInside; public event Action? Entered; public event Action? Exited; public static LocalPlayerTrigger? Arm(Collider? bounds) { if ((Object)(object)bounds == (Object)null) { return null; } bounds.isTrigger = true; Rigidbody val = ((Component)bounds).gameObject.GetComponent() ?? ((Component)bounds).gameObject.AddComponent(); val.isKinematic = false; val.useGravity = false; val.constraints = (RigidbodyConstraints)126; LocalPlayerTrigger result = default(LocalPlayerTrigger); if (!((Component)bounds).TryGetComponent(ref result)) { result = ((Component)bounds).gameObject.AddComponent(); } return result; } private void OnDisable() { playerInside = false; } private void OnDestroy() { this.Entered = null; this.Exited = null; playerCollider = null; playerInside = false; } private void OnTriggerEnter(Collider other) { MarkEntered(other); } private void OnTriggerStay(Collider other) { MarkEntered(other); } private void OnTriggerExit(Collider other) { if (MatchesPlayer(other)) { playerInside = false; this.Exited?.Invoke(other); } } private void MarkEntered(Collider other) { if (!playerInside && MatchesPlayer(other)) { playerInside = true; this.Entered?.Invoke(other); } } private bool MatchesPlayer(Collider other) { if ((Object)(object)other == (Object)null) { return false; } Collider val = LocalPlayerCollider.Current(); if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)playerCollider) { playerCollider = val; } return (Object)(object)other == (Object)(object)playerCollider; } } [Serializable] public class PlayerAnimatorControllerReplacement { public RuntimeAnimatorController animatorController = null; public RuntimeAnimatorController remoteAnimatorController = null; } public class PlayerAnimatorTools { private static readonly int SprintingHash = Animator.StringToHash("Sprinting"); private static readonly int JumpingHash = Animator.StringToHash("Jumping"); private static readonly int WalkingHash = Animator.StringToHash("Walking"); private static readonly int CrouchingHash = Animator.StringToHash("crouching"); private static readonly string[] RequiredPlayerLayers = new string[5] { "UpperBodyEmotes", "EmotesNoArms", "HoldingItemsRightHand", "HoldingItemsBothHands", "SpecialAnimations" }; private Animator? animator; private RuntimeAnimatorController? originalController; private RuntimeAnimatorController? replacementController; public bool IsActive { get; private set; } public bool ApplyHeld(PlayerControllerB? player, PlayerAnimatorControllerReplacement? replacement) { if (IsActive) { return true; } if ((Object)(object)player == (Object)null || replacement == null) { return false; } RuntimeAnimatorController targetController = (((Object)(object)player == (Object)(object)GameNetworkManager.Instance.localPlayerController) ? replacement.animatorController : replacement.remoteAnimatorController); return Apply(player.playerBodyAnimator, targetController); } public bool Apply(Animator? targetAnimator, RuntimeAnimatorController? targetController) { //IL_0052: 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) if (IsActive) { return true; } if ((Object)(object)targetAnimator == (Object)null || (Object)(object)targetController == (Object)null) { return false; } animator = targetAnimator; originalController = targetAnimator.runtimeAnimatorController; replacementController = targetController; AnimatorStateInfo currentAnimatorStateInfo = targetAnimator.GetCurrentAnimatorStateInfo(0); float normalizedTime = ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).normalizedTime; bool flag = targetAnimator.GetBool(CrouchingHash); bool flag2 = targetAnimator.GetBool(WalkingHash); bool flag3 = targetAnimator.GetBool(JumpingHash); bool flag4 = targetAnimator.GetBool(SprintingHash); targetAnimator.runtimeAnimatorController = targetController; for (int i = 0; i < RequiredPlayerLayers.Length; i++) { if (targetAnimator.GetLayerIndex(RequiredPlayerLayers[i]) < 0) { targetAnimator.runtimeAnimatorController = originalController; Log.Warn("Player animator replacement missing layer " + RequiredPlayerLayers[i]); animator = null; originalController = null; replacementController = null; return false; } } targetAnimator.Rebind(); targetAnimator.Play(((AnimatorStateInfo)(ref currentAnimatorStateInfo)).fullPathHash, 0, normalizedTime); targetAnimator.SetBool(CrouchingHash, flag); targetAnimator.SetBool(WalkingHash, flag2); targetAnimator.SetBool(JumpingHash, flag3); targetAnimator.SetBool(SprintingHash, flag4); targetAnimator.Update(0f); IsActive = true; return true; } public void Restore() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (IsActive) { if ((Object)(object)animator != (Object)null && (Object)(object)animator.runtimeAnimatorController == (Object)(object)replacementController) { AnimatorStateInfo currentAnimatorStateInfo = animator.GetCurrentAnimatorStateInfo(0); float normalizedTime = ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).normalizedTime; bool flag = animator.GetBool(CrouchingHash); bool flag2 = animator.GetBool(WalkingHash); bool flag3 = animator.GetBool(JumpingHash); bool flag4 = animator.GetBool(SprintingHash); animator.runtimeAnimatorController = originalController; animator.Rebind(); animator.Play(((AnimatorStateInfo)(ref currentAnimatorStateInfo)).fullPathHash, 0, normalizedTime); animator.SetBool(CrouchingHash, flag); animator.SetBool(WalkingHash, flag2); animator.SetBool(JumpingHash, flag3); animator.SetBool(SprintingHash, flag4); animator.Update(0f); } animator = null; originalController = null; replacementController = null; IsActive = false; } } } public abstract class PlayerAnimatorReplacement : GrabbableObject { [Header("Player Animator")] [SerializeField] private PlayerAnimatorControllerReplacement playerAnimator = new PlayerAnimatorControllerReplacement(); private readonly PlayerAnimatorTools playerAnimatorTools = new PlayerAnimatorTools(); protected bool ApplyHeldPlayerAnimator() { return playerAnimatorTools.ApplyHeld(base.playerHeldBy, playerAnimator); } protected void RestoreHeldPlayerAnimator() { playerAnimatorTools.Restore(); } protected override void __initializeVariables() { ((GrabbableObject)this).__initializeVariables(); } protected override void __initializeRpcs() { ((GrabbableObject)this).__initializeRpcs(); } protected internal override string __getTypeName() { return "PlayerAnimatorReplacement"; } } public static class UnlockableTools { public static bool Bought(NamespacedKey key) { if (key == null) { Log.Warn("Dawn unlockable bought check skipped because key was missing"); return false; } StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { Log.Debug($"Dawn unlockable bought check skipped because no round exists for {key}"); return false; } List list = instance.unlockablesList?.unlockables; if (list == null) { Log.Debug($"Dawn unlockable bought check skipped because no unlockable list exists for {key}"); return false; } for (int i = 0; i < list.Count; i++) { UnlockableItem val = list[i]; if (val == null) { continue; } DawnUnlockableItemInfo dawnInfo = UnlockableItemExtensions.GetDawnInfo(val); if (dawnInfo != null && ((object)((DawnBaseInfo)(object)dawnInfo).TypedKey).Equals((object?)key)) { if (val.hasBeenUnlockedByPlayer) { Log.Debug($"Dawn unlockable bought check passed for {key}"); return true; } Log.Debug($"Dawn unlockable bought check failed because unlockable is locked for {key}"); return false; } } Log.Debug($"Dawn unlockable bought check failed because unlockable was not found for {key}"); return false; } public static bool Bought(string namespacedKey) { if (string.IsNullOrWhiteSpace(namespacedKey)) { Log.Warn("Dawn unlockable bought check skipped because key string was empty"); return false; } NamespacedKey val = default(NamespacedKey); if (!NamespacedKey.TryParse(namespacedKey, ref val)) { Log.Warn("Dawn unlockable bought check skipped because key string could not be parsed for " + namespacedKey); return false; } return Bought(val.AsTyped()); } } } namespace HatLib.Patches { [HarmonyPatch(typeof(GameNetworkManager))] internal static class GameNetworkManagerPatches { [HarmonyPostfix] [HarmonyPatch("Start")] private static void StartPostfix() { HatNet.Init(); } [HarmonyPostfix] [HarmonyPatch("SaveGame")] private static void SaveGamePostfix() { RoundEvents.GameSaved(); } } [HarmonyPatch(typeof(StartOfRound))] internal static class StartOfRoundPatches { [HarmonyPostfix] [HarmonyPatch("Start")] private static void StartPostfix(StartOfRound __instance) { RoundEvents.RoundStart(__instance); } [HarmonyPostfix] [HarmonyPatch("EndOfGameClientRpc")] private static void EndOfGameClientRpcPostfix(StartOfRound __instance) { RoundEvents.RoundEnd(__instance); } [HarmonyPostfix] [HarmonyPatch("ShipHasLeft")] private static void ShipHasLeftPostfix(StartOfRound __instance) { RoundEvents.ShipLeft(__instance); } [HarmonyPostfix] [HarmonyPatch("SetShipReadyToLand")] private static void SetShipReadyToLandPostfix(StartOfRound __instance) { RoundEvents.ShipReadyToLand(__instance); } [HarmonyPostfix] [HarmonyPatch("LoadShipGrabbableItems")] private static void LoadShipGrabbableItemsPostfix(StartOfRound __instance) { RoundEvents.ShipItemsLoaded(__instance); } [HarmonyPostfix] [HarmonyPatch("SyncShipUnlockablesClientRpc")] private static void SyncShipUnlockablesClientRpcPostfix(StartOfRound __instance) { RoundEvents.ShipUnlockablesSynced(__instance); } [HarmonyPostfix] [HarmonyPatch("ResetPlayersLoadedValueClientRpc")] private static void ResetPlayersLoadedValueClientRpcPostfix(StartOfRound __instance) { RoundEvents.PlayersLoadedReset(__instance); } [HarmonyPostfix] [HarmonyPatch("OnLocalDisconnect")] private static void OnLocalDisconnectPostfix(StartOfRound __instance) { RoundEvents.Disconnect(__instance); HatNet.Shutdown(); } [HarmonyPostfix] [HarmonyPatch("OnDestroy")] private static void OnDestroyPostfix(StartOfRound __instance) { RoundEvents.Destroy(__instance); HatNet.Shutdown(); } } } namespace __GEN { internal class NetworkVariableSerializationHelper { [RuntimeInitializeOnLoadMethod] internal static void InitializeSerialization() { } } } namespace HatLib.NetcodePatcher { [AttributeUsage(AttributeTargets.Module)] internal class NetcodePatchedAssemblyAttribute : Attribute { } }