using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; 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.4")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.4.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 ServerSyncModTemplate { [BepInPlugin("sighsorry.ServeYouRight", "ServeYouRight", "1.0.4")] [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.4"; 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 PerModConfigs; private static bool _pendingDynamicConfigSave; public void Awake() { _instance = this; RunWithConfigAutoSaveDisabled(delegate { JotunnBridge.InitializeAndEnableModQuery(); 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; RunWithConfigAutoSaveDisabled(SaveConfigWithoutWatcher); } 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_0009: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected I4, but got Unknown PerModCategoryConfig orCreatePerModConfig = GetOrCreatePerModConfig(mod); return (category - 5) switch { 1 => orCreatePerModConfig.Food.Value == Toggle.On, 2 => orCreatePerModConfig.Meads.Value == Toggle.On, 0 => orCreatePerModConfig.Feasts.Value == Toggle.On, _ => false, }; } private static PerModCategoryConfig GetOrCreatePerModConfig(FoodSourceMod mod) { lock (DynamicConfigLock) { if (PerModConfigs.TryGetValue(mod.Id, out PerModCategoryConfig value)) { return value; } if ((Object)(object)_instance == (Object)null) { throw new InvalidOperationException("Plugin instance is not initialized."); } string text = "ServingTray - " + SanitizeConfigText(mod.DisplayName) + " (" + SanitizeConfigText(mod.Id) + ")"; PerModCategoryConfig perModCategoryConfig = new PerModCategoryConfig(_instance.config(text, "Food", Toggle.On, "If on, '" + mod.DisplayName + "' Food items go to 'Food - " + mod.DisplayName + "'. If off, they merge into vanilla Food.", 300), _instance.config(text, "Meads", Toggle.On, "If on, '" + mod.DisplayName + "' Mead items go to 'Meads - " + mod.DisplayName + "'. If off, they merge into vanilla Meads.", 200), _instance.config(text, "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] = perModCategoryConfig; _pendingDynamicConfigSave = true; return perModCategoryConfig; } } 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 config(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(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(StringComparer.OrdinalIgnoreCase); } } [HarmonyPatch(typeof(ObjectDB), "Awake")] public static class ObjectDbAwakePatch { private static void Postfix(ObjectDB __instance) { FeasterFoodInjector.Inject(__instance); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] public static class ObjectDbCopyOtherDbPatch { private static void Postfix(ObjectDB __instance) { 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) && !((Object)(object)buildPieces == (Object)null) && buildPieces.m_canRemoveFeasts) { FeasterFoodInjector.RefreshFoodPiecesAndMenu(ObjectDB.instance, buildPieces); } } } [HarmonyPatch(typeof(PieceTable), "UpdateAvailable")] public static class PieceTableUpdateAvailablePatch { private static void Postfix(PieceTable __instance) { if (!((Object)(object)__instance == (Object)null) && __instance.m_canRemoveFeasts) { FeasterFoodInjector.ApplyCustomCategoryLabels(__instance); } } } [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() == (Object)null) { return; } ParticleSystem[] componentsInChildren = ((Component)__instance).GetComponentsInChildren(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(); if ((Object)(object)component != (Object)null) { ((Renderer)component).enabled = false; } } } } internal static class FeasterFoodInjector { private static bool _isInjecting; private static readonly Dictionary ModCategoryCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary SourceModHitCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly object ModCategoryLock = new object(); internal static void RefreshFoodPiecesAndMenu(ObjectDB? objectDb, PieceTable? buildPieces = null) { Inject(objectDb); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { localPlayer.UpdateKnownRecipesList(); localPlayer.UpdateAvailablePiecesList(); ApplyCustomCategoryLabels(buildPieces ?? localPlayer.m_buildPieces); } } public static void Inject(ObjectDB? objectDb) { if (_isInjecting || (Object)(object)objectDb == (Object)null) { return; } _isInjecting = true; try { List candidateFoods = GetCandidateFoods(objectDb); if (candidateFoods.Count == 0) { return; } foreach (PieceTable feasterPieceTable in GetFeasterPieceTables(objectDb)) { int num = InjectIntoTable(feasterPieceTable, candidateFoods); if (num > 0) { ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogInfo((object)$"Added {num} mod food pieces to feaster table '{((Object)feasterPieceTable).name}'."); } ApplyCustomCategoryLabels(feasterPieceTable); } } catch (Exception arg) { ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogError((object)$"Failed to inject feaster foods: {arg}"); } finally { _isInjecting = false; ServerSyncModTemplatePlugin.FlushPendingDynamicConfigSave(); } } private static List 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 list = new List(); FeastRoutingData feastRoutingData = BuildFeastRoutingData(objectDb); foreach (GameObject item in objectDb.m_items) { if ((Object)(object)item == (Object)null) { continue; } ItemDrop component = item.GetComponent(); 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)5)); } else if (!feastRoutingData.ResultPrefabNames.Contains(prefabName) && !((Object)(object)((Component)component).GetComponent() != (Object)null) && (int)val.m_itemType == 2 && LooksLikeConsumableFood(val)) { PieceCategory category = (PieceCategory)(val.m_isDrink ? 7 : 6); 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 dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (GameObject item in objectDb.m_items) { if ((Object)(object)item == (Object)null) { continue; } ItemDrop component = item.GetComponent(); if (!((Object)(object)component == (Object)null)) { string prefabName = Utils.GetPrefabName(((Object)item).name); if (!dictionary.ContainsKey(prefabName)) { dictionary[prefabName] = component; } } } Dictionary dictionary2 = new Dictionary(StringComparer.OrdinalIgnoreCase); HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair item2 in dictionary) { string key = item2.Key; ItemDrop value = item2.Value; SharedData val = value.m_itemData?.m_shared; Feast component2 = ((Component)value).GetComponent(); 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() != (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 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() != (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; } private static IEnumerable GetFeasterPieceTables(ObjectDB objectDb) { HashSet uniqueTables = new HashSet(); foreach (GameObject item in objectDb.m_items) { if (!((Object)(object)item == (Object)null)) { PieceTable val = item.GetComponent()?.m_itemData?.m_shared?.m_buildPieces; if (!((Object)(object)val == (Object)null) && val.m_canRemoveFeasts && (val.m_categories.Contains((PieceCategory)6) || val.m_categories.Contains((PieceCategory)7) || val.m_categories.Contains((PieceCategory)5)) && uniqueTables.Add(val)) { yield return val; } } } } private static int InjectIntoTable(PieceTable table, List candidates) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) EnsureBaseCategories(table); HashSet 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() != (Object)null; if (!num2 || flag) { PieceCategory category = candidate.Category; PieceCategory category2 = ResolveTargetCategory(table, ((Component)itemDrop).gameObject, category); if (TryGetTemplate(table, category2, out Piece templatePiece, out WearNTear templateWearNTear) && PrepareFoodPrefab(placePrefab, itemDrop, category2, templatePiece, templateWearNTear) && !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)6, GetBaseCategoryLabel(table, (PieceCategory)6)); EnsureBaseCategoryExists(table, (PieceCategory)7, GetBaseCategoryLabel(table, (PieceCategory)7)); EnsureBaseCategoryExists(table, (PieceCategory)5, GetBaseCategoryLabel(table, (PieceCategory)5)); } public static void ApplyCustomCategoryLabels(PieceTable? table) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)table == (Object)null) { return; } List list; lock (ModCategoryLock) { list = ModCategoryCache.Values.ToList(); } foreach (ModCategoryInfo item in list) { int num = table.m_categories.IndexOf(item.Category); if (num >= 0) { while (table.m_categoryLabels.Count <= num) { table.m_categoryLabels.Add(string.Empty); } string baseCategoryLabel = GetBaseCategoryLabel(table, item.BaseCategory); string text = ResolveCategoryDisplayLabel(item.BaseCategory, baseCategoryLabel); table.m_categoryLabels[num] = text + " - " + item.SourceMod.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(); if ((Object)(object)component == (Object)null || component.m_repairPiece || component.m_removePiece) { continue; } WearNTear component2 = piece.GetComponent(); 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() == (Object)null) { ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogWarning((object)("Skipping '" + ((Object)foodPrefab).name + "' because it has no ZNetView.")); return false; } Piece val = foodPrefab.GetComponent(); if ((Object)(object)val == (Object)null) { val = foodPrefab.AddComponent(); CopyPieceTemplate(templatePiece, val); } WearNTear component = foodPrefab.GetComponent(); if ((Object)(object)component == (Object)null) { component = foodPrefab.AddComponent(); 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() == (Object)null) { foodPrefab.AddComponent(); } return true; } private static void CopyPieceTemplate(Piece source, Piece destination) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) 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(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(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 BuildExistingFoodHashSet(PieceTable table) { HashSet hashSet = new HashSet(); foreach (GameObject piece in table.m_pieces) { if ((Object)(object)piece == (Object)null) { continue; } ItemDrop component = piece.GetComponent(); if ((Object)(object)component == (Object)null) { continue; } hashSet.Add(GetPrefabNameHash(((Component)component).gameObject)); if (!((Object)(object)piece.GetComponent() != (Object)null) && !((Object)(object)piece.GetComponent() != (Object)null)) { continue; } Requirement[] array = piece.GetComponent()?.m_resources ?? Array.Empty(); 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 PieceCategory ResolveTargetCategory(PieceTable table, GameObject foodPrefab, PieceCategory baseCategory) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) string baseCategoryLabel = GetBaseCategoryLabel(table, baseCategory); EnsureBaseCategoryExists(table, baseCategory, baseCategoryLabel); if (!TryResolveFoodSourceMod(foodPrefab, out var sourceMod)) { return baseCategory; } if (!ServerSyncModTemplatePlugin.UseModSpecificTab(sourceMod, baseCategory)) { return baseCategory; } return ResolveOrCreateModCategory(baseCategory, sourceMod); } 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 PieceCategory ResolveOrCreateModCategory(PieceCategory baseCategory, FoodSourceMod sourceMod) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected I4, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0038: 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_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) string key = $"{sourceMod.Id}:{(int)baseCategory}"; lock (ModCategoryLock) { if (ModCategoryCache.TryGetValue(key, out ModCategoryInfo value)) { return value.Category; } string text = BuildStableCategoryKey(baseCategory, sourceMod.Id); if (!JotunnBridge.TryAddPieceCategory(text, out var category)) { ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogWarning((object)$"Could not create Jotunn category '{text}'. Falling back to vanilla category '{baseCategory}'."); return baseCategory; } ModCategoryCache[key] = new ModCategoryInfo(category, baseCategory, sourceMod); return category; } } private static string ResolveCategoryDisplayLabel(PieceCategory category, string baseLabel) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrWhiteSpace(baseLabel)) { string text = baseLabel.Trim(); string text2 = text; if (LooksLikeLocalizationTokenKey(text) && !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 (LooksLikeLocalizationTokenKey(text)) { 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.IndexOfAny(new char[4] { ' ', '\t', '\r', '\n' }) >= 0) { return false; } if (!value.Contains("_")) { return false; } foreach (char c in value) { if (!char.IsLetterOrDigit(c) && c != '_' && c != '.' && c != '-') { return false; } } return true; } private static string BuildStableCategoryKey(PieceCategory baseCategory, string modId) { //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 //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected I4, but got Unknown string arg = (baseCategory - 5) switch { 1 => "food", 2 => "meads", 0 => "feasts", _ => $"cat{(int)baseCategory}", }; int num = StringExtensionMethods.GetStableHashCode(modId ?? string.Empty); if (num == int.MinValue) { num = int.MaxValue; } num = Math.Abs(num); return $"syr_{arg}_{num}"; } 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 - 5) 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 (JotunnBridge.TryGetPrefabSourceMod(prefabName, out var sourceMod3)) { sourceMod = sourceMod3; 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 MaterialToResultPrefab { get; } public HashSet ResultPrefabNames { get; } public FeastRoutingData(Dictionary materialToResultPrefab, HashSet resultPrefabNames) { MaterialToResultPrefab = materialToResultPrefab; ResultPrefabNames = resultPrefabNames; } } internal sealed class ModCategoryInfo { public PieceCategory Category { get; } public PieceCategory BaseCategory { get; } public FoodSourceMod SourceMod { get; } public ModCategoryInfo(PieceCategory category, PieceCategory baseCategory, FoodSourceMod sourceMod) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) Category = category; BaseCategory = baseCategory; SourceMod = sourceMod; } } internal sealed class PerModCategoryConfig { public ConfigEntry Food { get; } public ConfigEntry Meads { get; } public ConfigEntry Feasts { get; } public PerModCategoryConfig(ConfigEntry food, ConfigEntry meads, ConfigEntry feasts) { Food = food; Meads = meads; Feasts = feasts; } } 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"; private static readonly HashSet WackyCloneHitCache = new HashSet(StringComparer.OrdinalIgnoreCase); public static bool TryResolveSourceMod(string prefabName, out FoodSourceMod sourceMod) { sourceMod = default(FoodSourceMod); EnsureInitialized(); if (_wackyGetClonedMap == null) { return false; } if (WackyCloneHitCache.Contains(prefabName)) { sourceMod = new FoodSourceMod("WackyMole.WackysDatabase", _wackyDisplayName); return true; } try { if (string.IsNullOrWhiteSpace(_wackyGetClonedMap.Invoke(null, new object[1] { prefabName }) as string)) { return false; } WackyCloneHitCache.Add(prefabName); 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 static class JotunnBridge { private static bool _initialized; private static bool _available; private static MethodInfo? _modQueryEnable; private static MethodInfo? _modQueryGetPrefab; private static PropertyInfo? _pieceManagerInstance; private static MethodInfo? _pieceManagerAddPieceCategory; public static void InitializeAndEnableModQuery() { if (_initialized) { EnableModQuery(); return; } _initialized = true; try { Type? type = Type.GetType("Jotunn.Utils.ModQuery, Jotunn"); Type type2 = Type.GetType("Jotunn.Managers.PieceManager, Jotunn"); _modQueryEnable = type?.GetMethod("Enable", BindingFlags.Static | BindingFlags.Public); _modQueryGetPrefab = type?.GetMethod("GetPrefab", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); _pieceManagerInstance = type2?.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public); _pieceManagerAddPieceCategory = type2?.GetMethod("AddPieceCategory", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); _available = _modQueryEnable != null && _modQueryGetPrefab != null && _pieceManagerInstance != null && _pieceManagerAddPieceCategory != null; } catch (Exception ex) { _available = false; ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogWarning((object)("Failed to initialize Jotunn bridge: " + ex.Message)); } if (!_available) { ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogWarning((object)"Jotunn bridge is unavailable. Mod-specific tabs will fall back to vanilla categories."); } else { EnableModQuery(); } } public static bool TryGetPrefabSourceMod(string prefabName, out FoodSourceMod sourceMod) { sourceMod = default(FoodSourceMod); if (!_available || _modQueryGetPrefab == null) { return false; } try { object obj = _modQueryGetPrefab.Invoke(null, new object[1] { prefabName }); if (obj == null) { return false; } object obj2 = obj.GetType().GetProperty("SourceMod", BindingFlags.Instance | BindingFlags.Public)?.GetValue(obj); if (obj2 == null) { return false; } BepInPlugin val = (BepInPlugin)((obj2 is BepInPlugin) ? obj2 : null); if (val != null) { string displayName = (string.IsNullOrWhiteSpace(val.Name) ? val.GUID : val.Name); sourceMod = new FoodSourceMod(val.GUID, displayName); return true; } object obj3 = obj2.GetType().GetProperty("GUID", BindingFlags.Instance | BindingFlags.Public)?.GetValue(obj2) as string; string text = obj2.GetType().GetProperty("Name", BindingFlags.Instance | BindingFlags.Public)?.GetValue(obj2) as string; if (obj3 == null) { obj3 = string.Empty; } string text2 = (string)obj3; if (string.IsNullOrWhiteSpace(text2)) { return false; } string displayName2 = (string.IsNullOrWhiteSpace(text) ? text2 : text); sourceMod = new FoodSourceMod(text2, displayName2); return true; } catch (Exception ex) { ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogDebug((object)("ModQuery.GetPrefab failed for '" + prefabName + "': " + ex.Message)); return false; } } public static bool TryAddPieceCategory(string categoryName, out PieceCategory category) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected I4, but got Unknown category = (PieceCategory)6; if (!_available || _pieceManagerInstance == null || _pieceManagerAddPieceCategory == null) { return false; } try { object value = _pieceManagerInstance.GetValue(null); if (value == null) { return false; } object obj = _pieceManagerAddPieceCategory.Invoke(value, new object[1] { categoryName }); if (obj is PieceCategory val) { category = (PieceCategory)(int)val; return true; } if (obj is int num) { category = (PieceCategory)num; return true; } return false; } catch (Exception ex) { ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogWarning((object)("Failed to add Jotunn piece category '" + categoryName + "': " + ex.Message)); return false; } } private static void EnableModQuery() { if (!_available || _modQueryEnable == null) { return; } try { _modQueryEnable.Invoke(null, null); } catch (Exception ex) { ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogWarning((object)("Failed to enable Jotunn ModQuery: " + ex.Message)); } } } internal sealed class ServeYouRightInjectedPieceMarker : MonoBehaviour { } }