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 ServeYouRight v1.0.7
ServeYouRight.dll
Decompiled 3 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using ModAssetOwnership; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ServeYouRight")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyProduct("ServeYouRight")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")] [assembly: AssemblyFileVersion("1.0.7")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.7.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] 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 ModAssetOwnership { internal sealed class AssetOwner { internal readonly string Guid; internal readonly string Name; internal readonly string AssemblyName; internal readonly string[] Resources; internal AssetOwner(string guid, string name, string assemblyName, string[] resources) { Guid = guid; Name = (string.IsNullOrWhiteSpace(name) ? guid : name); AssemblyName = assemblyName; Resources = resources; } } internal static class AssetOwnerMatching { internal static AssetOwner? Resolve(string bundleName, IReadOnlyList<AssetOwner> plugins) { if (string.IsNullOrWhiteSpace(bundleName)) { return null; } AssetOwner assetOwner = null; bool flag = false; foreach (AssetOwner plugin in plugins) { if (string.IsNullOrWhiteSpace(plugin.Guid)) { continue; } bool flag2 = false; string[] resources = plugin.Resources; foreach (string text in resources) { if (text.Equals(bundleName, StringComparison.OrdinalIgnoreCase) || text.EndsWith("." + bundleName, StringComparison.OrdinalIgnoreCase)) { flag2 = true; break; } } if (flag2) { flag = true; if (assetOwner != null && !assetOwner.Guid.Equals(plugin.Guid, StringComparison.OrdinalIgnoreCase)) { return null; } assetOwner = plugin; } } if (flag) { return assetOwner; } string text2 = Normalize(Path.GetFileNameWithoutExtension(bundleName)); if (text2.Length == 0) { return null; } foreach (AssetOwner plugin2 in plugins) { if (!string.IsNullOrWhiteSpace(plugin2.Guid) && (!(text2 != Normalize(plugin2.Name)) || !(text2 != Normalize(plugin2.Guid)) || !(text2 != Normalize(plugin2.AssemblyName)))) { if (assetOwner != null && !assetOwner.Guid.Equals(plugin2.Guid, StringComparison.OrdinalIgnoreCase)) { return null; } assetOwner = plugin2; } } return assetOwner; } internal static void Add(Dictionary<string, AssetOwner> owners, HashSet<string> ambiguous, string name, AssetOwner owner) { if (!ambiguous.Contains(name)) { if (owners.TryGetValue(name, out AssetOwner value) && !value.Guid.Equals(owner.Guid, StringComparison.OrdinalIgnoreCase)) { owners.Remove(name); ambiguous.Add(name); } else { owners[name] = owner; } } } private static string Normalize(string value) { StringBuilder stringBuilder = new StringBuilder(); string text = value ?? ""; foreach (char c in text) { if (char.IsLetterOrDigit(c)) { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } } } namespace ServerSyncModTemplate { [BepInPlugin("sighsorry.ServeYouRight", "ServeYouRight", "1.0.7")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class ServerSyncModTemplatePlugin : BaseUnityPlugin { public enum Toggle { On = 1, Off = 0 } private class ConfigurationManagerAttributes { public int? Order; } internal const string ModName = "ServeYouRight"; internal const string ModVersion = "1.0.7"; internal const string Author = "sighsorry"; private const string ModGUID = "sighsorry.ServeYouRight"; private static string ConfigFileName = "sighsorry.ServeYouRight.cfg"; private static string ConfigFileFullPath; private static ServerSyncModTemplatePlugin? _instance; private readonly Harmony _harmony = new Harmony("sighsorry.ServeYouRight"); public static readonly ManualLogSource ServerSyncModTemplateLogger; private FileSystemWatcher? _watcher; private readonly object _reloadLock = new object(); private static readonly object DynamicConfigLock; private DateTime _lastConfigReloadTime; private static readonly TimeSpan ConfigReloadDelay; private static readonly Dictionary<string, PerModCategoryConfig> PerModConfigs; private static bool _pendingDynamicConfigSave; public void Awake() { _instance = this; RunWithConfigAutoSaveDisabled(delegate { FoodSourceCatalog.Initialize(); Localization.OnLanguageChange = (Action)Delegate.Combine(Localization.OnLanguageChange, new Action(OnLanguageChange)); Assembly executingAssembly = Assembly.GetExecutingAssembly(); _harmony.PatchAll(executingAssembly); SaveConfigWithoutWatcher(); SetupWatcher(); }); } private void OnDestroy() { Localization.OnLanguageChange = (Action)Delegate.Remove(Localization.OnLanguageChange, new Action(OnLanguageChange)); _watcher?.Dispose(); _watcher = null; try { RunWithConfigAutoSaveDisabled(SaveConfigWithoutWatcher); } finally { FoodSourceCatalog.Dispose(); ServingTrayMenu.Dispose(); FeasterFoodInjector.ResetWorld(); _harmony.UnpatchSelf(); PerModConfigs.Clear(); _instance = null; } } private void Update() { if (FoodSourceCatalog.RefreshPending) { FoodSourceCatalog.RefreshPending = false; FeasterFoodInjector.RefreshFoodPiecesAndMenu(ObjectDB.instance); } } private void OnLanguageChange() { FeasterFoodInjector.RefreshFoodPiecesAndMenu(ObjectDB.instance); } private void SetupWatcher() { _watcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName); _watcher.Changed += ReadConfigValues; _watcher.Created += ReadConfigValues; _watcher.Renamed += ReadConfigValues; _watcher.IncludeSubdirectories = true; _watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _watcher.EnableRaisingEvents = true; } private void ReadConfigValues(object sender, FileSystemEventArgs e) { DateTime utcNow = DateTime.UtcNow; if (utcNow - _lastConfigReloadTime < ConfigReloadDelay) { return; } _lastConfigReloadTime = utcNow; lock (_reloadLock) { if (!File.Exists(ConfigFileFullPath)) { ServerSyncModTemplateLogger.LogWarning((object)"Config file does not exist. Skipping reload."); return; } try { ServerSyncModTemplateLogger.LogDebug((object)"Reloading configuration..."); ReloadConfigValues(); ServerSyncModTemplateLogger.LogInfo((object)"Configuration reload complete."); } catch (Exception ex) { ServerSyncModTemplateLogger.LogError((object)("Error reloading configuration: " + ex.Message)); } finally { _lastConfigReloadTime = DateTime.UtcNow; } } } private void ReloadConfigValues() { RunWithConfigAutoSaveDisabled(delegate { ((BaseUnityPlugin)this).Config.Reload(); FeasterFoodInjector.RefreshFoodPiecesAndMenu(ObjectDB.instance); }); } private void RunWithConfigAutoSaveDisabled(Action action) { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; try { action(); } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } private void SaveConfigWithoutWatcher() { FileSystemWatcher watcher = _watcher; bool flag = watcher?.EnableRaisingEvents ?? false; if (flag) { watcher.EnableRaisingEvents = false; } try { ((BaseUnityPlugin)this).Config.Save(); } finally { if (flag && watcher != null) { watcher.EnableRaisingEvents = true; } } } internal static bool UseModSpecificTab(FoodSourceMod mod, PieceCategory category) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected I4, but got Unknown PerModCategoryConfig orCreatePerModConfig = GetOrCreatePerModConfig(mod); if ((int)category != 0) { return (category - 6) switch { 1 => orCreatePerModConfig.Food.Value == Toggle.On, 2 => orCreatePerModConfig.Meads.Value == Toggle.On, 0 => orCreatePerModConfig.Feasts.Value == Toggle.On, _ => false, }; } return orCreatePerModConfig.Misc.Value == Toggle.On; } private static PerModCategoryConfig GetOrCreatePerModConfig(FoodSourceMod mod) { lock (DynamicConfigLock) { if (PerModConfigs.TryGetValue(mod.Id, out PerModCategoryConfig value)) { return value; } ServerSyncModTemplatePlugin instance = _instance; if ((Object)(object)instance == (Object)null) { throw new InvalidOperationException("Plugin instance is not initialized."); } string section = "ServingTray - " + SanitizeConfigText(mod.DisplayName) + " (" + SanitizeConfigText(mod.Id) + ")"; PerModCategoryConfig created = null; instance.RunWithConfigAutoSaveDisabled(delegate { created = new PerModCategoryConfig(instance.config(section, "Misc", Toggle.On, "If on, '" + mod.DisplayName + "' Misc pieces on Feaster go to 'Misc - " + mod.DisplayName + "'. If off, they merge into vanilla Misc.", 400), instance.config(section, "Food", Toggle.On, "If on, '" + mod.DisplayName + "' Food items go to 'Food - " + mod.DisplayName + "'. If off, they merge into vanilla Food.", 300), instance.config(section, "Meads", Toggle.On, "If on, '" + mod.DisplayName + "' Mead items go to 'Meads - " + mod.DisplayName + "'. If off, they merge into vanilla Meads.", 200), instance.config(section, "Feasts", Toggle.On, "If on, '" + mod.DisplayName + "' Feast items go to 'Feasts - " + mod.DisplayName + "'. If off, they merge into vanilla Feasts.", 100)); }); PerModConfigs[mod.Id] = created; _pendingDynamicConfigSave = true; return created; } } internal static void FlushPendingDynamicConfigSave() { ServerSyncModTemplatePlugin instance = _instance; if (!((Object)(object)instance == (Object)null)) { bool pendingDynamicConfigSave; lock (DynamicConfigLock) { pendingDynamicConfigSave = _pendingDynamicConfigSave; _pendingDynamicConfigSave = false; } if (pendingDynamicConfigSave) { instance.RunWithConfigAutoSaveDisabled(instance.SaveConfigWithoutWatcher); } } } private static string SanitizeConfigText(string text) { if (string.IsNullOrWhiteSpace(text)) { return "Unknown"; } char[] array = text.ToCharArray(); for (int i = 0; i < array.Length; i++) { if (array[i] == '\r' || "=\n\t\\\"'[]".IndexOf(array[i]) >= 0 || char.IsControl(array[i])) { array[i] = '_'; } } string text2 = new string(array).Trim(); if (!string.IsNullOrWhiteSpace(text2)) { return text2; } return "Unknown"; } private ConfigEntry<T> config<T>(string group, string name, T value, string description, int order) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown return ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = order } })); } static ServerSyncModTemplatePlugin() { string configPath = Paths.ConfigPath; char directorySeparatorChar = Path.DirectorySeparatorChar; ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName; ServerSyncModTemplateLogger = Logger.CreateLogSource("ServeYouRight"); DynamicConfigLock = new object(); ConfigReloadDelay = TimeSpan.FromSeconds(1.0); PerModConfigs = new Dictionary<string, PerModCategoryConfig>(StringComparer.OrdinalIgnoreCase); } } internal sealed class PerModCategoryConfig { public ConfigEntry<ServerSyncModTemplatePlugin.Toggle> Misc { get; } public ConfigEntry<ServerSyncModTemplatePlugin.Toggle> Food { get; } public ConfigEntry<ServerSyncModTemplatePlugin.Toggle> Meads { get; } public ConfigEntry<ServerSyncModTemplatePlugin.Toggle> Feasts { get; } public PerModCategoryConfig(ConfigEntry<ServerSyncModTemplatePlugin.Toggle> misc, ConfigEntry<ServerSyncModTemplatePlugin.Toggle> food, ConfigEntry<ServerSyncModTemplatePlugin.Toggle> meads, ConfigEntry<ServerSyncModTemplatePlugin.Toggle> feasts) { Misc = misc; Food = food; Meads = meads; Feasts = feasts; } } [HarmonyPatch(typeof(ObjectDB), "Awake")] public static class ObjectDbAwakePatch { [HarmonyPriority(0)] private static void Postfix(ObjectDB __instance) { FeasterFoodInjector.BeginWorld(); FeasterFoodInjector.Inject(__instance); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] public static class ObjectDbCopyOtherDbPatch { [HarmonyPriority(0)] private static void Postfix(ObjectDB __instance) { FeasterFoodInjector.BeginWorld(); FeasterFoodInjector.Inject(__instance); } } [HarmonyPatch(typeof(Player), "SetPlaceMode")] public static class PlayerSetPlaceModePatch { private static void Postfix(Player __instance, PieceTable buildPieces) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && FeasterFoodInjector.IsFeasterTool(buildPieces)) { FeasterFoodInjector.RefreshFoodPiecesAndMenu(ObjectDB.instance); } } } [HarmonyPatch(typeof(Game), "Start")] internal static class GameStartedPatch { [HarmonyPriority(0)] private static void Postfix() { FeasterFoodInjector.BeginWorld(); FeasterFoodInjector.RefreshFoodPiecesAndMenu(ObjectDB.instance); } } [HarmonyPatch(typeof(ZNetScene), "OnDestroy")] internal static class FoodWorldShutdownPatch { private static void Prefix() { FoodSourceCatalog.ResetWorld(); FeasterFoodInjector.ResetWorld(); } } [HarmonyPatch(typeof(ItemDrop), "MakePiece")] public static class ItemDropMakePiecePatch { private static void Postfix(ItemDrop __instance) { //IL_002e: 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) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)((Component)__instance).GetComponent<ServeYouRightInjectedPieceMarker>() == (Object)null) { return; } ParticleSystem[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<ParticleSystem>(true); foreach (ParticleSystem obj in componentsInChildren) { obj.Stop(true, (ParticleSystemStopBehavior)0); obj.Clear(true); EmissionModule emission = obj.emission; ((EmissionModule)(ref emission)).enabled = false; MainModule main = obj.main; ((MainModule)(ref main)).playOnAwake = false; ParticleSystemRenderer component = ((Component)obj).GetComponent<ParticleSystemRenderer>(); if ((Object)(object)component != (Object)null) { ((Renderer)component).enabled = false; } } } } internal static class FeasterFoodInjector { private static bool _isInjecting; private static bool _shuttingDown; private static readonly Dictionary<Piece, (PieceCategory Category, FoodSourceMod? Source)> FoodGroups = new Dictionary<Piece, (PieceCategory, FoodSourceMod?)>(); private static readonly Action<Player> UpdateKnownRecipes = AccessTools.MethodDelegate<Action<Player>>(AccessTools.Method(typeof(Player), "UpdateKnownRecipesList", (Type[])null, (Type[])null), (object)null, true); private static readonly Action<Player> UpdateAvailablePieces = AccessTools.MethodDelegate<Action<Player>>(AccessTools.Method(typeof(Player), "UpdateAvailablePiecesList", (Type[])null, (Type[])null), (object)null, true); private static readonly Dictionary<string, FoodSourceMod> SourceModHitCache = new Dictionary<string, FoodSourceMod>(StringComparer.OrdinalIgnoreCase); internal static void ResetWorld() { _shuttingDown = true; FoodGroups.Clear(); SourceModHitCache.Clear(); } internal static void BeginWorld() { _shuttingDown = false; } internal static void RefreshFoodPiecesAndMenu(ObjectDB? objectDb) { if (!_shuttingDown && !((Object)(object)objectDb == (Object)null)) { Inject(objectDb); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { UpdateKnownRecipes(localPlayer); UpdateAvailablePieces(localPlayer); ServingTrayMenu.Refresh(); } } } public static void Inject(ObjectDB? objectDb) { if (_isInjecting || _shuttingDown || (Object)(object)objectDb == (Object)null) { return; } _isInjecting = true; try { PieceTable feasterPieceTable = GetFeasterPieceTable(objectDb); if (!((Object)(object)feasterPieceTable == (Object)null)) { FoodSourceCatalog.Refresh(); SourceModHitCache.Clear(); FoodGroups.Clear(); List<CandidateFood> candidateFoods = GetCandidateFoods(objectDb); int num = InjectIntoTable(feasterPieceTable, candidateFoods); UpdateExistingMenuGroups(feasterPieceTable); if (num > 0) { ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogInfo((object)$"Added {num} mod food pieces to feaster table '{((Object)feasterPieceTable).name}'."); } } } catch (Exception arg) { ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogError((object)$"Failed to inject feaster foods: {arg}"); } finally { _isInjecting = false; ServerSyncModTemplatePlugin.FlushPendingDynamicConfigSave(); } } private static List<CandidateFood> GetCandidateFoods(ObjectDB objectDb) { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Invalid comparison between Unknown and I4 //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) List<CandidateFood> list = new List<CandidateFood>(); FeastRoutingData feastRoutingData = BuildFeastRoutingData(objectDb); foreach (GameObject item in objectDb.m_items) { if ((Object)(object)item == (Object)null) { continue; } ItemDrop component = item.GetComponent<ItemDrop>(); SharedData val = component?.m_itemData?.m_shared; if (!((Object)(object)component == (Object)null) && val != null) { string prefabName = Utils.GetPrefabName(((Object)item).name); if (feastRoutingData.MaterialToResultPrefab.TryGetValue(prefabName, out GameObject value)) { list.Add(new CandidateFood(component, value, (PieceCategory)6)); } else if (!feastRoutingData.ResultPrefabNames.Contains(prefabName) && !((Object)(object)((Component)component).GetComponent<Feast>() != (Object)null) && (int)val.m_itemType == 2 && LooksLikeConsumableFood(val)) { PieceCategory category = (PieceCategory)(val.m_isDrink ? 8 : 7); list.Add(new CandidateFood(component, ((Component)component).gameObject, category)); } } } return list; } private static FeastRoutingData BuildFeastRoutingData(ObjectDB objectDb) { //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Invalid comparison between Unknown and I4 //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Invalid comparison between Unknown and I4 //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Invalid comparison between Unknown and I4 //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Invalid comparison between Unknown and I4 Dictionary<string, ItemDrop> dictionary = new Dictionary<string, ItemDrop>(StringComparer.OrdinalIgnoreCase); foreach (GameObject item in objectDb.m_items) { if ((Object)(object)item == (Object)null) { continue; } ItemDrop component = item.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null)) { string prefabName = Utils.GetPrefabName(((Object)item).name); if (!dictionary.ContainsKey(prefabName)) { dictionary[prefabName] = component; } } } Dictionary<string, GameObject> dictionary2 = new Dictionary<string, GameObject>(StringComparer.OrdinalIgnoreCase); HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair<string, ItemDrop> item2 in dictionary) { string key = item2.Key; ItemDrop value = item2.Value; SharedData val = value.m_itemData?.m_shared; Feast component2 = ((Component)value).GetComponent<Feast>(); if ((Object)(object)component2 == (Object)null || val == null) { continue; } ItemDrop foodItem = component2.m_foodItem; string text = (((Object)(object)foodItem != (Object)null) ? Utils.GetPrefabName(((Object)((Component)foodItem).gameObject).name) : string.Empty); bool flag = !string.IsNullOrWhiteSpace(text) && !string.Equals(text, key, StringComparison.OrdinalIgnoreCase); SharedData val2 = foodItem?.m_itemData?.m_shared; bool flag2 = (Object)(object)foodItem != (Object)null && ((Object)(object)((Component)foodItem).GetComponent<Feast>() != (Object)null || (val2 != null && ((int)val2.m_itemType == 2 || LooksLikeConsumableFood(val2)))); if ((int)val.m_itemType == 1) { if (flag && flag2) { dictionary2[key] = ((Component)foodItem).gameObject; hashSet.Add(text); } } else if (flag && flag2 && (int)val.m_itemType != 2 && !LooksLikeConsumableFood(val)) { dictionary2[key] = ((Component)foodItem).gameObject; hashSet.Add(text); } else { hashSet.Add(key); } } foreach (KeyValuePair<string, ItemDrop> item3 in dictionary) { string key2 = item3.Key; SharedData val3 = item3.Value.m_itemData?.m_shared; if (val3 == null || (int)val3.m_itemType != 1) { continue; } ItemDrop appendToolTip = val3.m_appendToolTip; if (!((Object)(object)appendToolTip == (Object)null)) { string prefabName2 = Utils.GetPrefabName(((Object)((Component)appendToolTip).gameObject).name); if (!string.Equals(prefabName2, key2, StringComparison.OrdinalIgnoreCase) && (hashSet.Contains(prefabName2) || (Object)(object)((Component)appendToolTip).GetComponent<Feast>() != (Object)null)) { dictionary2[key2] = ((Component)appendToolTip).gameObject; hashSet.Add(prefabName2); } } } return new FeastRoutingData(dictionary2, hashSet); } private static bool LooksLikeConsumableFood(SharedData shared) { if (!(shared.m_food > 0f) && !(shared.m_foodStamina > 0f) && !(shared.m_foodEitr > 0f)) { return shared.m_isDrink; } return true; } internal static PieceTable? GetFeasterPieceTable(ObjectDB? objectDb) { GameObject val = (((Object)(object)objectDb != (Object)null) ? objectDb.GetItemPrefab("Feaster") : null); if (!((Object)(object)val != (Object)null)) { return null; } return val.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_buildPieces; } internal static bool IsFeasterTool(PieceTable? table) { if ((Object)(object)table != (Object)null) { return (Object)(object)table == (Object)(object)GetFeasterPieceTable(ObjectDB.instance); } return false; } private static void UpdateExistingMenuGroups(PieceTable table) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Invalid comparison between Unknown and I4 //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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_0059: Invalid comparison between Unknown and I4 //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Invalid comparison between Unknown and I4 Piece val = default(Piece); foreach (GameObject piece in table.m_pieces) { if ((Object)(object)piece == (Object)null || !piece.TryGetComponent<Piece>(ref val) || FoodGroups.ContainsKey(val) || val.m_repairPiece || val.m_removePiece) { continue; } PieceCategory category = val.m_category; if ((int)category == 0 || (int)category == 7 || (int)category == 8 || (int)category == 6) { FoodSourceMod? item = null; if (TryResolveFoodSourceMod(piece, out var sourceMod) && ServerSyncModTemplatePlugin.UseModSpecificTab(sourceMod, category)) { item = sourceMod; } FoodGroups[val] = (category, item); } } } private static int InjectIntoTable(PieceTable table, List<CandidateFood> candidates) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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_006e: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) EnsureBaseCategories(table); HashSet<int> hashSet = BuildExistingFoodHashSet(table); int num = 0; foreach (CandidateFood candidate in candidates) { ItemDrop itemDrop = candidate.ItemDrop; GameObject placePrefab = candidate.PlacePrefab; int prefabNameHash = GetPrefabNameHash(((Component)itemDrop).gameObject); bool num2 = hashSet.Contains(prefabNameHash); bool flag = (Object)(object)placePrefab.GetComponent<ServeYouRightInjectedPieceMarker>() != (Object)null; if (num2 && !flag) { continue; } PieceCategory category = candidate.Category; PieceCategory category2 = category; if (TryGetTemplate(table, category2, out Piece templatePiece, out WearNTear templateWearNTear) && PrepareFoodPrefab(placePrefab, itemDrop, category2, templatePiece, templateWearNTear)) { FoodSourceMod? item = null; if (TryResolveFoodSourceMod(((Component)itemDrop).gameObject, out var sourceMod) && ServerSyncModTemplatePlugin.UseModSpecificTab(sourceMod, category)) { item = sourceMod; } FoodGroups[placePrefab.GetComponent<Piece>()] = (category, item); if (!table.m_pieces.Contains(placePrefab)) { table.m_pieces.Add(placePrefab); hashSet.Add(prefabNameHash); num++; } } } return num; } private static void EnsureBaseCategories(PieceTable table) { EnsureBaseCategoryExists(table, (PieceCategory)7, GetBaseCategoryLabel(table, (PieceCategory)7)); EnsureBaseCategoryExists(table, (PieceCategory)8, GetBaseCategoryLabel(table, (PieceCategory)8)); EnsureBaseCategoryExists(table, (PieceCategory)6, GetBaseCategoryLabel(table, (PieceCategory)6)); } internal static void GetMenuGroup(PieceTable table, Piece piece, out string key, out string label) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: 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_002f: Expected I4, but got Unknown //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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) PieceCategory val = piece.m_category; FoodSourceMod? foodSourceMod = null; if (FoodGroups.TryGetValue(piece, out (PieceCategory, FoodSourceMod?) value)) { (val, foodSourceMod) = value; } key = (int)val + ":" + foodSourceMod?.Id; label = ResolveCategoryDisplayLabel(val, GetBaseCategoryLabel(table, val)); if (foodSourceMod.HasValue) { label = label + " - " + foodSourceMod.Value.DisplayName; } } private static bool TryGetTemplate(PieceTable table, PieceCategory category, out Piece templatePiece, out WearNTear templateWearNTear) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) templatePiece = null; templateWearNTear = null; Piece val = null; WearNTear val2 = null; foreach (GameObject piece in table.m_pieces) { if ((Object)(object)piece == (Object)null) { continue; } Piece component = piece.GetComponent<Piece>(); if ((Object)(object)component == (Object)null || component.m_repairPiece || component.m_removePiece) { continue; } WearNTear component2 = piece.GetComponent<WearNTear>(); if (!((Object)(object)component2 == (Object)null)) { if (component.m_category == category) { templatePiece = component; templateWearNTear = component2; return true; } if ((Object)(object)val == (Object)null) { val = component; val2 = component2; } } } if ((Object)(object)val != (Object)null) { templatePiece = val; templateWearNTear = val2; return true; } ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogWarning((object)("No feaster template piece found for table '" + ((Object)table).name + "'.")); return false; } private static bool PrepareFoodPrefab(GameObject foodPrefab, ItemDrop sourceItemDrop, PieceCategory category, Piece templatePiece, WearNTear templateWearNTear) { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected O, but got Unknown if ((Object)(object)foodPrefab.GetComponent<ZNetView>() == (Object)null) { ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogWarning((object)("Skipping '" + ((Object)foodPrefab).name + "' because it has no ZNetView.")); return false; } Piece val = foodPrefab.GetComponent<Piece>(); if ((Object)(object)val == (Object)null) { val = foodPrefab.AddComponent<Piece>(); CopyPieceTemplate(templatePiece, val); } WearNTear component = foodPrefab.GetComponent<WearNTear>(); if ((Object)(object)component == (Object)null) { component = foodPrefab.AddComponent<WearNTear>(); CopyWearNTearTemplate(templateWearNTear, component); } SharedData shared = sourceItemDrop.m_itemData.m_shared; val.m_name = shared.m_name; val.m_description = shared.m_description; val.m_enabled = true; val.m_category = category; val.m_repairPiece = false; val.m_removePiece = false; val.m_resources = (Requirement[])(object)new Requirement[1] { new Requirement { m_resItem = sourceItemDrop, m_amount = 1, m_amountPerLevel = 1, m_recover = true } }; Sprite[] icons = shared.m_icons; if (icons != null && icons.Length > 0) { val.m_icon = sourceItemDrop.m_itemData.GetIcon(); } if ((Object)(object)foodPrefab.GetComponent<ServeYouRightInjectedPieceMarker>() == (Object)null) { foodPrefab.AddComponent<ServeYouRightInjectedPieceMarker>(); } return true; } private static void CopyPieceTemplate(Piece source, Piece destination) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) destination.m_usage = source.m_usage; destination.m_targetNonPlayerBuilt = source.m_targetNonPlayerBuilt; destination.m_icon = source.m_icon; destination.m_isUpgrade = source.m_isUpgrade; destination.m_comfort = source.m_comfort; destination.m_comfortGroup = source.m_comfortGroup; destination.m_comfortObject = source.m_comfortObject; destination.m_groundPiece = source.m_groundPiece; destination.m_allowAltGroundPlacement = source.m_allowAltGroundPlacement; destination.m_groundOnly = source.m_groundOnly; destination.m_cultivatedGroundOnly = source.m_cultivatedGroundOnly; destination.m_waterPiece = source.m_waterPiece; destination.m_clipGround = source.m_clipGround; destination.m_clipEverything = source.m_clipEverything; destination.m_noInWater = source.m_noInWater; destination.m_notOnWood = source.m_notOnWood; destination.m_notOnTiltingSurface = source.m_notOnTiltingSurface; destination.m_inCeilingOnly = source.m_inCeilingOnly; destination.m_notOnFloor = source.m_notOnFloor; destination.m_noClipping = source.m_noClipping; destination.m_onlyInTeleportArea = source.m_onlyInTeleportArea; destination.m_allowedInDungeons = source.m_allowedInDungeons; destination.m_spaceRequirement = source.m_spaceRequirement; destination.m_canRotate = source.m_canRotate; destination.m_randomInitBuildRotation = source.m_randomInitBuildRotation; destination.m_canBeRemoved = source.m_canBeRemoved; destination.m_canRockJade = source.m_canRockJade; destination.m_allowRotatedOverlap = source.m_allowRotatedOverlap; destination.m_vegetationGroundOnly = source.m_vegetationGroundOnly; destination.m_blockingPieces = ((source.m_blockingPieces == null) ? null : new List<Piece>(source.m_blockingPieces)); destination.m_blockRadius = source.m_blockRadius; destination.m_mustConnectTo = source.m_mustConnectTo; destination.m_connectRadius = source.m_connectRadius; destination.m_mustBeAboveConnected = source.m_mustBeAboveConnected; destination.m_noVines = source.m_noVines; destination.m_extraPlacementDistance = source.m_extraPlacementDistance; destination.m_onlyInBiome = source.m_onlyInBiome; destination.m_harvest = source.m_harvest; destination.m_harvestRadius = source.m_harvestRadius; destination.m_harvestRadiusMaxLevel = source.m_harvestRadiusMaxLevel; destination.m_placeEffect = source.m_placeEffect; destination.m_dlc = source.m_dlc; destination.m_craftingStation = source.m_craftingStation; destination.m_returnResourceHeightOffset = source.m_returnResourceHeightOffset; destination.m_destroyedLootPrefab = source.m_destroyedLootPrefab; } private static void CopyWearNTearTemplate(WearNTear source, WearNTear destination) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) destination.m_onDestroyed = source.m_onDestroyed; destination.m_onDamaged = source.m_onDamaged; destination.m_new = source.m_new; destination.m_worn = source.m_worn; destination.m_broken = source.m_broken; destination.m_wet = source.m_wet; destination.m_noRoofWear = source.m_noRoofWear; destination.m_noSupportWear = source.m_noSupportWear; destination.m_ashDamageImmune = source.m_ashDamageImmune; destination.m_ashDamageResist = source.m_ashDamageResist; destination.m_burnable = source.m_burnable; destination.m_materialType = source.m_materialType; destination.m_supports = source.m_supports; destination.m_comOffset = source.m_comOffset; destination.m_forceCorrectCOMCalculation = source.m_forceCorrectCOMCalculation; destination.m_staticPosition = source.m_staticPosition; destination.m_nonSolidRenderers = ((source.m_nonSolidRenderers == null) ? null : new List<Renderer>(source.m_nonSolidRenderers)); destination.m_health = source.m_health; destination.m_damages = source.m_damages; destination.m_minToolTier = source.m_minToolTier; destination.m_hitNoise = source.m_hitNoise; destination.m_destroyNoise = source.m_destroyNoise; destination.m_triggerPrivateArea = source.m_triggerPrivateArea; destination.m_destroyedEffect = source.m_destroyedEffect; destination.m_hitEffect = source.m_hitEffect; destination.m_switchEffect = source.m_switchEffect; destination.m_autoCreateFragments = source.m_autoCreateFragments; destination.m_fragmentRoots = ((source.m_fragmentRoots == null) ? null : ((GameObject[])source.m_fragmentRoots.Clone())); } private static HashSet<int> BuildExistingFoodHashSet(PieceTable table) { HashSet<int> hashSet = new HashSet<int>(); foreach (GameObject piece in table.m_pieces) { if ((Object)(object)piece == (Object)null) { continue; } ItemDrop component = piece.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null) { continue; } hashSet.Add(GetPrefabNameHash(((Component)component).gameObject)); if (!((Object)(object)piece.GetComponent<Feast>() != (Object)null) && !((Object)(object)piece.GetComponent<ServeYouRightInjectedPieceMarker>() != (Object)null)) { continue; } Requirement[] array = piece.GetComponent<Piece>()?.m_resources ?? Array.Empty<Requirement>(); foreach (Requirement val in array) { if ((Object)(object)val?.m_resItem != (Object)null) { hashSet.Add(GetPrefabNameHash(((Component)val.m_resItem).gameObject)); } } } return hashSet; } private static string GetBaseCategoryLabel(PieceTable table, PieceCategory category) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) int num = table.m_categories.IndexOf(category); if (num >= 0 && num < table.m_categoryLabels.Count) { string text = table.m_categoryLabels[num]; if (!string.IsNullOrWhiteSpace(text)) { return text; } } return GetFallbackCategoryLabel(category); } private static string ResolveCategoryDisplayLabel(PieceCategory category, string baseLabel) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrWhiteSpace(baseLabel)) { string text = baseLabel.Trim(); string text2 = text; bool flag = LooksLikeLocalizationTokenKey(text); if (flag && !text.StartsWith("$", StringComparison.Ordinal)) { text2 = "$" + text; } if (Localization.instance != null) { string text3 = Localization.instance.Localize(text2); if (!string.IsNullOrWhiteSpace(text3) && !string.Equals(text3, text2, StringComparison.Ordinal)) { return text3.Trim(); } } if (flag) { return GetFallbackCategoryLabel(category); } return text; } return GetFallbackCategoryLabel(category); } private static bool LooksLikeLocalizationTokenKey(string value) { if (string.IsNullOrWhiteSpace(value)) { return false; } if (value.StartsWith("$", StringComparison.Ordinal)) { return true; } if (!value.Contains("_")) { return false; } foreach (char c in value) { if (!char.IsLetterOrDigit(c) && c != '_' && c != '.' && c != '-') { return false; } } return true; } private unsafe static string GetFallbackCategoryLabel(PieceCategory category) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected I4, but got Unknown return (category - 6) switch { 1 => "Food", 2 => "Meads", 0 => "Feasts", _ => ((object)(*(PieceCategory*)(&category))/*cast due to .constrained prefix*/).ToString(), }; } private static bool TryResolveFoodSourceMod(GameObject foodPrefab, out FoodSourceMod sourceMod) { string prefabName = Utils.GetPrefabName(((Object)foodPrefab).name); if (SourceModHitCache.TryGetValue(prefabName, out var value)) { sourceMod = value; return true; } if (KnownCloneSourceBridge.TryResolveSourceMod(prefabName, out var sourceMod2)) { sourceMod = sourceMod2; SourceModHitCache[prefabName] = sourceMod; return true; } if (FoodSourceCatalog.TryGetOwner(prefabName, out var source)) { sourceMod = source; SourceModHitCache[prefabName] = sourceMod; return true; } sourceMod = default(FoodSourceMod); return false; } private static void EnsureBaseCategoryExists(PieceTable table, PieceCategory category, string label) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) if (table.m_categories.IndexOf(category) < 0) { table.m_categories.Add(category); while (table.m_categoryLabels.Count < table.m_categories.Count - 1) { table.m_categoryLabels.Add(((object)table.m_categories[table.m_categoryLabels.Count]/*cast due to .constrained prefix*/).ToString()); } table.m_categoryLabels.Add(label); } } private static int GetPrefabNameHash(GameObject prefab) { return StringExtensionMethods.GetStableHashCode(Utils.GetPrefabName(((Object)prefab).name)); } } internal readonly struct FoodSourceMod { public string Id { get; } public string DisplayName { get; } public FoodSourceMod(string id, string displayName) { Id = id; DisplayName = displayName; } } internal readonly struct CandidateFood { public ItemDrop ItemDrop { get; } public GameObject PlacePrefab { get; } public PieceCategory Category { get; } public CandidateFood(ItemDrop itemDrop, GameObject placePrefab, PieceCategory category) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) ItemDrop = itemDrop; PlacePrefab = placePrefab; Category = category; } } internal sealed class FeastRoutingData { public Dictionary<string, GameObject> MaterialToResultPrefab { get; } public HashSet<string> ResultPrefabNames { get; } public FeastRoutingData(Dictionary<string, GameObject> materialToResultPrefab, HashSet<string> resultPrefabNames) { MaterialToResultPrefab = materialToResultPrefab; ResultPrefabNames = resultPrefabNames; } } internal static class KnownCloneSourceBridge { private const string WackyGuid = "WackyMole.WackysDatabase"; private const string DefaultWackyName = "WackysDatabase"; private static bool _initialized; private static MethodInfo? _wackyGetClonedMap; private static string _wackyDisplayName = "WackysDatabase"; public static bool TryResolveSourceMod(string prefabName, out FoodSourceMod sourceMod) { sourceMod = default(FoodSourceMod); EnsureInitialized(); if (_wackyGetClonedMap == null) { return false; } try { if (string.IsNullOrWhiteSpace(_wackyGetClonedMap.Invoke(null, new object[1] { prefabName }) as string)) { return false; } sourceMod = new FoodSourceMod("WackyMole.WackysDatabase", _wackyDisplayName); return true; } catch (Exception ex) { ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogDebug((object)("Known clone source resolution failed for '" + prefabName + "': " + ex.Message)); return false; } } private static void EnsureInitialized() { if (_initialized) { return; } _initialized = true; try { _wackyGetClonedMap = Type.GetType("API.WackyAPI, WackysDatabase")?.GetMethod("GetClonedMap", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); if (Chainloader.PluginInfos.TryGetValue("WackyMole.WackysDatabase", out var value)) { object obj; if (value == null) { obj = null; } else { BepInPlugin metadata = value.Metadata; obj = ((metadata != null) ? metadata.Name : null); } if (obj == null) { obj = string.Empty; } string text = (string)obj; if (!string.IsNullOrWhiteSpace(text)) { _wackyDisplayName = text; } } } catch (Exception ex) { _wackyGetClonedMap = null; ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogDebug((object)("Known clone source bridge init failed: " + ex.Message)); } } } internal sealed class ServeYouRightInjectedPieceMarker : MonoBehaviour { } internal static class FoodSourceCatalog { private sealed class BundleContents { internal readonly AssetBundle Bundle; internal readonly string Name; internal readonly string[] Paths; internal BundleContents(AssetBundle bundle) { Bundle = bundle; Name = ((Object)bundle).name; Paths = bundle.GetAllAssetNames(); } } private const string DataForgeGuid = "sighsorry.DataForge"; private static readonly Dictionary<string, AssetOwner> Owners = new Dictionary<string, AssetOwner>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> Ambiguous = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<int, BundleContents> Bundles = new Dictionary<int, BundleContents>(); private static readonly HashSet<string> Vanilla = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> DataForgeClones = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static bool _vanillaLoaded; private static MethodInfo? _getCloneSources; private static MethodInfo? _getState; private static object? _itemsDomain; private static EventInfo? _changed; private static Delegate? _handler; private static string _dataForgeName = "DataForge"; internal static bool RefreshPending; internal static void Initialize() { if (!Chainloader.PluginInfos.TryGetValue("sighsorry.DataForge", out var value)) { return; } try { Type type = ((object)value.Instance).GetType().Assembly.GetType("DataForge.DataForgeApi"); Type type2 = type?.Assembly.GetType("DataForge.DataForgeDomain"); if (!(type == null) && !(type2 == null) && (int?)type.GetProperty("ApiVersion")?.GetValue(null) == 1) { _itemsDomain = Enum.Parse(type2, "Items"); _getCloneSources = type.GetMethod("GetCloneSources", new Type[1] { type2 }); _getState = type.GetMethod("GetState", new Type[1] { type2 }); _changed = type.GetEvent("Changed"); if (!(_getCloneSources == null) && !(_getState == null) && !(_changed?.EventHandlerType == null)) { Type type3 = _changed.EventHandlerType.GetGenericArguments()[0]; MethodInfo method = typeof(FoodSourceCatalog).GetMethod("OnChanged", BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(type3); _handler = Delegate.CreateDelegate(_changed.EventHandlerType, method); _changed.AddEventHandler(null, _handler); _dataForgeName = (string.IsNullOrWhiteSpace(value.Metadata.Name) ? "sighsorry.DataForge" : value.Metadata.Name); } } } catch (Exception ex) { LogFailure("DataForge API binding", ex); } } private static void OnChanged<T>(T change) { if (change != null && change.GetType().GetProperty("Domain")?.GetValue(change)?.ToString() == "Items") { RefreshPending = true; } } internal static void Refresh() { List<AssetOwner> list = new List<AssetOwner>(); foreach (PluginInfo value2 in Chainloader.PluginInfos.Values) { try { Assembly assembly = ((object)value2.Instance)?.GetType().Assembly; if (assembly != null) { list.Add(new AssetOwner(value2.Metadata.GUID, value2.Metadata.Name, assembly.GetName().Name ?? "", assembly.GetManifestResourceNames())); } } catch (Exception ex) { LogFailure("Plugin resource lookup", ex); } } foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles()) { int instanceID = ((Object)allLoadedAssetBundle).GetInstanceID(); if (!Bundles.TryGetValue(instanceID, out BundleContents value) || value.Bundle != allLoadedAssetBundle) { try { Bundles[instanceID] = new BundleContents(allLoadedAssetBundle); } catch (Exception ex2) { LogFailure("Bundle asset lookup", ex2); } } } Owners.Clear(); Ambiguous.Clear(); foreach (BundleContents value3 in Bundles.Values) { AssetOwner assetOwner = AssetOwnerMatching.Resolve(value3.Name, list); if (assetOwner == null) { continue; } string[] paths = value3.Paths; foreach (string text in paths) { if (text.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase)) { AssetOwnerMatching.Add(Owners, Ambiguous, Path.GetFileNameWithoutExtension(text), assetOwner); } } } LoadVanillaNames(); RefreshCloneSnapshot(); } private static void LoadVanillaNames() { if (_vanillaLoaded) { return; } string path = Path.Combine(Application.dataPath, "StreamingAssets", "SoftRef", "manifest_extended"); if (!File.Exists(path)) { return; } try { foreach (string item in File.ReadLines(path)) { int num = item.IndexOf("path in bundle:", StringComparison.OrdinalIgnoreCase); if (num >= 0) { string text = item.Substring(num + "path in bundle:".Length).Trim(); if (text.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase)) { Vanilla.Add(Path.GetFileNameWithoutExtension(text)); } } } _vanillaLoaded = true; } catch (Exception ex) { LogFailure("Vanilla asset lookup", ex); } } private static void RefreshCloneSnapshot() { DataForgeClones.Clear(); if (_getState == null || _getCloneSources == null || _itemsDomain == null) { return; } try { object[] parameters = new object[1] { _itemsDomain }; object obj = _getState.Invoke(null, parameters); object obj2 = obj?.GetType().GetProperty("IsReady")?.GetValue(obj); if (!(obj2 is bool) || !(bool)obj2 || !(_getCloneSources.Invoke(null, parameters) is IEnumerable enumerable)) { return; } foreach (object item2 in enumerable) { if (item2.GetType().GetProperty("Key")?.GetValue(item2) is string item) { DataForgeClones.Add(item); } } } catch (Exception ex) { LogFailure("DataForge clone lookup", ex); } } internal static bool TryGetOwner(string name, out FoodSourceMod source) { if (DataForgeClones.Contains(name)) { source = new FoodSourceMod("sighsorry.DataForge", _dataForgeName); return true; } if (!Vanilla.Contains(name) && Owners.TryGetValue(name, out AssetOwner value)) { source = new FoodSourceMod(value.Guid, value.Name); return true; } source = default(FoodSourceMod); return false; } internal static void ResetWorld() { Owners.Clear(); Ambiguous.Clear(); Bundles.Clear(); DataForgeClones.Clear(); RefreshPending = false; } internal static void Dispose() { try { if ((object)_handler != null) { _changed?.RemoveEventHandler(null, _handler); } } catch (Exception ex) { LogFailure("DataForge event cleanup", ex); } _handler = null; _changed = null; _getCloneSources = null; _getState = null; _itemsDomain = null; ResetWorld(); } private static void LogFailure(string operation, Exception ex) { ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogDebug((object)(operation + " unavailable: " + ex.GetBaseException().Message)); } } internal static class ServingTrayMenu { private sealed class Registration { private readonly FoodList _categories = new FoodList(); private IPieceList? _original; internal PieceTable? Tool; internal bool WasSimple; internal bool Opening; internal bool UseCategories(List<IPieceList> lists) { if (lists.Contains((IPieceList)(object)_categories)) { return true; } int num = lists.FindIndex((IPieceList list) => list is ByUsagePieceList); if (num < 0) { return false; } _original = lists[num]; _categories.DisplayName = _original.DisplayName; lists[num] = (IPieceList)(object)_categories; return true; } internal void RestoreCategories(List<IPieceList> lists) { int num = lists.IndexOf((IPieceList)(object)_categories); if (num >= 0 && _original != null) { lists[num] = _original; } _original = null; _categories.Clear(); } internal void Restore(List<IPieceList> lists) { RestoreCategories(lists); if ((Object)(object)Tool != (Object)null && !Tool.m_hideAdvancedMenu) { Tool.m_hideAdvancedMenu = WasSimple; } Tool = null; } } private sealed class FoodList : IPieceList { private readonly Dictionary<string, int> _ids = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private readonly List<(int Id, string Label)> _tags = new List<(int, string)>(); private readonly Dictionary<Piece, int> _pieceTags = new Dictionary<Piece, int>(); private readonly HashSet<int> _availableTags = new HashSet<int>(); private readonly HashSet<Piece> _seen = new HashSet<Piece>(); public string DisplayName { get; internal set; } = "$hud_byusage"; public bool ShowTags => true; public bool CanCustomizeTags => false; public int TagCount => _tags.Count; public int TagSeparatorIndex => -1; public string GetTagDisplayName(int index) { return _tags[index].Label; } public int GetTagIdByIndex(int index) { return _tags[index].Id; } internal void Clear() { _tags.Clear(); _pieceTags.Clear(); _availableTags.Clear(); _seen.Clear(); } public void UpdateAvailableTags(PieceTable table) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Invalid comparison between Unknown and I4 Clear(); if (!FeasterFoodInjector.IsFeasterTool(table)) { return; } Piece val = default(Piece); foreach (GameObject piece in table.m_pieces) { if (!((Object)(object)piece == (Object)null) && piece.TryGetComponent<Piece>(ref val) && table.m_availablePieces.Contains(val) && !val.m_repairPiece && !val.m_removePiece && (int)val.m_category != 100) { FeasterFoodInjector.GetMenuGroup(table, val, out string key, out string label); if (!_ids.TryGetValue(key, out var value)) { value = _ids.Count; _ids.Add(key, value); } _pieceTags[val] = value; if (_availableTags.Add(value)) { _tags.Add((value, label)); } } } } public void GetAvailablePiecesWithTag(int tagId, PieceTable table, IList<Piece> resultOut) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Invalid comparison between Unknown and I4 if (!FeasterFoodInjector.IsFeasterTool(table)) { return; } bool flag = _availableTags.Contains(tagId); _seen.Clear(); Piece val = default(Piece); foreach (GameObject piece in table.m_pieces) { if (!((Object)(object)piece == (Object)null) && piece.TryGetComponent<Piece>(ref val) && table.m_availablePieces.Contains(val) && _seen.Add(val) && (!flag || tagId == -1 || val.m_repairPiece || val.m_removePiece || (int)val.m_category == 100 || (_pieceTags.TryGetValue(val, out var value) && value == tagId))) { resultOut.Add(val); } } _seen.Clear(); } } private static readonly FieldRef<BuildUi, List<IPieceList>> Lists = AccessTools.FieldRefAccess<BuildUi, List<IPieceList>>("m_pieceLists"); private static readonly FieldRef<BuildUi, PieceTable> Tool = AccessTools.FieldRefAccess<BuildUi, PieceTable>("m_currentBuildTool"); private static readonly FieldRef<BuildUi, int> CurrentList = AccessTools.FieldRefAccess<BuildUi, int>("m_currentPieceList"); private static readonly Dictionary<BuildUi, Registration> Registrations = new Dictionary<BuildUi, Registration>(); internal static void BeforeOpen(BuildUi menu) { if (!Registrations.TryGetValue(menu, out Registration value)) { Registrations.Add(menu, value = new Registration()); } value.Restore(Lists.Invoke(menu)); Player localPlayer = Player.m_localPlayer; PieceTable val = ((localPlayer != null) ? localPlayer.GetBuildTool() : null); if (FeasterFoodInjector.IsFeasterTool(val)) { value.Opening = true; try { FeasterFoodInjector.RefreshFoodPiecesAndMenu(ObjectDB.instance); } finally { value.Opening = false; } if (value.UseCategories(Lists.Invoke(menu))) { value.Tool = val; value.WasSimple = val.m_hideAdvancedMenu; val.m_hideAdvancedMenu = false; } } } internal static void Close(BuildUi menu) { if (Registrations.TryGetValue(menu, out Registration value)) { value.Restore(Lists.Invoke(menu)); } } internal static void Refresh() { BuildUi val = (((Object)(object)Hud.instance != (Object)null) ? Hud.instance.m_buildUi : null); if (!((Object)(object)val == (Object)null) && ((Behaviour)val).isActiveAndEnabled && !((Object)(object)Player.m_localPlayer == (Object)null) && Registrations.TryGetValue(val, out Registration value) && !value.Opening) { PieceTable val2 = Tool.Invoke(val); if (!((Object)(object)val2 != (Object)(object)Player.m_localPlayer.GetBuildTool()) && FeasterFoodInjector.IsFeasterTool(val2)) { val.SelectPieceList(CurrentList.Invoke(val), true); } } } internal static void Detach(BuildUi menu, bool destroying) { if (Registrations.TryGetValue(menu, out Registration value)) { value.Restore(Lists.Invoke(menu)); Registrations.Remove(menu); if (!destroying && ((Behaviour)menu).isActiveAndEnabled && (Object)(object)Player.m_localPlayer != (Object)null) { menu.SelectPieceList(CurrentList.Invoke(menu), true); } } } internal static void Dispose() { foreach (BuildUi item in new List<BuildUi>(Registrations.Keys)) { if ((Object)(object)item != (Object)null) { Detach(item, destroying: false); } else { Registrations.Remove(item); } } } } [HarmonyPatch(typeof(BuildUi), "OpenBuildMenu")] internal static class TrayMenuOpenPatch { private static void Prefix(BuildUi __instance) { ServingTrayMenu.BeforeOpen(__instance); } private static Exception? Finalizer(BuildUi __instance, Exception? __exception) { if (__exception != null) { ServingTrayMenu.Close(__instance); } return __exception; } } [HarmonyPatch(typeof(BuildUi), "Close")] internal static class TrayMenuClosePatch { private static void Postfix(BuildUi __instance) { ServingTrayMenu.Close(__instance); } } [HarmonyPatch(typeof(BuildUi), "OnDestroy")] internal static class TrayMenuDestroyPatch { private static void Prefix(BuildUi __instance) { ServingTrayMenu.Detach(__instance, destroying: true); } } }