using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Net.WebSockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using UnityEngine; using UnityEngine.Networking; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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 ChallengeHubValheim { internal static class AdvancedQoLFeature { private static Plugin _plugin; private static AdvancedQoLBehaviour _behaviour; private static ConfigEntry _harvestRadius; private static ConfigEntry _harvestMaxTargets; private static ConfigEntry _maxGridSize; private static ConfigEntry _doorCloseDelay; private static ConfigEntry _doorSafetyRadius; private static ConfigEntry _fuelContainerRadius; private static ConfigEntry _storageMaxContainers; private static ConfigEntry _signMaxFontSize; private static readonly float[] RotationSteps = new float[6] { 1f, 5f, 15f, 22.5f, 45f, 90f }; private const string ContainerLockOwnerKey = "ChallengeHub.QoL.LockOwner"; private const string ContainerLockUntilKey = "ChallengeHub.QoL.LockUntil"; internal static float HarvestRadius { get { QoLWebConfig qoLWebConfig = RemoteQol(); if (qoLWebConfig == null || !(qoLWebConfig.harvestRadius > 0f)) { return Mathf.Clamp(_harvestRadius?.Value ?? 7f, 2f, 20f); } return Mathf.Clamp(RemoteQol().harvestRadius, 2f, 20f); } } internal static int HarvestMaxTargets { get { QoLWebConfig qoLWebConfig = RemoteQol(); if (qoLWebConfig == null || qoLWebConfig.harvestMaxTargets <= 0) { return Mathf.Clamp(_harvestMaxTargets?.Value ?? 24, 2, 80); } return Mathf.Clamp(RemoteQol().harvestMaxTargets, 2, 80); } } internal static int MaximumGridSize { get { QoLWebConfig qoLWebConfig = RemoteQol(); if (qoLWebConfig == null || qoLWebConfig.maxPlantGridSize <= 0) { return Mathf.Clamp(_maxGridSize?.Value ?? 5, 2, 5); } return Mathf.Clamp(RemoteQol().maxPlantGridSize, 2, 5); } } internal static float DoorCloseDelay { get { QoLWebConfig qoLWebConfig = RemoteQol(); if (qoLWebConfig == null || !(qoLWebConfig.doorCloseDelaySeconds > 0f)) { return Mathf.Clamp(_doorCloseDelay?.Value ?? 5f, 1f, 60f); } return Mathf.Clamp(RemoteQol().doorCloseDelaySeconds, 1f, 60f); } } internal static float DoorSafetyRadius { get { QoLWebConfig qoLWebConfig = RemoteQol(); if (qoLWebConfig == null || !(qoLWebConfig.doorSafetyRadius > 0f)) { return Mathf.Clamp(_doorSafetyRadius?.Value ?? 1.8f, 0.8f, 5f); } return Mathf.Clamp(RemoteQol().doorSafetyRadius, 0.8f, 5f); } } internal static float FuelContainerRadius { get { QoLWebConfig qoLWebConfig = RemoteQol(); if (qoLWebConfig == null || !(qoLWebConfig.fuelContainerRadius > 0f)) { return Mathf.Clamp(_fuelContainerRadius?.Value ?? 12f, 2f, 30f); } return Mathf.Clamp(RemoteQol().fuelContainerRadius, 2f, 30f); } } internal static int StorageMaxContainers { get { QoLWebConfig qoLWebConfig = RemoteQol(); if (qoLWebConfig == null || qoLWebConfig.storageMaxContainers <= 0) { return Mathf.Clamp(_storageMaxContainers?.Value ?? 20, 1, 40); } return Mathf.Clamp(RemoteQol().storageMaxContainers, 1, 40); } } internal static int SignMaxFontSize { get { QoLWebConfig qoLWebConfig = RemoteQol(); if (qoLWebConfig == null || qoLWebConfig.signMaxFontSize <= 0) { return Mathf.Clamp(_signMaxFontSize?.Value ?? 48, 18, 64); } return Mathf.Clamp(RemoteQol().signMaxFontSize, 18, 64); } } internal static float SwimCriticalStaminaPercent { get { QoLWebConfig qoLWebConfig = RemoteQol(); if (qoLWebConfig == null || !(qoLWebConfig.swimCriticalStaminaPercent > 0f)) { return 0.25f; } return Mathf.Clamp(RemoteQol().swimCriticalStaminaPercent, 0.05f, 0.9f); } } private static QoLWebConfig RemoteQol() { return _plugin?.RemoteConfig?.qol; } internal static void Initialize(Plugin plugin) { if (!((Object)(object)plugin == (Object)null)) { _plugin = plugin; _harvestRadius = ((BaseUnityPlugin)plugin).Config.Bind("QoLSkilltree.Advanced", "AreaHarvestRadius", 7f, "Radius der bewusst mit Alt+Benutzen ausgeloesten Flaechenernte."); _harvestMaxTargets = ((BaseUnityPlugin)plugin).Config.Bind("QoLSkilltree.Advanced", "AreaHarvestMaxTargets", 24, "Maximale Anzahl zusaetzlicher Pickables pro Flaechenernte."); _maxGridSize = ((BaseUnityPlugin)plugin).Config.Bind("QoLSkilltree.Advanced", "MaximumPlantGridSize", 5, "Maximal erlaubte Rastergroesse. Unterstuetzt werden 2, 3 und 5."); _doorCloseDelay = ((BaseUnityPlugin)plugin).Config.Bind("QoLSkilltree.Advanced", "DoorCloseDelaySeconds", 5f, "Verzoegerung des Tuerwaechters."); _doorSafetyRadius = ((BaseUnityPlugin)plugin).Config.Bind("QoLSkilltree.Advanced", "DoorSafetyRadius", 1.8f, "Tuer wird nur geschlossen, wenn kein Spieler in diesem Radius steht."); _fuelContainerRadius = ((BaseUnityPlugin)plugin).Config.Bind("QoLSkilltree.Advanced", "FuelContainerRadius", 12f, "Reichweite berechtigter Lager fuer den Brennstoffhelfer."); _storageMaxContainers = ((BaseUnityPlugin)plugin).Config.Bind("QoLSkilltree.Advanced", "StorageMaxContainers", 20, "Maximale Anzahl Kisten pro bewusst ausgeloestem Lager-Routing."); _signMaxFontSize = ((BaseUnityPlugin)plugin).Config.Bind("QoLSkilltree.Advanced", "SignMaximumFontSize", 48, "Sicheres Schriftgroessenlimit der Schildwerkstatt."); _behaviour = ((Component)plugin).gameObject.GetComponent() ?? ((Component)plugin).gameObject.AddComponent(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Erweiterte QoL-Talente bereit: Lager-Routing, Landwirtschaft, Tueren, Brennstoff, Rotation, Schilder und Schwimm-HUD."); } } } internal static void OnTalentDataChanged() { _behaviour?.OnTalentDataChanged(); } internal static IEnumerator IntelligentStoreNearby() { Player player = Player.m_localPlayer; TalentData data = TalentStore.GetLocalData(); if ((Object)(object)player == (Object)null || data == null || !data.HasSkill("storage.quick_store")) { yield break; } Inventory playerInventory = ((Humanoid)player).GetInventory(); if (playerInventory == null) { yield break; } List containers = (from container in QoLSkillRuntimeFeature.FindNearbyContainers(((Component)player).transform.position, QoLSkillRuntimeFeature.ContainerRadius) where IsContainerEligible(container, player.GetPlayerID()) select container).Take((!data.HasSkill("storage.stack_nearby")) ? 1 : StorageMaxContainers).ToList(); if (containers.Count == 0) { QoLSkillRuntimeFeature.ShowCenter("Keine berechtigte freie Kiste in Reichweite."); yield break; } int movedItems = 0; HashSet touched = new HashSet(); List list = (from item in playerInventory.GetAllItems() where item != null && item.m_stack > 0 && !item.m_equipped && item.m_gridPos.y != 0 where !QoLSkillRuntimeFeature.IsReservedItem(data, playerInventory, item) select item).ToList(); foreach (ItemData source in list) { if (source == null || source.m_stack <= 0) { continue; } string itemKey = QoLSkillRuntimeFeature.ItemKey(source); string group = MaterialGroup(source); List list2 = (from container in containers select new { Container = container, Score = StorageTargetScore(container.GetInventory(), source, itemKey, @group, data.AllowEmptyStorageTargets) } into entry where entry.Score > 0 orderby entry.Score descending, Vector3.Distance(((Component)player).transform.position, ((Component)entry.Container).transform.position) select entry.Container).ToList(); foreach (Container item in list2) { if (source.m_stack <= 0) { break; } Inventory inventory = item.GetInventory(); if (inventory != null) { inventory.CanAddItem(source, source.m_stack); } if (!TryAcquireContainerLock(item, player.GetPlayerID())) { continue; } try { ClaimContainer(item); int num = CountCompatible(inventory, source); ItemData val = source.Clone(); val.m_stack = source.m_stack; try { inventory.AddItem(val); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Lager-Routing Ziel uebersprungen: " + ex.Message)); } goto end_IL_03a6; } int num2 = Mathf.Clamp(CountCompatible(inventory, source) - num, 0, source.m_stack); if (num2 <= 0) { continue; } playerInventory.RemoveItem(source, num2); movedItems += num2; touched.Add(item); InventoryReflection.NotifyChanged(inventory); InventoryReflection.NotifyChanged(playerInventory); SaveContainer(item); goto IL_04d7; end_IL_03a6:; } finally { ReleaseContainerLock(item, player.GetPlayerID()); } continue; IL_04d7: yield return null; } } int count = touched.Count; if (movedItems > 0) { MeadowsSettlementFeature.TouchLocal(((Component)player).transform.position, "qol_storage"); } QoLSkillRuntimeFeature.ShowCenter((movedItems > 0) ? (movedItems + " Gegenstaende auf " + count + " Lager verteilt.") : "Keine passende Lagergruppe oder kein freier Stapel gefunden."); } private static bool IsContainerEligible(Container container, long playerId) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)container == (Object)null || container.GetInventory() == null || container.IsInUse()) { return false; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && !MeadowsSettlementFeature.CanModify(localPlayer, ((Component)container).transform.position, showMessage: false)) { return false; } try { Vagon wagon = container.m_wagon; if ((Object)(object)wagon != (Object)null && wagon.InUse()) { return false; } } catch { } try { MethodInfo methodInfo = AccessTools.Method(typeof(Container), "CheckAccess", (Type[])null, (Type[])null); if (methodInfo != null) { object obj2 = methodInfo.Invoke(container, new object[1] { playerId }); if (obj2 is bool && !(bool)obj2) { return false; } } } catch { return false; } try { if (container.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false)) { return false; } } catch { } return true; } private static bool TryAcquireContainerLock(Container container, long playerId) { try { ZNetView val = (((Object)(object)container.m_rootObjectOverride != (Object)null) ? container.m_rootObjectOverride : ((Component)container).GetComponent()); ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if (val2 == null || (Object)(object)ZNet.instance == (Object)null) { return false; } if (!val.IsOwner()) { val.ClaimOwnership(); } long ticks = ZNet.instance.GetTime().Ticks; long num = val2.GetLong("ChallengeHub.QoL.LockUntil", 0L); long num2 = val2.GetLong("ChallengeHub.QoL.LockOwner", 0L); if (num > ticks && num2 != 0L && num2 != playerId) { return false; } val2.Set("ChallengeHub.QoL.LockOwner", playerId); val2.Set("ChallengeHub.QoL.LockUntil", ticks + TimeSpan.FromSeconds(12.0).Ticks); return true; } catch { return false; } } private static void ReleaseContainerLock(Container container, long playerId) { try { ZNetView val = (((Object)(object)container.m_rootObjectOverride != (Object)null) ? container.m_rootObjectOverride : ((Component)container).GetComponent()); ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if (val2 != null && val2.GetLong("ChallengeHub.QoL.LockOwner", 0L) == playerId) { val2.Set("ChallengeHub.QoL.LockOwner", 0L); val2.Set("ChallengeHub.QoL.LockUntil", 0L); } } catch { } } private static int StorageTargetScore(Inventory inventory, ItemData source, string itemKey, string group, bool allowEmpty) { if (inventory == null || source == null) { return 0; } List allItems = inventory.GetAllItems(); if (allItems.Any((ItemData item) => item != null && string.Equals(QoLSkillRuntimeFeature.ItemKey(item), itemKey, StringComparison.Ordinal))) { return 300; } if (!string.IsNullOrEmpty(group) && allItems.Any((ItemData item) => item != null && MaterialGroup(item) == group)) { return 200; } if (allowEmpty && allItems.Count == 0) { return 100; } return 0; } private static int CountCompatible(Inventory inventory, ItemData source) { if (inventory == null || source == null) { return 0; } string key = QoLSkillRuntimeFeature.ItemKey(source); return (from item in inventory.GetAllItems() where item != null && string.Equals(QoLSkillRuntimeFeature.ItemKey(item), key, StringComparison.Ordinal) && item.m_quality == source.m_quality && item.m_variant == source.m_variant && item.m_worldLevel == source.m_worldLevel select item).Sum((ItemData item) => Math.Max(0, item.m_stack)); } private static string MaterialGroup(ItemData item) { //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Invalid comparison between Unknown and I4 //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Invalid comparison between Unknown and I4 //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Invalid comparison between Unknown and I4 //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Expected I4, but got Unknown if (item?.m_shared == null) { return string.Empty; } string text = (QoLSkillRuntimeFeature.ItemKey(item) + " " + item.m_shared.m_name).ToLowerInvariant(); if (ContainsAny(text, "ore", "copper", "tin", "iron", "silver", "blackmetal", "flametal")) { return "ore-metal"; } if (ContainsAny(text, "wood", "finewood", "roundlog", "yggdrasil")) { return "wood"; } if (ContainsAny(text, "seed", "seeds", "carrot", "turnip", "onion", "barley", "flax", "sapling")) { return "seed-crop"; } if (ContainsAny(text, "trophy")) { return "trophy"; } if (ContainsAny(text, "hide", "leather", "pelt", "wolf", "deer")) { return "hide"; } if (ContainsAny(text, "stone", "marble", "coal", "resin", "tar")) { return "construction"; } if ((int)item.m_shared.m_itemType == 2) { return "food"; } if ((int)item.m_shared.m_itemType == 9 || (int)item.m_shared.m_itemType == 23) { return "ammo"; } return "type-" + ((int)item.m_shared.m_itemType).ToString(CultureInfo.InvariantCulture); } private static bool ContainsAny(string text, params string[] values) { return values.Any((string value) => text.Contains(value)); } private static void ClaimContainer(Container container) { try { ZNetView val = (((Object)(object)container.m_rootObjectOverride != (Object)null) ? container.m_rootObjectOverride : ((Component)container).GetComponent()); if ((Object)(object)val != (Object)null && val.IsValid() && !val.IsOwner()) { val.ClaimOwnership(); } } catch { } } private static void SaveContainer(Container container) { try { AccessTools.Method(typeof(Container), "Save", (Type[])null, (Type[])null)?.Invoke(container, Array.Empty()); } catch { } } internal static string BuildPlantProgress(Plant plant) { if ((Object)(object)plant == (Object)null || !TalentStore.HasSkill("comfort.growth_sight")) { return string.Empty; } try { ZNetView component = ((Component)plant).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if (val == null || (Object)(object)ZNet.instance == (Object)null) { return string.Empty; } long ticks = val.GetLong(ZDOVars.s_plantTime, ZNet.instance.GetTime().Ticks); double num = Math.Max(0.0, (ZNet.instance.GetTime() - new DateTime(ticks)).TotalSeconds); MethodInfo methodInfo = AccessTools.Method(typeof(Plant), "GetGrowTime", (Type[])null, (Type[])null); float val2 = ((methodInfo != null) ? Convert.ToSingle(methodInfo.Invoke(plant, Array.Empty()), CultureInfo.InvariantCulture) : plant.m_growTimeMax); val2 = Math.Max(1f, val2); float num2 = Mathf.Clamp01((float)(num / (double)val2)); TimeSpan remaining = TimeSpan.FromSeconds(Math.Max(0.0, (double)val2 - num)); return ProgressLine(num2, remaining, (num2 >= 1f) ? "erntereif" : "waechst"); } catch { return string.Empty; } } internal static string BuildPickableProgress(Pickable pickable) { if ((Object)(object)pickable == (Object)null || !TalentStore.HasSkill("comfort.growth_sight")) { return string.Empty; } try { ZNetView component = ((Component)pickable).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if (val == null) { return string.Empty; } if (!val.GetBool(ZDOVars.s_picked, false)) { return ProgressLine(1f, TimeSpan.Zero, "erntereif"); } if (pickable.m_respawnTimeMinutes <= 0f) { return "\nNicht nachwachsend"; } long num = val.GetLong(ZDOVars.s_pickedTime, 0L); if (num <= 1 || (Object)(object)ZNet.instance == (Object)null) { return string.Empty; } double num2 = Math.Max(0.0, (ZNet.instance.GetTime() - new DateTime(num)).TotalMinutes); float percent = Mathf.Clamp01((float)(num2 / (double)pickable.m_respawnTimeMinutes)); TimeSpan remaining = TimeSpan.FromMinutes(Math.Max(0.0, (double)pickable.m_respawnTimeMinutes - num2)); return ProgressLine(percent, remaining, "Regeneration"); } catch { return string.Empty; } } private static string ProgressLine(float percent, TimeSpan remaining, string label) { string text = ((percent >= 0.99f) ? "#8CFF8C" : ((percent >= 0.66f) ? "#E7E47A" : ((percent >= 0.33f) ? "#FFC266" : "#FF8B78"))); string text2 = ((remaining.TotalSeconds <= 1.0) ? "bereit" : HumanDuration(remaining)); return "\n" + label + ": " + Mathf.RoundToInt(percent * 100f) + "% · " + text2 + ""; } private static string HumanDuration(TimeSpan time) { if (time.TotalHours >= 1.0) { return Math.Floor(time.TotalHours) + " Std. " + time.Minutes + " Min."; } if (time.TotalMinutes >= 1.0) { return Math.Floor(time.TotalMinutes) + " Min. " + time.Seconds + " Sek."; } return Math.Max(1, time.Seconds) + " Sek."; } internal static IEnumerator HarvestArea(Pickable origin, Player player) { if ((Object)(object)origin == (Object)null || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || !TalentStore.HasSkill("comfort.area_harvest")) { yield break; } string prefab = Utils.GetPrefabName(((Component)origin).gameObject); if (string.IsNullOrWhiteSpace(prefab)) { yield break; } List list = (from target in (from hit in Physics.OverlapSphere(((Component)origin).transform.position, HarvestRadius) select (!((Object)(object)hit != (Object)null)) ? null : ((Component)hit).GetComponentInParent() into target where (Object)(object)target != (Object)null && (Object)(object)target != (Object)(object)origin && Utils.GetPrefabName(((Component)target).gameObject) == prefab select target).Distinct() orderby Vector3.Distance(((Component)origin).transform.position, ((Component)target).transform.position) select target).Take(HarvestMaxTargets).ToList(); int harvested = 0; foreach (Pickable item in list) { if (CanHarvest(item) && MeadowsSettlementFeature.CanModify(player, ((Component)item).transform.position, showMessage: false) && PrivateArea.CheckAccess(((Component)item).transform.position, 0f, false, false) && HasLineOfSight(player, ((Component)item).transform.position)) { try { item.Interact((Humanoid)(object)player, false, false); harvested++; } catch { } if (harvested % 5 == 0) { yield return null; } } } if (harvested > 0) { MeadowsSettlementFeature.TouchLocal(((Component)origin).transform.position, "qol_harvest"); QoLSkillRuntimeFeature.ShowCenter("Flächenernte: " + harvested + " zusätzliche Ziele."); } } private static bool CanHarvest(Pickable target) { try { ZNetView component = ((Component)target).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); return (Object)(object)component != (Object)null && component.IsValid() && val != null && !val.GetBool(ZDOVars.s_picked, false) && val.GetBool(ZDOVars.s_enabled, true) && target.GetEnabled != 0; } catch { return false; } } private static bool HasLineOfSight(Player player, Vector3 target) { //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_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_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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_006f: Unknown result type (might be due to invalid IL or missing references) Vector3 eyePoint = ((Character)player).GetEyePoint(); Vector3 val = target + Vector3.up * 0.5f - eyePoint; RaycastHit val2 = default(RaycastHit); if (!Physics.Raycast(eyePoint, ((Vector3)(ref val)).normalized, ref val2, ((Vector3)(ref val)).magnitude, LayerMask.GetMask(new string[4] { "Default", "static_solid", "piece", "terrain" }))) { return true; } return Vector3.Distance(((RaycastHit)(ref val2)).point, target) < 1.25f; } internal static IEnumerator PlantGridExtras(Player player, Piece piecePrefab, Vector3 center, Quaternion rotation, int requestedGridSize) { //IL_0015: 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_001c: 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) if ((Object)(object)player == (Object)null || (Object)(object)piecePrefab == (Object)null || !TalentStore.HasSkill("building.grid_planting")) { yield break; } Plant plantPrefab = ((Component)piecePrefab).GetComponentInChildren(true); if ((Object)(object)plantPrefab == (Object)null) { yield break; } int size = ((requestedGridSize <= 2) ? 2 : ((requestedGridSize >= 5 && MaximumGridSize >= 5) ? 5 : 3)); float spacing = Mathf.Max(1.25f, plantPrefab.m_growRadius * 2.15f); List list = GridPositions(center, ((Component)player).transform.rotation, size, spacing); int placed = 0; foreach (Vector3 item in list) { if (Vector3.Distance(item, center) < 0.2f || !TryGroundPosition(item, out var position) || !CanPlantAt(player, plantPrefab, position)) { continue; } if (!player.HaveRequirements(piecePrefab, (RequirementMode)0)) { break; } try { TerrainModifier.SetTriggerOnPlaced(true); GameObject val = Object.Instantiate(((Component)piecePrefab).gameObject, position, rotation); TerrainModifier.SetTriggerOnPlaced(false); Piece component = val.GetComponent(); if ((Object)(object)component != (Object)null) { component.SetCreator(player.GetPlayerID()); } WearNTear component2 = val.GetComponent(); if (component2 != null) { component2.OnPlaced(); } piecePrefab.m_placeEffect.Create(position, rotation, val.transform, 1f, -1); player.ConsumeResources(piecePrefab.m_resources, 0, -1, 1); ConsumePlantingCost(player); placed++; } catch (Exception ex) { TerrainModifier.SetTriggerOnPlaced(false); ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Rasterpflanzung Position uebersprungen: " + ex.Message)); } } if (placed % 3 == 0) { yield return null; } } if (placed > 0) { MeadowsSettlementFeature.TouchLocal(center, "qol_planting"); QoLSkillRuntimeFeature.ShowCenter("Rasterpflanzung: " + placed + " zusätzliche Pflanzen gesetzt."); } } internal static List GridPositions(Vector3 center, Quaternion playerRotation, int size, float spacing) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) List list = new List(); float num = (float)(size - 1) * 0.5f; Vector3 val = playerRotation * Vector3.right; Vector3 val2 = playerRotation * Vector3.forward; val.y = 0f; val2.y = 0f; ((Vector3)(ref val)).Normalize(); ((Vector3)(ref val2)).Normalize(); for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { list.Add(center + val * (((float)j - num) * spacing) + val2 * (((float)i - num) * spacing)); } } return list; } private static bool TryGroundPosition(Vector3 raw, out Vector3 position) { //IL_0001: 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_0007: 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_001b: 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) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) position = raw; float y = default(float); if (Heightmap.GetHeight(raw, ref y)) { position.y = y; return true; } RaycastHit val = default(RaycastHit); if (Physics.Raycast(raw + Vector3.up * 8f, Vector3.down, ref val, 20f, LayerMask.GetMask(new string[3] { "terrain", "piece", "static_solid" }))) { position = ((RaycastHit)(ref val)).point; return true; } return false; } private static bool CanPlantAt(Player player, Plant plant, Vector3 position) { //IL_0001: 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: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0072: 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_005d: Unknown result type (might be due to invalid IL or missing references) if (!MeadowsSettlementFeature.CanModify(player, position, showMessage: false)) { return false; } if (!PrivateArea.CheckAccess(position, 0f, false, false)) { return false; } if (!HasLineOfSight(player, position)) { return false; } Heightmap val = Heightmap.FindHeightmap(position); if ((Object)(object)val == (Object)null) { return false; } Biome biome = val.GetBiome(position, 0.02f, false); if ((plant.m_biome & biome) == 0) { return false; } if (plant.m_needCultivatedGround && !val.IsCultivated(position)) { return false; } return Physics.OverlapSphere(position + Vector3.up * 0.5f, Mathf.Max(0.5f, plant.m_growRadius * 0.85f), LayerMask.GetMask(new string[3] { "piece", "piece_nonsolid", "Default_small" })).All((Collider collider) => (Object)(object)collider == (Object)null || collider.isTrigger); } private static void ConsumePlantingCost(Player player) { try { ((Character)player).UseStamina(1f); } catch { } try { ItemData representativeEquippedItem = PlayerItemReflection.GetRepresentativeEquippedItem(player); ((representativeEquippedItem != null) ? AccessTools.Method(((object)representativeEquippedItem).GetType(), "UseDurability", (Type[])null, (Type[])null) : null)?.Invoke(representativeEquippedItem, new object[1] { 1f }); } catch { } } internal static bool TryFuelHoveredDevice(Player player, out string result) { result = string.Empty; TalentData localData = TalentStore.GetLocalData(); if ((Object)(object)player == (Object)null || localData == null || !localData.HasSkill("storage.fuel_helper")) { return false; } Fireplace val = LookTarget.FindComponent(7f); if ((Object)(object)val != (Object)null) { return FillFireplace(player, localData, val, out result); } Smelter val2 = LookTarget.FindComponent(7f); if ((Object)(object)val2 != (Object)null) { return FillSmelter(player, localData, val2, out result); } result = "Keine Feuerstelle oder Produktionsanlage anvisiert."; return false; } private static bool FillFireplace(Player player, TalentData data, Fireplace fireplace, out string result) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) result = string.Empty; if (fireplace.m_infiniteFuel || (Object)(object)fireplace.m_fuelItem == (Object)null || !MeadowsSettlementFeature.CanModify(player, ((Component)fireplace).transform.position, showMessage: false) || !PrivateArea.CheckAccess(((Component)fireplace).transform.position, 0f, false, false)) { return false; } ZNetView component = ((Component)fireplace).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if (val == null) { return false; } float num = val.GetFloat(ZDOVars.s_fuel, 0f); int num2 = Mathf.Clamp(Mathf.CeilToInt(fireplace.m_maxFuel * (float)data.FuelTargetPercent / 100f), 1, Mathf.CeilToInt(fireplace.m_maxFuel)); int num3 = Math.Max(0, num2 - Mathf.CeilToInt(num)); if (num3 == 0) { result = "Brennstoffziel bereits erreicht."; return true; } string name = fireplace.m_fuelItem.m_itemData.m_shared.m_name; int num4 = ConsumeFuel(player, data, name, num3, ((Component)fireplace).transform.position, delegate { fireplace.AddFuel(1f); }); if (num4 > 0) { MeadowsSettlementFeature.TouchLocal(((Component)fireplace).transform.position, "qol_fuel"); } result = ((num4 > 0) ? ("Brennstoffhelfer: " + num4 + " × " + QoLSkillRuntimeFeature.Localize(name) + " nachgefüllt.") : "Kein berechtigter Brennstoff vorhanden."); return num4 > 0; } private static bool FillSmelter(Player player, TalentData data, Smelter smelter, out string result) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) result = string.Empty; if ((Object)(object)smelter.m_fuelItem == (Object)null || !MeadowsSettlementFeature.CanModify(player, ((Component)smelter).transform.position, showMessage: false) || !PrivateArea.CheckAccess(((Component)smelter).transform.position, 0f, false, false)) { return false; } MethodInfo methodInfo = AccessTools.Method(typeof(Smelter), "GetFuel", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(Smelter), "SetFuel", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { return false; } float num = Convert.ToSingle(methodInfo.Invoke(smelter, Array.Empty()), CultureInfo.InvariantCulture); int num2 = Mathf.Clamp(Mathf.CeilToInt((float)(smelter.m_maxFuel * data.FuelTargetPercent) / 100f), 1, smelter.m_maxFuel); int num3 = Math.Max(0, num2 - Mathf.CeilToInt(num)); if (num3 == 0) { result = "Brennstoffziel bereits erreicht."; return true; } string name = smelter.m_fuelItem.m_itemData.m_shared.m_name; int num4 = 0; int num5 = ConsumeFuel(player, data, name, num3, ((Component)smelter).transform.position, delegate { }); if (num5 > 0) { ZNetView val = ((Component)smelter).GetComponent() ?? ((Component)smelter).GetComponentInParent(); if ((Object)(object)val != (Object)null && !val.IsOwner()) { val.ClaimOwnership(); } methodInfo2.Invoke(smelter, new object[1] { num + (float)num5 }); num4 = num5; } if (num4 > 0) { MeadowsSettlementFeature.TouchLocal(((Component)smelter).transform.position, "qol_fuel"); } result = ((num4 > 0) ? ("Brennstoffhelfer: " + num4 + " × " + QoLSkillRuntimeFeature.Localize(name) + " nachgefüllt.") : "Kein berechtigter Brennstoff vorhanden."); return num4 > 0; } private static int ConsumeFuel(Player player, TalentData data, string itemName, int need, Vector3 devicePosition, Action addOne) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) int i = 0; Inventory inventory = ((Humanoid)player).GetInventory(); for (; i < need; i++) { if (!inventory.HaveItem(itemName, true)) { break; } inventory.RemoveItem(itemName, 1, -1, true); addOne(); } if (i >= need || !data.HasSkill("storage.fuel_helper_storage")) { return i; } foreach (Container item in from c in QoLSkillRuntimeFeature.FindNearbyContainers(devicePosition, FuelContainerRadius) where IsContainerEligible(c, player.GetPlayerID()) select c) { Inventory inventory2 = item.GetInventory(); if (inventory2 == null || !TryAcquireContainerLock(item, player.GetPlayerID())) { continue; } try { ClaimContainer(item); for (; i < need; i++) { if (!inventory2.HaveItem(itemName, true)) { break; } inventory2.RemoveItem(itemName, 1, -1, true); addOne(); } InventoryReflection.NotifyChanged(inventory2); SaveContainer(item); } finally { ReleaseContainerLock(item, player.GetPlayerID()); } if (i >= need) { break; } } return i; } internal static string BuildSignRichText(string raw, TalentData data) { string text = Regex.Replace(raw ?? string.Empty, "<[^>]+>", string.Empty).Replace("\r", " ").Replace("\0", string.Empty); if (text.Length > 120) { text = text.Substring(0, 120); } string text2 = ((data.SignAlignment == 1) ? "center" : ((data.SignAlignment == 2) ? "right" : "left")); string text3 = text; if (data.SignBold) { text3 = "" + text3 + ""; } if (data.SignItalic) { text3 = "" + text3 + ""; } string text4 = (data.SignColor = NormalizeSignColor(data.SignColor)); return "" + text3 + ""; } private static string NormalizeSignColor(string value) { string text = (value ?? string.Empty).Trim(); if (!Regex.IsMatch(text, "^#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$")) { return "#FFFFFF"; } return text.ToUpperInvariant(); } internal static void CycleFineRotationStep(float direction) { TalentData localData = TalentStore.GetLocalData(); if (localData == null || !localData.HasSkill("building.fine_rotation")) { return; } int num = 0; float num2 = float.MaxValue; for (int i = 0; i < RotationSteps.Length; i++) { float num3 = Mathf.Abs(RotationSteps[i] - localData.FineRotationStep); if (num3 < num2) { num2 = num3; num = i; } } num = ((direction >= 0f) ? ((num + 1) % RotationSteps.Length) : ((num - 1 + RotationSteps.Length) % RotationSteps.Length)); localData.FineRotationStep = RotationSteps[num]; TalentStore.SaveLocalSettings(); QoLSkillRuntimeFeature.ShowCenter("Drehschritt: " + localData.FineRotationStep.ToString("0.#", CultureInfo.InvariantCulture) + "°"); } internal static string RotationHudText() { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007a: 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) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) TalentData localData = TalentStore.GetLocalData(); if (localData == null || !localData.HasSkill("building.fine_rotation")) { return string.Empty; } string text = ((localData.FineRotationAxis == 1) ? "X" : ((localData.FineRotationAxis == 2) ? "Z" : "Y")); Player localPlayer = Player.m_localPlayer; GameObject val = (((Object)(object)localPlayer != (Object)null) ? PlayerBuildReflection.GetPlacementGhost(localPlayer) : null); Vector3 val2; if (!((Object)(object)val != (Object)null)) { val2 = Vector3.zero; } else { Quaternion rotation = val.transform.rotation; val2 = ((Quaternion)(ref rotation)).eulerAngles; } Vector3 val3 = val2; string text2 = "Welt X " + FormatWorldAngle(val3.x) + " | Y " + FormatWorldAngle(val3.y) + " | Z " + FormatWorldAngle(val3.z); return text2 + " · Achse " + text + " · Schritt " + localData.FineRotationStep.ToString("0.#", CultureInfo.InvariantCulture) + "° · " + (localData.FineRotationLocalFrame ? "Eingabe lokal" : "Eingabe Welt") + " [Alt+V Achse | Alt+Shift+V Rahmen | Alt+Shift+Rad Schritt | Alt+Z Reset]"; } private static string FormatWorldAngle(float angle) { float num = Mathf.Repeat(angle, 360f); if (num >= 359.95f || num < 0.05f) { num = 0f; } return num.ToString("0.0", CultureInfo.InvariantCulture) + "°"; } internal static void ApplyFineRotation(Player player) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0076: 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) TalentData localData = TalentStore.GetLocalData(); if (!((Object)(object)player == (Object)null) && localData != null && localData.HasSkill("building.fine_rotation")) { GameObject placementGhost = PlayerBuildReflection.GetPlacementGhost(player); Piece selectedPiece = PlayerBuildReflection.GetSelectedPiece(player); if (!((Object)(object)placementGhost == (Object)null) && !((Object)(object)selectedPiece == (Object)null) && !IsRotationExcluded(selectedPiece)) { Quaternion val = Quaternion.Euler(localData.FineRotationX, localData.FineRotationY, localData.FineRotationZ); placementGhost.transform.rotation = (localData.FineRotationLocalFrame ? (placementGhost.transform.rotation * val) : (val * placementGhost.transform.rotation)); } } } private static bool IsRotationExcluded(Piece piece) { if ((Object)(object)piece == (Object)null) { return true; } if ((Object)(object)((Component)piece).GetComponentInChildren(true) != (Object)null) { return true; } if ((Object)(object)((Component)piece).GetComponentInChildren(true) != (Object)null) { return true; } return ContainsAny(Utils.GetPrefabName(((Component)piece).gameObject).ToLowerInvariant(), "sapling", "seed", "terrain", "raise", "cultivate", "path"); } internal static void AdjustFineRotation(float delta) { TalentData localData = TalentStore.GetLocalData(); if (localData != null && localData.HasSkill("building.fine_rotation")) { float num = Mathf.Sign(delta) * localData.FineRotationStep; if (localData.FineRotationAxis == 1) { localData.FineRotationX = Mathf.Repeat(localData.FineRotationX + num, 360f); } else if (localData.FineRotationAxis == 2) { localData.FineRotationZ = Mathf.Repeat(localData.FineRotationZ + num, 360f); } else { localData.FineRotationY = Mathf.Repeat(localData.FineRotationY + num, 360f); } TalentStore.SaveLocalSettings(); } } internal static string SwimOverview(Player player) { //IL_00cc: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || !TalentStore.HasSkill("orientation.swim_overview")) { return string.Empty; } try { if (!((Character)player).IsSwimming()) { return string.Empty; } } catch { return string.Empty; } float num = 0f; float num2 = 0f; try { num = player.GetStamina(); num2 = ((Character)player).GetMaxStamina(); } catch { } float num3 = 0f; try { num3 = ((Character)player).GetSkills().GetSkillLevel((SkillType)103); } catch { } float num4 = ReadFloatField(player, "m_swimStaminaDrainMinSkill", 5f); float num5 = ReadFloatField(player, "m_swimStaminaDrainMaxSkill", 2f); float num6 = Mathf.Lerp(num4, num5, Mathf.Clamp01(num3 / 100f)); float num7 = ((num6 > 0.01f) ? (num / num6) : 999f); float num8 = EstimateGroundDistance(((Component)player).transform.position); string text = ((num2 > 0f && num / num2 < SwimCriticalStaminaPercent) ? " · KRITISCH" : string.Empty); return "Schwimmen: " + num.ToString("0", CultureInfo.InvariantCulture) + "/" + num2.ToString("0", CultureInfo.InvariantCulture) + " · ca. " + num7.ToString("0", CultureInfo.InvariantCulture) + " s · Effizienz " + Mathf.RoundToInt((1f - num6 / Math.Max(0.01f, num4)) * 100f) + "% · fester Grund ~" + ((num8 < 999f) ? (Mathf.RoundToInt(num8) + " m") : ">100 m") + text; } private static float ReadFloatField(object instance, string name, float fallback) { try { FieldInfo fieldInfo = AccessTools.Field(instance.GetType(), name); return (fieldInfo != null) ? Convert.ToSingle(fieldInfo.GetValue(instance), CultureInfo.InvariantCulture) : fallback; } catch { return fallback; } } private static float EstimateGroundDistance(Vector3 position) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: 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_009b: Unknown result type (might be due to invalid IL or missing references) int mask = LayerMask.GetMask(new string[3] { "terrain", "static_solid", "piece" }); float num = 999f; Vector3 val = default(Vector3); RaycastHit val2 = default(RaycastHit); for (int i = 0; i < 16; i++) { float num2 = (float)i * 22.5f * ((float)Math.PI / 180f); ((Vector3)(ref val))..ctor(Mathf.Cos(num2), 0f, Mathf.Sin(num2)); if (Physics.Raycast(position + Vector3.up, val, ref val2, 100f, mask)) { num = Mathf.Min(num, ((RaycastHit)(ref val2)).distance); } } float num3 = default(float); if (Heightmap.GetHeight(position, ref num3)) { num = Mathf.Min(num, Mathf.Max(0f, position.y - num3)); } return num; } } internal sealed class AdvancedQoLBehaviour : MonoBehaviour { private readonly Dictionary _scheduledDoors = new Dictionary(); private readonly List _gridPreview = new List(); private Material _gridPreviewMaterial; private bool _cursorStateCaptured; private bool _previousCursorVisible; private CursorLockMode _previousCursorLock; private float _nextDoorCheck; private float _nextPreviewUpdate; private bool _showSignEditor; private Sign _editingSign; private string _signText = string.Empty; private Rect _signWindow = new Rect(80f, 80f, 520f, 430f); private static readonly int SignWindowId = "ChallengeHub_SignWorkshop_v280".GetHashCode(); private GUIStyle _hudStyle; private GUIStyle _signPreviewStyle; internal void ScheduleDoor(Door door) { if (!((Object)(object)door == (Object)null) && TalentStore.HasSkill("comfort.door_guardian")) { _scheduledDoors[door] = Time.realtimeSinceStartup + AdvancedQoLFeature.DoorCloseDelay; } } internal void OnTalentDataChanged() { if (!TalentStore.HasSkill("building.sign_workshop")) { CloseSignEditor(); } if (!TalentStore.HasSkill("building.grid_planting")) { ClearGridPreview(); } } private void Update() { //IL_0342: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { ClearGridPreview(); return; } bool flag = Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307); bool flag2 = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303); if (flag && !TalentMenuBehaviour.IsVisible) { if (Input.GetKeyDown((KeyCode)117) && TalentStore.HasSkill("storage.stack_nearby")) { TalentData localData = TalentStore.GetLocalData(); localData.AllowEmptyStorageTargets = !localData.AllowEmptyStorageTargets; TalentStore.SaveLocalSettings(); QoLSkillRuntimeFeature.ShowCenter("Leere Lagerziele: " + (localData.AllowEmptyStorageTargets ? "erlaubt" : "nur vorhandene Typen/Gruppen")); } else if (Input.GetKeyDown((KeyCode)106) && TalentStore.HasSkill("building.grid_planting")) { TalentData localData2 = TalentStore.GetLocalData(); int num = (localData2.FarmingGridSize = ((localData2.FarmingGridSize == 2) ? 3 : ((localData2.FarmingGridSize == 3 && AdvancedQoLFeature.MaximumGridSize >= 5) ? 5 : 2))); TalentStore.SaveLocalSettings(); QoLSkillRuntimeFeature.ShowCenter("Pflanzraster: " + num + "×" + num); } else if (Input.GetKeyDown((KeyCode)104) && TalentStore.HasSkill("storage.fuel_helper")) { TalentData localData3 = TalentStore.GetLocalData(); localData3.FuelTargetPercent = ((localData3.FuelTargetPercent >= 100) ? 25 : (localData3.FuelTargetPercent + 25)); TalentStore.SaveLocalSettings(); QoLSkillRuntimeFeature.ShowCenter("Brennstoffziel: " + localData3.FuelTargetPercent + "%"); } else if (Input.GetKeyDown((KeyCode)121) && TalentStore.HasSkill("storage.fuel_helper")) { AdvancedQoLFeature.TryFuelHoveredDevice(localPlayer, out var result); QoLSkillRuntimeFeature.ShowCenter(result); } else if (Input.GetKeyDown((KeyCode)100) && TalentStore.HasSkill("comfort.door_guardian")) { TalentData localData4 = TalentStore.GetLocalData(); localData4.AutoCloseDoorsInDungeons = !localData4.AutoCloseDoorsInDungeons; TalentStore.SaveLocalSettings(); QoLSkillRuntimeFeature.ShowCenter("Türwächter in Dungeons: " + (localData4.AutoCloseDoorsInDungeons ? "an" : "aus")); } else if (Input.GetKeyDown((KeyCode)105) && TalentStore.HasSkill("building.sign_workshop")) { Sign val = LookTarget.FindComponent(7f); if ((Object)(object)val != (Object)null) { OpenSignEditor(val); } } else if (Input.GetKeyDown((KeyCode)118) && TalentStore.HasSkill("building.fine_rotation")) { TalentData localData5 = TalentStore.GetLocalData(); if (flag2) { localData5.FineRotationLocalFrame = !localData5.FineRotationLocalFrame; } else { localData5.FineRotationAxis = (localData5.FineRotationAxis + 1) % 3; } TalentStore.SaveLocalSettings(); QoLSkillRuntimeFeature.ShowCenter(AdvancedQoLFeature.RotationHudText()); } else if (Input.GetKeyDown((KeyCode)122) && TalentStore.HasSkill("building.fine_rotation")) { TalentData localData6 = TalentStore.GetLocalData(); if (flag2) { localData6.FineRotationX = (localData6.FineRotationY = (localData6.FineRotationZ = 0f)); } else if (localData6.FineRotationAxis == 1) { localData6.FineRotationX = 0f; } else if (localData6.FineRotationAxis == 2) { localData6.FineRotationZ = 0f; } else { localData6.FineRotationY = 0f; } TalentStore.SaveLocalSettings(); QoLSkillRuntimeFeature.ShowCenter("Drehung zurückgesetzt."); } float y = Input.mouseScrollDelta.y; if (Mathf.Abs(y) > 0.01f && TalentStore.HasSkill("building.fine_rotation")) { if (flag2) { AdvancedQoLFeature.CycleFineRotationStep(y); } else { AdvancedQoLFeature.AdjustFineRotation(y); } } } if (Time.realtimeSinceStartup >= _nextDoorCheck) { _nextDoorCheck = Time.realtimeSinceStartup + 0.25f; ProcessDoors(localPlayer); } if (Time.realtimeSinceStartup >= _nextPreviewUpdate) { _nextPreviewUpdate = Time.realtimeSinceStartup + 0.15f; UpdateGridPreview(localPlayer, flag); } } private void ProcessDoors(Player player) { //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) KeyValuePair[] array = _scheduledDoors.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; Door door = keyValuePair.Key; if ((Object)(object)door == (Object)null) { _scheduledDoors.Remove(keyValuePair.Key); } else { if (Time.realtimeSinceStartup < keyValuePair.Value) { continue; } _scheduledDoors.Remove(door); TalentData localData = TalentStore.GetLocalData(); if (localData == null || !localData.HasSkill("comfort.door_guardian") || (((Character)player).InInterior() && !localData.AutoCloseDoorsInDungeons)) { continue; } ZNetView component = ((Component)door).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if (val == null || val.GetInt(ZDOVars.s_state, 0) == 0 || door.m_canNotBeClosed) { continue; } if (Player.GetAllPlayers().Any((Player other) => (Object)(object)other != (Object)null && Vector3.Distance(((Component)other).transform.position, ((Component)door).transform.position) <= AdvancedQoLFeature.DoorSafetyRadius)) { _scheduledDoors[door] = Time.realtimeSinceStartup + 1f; } else if (MeadowsSettlementFeature.CanModify(player, ((Component)door).transform.position, showMessage: false) && (!door.m_checkGuardStone || PrivateArea.CheckAccess(((Component)door).transform.position, 0f, false, false))) { try { door.Interact((Humanoid)(object)player, false, false); } catch { } } } } } private void UpdateGridPreview(Player player, bool alt) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Expected O, but got Unknown //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) if (!alt || !TalentStore.HasSkill("building.grid_planting")) { ClearGridPreview(); return; } Piece selectedPiece = PlayerBuildReflection.GetSelectedPiece(player); GameObject placementGhost = PlayerBuildReflection.GetPlacementGhost(player); Plant val = (((Object)(object)selectedPiece != (Object)null) ? ((Component)selectedPiece).GetComponentInChildren(true) : null); if ((Object)(object)selectedPiece == (Object)null || (Object)(object)placementGhost == (Object)null || (Object)(object)val == (Object)null) { ClearGridPreview(); return; } int farmingGridSize = TalentStore.GetLocalData().FarmingGridSize; float spacing = Mathf.Max(1.25f, val.m_growRadius * 2.15f); List list = AdvancedQoLFeature.GridPositions(placementGhost.transform.position, ((Component)player).transform.rotation, farmingGridSize, spacing); if ((Object)(object)_gridPreviewMaterial == (Object)null) { Shader val2 = Shader.Find("Unlit/Color") ?? Shader.Find("Standard"); if ((Object)(object)val2 != (Object)null) { _gridPreviewMaterial = new Material(val2) { color = new Color(0.25f, 1f, 0.35f, 0.55f) }; } } while (_gridPreview.Count < list.Count) { GameObject val3 = GameObject.CreatePrimitive((PrimitiveType)2); ((Object)val3).name = "ChallengeHub_PlantGridPreview"; Object.Destroy((Object)(object)val3.GetComponent()); val3.transform.localScale = new Vector3(0.25f, 0.02f, 0.25f); Renderer component = val3.GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)_gridPreviewMaterial != (Object)null) { component.sharedMaterial = _gridPreviewMaterial; } _gridPreview.Add(val3); } float num = default(float); for (int i = 0; i < _gridPreview.Count; i++) { bool flag = i < list.Count; _gridPreview[i].SetActive(flag); if (flag) { Vector3 val4 = list[i]; if (Heightmap.GetHeight(val4, ref num)) { val4.y = num + 0.03f; } _gridPreview[i].transform.position = val4; } } } private void ClearGridPreview() { foreach (GameObject item in _gridPreview) { if ((Object)(object)item != (Object)null) { Object.Destroy((Object)(object)item); } } _gridPreview.Clear(); } private void OpenSignEditor(Sign sign) { //IL_0014: 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_0087: 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) if (!((Object)(object)sign == (Object)null) && MeadowsSettlementFeature.CanModify(Player.m_localPlayer, ((Component)sign).transform.position, showMessage: false) && PrivateArea.CheckAccess(((Component)sign).transform.position, 0f, true, false)) { _editingSign = sign; string text = string.Empty; try { text = sign.GetText(); } catch { } _signText = Regex.Replace(text ?? string.Empty, "<[^>]+>", string.Empty); if (!_cursorStateCaptured) { _previousCursorVisible = Cursor.visible; _previousCursorLock = Cursor.lockState; _cursorStateCaptured = true; } _showSignEditor = true; Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; } } private void CloseSignEditor() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) _showSignEditor = false; _editingSign = null; if (_cursorStateCaptured) { Cursor.visible = _previousCursorVisible; Cursor.lockState = _previousCursorLock; _cursorStateCaptured = false; } } private void OnGUI() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Expected O, but got Unknown //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) if (_hudStyle == null) { _hudStyle = new GUIStyle(GUI.skin.box) { alignment = (TextAnchor)4, fontSize = 15, wordWrap = true }; _signPreviewStyle = new GUIStyle(GUI.skin.box) { alignment = (TextAnchor)4, fontSize = 18, wordWrap = true, richText = true }; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && !TalentMenuBehaviour.IsVisible) { string text = AdvancedQoLFeature.RotationHudText(); if (!string.IsNullOrEmpty(text) && (Object)(object)PlayerBuildReflection.GetPlacementGhost(localPlayer) != (Object)null) { GUI.Box(new Rect((float)Screen.width * 0.5f - 330f, (float)Screen.height - 178f, 660f, 30f), text, _hudStyle); } string text2 = AdvancedQoLFeature.SwimOverview(localPlayer); if (!string.IsNullOrEmpty(text2)) { GUI.Box(new Rect((float)Screen.width * 0.5f - 330f, 86f, 660f, 34f), text2, _hudStyle); } } if (_showSignEditor && (Object)(object)_editingSign != (Object)null) { _signWindow = GUI.Window(SignWindowId, _signWindow, new WindowFunction(DrawSignWindow), "ChallengeHub Schildwerkstatt"); } } private void DrawSignWindow(int id) { //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) TalentData localData = TalentStore.GetLocalData(); if (localData == null) { CloseSignEditor(); return; } GUILayout.Label("Text (max. 120 Zeichen)", Array.Empty()); _signText = GUILayout.TextArea(_signText ?? string.Empty, 120, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(80f) }); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Farbe", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(55f) }); localData.SignColor = GUILayout.TextField(localData.SignColor ?? "#FFFFFF", 9, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) }); GUILayout.Label("Größe", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }); if (int.TryParse(GUILayout.TextField(localData.SignFontSize.ToString(CultureInfo.InvariantCulture), 3, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }), out var result)) { localData.SignFontSize = Mathf.Clamp(result, 12, AdvancedQoLFeature.SignMaxFontSize); } localData.SignBold = GUILayout.Toggle(localData.SignBold, "Fett", Array.Empty()); localData.SignItalic = GUILayout.Toggle(localData.SignItalic, "Kursiv", Array.Empty()); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Links", Array.Empty())) { localData.SignAlignment = 0; } if (GUILayout.Button("Mitte", Array.Empty())) { localData.SignAlignment = 1; } if (GUILayout.Button("Rechts", Array.Empty())) { localData.SignAlignment = 2; } GUILayout.EndHorizontal(); GUILayout.Label("Vorschau:", Array.Empty()); GUILayout.Box(AdvancedQoLFeature.BuildSignRichText(_signText, localData), _signPreviewStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(70f) }); GUILayout.BeginHorizontal(Array.Empty()); for (int i = 0; i < 3; i++) { int index = i; if (GUILayout.Button("Vorlage " + (i + 1), Array.Empty())) { ApplyOrSavePreset(localData, index, Event.current.shift); } } GUILayout.EndHorizontal(); GUILayout.Label("Shift+Klick speichert den aktuellen Stil in die Vorlage.", Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Speichern", Array.Empty())) { try { _editingSign.SetText(AdvancedQoLFeature.BuildSignRichText(_signText, localData)); MeadowsSettlementFeature.TouchLocal(((Component)_editingSign).transform.position, "qol_sign"); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Schildtext konnte nicht gespeichert werden: " + ex.Message)); } } TalentStore.SaveLocalSettings(); CloseSignEditor(); } if (GUILayout.Button("Abbrechen", Array.Empty())) { CloseSignEditor(); } GUILayout.EndHorizontal(); GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _signWindow)).width, 22f)); } private static void ApplyOrSavePreset(TalentData data, int index, bool save) { while (data.SignStylePresets.Count <= index) { data.SignStylePresets.Add(new SignStylePresetData { Name = "Vorlage " + (data.SignStylePresets.Count + 1) }); } SignStylePresetData signStylePresetData = data.SignStylePresets[index]; if (save) { signStylePresetData.Color = data.SignColor; signStylePresetData.FontSize = data.SignFontSize; signStylePresetData.Bold = data.SignBold; signStylePresetData.Italic = data.SignItalic; signStylePresetData.Alignment = data.SignAlignment; signStylePresetData.Normalize(); TalentStore.SaveLocalSettings(); } else { data.SignColor = signStylePresetData.Color; data.SignFontSize = signStylePresetData.FontSize; data.SignBold = signStylePresetData.Bold; data.SignItalic = signStylePresetData.Italic; data.SignAlignment = signStylePresetData.Alignment; } } private void OnDestroy() { CloseSignEditor(); ClearGridPreview(); if ((Object)(object)_gridPreviewMaterial != (Object)null) { Object.Destroy((Object)(object)_gridPreviewMaterial); } _gridPreviewMaterial = null; } } [HarmonyPatch(typeof(Plant), "GetHoverText")] internal static class QoLPlantProgressPatch { [HarmonyPostfix] private static void Postfix(Plant __instance, ref string __result) { __result += AdvancedQoLFeature.BuildPlantProgress(__instance); } } [HarmonyPatch(typeof(Pickable), "GetHoverText")] internal static class QoLPickableProgressPatch { [HarmonyPostfix] private static void Postfix(Pickable __instance, ref string __result) { __result += AdvancedQoLFeature.BuildPickableProgress(__instance); } } [HarmonyPatch(typeof(Pickable), "Interact")] internal static class QoLAreaHarvestPatch { [HarmonyPostfix] private static void Postfix(Pickable __instance, Humanoid character, bool repeat, bool alt) { Player val = (Player)(object)((character is Player) ? character : null); if (!(!alt || repeat) && !((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && TalentStore.HasSkill("comfort.area_harvest")) { Plugin instance = Plugin.Instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(AdvancedQoLFeature.HarvestArea(__instance, val)); } } } } [HarmonyPatch(typeof(Door), "Interact")] internal static class QoLDoorGuardianPatch { [HarmonyPostfix] private static void Postfix(Door __instance, Humanoid character, bool hold, ref bool __result) { Player val = (Player)(object)((character is Player) ? character : null); if (!__result || hold || (Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer || !TalentStore.HasSkill("comfort.door_guardian")) { return; } object obj; if (__instance == null) { obj = null; } else { ZNetView component = ((Component)__instance).GetComponent(); obj = ((component != null) ? component.GetZDO() : null); } ZDO val2 = (ZDO)obj; if (val2 != null && val2.GetInt(ZDOVars.s_state, 0) != 0) { Plugin instance = Plugin.Instance; if (instance != null) { ((Component)instance).GetComponent()?.ScheduleDoor(__instance); } } } } [HarmonyPatch(typeof(Player), "UpdatePlacementGhost")] internal static class QoLFineRotationGhostPatch { [HarmonyPostfix] private static void Postfix(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { AdvancedQoLFeature.ApplyFineRotation(__instance); } } } [HarmonyPatch(typeof(Player), "PlacePiece", new Type[] { typeof(Piece), typeof(Vector3), typeof(Quaternion), typeof(bool) })] internal static class QoLGridPlantingPatch { internal sealed class State { internal Piece Piece; internal Vector3 Position; internal Quaternion Rotation; internal bool Requested; internal int Size; } [HarmonyPrefix] private static void Prefix(Player __instance, out State __state) { //IL_0083: 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_0094: 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) __state = null; if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && TalentStore.HasSkill("building.grid_planting") && (Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307))) { Piece selectedPiece = PlayerBuildReflection.GetSelectedPiece(__instance); GameObject placementGhost = PlayerBuildReflection.GetPlacementGhost(__instance); if (!((Object)(object)selectedPiece == (Object)null) && !((Object)(object)placementGhost == (Object)null) && !((Object)(object)((Component)selectedPiece).GetComponentInChildren(true) == (Object)null)) { TalentData localData = TalentStore.GetLocalData(); __state = new State { Piece = selectedPiece, Position = placementGhost.transform.position, Rotation = placementGhost.transform.rotation, Requested = true, Size = (localData?.FarmingGridSize ?? 3) }; } } } [HarmonyPostfix] private static void Postfix(Player __instance, State __state) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (__state != null && __state.Requested && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { Plugin instance = Plugin.Instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(AdvancedQoLFeature.PlantGridExtras(__instance, __state.Piece, __state.Position, __state.Rotation, __state.Size)); } } } } internal static class StartupWelcomeFeature { internal static void Initialize(Plugin plugin) { if (!((Object)(object)plugin == (Object)null) && !Application.isBatchMode && (Object)(object)((Component)plugin).GetComponent() == (Object)null) { ((Component)plugin).gameObject.AddComponent(); } } } internal sealed class BlutEidStartupBehaviour : MonoBehaviour { [Serializable] private sealed class MainMenuCharacterRegistration { public string action; public string clientVersion; public string challengeShortCode; public string linkCode; public string characterId; public string characterName; } [Serializable] private sealed class DeathRunEnvelope { public bool ok; public bool worldReady; public DeathRunWeb run; } [Serializable] private sealed class DeathRunWeb { public string id; public string worldCode; public string worldPackageStatus; } internal static bool CharacterSelected; private Texture2D _image; private Texture2D _panel; private GUIStyle _button; private GUIStyle _title; private GUIStyle _status; private GUIStyle _version; private string _message = "Vorzeichen des Nordens · Charakter auswählen und Challenge-Welt starten"; private bool _resumeRunning; private string _installedWorldCode = string.Empty; private void OnGUI() { //IL_0054: 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) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) if (IsMainMenuHome()) { EnsureAssets(); float num = Mathf.Clamp((float)Screen.width * 0.34f, 390f, 610f); float num2 = num / 1.777f; Rect val = default(Rect); ((Rect)(ref val))..ctor((float)Screen.width - num - 24f, 24f, num, num2 + 128f); GUI.DrawTexture(val, (Texture)(object)_panel, (ScaleMode)0); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref val)).x + 6f, ((Rect)(ref val)).y + 6f, num - 12f, num2 - 2f); if ((Object)(object)_image != (Object)null) { GUI.DrawTexture(val2, (Texture)(object)_image, (ScaleMode)2, true); } if (GUI.Button(val2, GUIContent.none, _button)) { HandleCardClick(); } GUI.Label(new Rect(((Rect)(ref val)).x + 18f, ((Rect)(ref val2)).yMax + 8f, num - 36f, 34f), "Der Blut-Eid: Vorzeichen des Nordens", _title); GUI.Label(new Rect(((Rect)(ref val)).x + 18f, ((Rect)(ref val2)).yMax + 45f, num - 36f, 42f), _message, _status); GUI.Label(new Rect(((Rect)(ref val)).x + 18f, ((Rect)(ref val2)).yMax + 91f, num - 36f, 24f), "ChallengeHub Blut-Eid v2.12.50 · Modpaket aktuell", _version); } } private static bool IsMainMenuHome() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); if (string.Equals(((Scene)(ref activeScene)).name, "start", StringComparison.OrdinalIgnoreCase)) { return (Object)(object)Player.m_localPlayer == (Object)null; } return false; } private void HandleCardClick() { FejdStartup startup = Object.FindFirstObjectByType(); if ((Object)(object)startup == (Object)null) { _message = "Valheim-Hauptmenü ist noch nicht bereit"; return; } if (!string.IsNullOrWhiteSpace(_installedWorldCode)) { RefreshAndSelectWorld(startup, _installedWorldCode); return; } if (!CharacterSelected) { _message = "Schritt 1 von 2 · Charakter auswählen"; AccessTools.Method(typeof(FejdStartup), "ShowCharacterSelection", (Type[])null, (Type[])null)?.Invoke(startup, null); return; } PlayerLinkSetupFeature playerLinkSetupFeature = (((Object)(object)Plugin.Instance != (Object)null) ? ((Component)Plugin.Instance).GetComponent() : null); _message = "Schritt 2 von 2 · Blut-Eid-Welt wird eingerichtet ..."; if ((Object)(object)playerLinkSetupFeature == (Object)null) { _message = "ChallengeHub-Verbindung ist nicht bereit"; return; } playerLinkSetupFeature.SelectDeathRunFromMainMenu(delegate(string message) { if (message.IndexOf("eingerichtet", StringComparison.OrdinalIgnoreCase) < 0) { _message = message; } else { ((MonoBehaviour)this).StartCoroutine(RegisterSelectedCharacterAndOpenWebApp(startup)); } }); } private IEnumerator RegisterSelectedCharacterAndOpenWebApp(FejdStartup startup) { PlayerProfile val = SelectedProfile(startup); if (val == null) { CharacterSelected = false; _message = "Bitte zuerst einen Charakter auswählen"; yield break; } string text = ((Plugin.PlayerLinkCode != null) ? (Plugin.PlayerLinkCode.Value ?? string.Empty).Trim() : string.Empty); if (string.IsNullOrWhiteSpace(text)) { _message = "Player-Link-Code fehlt · bitte im ChallengeHub-Dialog eintragen"; yield break; } string characterId = val.GetPlayerID().ToString(); string characterName = val.GetName() ?? "Valheim-Charakter"; MainMenuCharacterRegistration value = new MainMenuCharacterRegistration { action = "register", challengeShortCode = "BLUTEID", linkCode = text, characterId = characterId, characterName = characterName, clientVersion = "2.12.50" }; string text2 = Plugin.ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/api/valheim/admission"; byte[] bytes = Encoding.UTF8.GetBytes(ChallengeHubJson.Serialize(value)); UnityWebRequest request = new UnityWebRequest(text2, "POST"); try { request.uploadHandler = (UploadHandler)new UploadHandlerRaw(bytes); request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); request.SetRequestHeader("Content-Type", "application/json"); request.SetRequestHeader("x-challengehub-key", Plugin.ApiKey.Value ?? string.Empty); request.timeout = 15; yield return request.SendWebRequest(); if ((int)request.result != 1) { _message = ((request.responseCode == 401) ? "Player-Link-Code oder Challenge-Zugang ungültig" : "Charakter konnte nicht übertragen werden"); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Blut-Eid Charakterregistrierung fehlgeschlagen: HTTP " + request.responseCode + " " + request.error)); } yield break; } } finally { ((IDisposable)request)?.Dispose(); } _message = characterName + " übertragen · Schwierigkeit in der Webapp wählen"; Application.OpenURL(Plugin.ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/deathrun?characterId=" + UnityWebRequest.EscapeURL(characterId) + "&source=valheim"); yield return WaitForPreparedWorld(startup, characterId); } private IEnumerator WaitForPreparedWorld(FejdStartup startup, string characterId) { float deadline = Time.realtimeSinceStartup + 1800f; while (Time.realtimeSinceStartup < deadline) { string text = Plugin.ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/api/valheim/deathrun?characterId=" + UnityWebRequest.EscapeURL(characterId); UnityWebRequest request = UnityWebRequest.Get(text); try { ApplyMainMenuAuthorization(request); request.timeout = 12; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Blut-Eid: Run-Status wird direkt bei ChallengeHub abgefragt."); } yield return request.SendWebRequest(); if ((int)request.result == 1) { DeathRunEnvelope deathRunEnvelope = ChallengeHubJson.Deserialize(request.downloadHandler.text); if (deathRunEnvelope != null && deathRunEnvelope.ok && deathRunEnvelope.run != null) { if (deathRunEnvelope.worldReady || string.Equals(deathRunEnvelope.run.worldPackageStatus, "ready", StringComparison.OrdinalIgnoreCase)) { yield return DownloadAndInstallWorld(startup, deathRunEnvelope.run); yield break; } _message = "Run " + deathRunEnvelope.run.worldCode + " · Welt wird vorbereitet ..."; } } } finally { ((IDisposable)request)?.Dispose(); } yield return (object)new WaitForSecondsRealtime(4f); } _message = "Weltvorbereitung dauert länger · Valheim kann geöffnet bleiben"; } internal void ResumeSelectedRun(FejdStartup startup, PlayerProfile selectedProfile) { if (!_resumeRunning && (Object)(object)startup != (Object)null && selectedProfile != null) { ((MonoBehaviour)this).StartCoroutine(ResumeSelectedRunRoutine(startup, selectedProfile)); } } private IEnumerator ResumeSelectedRunRoutine(FejdStartup startup, PlayerProfile selectedProfile) { _resumeRunning = true; yield return (object)new WaitForSecondsRealtime(0.35f); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Blut-Eid: aktiver Run wird für Charakter " + (selectedProfile.GetName() ?? "unbekannt") + " gesucht.")); } string characterId = selectedProfile.GetPlayerID().ToString(); string text = Plugin.ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/api/valheim/deathrun?characterId=" + UnityWebRequest.EscapeURL(characterId); UnityWebRequest request = UnityWebRequest.Get(text); try { ApplyMainMenuAuthorization(request); request.timeout = 12; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"Blut-Eid: vorhandener Run wird direkt bei ChallengeHub abgefragt."); } yield return request.SendWebRequest(); if ((int)request.result != 1) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("Blut-Eid Run-Wiederaufnahme fehlgeschlagen: HTTP " + request.responseCode + " " + request.error)); } _resumeRunning = false; yield break; } DeathRunEnvelope deathRunEnvelope = ChallengeHubJson.Deserialize(request.downloadHandler.text); if (deathRunEnvelope == null || !deathRunEnvelope.ok || deathRunEnvelope.run == null) { _resumeRunning = false; yield break; } _message = "Aktiver Run " + deathRunEnvelope.run.worldCode + " wird fortgesetzt ..."; bool flag = deathRunEnvelope.worldReady || string.Equals(deathRunEnvelope.run.worldPackageStatus, "ready", StringComparison.OrdinalIgnoreCase); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)("Blut-Eid: Run-Antwort empfangen; ready=" + flag + "; worldReady=" + deathRunEnvelope.worldReady + "; packageStatus=" + (deathRunEnvelope.run.worldPackageStatus ?? ""))); } if (flag) { string path = Path.Combine(Application.persistentDataPath, "worlds_local"); string path2 = Path.Combine(path, deathRunEnvelope.run.worldCode + ".db"); string path3 = Path.Combine(path, deathRunEnvelope.run.worldCode + ".fwl"); if (File.Exists(path2) && File.Exists(path3)) { _installedWorldCode = deathRunEnvelope.run.worldCode; RefreshAndSelectWorld(startup, deathRunEnvelope.run.worldCode); } else { _message = deathRunEnvelope.run.worldCode + " fehlt lokal · wird erneut heruntergeladen ..."; yield return DownloadAndInstallWorld(startup, deathRunEnvelope.run); } } else { yield return WaitForPreparedWorld(startup, characterId); } } finally { ((IDisposable)request)?.Dispose(); } _resumeRunning = false; } private static void ApplyMainMenuAuthorization(UnityWebRequest request) { string text = ((Plugin.PlayerLinkCode != null) ? (Plugin.PlayerLinkCode.Value ?? string.Empty).Trim() : string.Empty); request.SetRequestHeader("x-challengehub-key", Plugin.ApiKey.Value ?? string.Empty); request.SetRequestHeader("x-player-link-code", text); } private IEnumerator DownloadAndInstallWorld(FejdStartup startup, DeathRunWeb run) { _message = run.worldCode + " · Welt wird heruntergeladen ..."; string text = Plugin.ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/api/valheim/deathrun/world?runId=" + UnityWebRequest.EscapeURL(run.id); UnityWebRequest request = UnityWebRequest.Get(text); try { ApplyMainMenuAuthorization(request); request.timeout = 60; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Blut-Eid: fertiges Weltpaket wird direkt heruntergeladen."); } yield return request.SendWebRequest(); if ((int)request.result != 1) { _message = "Welt-Download fehlgeschlagen · erneut auf die Kachel klicken"; yield break; } try { string text2 = Path.Combine(Application.persistentDataPath, "worlds_local"); Directory.CreateDirectory(text2); using (MemoryStream stream = new MemoryStream(request.downloadHandler.data, writable: false)) { using ZipArchive zipArchive = new ZipArchive(stream, ZipArchiveMode.Read, leaveOpen: false); int num = 0; foreach (ZipArchiveEntry entry in zipArchive.Entries) { string fileName = Path.GetFileName(entry.FullName); string extension = Path.GetExtension(fileName); if (string.IsNullOrWhiteSpace(fileName) || (!extension.Equals(".db", StringComparison.OrdinalIgnoreCase) && !extension.Equals(".fwl", StringComparison.OrdinalIgnoreCase))) { continue; } string path = Path.Combine(text2, fileName); using (Stream stream2 = entry.Open()) { using FileStream destination = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None); stream2.CopyTo(destination); } num++; } if (num < 2) { throw new InvalidDataException("Weltpaket enthält keine vollständige .db/.fwl-Welt."); } } _installedWorldCode = run.worldCode; RefreshAndSelectWorld(startup, run.worldCode); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Blut-Eid-Welt automatisch installiert: " + run.worldCode)); } } catch (Exception ex) { _message = "Welt konnte lokal nicht installiert werden"; ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("Automatische Blut-Eid-Weltinstallation fehlgeschlagen: " + ex)); } } } finally { ((IDisposable)request)?.Dispose(); } } private void RefreshAndSelectWorld(FejdStartup startup, string worldCode) { try { SaveSystem.InvalidateCache(); SaveSystem.ClearWorldListCache(false); AccessTools.Method(typeof(FejdStartup), "ShowStartGame", (Type[])null, (Type[])null)?.Invoke(startup, null); AccessTools.Method(typeof(FejdStartup), "UpdateWorldList", new Type[1] { typeof(bool) }, (Type[])null)?.Invoke(startup, new object[1] { false }); FieldInfo fieldInfo = AccessTools.Field(typeof(FejdStartup), "m_worlds"); IList list = ((fieldInfo != null) ? (fieldInfo.GetValue(startup) as IList) : null); FieldInfo fieldInfo2 = AccessTools.Field(typeof(World), "m_fileName"); FieldInfo fieldInfo3 = AccessTools.Field(typeof(World), "m_name"); int num = -1; if (list != null) { for (int i = 0; i < list.Count; i++) { World obj = list[i]; string a = ((fieldInfo2 != null) ? (fieldInfo2.GetValue(obj) as string) : string.Empty); string a2 = ((fieldInfo3 != null) ? (fieldInfo3.GetValue(obj) as string) : string.Empty); if (string.Equals(a, worldCode, StringComparison.OrdinalIgnoreCase) || string.Equals(a2, worldCode, StringComparison.OrdinalIgnoreCase)) { num = i; break; } } } if (num < 0) { _message = worldCode + " installiert · erneut auf die Kachel klicken"; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Blut-Eid: installierte Welt wurde nach UpdateWorldList nicht gefunden: " + worldCode)); } return; } AccessTools.Method(typeof(FejdStartup), "SetSelectedWorld", new Type[2] { typeof(int), typeof(bool) }, (Type[])null)?.Invoke(startup, new object[2] { num, true }); AccessTools.Method(typeof(FejdStartup), "RefreshWorldSelection", (Type[])null, (Type[])null)?.Invoke(startup, null); _message = worldCode + " ausgewählt · Multiplayer und Passwort festlegen, dann Start drücken"; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Blut-Eid: Weltliste aktualisiert und Run-Welt ausgewählt: " + worldCode)); } } catch (Exception ex) { _message = "Weltliste konnte nicht aktualisiert werden · erneut auf die Kachel klicken"; ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("Blut-Eid Weltlisten-Aktualisierung fehlgeschlagen: " + ex)); } } } internal static PlayerProfile SelectedProfile(FejdStartup startup) { FieldInfo fieldInfo = AccessTools.Field(typeof(FejdStartup), "m_profiles"); FieldInfo fieldInfo2 = AccessTools.Field(typeof(FejdStartup), "m_profileIndex"); IList list = ((fieldInfo != null) ? (fieldInfo.GetValue(startup) as IList) : null); int num = ((fieldInfo2 != null) ? Convert.ToInt32(fieldInfo2.GetValue(startup)) : (-1)); if (list == null || num < 0 || num >= list.Count) { return null; } return list[num]; } private void EnsureAssets() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Expected O, but got Unknown //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Expected O, but got Unknown //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Expected O, but got Unknown if (_button != null) { return; } _button = new GUIStyle(GUI.skin.button); GUIStyleState normal = _button.normal; GUIStyleState hover = _button.hover; Texture2D val = (_button.active.background = null); Texture2D background = (hover.background = val); normal.background = background; _button.focused.background = null; _panel = new Texture2D(1, 1, (TextureFormat)4, false); _panel.SetPixel(0, 0, new Color(0.035f, 0.045f, 0.04f, 0.94f)); _panel.Apply(); _title = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)4, fontSize = 27, fontStyle = (FontStyle)1 }; _title.normal.textColor = new Color(0.96f, 0.77f, 0.35f, 1f); _status = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)4, fontSize = 17, fontStyle = (FontStyle)1, wordWrap = true }; _status.normal.textColor = new Color(0.55f, 0.86f, 0.62f, 1f); _version = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)4, fontSize = 14, fontStyle = (FontStyle)1 }; _version.normal.textColor = new Color(0.9f, 0.91f, 0.86f, 1f); try { using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ChallengeHubValheim.Assets.BlutEid.png"); using MemoryStream memoryStream = new MemoryStream(); stream?.CopyTo(memoryStream); _image = new Texture2D(2, 2, (TextureFormat)4, false); if (stream == null || !LoadImageCompat(_image, memoryStream.ToArray())) { Object.Destroy((Object)(object)_image); _image = null; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Blut-Eid-Titelbild konnte nicht geladen werden: " + ex.Message)); } } } private static bool LoadImageCompat(Texture2D texture, byte[] bytes) { Type type = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule", throwOnError: false); MethodInfo methodInfo = ((type != null) ? type.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[3] { typeof(Texture2D), typeof(byte[]), typeof(bool) }, null) : null); if (methodInfo == null) { return false; } object obj = methodInfo.Invoke(null, new object[3] { texture, bytes, true }); if (obj is bool) { return (bool)obj; } return false; } } [HarmonyPatch(typeof(FejdStartup), "OnSelelectCharacterBack")] internal static class BlutEidShowCardOnMainMenuReturnPatch { private static void Postfix() { BlutEidStartupBehaviour.CharacterSelected = false; } } [HarmonyPatch(typeof(FejdStartup), "OnCharacterStart")] internal static class BlutEidCharacterStartFallbackPatch { private static void Prefix(FejdStartup __instance, ref PlayerProfile __state) { __state = BlutEidStartupBehaviour.SelectedProfile(__instance); } private static void Postfix(FejdStartup __instance, PlayerProfile __state) { BlutEidStartupBehaviour.CharacterSelected = true; BlutEidStartupBehaviour blutEidStartupBehaviour = (((Object)(object)Plugin.Instance != (Object)null) ? ((Component)Plugin.Instance).GetComponent() : null); if ((Object)(object)blutEidStartupBehaviour != (Object)null && __state != null) { blutEidStartupBehaviour.ResumeSelectedRun(__instance, __state); return; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Blut-Eid: ausgewähltes Charakterprofil konnte beim Start nicht übernommen werden."); } } private static IEnumerator OpenWorldSelectionIfStillClosed(FejdStartup startup) { yield return (object)new WaitForSecondsRealtime(0.35f); if ((Object)(object)startup == (Object)null) { yield break; } Scene activeScene = SceneManager.GetActiveScene(); if (!string.Equals(((Scene)(ref activeScene)).name, "start", StringComparison.OrdinalIgnoreCase)) { yield break; } FieldInfo fieldInfo = AccessTools.Field(typeof(FejdStartup), "m_startGamePanel"); GameObject val = (GameObject)((fieldInfo != null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val != (Object)null && val.activeInHierarchy) { yield break; } MethodInfo methodInfo = AccessTools.Method(typeof(FejdStartup), "ShowStartGame", (Type[])null, (Type[])null); if (methodInfo == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Blut-Eid: FejdStartup.ShowStartGame wurde nicht gefunden."); } yield break; } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"Blut-Eid: Weltwahl-Fallback nach Charakterauswahl aktiviert."); } methodInfo.Invoke(startup, null); } } [Serializable] internal sealed class BossArenaResetRecord { public string id = string.Empty; public string boss = string.Empty; public float x; public float y; public float z; public string queuedAtUtc = string.Empty; } [Serializable] internal sealed class BossArenaResetStore { public BossArenaResetRecord[] pending = new BossArenaResetRecord[0]; } internal static class BossArenaResetFeature { private static Plugin _plugin; private static ConfigEntry _enabled; private static ConfigEntry _radius; private static ConfigEntry _clearSeconds; private static readonly List Pending = new List(); private static readonly Dictionary ClearSince = new Dictionary(StringComparer.OrdinalIgnoreCase); private static bool _loaded; private static bool _processing; private static string StorePath => Path.Combine(Paths.ConfigPath, "ChallengeHubValheim", "boss-arena-resets.json"); private static float Radius => Mathf.Clamp(_radius?.Value ?? 100f, 25f, 200f); internal static void Initialize(Plugin plugin) { _plugin = plugin; _enabled = ((BaseUnityPlugin)plugin).Config.Bind("BossArenaReset", "Enabled", true, "Setzt den Bossbereich nach dem Bosskill zurueck, sobald kein Spieler mehr im Radius ist."); _radius = ((BaseUnityPlugin)plugin).Config.Bind("BossArenaReset", "Radius", 100f, "Radius des Bossbereichs in Metern."); _clearSeconds = ((BaseUnityPlugin)plugin).Config.Bind("BossArenaReset", "PlayerFreeConfirmSeconds", 5f, "Durchgehend spielerfreie Zeit vor dem Reset."); ((MonoBehaviour)plugin).StartCoroutine(Loop()); } internal static void Queue(Vector3 center, string boss, string completionId) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_plugin == (Object)null || _enabled == null || !_enabled.Value || !ChallengeHubWorldState.IsServer) { return; } EnsureLoaded(); string id = (string.IsNullOrWhiteSpace(completionId) ? (Plugin.NormalizeKey(boss) + ":" + Mathf.RoundToInt(center.x) + ":" + Mathf.RoundToInt(center.z)) : completionId); if (!Pending.Any((BossArenaResetRecord record) => string.Equals(record.id, id, StringComparison.OrdinalIgnoreCase))) { Pending.Add(new BossArenaResetRecord { id = id, boss = Plugin.CanonicalBossKey(boss), x = center.x, y = center.y, z = center.z, queuedAtUtc = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture) }); Save(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Bossbereich-Reset vorgemerkt: " + boss + "; Radius=" + Radius.ToString("0", CultureInfo.InvariantCulture) + "m.")); } } } private static IEnumerator Loop() { Vector3 center = default(Vector3); while (true) { yield return (object)new WaitForSeconds(2f); if (_processing || _enabled == null || !_enabled.Value || !ChallengeHubWorldState.IsServer || !ChallengeHubServerGateFeature.GameplayAllowed || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null) { continue; } EnsureLoaded(); BossArenaResetRecord bossArenaResetRecord = Pending.FirstOrDefault(); if (bossArenaResetRecord != null) { ((Vector3)(ref center))..ctor(bossArenaResetRecord.x, bossArenaResetRecord.y, bossArenaResetRecord.z); float value; if (AnyPlayerNear(center, Radius)) { ClearSince.Remove(bossArenaResetRecord.id); } else if (!ClearSince.TryGetValue(bossArenaResetRecord.id, out value)) { ClearSince[bossArenaResetRecord.id] = Time.realtimeSinceStartup; } else if (!(Time.realtimeSinceStartup - value < Mathf.Clamp(_clearSeconds?.Value ?? 5f, 2f, 60f))) { _processing = true; yield return Reset(bossArenaResetRecord, center); _processing = false; } } } } private static IEnumerator Reset(BossArenaResetRecord record, Vector3 center) { //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) List list = FindNearby(center); HashSet protectedPlayers = ServerAuthoritativeZdoDestroyer.CollectProtectedPlayerZdos(); int removed = 0; int preserved = 0; int failed = 0; int batch = 0; foreach (ZDO item in list) { if (item == null || !item.IsValid() || Utils.DistanceXZ(item.GetPosition(), center) > Radius) { continue; } GameObject val = null; try { val = ZNetScene.instance.GetPrefab(item.GetPrefab()); } catch { } if ((Object)(object)val == (Object)null || Preserve(item, val, protectedPlayers)) { preserved++; } else if (Resettable(item, val)) { ServerAuthoritativeZdoDestroyer.DestroyResult destroyResult = ServerAuthoritativeZdoDestroyer.Destroy(item, protectedPlayers, "boss_arena_reset:" + record.boss); if (destroyResult == ServerAuthoritativeZdoDestroyer.DestroyResult.Destroyed || destroyResult == ServerAuthoritativeZdoDestroyer.DestroyResult.AlreadyGone) { removed++; } else { failed++; } int num = batch + 1; batch = num; if (num >= 24) { ServerAuthoritativeZdoDestroyer.FlushDestroyed("boss_arena_reset_batch"); batch = 0; yield return null; } } } ServerAuthoritativeZdoDestroyer.FlushDestroyed("boss_arena_reset_final"); Pending.Remove(record); ClearSince.Remove(record.id); Save(); _plugin?.SendServerEvent("boss_arena_reset", new Dictionary { { "boss", record.boss }, { "position", Plugin.SerializeVector(center) }, { "radius", Radius }, { "removedObjects", removed }, { "preservedObjects", preserved }, { "failedObjects", failed } }); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Bossbereich-Reset abgeschlossen: " + record.boss + "; entfernt=" + removed + "; bewahrt=" + preserved + "; fehlgeschlagen=" + failed + ".")); } } private static bool Preserve(ZDO zdo, GameObject prefab, HashSet protectedPlayers) { if (ServerAuthoritativeZdoDestroyer.IsProtectedPlayerZdo(zdo, protectedPlayers)) { return true; } if ((Object)(object)prefab.GetComponent() != (Object)null || (Object)(object)prefab.GetComponentInChildren(true) != (Object)null) { return true; } if ((Object)(object)prefab.GetComponentInChildren(true) != (Object)null || (Object)(object)prefab.GetComponentInChildren(true) != (Object)null) { return true; } if ((Object)(object)prefab.GetComponentInChildren(true) != (Object)null) { return true; } string text = Plugin.NormalizeKey(Utils.GetPrefabName(prefab)); if (!text.Contains("locationproxy") && !text.Contains("bossstone") && !text.Contains("bossaltar") && !text.Contains("offer") && !text.Contains("challengehub_guardian") && !text.Contains("guardstone")) { return text.Contains("ward"); } return true; } private static bool Resettable(ZDO zdo, GameObject prefab) { long num = 0L; try { num = zdo.GetLong(ZDOVars.s_creator, 0L); } catch { } if (num != 0L && ((Object)(object)prefab.GetComponentInChildren(true) != (Object)null || (Object)(object)prefab.GetComponentInChildren(true) != (Object)null)) { return true; } Character componentInChildren = prefab.GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null && !componentInChildren.IsPlayer()) { return true; } return (Object)(object)prefab.GetComponentInChildren(true) != (Object)null; } private static List FindNearby(Vector3 center) { //IL_0019: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) List list = new List(); try { int num = Mathf.CeilToInt(Radius / 64f) + 1; Vector2i zone = ZoneSystem.GetZone(center); MethodInfo method = typeof(ZDOMan).GetMethod("FindSectorObjects", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[5] { typeof(Vector2i), typeof(int), typeof(int), typeof(List), typeof(List) }, null); if (method != null) { method.Invoke(ZDOMan.instance, new object[5] { zone, num, 0, list, null }); } else { typeof(ZDOMan).GetMethod("FindSectorObjects", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[3] { typeof(Vector2i), typeof(int), typeof(List) }, null)?.Invoke(ZDOMan.instance, new object[3] { zone, num, list }); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Bossbereich-ZDO-Suche fehlgeschlagen: " + ex.Message)); } } return (from zdo in list where zdo != null && zdo.IsValid() group zdo by zdo.m_uid into @group select @group.First()).ToList(); } private static bool AnyPlayerNear(Vector3 center, float radius) { //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) float squared = radius * radius; return Player.GetAllPlayers().Any(delegate(Player player) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return false; } Vector3 val = ((Component)player).transform.position - center; val.y = 0f; return ((Vector3)(ref val)).sqrMagnitude <= squared; }); } private static void EnsureLoaded() { if (_loaded) { return; } _loaded = true; try { if (File.Exists(StorePath)) { BossArenaResetStore bossArenaResetStore = JsonUtility.FromJson(File.ReadAllText(StorePath)); Pending.AddRange((bossArenaResetStore?.pending ?? new BossArenaResetRecord[0]).Where((BossArenaResetRecord record) => record != null && !string.IsNullOrWhiteSpace(record.id))); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Bossbereich-Resetliste konnte nicht geladen werden: " + ex.Message)); } } } private static void Save() { try { Directory.CreateDirectory(Path.GetDirectoryName(StorePath)); string text = StorePath + ".tmp"; File.WriteAllText(text, JsonUtility.ToJson((object)new BossArenaResetStore { pending = Pending.ToArray() }, true)); if (File.Exists(StorePath)) { File.Delete(StorePath); } File.Move(text, StorePath); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Bossbereich-Resetliste konnte nicht gespeichert werden: " + ex.Message)); } } } } internal sealed class BuilderCollectorGoalFeature : MonoBehaviour { private sealed class StabilityState { internal float LastObserved; internal float ActiveSeconds; internal bool WasValid; internal bool Initialized; } private static Plugin _plugin; private static readonly Dictionary Stability = new Dictionary(); private static readonly Dictionary> OutpostBiomes = new Dictionary>(); private static readonly Dictionary Counts = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> Sets = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> Roads = new Dictionary>(); private static readonly HashSet Sent = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly HashSet PortalViolationZones = new HashSet(StringComparer.OrdinalIgnoreCase); private float _nextScan; private static readonly HashSet RareMaterials = new HashSet(new string[16] { "surtlingcore", "chain", "crystal", "fenrishairbundle", "fenrisclaw", "softtissue", "blackcore", "sealbreakerfragment", "flametalnew", "flametalore", "queenbee", "dragontear", "yagluthdrop", "bonemasswishbone", "mandible", "bilebag" }.Select(Plugin.NormalizeKey), StringComparer.OrdinalIgnoreCase); internal static void Initialize(Plugin plugin) { _plugin = plugin; ((Component)plugin).gameObject.AddComponent(); } private void Update() { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && !(Time.realtimeSinceStartup < _nextScan)) { _nextScan = Time.realtimeSinceStartup + 10f; Piece[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Piece guardian in array) { ScanSupplyZone(guardian); ScanZone(guardian); } ScanRoadNetworks(); } } internal static void Observe(string type, Player actor, string item, string biome, Dictionary data) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_03bd: Unknown result type (might be due to invalid IL or missing references) //IL_03c2: Unknown result type (might be due to invalid IL or missing references) //IL_03f6: Unknown result type (might be due to invalid IL or missing references) //IL_03fb: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019c: 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) //IL_0446: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_plugin == (Object)null || (Object)(object)actor == (Object)null) { return; } int n = Amount(data); if (type == "portal_violation") { Piece val = FindGuardian(EventPosition(data, ((Component)actor).transform.position), "builder", "neutral"); if ((Object)(object)val != (Object)null) { string text = Zone(val); PortalViolationZones.Add(text); string key = OwnerId(val) + ":portal_hub_clean:" + text; if (Stability.TryGetValue(key, out var value)) { value.ActiveSeconds = 0f; value.WasValid = false; } Progress(OwnerId(val), actor, "builder", "portal_hub_clean", text, "reset", 0, "portal_violation:" + text + ":" + DateTime.UtcNow.Ticks); } } if (type == "build_completed" && IsRoad(item)) { Piece val2 = FindGuardian(EventPosition(data, ((Component)actor).transform.position), "builder", "neutral"); long num = OwnerId(val2); if (num == 0L) { num = actor.GetPlayerID(); } if (!Roads.TryGetValue(num, out var value2)) { value2 = (Roads[num] = new List()); } Vector3 pos = EventPosition(data, ((Component)actor).transform.position); if (!value2.Any((Vector3 p) => Horizontal(p, pos) < 8f)) { value2.Add(pos); } float num2 = (RoadConnectsInfrastructure(value2, num) ? ConnectedRoadLength(value2, 12f) : 0f); string text2 = (((Object)(object)val2 != (Object)null) ? Zone(val2) : ("player:" + num)); Progress(num, actor, "builder", "road_network", text2, "set", Mathf.RoundToInt(num2), "road:" + text2 + ":" + value2.Count); if (num2 >= 300f) { Award(num, actor, "builder", "road_network", "global", pos); } } if (type == "craft_completed") { if (IsGear(item)) { string text3 = GearTier(item); if (text3 != "unknown") { AddSet(actor, "gear:" + text3, item); Progress(actor.GetPlayerID(), actor, "collector", "gear_set", text3 + ":" + item, "confirm", 1, "gear:" + actor.GetPlayerID() + ":" + text3 + ":" + item); if (Set(actor, "gear:" + text3).Count >= 3 && Set(actor, "gear:" + text3).Any(IsWeapon) && Set(actor, "gear:" + text3).Any(IsArmor)) { Award(actor, "collector", "gear_set", text3); } } } Piece val3 = FindGuardian(EventPosition(data, ((Component)actor).transform.position), "builder", "neutral"); if ((Object)(object)val3 != (Object)null) { CraftingStation val4 = NearestStation(EventPosition(data, ((Component)actor).transform.position), val3); if ((Object)(object)val4 != (Object)null && CountStationUpgrades(val4) >= 2) { string scope = Plugin.NormalizeKey(((Object)val4).name); Award(OwnerId(val3), actor, "collector", "crafting_unlock", scope, ((Component)val4).transform.position); } } } if ((type == "item_pickup" || type == "item_drop_spawned") && IsRare(item)) { AddSet(actor, "raretypes", item); Add(actor, "rareamount", n); if (HasKillSource(data)) { Award(actor, "collector", "rare_drop", item); } } if (!(type == "trophy_found")) { return; } string text4 = Value(data, "trophy", item); string text5 = TrophyBiome(text4); if (!string.IsNullOrEmpty(text5)) { AddSet(actor, "trophies:" + text5, text4); Progress(actor.GetPlayerID(), actor, "collector", "full_trophy_set", text5 + ":" + text4, "confirm", 1, "trophy:" + actor.GetPlayerID() + ":" + text5 + ":" + text4); if (TrophySetComplete(text5, Set(actor, "trophies:" + text5))) { Award(actor, "collector", "full_trophy_set", text5); } } } private static void ScanZone(Piece guardian) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_0386: Unknown result type (might be due to invalid IL or missing references) //IL_03da: Unknown result type (might be due to invalid IL or missing references) //IL_0435: Unknown result type (might be due to invalid IL or missing references) //IL_0557: Unknown result type (might be due to invalid IL or missing references) string text = (((Object)(object)guardian != (Object)null) ? GuardianStoneProtectionFeature.DetectGuardianType(((Component)guardian).gameObject) : ""); if (text != "builder" && text != "neutral") { return; } long ownerId = OwnerId(guardian); if (ownerId == 0L) { return; } Player val = ReportingPlayer(ownerId); if ((Object)(object)val == (Object)null) { return; } float radius = GuardianZoneAccessFeature.Radius(guardian); Vector3 center = ((Component)guardian).transform.position; string text2 = Zone(guardian); Piece[] pieces = (from p in Object.FindObjectsByType((FindObjectsSortMode)0) where (Object)(object)p != (Object)null && Horizontal(((Component)p).transform.position, center) <= radius select p).ToArray(); pieces.Select((Piece p) => Plugin.NormalizeKey(((Object)p).name)).ToArray(); Container[] array = (from c in Object.FindObjectsByType((FindObjectsSortMode)0) where (Object)(object)c != (Object)null && Horizontal(((Component)c).transform.position, center) <= radius select c).ToArray(); int num = array.Count((Container c) => c.GetInventory() != null && c.GetInventory().NrOfItemsIncludingStacks() > 0); int num2 = Object.FindObjectsByType((FindObjectsSortMode)0).Count((CraftingStation x) => (Object)(object)x != (Object)null && Horizontal(((Component)x).transform.position, center) <= radius); int num3 = pieces.Length; bool flag = Object.FindObjectsByType((FindObjectsSortMode)0).Any((CraftingStation x) => (Object)(object)x != (Object)null && Horizontal(((Component)x).transform.position, center) <= radius && Plugin.NormalizeKey(((Object)x).name).Contains("workbench")); bool flag2 = Object.FindObjectsByType((FindObjectsSortMode)0).Any((Bed x) => (Object)(object)x != (Object)null && Horizontal(((Component)x).transform.position, center) <= radius && BedReady(x) && BedOwnerId(x) == ownerId); bool flag3 = Object.FindObjectsByType((FindObjectsSortMode)0).Any((Fireplace x) => (Object)(object)x != (Object)null && Horizontal(((Component)x).transform.position, center) <= radius && FireActive(x)); bool flag4 = Object.FindObjectsByType((FindObjectsSortMode)0).Any((CookingStation x) => (Object)(object)x != (Object)null && Horizontal(((Component)x).transform.position, center) <= radius); bool flag5 = flag && flag2 && flag3 && flag4 && num >= 1; Timed(ownerId, val, "safe_base", text2, flag5, 600f, "builder", center); Timed(ownerId, val, "comfort_base", text2, flag5 && num2 >= 3 && num >= 4 && num3 >= 30 && ComfortValue(pieces) >= 12, 600f, "builder", center); string text3 = Plugin.NormalizeKey(Plugin.CurrentBiome(center)); if (Timed(valid: Dangerous(text3) && flag && flag2 && flag3 && num >= 1 && num3 >= 20, ownerId: ownerId, reporting: val, goal: "biome_outpost", scope: text2, seconds: 1800f, style: "builder", position: center)) { if (!OutpostBiomes.TryGetValue(ownerId, out var value)) { value = (OutpostBiomes[ownerId] = new HashSet()); } value.Add(text3); Progress(ownerId, val, "builder", "reset_safe_zone", text3, "confirm", 1, "outpost:" + text2); if (value.Count >= 2) { Award(ownerId, val, "builder", "reset_safe_zone", "global", center); } } TeleportWorld[] array2 = (from x in Object.FindObjectsByType((FindObjectsSortMode)0) where (Object)(object)x != (Object)null && Horizontal(((Component)x).transform.position, center) <= radius select x).ToArray(); Timed(ownerId, val, "portal_hub_clean", text2, !PortalViolationZones.Contains(text2) && array2.Length == 1 && PortalConnected(array2[0]) && flag && flag3 && num >= 1, 1800f, "builder", center); Timed(valid: Object.FindObjectsByType((FindObjectsSortMode)0).Any((Ship s) => (Object)(object)s != (Object)null && Horizontal(((Component)s).transform.position, center) <= 30f && pieces.Any((Piece p) => (Object)(object)((Component)p).GetComponent() == (Object)null && Horizontal(((Component)p).transform.position, ((Component)s).transform.position) <= 8f)) && flag && num >= 1 && num3 >= 20, ownerId: ownerId, reporting: val, goal: "harbor_ship", scope: text2, seconds: 300f, style: "builder", position: center); int num4 = array.Count(IsOrganized); Timed(ownerId, val, "storage_system", text2, num >= 6 && num4 >= 4 && num2 >= 3, 600f, "builder", center); int num5 = 0; HashSet hashSet2 = new HashSet(); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); Container[] array3 = array; for (int num6 = 0; num6 < array3.Length; num6++) { Inventory inventory = array3[num6].GetInventory(); foreach (ItemData item in ((inventory != null) ? inventory.GetAllItems() : null) ?? new List()) { string text4 = ItemName(item); if (IsRareMaterial(text4)) { num5 += Math.Max(1, item.m_stack); hashSet2.Add(text4); } string text5 = MetalIngotTier(text4); if (!string.IsNullOrEmpty(text5)) { dictionary[text5] = (dictionary.ContainsKey(text5) ? dictionary[text5] : 0) + Math.Max(1, item.m_stack); } } } Timed(ownerId, val, "rare_material_stockpile", text2, num5 >= 30 && hashSet2.Count >= 4, 600f, "collector", center); string[] array4 = new string[7] { "copper", "tin", "bronze", "iron", "silver", "blackmetal", "flametal" }; foreach (string text6 in array4) { StableInventoryCount(ownerId, val, "collector", "metal_tier", text6 + "@" + text2, dictionary.ContainsKey(text6) ? dictionary[text6] : 0, 10, 600f); } } private static void ScanSupplyZone(Piece guardian) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) string text = (((Object)(object)guardian != (Object)null) ? GuardianStoneProtectionFeature.DetectGuardianType(((Component)guardian).gameObject) : ""); if (text != "farmer" && text != "neutral") { return; } long num = OwnerId(guardian); Player val = ReportingPlayer(num); if (num == 0L || (Object)(object)val == (Object)null) { return; } float radius = GuardianZoneAccessFeature.Radius(guardian); Vector3 center = ((Component)guardian).transform.position; string scope = Zone(guardian); Container[] array = (from c in Object.FindObjectsByType((FindObjectsSortMode)0) where (Object)(object)c != (Object)null && Horizontal(((Component)c).transform.position, center) <= radius select c).ToArray(); int num2 = 0; HashSet hashSet = new HashSet(); int num3 = 0; HashSet hashSet2 = new HashSet(); Container[] array2 = array; for (int num4 = 0; num4 < array2.Length; num4++) { Inventory inventory = array2[num4].GetInventory(); foreach (ItemData item in ((inventory != null) ? inventory.GetAllItems() : null) ?? new List()) { string text2 = ItemName(item); if (IsSeed(text2)) { num2 += Math.Max(1, item.m_stack); hashSet.Add(text2); } if (IsFinishedMead(text2)) { num3 += Math.Max(1, item.m_stack); hashSet2.Add(text2); } } } Timed(num, val, "seed_stockpile", scope, num2 >= 30 && hashSet.Count >= 3, 600f, "farmer", center); Timed(num, val, "mead_supply", scope, num3 >= 12 && hashSet2.Count >= 2, 600f, "farmer", center); } private static void ScanRoadNetworks() { foreach (IGrouping item in from p in Object.FindObjectsByType((FindObjectsSortMode)0) where (Object)(object)p != (Object)null && p.GetCreator() != 0L && IsRoad(Plugin.NormalizeKey(((Object)p).name)) group p by p.GetCreator()) { long key = item.Key; Player val = ReportingPlayer(key); if (!((Object)(object)val == (Object)null)) { List list = item.Select((Piece p) => ((Component)p).transform.position).ToList(); float num = (RoadConnectsInfrastructure(list, key) ? ConnectedRoadLength(list, 12f) : 0f); Progress(key, val, "builder", "road_network", "player:" + key, "set", Mathf.RoundToInt(num), "roadscan:" + key + ":" + list.Count + ":" + Mathf.RoundToInt(num)); } } } private static bool Timed(long ownerId, Player reporting, string goal, string scope, bool valid, float seconds, string style, Vector3 position) { //IL_015d: Unknown result type (might be due to invalid IL or missing references) string text = ownerId + ":" + goal + ":" + scope; float realtimeSinceStartup = Time.realtimeSinceStartup; if (!Stability.TryGetValue(text, out var value)) { Dictionary stability = Stability; StabilityState obj = new StabilityState { LastObserved = realtimeSinceStartup }; value = obj; stability[text] = obj; } float num = realtimeSinceStartup - value.LastObserved; value.LastObserved = realtimeSinceStartup; if (!valid) { value.ActiveSeconds = 0f; if (!value.Initialized || value.WasValid) { Progress(ownerId, reporting, style, goal, scope, "reset", 0, "invalid:" + text + ":" + Mathf.FloorToInt(realtimeSinceStartup / 10f)); } value.Initialized = true; value.WasValid = false; return false; } if (num > 0f && num <= 25f) { value.ActiveSeconds += num; } value.WasValid = true; value.Initialized = true; Progress(ownerId, reporting, style, goal, scope, "add", Mathf.RoundToInt(Mathf.Clamp(num, 0f, 25f)), "tick:" + text + ":" + Mathf.FloorToInt(realtimeSinceStartup / 10f)); if (value.ActiveSeconds < seconds) { return false; } Award(ownerId, reporting, style, goal, "global", position); return true; } private static void StableInventoryCount(long ownerId, Player reporting, string style, string goal, string scope, int amount, int required, float seconds) { string text = ownerId + ":" + goal + ":" + scope + ":inventory"; float realtimeSinceStartup = Time.realtimeSinceStartup; if (!Stability.TryGetValue(text, out var value)) { Dictionary stability = Stability; StabilityState obj = new StabilityState { LastObserved = realtimeSinceStartup }; value = obj; stability[text] = obj; } float num = realtimeSinceStartup - value.LastObserved; value.LastObserved = realtimeSinceStartup; if (amount < required) { value.ActiveSeconds = 0f; if (!value.Initialized || value.WasValid) { Progress(ownerId, reporting, style, goal, scope, "reset", 0, "inventory-invalid:" + text + ":" + Mathf.FloorToInt(realtimeSinceStartup / 10f)); } value.Initialized = true; value.WasValid = false; return; } if (num > 0f && num <= 25f) { value.ActiveSeconds += num; } value.Initialized = true; value.WasValid = true; if (value.ActiveSeconds >= seconds) { Progress(ownerId, reporting, style, goal, scope, "set", amount, "inventory-confirm:" + text + ":" + amount); } } private static void Award(Player player, string style, string goal, string scope) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player != (Object)null) { Award(player.GetPlayerID(), player, style, goal, scope, ((Component)player).transform.position); } } private static void Award(long ownerId, Player reporting, string style, string goal, string scope, Vector3 position) { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)reporting == (Object)null) && ownerId != 0L && ScoringEmissionAllowed() && Sent.Add(ownerId + ":" + goal + ":" + scope)) { _plugin.SendEvent("playstyle_goal", reporting, new Dictionary { { "playstyle", style }, { "goal", goal }, { "scope", scope }, { "creditedPlayerId", ownerId.ToString() }, { "attributionMethod", "server_guardian_zdo_2_9_27" }, { "position", Plugin.SerializeVector(position) }, { "biome", Plugin.CurrentBiome(position) } }); } } private static void Progress(long ownerId, Player reporting, string style, string goal, string scope, string operation, int value, string evidence) { if (!((Object)(object)reporting == (Object)null) && ownerId != 0L && ScoringEmissionAllowed()) { _plugin.SendEvent("goal_progress", reporting, new Dictionary { { "playstyle", style }, { "goal", goal }, { "scope", scope }, { "operation", operation }, { "value", value }, { "evidenceId", evidence }, { "creditedPlayerId", ownerId.ToString() }, { "attributionMethod", "server_guardian_progress_2_9_27" } }); } } private static Piece FindGuardian(Vector3 pos, params string[] allowed) { //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) return (from p in Object.FindObjectsByType((FindObjectsSortMode)0) where allowed.Contains(GuardianStoneProtectionFeature.DetectGuardianType(((Component)p).gameObject)) && Horizontal(((Component)p).transform.position, pos) <= GuardianZoneAccessFeature.Radius(p) orderby Horizontal(((Component)p).transform.position, pos) select p).FirstOrDefault(); } private static bool ScoringEmissionAllowed() { if (!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer()) { return CharacterAdmissionFeature.ChallengeScoringAllowed; } return true; } private static long OwnerId(Piece p) { if ((Object)(object)p == (Object)null) { return 0L; } try { long result = p.GetCreator(); if (result != 0L) { return result; } ZNetView component = ((Component)p).GetComponent(); object s; if (component == null) { s = null; } else { ZDO zDO = component.GetZDO(); s = ((zDO != null) ? zDO.GetString("ChallengeHub.Guardian.OwnerId", "0") : null); } long.TryParse((string?)s, out result); return result; } catch { return 0L; } } private static Player Owner(Piece p) { long id = OwnerId(p); return ((IEnumerable)Player.GetAllPlayers()).FirstOrDefault((Func)((Player x) => (Object)(object)x != (Object)null && x.GetPlayerID() == id)); } private static Player ReportingPlayer(long ownerId) { return ((IEnumerable)Player.GetAllPlayers()).FirstOrDefault((Func)((Player x) => (Object)(object)x != (Object)null && x.GetPlayerID() == ownerId)) ?? Player.m_localPlayer ?? ((IEnumerable)Player.GetAllPlayers()).FirstOrDefault((Func)((Player x) => (Object)(object)x != (Object)null)); } private static string Zone(Piece p) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) return p.GetCreator() + ":" + Mathf.RoundToInt(((Component)p).transform.position.x / 10f) + ":" + Mathf.RoundToInt(((Component)p).transform.position.z / 10f); } private static float Horizontal(Vector3 a, Vector3 b) { //IL_0000: 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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) float num = a.x - b.x; float num2 = a.z - b.z; return Mathf.Sqrt(num * num + num2 * num2); } private static float ConnectedRoadLength(List points, float maxGap) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: 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) if (points == null || points.Count < 2) { return 0f; } float num = 0f; HashSet hashSet = new HashSet(); for (int i = 0; i < points.Count; i++) { if (hashSet.Contains(i)) { continue; } Queue queue = new Queue(); queue.Enqueue(i); hashSet.Add(i); float num2 = 0f; while (queue.Count > 0) { int index = queue.Dequeue(); int num3 = -1; float num4 = float.MaxValue; for (int j = 0; j < points.Count; j++) { if (!hashSet.Contains(j)) { float num5 = Horizontal(points[index], points[j]); if (num5 <= maxGap && num5 < num4) { num3 = j; num4 = num5; } } } if (num3 >= 0) { hashSet.Add(num3); queue.Enqueue(num3); num2 += num4; } for (int k = 0; k < points.Count; k++) { if (!hashSet.Contains(k)) { float num6 = Horizontal(points[index], points[k]); if (num6 <= maxGap) { hashSet.Add(k); queue.Enqueue(k); num2 += num6; } } } } num = Mathf.Max(num, num2); } return num; } private static int CountStations(Piece g) { float r = GuardianZoneAccessFeature.Radius(g); return Object.FindObjectsByType((FindObjectsSortMode)0).Count((Piece p) => Horizontal(((Component)p).transform.position, ((Component)g).transform.position) <= r && IsStation(Plugin.NormalizeKey(((Object)p).name))); } private static CraftingStation NearestStation(Vector3 pos, Piece guardian) { //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) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) float r = GuardianZoneAccessFeature.Radius(guardian); CraftingStation val = (from s in Object.FindObjectsByType((FindObjectsSortMode)0) where (Object)(object)s != (Object)null && Horizontal(((Component)s).transform.position, ((Component)guardian).transform.position) <= r orderby Horizontal(((Component)s).transform.position, pos) select s).FirstOrDefault(); if (!((Object)(object)val != (Object)null) || !(Horizontal(((Component)val).transform.position, pos) <= 8f)) { return null; } return val; } private static int CountStationUpgrades(CraftingStation station) { try { if ((Object)(object)station == (Object)null) { return 0; } MethodInfo methodInfo = AccessTools.Method(((object)station).GetType(), "GetLevel", (Type[])null, (Type[])null); return (methodInfo != null) ? Math.Max(0, Convert.ToInt32(methodInfo.Invoke(station, null)) - 1) : 0; } catch { return 0; } } private static string StationScope(Piece g) { float r = GuardianZoneAccessFeature.Radius(g); CraftingStation val = (from x in Object.FindObjectsByType((FindObjectsSortMode)0) where (Object)(object)x != (Object)null && Horizontal(((Component)x).transform.position, ((Component)g).transform.position) <= r orderby Horizontal(((Component)x).transform.position, ((Component)g).transform.position) select x).FirstOrDefault(); if (!((Object)(object)val != (Object)null)) { return Zone(g); } return Plugin.NormalizeKey(((Object)val).name); } private static bool PortalConnected(TeleportWorld portal) { try { ZNetView component = ((Component)portal).GetComponent(); object obj; if (component == null) { obj = null; } else { ZDO zDO = component.GetZDO(); obj = ((zDO != null) ? zDO.GetString("tag", "") : null); } if (obj == null) { obj = ""; } string tag = (string)obj; if (string.IsNullOrWhiteSpace(tag)) { return false; } return Object.FindObjectsByType((FindObjectsSortMode)0).Count(delegate(TeleportWorld p) { if ((Object)(object)p != (Object)null && (Object)(object)p != (Object)(object)portal) { ZNetView component2 = ((Component)p).GetComponent(); object obj3; if (component2 == null) { obj3 = null; } else { ZDO zDO2 = component2.GetZDO(); obj3 = ((zDO2 != null) ? zDO2.GetString("tag", "") : null); } if (obj3 == null) { obj3 = ""; } return (string?)obj3 == tag; } return false; }) >= 1; } catch { return false; } } private static bool IsOrganized(Container c) { object obj; if (c == null) { obj = null; } else { Inventory inventory = c.GetInventory(); obj = ((inventory != null) ? inventory.GetAllItems() : null); } List list = (List)obj; if (list == null || list.Count == 0) { return false; } float num = list.Sum((ItemData i) => Math.Max(1, i.m_stack)); float num2 = (from i in list group i by i.m_shared.m_itemType into g select g.Sum((ItemData i) => Math.Max(1, i.m_stack))).DefaultIfEmpty(0).Max(); if (num > 0f) { return num2 / num >= 0.7f; } return false; } private static bool BedReady(Bed bed) { //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_0059: 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_0063: Unknown result type (might be due to invalid IL or missing references) try { MethodInfo methodInfo = AccessTools.Method(((object)bed).GetType(), "GetOwner", (Type[])null, (Type[])null); long num; if (!(methodInfo != null)) { Piece component = ((Component)bed).GetComponent(); num = ((component != null) ? component.GetCreator() : 0); } else { num = Convert.ToInt64(methodInfo.Invoke(bed, null)); } if (num == 0L) { return false; } return Physics.Raycast(((Component)bed).transform.position + Vector3.up * 0.3f, Vector3.up, 3.5f, -1, (QueryTriggerInteraction)1); } catch { return false; } } private static long BedOwnerId(Bed bed) { try { MethodInfo methodInfo = AccessTools.Method(((object)bed).GetType(), "GetOwner", (Type[])null, (Type[])null); long result; if (!(methodInfo != null)) { Piece component = ((Component)bed).GetComponent(); result = ((component != null) ? component.GetCreator() : 0); } else { result = Convert.ToInt64(methodInfo.Invoke(bed, null)); } return result; } catch { return 0L; } } private static bool RoadConnectsInfrastructure(List points, long ownerId) { //IL_00ae: 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) if (points == null || points.Count < 2) { return false; } List list = (from p in (from p in Object.FindObjectsByType((FindObjectsSortMode)0) where (Object)(object)p != (Object)null && p.GetCreator() == ownerId select p).Where(delegate(Piece p) { string text = Plugin.NormalizeKey(((Object)p).name); return IsStation(text) || text.Contains("portal") || text.Contains("bed") || !string.IsNullOrWhiteSpace(GuardianStoneProtectionFeature.DetectGuardianType(((Component)p).gameObject)); }) select ((Component)p).transform.position into a where points.Any((Vector3 point) => Horizontal(point, a) <= 20f) select a).ToList(); for (int num = 0; num < list.Count; num++) { for (int num2 = num + 1; num2 < list.Count; num2++) { if (Horizontal(list[num], list[num2]) >= 100f) { return true; } } } return false; } private static bool FireActive(Fireplace fire) { try { MethodInfo methodInfo = AccessTools.Method(((object)fire).GetType(), "IsBurning", (Type[])null, (Type[])null); int result; if (!(methodInfo != null)) { ZNetView component = ((Component)fire).GetComponent(); if (component == null) { result = 0; } else { ZDO zDO = component.GetZDO(); result = ((((zDO != null) ? new float?(zDO.GetFloat("fuel", 0f)) : ((float?)null)) > 0f) ? 1 : 0); } } else { result = (Convert.ToBoolean(methodInfo.Invoke(fire, null)) ? 1 : 0); } return (byte)result != 0; } catch { return false; } } private static int ComfortValue(Piece[] pieces) { Dictionary dictionary = new Dictionary(); Piece[] array = (Piece[])(((object)pieces) ?? ((object)new Piece[0])); foreach (Piece val in array) { try { FieldInfo fieldInfo = AccessTools.Field(typeof(Piece), "m_comfort"); FieldInfo fieldInfo2 = AccessTools.Field(typeof(Piece), "m_comfortGroup"); int num = ((fieldInfo != null) ? Math.Max(0, Convert.ToInt32(fieldInfo.GetValue(val))) : 0); string key = ((fieldInfo2 != null) ? Convert.ToString(fieldInfo2.GetValue(val)) : ((Object)val).name); if (!dictionary.ContainsKey(key) || dictionary[key] < num) { dictionary[key] = num; } } catch { } } return dictionary.Values.Sum(); } private static bool IsStation(string n) { if (!n.Contains("workbench") && !n.Contains("forge") && !n.Contains("cauldron") && !n.Contains("artisan") && !n.Contains("blackforge") && !n.Contains("magetable")) { return n.Contains("stonecutter"); } return true; } private static bool IsRoad(string n) { n = Plugin.NormalizeKey(n); if (!n.Contains("pavedroad") && !n.Contains("path") && !n.Contains("roadsign") && !n.Contains("sign") && !n.Contains("standingtorch") && !n.Contains("woodtorch")) { return n.Contains("woodpole"); } return true; } private static bool Dangerous(string b) { if (!b.Contains("swamp") && !b.Contains("mountain") && !b.Contains("plain") && !b.Contains("mist")) { return b.Contains("ash"); } return true; } private static bool IsRareMaterial(string s) { s = Plugin.NormalizeKey(s).Replace("(clone)", ""); return RareMaterials.Contains(s); } private static bool IsRare(string s) { s = Plugin.NormalizeKey(s).Replace("(clone)", ""); if (!IsRareMaterial(s)) { return s.StartsWith("trophy"); } return true; } private static bool IsSeed(string s) { switch (s) { default: if (!s.Contains("seedcarrot") && !s.Contains("seedturnip")) { return s.Contains("seedonion"); } break; case "carrotseeds": case "turnipseeds": case "onionseeds": case "barley": case "flax": break; } return true; } private static bool IsFinishedMead(string s) { s = Plugin.NormalizeKey(s); if (s.StartsWith("mead") || s.StartsWith("potion") || s.Contains("wine")) { return !s.Contains("base"); } return false; } private static bool IsGear(string s) { if (!IsWeapon(s)) { return IsArmor(s); } return true; } private static bool IsWeapon(string s) { if (!s.Contains("sword") && !s.Contains("axe") && !s.Contains("bow") && !s.Contains("mace") && !s.Contains("spear") && !s.Contains("knife") && !s.Contains("pickaxe")) { return s.Contains("shield"); } return true; } private static bool IsArmor(string s) { if (!s.Contains("helmet") && !s.Contains("armor") && !s.Contains("chest") && !s.Contains("legs")) { return s.Contains("cape"); } return true; } private static string MetalTier(string s) { s = Plugin.NormalizeKey(s).Replace("$item_", "").Replace("(clone)", ""); string[] array = new string[7] { "bronze", "iron", "silver", "blackmetal", "flametal", "copper", "tin" }; foreach (string text in array) { if (s == text || s == text + "bar" || s == text + "scrap" || s == text + "ore") { return text; } } return ""; } private static string MetalIngotTier(string s) { s = Plugin.NormalizeKey(s).Replace("$item_", "").Replace("(clone)", ""); string[] array = new string[7] { "copper", "tin", "bronze", "iron", "silver", "blackmetal", "flametal" }; foreach (string text in array) { if (s == text || s == text + "bar") { return text; } } return ""; } private static string GearTier(string s) { s = Plugin.NormalizeKey(s); if (s.Contains("flametal") || s.Contains("asksvin") || s.Contains("ashlands")) { return "ashlands"; } if (s.Contains("carapace") || s.Contains("eitr") || s.Contains("blackforge")) { return "mistlands"; } if (s.Contains("blackmetal") || s.Contains("padded")) { return "blackmetal"; } if (s.Contains("silver") || s.Contains("wolf")) { return "silver"; } if (s.Contains("iron") || s.Contains("root")) { return "iron"; } if (s.Contains("bronze") || s.Contains("troll")) { return "bronze"; } if (s.Contains("copper") || s.Contains("leather") || s.Contains("flint")) { return "early"; } return "unknown"; } private static string TrophyBiome(string t) { t = Plugin.NormalizeKey(t); if (t.Contains("boar") || t.Contains("deer") || t.Contains("neck")) { return "meadows"; } if (t.Contains("greydwarf") || t.Contains("troll") || t.Contains("skeleton")) { return "blackforest"; } if (t.Contains("draugr") || t.Contains("blob") || t.Contains("wraith") || t.Contains("abomination")) { return "swamp"; } if (t.Contains("wolf") || t.Contains("drake") || t.Contains("fenring") || t.Contains("golem")) { return "mountain"; } if (t.Contains("fuling") || t.Contains("lox") || t.Contains("deathsquito")) { return "plains"; } if (t.Contains("seeker") || t.Contains("gjall") || t.Contains("hare")) { return "mistlands"; } if (t.Contains("charred") || t.Contains("asksvin") || t.Contains("morgen") || t.Contains("volture")) { return "ashlands"; } return ""; } private static bool TrophySetComplete(string biome, HashSet found) { return (biome switch { "mistlands" => new string[3][] { new string[1] { "seeker" }, new string[1] { "gjall" }, new string[1] { "hare" } }, "plains" => new string[3][] { new string[1] { "fuling" }, new string[1] { "lox" }, new string[1] { "deathsquito" } }, "mountain" => new string[3][] { new string[1] { "wolf" }, new string[1] { "drake" }, new string[1] { "fenring" } }, "swamp" => new string[3][] { new string[1] { "draugr" }, new string[1] { "blob" }, new string[1] { "wraith" } }, "blackforest" => new string[3][] { new string[1] { "greydwarf" }, new string[1] { "troll" }, new string[1] { "skeleton" } }, "meadows" => new string[3][] { new string[1] { "boar" }, new string[1] { "deer" }, new string[1] { "neck" } }, _ => new string[3][] { new string[1] { "charred" }, new string[1] { "asksvin" }, new string[1] { "volture" } }, }).All((string[] group) => found.Any((string item) => group.Any((string token) => item.Contains(token)))); } private static string ItemName(ItemData i) { return Plugin.NormalizeKey(((Object)(object)i?.m_dropPrefab != (Object)null) ? ((Object)i.m_dropPrefab).name : (i?.m_shared?.m_name ?? "")); } private static int ContainerCategories(Container[] cs) { HashSet hashSet = new HashSet(); for (int i = 0; i < cs.Length; i++) { Inventory inventory = cs[i].GetInventory(); foreach (ItemData item in ((inventory != null) ? inventory.GetAllItems() : null) ?? new List()) { hashSet.Add(((object)Unsafe.As(ref item.m_shared.m_itemType)/*cast due to .constrained prefix*/).ToString()); } } return hashSet.Count; } private static void Add(Player p, string k, int n) { Counts[p.GetPlayerID() + ":" + k] = Get(p, k) + Math.Max(1, n); } private static int Get(Player p, string k) { if (!Counts.TryGetValue(p.GetPlayerID() + ":" + k, out var value)) { return 0; } return value; } private static void AddSet(Player p, string k, string v) { Set(p, k).Add(v); } private static HashSet Set(Player p, string k) { string key = p.GetPlayerID() + ":" + k; if (!Sets.TryGetValue(key, out var value)) { value = (Sets[key] = new HashSet()); } return value; } private static int Amount(Dictionary d) { if (d != null && (d.TryGetValue("quantity", out var value) || d.TryGetValue("amount", out value)) && int.TryParse(Convert.ToString(value), out var result)) { return Math.Max(1, result); } return 1; } private static string Value(Dictionary d, string k, string f) { if (d == null || !d.TryGetValue(k, out var value)) { return f; } return Plugin.NormalizeKey(Convert.ToString(value)); } private static bool HasKillSource(Dictionary d) { if (string.IsNullOrEmpty(Value(d, "dropSourceEventId", ""))) { return Value(d, "attributionMethod", "").Contains("last_hit"); } return true; } private static Vector3 EventPosition(Dictionary d, Vector3 f) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0057: 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_0058: Unknown result type (might be due to invalid IL or missing references) try { if (d == null || !d.TryGetValue("position", out var value)) { return f; } return (Vector3)((!(value is IDictionary dictionary)) ? f : new Vector3(Convert.ToSingle(dictionary["x"]), Convert.ToSingle(dictionary["y"]), Convert.ToSingle(dictionary["z"]))); } catch { return f; } } } internal static class CartographyMapModeFeature { private const float PendingGridSize = 12f; private const float CartographyRevealRadius = 100f; private const int MaxPendingPoints = 800; private const int PendingPayloadVersion = 1; private const float DeferredTrailSampleSeconds = 3f; private const float DeferredTrailMinDistance = 12f; private const string PlayerDataKeyPrefix = "ChallengeHub.Cartography.Pending.v1."; private static readonly Queue PendingExplorations = new Queue(); private static readonly HashSet PendingCells = new HashSet(); private static float _lastPendingLogAt; private static MethodInfo _exploreVectorMethod; private static MethodInfo _exploreFloatMethod; private static bool _exploreMethodsResolved; private static readonly object[] ExploreVectorInvokeArgs = new object[2]; private static readonly object[] ExploreFloatInvokeArgs = new object[3]; private static bool _samplerStarted; private static bool _hasLastSample; private static Vector3 _lastSamplePosition; private static int _lastLoadedPlayerInstanceId; private static string _lastLoadedWorldKey = string.Empty; internal static int PendingExplorationCount => PendingExplorations.Count; internal static void Initialize(Plugin owner) { if (!((Object)(object)owner == (Object)null) && !_samplerStarted) { _samplerStarted = true; ((MonoBehaviour)owner).StartCoroutine(DeferredTrailSamplingLoop()); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Kartografie 2.2.31 initialisiert: Spieler-Persistenz, Grabstein-Puffer und kartentisch-exklusiver Karten-Commit aktiv."); } } } private static IEnumerator DeferredTrailSamplingLoop() { WaitForSeconds wait = new WaitForSeconds(3f); while (true) { yield return wait; if (!ShouldBufferExploration()) { _hasLastSample = false; continue; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !((Character)localPlayer).IsOwner()) { continue; } TryRestorePendingFromPlayer(localPlayer, "Spieler geladen"); Vector3 position = ((Component)localPlayer).transform.position; if (_hasLastSample) { float num = position.x - _lastSamplePosition.x; float num2 = position.z - _lastSamplePosition.z; if (num * num + num2 * num2 < 144f) { continue; } } _lastSamplePosition = position; _hasLastSample = true; AddPendingPosition(position); } } internal static bool ShouldBufferExploration() { if (Plugin.IsCartographyOnlyMapModeEnabled() && !Plugin.MapRevealFogWhileWalkingEnabled()) { return Plugin.MapRevealFogAtCartographyTableEnabled(); } return false; } private static bool AddPendingPosition(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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) long item = BuildCellKey(position); if (!PendingCells.Add(item)) { return false; } if (PendingExplorations.Count >= 800) { Vector3 position2 = PendingExplorations.Dequeue(); PendingCells.Remove(BuildCellKey(position2)); } PendingExplorations.Enqueue(position); float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup - _lastPendingLogAt >= 15f) { _lastPendingLogAt = realtimeSinceStartup; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Kartografie: " + PendingExplorations.Count + " Erkundungspunkte warten auf den Kartentisch.")); } } return true; } private static int AddPendingPositions(IEnumerable points) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) if (points == null) { return 0; } int num = 0; foreach (Vector3 point in points) { if (AddPendingPosition(point)) { num++; } } return num; } private static long BuildCellKey(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.FloorToInt(position.x / 12f); int num2 = Mathf.FloorToInt(position.z / 12f); return ((long)num << 32) ^ (uint)num2; } internal static void ApplyPendingExplorationAtCartographyTable() { FlushPendingExplorationAtCartographyTable("Kartentisch gelesen"); } private static int FlushPendingExplorationAtCartographyTable(string reason) { //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_007f: Unknown result type (might be due to invalid IL or missing references) if (!ShouldBufferExploration() || (Object)(object)Minimap.instance == (Object)null) { return 0; } Player localPlayer = Player.m_localPlayer; MergeStoredPlayerPoints(localPlayer); if (PendingExplorations.Count == 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)((string.IsNullOrWhiteSpace(reason) ? "Kartografie" : reason) + ": keine neuen gepufferten Erkundungsdaten vorhanden.")); } return 0; } List list = new List(PendingExplorations); int num = 0; try { foreach (Vector3 item in list) { if (!InvokeVanillaExplore(Minimap.instance, item, 100f)) { throw new MissingMethodException("Keine kompatible private Minimap.Explore-Ueberladung gefunden. " + DescribeExploreMethods(((object)Minimap.instance).GetType())); } num++; } RefreshExploreMap(Minimap.instance); SaveMapData(Minimap.instance); ClearRamBuffer(); ClearPlayerStorage(localPlayer); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)((string.IsNullOrWhiteSpace(reason) ? "Kartografie" : reason) + ": " + num + " gepufferte Erkundungspunkte wurden dauerhaft uebertragen.")); } return num; } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)((string.IsNullOrWhiteSpace(reason) ? "Kartografie" : reason) + ": gepufferte Erkundung konnte nicht vollstaendig uebertragen werden: " + ex.Message)); } return 0; } } internal static int TryRestorePendingFromPlayer(Player player, string reason) { if (!ShouldBufferExploration() || !IsLocalOwnedPlayer(player)) { return 0; } string text = ResolvePlayerStorageKey(); int instanceID = ((Object)player).GetInstanceID(); if (_lastLoadedPlayerInstanceId == instanceID && string.Equals(_lastLoadedWorldKey, text, StringComparison.Ordinal)) { return 0; } string text2 = ReadPlayerCustomData(player, text); if (string.IsNullOrWhiteSpace(text2)) { return 0; } List list = DeserializePointsFromString(text2); if (list.Count == 0) { return 0; } int num = AddPendingPositions(list); _lastLoadedPlayerInstanceId = instanceID; _lastLoadedWorldKey = text; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)((string.IsNullOrWhiteSpace(reason) ? "Spieler geladen" : reason) + ": " + list.Count + " gespeicherte Erkundungspunkte wieder in den RAM-Puffer uebernommen" + ((num != list.Count) ? (" (davon " + num + " neu)") : string.Empty) + ".")); } return num; } internal static int PersistPendingToPlayer(string reason, bool clearRam) { Player localPlayer = Player.m_localPlayer; if (!ShouldBufferExploration() || !IsLocalOwnedPlayer(localPlayer)) { return 0; } List list = CollectCombinedPendingPoints(localPlayer); if (list.Count > 0) { string key = ResolvePlayerStorageKey(); string text = SerializePointsToString(list); bool flag = !string.Equals(ReadPlayerCustomData(localPlayer, key), text, StringComparison.Ordinal); if (!WritePlayerCustomData(localPlayer, key, text)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Kartografie: Spieler-CustomData konnte nicht geschrieben werden."); } return 0; } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)((string.IsNullOrWhiteSpace(reason) ? "Charakterspeicherung" : reason) + ": " + list.Count + " gepufferte Erkundungspunkte im Spieler gespeichert.")); } if (flag) { CharacterAdmissionFeature.RequestCheckpointSoon(); } } if (clearRam) { ClearRamBuffer(); } return list.Count; } internal static void PersistForProfileSave() { PersistPendingToPlayer("Charakterspeicherung", clearRam: false); } internal static byte[] TakePendingForTombstone(Player player, out int count) { count = 0; if (!ShouldBufferExploration() || !IsLocalOwnedPlayer(player)) { return Array.Empty(); } List list = CollectCombinedPendingPoints(player); count = list.Count; if (count <= 0) { ClearRamBuffer(); ClearPlayerStorage(player); return Array.Empty(); } byte[] result = SerializePoints(list); if (!ClearPlayerStorage(player)) { count = 0; ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)"Tod: Spieler-Puffer konnte nicht atomar geleert werden. Kein Grabstein-Punktpuffer erzeugt, um eine doppelte Kopie zu verhindern."); } return Array.Empty(); } ClearRamBuffer(); _lastLoadedPlayerInstanceId = 0; _lastLoadedWorldKey = string.Empty; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Tod: " + count + " gepufferte Erkundungspunkte fuer den Grabstein entnommen; Spieler-Puffer geleert.")); } return result; } internal static int ImportPendingFromTombstone(byte[] payload) { return AddPendingPositions(DeserializePoints(payload)); } internal static int CountSerializedPoints(byte[] payload) { return DeserializePoints(payload).Count; } private static List CollectCombinedPendingPoints(Player player) { //IL_0015: 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_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: 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) //IL_0069: 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_0073: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = new Dictionary(); foreach (Vector3 pendingExploration in PendingExplorations) { dictionary[BuildCellKey(pendingExploration)] = pendingExploration; } if ((Object)(object)player != (Object)null) { foreach (Vector3 item in DeserializePointsFromString(ReadPlayerCustomData(player, ResolvePlayerStorageKey()))) { dictionary[BuildCellKey(item)] = item; } } return dictionary.Values.Take(800).ToList(); } private static int MergeStoredPlayerPoints(Player player) { if (!IsLocalOwnedPlayer(player)) { return 0; } return AddPendingPositions(DeserializePointsFromString(ReadPlayerCustomData(player, ResolvePlayerStorageKey()))); } private static void ClearRamBuffer() { PendingExplorations.Clear(); PendingCells.Clear(); _hasLastSample = false; } private static bool ClearPlayerStorage(Player player) { if (!IsLocalOwnedPlayer(player)) { return false; } string key = ResolvePlayerStorageKey(); if (!WritePlayerCustomData(player, key, string.Empty)) { return false; } return string.IsNullOrWhiteSpace(ReadPlayerCustomData(player, key)); } private static bool IsLocalOwnedPlayer(Player player) { if ((Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer) { return ((Character)player).IsOwner(); } return false; } private static byte[] SerializePoints(IList points) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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) if (points == null || points.Count == 0) { return Array.Empty(); } using MemoryStream memoryStream = new MemoryStream(); using BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true); binaryWriter.Write(1); binaryWriter.Write(Mathf.Min(points.Count, 800)); for (int i = 0; i < points.Count && i < 800; i++) { binaryWriter.Write(points[i].x); binaryWriter.Write(points[i].y); binaryWriter.Write(points[i].z); } binaryWriter.Flush(); return memoryStream.ToArray(); } private static string SerializePointsToString(IList points) { byte[] array = SerializePoints(points); if (array.Length != 0) { return Convert.ToBase64String(array); } return string.Empty; } private static List DeserializePointsFromString(string encoded) { if (string.IsNullOrWhiteSpace(encoded)) { return new List(); } try { return DeserializePoints(Convert.FromBase64String(encoded)); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Kartografie: gespeicherter Spieler-Puffer ist ungueltig: " + ex.Message)); } return new List(); } } private static List DeserializePoints(byte[] payload) { //IL_007d: Unknown result type (might be due to invalid IL or missing references) List list = new List(); if (payload == null || payload.Length < 8) { return list; } try { using MemoryStream memoryStream = new MemoryStream(payload, writable: false); using BinaryReader binaryReader = new BinaryReader(memoryStream, Encoding.UTF8, leaveOpen: true); if (binaryReader.ReadInt32() != 1) { return list; } int num = binaryReader.ReadInt32(); if (num < 0 || num > 800) { return list; } for (int i = 0; i < num; i++) { if (memoryStream.Length - memoryStream.Position < 12) { break; } list.Add(new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle())); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Kartografie: Punkt-Puffer konnte nicht gelesen werden: " + ex.Message)); } } return list; } internal static string CurrentWorldStorageToken() { return ResolvePlayerStorageKey(); } private static string ResolvePlayerStorageKey() { string text = ResolveWorldIdentity(); StringBuilder stringBuilder = new StringBuilder(text.Length); string text2 = text; foreach (char c in text2) { if (char.IsLetterOrDigit(c) || c == '-' || c == '_') { stringBuilder.Append(c); } } if (stringBuilder.Length == 0) { stringBuilder.Append("default"); } return "ChallengeHub.Cartography.Pending.v1." + stringBuilder; } private static string ResolveWorldIdentity() { object instance = ZNet.instance; if (instance != null) { string[] array = new string[2] { "GetWorldUID", "GetWorldUid" }; foreach (string name in array) { try { object obj = instance.GetType().GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null)?.Invoke(instance, null); string text = ((obj != null) ? Convert.ToString(obj, CultureInfo.InvariantCulture) : string.Empty); if (!string.IsNullOrWhiteSpace(text) && !string.Equals(text, "0", StringComparison.Ordinal)) { return text; } } catch { } } try { object obj3 = instance.GetType().GetField("m_world", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance); if (obj3 != null) { array = new string[2] { "m_uid", "m_name" }; foreach (string name2 in array) { object obj4 = obj3.GetType().GetField(name2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj3); string text2 = ((obj4 != null) ? Convert.ToString(obj4, CultureInfo.InvariantCulture) : string.Empty); if (!string.IsNullOrWhiteSpace(text2) && !string.Equals(text2, "0", StringComparison.Ordinal)) { return text2; } } } } catch { } } try { if (Plugin.WorldName != null && !string.IsNullOrWhiteSpace(Plugin.WorldName.Value)) { return Plugin.WorldName.Value; } } catch { } return "default"; } private static string ReadPlayerCustomData(Player player, string key) { if ((Object)(object)player == (Object)null || string.IsNullOrWhiteSpace(key)) { return string.Empty; } try { MethodInfo method = ((object)player).GetType().GetMethod("GetCustomData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null); if (method != null) { return Convert.ToString(method.Invoke(player, new object[1] { key })) ?? string.Empty; } } catch { } try { if (((object)player).GetType().GetField("m_customData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(player) is IDictionary dictionary && dictionary.TryGetValue(key, out var value)) { return value ?? string.Empty; } } catch { } return string.Empty; } private static bool WritePlayerCustomData(Player player, string key, string value) { if ((Object)(object)player == (Object)null || string.IsNullOrWhiteSpace(key)) { return false; } try { MethodInfo method = ((object)player).GetType().GetMethod("SetCustomData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2] { typeof(string), typeof(string) }, null); if (method != null) { method.Invoke(player, new object[2] { key, value ?? string.Empty }); return true; } } catch { } try { if (((object)player).GetType().GetField("m_customData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(player) is IDictionary dictionary) { dictionary[key] = value ?? string.Empty; return true; } } catch { } return false; } internal static MethodInfo FindExploreVectorMethod() { ResolveExploreMethods(); return _exploreVectorMethod; } internal static MethodInfo FindExploreFloatMethod() { ResolveExploreMethods(); return _exploreFloatMethod; } private static void ResolveExploreMethods() { if (_exploreMethodsResolved) { return; } _exploreMethodsResolved = true; foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(Minimap))) { if (string.Equals(declaredMethod.Name, "Explore", StringComparison.Ordinal)) { ParameterInfo[] parameters = declaredMethod.GetParameters(); if (_exploreVectorMethod == null && parameters.Length == 2 && parameters[0].ParameterType == typeof(Vector3) && parameters[1].ParameterType == typeof(float)) { _exploreVectorMethod = declaredMethod; } else if (_exploreFloatMethod == null && parameters.Length == 3 && parameters[0].ParameterType == typeof(float) && parameters[1].ParameterType == typeof(float) && parameters[2].ParameterType == typeof(float)) { _exploreFloatMethod = declaredMethod; } } } } private static bool InvokeVanillaExplore(Minimap minimap, Vector3 point, float radius) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)minimap == (Object)null) { return false; } ResolveExploreMethods(); if (_exploreVectorMethod != null) { ExploreVectorInvokeArgs[0] = point; ExploreVectorInvokeArgs[1] = radius; _exploreVectorMethod.Invoke(minimap, ExploreVectorInvokeArgs); return true; } if (_exploreFloatMethod != null) { ExploreFloatInvokeArgs[0] = point.x; ExploreFloatInvokeArgs[1] = point.z; ExploreFloatInvokeArgs[2] = radius; _exploreFloatMethod.Invoke(minimap, ExploreFloatInvokeArgs); return true; } return false; } private static string DescribeExploreMethods(Type minimapType) { try { string[] array = (from method in AccessTools.GetDeclaredMethods(minimapType) where method.Name.IndexOf("Explore", StringComparison.OrdinalIgnoreCase) >= 0 select method.Name + "(" + string.Join(", ", (from parameter in method.GetParameters() select parameter.ParameterType.Name).ToArray()) + ")").Distinct().ToArray(); return (array.Length == 0) ? "Keine Methode mit 'Explore' im Namen gefunden." : ("Gefundene Methoden: " + string.Join("; ", array)); } catch (Exception ex) { return "Methoden konnten nicht aufgelistet werden: " + ex.Message; } } internal static void RefreshExploreMap(Minimap minimap) { if ((Object)(object)minimap == (Object)null) { return; } string[] array = new string[4] { "UpdateExploreMap", "UpdateExploreTexture", "UpdateFogTexture", "UpdateFog" }; foreach (string text in array) { try { MethodInfo methodInfo = AccessTools.Method(((object)minimap).GetType(), text, Type.EmptyTypes, (Type[])null); if (methodInfo == null) { continue; } methodInfo.Invoke(minimap, null); break; } catch { } } } internal static void SaveMapData(Minimap minimap) { if ((Object)(object)minimap == (Object)null) { return; } try { AccessTools.GetDeclaredMethods(((object)minimap).GetType()).FirstOrDefault((MethodInfo candidate) => string.Equals(candidate.Name, "SaveMapData", StringComparison.Ordinal) && candidate.GetParameters().Length == 0)?.Invoke(minimap, null); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Kartografie: SaveMapData konnte nicht aufgerufen werden: " + ex.Message)); } } } internal static void HideSmallMinimap(Minimap minimap) { try { if (!((Object)(object)minimap == (Object)null) && !Plugin.MapMinimapEnabled() && (Object)(object)minimap.m_smallRoot != (Object)null && minimap.m_smallRoot.activeSelf) { minimap.m_smallRoot.SetActive(false); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Kleine Minimap konnte nicht ausgeblendet werden: " + ex.Message)); } } } } [HarmonyPatch(typeof(Minimap), "UpdateExplore")] internal static class BlockVanillaUpdateExplorePatch { private static bool Prefix() { return !CartographyMapModeFeature.ShouldBufferExploration(); } } [HarmonyPatch] internal static class MapTableReadMapPatch { private static bool Prepare() { return CartographyMapModeFeature.ShouldBufferExploration(); } private static IEnumerable TargetMethods() { Type type = AccessTools.TypeByName("MapTable"); if (type == null) { yield break; } foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type)) { if (string.Equals(declaredMethod.Name, "ReadMap", StringComparison.Ordinal) || string.Equals(declaredMethod.Name, "OnRead", StringComparison.Ordinal)) { yield return declaredMethod; } } } private static void Postfix() { CartographyMapModeFeature.ApplyPendingExplorationAtCartographyTable(); } } [HarmonyPatch(typeof(Minimap), "Start")] internal static class MinimapStartHideSmallRootPatch { private static void Postfix(Minimap __instance) { CartographyMapModeFeature.HideSmallMinimap(__instance); } } [HarmonyPatch(typeof(Minimap), "SetMapMode")] internal static class MinimapSetMapModeHideSmallRootPatch { private static bool Prefix(ref MapMode __0) { if (!Plugin.IsCartographyOnlyMapModeEnabled() && Plugin.IsNoMapEnabled() && __0) { __0 = (MapMode)0; return true; } if (!Plugin.MapLargeMapEnabled() && (int)__0 == 2) { return false; } return true; } private static void Postfix(Minimap __instance) { CartographyMapModeFeature.HideSmallMinimap(__instance); } } [HarmonyPatch] internal static class CartographyPlayerLoadedPatch { private static IEnumerable TargetMethods() { string[] array = new string[2] { "Start", "OnSpawned" }; foreach (string methodName in array) { foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(Player))) { if (string.Equals(declaredMethod.Name, methodName, StringComparison.Ordinal)) { yield return declaredMethod; } } } } private static void Postfix(Player __instance) { CartographyMapModeFeature.TryRestorePendingFromPlayer(__instance, "Spieler geladen"); } } [HarmonyPatch] internal static class CartographyLogoutPersistencePatch { private static IEnumerable TargetMethods() { foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(Game))) { if (string.Equals(declaredMethod.Name, "Logout", StringComparison.Ordinal) || string.Equals(declaredMethod.Name, "LogoutAndExit", StringComparison.Ordinal)) { yield return declaredMethod; } } foreach (MethodInfo declaredMethod2 in AccessTools.GetDeclaredMethods(typeof(ZNet))) { if (string.Equals(declaredMethod2.Name, "Shutdown", StringComparison.Ordinal)) { yield return declaredMethod2; } } } private static void Prefix() { CartographyMapModeFeature.PersistPendingToPlayer("Logout", clearRam: true); } } [HarmonyPatch] internal static class CartographyProfileSaveMirrorPatch { private static IEnumerable TargetMethods() { Type typeFromHandle = typeof(PlayerProfile); foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeFromHandle)) { if (string.Equals(declaredMethod.Name, "SavePlayerData", StringComparison.Ordinal) || string.Equals(declaredMethod.Name, "SavePlayerToDisk", StringComparison.Ordinal)) { yield return declaredMethod; } } } private static void Prefix() { CartographyMapModeFeature.PersistForProfileSave(); } } internal sealed class ChallengeHubAdminConsoleFeature : MonoBehaviour { [CompilerGenerated] private static class <>O { public static Action <0>__RpcRequest; public static Action <1>__RpcResponse; public static ConsoleEvent <2>__Command; } private const string RequestRpc = "ChallengeHub_AdminConsole_Request_v1"; private const string ResponseRpc = "ChallengeHub_AdminConsole_Response_v1"; private static ZRoutedRpc _rpc; private static bool _registered; private float _next; internal static void Initialize(Plugin plugin) { if ((Object)(object)plugin != (Object)null && (Object)(object)((Component)plugin).gameObject.GetComponent() == (Object)null) { ((Component)plugin).gameObject.AddComponent(); } } private void Update() { //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown if (Time.unscaledTime < _next) { return; } _next = Time.unscaledTime + 2f; if (ZRoutedRpc.instance != null && _rpc != ZRoutedRpc.instance) { _rpc = ZRoutedRpc.instance; _rpc.Register("ChallengeHub_AdminConsole_Request_v1", (Action)RpcRequest); _rpc.Register("ChallengeHub_AdminConsole_Response_v1", (Action)RpcResponse); } if (!_registered) { _registered = true; object obj = <>O.<2>__Command; if (obj == null) { ConsoleEvent val = Command; <>O.<2>__Command = val; obj = (object)val; } new ConsoleCommand("ch_admin", "Admin-only: help | status | timers [Filter] | resets | world | players | diagnostics", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } } private static void Command(ConsoleEventArgs args) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown string text = ((args.Args != null && args.Args.Length > 1) ? args.Args[1] : "help"); string text2 = ((args.Args != null && args.Args.Length > 2) ? args.Args[2] : ""); ZPackage val = new ZPackage(); val.Write(text); val.Write(text2); if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { RpcRequest(0L, val); } else { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(ServerPeerId(), "ChallengeHub_AdminConsole_Request_v1", new object[1] { val }); } } args.Context.AddString("ChallengeHub Admin-Abfrage an den Server gesendet."); } private static void RpcRequest(long sender, ZPackage package) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || package == null || !WorldEventConsoleFeature.IsAdmin(sender)) { return; } string action = package.ReadString(); string filter = package.ReadString(); string text = Build(action, filter); if (sender == 0L) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)text); } PrintLocal(text); } else { ZPackage val = new ZPackage(); val.Write(text); ZRoutedRpc.instance.InvokeRoutedRPC(sender, "ChallengeHub_AdminConsole_Response_v1", new object[1] { val }); } } private static void RpcResponse(long sender, ZPackage package) { if (package != null && sender == ServerPeerId()) { PrintLocal(package.ReadString()); } } private static string Build(string action, string filter) { switch ((action ?? "").ToLowerInvariant()) { case "help": return "ch_admin status | timers [Filter] | resets | world | players | diagnostics\nch_worldevent list | status | start [Minuten] | test [Minuten] | stop "; case "timers": return TimerSnapshot(filter); case "resets": return TimerSnapshot("reset") + "\n" + TimerSnapshot("restor") + "\n" + TimerSnapshot("renatur"); case "players": return "Spieler online: " + (Player.GetAllPlayers()?.Count ?? 0) + "\n" + string.Join("\n", (Player.GetAllPlayers() ?? new List()).Select((Player player) => player.GetPlayerName() + " | ID " + player.GetPlayerID()).ToArray()); case "world": { string[] obj = new string[6] { "Server: ", ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()).ToString(), " | Welt: ", null, null, null }; ZNet instance = ZNet.instance; obj[3] = ((instance != null) ? instance.GetWorldName() : null) ?? "-"; obj[4] = " | aktive Sagenereignisse: "; obj[5] = WorldEventGameplayFeature.ScheduleSnapshot().Length.ToString(); return string.Concat(obj); } case "diagnostics": return "ChallengeHub 2.12.50\n" + Build("world", "") + "\n" + Build("players", "") + "\n" + TimerSnapshot(""); default: return "ChallengeHub Systeme aktiv. Nutze ch_admin help für alle Admin-Befehle."; } } private static string TimerSnapshot(string filter) { string value = (filter ?? "").ToLowerInvariant(); StringBuilder stringBuilder = new StringBuilder("Server-Timer" + (string.IsNullOrEmpty(filter) ? "" : (" · Filter " + filter)) + ":"); int num = 0; foreach (Type item in from type in typeof(Plugin).Assembly.GetTypes() where type.Namespace == typeof(Plugin).Namespace && type.Name.EndsWith("Feature") select type) { object obj = null; try { obj = Resources.FindObjectsOfTypeAll(item).FirstOrDefault(); } catch { } FieldInfo[] fields = item.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { string text = (item.Name + "." + fieldInfo.Name).ToLowerInvariant(); if ((!text.Contains("timer") && !text.Contains("next") && !text.Contains("cooldown") && !text.Contains("expire") && !text.Contains("due") && !text.Contains("deadline") && !text.Contains("lastscan") && !text.Contains("lastreset")) || (!string.IsNullOrEmpty(value) && !text.Contains(value))) { continue; } try { object value2 = fieldInfo.GetValue(fieldInfo.IsStatic ? null : obj); if (value2 != null && !(value2 is Delegate) && !(value2 is IDictionary) && (!(value2 is IEnumerable) || value2 is string)) { stringBuilder.Append("\n").Append(item.Name).Append(".") .Append(fieldInfo.Name) .Append(" = ") .Append(Format(value2)); if (++num >= 80) { return stringBuilder.ToString() + "\n… Ausgabe auf 80 Timer begrenzt"; } } } catch { } } } if (num != 0) { return stringBuilder.ToString(); } return stringBuilder?.ToString() + " keine passenden Timer gefunden"; } private static string Format(object value) { if (value is DateTime dateTime) { return dateTime.ToUniversalTime().ToString("u"); } if (value is DateTimeOffset { UtcDateTime: var utcDateTime }) { return utcDateTime.ToString("u"); } if (value is float num) { return num.ToString("0.0", CultureInfo.InvariantCulture) + " s/runtime"; } return Convert.ToString(value, CultureInfo.InvariantCulture); } private static long ServerPeerId() { try { MethodInfo methodInfo = AccessTools.Method(((object)ZRoutedRpc.instance)?.GetType(), "GetServerPeerID", (Type[])null, (Type[])null) ?? AccessTools.Method(((object)ZRoutedRpc.instance)?.GetType(), "GetServerPeerId", (Type[])null, (Type[])null); return (methodInfo == null) ? 0 : Convert.ToInt64(methodInfo.Invoke(ZRoutedRpc.instance, null)); } catch { return 0L; } } private static void PrintLocal(string text) { try { object obj = AccessTools.Field(typeof(Terminal), "m_terminalInstance")?.GetValue(null); MethodInfo methodInfo = AccessTools.Method(obj?.GetType(), "AddString", new Type[1] { typeof(string) }, (Type[])null); string[] array = (text ?? "").Split(new char[1] { '\n' }); foreach (string text2 in array) { methodInfo?.Invoke(obj, new object[1] { text2 }); } } catch { } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)text); } } } internal sealed class ChallengeHubApiTokenFeature : MonoBehaviour { [Serializable] private sealed class TokenResponse { public bool ok; public string token; public string expiresAt; public string error; public string actorType; } private static Plugin _plugin; private static bool _acquiring; internal static string Token { get; private set; } = string.Empty; internal static string ExpiresAt { get; private set; } = string.Empty; internal static string LastError { get; private set; } = string.Empty; internal static string ActorType { get; private set; } = string.Empty; internal static void Initialize(Plugin plugin) { _plugin = plugin; if ((Object)(object)((Component)plugin).gameObject.GetComponent() == (Object)null) { ((Component)plugin).gameObject.AddComponent(); } } internal static void ApplyAuthorization(UnityWebRequest request) { if (!string.IsNullOrWhiteSpace(Token)) { request.SetRequestHeader("Authorization", "Bearer " + Token); } else { request.SetRequestHeader("x-challengehub-key", Plugin.ApiKey.Value); } } internal static void AcceptToken(string token, string expiresAt, string actorType) { Token = token ?? string.Empty; ExpiresAt = expiresAt ?? string.Empty; ActorType = actorType ?? string.Empty; LastError = string.Empty; if (!string.IsNullOrWhiteSpace(Token)) { DeathRunCounterFeature.RequestImmediateRefresh(); } } internal static void Invalidate(string reason) { Token = string.Empty; ExpiresAt = string.Empty; ActorType = string.Empty; LastError = reason ?? "Token wurde vom Server abgelehnt."; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"ChallengeHub API-Token wurde abgelehnt und wird automatisch erneuert."); } } private IEnumerator Start() { while (true) { if (NeedsToken()) { yield return Acquire(); } yield return (object)new WaitForSeconds(NeedsToken() ? 5f : 300f); } } internal static IEnumerator EnsureAvailable() { if (string.IsNullOrWhiteSpace(Token)) { if (NeedsToken()) { yield return Acquire(); } while (_acquiring && string.IsNullOrWhiteSpace(Token)) { yield return null; } } } private static bool NeedsToken() { if (string.IsNullOrWhiteSpace(Token) || string.IsNullOrWhiteSpace(ExpiresAt)) { return true; } if (DateTime.TryParse(ExpiresAt, out var result)) { return result.ToUniversalTime() <= DateTime.UtcNow.AddMinutes(10.0); } return true; } private static IEnumerator Acquire() { if (_acquiring) { yield break; } _acquiring = true; yield return (object)new WaitForSeconds(2f); Player localPlayer = Player.m_localPlayer; bool flag = (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && (Object)(object)localPlayer == (Object)null; if (!flag && (Object)(object)localPlayer == (Object)null) { _acquiring = false; yield break; } Dictionary data = new Dictionary { { "apiKey", Plugin.ApiKey.Value }, { "actorType", flag ? "server" : "player" }, { "challengeShortCode", Plugin.ChallengeShortCode.Value }, { "serverId", Plugin.ServerId.Value }, { "worldUid", ((Object)(object)ZNet.instance == (Object)null) ? string.Empty : ZNet.instance.GetWorldUID().ToString() }, { "playerId", ((Object)(object)localPlayer == (Object)null) ? string.Empty : localPlayer.GetPlayerID().ToString() }, { "linkCode", Plugin.PlayerLinkCode.Value } }; byte[] bytes = Encoding.UTF8.GetBytes(Plugin.ToJson(data)); UnityWebRequest request = new UnityWebRequest(Plugin.ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/api/valheim/token", "POST"); try { request.uploadHandler = (UploadHandler)new UploadHandlerRaw(bytes); request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); request.SetRequestHeader("Content-Type", "application/json"); request.SetRequestHeader("x-challengehub-key", Plugin.ApiKey.Value); request.timeout = 15; yield return request.SendWebRequest(); TokenResponse tokenResponse = null; try { tokenResponse = ChallengeHubJson.Deserialize(request.downloadHandler.text ?? string.Empty); } catch { } if ((int)request.result == 1 && tokenResponse != null && tokenResponse.ok) { Token = tokenResponse.token ?? string.Empty; ExpiresAt = tokenResponse.expiresAt ?? string.Empty; ActorType = tokenResponse.actorType ?? string.Empty; LastError = string.Empty; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("ChallengeHub kurzlebiges " + ActorType + "-API-Token aktiviert.")); } DeathRunCounterFeature.RequestImmediateRefresh(); if ((Object)(object)_plugin != (Object)null) { ((MonoBehaviour)_plugin).StartCoroutine(_plugin.RefreshRemoteConfigAfterToken()); } } else { LastError = ((tokenResponse != null && !string.IsNullOrWhiteSpace(tokenResponse.error)) ? tokenResponse.error : request.error); } } finally { ((IDisposable)request)?.Dispose(); } _acquiring = false; } } [DefaultExecutionOrder(10000)] internal sealed class ChallengeHubCursorController : MonoBehaviour { [HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")] private static class GameCameraMouseCapturePatch { private static bool Prefix(GameCamera __instance) { if (!IsActive) { return true; } if ((Object)(object)__instance != (Object)null) { Traverse.Create((object)__instance).Field("m_mouseCapture").SetValue((object)false); } if ((Object)(object)_instance != (Object)null) { _instance.Enforce(); } return false; } } [HarmonyPatch(typeof(GameCamera), "UpdateCamera")] private static class GameCameraInputPatch { private static bool Prefix() { return !IsActive; } } [HarmonyPatch(typeof(PlayerController), "TakeInput")] private static class PlayerControllerInputPatch { private static bool Prefix(ref bool __result) { if (!IsActive) { return true; } __result = false; return false; } } private static ChallengeHubCursorController _instance; private readonly HashSet _owners = new HashSet(StringComparer.OrdinalIgnoreCase); private bool _saved; private bool _previousVisible; private CursorLockMode _previousLock; internal static bool IsActive { get { if ((Object)(object)_instance != (Object)null) { return _instance._owners.Count > 0; } return false; } } internal static void Initialize(Plugin plugin) { _instance = ((Component)plugin).gameObject.GetComponent() ?? ((Component)plugin).gameObject.AddComponent(); } internal static void Acquire(string owner) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_instance == (Object)null) && !string.IsNullOrWhiteSpace(owner)) { if (_instance._owners.Count == 0) { _instance._previousVisible = Cursor.visible; _instance._previousLock = Cursor.lockState; _instance._saved = true; } _instance._owners.Add(owner); _instance.Enforce(); } } internal static void Release(string owner) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_instance == (Object)null) && !string.IsNullOrWhiteSpace(owner)) { _instance._owners.Remove(owner); if (_instance._owners.Count == 0 && _instance._saved) { Cursor.lockState = _instance._previousLock; Cursor.visible = _instance._previousVisible; _instance._saved = false; } } } private void Update() { if (_owners.Count > 0) { Enforce(); } } private void LateUpdate() { if (_owners.Count > 0) { Enforce(); } } private void OnGUI() { if (_owners.Count > 0) { Enforce(); } } private void Enforce() { Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; } private void OnApplicationFocus(bool focused) { if (focused && _owners.Count > 0) { Enforce(); } } private void OnDisable() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) _owners.Clear(); if (_saved) { Cursor.lockState = _previousLock; Cursor.visible = _previousVisible; _saved = false; } } } internal sealed class ChallengeHubEventOutbox : MonoBehaviour { private static Plugin _plugin; private static string _directory; private static readonly HashSet Sending = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> DeliveryCallbacks = new Dictionary>(StringComparer.OrdinalIgnoreCase); internal static int PendingCount { get; private set; } internal static int QuarantineCount { get; private set; } internal static string LastError { get; private set; } = string.Empty; internal static string LastSuccessAt { get; private set; } = string.Empty; internal static string OldestPendingAt { get; private set; } = string.Empty; internal static void Initialize(Plugin plugin) { _plugin = plugin; _directory = Path.Combine(Paths.ConfigPath, "ChallengeHubValheim", "event-outbox"); Directory.CreateDirectory(_directory); if ((Object)(object)((Component)plugin).gameObject.GetComponent() == (Object)null) { ((Component)plugin).gameObject.AddComponent(); } RefreshMetrics(); } internal static string Enqueue(string json, string evidenceId) { if (string.IsNullOrWhiteSpace(json)) { return null; } try { Directory.CreateDirectory(_directory); string text = evidenceId ?? Guid.NewGuid().ToString("N"); string text2 = new string(text.Where(char.IsLetterOrDigit).Take(42).ToArray()); using (SHA256 sHA = SHA256.Create()) { string text3 = BitConverter.ToString(sHA.ComputeHash(Encoding.UTF8.GetBytes(text)), 0, 8).Replace("-", string.Empty).ToLowerInvariant(); text2 = (string.IsNullOrWhiteSpace(text2) ? "event" : text2) + "-" + text3; } string text4 = Path.Combine(_directory, DateTime.UtcNow.ToString("yyyyMMddHHmmssffff") + "-" + text2 + ".json"); string text5 = text4 + ".tmp"; File.WriteAllText(text5, json); File.Move(text5, text4); EnforceLimit(); RefreshMetrics(); return text4; } catch (Exception ex) { LastError = "Outbox konnte Ereignis nicht speichern: " + ex.Message; ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)LastError); } return null; } } internal static void Delivered(string path) { try { if (!string.IsNullOrWhiteSpace(path) && File.Exists(path)) { File.Delete(path); } } catch (Exception ex) { LastError = "Zugestelltes Outbox-Ereignis konnte nicht entfernt werden: " + ex.Message; } if (!string.IsNullOrWhiteSpace(path)) { Sending.Remove(path); } LastSuccessAt = DateTime.UtcNow.ToString("O"); RefreshMetrics(); CompleteDelivery(path, delivered: true); } internal static void RegisterDeliveryCallback(string path, Action callback) { if (!string.IsNullOrWhiteSpace(path) && callback != null) { DeliveryCallbacks[path] = callback; } } internal static void BeginDelivery(string path) { if (!string.IsNullOrWhiteSpace(path)) { Sending.Add(path); } } internal static void Failed(string path, string error) { if (!string.IsNullOrWhiteSpace(path)) { Sending.Remove(path); } LastError = error ?? "Unbekannter Zustellfehler"; RefreshMetrics(); } internal static void AdmissionRejected(string path, string error) { try { if (!string.IsNullOrWhiteSpace(path) && File.Exists(path)) { string text = path + ".admission"; if (File.Exists(text)) { File.Delete(text); } File.Move(path, text); } } catch (Exception ex) { error = (error ?? "Zulassung abgelehnt") + "; Archivierung fehlgeschlagen: " + ex.Message; } if (!string.IsNullOrWhiteSpace(path)) { Sending.Remove(path); } LastError = error ?? "Ereignis wegen fehlender Charakterzulassung zurückgestellt."; RefreshMetrics(); CompleteDelivery(path, delivered: false); } private IEnumerator Start() { while (true) { yield return (object)new WaitForSeconds(8f); if ((Object)(object)_plugin == (Object)null || _plugin.InflightPostCount >= 3) { continue; } string text = NextPending(); if (text != null) { string json; try { json = File.ReadAllText(text); } catch (Exception ex) { Quarantine(text, ex.Message); continue; } BeginDelivery(text); ((MonoBehaviour)this).StartCoroutine(_plugin.PostJson(_plugin.EventEndpoint, json, text)); } } } private static string NextPending() { try { return (from path in Directory.GetFiles(_directory, "*.json") where !Sending.Contains(path) orderby DeliveryPriority(path) select path).ThenByDescending((string path) => path, StringComparer.OrdinalIgnoreCase).FirstOrDefault(); } catch (Exception ex) { LastError = ex.Message; return null; } } private static int DeliveryPriority(string path) { try { string text = File.ReadAllText(path); if (text.IndexOf("\"eventType\":\"deathrun_trophy_snapshot\"", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("\"eventType\":\"trophy_found\"", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("\"eventType\":\"boss_kill\"", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("\"eventType\":\"playstyle_goal\"", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("\"eventType\":\"goal_progress\"", StringComparison.OrdinalIgnoreCase) >= 0) { return 0; } } catch { } return 1; } private static void Quarantine(string path, string reason) { try { string text = path + ".invalid"; if (File.Exists(text)) { File.Delete(text); } File.Move(path, text); } catch { } Sending.Remove(path); LastError = "Beschaedigtes Outbox-Ereignis isoliert: " + reason; RefreshMetrics(); CompleteDelivery(path, delivered: false); } private static void EnforceLimit() { FileInfo[] array = (from file in new DirectoryInfo(_directory).GetFiles("*.json") orderby file.CreationTimeUtc select file).ToArray(); long num = array.Sum((FileInfo file) => file.Length); FileInfo[] array2 = array; foreach (FileInfo fileInfo in array2) { if (array.Length > 10000 || num > 52428800) { string text = fileInfo.FullName + ".overflow"; if (File.Exists(text)) { File.Delete(text); } num -= fileInfo.Length; string fullName = fileInfo.FullName; fileInfo.MoveTo(text); Sending.Remove(fullName); CompleteDelivery(fullName, delivered: false); continue; } break; } } private static void CompleteDelivery(string path, bool delivered) { if (string.IsNullOrWhiteSpace(path) || !DeliveryCallbacks.TryGetValue(path, out var value)) { return; } DeliveryCallbacks.Remove(path); try { value(delivered); } catch (Exception ex) { LastError = "Outbox-Zustellbestaetigung fehlgeschlagen: " + ex.Message; } } private static void RefreshMetrics() { try { FileInfo[] files = new DirectoryInfo(_directory).GetFiles("*.json"); PendingCount = files.Length; QuarantineCount = new DirectoryInfo(_directory).GetFiles("*.invalid").Length + new DirectoryInfo(_directory).GetFiles("*.overflow").Length + new DirectoryInfo(_directory).GetFiles("*.admission").Length; OldestPendingAt = ((files.Length == 0) ? string.Empty : files.Min((FileInfo file) => file.CreationTimeUtc).ToString("O")); } catch { } } } internal static class ChallengeHubJson { private static readonly JsonSerializerSettings ResponseSettings = new JsonSerializerSettings { NullValueHandling = (NullValueHandling)1, MissingMemberHandling = (MissingMemberHandling)0 }; internal static T Deserialize(string json) where T : class { if (string.IsNullOrWhiteSpace(json)) { return null; } return JsonConvert.DeserializeObject(json, ResponseSettings); } internal static string Serialize(object value) { return JsonConvert.SerializeObject(value); } } internal static class ChallengeHubServerGateFeature { private enum ClientGateState { Idle, Pending, Verifying, Approved, Denied } private sealed class ServerProof { internal string RequestNonce = string.Empty; internal string Status = string.Empty; internal string Message = string.Empty; internal string ServerId = string.Empty; internal string WorldUid = string.Empty; internal string WorldName = string.Empty; internal string ChallengeShortCode = string.Empty; internal string ModVersion = string.Empty; internal int ProtocolVersion; internal string SessionNonce = string.Empty; internal string LeaseToken = string.Empty; internal string LeaseExpiresAt = string.Empty; } [Serializable] private sealed class GateResponse { public bool ok; public bool allowed; public bool recorded; public bool serverGateReviewRequired; public string error; public string status; public string action; public string message; public string registrationId; public string leaseToken; public string leaseExpiresAt; public string serverId; public string worldUid; public string worldName; public string challengeShortCode; public string characterStatus; public string allowedServerId; public string allowedWorldUid; public int protocolVersion; public int requiredProtocolVersion; public int serverGateWarningCount; } private const string GatePath = "/api/valheim/server-gate"; private const string RequestRpc = "ChallengeHub_RPC_ServerGateRequest_v1"; private const string ResponseRpc = "ChallengeHub_RPC_ServerGateResponse_v1"; private const float RuntimeTickSeconds = 0.25f; private const float ProofRequestIntervalSeconds = 1f; private const float StatusMessageIntervalSeconds = 4f; private static Plugin _plugin; private static ZRoutedRpc _registeredRpcInstance; private static bool _rpcRegistrationLoopRunning; private static ClientGateState _clientState; private static string _worldSessionKey = string.Empty; private static string _currentWorldUid = string.Empty; private static string _clientAttemptId = string.Empty; private static string _clientRequestNonce = string.Empty; private static float _clientDeadline; private static float _nextProofRequestAt; private static float _nextStatusMessageAt; private static float _deniedAt; private static bool _leaveAttempted; private static bool _verifyRunning; private static bool _denialReportRunning; private static string _denialReason = string.Empty; private static string _denialMessage = string.Empty; private static string _lastPendingReason = string.Empty; private static ServerProof _lastProof; private static string _serverSessionNonce = string.Empty; private static string _serverRegistrationStatus = "unknown"; private static string _serverRegistrationMessage = string.Empty; private static string _serverLeaseToken = string.Empty; private static string _serverLeaseExpiresAt = string.Empty; private static DateTime _serverLeaseExpiryUtc = DateTime.MinValue; private static bool _serverRegistrationApproved; private static bool _serverRegistrationRunning; private static bool _serverEverRegistered; private static float _nextServerRegistrationAt; private static string _lastServerStatusLog = string.Empty; internal static bool GameplayAllowed { get { if (!GateEnabled()) { return true; } if ((Object)(object)ZNet.instance == (Object)null || string.IsNullOrWhiteSpace(ReadCurrentWorldUid())) { return false; } if (IsDedicatedServer()) { return ServerLeaseIsUsable(); } if (ZNet.instance.IsServer()) { if (ServerLeaseIsUsable()) { return _clientState == ClientGateState.Approved; } return false; } return _clientState == ClientGateState.Approved; } } internal static bool WorldAccessGranted => GameplayAllowed; internal static bool ShouldBlockLocalPlayerSpawn { get { //IL_0020: 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) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Invalid comparison between Unknown and I4 if (!GateEnabled() || IsDedicatedServer()) { return false; } if ((Object)(object)ZNet.instance == (Object)null) { return false; } try { ConnectionStatus connectionStatus = ZNet.GetConnectionStatus(); if ((int)connectionStatus != 2 && (int)connectionStatus != 1) { return false; } } catch { } return _clientState != ClientGateState.Approved; } } internal static bool ShouldBlockCharacterPersistence { get { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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_0026: Invalid comparison between Unknown and I4 //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Invalid comparison between Unknown and I4 if (!GateEnabled() || IsDedicatedServer() || (Object)(object)ZNet.instance == (Object)null) { return false; } try { ConnectionStatus connectionStatus = ZNet.GetConnectionStatus(); if ((int)connectionStatus != 2 && (int)connectionStatus != 1) { return false; } } catch { } return _clientState != ClientGateState.Approved; } } internal static string CurrentStatus { get { if (!GateEnabled()) { return "disabled"; } if (_clientState == ClientGateState.Approved) { return "approved"; } if (_clientState == ClientGateState.Denied) { return "denied"; } if (_clientState == ClientGateState.Verifying) { return "verifying"; } return "pending"; } } internal static void RejectDeathRunWorld(string message) { if (DeathRunCounterFeature.Enabled && !_leaveAttempted) { _clientState = ClientGateState.Denied; _denialReason = "deathrun_world_integrity"; _denialMessage = (string.IsNullOrWhiteSpace(message) ? "DeathRun-Weltprüfung fehlgeschlagen." : message); ShowMessage(_denialMessage); LeaveRejectedWorld(); } } internal static void Initialize(Plugin plugin) { _plugin = plugin; if (!((Object)(object)_plugin == (Object)null)) { ((MonoBehaviour)_plugin).StartCoroutine(RpcRegistrationLoop()); ((MonoBehaviour)_plugin).StartCoroutine(RuntimeLoop()); } } internal static void Shutdown() { _plugin = null; _registeredRpcInstance = null; _rpcRegistrationLoopRunning = false; ResetWorldSession(); ResetServerSession(); } private static IEnumerator RpcRegistrationLoop() { if (_rpcRegistrationLoopRunning) { yield break; } _rpcRegistrationLoopRunning = true; try { while ((Object)(object)_plugin != (Object)null) { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null && instance != _registeredRpcInstance) { try { instance.Register("ChallengeHub_RPC_ServerGateRequest_v1", (Action)RPC_ServerGateRequest); instance.Register("ChallengeHub_RPC_ServerGateResponse_v1", (Action)RPC_ServerGateResponse); _registeredRpcInstance = instance; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"ChallengeHub Server-Gate-RPCs registriert."); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("ChallengeHub Server-Gate-RPC-Registrierung fehlgeschlagen: " + ex.Message)); } } } yield return (object)new WaitForSeconds(1f); } } finally { _rpcRegistrationLoopRunning = false; } } private static IEnumerator RuntimeLoop() { while ((Object)(object)_plugin != (Object)null) { yield return (object)new WaitForSeconds(0.25f); if (!GateEnabled()) { _clientState = ClientGateState.Approved; continue; } ZNet instance = ZNet.instance; string text = ReadCurrentWorldUid(); if ((Object)(object)instance == (Object)null || string.IsNullOrWhiteSpace(text)) { if (!string.IsNullOrWhiteSpace(_worldSessionKey)) { ResetWorldSession(); ResetServerSession(); } continue; } bool server = instance.IsServer(); string text2 = (server ? "server:" : "client:") + text + ":" + ResolveServerPeerId(); if (!string.Equals(text2, _worldSessionKey, StringComparison.Ordinal)) { BeginWorldSession(text2, text, server); } float now = Time.realtimeSinceStartup; if (server && !_serverRegistrationRunning && now >= _nextServerRegistrationAt) { _nextServerRegistrationAt = now + EffectiveHeartbeatSeconds(); yield return RegisterOrRefreshServer(text); } if (IsDedicatedServer()) { continue; } if (!PlayerLinkSetupFeature.HasRequiredGateConfiguration()) { _lastPendingReason = "client_setup_required"; _clientDeadline = now + EffectiveVerificationTimeoutSeconds(); ShowPendingStatus(); } else { if (_clientState == ClientGateState.Approved) { continue; } if (_clientState == ClientGateState.Denied) { ShowDeniedStatus(); if (!_leaveAttempted && now >= _deniedAt + EffectiveDisconnectDelaySeconds()) { LeaveRejectedWorld(); } continue; } if (now >= _clientDeadline && !_verifyRunning && !_serverRegistrationRunning) { string reason = (string.IsNullOrWhiteSpace(_lastPendingReason) ? "no_challengehub_server_proof" : _lastPendingReason); string message = BuildDenialMessage(reason, null); Deny(reason, message, null); continue; } ShowPendingStatus(); if (server) { if (ServerLeaseIsUsable() && !_verifyRunning) { ServerProof proof = BuildServerProof(_clientRequestNonce, EffectiveProtocolVersion()); yield return VerifyServerProof(proof); } else if (!_serverRegistrationApproved) { _lastPendingReason = (string.IsNullOrWhiteSpace(_serverRegistrationStatus) ? "server_pending_approval" : ("server_" + _serverRegistrationStatus)); } } else if (_lastProof != null && IsApprovedProof(_lastProof) && !_verifyRunning) { yield return VerifyServerProof(_lastProof); } else if (now >= _nextProofRequestAt) { _nextProofRequestAt = now + 1f; SendProofRequest(); } } } } private static void BeginWorldSession(string sessionKey, string worldUid, bool server) { _worldSessionKey = sessionKey; _currentWorldUid = worldUid; _clientAttemptId = Guid.NewGuid().ToString("N"); _clientRequestNonce = Guid.NewGuid().ToString("N"); _clientDeadline = Time.realtimeSinceStartup + EffectiveVerificationTimeoutSeconds(); _nextProofRequestAt = 0f; _nextStatusMessageAt = 0f; _deniedAt = 0f; _leaveAttempted = false; _verifyRunning = false; _denialReportRunning = false; _denialReason = string.Empty; _denialMessage = string.Empty; _lastPendingReason = string.Empty; _lastProof = null; _clientState = ClientGateState.Pending; if (server) { ResetServerSession(); _serverSessionNonce = Guid.NewGuid().ToString("N"); _nextServerRegistrationAt = 0f; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("ChallengeHub Server-Gate: Weltpruefung gestartet; WorldUid=" + worldUid + "; Versuch=" + _clientAttemptId)); } } private static void ResetWorldSession() { _clientState = ClientGateState.Idle; _worldSessionKey = string.Empty; _currentWorldUid = string.Empty; _clientAttemptId = string.Empty; _clientRequestNonce = string.Empty; _clientDeadline = 0f; _nextProofRequestAt = 0f; _nextStatusMessageAt = 0f; _deniedAt = 0f; _leaveAttempted = false; _verifyRunning = false; _denialReportRunning = false; _denialReason = string.Empty; _denialMessage = string.Empty; _lastPendingReason = string.Empty; _lastProof = null; } private static void ResetServerSession() { _serverSessionNonce = string.Empty; _serverRegistrationStatus = "unknown"; _serverRegistrationMessage = string.Empty; _serverLeaseToken = string.Empty; _serverLeaseExpiresAt = string.Empty; _serverLeaseExpiryUtc = DateTime.MinValue; _serverRegistrationApproved = false; _serverRegistrationRunning = false; _serverEverRegistered = false; _nextServerRegistrationAt = 0f; _lastServerStatusLog = string.Empty; } private static IEnumerator RegisterOrRefreshServer(string worldUid) { if (_serverRegistrationRunning || (Object)(object)_plugin == (Object)null) { yield break; } _serverRegistrationRunning = true; UnityWebRequest request = null; try { if (string.IsNullOrWhiteSpace(_serverSessionNonce)) { _serverSessionNonce = Guid.NewGuid().ToString("N"); } Dictionary payload = new Dictionary { { "action", _serverEverRegistered ? "heartbeat" : "register" }, { "challengeShortCode", _plugin.CurrentChallengeShortCode() }, { "serverId", _plugin.CurrentServerId() }, { "worldUid", worldUid }, { "worldName", RuntimeWorldName() }, { "sessionNonce", _serverSessionNonce }, { "modVersion", "2.12.50" }, { "protocolVersion", EffectiveProtocolVersion() }, { "registrationSecret", _plugin.CurrentServerGateRegistrationSecret() }, { "leaseToken", _serverLeaseToken ?? string.Empty }, { "leaseExpiresAt", _serverLeaseExpiresAt ?? string.Empty } }; string text = ((Plugin.ApiBaseUrl != null) ? (Plugin.ApiBaseUrl.Value ?? string.Empty).TrimEnd(new char[1] { '/' }) : string.Empty); if (string.IsNullOrWhiteSpace(text)) { UpdateServerRegistrationFailure("missing_api_base_url", "ChallengeHub ApiBaseUrl fehlt."); yield break; } request = CreateJsonRequest(text + "/api/valheim/server-gate", payload, 12); yield return request.SendWebRequest(); GateResponse gateResponse = ParseResponse(request); if (gateResponse == null) { UpdateServerRegistrationFailure("invalid_web_response", (request.downloadHandler != null) ? request.downloadHandler.text : string.Empty); yield break; } _serverEverRegistered = true; _serverRegistrationStatus = gateResponse.status ?? (gateResponse.allowed ? "approved" : (gateResponse.error ?? "pending")); _serverRegistrationMessage = gateResponse.message ?? string.Empty; if (gateResponse.allowed && string.Equals(gateResponse.status, "disabled", StringComparison.OrdinalIgnoreCase)) { _serverRegistrationApproved = true; _serverRegistrationStatus = "disabled"; _serverLeaseToken = "challengehub-gate-disabled"; DateTime maxValue = DateTime.MaxValue; _serverLeaseExpiresAt = maxValue.ToString("O", CultureInfo.InvariantCulture); _serverLeaseExpiryUtc = DateTime.MaxValue; _clientDeadline = Mathf.Max(_clientDeadline, Time.realtimeSinceStartup + EffectiveVerificationTimeoutSeconds()); LogServerStatusOnce("approved", "ChallengeHub Server-Gate ist in der Web-App deaktiviert; Weltbeitritt wird von der Web-App freigegeben."); } else if (gateResponse.allowed && !string.IsNullOrWhiteSpace(gateResponse.leaseToken)) { _serverRegistrationApproved = true; _serverLeaseToken = gateResponse.leaseToken; _serverLeaseExpiresAt = gateResponse.leaseExpiresAt ?? string.Empty; _serverLeaseExpiryUtc = (DateTime.TryParse(_serverLeaseExpiresAt, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out var result) ? result.ToUniversalTime() : DateTime.UtcNow.AddMinutes(1.0)); _clientDeadline = Mathf.Max(_clientDeadline, Time.realtimeSinceStartup + EffectiveVerificationTimeoutSeconds()); LogServerStatusOnce("approved", "ChallengeHub Server-Gate: Server/Welt freigegeben; ServerId=" + _plugin.CurrentServerId() + "; WorldUid=" + worldUid); } else { _serverRegistrationApproved = false; _serverLeaseToken = string.Empty; _serverLeaseExpiryUtc = DateTime.MinValue; LogServerStatusOnce(_serverRegistrationStatus, "ChallengeHub Server-Gate: Serverstatus=" + _serverRegistrationStatus + "; " + _serverRegistrationMessage); } } finally { if (request != null) { request.Dispose(); } _serverRegistrationRunning = false; } } private static void UpdateServerRegistrationFailure(string reason, string message) { _serverRegistrationStatus = reason; _serverRegistrationMessage = message ?? string.Empty; if (!ServerLeaseIsUsable()) { _serverRegistrationApproved = false; } LogServerStatusOnce(reason, "ChallengeHub Server-Gate-Registrierung fehlgeschlagen: " + reason + " / " + message); } private static void LogServerStatusOnce(string status, string message) { string text = (status ?? string.Empty) + "|" + (message ?? string.Empty); if (string.Equals(text, _lastServerStatusLog, StringComparison.Ordinal)) { return; } _lastServerStatusLog = text; if (string.Equals(status, "approved", StringComparison.OrdinalIgnoreCase)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)message); } } else { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)message); } } } private static void SendProofRequest() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown if (ZRoutedRpc.instance == null || _clientState == ClientGateState.Denied || _clientState == ClientGateState.Approved) { return; } long num = ResolveServerPeerId(); if (num == 0L) { _lastPendingReason = "server_peer_unknown"; return; } try { ZPackage val = new ZPackage(); val.Write(_clientRequestNonce); val.Write(EffectiveProtocolVersion()); val.Write("2.12.50"); ZRoutedRpc.instance.InvokeRoutedRPC(num, "ChallengeHub_RPC_ServerGateRequest_v1", new object[1] { val }); _lastPendingReason = "waiting_for_server_proof"; } catch (Exception ex) { _lastPendingReason = "server_proof_request_failed"; ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("ChallengeHub Server-Gate-Anfrage fehlgeschlagen: " + ex.Message)); } } } private static void RPC_ServerGateRequest(long sender, ZPackage package) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || package == null || ZRoutedRpc.instance == null) { return; } try { string requestNonce = package.ReadString(); int num = package.ReadInt(); string text = package.ReadString(); ServerProof serverProof = BuildServerProof(requestNonce, num); if (num != EffectiveProtocolVersion()) { serverProof.Status = "protocol_mismatch"; serverProof.Message = "Client-/Server-Gate-Protokoll stimmt nicht ueberein."; serverProof.LeaseToken = string.Empty; } SendServerProof(sender, serverProof); ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("ChallengeHub Server-Gate-Nachweis an Peer " + sender + " gesendet; Client=" + text + "; Status=" + serverProof.Status)); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("ChallengeHub Server-Gate-Anfrage war ungueltig: " + ex.Message)); } } } private static ServerProof BuildServerProof(string requestNonce, int requestedProtocol) { return new ServerProof { RequestNonce = (requestNonce ?? string.Empty), Status = (ServerLeaseIsUsable() ? "approved" : (_serverRegistrationStatus ?? "pending")), Message = (_serverRegistrationMessage ?? string.Empty), ServerId = (((Object)(object)_plugin != (Object)null) ? _plugin.CurrentServerId() : string.Empty), WorldUid = ReadCurrentWorldUid(), WorldName = RuntimeWorldName(), ChallengeShortCode = (((Object)(object)_plugin != (Object)null) ? _plugin.CurrentChallengeShortCode() : string.Empty), ModVersion = "2.12.50", ProtocolVersion = EffectiveProtocolVersion(), SessionNonce = (_serverSessionNonce ?? string.Empty), LeaseToken = (ServerLeaseIsUsable() ? (_serverLeaseToken ?? string.Empty) : string.Empty), LeaseExpiresAt = (ServerLeaseIsUsable() ? (_serverLeaseExpiresAt ?? string.Empty) : string.Empty) }; } private static void SendServerProof(long targetPeerId, ServerProof proof) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(proof.RequestNonce ?? string.Empty); val.Write(proof.Status ?? string.Empty); val.Write(proof.Message ?? string.Empty); val.Write(proof.ServerId ?? string.Empty); val.Write(proof.WorldUid ?? string.Empty); val.Write(proof.WorldName ?? string.Empty); val.Write(proof.ChallengeShortCode ?? string.Empty); val.Write(proof.ModVersion ?? string.Empty); val.Write(proof.ProtocolVersion); val.Write(proof.SessionNonce ?? string.Empty); val.Write(proof.LeaseToken ?? string.Empty); val.Write(proof.LeaseExpiresAt ?? string.Empty); ZRoutedRpc.instance.InvokeRoutedRPC(targetPeerId, "ChallengeHub_RPC_ServerGateResponse_v1", new object[1] { val }); } private static void RPC_ServerGateResponse(long sender, ZPackage package) { if (package == null || (Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer()) { return; } long num = ResolveServerPeerId(); if (num != 0L && sender != num) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("ChallengeHub Server-Gate-Nachweis von unbekanntem Peer verworfen: " + sender)); } return; } try { ServerProof serverProof = new ServerProof { RequestNonce = package.ReadString(), Status = package.ReadString(), Message = package.ReadString(), ServerId = package.ReadString(), WorldUid = package.ReadString(), WorldName = package.ReadString(), ChallengeShortCode = package.ReadString(), ModVersion = package.ReadString(), ProtocolVersion = package.ReadInt(), SessionNonce = package.ReadString(), LeaseToken = package.ReadString(), LeaseExpiresAt = package.ReadString() }; if (string.Equals(serverProof.RequestNonce, _clientRequestNonce, StringComparison.Ordinal)) { if (!string.Equals(serverProof.WorldUid, _currentWorldUid, StringComparison.Ordinal)) { Deny("server_proof_world_mismatch", "Der Servernachweis gehoert nicht zur geladenen Welt-UID.", serverProof); return; } _lastProof = serverProof; _clientDeadline = Mathf.Max(_clientDeadline, Time.realtimeSinceStartup + EffectiveVerificationTimeoutSeconds()); _lastPendingReason = (IsApprovedProof(serverProof) ? "web_verification_pending" : (string.IsNullOrWhiteSpace(serverProof.Status) ? "server_pending_approval" : ("server_" + serverProof.Status))); } } catch (Exception ex) { _lastPendingReason = "invalid_server_proof"; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("ChallengeHub Server-Gate-Nachweis konnte nicht gelesen werden: " + ex.Message)); } } } private static bool IsApprovedProof(ServerProof proof) { if (proof != null && string.Equals(proof.Status, "approved", StringComparison.OrdinalIgnoreCase) && proof.ProtocolVersion == EffectiveProtocolVersion() && !string.IsNullOrWhiteSpace(proof.ServerId) && !string.IsNullOrWhiteSpace(proof.WorldUid) && !string.IsNullOrWhiteSpace(proof.SessionNonce)) { return !string.IsNullOrWhiteSpace(proof.LeaseToken); } return false; } private static IEnumerator VerifyServerProof(ServerProof proof) { if (_verifyRunning || proof == null) { yield break; } _verifyRunning = true; _clientState = ClientGateState.Verifying; UnityWebRequest request = null; try { string text = ReadProfileCharacterId(); if (string.IsNullOrWhiteSpace(text) || text == "0") { _clientState = ClientGateState.Pending; _lastPendingReason = "character_profile_not_ready"; yield break; } string text2 = ((Plugin.ApiBaseUrl != null) ? (Plugin.ApiBaseUrl.Value ?? string.Empty).TrimEnd(new char[1] { '/' }) : string.Empty); if (string.IsNullOrWhiteSpace(text2)) { HandleVerificationTransportFailure("missing_api_base_url", proof); yield break; } Dictionary payload = new Dictionary { { "action", "verify" }, { "challengeShortCode", proof.ChallengeShortCode }, { "serverId", proof.ServerId }, { "worldUid", proof.WorldUid }, { "worldName", proof.WorldName }, { "sessionNonce", proof.SessionNonce }, { "leaseToken", proof.LeaseToken }, { "leaseExpiresAt", proof.LeaseExpiresAt }, { "modVersion", proof.ModVersion }, { "protocolVersion", proof.ProtocolVersion }, { "characterId", text }, { "characterName", ReadProfileCharacterName() }, { "attemptId", _clientAttemptId } }; request = CreateJsonRequest(text2 + "/api/valheim/server-gate", payload, Mathf.CeilToInt(EffectiveVerificationTimeoutSeconds())); yield return request.SendWebRequest(); GateResponse gateResponse = ParseResponse(request); if (gateResponse == null) { HandleVerificationTransportFailure("invalid_web_response", proof); } else if (gateResponse.allowed) { if (_clientState != ClientGateState.Denied) { _clientState = ClientGateState.Approved; _lastPendingReason = string.Empty; _denialReason = string.Empty; _denialMessage = string.Empty; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("ChallengeHub Server-Gate: Zugang freigegeben; ServerId=" + proof.ServerId + "; WorldUid=" + proof.WorldUid + "; Challenge=" + proof.ChallengeShortCode)); } ShowMessage("ChallengeHub-Server und Welt bestaetigt."); } } else { string reason = (string.IsNullOrWhiteSpace(gateResponse.error) ? "server_gate_rejected" : gateResponse.error); Deny(reason, BuildDenialMessage(reason, gateResponse), proof); } } finally { if (request != null) { request.Dispose(); } _verifyRunning = false; if (_clientState == ClientGateState.Verifying) { _clientState = ClientGateState.Pending; } } } private static void HandleVerificationTransportFailure(string reason, ServerProof proof, string details = null) { _lastPendingReason = reason; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("ChallengeHub Server-Gate-Webpruefung fehlgeschlagen: " + reason + (string.IsNullOrWhiteSpace(details) ? string.Empty : (" / " + details)))); } if (!EffectiveFailClosed() && IsApprovedProof(proof)) { _clientState = ClientGateState.Approved; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"ChallengeHub Server-Gate FailClosed ist deaktiviert; gueltiger Server-RPC-Nachweis wird vorlaeufig akzeptiert."); } } else { _clientState = ClientGateState.Pending; } } private static void Deny(string reason, string message, ServerProof proof) { if (_clientState != ClientGateState.Denied) { _clientState = ClientGateState.Denied; _denialReason = (string.IsNullOrWhiteSpace(reason) ? "server_gate_rejected" : reason); _denialMessage = (string.IsNullOrWhiteSpace(message) ? BuildDenialMessage(_denialReason, null) : message); _deniedAt = Time.realtimeSinceStartup; _leaveAttempted = false; RemoveCurrentWorldHistory(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("ChallengeHub Server-Gate: Zugang verweigert; Grund=" + _denialReason + "; WorldUid=" + _currentWorldUid + "; Server=" + ((proof != null) ? proof.ServerId : string.Empty))); } ShowMessage(_denialMessage); if (!_denialReportRunning && (Object)(object)_plugin != (Object)null) { ((MonoBehaviour)_plugin).StartCoroutine(ReportDeniedEntry(proof)); } } } private static IEnumerator ReportDeniedEntry(ServerProof proof) { if (_denialReportRunning) { yield break; } _denialReportRunning = true; UnityWebRequest request = null; try { string value = ReadProfileCharacterId(); string text = ((Plugin.ApiBaseUrl != null) ? (Plugin.ApiBaseUrl.Value ?? string.Empty).TrimEnd(new char[1] { '/' }) : string.Empty); if (!string.IsNullOrWhiteSpace(text) && !string.IsNullOrWhiteSpace(value)) { Dictionary payload = new Dictionary { { "action", "report_denial" }, { "challengeShortCode", ((Object)(object)_plugin != (Object)null) ? _plugin.CurrentChallengeShortCode() : string.Empty }, { "characterId", value }, { "characterName", ReadProfileCharacterName() }, { "attemptId", _clientAttemptId }, { "reason", _denialReason }, { "serverId", (proof != null) ? proof.ServerId : (((Object)(object)_plugin != (Object)null) ? _plugin.CurrentServerId() : string.Empty) }, { "worldUid", _currentWorldUid }, { "worldName", (proof != null) ? proof.WorldName : RuntimeWorldName() }, { "protocolVersion", EffectiveProtocolVersion() } }; request = CreateJsonRequest(text + "/api/valheim/server-gate", payload, 5); yield return request.SendWebRequest(); } } finally { if (request != null) { request.Dispose(); } _denialReportRunning = false; } } private static string BuildDenialMessage(string reason, GateResponse response) { if (response != null && !string.IsNullOrWhiteSpace(response.message)) { return "ChallengeHub-Zutritt verweigert\n\n" + response.message + "\n\nDer Charakter wurde fuer diese Welt nicht gespeichert. Starte eine freigegebene ChallengeHub-Welt oder deaktiviere das Profil Neu2."; } string text; switch ((reason ?? string.Empty).Trim().ToLowerInvariant()) { case "server_pending": case "server_pending_approval": text = "Dieser Server und seine Welt-UID warten in der Web-App auf administrative Freigabe."; break; case "server_blocked": text = "Dieser ChallengeHub-Server ist administrativ gesperrt."; break; case "bound_character_wrong_world": case "bound_character_wrong_challenge": case "bound_character_wrong_server": text = "Der ausgewaehlte Charakter ist dauerhaft an eine andere ChallengeHub-Challenge, einen anderen Server oder eine andere Welt gebunden."; break; case "character_requires_admin_review": text = "Der Charakter muss nach wiederholten falschen Weltbeitritten administrativ geprueft werden."; break; case "protocol_mismatch": case "server_protocol_mismatch": text = "Client und Server verwenden unterschiedliche ChallengeHub-Server-Gate-Versionen."; break; case "server_gate_web_unreachable": case "invalid_web_response": case "missing_api_base_url": case "server_gate_verification_exception": text = "Die ChallengeHub-Web-App konnte den Server nicht bestaetigen."; break; default: text = "Die aktuelle Welt hat keinen gueltigen ChallengeHub-Servernachweis."; break; } return "ChallengeHub-Zutritt verweigert\n\n" + text + "\n\nDer Charakter wurde fuer diese Welt nicht gespeichert. Starte eine freigegebene ChallengeHub-Welt oder deaktiviere das Profil Neu2."; } private static void ShowPendingStatus() { if (!(Time.realtimeSinceStartup < _nextStatusMessageAt)) { _nextStatusMessageAt = Time.realtimeSinceStartup + 4f; string message = "ChallengeHub prueft Server und Welt …"; if (!string.IsNullOrWhiteSpace(_lastPendingReason) && _lastPendingReason.Contains("pending")) { message = "ChallengeHub-Server wartet auf Freigabe …"; } ShowMessage(message); } } private static void ShowDeniedStatus() { if (!(Time.realtimeSinceStartup < _nextStatusMessageAt)) { _nextStatusMessageAt = Time.realtimeSinceStartup + 1f; ShowMessage(_denialMessage); } } private static void ShowMessage(string message) { if (string.IsNullOrWhiteSpace(message)) { return; } try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { ((Character)localPlayer).Message((MessageType)2, message, 0, (Sprite)null); } else if ((Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)2, message, 0, (Sprite)null, false); } } catch { } } private static void LeaveRejectedWorld() { _leaveAttempted = true; RemoveCurrentWorldHistory(); if (!TryInvokeLogout() && !TryInvokeShutdown()) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)"ChallengeHub Server-Gate konnte die Rueckkehr ins Hauptmenue nicht automatisch ausloesen. Bitte manuell ausloggen; Charakter-Saves bleiben blockiert."); } } } private static bool TryInvokeLogout() { try { Game instance = Game.instance; if ((Object)(object)instance == (Object)null) { return false; } MethodInfo methodInfo = AccessTools.Method(((object)instance).GetType(), "Logout", new Type[2] { typeof(bool), typeof(bool) }, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(instance, new object[2] { false, true }); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"ChallengeHub Server-Gate: Rueckkehr ins Hauptmenue ohne Charakter-Save ausgeloest."); } return true; } string[] array = new string[2] { "Logout", "LogoutAndExit" }; foreach (string name in array) { foreach (MethodInfo item in from candidate in AccessTools.GetDeclaredMethods(((object)instance).GetType()) where string.Equals(candidate.Name, name, StringComparison.Ordinal) orderby candidate.GetParameters().Length select candidate) { if (TryBuildSafeLogoutArguments(item.GetParameters(), out var args)) { item.Invoke(item.IsStatic ? null : instance, args); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("ChallengeHub Server-Gate: Rueckkehr ins Hauptmenue ausgeloest (" + name + ", Save=false soweit unterstuetzt).")); } return true; } } } } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("ChallengeHub Server-Gate Logout-Aufruf fehlgeschlagen: " + Unwrap(ex))); } } return false; } private static bool TryBuildSafeLogoutArguments(ParameterInfo[] parameters, out object[] args) { args = new object[(parameters != null) ? parameters.Length : 0]; if (parameters == null) { return true; } for (int i = 0; i < parameters.Length; i++) { ParameterInfo parameterInfo = parameters[i]; string text = parameterInfo.Name ?? string.Empty; if (parameterInfo.ParameterType == typeof(bool) && text.IndexOf("save", StringComparison.OrdinalIgnoreCase) >= 0) { args[i] = false; continue; } if (parameterInfo.HasDefaultValue) { args[i] = parameterInfo.DefaultValue; continue; } Type parameterType = parameterInfo.ParameterType; if (parameterType.IsByRef) { return false; } if (!parameterType.IsValueType) { args[i] = null; continue; } try { args[i] = Activator.CreateInstance(parameterType); } catch { return false; } } return true; } private static bool TryInvokeShutdown() { try { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null) { return false; } foreach (MethodInfo item in from candidate in AccessTools.GetDeclaredMethods(((object)instance).GetType()) where string.Equals(candidate.Name, "Shutdown", StringComparison.Ordinal) orderby candidate.GetParameters().Length select candidate) { if (TryBuildDefaultArguments(item.GetParameters(), out var args)) { item.Invoke(item.IsStatic ? null : instance, args); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"ChallengeHub Server-Gate: Netzwerk-Shutdown ausgeloest."); } return true; } } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("ChallengeHub Server-Gate Shutdown-Aufruf fehlgeschlagen: " + Unwrap(ex))); } } return false; } private static bool TryBuildDefaultArguments(ParameterInfo[] parameters, out object[] args) { args = new object[(parameters != null) ? parameters.Length : 0]; if (parameters == null) { return true; } for (int i = 0; i < parameters.Length; i++) { ParameterInfo parameterInfo = parameters[i]; if (parameterInfo.HasDefaultValue) { args[i] = parameterInfo.DefaultValue; continue; } Type parameterType = parameterInfo.ParameterType; if (parameterType.IsByRef) { return false; } if (!parameterType.IsValueType) { args[i] = null; continue; } try { args[i] = Activator.CreateInstance(parameterType); } catch { return false; } } return true; } private static void RemoveCurrentWorldHistory() { if (string.IsNullOrWhiteSpace(_currentWorldUid)) { return; } try { PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null); if (val == null) { return; } FieldInfo fieldInfo = AccessTools.Field(((object)val).GetType(), "m_worldData"); if (!(((fieldInfo != null) ? fieldInfo.GetValue(val) : null) is IDictionary dictionary)) { return; } object obj = null; foreach (DictionaryEntry item in dictionary) { if (string.Equals((item.Key != null) ? Convert.ToString(item.Key, CultureInfo.InvariantCulture) : string.Empty, _currentWorldUid, StringComparison.Ordinal)) { obj = item.Key; break; } } if (obj != null) { dictionary.Remove(obj); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("ChallengeHub Server-Gate: nicht freigegebene Welt-UID aus dem ungespeicherten Profilzustand entfernt: " + _currentWorldUid)); } } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("ChallengeHub Server-Gate konnte die lokale Welt-Historie nicht bereinigen: " + ex.Message)); } } } private static UnityWebRequest CreateJsonRequest(string url, Dictionary payload, int timeoutSeconds) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_0028: 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_0033: Expected O, but got Unknown //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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_0074: Expected O, but got Unknown byte[] bytes = Encoding.UTF8.GetBytes(Plugin.ToJson(payload)); UnityWebRequest val = new UnityWebRequest(url, "POST") { uploadHandler = (UploadHandler)new UploadHandlerRaw(bytes), downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(), timeout = Math.Max(3, timeoutSeconds) }; val.SetRequestHeader("Content-Type", "application/json"); val.SetRequestHeader("x-challengehub-key", (Plugin.ApiKey != null) ? Plugin.ApiKey.Value : string.Empty); return val; } private static GateResponse ParseResponse(UnityWebRequest request) { if (request == null || request.downloadHandler == null) { return null; } string text = request.downloadHandler.text; if (string.IsNullOrWhiteSpace(text)) { return null; } try { return ChallengeHubJson.Deserialize(text); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("ChallengeHub Server-Gate-Antwort ist ungueltig: " + ex.Message + " / " + text)); } return null; } } private static bool ServerLeaseIsUsable() { if (_serverRegistrationApproved && !string.IsNullOrWhiteSpace(_serverLeaseToken)) { return _serverLeaseExpiryUtc > DateTime.UtcNow.AddSeconds(2.0); } return false; } private static long ResolveServerPeerId() { try { long num = DungeonTalentRewardFeature.ResolveServerPeerForClient(); if (num != 0L) { return num; } if (ZRoutedRpc.instance == null) { return 0L; } MethodInfo methodInfo = AccessTools.Method(((object)ZRoutedRpc.instance).GetType(), "GetServerPeerID", (Type[])null, (Type[])null) ?? AccessTools.Method(((object)ZRoutedRpc.instance).GetType(), "GetServerPeerId", (Type[])null, (Type[])null); return (methodInfo != null) ? Convert.ToInt64(methodInfo.Invoke(ZRoutedRpc.instance, null), CultureInfo.InvariantCulture) : 0; } catch { return 0L; } } private static string ReadCurrentWorldUid() { try { if ((Object)(object)ZNet.instance == (Object)null) { return string.Empty; } MethodInfo methodInfo = AccessTools.Method(((object)ZNet.instance).GetType(), "GetWorldUID", (Type[])null, (Type[])null) ?? AccessTools.Method(((object)ZNet.instance).GetType(), "GetWorldUid", (Type[])null, (Type[])null); object obj = ((methodInfo != null) ? methodInfo.Invoke(ZNet.instance, null) : null); return (obj != null) ? Convert.ToString(obj, CultureInfo.InvariantCulture) : string.Empty; } catch { return string.Empty; } } private static string RuntimeWorldName() { try { PropertyInfo propertyInfo = AccessTools.Property(typeof(ZNet), "World"); object obj = ((propertyInfo != null) ? propertyInfo.GetValue(null, null) : null); if (obj != null) { FieldInfo fieldInfo = AccessTools.Field(obj.GetType(), "m_name"); string text = ((fieldInfo != null) ? Convert.ToString(fieldInfo.GetValue(obj), CultureInfo.InvariantCulture) : string.Empty); if (!string.IsNullOrWhiteSpace(text)) { return text; } } } catch { } if (!((Object)(object)_plugin != (Object)null)) { return string.Empty; } return _plugin.CurrentWorldName(); } private static string ReadProfileCharacterId() { try { PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null); return (val != null) ? val.GetPlayerID().ToString(CultureInfo.InvariantCulture) : string.Empty; } catch { try { return ((Object)(object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetPlayerID().ToString(CultureInfo.InvariantCulture) : string.Empty; } catch { return string.Empty; } } } private static string ReadProfileCharacterName() { try { PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null); return (val != null) ? val.GetName() : (((Object)(object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetPlayerName() : string.Empty); } catch { return string.Empty; } } private static bool IsDedicatedServer() { try { return (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsDedicated(); } catch { return false; } } private static bool GateEnabled() { if ((Object)(object)_plugin != (Object)null) { return _plugin.EffectiveServerGateEnabled(); } return false; } private static bool EffectiveFailClosed() { if (!((Object)(object)_plugin == (Object)null)) { return _plugin.EffectiveServerGateFailClosed(); } return true; } private static int EffectiveProtocolVersion() { if (!((Object)(object)_plugin != (Object)null)) { return 1; } return _plugin.EffectiveServerGateProtocolVersion(); } private static float EffectiveVerificationTimeoutSeconds() { if (!((Object)(object)_plugin != (Object)null)) { return 10f; } return _plugin.EffectiveServerGateVerificationTimeoutSeconds(); } private static float EffectiveDisconnectDelaySeconds() { if (!((Object)(object)_plugin != (Object)null)) { return 3f; } return _plugin.EffectiveServerGateDisconnectDelaySeconds(); } private static float EffectiveHeartbeatSeconds() { if (!((Object)(object)_plugin != (Object)null)) { return 30f; } return _plugin.EffectiveServerGateHeartbeatSeconds(); } private static string Unwrap(Exception ex) { if (!(ex is TargetInvocationException { InnerException: not null } ex2)) { return ex.Message; } return ex2.InnerException.Message; } } [HarmonyPatch(typeof(Game), "UpdateRespawn")] internal static class ChallengeHubServerGateSpawnBlockPatch { private static bool Prefix() { return !ChallengeHubServerGateFeature.ShouldBlockLocalPlayerSpawn; } } [HarmonyPatch(typeof(Game), "SavePlayerProfile")] internal static class ChallengeHubServerGateGameSaveBlockPatch { private static float _nextWarningAt; private static bool Prefix() { if (!ChallengeHubServerGateFeature.ShouldBlockCharacterPersistence) { return true; } if (Time.realtimeSinceStartup >= _nextWarningAt) { _nextWarningAt = Time.realtimeSinceStartup + 5f; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"ChallengeHub Server-Gate blockiert Game.SavePlayerProfile vor gueltiger Weltfreigabe."); } } return false; } } [HarmonyPatch] internal static class ChallengeHubServerGateProfileSaveBlockPatch { private static float _nextWarningAt; private static IEnumerable TargetMethods() { foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(PlayerProfile))) { if (string.Equals(declaredMethod.Name, "SavePlayerData", StringComparison.Ordinal) || string.Equals(declaredMethod.Name, "SavePlayerToDisk", StringComparison.Ordinal)) { yield return declaredMethod; } } } private static bool Prefix(MethodBase __originalMethod) { if (!ChallengeHubServerGateFeature.ShouldBlockCharacterPersistence) { return true; } if (Time.realtimeSinceStartup >= _nextWarningAt) { _nextWarningAt = Time.realtimeSinceStartup + 5f; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("ChallengeHub Server-Gate blockiert Charakter-Speicherung vor gueltiger Weltfreigabe: " + ((__originalMethod != null) ? __originalMethod.Name : "unknown"))); } } return false; } } internal static class ChallengeHubWindowTheme { private static Texture2D _background; private static Texture2D _panel; private static Texture2D _button; private static Texture2D _buttonHover; internal static GUIStyle Window; internal static GUIStyle Button; internal static GUIStyle Label; internal static GUIStyle Title; internal static void Apply() { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Expected O, but got Unknown //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Expected O, but got Unknown //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Expected O, but got Unknown //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Expected O, but got Unknown //IL_028c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_background == (Object)null) { Font font = ((IEnumerable)Resources.FindObjectsOfTypeAll()).FirstOrDefault((Func)((Font val) => ((Object)val).name == "Norsebold")) ?? GUI.skin.font; Font font2 = ((IEnumerable)Resources.FindObjectsOfTypeAll()).FirstOrDefault((Func)((Font val) => ((Object)val).name == "AveriaSansLibre-Bold" || ((Object)val).name == "AveriaSerifLibre-Bold")) ?? GUI.skin.font; _background = Solid(new Color(0.075f, 0.075f, 0.075f, 0.99f)); _panel = Solid(new Color(0.12f, 0.12f, 0.12f, 0.98f)); _button = Solid(new Color(0.42f, 0.42f, 0.42f, 1f)); _buttonHover = Solid(new Color(0.58f, 0.58f, 0.58f, 1f)); Window = new GUIStyle(GUI.skin.window) { font = font, fontSize = 24 }; Window.normal.background = _background; Window.normal.textColor = new Color(1f, 0.65f, 0f); Button = new GUIStyle(GUI.skin.button) { font = font2, fontSize = 14, wordWrap = true }; Button.normal.background = _button; Button.hover.background = _buttonHover; Button.active.background = _buttonHover; Button.normal.textColor = Color.white; Button.hover.textColor = Color.white; Button.active.textColor = Color.white; Label = new GUIStyle(GUI.skin.label) { font = font2, fontSize = 15, wordWrap = true }; Label.normal.textColor = new Color(0.9f, 0.9f, 0.9f); Title = new GUIStyle(Label) { font = font, fontSize = 22 }; Title.normal.textColor = new Color(1f, 0.65f, 0f); } GUI.skin.window = Window; GUI.skin.button = Button; GUI.skin.label = Label; } internal static void DrawPanel(Rect rect) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) GUI.DrawTexture(rect, (Texture)(object)_panel); } private static Texture2D Solid(Color color) { //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_000a: 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) //IL_0017: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, color); val.Apply(); return val; } } internal static class ChallengeHubWorldState { internal const string KindField = "ChallengeHub.WorldState.Kind"; internal const string KeyField = "ChallengeHub.WorldState.Key"; internal const string SchemaField = "ChallengeHub.WorldState.Schema"; internal const string WorldUidField = "ChallengeHub.WorldState.WorldUid"; internal const string PayloadField = "ChallengeHub.WorldState.Payload"; internal const string UpdatedField = "ChallengeHub.WorldState.UpdatedUtc"; internal const string Schema = "2.8.0"; private static readonly Dictionary Cache = new Dictionary(StringComparer.Ordinal); private static readonly HashSet StateIds = new HashSet(); private static long _worldUid = long.MinValue; private static bool _built; internal static bool IsServer { get { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } internal static long WorldUid { get { if ((Object)(object)ZNet.instance == (Object)null) { return 0L; } try { MethodInfo methodInfo = AccessTools.Method(((object)ZNet.instance).GetType(), "GetWorldUID", (Type[])null, (Type[])null) ?? AccessTools.Method(((object)ZNet.instance).GetType(), "GetWorldUid", (Type[])null, (Type[])null); if (methodInfo != null) { return Convert.ToInt64(methodInfo.Invoke(ZNet.instance, null), CultureInfo.InvariantCulture); } } catch { } try { object obj2 = AccessTools.Property(typeof(ZNet), "World")?.GetValue(null, null); FieldInfo fieldInfo = ((obj2 != null) ? AccessTools.Field(obj2.GetType(), "m_uid") : null); if (fieldInfo != null) { return Convert.ToInt64(fieldInfo.GetValue(obj2), CultureInfo.InvariantCulture); } } catch { } return 0L; } } internal static string Composite(string kind, string key) { return (kind ?? string.Empty).Trim() + "|" + (key ?? string.Empty).Trim(); } internal static ZDO Resolve(string kind, string key, Vector3 hiddenPosition, bool create) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(kind) || string.IsNullOrWhiteSpace(key)) { return null; } EnsureCache(); string key2 = Composite(kind, key); if (Cache.TryGetValue(key2, out var value) && IsValid(value)) { return value; } if (!create || !IsServer || ZDOMan.instance == null) { return null; } try { ZDO val = ZDOMan.instance.CreateNewZDO(hiddenPosition, 0); if (val == null) { return null; } val.Persistent = true; val.Distant = true; val.SetOwner(ZDOMan.GetSessionID()); val.Set("ChallengeHub.WorldState.Kind", kind); val.Set("ChallengeHub.WorldState.Key", key); val.Set("ChallengeHub.WorldState.Schema", "2.8.0"); val.Set("ChallengeHub.WorldState.WorldUid", WorldUid.ToString(CultureInfo.InvariantCulture)); val.Set("ChallengeHub.WorldState.UpdatedUtc", DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)); Cache[key2] = val; StateIds.Add(val.m_uid); return val; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("ChallengeHub-Weltzustand konnte nicht angelegt werden: " + kind + "/" + key + " :: " + ex.Message)); } return null; } } internal static IEnumerable Snapshot(string kind) { EnsureCache(); string prefix = (kind ?? string.Empty).Trim() + "|"; return (from pair in Cache where pair.Key.StartsWith(prefix, StringComparison.Ordinal) && IsValid(pair.Value) select pair.Value).ToArray(); } internal static bool IsWorldStateZdo(ZDO zdo) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (!IsValid(zdo)) { return false; } if (StateIds.Contains(zdo.m_uid)) { return true; } try { if (string.IsNullOrWhiteSpace(zdo.GetString("ChallengeHub.WorldState.Kind", string.Empty))) { return false; } StateIds.Add(zdo.m_uid); return true; } catch { return false; } } internal static string ReadPayload(ZDO zdo) { if (!IsValid(zdo)) { return string.Empty; } try { return zdo.GetString("ChallengeHub.WorldState.Payload", string.Empty); } catch { return string.Empty; } } internal static bool WritePayload(ZDO zdo, string payload, string reason) { if (!IsServer || !IsValid(zdo)) { return false; } try { if (!ValheimNetworkCompatibility.TryTakeServerOwnership(zdo, "world_state:" + (reason ?? "write"))) { return false; } zdo.Set("ChallengeHub.WorldState.Payload", payload ?? string.Empty); zdo.Set("ChallengeHub.WorldState.Schema", "2.8.0"); zdo.Set("ChallengeHub.WorldState.WorldUid", WorldUid.ToString(CultureInfo.InvariantCulture)); zdo.Set("ChallengeHub.WorldState.UpdatedUtc", DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)); return true; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("ChallengeHub-Weltzustand konnte nicht geschrieben werden: " + ex.Message)); } return false; } } internal static string Read(ZDO zdo, string field, string fallback = "") { if (!IsValid(zdo)) { return fallback ?? string.Empty; } try { return zdo.GetString(field, fallback ?? string.Empty); } catch { return fallback ?? string.Empty; } } internal static bool Write(ZDO zdo, string field, string value, string reason) { if (!IsServer || !IsValid(zdo) || string.IsNullOrWhiteSpace(field)) { return false; } try { if (!ValheimNetworkCompatibility.TryTakeServerOwnership(zdo, "world_state_field:" + (reason ?? field))) { return false; } zdo.Set(field, value ?? string.Empty); zdo.Set("ChallengeHub.WorldState.UpdatedUtc", DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)); return true; } catch { return false; } } internal static void Invalidate() { _worldUid = long.MinValue; _built = false; Cache.Clear(); StateIds.Clear(); } internal static string Vector(Vector3 value) { return value.x.ToString("R", CultureInfo.InvariantCulture) + "," + value.y.ToString("R", CultureInfo.InvariantCulture) + "," + value.z.ToString("R", CultureInfo.InvariantCulture); } internal static bool TryVector(string raw, out Vector3 result) { //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_0076: 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) result = Vector3.zero; string[] array = (raw ?? string.Empty).Split(new char[1] { ',' }); if (array.Length != 3) { return false; } if (!float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || !float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3) || !float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result4)) { return false; } result = new Vector3(result2, result3, result4); return true; } private static void EnsureCache() { //IL_00af: Unknown result type (might be due to invalid IL or missing references) long worldUid = WorldUid; if (_built && _worldUid == worldUid) { return; } _built = true; _worldUid = worldUid; Cache.Clear(); StateIds.Clear(); if (ZDOMan.instance == null) { return; } try { foreach (ZDO item in ValheimPrivateAccess.SnapshotAllZdos()) { if (IsValid(item)) { string text; string text2; try { text = item.GetString("ChallengeHub.WorldState.Kind", string.Empty); text2 = item.GetString("ChallengeHub.WorldState.Key", string.Empty); } catch { continue; } if (!string.IsNullOrWhiteSpace(text) && !string.IsNullOrWhiteSpace(text2)) { Cache[Composite(text, text2)] = item; StateIds.Add(item.m_uid); } } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("ChallengeHub-Weltzustandsregistry konnte nur teilweise aufgebaut werden: " + ex.Message)); } } } private static bool IsValid(ZDO zdo) { try { return zdo != null && zdo.IsValid(); } catch { return false; } } } internal static class CharacterAdmissionFeature { private sealed class CharacterState { internal byte[] SnapshotBytes; internal string SnapshotBase64; internal string SnapshotHash; internal List WorldHistory; internal Dictionary Skills; internal List> InventoryManifest; internal int InventoryItemCount; internal Vector3 Position; } [Serializable] private sealed class AdmissionResponse { public bool ok; public bool enabled; public bool locked; public bool freshActivation; public bool vaultStored; public bool checkpointImmediately; public bool warningIssued; public string error; public string status; public string action; public string message; public string restoreReason; public string serverSnapshot; public string serverSnapshotHash; public string allowedWorldUid; public int serverRevision; public int warningCount; public int vaultItemCount; public float waitingRadius; public float checkpointSeconds; public AdmissionVector waitingPosition; public AdmissionVector startPosition; public AdmissionVector serverPosition; public string[] foreignWorlds; } [Serializable] private sealed class AdmissionVector { public float x; public float y; public float z; public Vector3 ToVector3() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) return new Vector3(x, y, z); } } private const string AdmissionPath = "/api/valheim/admission"; private const float RuntimeTickSeconds = 0.25f; private const float InitialPlayerSettleSeconds = 0.75f; private const int MaxSnapshotBytes = 5242880; private static Plugin _plugin; private static Player _sessionPlayer; private static int _sessionPlayerInstanceId; private static string _sessionCharacterId = string.Empty; private static string _sessionWorldUid = string.Empty; private static int _clientRevision; private static bool _locked; private static bool _active; private static bool _requestRunning; private static bool _activationRunning; private static float _sessionStartedAt; private static float _nextCheckinAt; private static float _nextCheckpointAt; private static float _nextStatusMessageAt; private static float _checkpointSeconds = 30f; private static int _deathEpoch; private static float _deathTransitionUntil; private static string _lastStatus = string.Empty; private static string _lastMessage = string.Empty; internal static bool IsLocked { get { if (_locked && (Object)(object)_sessionPlayer != (Object)null) { return (Object)(object)Player.m_localPlayer == (Object)(object)_sessionPlayer; } return false; } } internal static bool ChallengeScoringAllowed { get { if (ChallengeHubServerGateFeature.GameplayAllowed) { if (AdmissionEnabled()) { return _active; } return true; } return false; } } internal static void NotifyLocalPlayerDeath(Player player) { if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer)) { _deathEpoch++; _deathTransitionUntil = Time.realtimeSinceStartup + 20f; _nextCheckinAt = _deathTransitionUntil; _nextCheckpointAt = _deathTransitionUntil + 5f; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("ChallengeHub Charakter-Checkpoint waehrend Tod/Respawn pausiert; Epoch=" + _deathEpoch)); } } } private static bool IsDeathTransition(Player player) { if ((Object)(object)player == (Object)null || Time.realtimeSinceStartup < _deathTransitionUntil) { return true; } try { return ((Character)player).IsDead(); } catch { return false; } } internal static void RequestCheckpointSoon(float delaySeconds = 1f) { if (_active && !((Object)(object)_sessionPlayer == (Object)null)) { float num = Time.realtimeSinceStartup + Mathf.Max(0.25f, delaySeconds); if (_nextCheckpointAt <= 0f || num < _nextCheckpointAt) { _nextCheckpointAt = num; } } } internal static void Initialize(Plugin plugin) { _plugin = plugin; if (!((Object)(object)_plugin == (Object)null)) { ((MonoBehaviour)_plugin).StartCoroutine(RuntimeLoop()); } } internal static void Shutdown() { _locked = false; _active = false; _requestRunning = false; _activationRunning = false; ResetSession(null); _plugin = null; } private static IEnumerator RuntimeLoop() { while ((Object)(object)_plugin != (Object)null) { yield return (object)new WaitForSeconds(0.25f); if (!AdmissionEnabled()) { if (_locked || _active || (Object)(object)_sessionPlayer != (Object)null) { ReleaseLock(preserveActive: false); ResetSession(null); } } else { if ((Object)(object)ZNet.instance == (Object)null) { continue; } if (!ChallengeHubServerGateFeature.WorldAccessGranted) { if ((Object)(object)_sessionPlayer != (Object)null) { ResetSession(null); } continue; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { if ((Object)(object)_sessionPlayer != (Object)null) { ResetSession(null); } continue; } int instanceID = ((Object)localPlayer).GetInstanceID(); string text = SafeCharacterId(localPlayer); string text2 = ReadCurrentWorldUid(); if ((Object)(object)_sessionPlayer == (Object)null || instanceID != _sessionPlayerInstanceId || !string.Equals(text, _sessionCharacterId, StringComparison.Ordinal) || !string.Equals(text2, _sessionWorldUid, StringComparison.Ordinal)) { bool num = _deathEpoch > 0 && !string.IsNullOrWhiteSpace(_sessionCharacterId) && string.Equals(text, _sessionCharacterId, StringComparison.Ordinal) && string.Equals(text2, _sessionWorldUid, StringComparison.Ordinal) && (_active || Time.realtimeSinceStartup < _deathTransitionUntil + 30f); int clientRevision = _clientRevision; ResetSession(localPlayer); _sessionPlayerInstanceId = instanceID; _sessionCharacterId = text; _sessionWorldUid = text2; _sessionStartedAt = Time.realtimeSinceStartup; _nextCheckinAt = (num ? float.MaxValue : (_sessionStartedAt + 0.75f)); if (num) { _clientRevision = Math.Max(clientRevision, LoadRevision()); _active = true; _nextCheckpointAt = Mathf.Max(Time.realtimeSinceStartup + 1f, _deathTransitionUntil + 1f); try { SEMan sEMan = ((Character)localPlayer).GetSEMan(); if (sEMan != null) { sEMan.RemoveAllStatusEffects(false); } } catch { } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("ChallengeHub Respawn erkannt; alter Serverstand wird nicht wiederhergestellt. Todes-Checkpoint folgt mit Revision=" + _clientRevision + ".")); } } _lastMessage = string.Empty; ReleaseLock(preserveActive: false); } if (_locked) { ShowPeriodicStatus(localPlayer); } float realtimeSinceStartup = Time.realtimeSinceStartup; if (!_requestRunning && !_activationRunning && !IsDeathTransition(localPlayer)) { if (!_active && realtimeSinceStartup >= _nextCheckinAt) { _nextCheckinAt = realtimeSinceStartup + EffectivePollSeconds(); yield return CheckIn(localPlayer); } else if (_active && realtimeSinceStartup >= _nextCheckpointAt) { _nextCheckpointAt = realtimeSinceStartup + _checkpointSeconds; yield return Checkpoint(localPlayer); } } } } } private static IEnumerator CheckIn(Player player) { if (IsDeathTransition(player)) { yield break; } int requestDeathEpoch = _deathEpoch; if (!TryCaptureState(player, out var state, out var error)) { HandleLocalFailure("Charakterprüfung konnte nicht erstellt werden: " + error); yield break; } AdmissionResponse response = null; yield return PostAdmission(BuildPayload("checkin", player, state), delegate(AdmissionResponse value) { response = value; }); if (requestDeathEpoch != _deathEpoch || IsDeathTransition(player)) { yield break; } if (response == null) { HandleLocalFailure("ChallengeHub-Aufnahmedienst ist nicht erreichbar."); yield break; } ApplyResponseSettings(response); _lastStatus = response.status ?? string.Empty; _lastMessage = response.message ?? string.Empty; if (string.Equals(response.status, "disabled", StringComparison.OrdinalIgnoreCase)) { _active = false; ReleaseLock(preserveActive: false); yield break; } switch ((response.action ?? string.Empty).Trim().ToLowerInvariant()) { case "release": SetActive(response, player); yield break; case "restore": yield return RestoreCanonicalState(player, response); yield break; case "clear_inventory": yield return ActivateApprovedCharacter(player, state, response); yield break; case "clear_initial_inventory": yield return SealInitialInventory(player, state, response); yield break; case "activate_current": yield return ActivateCurrentCharacter(player, state, response); yield break; } _active = false; ReleaseLock(preserveActive: false); _lastMessage = (string.IsNullOrWhiteSpace(response.message) ? "Freigabe steht noch aus. Weltzugang bleibt erlaubt; ChallengeHub-Wertung ist gesperrt." : response.message); Message(player, _lastMessage); } private static IEnumerator SealInitialInventory(Player player, CharacterState incomingState, AdmissionResponse response) { if (_activationRunning) { yield break; } _activationRunning = true; SetLocked(locked: true, "ChallengeHub sichert die beim Eintritt mitgebrachten Gegenstände …"); try { if (response == null || !response.vaultStored) { ReleaseTransactionWithMessage(player, "Der persönliche Eintrittstresor wurde noch nicht bestätigt. Inventar bleibt unverändert."); yield break; } if (!WriteLocalSafetyBackup("initial_inventory_vault", incomingState, out var error)) { ReleaseTransactionWithMessage(player, "Lokale Sicherheitskopie fehlgeschlagen. Gegenstände wurden nicht entfernt: " + error); yield break; } if (!TryClearInventory(player, out var error2)) { ReleaseTransactionWithMessage(player, "Eintrittsinventar konnte nicht sicher geleert werden: " + error2); yield break; } if (!TryCaptureState(player, out var state, out var error3) || state.InventoryItemCount > 0) { ReleaseTransactionWithMessage(player, "Inventarprüfung nach der Tresorsicherung ist fehlgeschlagen: " + error3); yield break; } Dictionary dictionary = BuildPayload("vault_ack", player, state); dictionary["inventoryEmpty"] = true; AdmissionResponse acknowledgement = null; yield return PostAdmission(dictionary, delegate(AdmissionResponse value) { acknowledgement = value; }); if (acknowledgement == null || !acknowledgement.ok) { ReleaseTransactionWithMessage(player, (acknowledgement != null && !string.IsNullOrWhiteSpace(acknowledgement.message)) ? acknowledgement.message : "Die Web-App hat die Eintrittstresor-Sicherung noch nicht bestätigt."); yield break; } SavePlayerProfile(player); ApplyResponseSettings(acknowledgement); if (string.Equals(acknowledgement.action, "release", StringComparison.OrdinalIgnoreCase)) { _clientRevision = Math.Max(1, acknowledgement.serverRevision); SaveRevision(_clientRevision); SetActive(acknowledgement, player); } else { _active = false; ReleaseLock(preserveActive: false); _lastMessage = (string.IsNullOrWhiteSpace(acknowledgement.message) ? "Eintrittsgegenstände sind sicher verwahrt. Freigabe steht weiter aus; Weltzugang bleibt frei." : acknowledgement.message); Message(player, _lastMessage); _nextCheckinAt = Time.realtimeSinceStartup + 1f; } } finally { _activationRunning = false; } } private static IEnumerator ActivateCurrentCharacter(Player player, CharacterState currentState, AdmissionResponse response) { if (_activationRunning) { yield break; } _activationRunning = true; SetLocked(locked: true, "ChallengeHub bindet den aktuellen Serverwelt-Stand an diesen Charakter …"); try { Dictionary dictionary = BuildPayload("activate", player, currentState); dictionary["initialInventoryCleared"] = true; AdmissionResponse activation = null; yield return PostAdmission(dictionary, delegate(AdmissionResponse value) { activation = value; }); if (activation == null || !activation.ok || !string.Equals(activation.action, "release", StringComparison.OrdinalIgnoreCase)) { ReleaseTransactionWithMessage(player, (activation != null && !string.IsNullOrWhiteSpace(activation.message)) ? activation.message : "Die Web-App hat die Weltbindung noch nicht bestätigt."); yield break; } _clientRevision = Math.Max(1, activation.serverRevision); SaveRevision(_clientRevision); SavePlayerProfile(player); SetActive(activation, player); } finally { _activationRunning = false; } } private static IEnumerator ActivateApprovedCharacter(Player player, CharacterState incomingState, AdmissionResponse response) { if (_activationRunning) { yield break; } _activationRunning = true; SetLocked(locked: true, "ChallengeHub versiegelt das Erstinventar und bindet den Charakter an diese Welt …"); try { if (!response.vaultStored) { ReleaseTransactionWithMessage(player, "Der persönliche Tresor wurde noch nicht bestätigt. Inventar bleibt unverändert; Weltzugang bleibt frei."); yield break; } if (!WriteLocalSafetyBackup("pre_activation_vault", incomingState, out var error)) { ReleaseTransactionWithMessage(player, "Lokale Sicherheitskopie fehlgeschlagen. Gegenstände wurden nicht entfernt: " + error); yield break; } if (!TryClearInventory(player, out var error2)) { ReleaseTransactionWithMessage(player, "Inventar konnte nicht sicher geleert werden: " + error2); yield break; } if (!TryCaptureState(player, out var state, out var error3) || state.InventoryItemCount > 0) { ReleaseTransactionWithMessage(player, "Inventarprüfung nach der Einlagerung ist fehlgeschlagen: " + error3); yield break; } Dictionary dictionary = BuildPayload("activate", player, state); dictionary["inventoryEmpty"] = true; AdmissionResponse activation = null; yield return PostAdmission(dictionary, delegate(AdmissionResponse value) { activation = value; }); if (activation == null || !activation.ok || !string.Equals(activation.action, "release", StringComparison.OrdinalIgnoreCase)) { ReleaseTransactionWithMessage(player, (activation != null && !string.IsNullOrWhiteSpace(activation.message)) ? activation.message : "Die Web-App hat die Aktivierung nicht bestätigt. Die lokale Tresorsicherung bleibt erhalten."); yield break; } ApplyResponseSettings(activation); _clientRevision = Math.Max(1, activation.serverRevision); SaveRevision(_clientRevision); SavePlayerProfile(player); SetActive(activation, player); } finally { _activationRunning = false; } } private static IEnumerator RestoreCanonicalState(Player player, AdmissionResponse response) { if (IsDeathTransition(player)) { yield break; } int deathEpoch = _deathEpoch; SetLocked(locked: true, response.message); if (TryCaptureState(player, out var state, out var _) && !WriteLocalSafetyBackup("foreign_state_before_restore", state, out var error2)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("ChallengeHub Fremdstand-Sicherheitskopie fehlgeschlagen: " + error2)); } } byte[] array; try { array = Convert.FromBase64String(response.serverSnapshot ?? string.Empty); } catch (Exception ex) { ReleaseTransactionWithMessage(player, "Serverstand ist ungültig und wurde nicht geladen: " + ex.Message); yield break; } if (array.Length == 0 || array.Length > 5242880) { ReleaseTransactionWithMessage(player, "Serverstand fehlt oder überschreitet die zulässige Größe."); yield break; } string text = response.serverSnapshotHash ?? string.Empty; string text2 = ComputeHash(array); if (!string.IsNullOrWhiteSpace(text) && !string.Equals(text, text2, StringComparison.OrdinalIgnoreCase)) { ReleaseTransactionWithMessage(player, "Serverstand-Prüfsumme stimmt nicht. Wiederherstellung wurde sicher abgebrochen."); yield break; } if (deathEpoch != _deathEpoch || IsDeathTransition(player)) { ReleaseLock(preserveActive: false); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"Charakter-Wiederherstellung wegen Todes-/Respawn-Uebergang verworfen."); } yield break; } if (!TryLoadSnapshot(player, array, out var error3)) { ReleaseTransactionWithMessage(player, "Serverstand konnte nicht auf den Charakter geladen werden: " + error3); yield break; } PruneForeignWorldHistory(string.IsNullOrWhiteSpace(response.allowedWorldUid) ? _sessionWorldUid : response.allowedWorldUid); if (response.serverPosition != null) { TeleportPlayer(player, response.serverPosition.ToVector3()); } SavePlayerProfile(player); _clientRevision = Math.Max(0, response.serverRevision); SaveRevision(_clientRevision); Dictionary payload = new Dictionary { { "action", "restore_ack" }, { "challengeShortCode", EffectiveChallengeShortCode() }, { "linkCode", (Plugin.PlayerLinkCode != null) ? Plugin.PlayerLinkCode.Value : string.Empty }, { "twitchLogin", (Plugin.TwitchLogin != null) ? Plugin.TwitchLogin.Value : string.Empty }, { "characterId", _sessionCharacterId }, { "characterName", player.GetPlayerName() }, { "currentWorldUid", _sessionWorldUid }, { "worldHistory", ReadWorldHistory() }, { "stateHash", text2 }, { "clientRevision", _clientRevision }, { "restoreReason", response.restoreReason ?? string.Empty } }; AdmissionResponse ackResponse = null; yield return PostAdmission(payload, delegate(AdmissionResponse value) { ackResponse = value; }); if (ackResponse == null || !ackResponse.ok || !string.Equals(ackResponse.action, "release", StringComparison.OrdinalIgnoreCase)) { ReleaseTransactionWithMessage(player, (ackResponse != null && !string.IsNullOrWhiteSpace(ackResponse.message)) ? ackResponse.message : "Serverstand wurde lokal geladen, aber die Web-App hat die Wiederherstellung noch nicht bestätigt."); yield break; } _lastMessage = (response.warningIssued ? (response.message + " Dieser Verstoß wurde protokolliert.") : response.message); SetActive(ackResponse, player); RequestCheckpointSoon(1.5f); Message(player, _lastMessage); } private static IEnumerator Checkpoint(Player player) { if (IsDeathTransition(player)) { yield break; } int requestDeathEpoch = _deathEpoch; if (!TryCaptureState(player, out var state, out var error)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("ChallengeHub Charakter-Checkpoint konnte nicht erstellt werden: " + error)); } yield break; } Dictionary dictionary = BuildPayload("checkpoint", player, state); dictionary["clientRevision"] = _clientRevision; AdmissionResponse response = null; yield return PostAdmission(dictionary, delegate(AdmissionResponse value) { response = value; }); if (requestDeathEpoch != _deathEpoch || IsDeathTransition(player)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"Veraltete Checkpoint-Antwort aus Todes-/Respawn-Uebergang verworfen."); } yield break; } if (response == null) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"ChallengeHub Charakter-Checkpoint konnte nicht an die Web-App gesendet werden."); } yield break; } ApplyResponseSettings(response); if (string.Equals(response.action, "restore", StringComparison.OrdinalIgnoreCase)) { yield return RestoreCanonicalState(player, response); } else if (!response.ok || !string.Equals(response.action, "checkpoint_accepted", StringComparison.OrdinalIgnoreCase)) { ReleaseTransactionWithMessage(player, string.IsNullOrWhiteSpace(response.message) ? "Serverseitiger Charakter-Checkpoint wurde abgelehnt. Weltzugang bleibt frei; die ChallengeHub-Wertung wird erneut geprüft." : response.message); _active = false; _nextCheckinAt = Time.realtimeSinceStartup + 1f; } else { _clientRevision = Math.Max(_clientRevision + 1, response.serverRevision); SaveRevision(_clientRevision); } } private static Dictionary BuildPayload(string action, Player player, CharacterState state) { //IL_0149: Unknown result type (might be due to invalid IL or missing references) return new Dictionary { { "action", action }, { "clientVersion", "2.12.50" }, { "challengeShortCode", EffectiveChallengeShortCode() }, { "linkCode", (Plugin.PlayerLinkCode != null) ? Plugin.PlayerLinkCode.Value : string.Empty }, { "twitchLogin", (Plugin.TwitchLogin != null) ? Plugin.TwitchLogin.Value : string.Empty }, { "characterId", _sessionCharacterId }, { "characterName", ((Object)(object)player != (Object)null) ? player.GetPlayerName() : string.Empty }, { "serverId", EffectiveServerId() }, { "worldName", EffectiveWorldName() }, { "currentWorldUid", _sessionWorldUid }, { "worldHistory", state.WorldHistory }, { "skills", state.Skills }, { "inventoryManifest", state.InventoryManifest }, { "stateHash", state.SnapshotHash }, { "stateSnapshot", state.SnapshotBase64 }, { "clientRevision", _clientRevision }, { "position", Plugin.SerializeVector(state.Position) } }; } private static IEnumerator PostAdmission(Dictionary payload, Action completed) { if (_requestRunning) { completed?.Invoke(null); yield break; } _requestRunning = true; UnityWebRequest request = null; try { string text = ((Plugin.ApiBaseUrl != null) ? (Plugin.ApiBaseUrl.Value ?? string.Empty).TrimEnd(new char[1] { '/' }) : string.Empty); if (string.IsNullOrWhiteSpace(text)) { completed?.Invoke(null); yield break; } string s = Plugin.ToJson(payload); byte[] bytes = Encoding.UTF8.GetBytes(s); request = new UnityWebRequest(text + "/api/valheim/admission", "POST"); request.uploadHandler = (UploadHandler)new UploadHandlerRaw(bytes); request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); request.timeout = 20; request.SetRequestHeader("Content-Type", "application/json"); request.SetRequestHeader("x-challengehub-key", (Plugin.ApiKey != null) ? Plugin.ApiKey.Value : string.Empty); yield return request.SendWebRequest(); AdmissionResponse obj = null; try { obj = ChallengeHubJson.Deserialize(request.downloadHandler.text); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("ChallengeHub Aufnahme-Antwort ist ungültig: " + ex.Message)); } } if ((int)request.result != 1) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("ChallengeHub Aufnahme-API fehlgeschlagen: " + request.error + " / " + request.downloadHandler.text)); } } completed?.Invoke(obj); } finally { if (request != null) { request.Dispose(); } _requestRunning = false; } } private static bool TryCaptureState(Player player, out CharacterState state, out string error) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) state = null; error = string.Empty; if ((Object)(object)player == (Object)null) { error = "Kein lokaler Spieler."; return false; } try { ZPackage val = new ZPackage(); MethodInfo methodInfo = AccessTools.Method(((object)player).GetType(), "Save", new Type[1] { typeof(ZPackage) }, (Type[])null); if (methodInfo == null) { error = "Player.Save(ZPackage) wurde nicht gefunden."; return false; } methodInfo.Invoke(player, new object[1] { val }); byte[] array = val.GetArray(); if (array == null || array.Length == 0) { error = "Leerer Charakter-Snapshot."; return false; } if (array.Length > 5242880) { error = "Charakter-Snapshot ist zu groß (" + array.Length + " Bytes)."; return false; } Dictionary dictionary = new Dictionary(); foreach (KeyValuePair item in _plugin.ReadSkillSnapshot(player)) { dictionary[item.Key] = Math.Round(item.Value, 2); } List> list = ReadInventoryManifest(player); state = new CharacterState { SnapshotBytes = array, SnapshotBase64 = Convert.ToBase64String(array), SnapshotHash = ComputeHash(array), WorldHistory = ReadWorldHistory(), Skills = dictionary, InventoryManifest = list, InventoryItemCount = list.Sum((Dictionary item) => Convert.ToInt32(item["stack"], CultureInfo.InvariantCulture)), Position = ((Component)player).transform.position }; return true; } catch (TargetInvocationException ex) { error = ((ex.InnerException != null) ? ex.InnerException.Message : ex.Message); return false; } catch (Exception ex2) { error = ex2.Message; return false; } } private static bool TryLoadSnapshot(Player player, byte[] snapshot, out string error) { //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Expected O, but got Unknown error = string.Empty; try { MethodInfo methodInfo = AccessTools.Method(((object)player).GetType(), "Load", new Type[1] { typeof(ZPackage) }, (Type[])null); if (methodInfo == null) { error = "Player.Load(ZPackage) wurde nicht gefunden."; return false; } try { ((Humanoid)player).UnequipAllItems(); } catch { } try { ((Humanoid)player).ResetLoadedWeapon(); } catch { } try { SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan != null) { sEMan.RemoveAllStatusEffects(false); } } catch { } if (AccessTools.Field(((object)player).GetType(), "m_knownStations")?.GetValue(player) is IDictionary dictionary) { dictionary.Clear(); } methodInfo.Invoke(player, new object[1] { (object)new ZPackage(snapshot) }); try { AccessTools.Method(((object)player).GetType(), "UpdateEquipmentStatusEffects", (Type[])null, (Type[])null)?.Invoke(player, null); } catch { } return true; } catch (TargetInvocationException ex) { error = ((ex.InnerException != null) ? ex.InnerException.Message : ex.Message); return false; } catch (Exception ex2) { error = ex2.Message; return false; } } private static List> ReadInventoryManifest(Player player) { List> list = new List>(); Inventory val = (((Object)(object)player != (Object)null) ? ((Humanoid)player).GetInventory() : null); if (val == null) { return list; } foreach (ItemData item in val.GetAllItems().ToList()) { if (item != null) { string text = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : string.Empty); string value = ((item.m_shared != null) ? item.m_shared.m_name : text); list.Add(new Dictionary { { "prefab", text }, { "name", value }, { "stack", Math.Max(0, item.m_stack) }, { "quality", Math.Max(0, item.m_quality) }, { "equipped", item.m_equipped } }); } } return list; } private static bool TryClearInventory(Player player, out string error) { error = string.Empty; try { Inventory val = (((Object)(object)player != (Object)null) ? ((Humanoid)player).GetInventory() : null); if (val == null) { error = "Inventar ist nicht verfügbar."; return false; } MethodInfo methodInfo = AccessTools.Method(((object)player).GetType(), "UnequipAllItems", (Type[])null, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(player, null); } foreach (ItemData item in val.GetAllItems().ToList()) { if (item != null && !val.RemoveItem(item)) { error = "Gegenstand konnte nicht entfernt werden: " + (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : "unbekannt"); return false; } } return val.NrOfItemsIncludingStacks() == 0; } catch (TargetInvocationException ex) { error = ((ex.InnerException != null) ? ex.InnerException.Message : ex.Message); return false; } catch (Exception ex2) { error = ex2.Message; return false; } } private static List ReadWorldHistory() { HashSet hashSet = new HashSet(StringComparer.Ordinal); try { PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null); if (val == null) { return hashSet.ToList(); } FieldInfo fieldInfo = AccessTools.Field(((object)val).GetType(), "m_worldData"); object obj = ((fieldInfo != null) ? fieldInfo.GetValue(val) : null); if (obj is IDictionary dictionary) { foreach (DictionaryEntry item in dictionary) { if (item.Key != null) { hashSet.Add(Convert.ToString(item.Key, CultureInfo.InvariantCulture)); } } } else if (obj is IEnumerable enumerable) { foreach (object item2 in enumerable) { if (item2 != null) { PropertyInfo property = item2.GetType().GetProperty("Key"); object obj2 = ((property != null) ? property.GetValue(item2, null) : null); if (obj2 != null) { hashSet.Add(Convert.ToString(obj2, CultureInfo.InvariantCulture)); } } } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("ChallengeHub Welthistorie konnte nicht gelesen werden: " + ex.Message)); } } return hashSet.OrderBy((string value) => value, StringComparer.Ordinal).ToList(); } private static void PruneForeignWorldHistory(string allowedWorldUid) { if (string.IsNullOrWhiteSpace(allowedWorldUid)) { return; } try { PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null); FieldInfo fieldInfo = ((val != null) ? AccessTools.Field(((object)val).GetType(), "m_worldData") : null); if (!(((fieldInfo != null) ? fieldInfo.GetValue(val) : null) is IDictionary dictionary)) { return; } List list = new List(); foreach (DictionaryEntry item in dictionary) { if (!string.Equals((item.Key != null) ? Convert.ToString(item.Key, CultureInfo.InvariantCulture) : string.Empty, allowedWorldUid, StringComparison.Ordinal)) { list.Add(item.Key); } } foreach (object item2 in list) { dictionary.Remove(item2); } if (list.Count > 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("ChallengeHub hat " + list.Count + " fremde Welteinträge aus dem lokalen Charakterprofil entfernt.")); } } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("ChallengeHub konnte fremde Welthistorie nicht bereinigen: " + ex.Message)); } } } private static void SavePlayerProfile(Player player) { try { Game instance = Game.instance; PlayerProfile val = (((Object)(object)instance != (Object)null) ? instance.GetPlayerProfile() : null); if (val == null || (Object)(object)player == (Object)null) { return; } MethodInfo methodInfo = AccessTools.Method(((object)val).GetType(), "SavePlayerData", new Type[1] { typeof(Player) }, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(val, new object[1] { player }); } MethodInfo methodInfo2 = AccessTools.Method(((object)instance).GetType(), "SavePlayerProfile", new Type[1] { typeof(bool) }, (Type[])null); if (methodInfo2 != null) { methodInfo2.Invoke(instance, new object[1] { true }); return; } MethodInfo methodInfo3 = AccessTools.Method(((object)val).GetType(), "Save", (Type[])null, (Type[])null); if (methodInfo3 != null) { methodInfo3.Invoke(val, null); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("ChallengeHub Charakterprofil konnte nicht sofort gespeichert werden: " + ex.Message)); } } } private static bool WriteLocalSafetyBackup(string reason, CharacterState state, out string error) { //IL_012a: Unknown result type (might be due to invalid IL or missing references) error = string.Empty; try { string text = Path.Combine(Paths.ConfigPath, "ChallengeHubValheim", "admission-backups"); Directory.CreateDirectory(text); string path = SanitizeFileName(_sessionCharacterId) + "-" + DateTime.UtcNow.ToString("yyyyMMdd-HHmmss", CultureInfo.InvariantCulture) + "-" + reason + ".json"; string text2 = Path.Combine(text, path); Dictionary data = new Dictionary { { "schema", "challengehub-character-backup-v1" }, { "reason", reason }, { "createdAtUtc", DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture) }, { "characterId", _sessionCharacterId }, { "worldUid", _sessionWorldUid }, { "stateHash", state.SnapshotHash }, { "stateSnapshot", state.SnapshotBase64 }, { "worldHistory", state.WorldHistory }, { "inventoryManifest", state.InventoryManifest }, { "position", Plugin.SerializeVector(state.Position) } }; File.WriteAllText(text2, Plugin.ToJson(data), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("ChallengeHub Charakter-Sicherheitskopie gespeichert: " + text2)); } return true; } catch (Exception ex) { error = ex.Message; return false; } } private static void SetActive(AdmissionResponse response, Player player) { _active = true; _clientRevision = Math.Max(_clientRevision, response?.serverRevision ?? 0); SaveRevision(_clientRevision); _checkpointSeconds = ((response != null && response.checkpointSeconds >= 10f) ? response.checkpointSeconds : EffectiveCheckpointSeconds()); _nextCheckpointAt = ((response != null && response.checkpointImmediately) ? (Time.realtimeSinceStartup + 1f) : (Time.realtimeSinceStartup + _checkpointSeconds)); ReleaseLock(preserveActive: true); MeadowsSettlementFeature.RequestAssignmentForLocalPlayer(); if (response != null && !string.IsNullOrWhiteSpace(response.message)) { Message(player, response.message); } } private static void SetLocked(bool locked, string message) { _locked = locked; if (locked) { _active = false; _lastMessage = (string.IsNullOrWhiteSpace(message) ? "ChallengeHub verarbeitet den Charakterstand …" : message); } } private static void ReleaseLock(bool preserveActive) { _locked = false; if (!preserveActive) { _active = false; } } private static void ReleaseTransactionWithMessage(Player player, string message) { ReleaseLock(preserveActive: false); _lastMessage = message ?? string.Empty; Message(player, _lastMessage); } private static void HandleLocalFailure(string message) { _locked = false; _lastMessage = message; if (EffectiveFailClosed()) { _active = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)(message + " Weltzugang bleibt erlaubt; ChallengeHub-Wertung bleibt fail-closed gesperrt.")); } } else { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)(message + " FailClosed ist deaktiviert; ChallengeHub-Wertung wird vorläufig erlaubt.")); } _active = true; } Message(_sessionPlayer, _lastMessage); } private static void ResetSession(Player player) { ReleaseLock(preserveActive: false); _sessionPlayer = player; _sessionPlayerInstanceId = (((Object)(object)player != (Object)null) ? ((Object)player).GetInstanceID() : 0); _sessionCharacterId = (((Object)(object)player != (Object)null) ? SafeCharacterId(player) : string.Empty); _sessionWorldUid = (((Object)(object)player != (Object)null) ? ReadCurrentWorldUid() : string.Empty); if ((Object)(object)_plugin != (Object)null) { _checkpointSeconds = _plugin.EffectiveCharacterCheckpointSeconds(); } _clientRevision = (((Object)(object)player != (Object)null) ? LoadRevision() : 0); _active = false; _requestRunning = false; _activationRunning = false; _lastStatus = string.Empty; _lastMessage = string.Empty; _nextStatusMessageAt = 0f; _nextCheckpointAt = 0f; } private static void ApplyResponseSettings(AdmissionResponse response) { if (response != null && response.checkpointSeconds >= 10f) { _checkpointSeconds = response.checkpointSeconds; } } private static void TeleportPlayer(Player player, Vector3 position) { //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return; } try { MethodInfo methodInfo = AccessTools.Method(((object)player).GetType(), "TeleportTo", new Type[3] { typeof(Vector3), typeof(Quaternion), typeof(bool) }, (Type[])null); if (methodInfo != null) { object obj = methodInfo.Invoke(player, new object[3] { position, ((Component)player).transform.rotation, true }); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { return; } } ((Component)player).transform.position = position; Rigidbody component = ((Component)player).GetComponent(); if ((Object)(object)component != (Object)null) { component.linearVelocity = Vector3.zero; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("ChallengeHub Teleport fehlgeschlagen: " + ex.Message)); } } } private static void ShowPeriodicStatus(Player player) { if (!((Object)(object)player == (Object)null) && !(Time.realtimeSinceStartup < _nextStatusMessageAt)) { _nextStatusMessageAt = Time.realtimeSinceStartup + 10f; Message(player, string.IsNullOrWhiteSpace(_lastMessage) ? "ChallengeHub-Aufnahme läuft. Der Server kann jederzeit verlassen werden." : (_lastMessage + "\nDer Server kann jederzeit verlassen werden.")); } } private static void Message(Player player, string message) { if ((Object)(object)player == (Object)null || string.IsNullOrWhiteSpace(message)) { return; } try { ((Character)player).Message((MessageType)2, message, 0, (Sprite)null); } catch { } } private static string ReadCurrentWorldUid() { try { if ((Object)(object)ZNet.instance == (Object)null) { return string.Empty; } MethodInfo methodInfo = AccessTools.Method(((object)ZNet.instance).GetType(), "GetWorldUID", (Type[])null, (Type[])null) ?? AccessTools.Method(((object)ZNet.instance).GetType(), "GetWorldUid", (Type[])null, (Type[])null); object obj = ((methodInfo != null) ? methodInfo.Invoke(ZNet.instance, null) : null); return (obj != null) ? Convert.ToString(obj, CultureInfo.InvariantCulture) : string.Empty; } catch { return string.Empty; } } private static string SafeCharacterId(Player player) { try { return ((Object)(object)player != (Object)null) ? player.GetPlayerID().ToString(CultureInfo.InvariantCulture) : string.Empty; } catch { return string.Empty; } } private static string ComputeHash(byte[] data) { using SHA256 sHA = SHA256.Create(); return BitConverter.ToString(sHA.ComputeHash(data ?? new byte[0])).Replace("-", string.Empty).ToLowerInvariant(); } private static string RevisionKey() { return "ChallengeHub.Admission.Revision." + SanitizeFileName(EffectiveServerId()) + "." + SanitizeFileName(_sessionCharacterId); } private static int LoadRevision() { try { return PlayerPrefs.GetInt(RevisionKey(), 0); } catch { return 0; } } private static void SaveRevision(int revision) { try { PlayerPrefs.SetInt(RevisionKey(), Math.Max(0, revision)); PlayerPrefs.Save(); } catch { } } private static string SanitizeFileName(string value) { string text = (string.IsNullOrWhiteSpace(value) ? "unknown" : value); char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { text = text.Replace(oldChar, '_'); } if (text.Length <= 80) { return text; } return text.Substring(0, 80); } private static bool AdmissionEnabled() { if ((Object)(object)_plugin != (Object)null) { return _plugin.EffectiveCharacterAdmissionEnabled(); } return false; } private static float EffectivePollSeconds() { if (!((Object)(object)_plugin != (Object)null)) { return 8f; } return _plugin.EffectiveAdmissionPollSeconds(); } private static float EffectiveCheckpointSeconds() { if (!((Object)(object)_plugin != (Object)null)) { return 30f; } return _plugin.EffectiveCharacterCheckpointSeconds(); } private static bool EffectiveFailClosed() { if (!((Object)(object)_plugin == (Object)null)) { return _plugin.EffectiveAdmissionFailClosed(); } return true; } private static string EffectiveChallengeShortCode() { if (!((Object)(object)_plugin != (Object)null)) { return string.Empty; } return _plugin.CurrentChallengeShortCode(); } private static string EffectiveServerId() { if (!((Object)(object)_plugin != (Object)null)) { return string.Empty; } return _plugin.CurrentServerId(); } private static string EffectiveWorldName() { if (!((Object)(object)_plugin != (Object)null)) { return string.Empty; } return _plugin.CurrentWorldName(); } } [HarmonyPatch(typeof(Character), "Damage", new Type[] { typeof(HitData) })] internal static class CharacterAdmissionDamagePatch { private static bool Prefix(Character __instance) { if (!CharacterAdmissionFeature.IsLocked) { return true; } Player val = (Player)(object)((__instance is Player) ? __instance : null); if (!((Object)(object)val == (Object)null)) { return (Object)(object)val != (Object)(object)Player.m_localPlayer; } return true; } } [HarmonyPatch(typeof(Humanoid), "DropItem", new Type[] { typeof(Inventory), typeof(ItemData), typeof(int) })] internal static class CharacterAdmissionDropItemPatch { private static bool Prefix(Humanoid __instance, ref bool __result) { if (!CharacterAdmissionFeature.IsLocked || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } __result = false; try { ((Character)Player.m_localPlayer).Message((MessageType)2, "Während der sicheren Tresor- oder Wiederherstellungstransaktion können keine Gegenstände abgelegt werden.", 0, (Sprite)null); } catch { } return false; } } [HarmonyPatch(typeof(Humanoid), "UseItem", new Type[] { typeof(Inventory), typeof(ItemData), typeof(bool) })] internal static class CharacterAdmissionUseItemPatch { private static bool Prefix(Humanoid __instance) { if (!CharacterAdmissionFeature.IsLocked || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } try { ((Character)Player.m_localPlayer).Message((MessageType)2, "Gegenstände sind während der sicheren Tresor- oder Wiederherstellungstransaktion vorübergehend gesperrt.", 0, (Sprite)null); } catch { } return false; } } [HarmonyPatch(typeof(Humanoid), "StartAttack", new Type[] { typeof(Character), typeof(bool) })] internal static class CharacterAdmissionAttackPatch { private static bool Prefix(Humanoid __instance, ref bool __result) { if (!CharacterAdmissionFeature.IsLocked || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Player), "PlacePiece", new Type[] { typeof(Piece), typeof(Vector3), typeof(Quaternion), typeof(bool) })] internal static class CharacterAdmissionBuildPatch { private static bool Prefix(Player __instance) { if (!CharacterAdmissionFeature.IsLocked || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } return false; } } internal static class ChatRestrictionFeature { private static readonly HashSet BlockedCommands = new HashSet(StringComparer.OrdinalIgnoreCase) { "printseeds" }; internal static void Patch(Harmony harmony) { if (harmony == null) { return; } MethodInfo methodInfo = AccessTools.Method(typeof(ChatRestrictionFeature), "InputPrefix", (Type[])null, (Type[])null); if (methodInfo == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"BlutEid Command-Block: Prefix nicht gefunden."); } return; } int num = 0; HashSet seen = new HashSet(); num += PatchInputText(harmony, AccessTools.TypeByName("Terminal"), methodInfo, seen); num += PatchInputText(harmony, AccessTools.TypeByName("Chat"), methodInfo, seen); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("BlutEid Command-Block aktiv: " + num + " InputText-Methode(n) gepatcht.")); } } private static int PatchInputText(Harmony harmony, Type type, MethodInfo prefix, HashSet seen) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown if (type == null) { return 0; } MethodInfo methodInfo = AccessTools.Method(type, "InputText", (Type[])null, (Type[])null); if (methodInfo == null || !seen.Add(methodInfo)) { return 0; } try { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(prefix), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return 1; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("BlutEid Command-Block: Patch fuer " + type.Name + ".InputText fehlgeschlagen: " + ex.Message)); } return 0; } } private static bool InputPrefix(object __instance) { string text = ReadInputText(__instance); if (string.IsNullOrWhiteSpace(text)) { return true; } string text2 = text.Trim(); if (__instance != null && string.Equals(__instance.GetType().Name, "Chat", StringComparison.Ordinal) && !text2.StartsWith("/", StringComparison.Ordinal)) { return true; } while (text2.StartsWith("/", StringComparison.Ordinal)) { text2 = text2.Substring(1).TrimStart(Array.Empty()); } if (text2.Length == 0) { return true; } int num = text2.IndexOfAny(new char[4] { ' ', '\t', '\r', '\n' }); string text3 = ((num >= 0) ? text2.Substring(0, num) : text2); if (!BlockedCommands.Contains(text3)) { return true; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("BlutEid: blockierter Command: " + text3)); } if ((Object)(object)Player.m_localPlayer != (Object)null) { ((Character)Player.m_localPlayer).Message((MessageType)2, "/" + text3 + " ist in BlutEid deaktiviert.", 0, (Sprite)null); } return false; } private static string ReadInputText(object instance) { if (instance == null) { return null; } Type type = instance.GetType(); FieldInfo fieldInfo = null; while (type != null && fieldInfo == null) { fieldInfo = type.GetField("m_input", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); type = type.BaseType; } if (fieldInfo == null) { return null; } object value = fieldInfo.GetValue(instance); if (value == null) { return null; } PropertyInfo property = value.GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property == null) { return null; } return property.GetValue(value, null) as string; } } internal sealed class CombatGoalFeature : MonoBehaviour { private sealed class Participant { internal Player Player; internal float Seconds; internal float OutsideSeconds; internal float MaxOutsideSeconds; internal bool DamagedBoss; internal bool Died; internal bool Prepared; } private sealed class BossFight { internal Character Boss; internal string NetworkId; internal string BossKey; internal Vector3 Center; internal bool PortalViolation; internal readonly Dictionary Players = new Dictionary(); } private sealed class LocalBossObservation { internal ZDOID BossId; internal string BossKey; internal Vector3 Position; internal float Seconds; internal bool Damaged; internal float LastSentAt; } private sealed class DangerRun { internal string Biome; internal float StartedAt; internal float Seconds; internal float Distance; internal Vector3 LastPosition; internal int Kills; internal bool Died; } private sealed class TombstoneWatch { internal TombStone Tombstone; internal long OwnerId; internal readonly Dictionary HelperSeconds = new Dictionary(); } private const string TombstoneOpenedRpc = "ChallengeHub_RPC_CombatRescueTombstoneOpened_v1"; private const string BossObservationRpc = "ChallengeHub_RPC_BossObservation_v1"; private const string BossDeathRpc = "ChallengeHub_RPC_BossDeath_v1"; private const string BossPlayerDeathRpc = "ChallengeHub_RPC_BossPlayerDeath_v1"; private const string TrophyPickupRpc = "ChallengeHub_RPC_TrophyPickup_v1"; private static Plugin _plugin; private static CombatGoalFeature _instance; private static readonly Dictionary Fights = new Dictionary(); private static readonly Dictionary NetworkFights = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary LocalBossObservations = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly HashSet CompletedNetworkBosses = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary DangerRuns = new Dictionary(); private static readonly Dictionary Tombstones = new Dictionary(); private static readonly Dictionary> ParticipatedBosses = new Dictionary>(); private static readonly HashSet Sent = new HashSet(StringComparer.OrdinalIgnoreCase); private static ZRoutedRpc _registeredRpcInstance; private float _nextTick; private float _nextLocalBossTick; internal static void Initialize(Plugin plugin) { _plugin = plugin; _instance = ((Component)plugin).gameObject.GetComponent() ?? ((Component)plugin).gameObject.AddComponent(); } private void Update() { EnsureRpcRegistered(); if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { TickLocalBosses(); } if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && !(Time.realtimeSinceStartup < _nextTick)) { _nextTick = Time.realtimeSinceStartup + 1f; TickBosses(); TickDangerRuns(); TickTombstones(); } } internal static void NotifyBossDamage(Character boss, Player attacker) { if ((Object)(object)ZNet.instance == (Object)null || !IsBoss(boss) || (Object)(object)attacker == (Object)null) { return; } if (ZNet.instance.IsServer()) { GetParticipant(GetFight(boss), attacker).DamagedBoss = true; } else if (((Character)attacker).IsOwner() && !((Object)(object)attacker != (Object)(object)Player.m_localPlayer)) { LocalBossObservation localBossObservation = ObserveLocalBoss(boss); if (localBossObservation != null) { localBossObservation.Damaged = true; SendBossObservation(attacker, localBossObservation, immediate: true); } } } internal static void NotifyBossDeath(Character boss) { if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)boss == (Object)null || !IsBoss(boss)) { return; } string networkId = BossNetworkId(boss); if (ZNet.instance.IsServer()) { CompleteBossOnce(networkId, GetFight(boss)); Fights.Remove(((Object)boss).GetInstanceID()); return; } ZNetView component = ((Component)boss).GetComponent(); if (!((Object)(object)component == (Object)null) && component.IsOwner() && !((Object)(object)Player.m_localPlayer == (Object)null)) { LocalBossObservation localBossObservation = ObserveLocalBoss(boss); if (localBossObservation != null) { SendBossObservation(Player.m_localPlayer, localBossObservation, immediate: true); } SendBossDeath(Player.m_localPlayer, boss); } } private void TickLocalBosses() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) if (Time.realtimeSinceStartup < _nextLocalBossTick || (Object)(object)Player.m_localPlayer == (Object)null) { return; } _nextLocalBossTick = Time.realtimeSinceStartup + 1f; Character[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Character val in array) { if (!IsBoss(val) || Vector3.Distance(((Component)Player.m_localPlayer).transform.position, ((Component)val).transform.position) > 120f) { continue; } LocalBossObservation localBossObservation = ObserveLocalBoss(val); if (localBossObservation != null) { localBossObservation.Seconds += 1f; localBossObservation.Position = ((Component)val).transform.position; if (Time.realtimeSinceStartup - localBossObservation.LastSentAt >= 5f) { SendBossObservation(Player.m_localPlayer, localBossObservation, immediate: false); } } } } private static LocalBossObservation ObserveLocalBoss(Character boss) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) ZNetView val = (((Object)(object)boss != (Object)null) ? ((Component)boss).GetComponent() : null); ZDO val2 = (((Object)(object)val != (Object)null) ? val.GetZDO() : null); if (val2 == null || !val2.IsValid()) { return null; } string key = ((object)Unsafe.As(ref val2.m_uid)/*cast due to .constrained prefix*/).ToString(); if (!LocalBossObservations.TryGetValue(key, out var value)) { value = new LocalBossObservation { BossId = val2.m_uid, BossKey = BossKey(boss), Position = ((Component)boss).transform.position }; LocalBossObservations[key] = value; } return value; } private static void SendBossObservation(Player player, LocalBossObservation observation, bool immediate) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_0026: 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) if (!((Object)(object)player == (Object)null) && observation != null && ZRoutedRpc.instance != null) { long num = ServerPeerId(); if (num != 0L) { ZPackage val = new ZPackage(); val.Write(observation.BossId); val.Write(observation.BossKey); val.Write(observation.Position); val.Write(player.GetPlayerID()); val.Write(player.GetPlayerName() ?? string.Empty); val.Write(observation.Seconds); val.Write(observation.Damaged); ZRoutedRpc.instance.InvokeRoutedRPC(num, "ChallengeHub_RPC_BossObservation_v1", new object[1] { val }); observation.LastSentAt = Time.realtimeSinceStartup; } } } private static void SendBossDeath(Player reporter, Character boss) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_0054: 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) ZNetView val = (((Object)(object)boss != (Object)null) ? ((Component)boss).GetComponent() : null); ZDO val2 = (((Object)(object)val != (Object)null) ? val.GetZDO() : null); if ((Object)(object)reporter == (Object)null || val2 == null || !val2.IsValid() || ZRoutedRpc.instance == null) { return; } long num = ServerPeerId(); if (num != 0L) { ZPackage val3 = new ZPackage(); val3.Write(val2.m_uid); val3.Write(BossKey(boss)); val3.Write(((Component)boss).transform.position); val3.Write(reporter.GetPlayerID()); val3.Write(reporter.GetPlayerName() ?? string.Empty); ZRoutedRpc.instance.InvokeRoutedRPC(num, "ChallengeHub_RPC_BossDeath_v1", new object[1] { val3 }); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Boss-Tod serverseitig gemeldet: " + BossKey(boss) + ".")); } } } internal static void NotifyPlayerDeath(Player player) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)player == (Object)null) { return; } if (!ZNet.instance.IsServer()) { if (((Character)player).IsOwner() && ZRoutedRpc.instance != null) { long num = ServerPeerId(); if (num != 0L) { ZPackage val = new ZPackage(); val.Write(player.GetPlayerID()); val.Write(player.GetPlayerName() ?? string.Empty); ZRoutedRpc.instance.InvokeRoutedRPC(num, "ChallengeHub_RPC_BossPlayerDeath_v1", new object[1] { val }); } } } else { MarkPlayerDeath(player); } } internal static void RequestServerTrophyValidation(Player player, string trophyName) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_0083: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || string.IsNullOrWhiteSpace(trophyName) || ZRoutedRpc.instance == null || (Object)(object)ZNet.instance == (Object)null) { return; } if (ZNet.instance.IsServer()) { Plugin.Instance?.ReportServerValidatedTrophy(player, trophyName); return; } long num = ServerPeerId(); if (num != 0L) { ZPackage val = new ZPackage(); val.Write(player.GetPlayerID()); val.Write(player.GetPlayerName() ?? string.Empty); val.Write(trophyName); val.Write(((Component)player).transform.position); ZRoutedRpc.instance.InvokeRoutedRPC(num, "ChallengeHub_RPC_TrophyPickup_v1", new object[1] { val }); } } private static void MarkPlayerDeath(Player player) { if ((Object)(object)player == (Object)null) { return; } long playerID = player.GetPlayerID(); foreach (BossFight value4 in Fights.Values) { if (value4.Players.TryGetValue(playerID, out var value)) { value.Died = true; } } foreach (BossFight item in NetworkFights.Values.Distinct()) { if (item.Players.TryGetValue(playerID, out var value2)) { value2.Died = true; } } if (DangerRuns.TryGetValue(playerID, out var value3)) { value3.Died = true; } } internal static void NotifyPortalViolation(string biome) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) foreach (BossFight value in Fights.Values) { if (string.Equals(Plugin.CurrentBiome(value.Center), biome, StringComparison.OrdinalIgnoreCase)) { value.PortalViolation = true; } } } internal static void NotifyDangerKill(Player player, string biome) { if (!((Object)(object)player == (Object)null) && Dangerous(biome)) { DangerRun dangerRun = GetDangerRun(player, biome); dangerRun.Kills++; TryCompleteDanger(player, dangerRun); } } private static void TickBosses() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) Character[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Character val in array) { if (!IsBoss(val)) { continue; } BossFight fight = GetFight(val); fight.Center = ((Component)val).transform.position; foreach (Player allPlayer in Player.GetAllPlayers()) { if (!((Object)(object)allPlayer == (Object)null)) { Participant value; if (Vector3.Distance(((Component)allPlayer).transform.position, fight.Center) <= 120f) { value = GetParticipant(fight, allPlayer); value.Seconds += 1f; value.OutsideSeconds = 0f; } else if (fight.Players.TryGetValue(allPlayer.GetPlayerID(), out value)) { value.OutsideSeconds += 1f; value.MaxOutsideSeconds = Mathf.Max(value.MaxOutsideSeconds, value.OutsideSeconds); } } } } } private static BossFight GetFight(Character boss) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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_0089: Unknown result type (might be due to invalid IL or missing references) int instanceID = ((Object)boss).GetInstanceID(); if (!Fights.TryGetValue(instanceID, out var value)) { string text = BossNetworkId(boss); if (string.IsNullOrWhiteSpace(text) || !NetworkFights.TryGetValue(text, out value)) { value = new BossFight { NetworkId = text, Boss = boss, BossKey = BossKey(boss), Center = ((Component)boss).transform.position }; } else { value.Boss = boss; value.BossKey = BossKey(boss); value.Center = ((Component)boss).transform.position; } Fights[instanceID] = value; if (!string.IsNullOrWhiteSpace(text)) { NetworkFights[text] = value; } } return value; } private static BossFight GetNetworkFight(string networkId, string bossKey, Vector3 position) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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) if (!NetworkFights.TryGetValue(networkId, out var value)) { value = new BossFight { NetworkId = networkId, BossKey = Plugin.CanonicalBossKey(bossKey), Center = position }; NetworkFights[networkId] = value; } value.BossKey = Plugin.CanonicalBossKey(bossKey); value.Center = position; return value; } private static Participant GetParticipant(BossFight fight, Player player) { long playerID = player.GetPlayerID(); if (!fight.Players.TryGetValue(playerID, out var value)) { value = new Participant { Player = player, Prepared = IsPrepared(player) }; fight.Players[playerID] = value; } return value; } private static void CompleteFight(BossFight fight) { if (fight == null) { return; } foreach (Participant value2 in fight.Players.Values) { Player player = value2.Player; if (!((Object)(object)player == (Object)null) && (value2.DamagedBoss || !(value2.Seconds < 60f))) { string bossKey = fight.BossKey; long playerID = player.GetPlayerID(); if (!ParticipatedBosses.TryGetValue(playerID, out var value)) { value = (ParticipatedBosses[playerID] = new HashSet(StringComparer.OrdinalIgnoreCase)); } value.Add(fight.BossKey); _plugin.SendEvent("fighter_boss_participation", player, new Dictionary { { "boss", fight.BossKey }, { "seconds", Mathf.RoundToInt(value2.Seconds) }, { "damagedBoss", value2.DamagedBoss }, { "goalLocallyObserved", true } }); if (value.Count >= 3) { Award(player, "boss_chain_3", "global", new Dictionary { { "boss", fight.BossKey }, { "distinctBosses", value.Count }, { "seconds", Mathf.RoundToInt(value2.Seconds) }, { "damagedBoss", value2.DamagedBoss } }); } if (!value2.Died) { Award(player, "deathless_boss", bossKey); } if (value2.Prepared && value2.Seconds >= 60f) { Award(player, "team_boss_ready", bossKey); } if (!fight.PortalViolation && value2.MaxOutsideSeconds <= 45f && value2.Seconds >= 60f) { Award(player, "boss_arena_defense", bossKey); } } } } private static void CompleteBossOnce(string networkId, BossFight fight) { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) string text = (string.IsNullOrWhiteSpace(networkId) ? ((fight?.BossKey ?? "unknown") + ":" + Mathf.RoundToInt(fight?.Center.x ?? 0f) + ":" + Mathf.RoundToInt(fight?.Center.z ?? 0f)) : networkId); if (CompletedNetworkBosses.Add(text)) { CompleteFight(fight); if (fight != null) { BossArenaResetFeature.Queue(fight.Center, fight.BossKey, text); } if (!string.IsNullOrWhiteSpace(networkId)) { NetworkFights.Remove(networkId); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Bossabschluss serverautoritär verarbeitet: " + (fight?.BossKey ?? "unknown") + "; Beteiligte=" + (fight?.Players.Count ?? 0) + ".")); } } } private static void TickDangerRuns() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) float realtimeSinceStartup = Time.realtimeSinceStartup; foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer == (Object)null) { continue; } string biome = Plugin.NormalizeKey(Plugin.CurrentBiome(((Component)allPlayer).transform.position)); if (!Dangerous(biome)) { DangerRuns.Remove(allPlayer.GetPlayerID()); continue; } DangerRun dangerRun = GetDangerRun(allPlayer, biome); float num = HorizontalDistance(dangerRun.LastPosition, ((Component)allPlayer).transform.position); if (num <= 60f) { dangerRun.Distance += num; } dangerRun.LastPosition = ((Component)allPlayer).transform.position; dangerRun.Seconds += 1f; if (realtimeSinceStartup - dangerRun.StartedAt > 1800f) { ResetDangerRun(dangerRun, allPlayer, biome, realtimeSinceStartup); } TryCompleteDanger(allPlayer, dangerRun); } } private static DangerRun GetDangerRun(Player player, string biome) { long playerID = player.GetPlayerID(); if (!DangerRuns.TryGetValue(playerID, out var value) || !string.Equals(value.Biome, biome, StringComparison.OrdinalIgnoreCase)) { value = new DangerRun(); DangerRuns[playerID] = value; ResetDangerRun(value, player, biome, Time.realtimeSinceStartup); } return value; } private static void ResetDangerRun(DangerRun run, Player player, string biome, float now) { //IL_0039: 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) run.Biome = biome; run.StartedAt = now; run.Seconds = 0f; run.Distance = 0f; run.Kills = 0; run.Died = false; run.LastPosition = ((Component)player).transform.position; } private static void TryCompleteDanger(Player player, DangerRun run) { if (!run.Died && run.Kills >= 25 && run.Seconds >= 600f && run.Distance >= 400f) { Award(player, "danger_biome_clear", run.Biome); } } private static void TickTombstones() { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) TombStone[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (TombStone val in array) { if ((Object)(object)val == (Object)null) { continue; } int instanceID = ((Object)val).GetInstanceID(); if (!Tombstones.TryGetValue(instanceID, out var value)) { value = new TombstoneWatch { Tombstone = val, OwnerId = OwnerId(val) }; Tombstones[instanceID] = value; } if (value.OwnerId == 0L) { value.OwnerId = OwnerId(val); } foreach (Player allPlayer in Player.GetAllPlayers()) { if (!((Object)(object)allPlayer == (Object)null) && allPlayer.GetPlayerID() != value.OwnerId) { if (Vector3.Distance(((Component)allPlayer).transform.position, ((Component)val).transform.position) <= 30f) { value.HelperSeconds[allPlayer.GetPlayerID()] = HelperSeconds(value, allPlayer.GetPlayerID()) + 1f; } else { value.HelperSeconds[allPlayer.GetPlayerID()] = 0f; } } } } } internal static void NotifyTombstoneOpened(TombStone stone, Player owner) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)stone == (Object)null || (Object)(object)owner == (Object)null) { return; } ZNetView component = ((Component)stone).GetComponent(); ZDO val = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); if (val == null || !val.IsValid()) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Rettungspruefung: Grabstein besitzt keine gueltige ZDO-ID."); } } else if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { if (ZRoutedRpc.instance == null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"Rettungspruefung: Grabstein-Oeffnung kann ohne ZRoutedRpc nicht an den Server gemeldet werden."); } return; } long num = ServerPeerId(); if (num == 0L) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"Rettungspruefung: Server-Peer-ID konnte nicht bestimmt werden."); } return; } ZPackage val2 = new ZPackage(); val2.Write(val.m_uid); val2.Write(owner.GetPlayerID()); val2.Write(owner.GetPlayerName() ?? string.Empty); val2.Write(((Component)stone).transform.position); ZRoutedRpc.instance.InvokeRoutedRPC(num, "ChallengeHub_RPC_CombatRescueTombstoneOpened_v1", new object[1] { val2 }); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)"Rettungspruefung: eigene Grabstein-Oeffnung serverseitig angefordert."); } } else { EvaluateTombstoneRescue(stone, owner); } } private static void EvaluateTombstoneRescue(TombStone stone, Player owner) { //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) if (!Tombstones.TryGetValue(((Object)stone).GetInstanceID(), out var value)) { value = new TombstoneWatch { Tombstone = stone, OwnerId = OwnerId(stone) }; Tombstones[((Object)stone).GetInstanceID()] = value; } if (value.OwnerId == 0L) { value.OwnerId = OwnerId(stone); } if (value.OwnerId != 0L && owner.GetPlayerID() != value.OwnerId) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Rettungspruefung abgelehnt: Oeffner ist nicht der Grabsteinbesitzer."); } return; } int num = 0; foreach (Player allPlayer in Player.GetAllPlayers()) { if (!((Object)(object)allPlayer == (Object)null) && allPlayer.GetPlayerID() != owner.GetPlayerID() && !(Vector3.Distance(((Component)allPlayer).transform.position, ((Component)stone).transform.position) > 30f) && !((Character)allPlayer).IsDead() && HelperSeconds(value, allPlayer.GetPlayerID()) >= 60f) { Award(allPlayer, "rescue_run", owner.GetPlayerID() + ":" + ((Object)stone).GetInstanceID()); num++; } } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Rettungspruefung abgeschlossen: Besitzer=" + owner.GetPlayerName() + "; qualifizierte Helfer=" + num + "; beobachtete Helfer=" + value.HelperSeconds.Count + ".")); } } private static void EnsureRpcRegistered() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance == null || _registeredRpcInstance == instance) { return; } try { instance.Register("ChallengeHub_RPC_CombatRescueTombstoneOpened_v1", (Action)RPC_TombstoneOpened); instance.Register("ChallengeHub_RPC_BossObservation_v1", (Action)RPC_BossObservation); instance.Register("ChallengeHub_RPC_BossDeath_v1", (Action)RPC_BossDeath); instance.Register("ChallengeHub_RPC_BossPlayerDeath_v1", (Action)RPC_BossPlayerDeath); instance.Register("ChallengeHub_RPC_TrophyPickup_v1", (Action)RPC_TrophyPickup); _registeredRpcInstance = instance; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Kampf-Goal-RPCs für serverautoritäre Bosswertung und Grabsteinrettung registriert."); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Kampf-Goal-RPCs konnten nicht registriert werden: " + ex.Message)); } } } private unsafe static void RPC_BossObservation(long sender, ZPackage package) { //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_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_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) if (package == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } ZDOID val; string text; Vector3 val2; long playerId; string playerName; float num; bool flag; try { val = package.ReadZDOID(); text = Plugin.CanonicalBossKey(package.ReadString()); val2 = package.ReadVector3(); playerId = package.ReadLong(); playerName = package.ReadString(); num = package.ReadSingle(); flag = package.ReadBool(); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Bossbeobachtung abgelehnt: ungültige RPC-Daten (" + ex.Message + ").")); } return; } if (!KnownBossKey(text) || !DungeonTalentRewardFeature.ValidateSenderOwnsPlayer(sender, playerId)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"Bossbeobachtung abgelehnt: Boss oder Peer-/Spielerbindung ungültig."); } return; } Player val3 = FindServerPlayer(playerId, playerName); ZDOMan instance = ZDOMan.instance; ZDO val4 = ((instance != null) ? instance.GetZDO(val) : null); if (!((Object)(object)val3 == (Object)null) && ValidateBossZdo(val4, text) && !(Vector3.Distance(val4.GetPosition(), val2) > 12f) && !(Vector3.Distance(((Component)val3).transform.position, val2) > 150f)) { string text2 = ((object)(*(ZDOID*)(&val))/*cast due to .constrained prefix*/).ToString(); if (!CompletedNetworkBosses.Contains(text2)) { Participant participant = GetParticipant(GetNetworkFight(text2, text, val4.GetPosition()), val3); participant.Seconds = Mathf.Max(participant.Seconds, Mathf.Clamp(num, 0f, 7200f)); participant.DamagedBoss |= flag; } } } private unsafe static void RPC_BossDeath(long sender, ZPackage package) { //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_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_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) if (package == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } ZDOID val; string text; Vector3 val2; long playerId; string playerName; try { val = package.ReadZDOID(); text = Plugin.CanonicalBossKey(package.ReadString()); val2 = package.ReadVector3(); playerId = package.ReadLong(); playerName = package.ReadString(); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Bossabschluss abgelehnt: ungültige RPC-Daten (" + ex.Message + ").")); } return; } if (!KnownBossKey(text) || !DungeonTalentRewardFeature.ValidateSenderOwnsPlayer(sender, playerId)) { return; } Player val3 = FindServerPlayer(playerId, playerName); string text2 = ((object)(*(ZDOID*)(&val))/*cast due to .constrained prefix*/).ToString(); NetworkFights.TryGetValue(text2, out var value); ZDOMan instance = ZDOMan.instance; ZDO val4 = ((instance != null) ? instance.GetZDO(val) : null); bool flag = ValidateBossZdo(val4, text) && Vector3.Distance(val4.GetPosition(), val2) <= 12f; bool flag2 = value != null && Vector3.Distance(value.Center, val2) <= 12f; if ((Object)(object)val3 == (Object)null || (!flag && !flag2) || Vector3.Distance(((Component)val3).transform.position, val2) > 200f) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"Bossabschluss abgelehnt: Boss-ZDO oder Reporterposition unplausibel."); } return; } BossFight fight = value ?? GetNetworkFight(text2, text, flag ? val4.GetPosition() : val2); Participant participant = GetParticipant(fight, val3); if (!participant.DamagedBoss && participant.Seconds < 1f) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"Bossabschluss abgelehnt: Reporter besitzt keine vorherige Bossbeobachtung."); } } else { CompleteBossOnce(text2, fight); } } private static void RPC_BossPlayerDeath(long sender, ZPackage package) { if (package == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } long playerId; string playerName; try { playerId = package.ReadLong(); playerName = package.ReadString(); } catch { return; } if (DungeonTalentRewardFeature.ValidateSenderOwnsPlayer(sender, playerId)) { Player val = FindServerPlayer(playerId, playerName); if ((Object)(object)val != (Object)null) { MarkPlayerDeath(val); } } } private static void RPC_TrophyPickup(long sender, ZPackage package) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) if (package == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } long playerId; string playerName; string text; Vector3 val; try { playerId = package.ReadLong(); playerName = package.ReadString(); text = package.ReadString(); val = package.ReadVector3(); } catch { return; } if (DungeonTalentRewardFeature.ValidateSenderOwnsPlayer(sender, playerId)) { Player val2 = FindServerPlayer(playerId, playerName); if (!((Object)(object)val2 == (Object)null) && !(Vector3.Distance(((Component)val2).transform.position, val) > 8f) && !string.IsNullOrWhiteSpace(KillAttributionTracker.TrophySpecies(text))) { Plugin.Instance?.ReportServerValidatedTrophy(val2, text); } } } private static Player FindServerPlayer(long playerId, string playerName) { return ((IEnumerable)Player.GetAllPlayers()).FirstOrDefault((Func)((Player player) => (Object)(object)player != (Object)null && player.GetPlayerID() == playerId && (string.IsNullOrWhiteSpace(playerName) || string.Equals(player.GetPlayerName(), playerName, StringComparison.OrdinalIgnoreCase)))); } private static void RPC_TombstoneOpened(long sender, ZPackage package) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) if (package == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } ZDOID requestedId; long ownerId; string text; Vector3 val; try { requestedId = package.ReadZDOID(); ownerId = package.ReadLong(); text = package.ReadString(); val = package.ReadVector3(); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Rettungspruefung abgelehnt: ungueltige RPC-Daten (" + ex.Message + ").")); } return; } if (!DungeonTalentRewardFeature.ValidateSenderOwnsPlayer(sender, ownerId)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"Rettungspruefung abgelehnt: Peer und Grabstein-Oeffner stimmen nicht ueberein."); } return; } Player val2 = ((IEnumerable)Player.GetAllPlayers()).FirstOrDefault((Func)((Player player) => (Object)(object)player != (Object)null && player.GetPlayerID() == ownerId)); if ((Object)(object)val2 == (Object)null || ((Character)val2).IsDead()) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"Rettungspruefung abgelehnt: Grabsteinbesitzer ist serverseitig nicht aktiv."); } return; } if (!string.IsNullOrWhiteSpace(text) && !string.Equals(val2.GetPlayerName(), text, StringComparison.OrdinalIgnoreCase)) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)"Rettungspruefung abgelehnt: Spielername stimmt nicht mit der Serverinstanz ueberein."); } return; } TombStone val3 = FindTombstone(requestedId, val); if ((Object)(object)val3 == (Object)null) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogWarning((object)"Rettungspruefung abgelehnt: gemeldeter Grabstein wurde serverseitig nicht gefunden."); } return; } if (Vector3.Distance(((Component)val3).transform.position, val) > 3f || Vector3.Distance(((Component)val2).transform.position, ((Component)val3).transform.position) > 8f) { ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogWarning((object)"Rettungspruefung abgelehnt: Grabstein- oder Spielerposition ist unplausibel."); } return; } long num = OwnerId(val3); if (num != 0L && num != ownerId) { ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogWarning((object)"Rettungspruefung abgelehnt: serverseitiger Grabsteinbesitzer stimmt nicht ueberein."); } return; } if (num == 0L) { string text2 = string.Empty; try { text2 = val3.GetOwnerName() ?? string.Empty; } catch { } if (string.IsNullOrWhiteSpace(text2) || !string.Equals(text2, val2.GetPlayerName(), StringComparison.OrdinalIgnoreCase)) { ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogWarning((object)"Rettungspruefung abgelehnt: Grabsteinbesitz konnte serverseitig nicht bestaetigt werden."); } return; } } EvaluateTombstoneRescue(val3, val2); } private static TombStone FindTombstone(ZDOID requestedId, Vector3 reportedPosition) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) TombStone result = null; float num = 3f; TombStone[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (TombStone val in array) { if (!((Object)(object)val == (Object)null)) { ZNetView component = ((Component)val).GetComponent(); ZDO val2 = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); if (val2 != null && val2.IsValid() && ((ZDOID)(ref val2.m_uid)).Equals(requestedId)) { return val; } float num2 = Vector3.Distance(((Component)val).transform.position, reportedPosition); if (num2 < num) { result = val; num = num2; } } } return result; } private static long ServerPeerId() { try { MethodInfo methodInfo = AccessTools.Method(((object)ZRoutedRpc.instance)?.GetType(), "GetServerPeerID", (Type[])null, (Type[])null) ?? AccessTools.Method(((object)ZRoutedRpc.instance)?.GetType(), "GetServerPeerId", (Type[])null, (Type[])null); return (methodInfo != null) ? Convert.ToInt64(methodInfo.Invoke(ZRoutedRpc.instance, null)) : 0; } catch { return 0L; } } private static float HelperSeconds(TombstoneWatch watch, long id) { if (!watch.HelperSeconds.TryGetValue(id, out var value)) { return 0f; } return value; } private static void Award(Player player, string goal, string scope, Dictionary extra = null) { //IL_00c9: 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) if ((Object)(object)_plugin == (Object)null || (Object)(object)player == (Object)null || ((!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer()) && !CharacterAdmissionFeature.ChallengeScoringAllowed)) { return; } string item = player.GetPlayerID() + ":" + goal + ":" + scope; if (Sent.Add(item)) { Dictionary dictionary = extra ?? new Dictionary(); dictionary["playstyle"] = "fighter"; dictionary["goal"] = goal; dictionary["scope"] = scope; dictionary["attributionMethod"] = "combat_phase_2_9_24"; dictionary["biome"] = Plugin.CurrentBiome(((Component)player).transform.position); dictionary["position"] = Plugin.SerializeVector(((Component)player).transform.position); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("ChallengeHub Kampf-Goal erkannt: fighter/" + goal + "; Scope=" + scope)); } _plugin.SendEvent("playstyle_goal", player, dictionary); } } private static bool IsPrepared(Player player) { try { int num = CollectionCount(AccessTools.Field(((object)player).GetType(), "m_foods")?.GetValue(player)); int num2 = 0; string[] array = new string[4] { "m_chestItem", "m_legItem", "m_helmetItem", "m_shoulderItem" }; foreach (string text in array) { if (AccessTools.Field(((object)player).GetType(), text)?.GetValue(player) != null) { num2++; } } object obj = AccessTools.Field(((object)player).GetType(), "m_rightItem")?.GetValue(player) ?? AccessTools.Field(((object)player).GetType(), "m_leftItem")?.GetValue(player); return num >= 3 && num2 >= 3 && obj != null && HasRested(player); } catch { return false; } } private static bool HasRested(Player player) { try { object sEMan = ((Character)player).GetSEMan(); if (!(AccessTools.Field(sEMan.GetType(), "m_statusEffects")?.GetValue(sEMan) is IEnumerable enumerable)) { return false; } foreach (object item in enumerable) { if (item != null && item.ToString().ToLowerInvariant().Contains("rested")) { return true; } } } catch { } return false; } private static int CollectionCount(object value) { if (!(value is ICollection collection)) { return 0; } return collection.Count; } private static long OwnerId(TombStone stone) { try { MethodInfo methodInfo = AccessTools.Method(((object)stone).GetType(), "GetOwner", (Type[])null, (Type[])null); if (methodInfo != null) { return Convert.ToInt64(methodInfo.Invoke(stone, new object[0])); } } catch { } return 0L; } private static string BossNetworkId(Character boss) { try { ZNetView val = (((Object)(object)boss != (Object)null) ? ((Component)boss).GetComponent() : null); ZDO val2 = (((Object)(object)val != (Object)null) ? val.GetZDO() : null); return (val2 != null && val2.IsValid()) ? ((object)Unsafe.As(ref val2.m_uid)/*cast due to .constrained prefix*/).ToString() : string.Empty; } catch { return string.Empty; } } private static string BossKey(Character boss) { if ((Object)(object)boss == (Object)null) { return "unknown"; } return Plugin.CanonicalBossKey(Plugin.NormalizeKey(boss.m_name) + " " + Plugin.NormalizeKey(((Object)boss).name)); } private static bool KnownBossKey(string key) { string text = Plugin.CanonicalBossKey(key); switch (text) { default: return text == "fader"; case "eikthyr": case "elder": case "bonemass": case "moder": case "yagluth": case "queen": return true; } } private static bool ValidateBossZdo(ZDO zdo, string bossKey) { if (zdo == null || !zdo.IsValid() || (Object)(object)ZNetScene.instance == (Object)null) { return false; } try { GameObject prefab = ZNetScene.instance.GetPrefab(zdo.GetPrefab()); Character val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent() : null); return IsBoss(val) && string.Equals(BossKey(val), Plugin.CanonicalBossKey(bossKey), StringComparison.OrdinalIgnoreCase); } catch { return false; } } private static bool IsBoss(Character character) { try { if ((Object)(object)character != (Object)null && character.m_boss) { return true; } } catch { } return false; } private static bool Dangerous(string biome) { string text = Plugin.NormalizeKey(biome); if (!text.Contains("swamp") && !text.Contains("mountain") && !text.Contains("plain") && !text.Contains("mist")) { return text.Contains("ash"); } return true; } private static float HorizontalDistance(Vector3 a, Vector3 b) { //IL_0000: 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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) float num = a.x - b.x; float num2 = a.z - b.z; return Mathf.Sqrt(num * num + num2 * num2); } } [HarmonyPatch(typeof(TombStone), "Interact")] internal static class CombatRescueTombstonePatch { private static void Postfix(TombStone __instance, Humanoid character, bool __result) { if (!__result) { return; } try { CombatGoalFeature.NotifyTombstoneOpened(__instance, (Player)(object)((character is Player) ? character : null)); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Rettungspruefung fehlgeschlagen: " + ex.Message)); } } } } internal static class CommunityProjectContainerFeature { internal const string StationKey = "ChallengeHub.ProjectStore.Station"; internal const string RevisionKey = "ChallengeHub.ProjectStore.Revision"; internal const string BaselineKey = "ChallengeHub.ProjectStore.Baseline"; internal const string CreditedPrefix = "ChallengeHub.ProjectStore.Credited."; private static ChallengeHubWorldStationMarker _pending; private static float _pendingAt; internal static bool TryHandleHotkey(Player player) { //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || !((Character)player).IsOwner() || (!Input.GetKey((KeyCode)306) && !Input.GetKey((KeyCode)305)) || !Input.GetKeyDown((KeyCode)107)) { return false; } GameObject val = Plugin.Instance?.FindPlayerHoverObject(player); if ((Object)(object)val == (Object)null) { return false; } ChallengeHubWorldStationMarker componentInParent = val.GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null && string.Equals(componentInParent.StationType, "project_store", StringComparison.OrdinalIgnoreCase)) { _pending = componentInParent; _pendingAt = Time.realtimeSinceStartup; ((Character)player).Message((MessageType)2, "Projektlager ausgewählt. Jetzt Kiste ansehen und STRG+K drücken.", 0, (Sprite)null); return true; } Container componentInParent2 = val.GetComponentInParent(); if ((Object)(object)componentInParent2 == (Object)null || (Object)(object)_pending == (Object)null || Time.realtimeSinceStartup - _pendingAt > 60f) { return false; } if (Vector3.Distance(((Component)componentInParent2).transform.position, ((Component)_pending).transform.position) > 25f) { ((Character)player).Message((MessageType)2, "Die Projektkiste muss höchstens 25 Meter vom Projektlager entfernt sein.", 0, (Sprite)null); return true; } ZNetView component = ((Component)componentInParent2).GetComponent(); ZNetView component2 = ((Component)_pending).GetComponent(); ZDO val2 = ((component != null) ? component.GetZDO() : null); ZDO val3 = ((component2 != null) ? component2.GetZDO() : null); if (val2 == null || val3 == null) { ((Character)player).Message((MessageType)2, "Kiste oder Projektlager besitzt keine gültige Netzwerk-ID.", 0, (Sprite)null); return true; } string zdoIdString = Plugin.GetZdoIdString((Component)(object)_pending); val2.Set("ChallengeHub.ProjectStore.Station", zdoIdString); val2.Set("ChallengeHub.ProjectStore.Revision", Math.Max(1, val2.GetInt("ChallengeHub.ProjectStore.Revision", 0) + 1)); val2.Set("ChallengeHub.ProjectStore.Baseline", 0); CommunityProjectContainerMonitor.Attach(componentInParent2); Plugin.Instance.SendEvent("project_container_bound", player, new Dictionary { { "stationId", zdoIdString }, { "containerZdo", Plugin.GetZdoIdString((Component)(object)componentInParent2) }, { "evidenceId", "project-bind:" + zdoIdString + ":" + Plugin.GetZdoIdString((Component)(object)componentInParent2) + ":" + val2.GetInt("ChallengeHub.ProjectStore.Revision", 1) }, { "position", Plugin.SerializeVector(((Component)componentInParent2).transform.position) } }); ((Character)player).Message((MessageType)2, "Projektkiste gebunden. Neue Einlagerungen werden serverseitig gezählt.", 0, (Sprite)null); _pending = null; return true; } } internal sealed class CommunityProjectContainerMonitor : MonoBehaviour { private Container _container; private float _next; internal static void Attach(Container container) { if ((Object)(object)container != (Object)null && (Object)(object)((Component)container).GetComponent() == (Object)null) { ((Component)container).gameObject.AddComponent(); } } private void Awake() { _container = ((Component)this).GetComponent(); Capture(report: false); } private void Update() { if (!(Time.time < _next)) { _next = Time.time + 2f; if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer()) { Capture(report: true); } } } private void Capture(bool report) { //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) Container container = _container; object obj; if (container == null) { obj = null; } else { ZNetView component = ((Component)container).GetComponent(); obj = ((component != null) ? component.GetZDO() : null); } ZDO val = (ZDO)obj; if (val == null || string.IsNullOrWhiteSpace(val.GetString("ChallengeHub.ProjectStore.Station", string.Empty))) { return; } Inventory inventory = _container.GetInventory(); Dictionary dictionary = (from i in ((inventory != null) ? inventory.GetAllItems() : null) ?? new List() group i by Plugin.NormalizeKey(i.m_shared.m_name)).ToDictionary, string, int>((IGrouping g) => g.Key, (IGrouping g) => g.Sum((ItemData i) => Math.Max(1, i.m_stack)), StringComparer.OrdinalIgnoreCase); if (val.GetInt("ChallengeHub.ProjectStore.Baseline", 0) == 0) { foreach (KeyValuePair item in dictionary) { val.Set(StringExtensionMethods.GetStableHashCode("ChallengeHub.ProjectStore.Credited." + item.Key), item.Value, false); } val.Set("ChallengeHub.ProjectStore.Baseline", 1); } else { if (!report) { return; } foreach (KeyValuePair item2 in dictionary) { string text = "ChallengeHub.ProjectStore.Credited." + item2.Key; int num = val.GetInt(StringExtensionMethods.GetStableHashCode(text), 0); int num2 = item2.Value - num; if (num2 > 0) { int num3 = Math.Max(1, val.GetInt("ChallengeHub.ProjectStore.Revision", 1) + 1); val.Set("ChallengeHub.ProjectStore.Revision", num3); val.Set(StringExtensionMethods.GetStableHashCode(text), item2.Value, false); Player val2 = CharacterOnDeathPatch.FindNearestPlayer(((Component)this).transform.position); if ((Object)(object)val2 != (Object)null) { Plugin.Instance.SendEvent("project_container_delta", val2, new Dictionary { { "stationId", val.GetString("ChallengeHub.ProjectStore.Station", string.Empty) }, { "containerZdo", Plugin.GetZdoIdString((Component)(object)_container) }, { "item", item2.Key }, { "quantity", num2 }, { "evidenceId", "project-delta:" + Plugin.GetZdoIdString((Component)(object)_container) + ":" + num3 }, { "position", Plugin.SerializeVector(((Component)this).transform.position) }, { "attributionMethod", "persistent_container_highwater_2_12_0" } }); } } } } } } [HarmonyPatch(typeof(Container), "Awake")] internal static class CommunityProjectContainerAwakePatch { private static void Postfix(Container __instance) { object obj; if (__instance == null) { obj = null; } else { ZNetView component = ((Component)__instance).GetComponent(); obj = ((component != null) ? component.GetZDO() : null); } ZDO val = (ZDO)obj; if (val != null && !string.IsNullOrWhiteSpace(val.GetString("ChallengeHub.ProjectStore.Station", string.Empty))) { CommunityProjectContainerMonitor.Attach(__instance); } } } internal sealed class CommunityWorldStationDefinition { public string Type; public string SystemKey; public string PrefabName; public string DisplayName; public string Description; public Color Color; } internal static class CommunityWorldStationFeature { internal const string TypeKey = "ChallengeHub.WorldStation.Type"; internal const string OwnerKey = "ChallengeHub.WorldStation.Owner"; internal const string CreatedKey = "ChallengeHub.WorldStation.CreatedUtc"; internal const string RevisionKey = "ChallengeHub.WorldStation.Revision"; private static Plugin _plugin; internal static readonly CommunityWorldStationDefinition[] Definitions = new CommunityWorldStationDefinition[10] { new CommunityWorldStationDefinition { Type = "notice_board", SystemKey = "weekly_board", PrefabName = "piece_challengehub_notice_board", DisplayName = "ChallengeHub-Anschlagtafel", Description = "Zeigt Wochenaufträge und angekündigte Weltereignisse.", Color = new Color(0.83f, 0.67f, 0.33f, 1f) }, new CommunityWorldStationDefinition { Type = "project_stone", SystemKey = "community_projects", PrefabName = "piece_challengehub_project_stone", DisplayName = "ChallengeHub-Projektstein", Description = "Markiert den Mittelpunkt eines gemeinsamen Bauprojekts.", Color = new Color(0.94f, 0.72f, 0.18f, 1f) }, new CommunityWorldStationDefinition { Type = "discovery_board", SystemKey = "rumors", PrefabName = "piece_challengehub_discovery_board", DisplayName = "ChallengeHub-Entdeckertafel", Description = "Meldet Fundhinweise und macht unabhängig bestätigte Orte sichtbar.", Color = new Color(0.23f, 0.58f, 1f, 1f) }, new CommunityWorldStationDefinition { Type = "contest_board", SystemKey = "rivalries", PrefabName = "piece_challengehub_contest_board", DisplayName = "ChallengeHub-Wettbewerbstafel", Description = "Zeigt Regeln, Teilnehmer und serverbestätigten Rangstand.", Color = new Color(0.86f, 0.25f, 0.18f, 1f) }, new CommunityWorldStationDefinition { Type = "expedition_board", SystemKey = "expeditions", PrefabName = "piece_challengehub_expedition_board", DisplayName = "ChallengeHub-Expeditionstafel", Description = "Treffpunkt für Anmeldung, Aufbruch, Route und Rückkehr.", Color = new Color(0.24f, 0.78f, 0.72f, 1f) }, new CommunityWorldStationDefinition { Type = "hunt_board", SystemKey = "hunt_board", PrefabName = "piece_challengehub_hunt_board", DisplayName = "ChallengeHub-Jagdtafel", Description = "Öffentliche Jagdaufträge mit serverbestätigten letzten Treffern.", Color = new Color(0.72f, 0.18f, 0.14f, 1f) }, new CommunityWorldStationDefinition { Type = "exhibition_board", SystemKey = "build_exhibitions", PrefabName = "piece_challengehub_exhibition_board", DisplayName = "ChallengeHub-Ausstellungstafel", Description = "Verbindet Bauwerke, F10-Bilder und die faire Bewertung.", Color = new Color(0.75f, 0.42f, 0.88f, 1f) }, new CommunityWorldStationDefinition { Type = "project_store", SystemKey = "delivery_projects", PrefabName = "piece_challengehub_project_store", DisplayName = "ChallengeHub-Projektlager", Description = "Bindepunkt für öffentlich benötigte Materialien und Lieferbeiträge.", Color = new Color(0.35f, 0.82f, 0.3f, 1f) }, new CommunityWorldStationDefinition { Type = "glory_stone", SystemKey = "hall_of_glory", PrefabName = "piece_challengehub_glory_stone", DisplayName = "ChallengeHub-Ruhmesstein", Description = "Mittelpunkt der physischen Trophäen-Ruhmeshalle.", Color = new Color(1f, 0.8f, 0.24f, 1f) }, new CommunityWorldStationDefinition { Type = "council_stone", SystemKey = "community_council", PrefabName = "piece_challengehub_council_stone", DisplayName = "ChallengeHub-Ratsstein", Description = "Versammlungsort für Vorschläge, Diskussionen und verbindliche Stimmen.", Color = new Color(0.92f, 0.92f, 0.98f, 1f) } }; private static readonly Dictionary Labels = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "notice_board", "Anschlagtafel" }, { "project_stone", "Projektstein" }, { "discovery_board", "Entdeckertafel" }, { "contest_board", "Wettbewerbstafel" }, { "expedition_board", "Expeditionstafel" }, { "hunt_board", "Jagdtafel" }, { "exhibition_board", "Ausstellungstafel" }, { "project_store", "Projektlager" }, { "glory_stone", "Ruhmesstein" }, { "council_stone", "Ratsstein" } }; internal static void Initialize(Plugin plugin) { _plugin = plugin; } internal static bool IsKnown(string type) { if (!string.IsNullOrWhiteSpace(type)) { return Labels.ContainsKey(type); } return false; } internal static string Label(string type) { if (!Labels.TryGetValue(type ?? string.Empty, out var value)) { return "Community-Station"; } return value; } private static Player ReportingPlayer(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return CharacterOnDeathPatch.FindNearestPlayer(position) ?? Player.m_localPlayer; } internal static void ReportPlaced(Piece piece) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_plugin == (Object)null || (Object)(object)piece == (Object)null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } ChallengeHubWorldStationMarker component = ((Component)piece).GetComponent(); if ((Object)(object)component == (Object)null || !IsKnown(component.StationType)) { return; } Player val = ReportingPlayer(((Component)piece).transform.position); if (!((Object)(object)val == (Object)null)) { ZNetView component2 = ((Component)piece).GetComponent(); ZDO val2 = (((Object)(object)component2 != (Object)null) ? component2.GetZDO() : null); if (val2 != null) { string zdoIdString = Plugin.GetZdoIdString((Component)(object)component); int revision = Math.Max(1, val2.GetInt("ChallengeHub.WorldStation.Revision", 1)); _plugin.SendEvent("world_station_placed", val, StationPayload(component, val2, zdoIdString, revision, "station-place:" + zdoIdString + ":" + revision)); } } } internal static void ReportRemoved(Piece piece) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_plugin == (Object)null || (Object)(object)piece == (Object)null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } ChallengeHubWorldStationMarker component = ((Component)piece).GetComponent(); if ((Object)(object)component == (Object)null || !IsKnown(component.StationType)) { return; } Player val = ReportingPlayer(((Component)piece).transform.position); if (!((Object)(object)val == (Object)null)) { ZNetView component2 = ((Component)piece).GetComponent(); ZDO val2 = (((Object)(object)component2 != (Object)null) ? component2.GetZDO() : null); if (val2 != null) { string zdoIdString = Plugin.GetZdoIdString((Component)(object)component); int revision = Math.Max(2, val2.GetInt("ChallengeHub.WorldStation.Revision", 1) + 1); _plugin.SendEvent("world_station_removed", val, StationPayload(component, val2, zdoIdString, revision, "station-remove:" + zdoIdString + ":" + revision)); } } } private static Dictionary StationPayload(ChallengeHubWorldStationMarker station, ZDO zdo, string stationId, int revision, string evidenceId) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: 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) string value = zdo.GetString("ChallengeHub.WorldStation.Type", station.StationType); return new Dictionary { { "systemKey", station.SystemKey }, { "stationType", value }, { "stationId", stationId }, { "stationRevision", revision }, { "stationOwnerPlayerId", zdo.GetLong("ChallengeHub.WorldStation.Owner", 0L).ToString() }, { "position", new Dictionary { { "x", ((Component)station).transform.position.x }, { "y", ((Component)station).transform.position.y }, { "z", ((Component)station).transform.position.z } } }, { "evidenceId", evidenceId } }; } internal static void ReportInteraction(ChallengeHubWorldStationMarker station, Player player) { //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_plugin == (Object)null) && !((Object)(object)station == (Object)null) && !((Object)(object)player == (Object)null)) { ZNetView component = ((Component)station).GetComponent(); ZDO val = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); string text = station.StationType; if (val != null) { text = val.GetString("ChallengeHub.WorldStation.Type", text); } if (IsKnown(text)) { _plugin.SendEvent("world_station_interaction", player, new Dictionary { { "systemKey", station.SystemKey }, { "stationType", text }, { "stationId", (val != null) ? Plugin.GetZdoIdString((Component)(object)station) : ((Object)((Component)station).gameObject).GetInstanceID().ToString() }, { "stationRevision", (val == null) ? 1 : val.GetInt("ChallengeHub.WorldStation.Revision", 1) }, { "stationOwnerPlayerId", (val != null) ? val.GetLong("ChallengeHub.WorldStation.Owner", 0L).ToString() : player.GetPlayerID().ToString() }, { "position", new Dictionary { { "x", ((Component)station).transform.position.x }, { "y", ((Component)station).transform.position.y }, { "z", ((Component)station).transform.position.z } } }, { "evidenceId", "station-open:" + text + ":" + ((val != null) ? Plugin.GetZdoIdString((Component)(object)station) : ((Object)((Component)station).gameObject).GetInstanceID().ToString()) + ":" + player.GetPlayerID() + ":" + DateTime.UtcNow.Ticks } }); GoalProgressFeature.OpenCommunityStation(station.SystemKey); QoLSkillRuntimeFeature.ShowCenter(Label(text) + " geöffnet. Der passende F6-Bereich wurde geöffnet."); } } } } internal sealed class ChallengeHubWorldStationMarker : MonoBehaviour, Hoverable, Interactable { public string StationType = string.Empty; public string SystemKey = string.Empty; private ZNetView _view; private float _nextProjectScan; private void Awake() { _view = ((Component)this).GetComponent(); ZDO val = (((Object)(object)_view != (Object)null) ? _view.GetZDO() : null); if (val == null) { return; } string text = val.GetString("ChallengeHub.WorldStation.Type", string.Empty); if (!string.IsNullOrWhiteSpace(text)) { StationType = text; } if (CommunityWorldStationFeature.IsKnown(StationType) && _view.IsOwner()) { val.Set("ChallengeHub.WorldStation.Type", StationType); if (val.GetLong("ChallengeHub.WorldStation.CreatedUtc", 0L) == 0L) { val.Set("ChallengeHub.WorldStation.CreatedUtc", DateTime.UtcNow.Ticks); } if (val.GetLong("ChallengeHub.WorldStation.Owner", 0L) == 0L && (Object)(object)Player.m_localPlayer != (Object)null) { val.Set("ChallengeHub.WorldStation.Owner", Player.m_localPlayer.GetPlayerID()); } if (val.GetInt("ChallengeHub.WorldStation.Revision", 0) <= 0) { val.Set("ChallengeHub.WorldStation.Revision", 1); } } } private void Update() { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) if (!string.Equals(StationType, "project_stone", StringComparison.OrdinalIgnoreCase) || Time.time < _nextProjectScan || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } _nextProjectScan = Time.time + 60f; if ((Object)(object)_view == (Object)null || !_view.IsValid() || _view.GetZDO() == null) { return; } Player val = CharacterOnDeathPatch.FindNearestPlayer(((Component)this).transform.position); if (!((Object)(object)val == (Object)null) && !(Vector3.Distance(((Component)val).transform.position, ((Component)this).transform.position) > 90f)) { int num = Object.FindObjectsByType((FindObjectsSortMode)0).Count((Piece piece) => (Object)(object)piece != (Object)null && (Object)(object)piece != (Object)(object)((Component)this).GetComponent() && piece.GetCreator() != 0L && Vector3.Distance(((Component)piece).transform.position, ((Component)this).transform.position) <= 80f); ZDO zDO = _view.GetZDO(); int num2 = Math.Max(1, zDO.GetInt(StringExtensionMethods.GetStableHashCode("ChallengeHub.ProjectStone.ScanRevision"), 0) + 1); zDO.Set(StringExtensionMethods.GetStableHashCode("ChallengeHub.ProjectStone.LastCount"), num, false); zDO.Set(StringExtensionMethods.GetStableHashCode("ChallengeHub.ProjectStone.ScanRevision"), num2, false); Plugin.Instance.SendEvent("community_project_snapshot", val, new Dictionary { { "stationId", Plugin.GetZdoIdString((Component)(object)this) }, { "value", num }, { "target", 50 }, { "scope", "existing_player_pieces" }, { "evidenceId", "project-snapshot:" + Plugin.GetZdoIdString((Component)(object)this) + ":" + num2 }, { "position", Plugin.SerializeVector(((Component)this).transform.position) }, { "attributionMethod", "server_existing_piece_snapshot_2_12_0" } }); } } public string GetHoverName() { return CommunityWorldStationFeature.Label(StationType); } public string GetHoverText() { return CommunityWorldStationFeature.Label(StationType) + "\n[$KEY_Use] Öffnen\nFortschritt wird serverseitig aus echten Spielereignissen ermittelt."; } public bool Interact(Humanoid user, bool hold, bool alt) { if (hold || alt) { return false; } Player val = (Player)(object)((user is Player) ? user : null); if ((Object)(object)val == (Object)null || !CommunityWorldStationFeature.IsKnown(StationType)) { return false; } CommunityWorldStationFeature.ReportInteraction(this, val); return true; } public bool UseItem(Humanoid user, ItemData item) { return false; } } internal sealed class ConfigurationManagerAttributes { public bool? Browsable; public bool? IsAdvanced; public int? Order; } internal static class ConfigManagerCompatibilityFeature { private static readonly HashSet VisibleSettings = new HashSet(StringComparer.OrdinalIgnoreCase) { Key("ChallengeHub", "ApiBaseUrl"), Key("ChallengeHub", "ApiKey"), Key("ChallengeHub", "ChallengeShortCode"), Key("ChallengeHub", "DefaultDifficulty"), Key("PlayerLink", "TwitchLogin"), Key("PlayerLink", "PlayerLinkCode"), Key("OBSReplay", "Enabled"), Key("OBSReplay", "Address"), Key("OBSReplay", "Port"), Key("OBSReplay", "Password"), Key("Server", "ServerId"), Key("Server", "WorldName"), Key("Gameplay", "EnableNoMap"), Key("Gameplay", "DisableBossVegvisir"), Key("Gameplay", "EnableDamageScaling"), Key("Map", "EnableLargeMap"), Key("Map", "EnableMinimap"), Key("Map", "RevealFogWhileWalking"), Key("Map", "RevealFogAtCartographyTable"), Key("GuardianStones", "EnableGuardianStones"), Key("GuardianStones", "EnableCustomGuardianStonePieces"), Key("GuardianStones", "DefaultRadius"), Key("GuardianStones", "EnableIndestructibleGuardianStones"), Key("GuardianStones", "NotifyOnPreventedDamage"), Key("GuardianStone.Reset", "EnableResetGuardian"), Key("GuardianStone.Reset", "DefaultRadius"), Key("GuardianStone.Reset", "ConfirmSeconds"), Key("GuardianStone.Reset", "RequireOwner"), Key("GuardianStone.Reset", "ReportEvents"), Key("ProtectedArea", "EnableProtectedAreaHud"), Key("ProtectedArea", "ProtectedAreaHudRadius"), Key("ProtectedArea", "ProtectedAreaHudIntervalSeconds"), Key("ProtectedArea", "ProtectedAreaHudCenterOnEnter"), Key("ProtectedArea", "CompactHud"), Key("ProtectedArea", "ShowCriteriaBreakdown"), Key("DungeonLifecycle", "EnableSafeDungeonLifecycle"), Key("DungeonLifecycle", "EnableAutomaticReset"), Key("DungeonLifecycle", "EmptyGraceSeconds"), Key("DungeonLifecycle", "GuardianEntranceSearchRadius"), Key("DungeonLifecycle", "WorkerSeconds"), Key("DungeonLifecycle", "BoundsPadding"), Key("DungeonLifecycle", "RandomizeLayoutOnReset"), Key("DungeonLifecycle", "EnableBackgroundEntranceRepair"), Key("TombstoneMap", "EnableTombstoneMapKnowledge"), Key("GuardianHover", "EnableGuardianHoverText"), Key("GuardianHover", "ShowControls"), Key("GuardianHover", "ShowFarmerAnimals"), Key("GuardianHover", "ShowFarmerFood"), Key("GuardianHover", "ShowFarmerChest"), Key("GuardianHover", "HoverRefreshSeconds"), Key("Advanced", "ConfigManagerSafeMode"), Key("Advanced", "PersistRemoteConfigToLocalFile") }; internal static void Apply(ConfigFile config, bool safeMode) { if (config == null || !safeMode) { return; } int num = 0; int num2 = 0; foreach (ConfigEntryBase entry in GetEntries(config)) { if (entry == null) { continue; } bool flag = VisibleSettings.Contains(Key(entry.Definition.Section, entry.Definition.Key)); if (SetBrowsable(entry, flag)) { if (flag) { num++; } else { num2++; } } } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Config-Manager Safe Mode aktiv: " + num + " Kernwerte sichtbar, " + num2 + " erweiterte Werte nur in de.challengehub.valheim.cfg. Verhindert das schwere 192-Eintraege-F1-Panel.")); } } private static IEnumerable GetEntries(ConfigFile config) { if (config == null) { yield break; } foreach (KeyValuePair item in config) { if (item.Value != null) { yield return item.Value; } } } private static bool SetBrowsable(ConfigEntryBase entry, bool browsable) { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown try { ConfigDescription description = entry.Description; List list = ((description != null && description.Tags != null) ? description.Tags.Where((object tag) => tag != null && tag.GetType().Name != "ConfigurationManagerAttributes" && !(tag is BrowsableAttribute)).ToList() : new List()); list.Add(new BrowsableAttribute(browsable)); list.Add(new ConfigurationManagerAttributes { Browsable = browsable, IsAdvanced = !browsable, Order = ((!browsable) ? (-10000) : 0) }); ConfigDescription val = new ConfigDescription((description != null) ? description.Description : string.Empty, (description != null) ? description.AcceptableValues : null, list.ToArray()); FieldInfo field = typeof(ConfigEntryBase).GetField("k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { field.SetValue(entry, val); return true; } PropertyInfo property = typeof(ConfigEntryBase).GetProperty("Description", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo methodInfo = ((property != null) ? property.GetSetMethod(nonPublic: true) : null); if (methodInfo != null) { methodInfo.Invoke(entry, new object[1] { val }); return true; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Config-Manager-Eintrag konnte nicht gefiltert werden: " + ((entry != null) ? entry.Definition.Section : null) + "." + ((entry != null) ? entry.Definition.Key : null) + " -> " + ex.Message)); } } return false; } private static string Key(string section, string name) { return (section ?? string.Empty) + "\u001f" + (name ?? string.Empty); } } internal static class DeathInventoryIntegrityFeature { internal sealed class DeathSnapshot { internal long PlayerId; internal string PlayerName; internal Vector3 Position; internal readonly Dictionary Items = new Dictionary(StringComparer.Ordinal); internal int Total; } internal static DeathSnapshot Capture(Player player) { //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) if ((Object)(object)player == (Object)null || !((Character)player).IsOwner()) { return null; } DeathSnapshot deathSnapshot = new DeathSnapshot { Position = ((Component)player).transform.position }; try { deathSnapshot.PlayerId = player.GetPlayerID(); } catch { } try { deathSnapshot.PlayerName = player.GetPlayerName() ?? string.Empty; } catch { deathSnapshot.PlayerName = string.Empty; } Inventory inventory = ((Humanoid)player).GetInventory(); foreach (ItemData item in ((inventory == null) ? null : inventory.GetAllItems()?.ToList()) ?? new List()) { if (item != null && item.m_stack > 0) { string key = ItemKey(item); deathSnapshot.Items[key] = (deathSnapshot.Items.TryGetValue(key, out var value) ? value : 0) + item.m_stack; deathSnapshot.Total += item.m_stack; } } return deathSnapshot; } internal static void VerifyAfterVanillaDeath(Player player, DeathSnapshot snapshot) { if (!((Object)(object)player == (Object)null) && snapshot != null && ((Character)player).IsOwner() && !((Object)(object)Plugin.Instance == (Object)null)) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(VerifyCoroutine(player, snapshot)); } } private static IEnumerator VerifyCoroutine(Player player, DeathSnapshot snapshot) { yield return null; try { SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan != null) { sEMan.RemoveAllStatusEffects(false); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Tod: Status-Effekte konnten nicht geleert werden: " + ex.Message)); } } if (snapshot.Total <= 0) { yield break; } TombStone verified = null; float deadline = Time.realtimeSinceStartup + 8f; while (Time.realtimeSinceStartup < deadline && (Object)(object)verified == (Object)null) { verified = ((IEnumerable)(from stone in Object.FindObjectsByType((FindObjectsSortMode)0) where IsOwnStone(stone, snapshot) && Vector3.Distance(((Component)stone).transform.position, snapshot.Position) <= 35f orderby Vector3.Distance(((Component)stone).transform.position, snapshot.Position) select stone)).FirstOrDefault((Func)delegate(TombStone stone) { Container component = ((Component)stone).GetComponent(); return ContainsSnapshot((component != null) ? component.GetInventory() : null, snapshot); }); if ((Object)(object)verified == (Object)null) { yield return (object)new WaitForSeconds(0.25f); } } Inventory inventory = ((Humanoid)player).GetInventory(); if ((Object)(object)verified == (Object)null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)"Tod-Inventarprüfung: vollständiger eigener Grabstein wurde nicht bestätigt; Charakterinventar wird aus Sicherheitsgründen nicht gelöscht."); } yield break; } if (!ContainsAnySnapshotItems(inventory, snapshot)) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)"Tod-Inventarprüfung: Vanilla-Übertragung war vollständig; keine Charakterkopie vorhanden."); } yield break; } try { ((Humanoid)player).UnequipAllItems(); } catch { } int num = 0; foreach (ItemData item in inventory.GetAllItems().ToList()) { if (item != null && snapshot.Items.ContainsKey(ItemKey(item))) { num += Math.Max(0, item.m_stack); inventory.RemoveItem(item); } } try { AccessTools.Method(((object)inventory).GetType(), "Changed", (Type[])null, (Type[])null)?.Invoke(inventory, null); } catch { } ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)("Tod-Inventarprüfung: doppelte Charakterkopie nach bestätigtem Grabstein entfernt; Einheiten=" + num + ".")); } } private static bool ContainsSnapshot(Inventory inventory, DeathSnapshot snapshot) { if (inventory == null) { return false; } Dictionary actual = InventoryCounts(inventory); int value; return snapshot.Items.All((KeyValuePair pair) => actual.TryGetValue(pair.Key, out value) && value >= pair.Value); } private static bool ContainsAnySnapshotItems(Inventory inventory, DeathSnapshot snapshot) { if (inventory == null) { return false; } return inventory.GetAllItems().Any((ItemData item) => item != null && item.m_stack > 0 && snapshot.Items.ContainsKey(ItemKey(item))); } private static Dictionary InventoryCounts(Inventory inventory) { Dictionary dictionary = new Dictionary(StringComparer.Ordinal); foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem != null && allItem.m_stack > 0) { string key = ItemKey(allItem); dictionary[key] = (dictionary.TryGetValue(key, out var value) ? value : 0) + allItem.m_stack; } } return dictionary; } private static string ItemKey(ItemData item) { string text = (((Object)(object)item?.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : (item?.m_shared?.m_name ?? "unknown")); return text + "|" + (item?.m_quality ?? 0) + "|" + (item?.m_variant ?? 0); } private static bool IsOwnStone(TombStone stone, DeathSnapshot snapshot) { if ((Object)(object)stone == (Object)null) { return false; } try { MethodInfo methodInfo = AccessTools.Method(((object)stone).GetType(), "GetOwner", (Type[])null, (Type[])null); if (methodInfo != null) { long num = Convert.ToInt64(methodInfo.Invoke(stone, Array.Empty())); if (num != 0L) { return num == snapshot.PlayerId; } } } catch { } try { return !string.IsNullOrWhiteSpace(snapshot.PlayerName) && string.Equals(stone.GetOwnerName(), snapshot.PlayerName, StringComparison.OrdinalIgnoreCase); } catch { return false; } } } internal sealed class DeathRunChronicleFeature : MonoBehaviour { private static Plugin _plugin; private readonly HashSet _reported = new HashSet(StringComparer.OrdinalIgnoreCase); private float _nextScan; private Biome _lastBiome; internal static void Initialize(Plugin plugin) { _plugin = plugin; if ((Object)(object)((Component)plugin).gameObject.GetComponent() == (Object)null) { ((Component)plugin).gameObject.AddComponent(); } } private void Update() { if (!(Time.realtimeSinceStartup < _nextScan)) { _nextScan = Time.realtimeSinceStartup + 2f; Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && ((Character)localPlayer).IsOwner() && CharacterAdmissionFeature.ChallengeScoringAllowed) { ExplorationEvidencePatch.ScanNearby(localPlayer); ObserveBiome(localPlayer); ObserveDungeon(localPlayer); } } } private unsafe void ObserveBiome(Player player) { //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_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_0015: 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Invalid comparison between Unknown and I4 Biome currentBiome = player.GetCurrentBiome(); if ((int)currentBiome != 0 && currentBiome != _lastBiome) { _lastBiome = currentBiome; string text = Plugin.NormalizeKey(((object)(*(Biome*)(¤tBiome))/*cast due to .constrained prefix*/).ToString()); if ((int)currentBiome != 1 && Once("biome:" + text)) { Chronicle(player, "new_biome_reached", "Neues Biom erreicht: " + ((object)(*(Biome*)(¤tBiome))/*cast due to .constrained prefix*/).ToString(), "biome:" + text, new Dictionary { { "biome", text }, { "discoveryType", "biome" } }); } } } private void ObserveDungeon(Player player) { //IL_002d: 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_008d: 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_00a9: 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_014b: Unknown result type (might be due to invalid IL or missing references) DungeonGenerator[] array; try { array = Object.FindObjectsOfType(); } catch { return; } DungeonGenerator[] array2 = array; foreach (DungeonGenerator val in array2) { if (!((Object)(object)val == (Object)null) && !(Vector3.Distance(((Component)player).transform.position, ((Component)val).transform.position) > 90f)) { string value = Plugin.NormalizeKey(((Object)((Component)val).gameObject).name + " " + ((Object)val).name); string text = DungeonType(value); if (string.IsNullOrWhiteSpace(text)) { text = "dungeon"; } Vector3 position = ((Component)val).transform.position; string text2 = text + ":" + Mathf.RoundToInt(position.x / 32f) + ":" + Mathf.RoundToInt(position.z / 32f); if (Once("location:" + text2)) { Chronicle(player, "dungeon_entered", DungeonLabel(text) + " betreten", text2, new Dictionary { { "locationType", text }, { "locationName", value }, { "biome", Plugin.CurrentBiome(((Component)player).transform.position) }, { "discoveryType", "dungeon" } }); } } } } private void Chronicle(Player player, string eventType, string label, string scope, Dictionary fields) { IngameCameraFeature.CaptureAutomaticChronicle(eventType, label, scope, fields); } private bool Once(string key) { return _reported.Add(Plugin.CurrentWorldUidString() + ":" + playerId() + ":" + key); } private static string playerId() { if (!((Object)(object)Player.m_localPlayer == (Object)null)) { return Player.m_localPlayer.GetPlayerID().ToString(); } return "none"; } private static string DungeonType(string value) { if (value.Contains("sunken") || value.Contains("crypt")) { return "sunken_crypt"; } if (value.Contains("burial") || value.Contains("tomb")) { return "burial_chambers"; } if (value.Contains("troll")) { return "troll_cave"; } if (value.Contains("frost") || value.Contains("mountaincave")) { return "frost_cave"; } if (value.Contains("infested") || value.Contains("mine")) { return "infested_mine"; } if (value.Contains("forestcrypt")) { return "burial_chambers"; } if (value.Contains("cave")) { return "cave"; } return string.Empty; } private static string DungeonLabel(string type) { return type switch { "sunken_crypt" => "Versunkene Krypta", "burial_chambers" => "Grabkammer", "troll_cave" => "Trollhöhle", "frost_cave" => "Frosthöhle", "infested_mine" => "Befallene Mine", "cave" => "Höhle", _ => "Verlies", }; } } internal sealed class DeathRunCounterFeature : MonoBehaviour { [Serializable] private sealed class RunEnvelope { public bool ok; public DeathRunState run; } [Serializable] internal sealed class DeathRunState { public string id; public string status; public string difficulty; public string competition; public string worldCode; public string worldUid; public string worldPreset; public int deathCount; public int bossIndex; public DeathRunCharacter[] characters; public string seedHash; public string rulesetVersion; public string worldPackageStatus; } [Serializable] internal sealed class DeathRunCharacter { public string id; public string name; public string userId; } private static readonly string[] Bosses = new string[7] { "eikthyr", "elder", "bonemass", "moder", "yagluth", "queen", "fader" }; private const float BossInventoryCheckRadius = 50f; private const float BossFightRadius = 150f; private static readonly string[][] TierTokens = new string[7][] { new string[12] { "wood", "stone", "flint", "leather", "deer", "boar", "neck", "resin", "feather", "honey", "raspberr", "mushroom" }, new string[10] { "hardantler", "copper", "tin", "bronze", "finewood", "corewood", "surtlingcore", "carrot", "troll", "ancientseed" }, new string[11] { "iron", "scrapiron", "elderbark", "guck", "root", "chain", "bloodbag", "entrail", "turnip", "witheredbone", "ooze" }, new string[8] { "silver", "obsidian", "wolf", "fenring", "crystal", "onion", "freezegland", "dragonegg" }, new string[9] { "blackmetal", "flax", "linen", "barley", "cloudberr", "lox", "needle", "tar", "totem" }, new string[11] { "eitr", "carapace", "mandible", "blackcore", "yggdrasil", "sap", "softtissue", "jotunpuff", "magecap", "royaljelly", "sealbreaker" }, new string[10] { "flametal", "asksvin", "grausten", "charred", "celestial", "sulfur", "proustite", "morgengland", "bellfragment", "vineberr" } }; private static readonly Dictionary ItemTierOverrides = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "arrowflint", 0 }, { "itemarrowflint", 0 } }; private static Plugin _plugin; private static DeathRunState _run; private static readonly Dictionary TierCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private static ObjectDB _consumeEffectCatalogSource; private static readonly Dictionary> ConsumeEffectItems = new Dictionary>(); private static readonly HashSet LoadedBossAltars = new HashSet(); private static bool _bossAltarCatalogInitialized; private float _nextPoll; private float _nextInventoryCheck; private float _nextIntegrityCheck; private long _sessionPlayerId; private Player _sessionPlayer; private bool _sessionReported; private bool _modsReported; private bool _catalogReported; private readonly Dictionary _lastViolationFingerprints = new Dictionary(StringComparer.Ordinal); private string _bossZoneKey = string.Empty; private bool _worldViolationReported; private static bool _bossFightActive; private static bool _bossFightInvalid; private static string _bossFightKey = string.Empty; private static Vector3 _bossFightCenter; private static readonly HashSet BossFightParticipants = new HashSet(StringComparer.Ordinal); internal static bool Enabled => string.Equals(Plugin.ChallengeShortCode?.Value, "BLUTEID", StringComparison.OrdinalIgnoreCase); internal static DeathRunState ActiveRun { get { if (_run == null || !(_run.status == "active")) { return null; } return _run; } } internal static bool GameplayReady { get { if (Enabled) { if (ActiveRun != null) { return string.Equals(ActiveRun.worldPackageStatus, "ready", StringComparison.OrdinalIgnoreCase); } return false; } return true; } } internal static string ExpectedBoss { get { if (ActiveRun == null || ActiveRun.bossIndex < 0 || ActiveRun.bossIndex >= Bosses.Length) { return string.Empty; } return Bosses[ActiveRun.bossIndex]; } } internal static string F10StatusLine { get { if (!Enabled) { return string.Empty; } if (ActiveRun == null) { return "Der Blut-Eid: Kein aktiver Run für diesen Charakter"; } string text = (string.IsNullOrWhiteSpace(ExpectedBoss) ? "abgeschlossen" : ExpectedBoss); return "Der Blut-Eid | " + ActiveRun.worldPreset + " | " + ActiveRun.worldCode + " | Tode: " + ActiveRun.deathCount + " | Nächster Boss: " + text; } } internal static void Initialize(Plugin plugin) { _plugin = plugin; if ((Object)(object)((Component)plugin).gameObject.GetComponent() == (Object)null) { ((Component)plugin).gameObject.AddComponent(); } } internal static void RequestImmediateRefresh() { DeathRunCounterFeature deathRunCounterFeature = (((Object)(object)_plugin != (Object)null) ? ((Component)_plugin).gameObject.GetComponent() : null); if ((Object)(object)deathRunCounterFeature != (Object)null) { deathRunCounterFeature._nextPoll = 0f; } } private IEnumerator Start() { while (true) { if (Enabled && (Object)(object)Player.m_localPlayer != (Object)null) { long playerID = Player.m_localPlayer.GetPlayerID(); if (_sessionPlayerId != playerID) { _sessionPlayerId = playerID; _sessionPlayer = Player.m_localPlayer; _sessionReported = false; _modsReported = false; _catalogReported = false; _worldViolationReported = false; _bossFightActive = false; _bossFightInvalid = false; _bossFightKey = string.Empty; _bossFightCenter = Vector3.zero; _bossZoneKey = string.Empty; _lastViolationFingerprints.Clear(); BossFightParticipants.Clear(); _run = null; _nextPoll = 0f; } if (Time.realtimeSinceStartup >= _nextPoll) { _nextPoll = Time.realtimeSinceStartup + 15f; yield return RefreshRun(Player.m_localPlayer); } if (ActiveRun != null && !_sessionReported) { IngameCameraFeature.CaptureAutomaticChronicle("session_started", "Challenge-Welt betreten", "world-enter:" + Plugin.CurrentWorldUidString(), RunFields()); _sessionReported = true; } if (ActiveRun != null && !_modsReported) { ReportLoadedMods(Player.m_localPlayer); _modsReported = true; } if (ActiveRun != null && !_catalogReported && (Object)(object)ObjectDB.instance != (Object)null) { ReportRuntimeCatalog(Player.m_localPlayer); _catalogReported = true; } if (ActiveRun != null) { ValidateBossZone(Player.m_localPlayer); } if (ActiveRun != null && _bossFightActive && Time.realtimeSinceStartup >= _nextInventoryCheck) { _nextInventoryCheck = Time.realtimeSinceStartup + 1f; ValidateActiveBossFight(); } if (ActiveRun != null && Time.realtimeSinceStartup >= _nextIntegrityCheck) { _nextIntegrityCheck = Time.realtimeSinceStartup + 10f; ValidateWorldAndPreset(Player.m_localPlayer); } } else if (Enabled && _sessionReported) { Player sessionPlayer = _sessionPlayer; if ((Object)(object)sessionPlayer != (Object)null) { _plugin.SendEvent("session_ended", sessionPlayer, RunFields()); } _sessionReported = false; _sessionPlayerId = 0L; _sessionPlayer = null; _run = null; } yield return (object)new WaitForSeconds(1f); } } private IEnumerator RefreshRun(Player player) { string url = Plugin.ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/api/valheim/deathrun?characterId=" + UnityWebRequest.EscapeURL(player.GetPlayerID().ToString()) + "&runtime=1"; for (int attempt = 0; attempt < 2; attempt++) { yield return ChallengeHubApiTokenFeature.EnsureAvailable(); UnityWebRequest request = UnityWebRequest.Get(url); try { ChallengeHubApiTokenFeature.ApplyAuthorization(request); request.timeout = 12; yield return request.SendWebRequest(); if ((int)request.result == 1) { RunEnvelope runEnvelope = null; try { runEnvelope = ChallengeHubJson.Deserialize(request.downloadHandler.text); } catch { } _run = ((runEnvelope != null && runEnvelope.ok) ? runEnvelope.run : null); break; } if (request.responseCode != 401 || attempt > 0) { break; } ChallengeHubApiTokenFeature.Invalidate("DeathRun-Status: HTTP 401"); } finally { ((IDisposable)request)?.Dispose(); } } } private void OnApplicationQuit() { if (Enabled && _sessionReported && (Object)(object)Player.m_localPlayer != (Object)null) { _plugin.SendEvent("session_ended", Player.m_localPlayer, RunFields()); } } internal static Dictionary RunFields() { return new Dictionary { { "deathRunId", (ActiveRun != null) ? ActiveRun.id : string.Empty }, { "deathRunWorldCode", (ActiveRun != null) ? ActiveRun.worldCode : string.Empty }, { "deathRunBossIndex", (ActiveRun != null) ? ActiveRun.bossIndex : (-1) }, { "expectedBoss", ExpectedBoss } }; } private void ValidateWorldAndPreset(Player player) { if (ActiveRun != null && !((Object)(object)player == (Object)null)) { string text = Plugin.CurrentWorldUidString(); bool flag = string.IsNullOrWhiteSpace(ActiveRun.worldUid) || string.Equals(text, ActiveRun.worldUid, StringComparison.Ordinal); string text2 = ReadWorldPresetEvidence(); bool flag2 = PresetEvidenceMatches(ActiveRun.worldPreset, text2); if (!(flag && flag2) && !_worldViolationReported) { _worldViolationReported = true; Dictionary dictionary = RunFields(); dictionary["actualWorldUid"] = text; dictionary["expectedWorldUid"] = ActiveRun.worldUid; dictionary["expectedPreset"] = ActiveRun.worldPreset; dictionary["observedPreset"] = text2; dictionary["message"] = ((!flag) ? "DeathRun wurde in einer anderen Welt gestartet." : "Die Vanilla-Weltmodifikatoren entsprechen nicht der gewählten DeathRun-Stufe."); _plugin.SendEvent("deathrun_world_violation", player, dictionary); ((Character)player).Message((MessageType)2, "DeathRunCounter: Welt oder Weltmodifikatoren sind ungültig.", 0, (Sprite)null); ChallengeHubServerGateFeature.RejectDeathRunWorld(Convert.ToString(dictionary["message"])); } } } private static string ReadWorldPresetEvidence() { List list = new List(); try { object value = Traverse.Create(typeof(Game)).Field("m_world").GetValue(); if (value == null && (Object)(object)ZNet.instance != (Object)null) { value = Traverse.Create((object)ZNet.instance).Field("m_world").GetValue(); } if (value != null) { FieldInfo[] fields = value.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { string text = fieldInfo.Name.ToLowerInvariant(); if (text.Contains("modifier") || text.Contains("preset") || text.Contains("difficulty")) { object value2 = fieldInfo.GetValue(value); list.Add(fieldInfo.Name + "=" + (value2 ?? "null")); } } } } catch { } return string.Join(";", list.ToArray()); } private static bool PresetEvidenceMatches(string expected, string evidence) { if (string.IsNullOrWhiteSpace(evidence)) { return true; } string text = (expected ?? string.Empty).ToLowerInvariant(); string text2 = evidence.ToLowerInvariant(); switch (text) { case "easy": return text2.Contains("easy"); case "hardcore": return text2.Contains("hardcore"); case "hard": if (text2.Contains("hard")) { return !text2.Contains("hardcore"); } return false; case "immersive": return text2.Contains("immersive"); default: return false; } } internal static bool ValidateInventory(Player player, bool report) { if (!Enabled || ActiveRun == null || (Object)(object)player == (Object)null) { return true; } List list = new List(); foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems()) { if (RequiredTier(allItem) > ActiveRun.bossIndex) { list.Add(ItemViolationLabel(allItem, "Inventar")); } } foreach (ItemData item in ActiveFoodItems(player)) { if (RequiredTier(item) > ActiveRun.bossIndex) { list.Add(ItemViolationLabel(item, "aktive Nahrung")); } } foreach (ItemData item2 in ActiveConsumeEffectItems(player)) { if (RequiredTier(item2) > ActiveRun.bossIndex) { list.Add(ItemViolationLabel(item2, "aktiver Trank/Met")); } } list = list.Distinct(StringComparer.OrdinalIgnoreCase).OrderBy((string result) => result, StringComparer.OrdinalIgnoreCase).ToList(); DeathRunCounterFeature deathRunCounterFeature = (((Object)(object)_plugin != (Object)null) ? ((Component)_plugin).gameObject.GetComponent() : null); string key = ActiveRun.id + "|" + player.GetPlayerID(); if (list.Count == 0) { deathRunCounterFeature?._lastViolationFingerprints.Remove(key); return true; } string text = string.Join(",", list); string value = string.Empty; if (report && (Object)(object)deathRunCounterFeature != (Object)null && (!deathRunCounterFeature._lastViolationFingerprints.TryGetValue(key, out value) || value != text)) { deathRunCounterFeature._lastViolationFingerprints[key] = text; Dictionary dictionary = RunFields(); dictionary["items"] = list.ToArray(); dictionary["message"] = "Nicht freigeschaltete Ausrüstung, Nahrung oder aktive Verbrauchseffekte: " + string.Join(", ", list.Take(8).ToArray()); _plugin.SendEvent("deathrun_progression_violation", player, dictionary); ((Character)player).Message((MessageType)2, "Blut-Eid: Unerlaubte Ausrüstung, Nahrung oder Trankwirkung", 0, (Sprite)null); } return false; } private void ValidateBossZone(Player player) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || ActiveRun == null || !TryFindBossAltar(((Component)player).transform.position, out var altar, out var boss)) { if (!string.IsNullOrEmpty(_bossZoneKey) && (Object)(object)player != (Object)null && ActiveRun != null) { _lastViolationFingerprints.Remove(ActiveRun.id + "|" + player.GetPlayerID()); } _bossZoneKey = string.Empty; return; } string text = boss + "|" + ((Object)altar).GetInstanceID(); if (!string.Equals(_bossZoneKey, text, StringComparison.Ordinal)) { _bossZoneKey = text; _lastViolationFingerprints.Remove(ActiveRun.id + "|" + player.GetPlayerID()); } ValidateInventory(player, report: true); } private static bool TryFindBossAltar(Vector3 position, out OfferingBowl altar, out string boss) { //IL_0071: 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) altar = null; boss = string.Empty; float num = 50f; EnsureBossAltarCatalog(); LoadedBossAltars.RemoveWhere((OfferingBowl candidate) => (Object)(object)candidate == (Object)null); foreach (OfferingBowl loadedBossAltar in LoadedBossAltars) { if ((Object)(object)loadedBossAltar == (Object)null) { continue; } string text = BossKeyForBowl(loadedBossAltar); if (Bosses.Contains(text)) { float num2 = Vector3.Distance(position, ((Component)loadedBossAltar).transform.position); if (!(num2 > num)) { num = num2; altar = loadedBossAltar; boss = text; } } } return (Object)(object)altar != (Object)null; } internal static void RegisterBossAltar(OfferingBowl altar) { if ((Object)(object)altar != (Object)null) { LoadedBossAltars.Add(altar); } } private static void EnsureBossAltarCatalog() { if (_bossAltarCatalogInitialized) { return; } _bossAltarCatalogInitialized = true; try { OfferingBowl[] array = Object.FindObjectsByType((FindObjectsSortMode)0); for (int i = 0; i < array.Length; i++) { RegisterBossAltar(array[i]); } } catch { } } private static IEnumerable ActiveFoodItems(Player player) { List list = new List(); try { if (!(AccessTools.Field(((object)player).GetType(), "m_foods")?.GetValue(player) is IEnumerable enumerable)) { return list; } foreach (object item in enumerable) { ItemData val = (ItemData)((item != null) ? /*isinst with value type is only supported in some contexts*/: null); if (val != null) { list.Add(val); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Blut-Eid: Aktive Nahrung konnte nicht geprüft werden: " + ex.Message)); } } return list; } private static IEnumerable ActiveConsumeEffectItems(Player player) { List list = new List(); try { if (((player != null) ? ((Character)player).GetSEMan() : null) == null || (Object)(object)ObjectDB.instance == (Object)null) { return list; } EnsureConsumeEffectCatalog(); foreach (StatusEffect statusEffect in ((Character)player).GetSEMan().GetStatusEffects()) { if (!((Object)(object)statusEffect == (Object)null) && ConsumeEffectItems.TryGetValue(statusEffect.NameHash(), out var value)) { list.AddRange(value); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Blut-Eid: Aktive Trank- und Met-Effekte konnten nicht geprüft werden: " + ex.Message)); } } return list; } private static void EnsureConsumeEffectCatalog() { if (_consumeEffectCatalogSource == ObjectDB.instance) { return; } _consumeEffectCatalogSource = ObjectDB.instance; ConsumeEffectItems.Clear(); if ((Object)(object)ObjectDB.instance == (Object)null) { return; } foreach (GameObject item in ObjectDB.instance.m_items) { ItemDrop val = (((Object)(object)item != (Object)null) ? item.GetComponent() : null); ItemData val2 = (((Object)(object)val != (Object)null) ? val.m_itemData : null); StatusEffect val3 = val2?.m_shared?.m_consumeStatusEffect; if (!((Object)(object)val3 == (Object)null)) { int key = val3.NameHash(); if (!ConsumeEffectItems.TryGetValue(key, out var value)) { value = (ConsumeEffectItems[key] = new List()); } value.Add(val2); } } } private static string ItemViolationLabel(ItemData item, string source) { return ((item != null) ? QoLSkillRuntimeFeature.LocalizedName(item) : "Unbekannter Gegenstand") + " (" + source + ")"; } internal static bool CanUse(Player player, ItemData item) { return true; } internal static bool CanCraft(Player player, Recipe recipe) { return true; } internal static bool CanBuild(Player player, Piece piece) { return true; } internal static bool CanProcess(Player player, object station, ItemData input) { return true; } private static void ValidateActiveBossFight() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (!_bossFightActive || ActiveRun == null) { return; } Character val = ((IEnumerable)Character.GetAllCharacters()).FirstOrDefault((Func)((Character candidate) => !((Object)(object)candidate == (Object)null) && candidate.m_boss && string.Equals(Plugin.CanonicalBossKey(Plugin.NormalizeKey(candidate.m_name) + " " + Plugin.NormalizeKey(((Object)candidate).name)), _bossFightKey, StringComparison.OrdinalIgnoreCase))); if ((Object)(object)val != (Object)null) { _bossFightCenter = ((Component)val).transform.position; } foreach (Player item in from value in Player.GetAllPlayers() where (Object)(object)value != (Object)null && Vector3.Distance(((Component)value).transform.position, _bossFightCenter) <= 150f select value) { BossFightParticipants.Add(item.GetPlayerID().ToString()); if (!ValidateInventory(item, report: false)) { _bossFightInvalid = true; ValidateInventory(item, report: true); } } } internal static bool FinishBossFight(string boss, IEnumerable participants) { //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) bool num = _bossFightActive && string.Equals(_bossFightKey, boss, StringComparison.OrdinalIgnoreCase); bool flag = participants?.All((Player candidate) => ValidateInventory(candidate, report: true)) ?? true; bool result = num && !_bossFightInvalid && flag; _bossFightActive = false; _bossFightKey = string.Empty; _bossFightCenter = Vector3.zero; BossFightParticipants.Clear(); return result; } private void ReportLoadedMods(Player player) { try { string[] source = new string[7] { "de.challengehub", "azuanticheat", "azumatt.azuanticheat", "serversync", "jsondotnet", "newtonsoftjsondetector", "bepinex" }; bool flag = false; bool flag2 = false; List list = new List(); foreach (KeyValuePair pluginInfo in Chainloader.PluginInfos) { string guid = pluginInfo.Key ?? string.Empty; PluginInfo value = pluginInfo.Value; object obj; if (value == null) { obj = null; } else { BepInPlugin metadata = value.Metadata; obj = ((metadata == null) ? null : metadata.Version?.ToString()); } if (obj == null) { obj = "unknown"; } string text = (string)obj; string text2 = "unavailable"; try { PluginInfo value2 = pluginInfo.Value; string text3 = ((value2 == null) ? null : ((object)value2.Instance)?.GetType().Assembly.Location); if (!string.IsNullOrWhiteSpace(text3) && File.Exists(text3)) { using FileStream inputStream = File.OpenRead(text3); using SHA256 sHA = SHA256.Create(); text2 = BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", "").ToLowerInvariant(); } } catch { } list.Add(guid + "@" + text + "#" + text2); if (guid.IndexOf("azuanticheat", StringComparison.OrdinalIgnoreCase) >= 0) { flag = true; flag2 = string.Equals(text, "4.3.11", StringComparison.OrdinalIgnoreCase); } if (!source.Any((string token) => guid.IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0)) { Dictionary dictionary = RunFields(); dictionary["item"] = guid; dictionary["message"] = "Nicht freigegebene Zusatz-Mod geladen: " + guid; _plugin.SendEvent("deathrun_mod_violation", player, dictionary); } } list.Sort(StringComparer.OrdinalIgnoreCase); string s = string.Join("\n", list.ToArray()); string value3; using (SHA256 sHA2 = SHA256.Create()) { value3 = BitConverter.ToString(sHA2.ComputeHash(Encoding.UTF8.GetBytes(s))).Replace("-", "").ToLowerInvariant(); } Dictionary dictionary2 = RunFields(); dictionary2["antiCheat"] = "AzuAntiCheat"; dictionary2["antiCheatPresent"] = flag; dictionary2["antiCheatVersionAllowed"] = flag2; dictionary2["modManifestHash"] = value3; dictionary2["modCount"] = list.Count; _plugin.SendEvent("deathrun_anticheat_attestation", player, dictionary2); if (!flag || !flag2) { Dictionary dictionary3 = RunFields(); dictionary3["item"] = "AzuAntiCheat"; dictionary3["message"] = ((!flag) ? "Pflichtmod AzuAntiCheat fehlt auf diesem DeathRun-Client." : "AzuAntiCheat muss exakt in Version 4.3.11 geladen sein."); _plugin.SendEvent("deathrun_anticheat_violation", player, dictionary3); ((Character)player).Message((MessageType)2, "DeathRunCounter: AzuAntiCheat fehlt – Run wird geprüft", 0, (Sprite)null); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("DeathRunCounter Mod-Prüfung fehlgeschlagen: " + ex.Message)); } } } private void ReportRuntimeCatalog(Player player) { try { List list = new List(); foreach (GameObject item in ObjectDB.instance.m_items.Where((GameObject val) => (Object)(object)val != (Object)null)) { ItemDrop component = item.GetComponent(); if (!((Object)(object)component == (Object)null)) { list.Add(((Object)item).name + "=" + RequiredTier(component.m_itemData)); } } list.Sort(StringComparer.Ordinal); string value; using (SHA256 sHA = SHA256.Create()) { value = BitConverter.ToString(sHA.ComputeHash(Encoding.UTF8.GetBytes(string.Join("\n", list.ToArray())))).Replace("-", "").ToLowerInvariant(); } Dictionary dictionary = RunFields(); dictionary["catalogVersion"] = (ActiveRun.rulesetVersion ?? "unknown") + ":" + Application.version; dictionary["catalogHash"] = value; dictionary["catalogItemCount"] = list.Count; _plugin.SendEvent("deathrun_item_catalog_attestation", player, dictionary); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("DeathRun-Gegenstandskatalog konnte nicht bestätigt werden: " + ex.Message)); } } } internal static int RequiredTier(ItemData item) { if (item == null || item.m_shared == null) { return 0; } string key = Plugin.NormalizeKey(((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : item.m_shared.m_name).Replace("_", string.Empty).Replace(" ", string.Empty); if (ItemTierOverrides.TryGetValue(key, out var value)) { return value; } string text = ((((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : string.Empty) + " " + item.m_shared.m_name).ToLowerInvariant(); int num = Math.Max(1, item.m_quality); string key2 = text + "|q" + num; if (TierCache.TryGetValue(key2, out var value2)) { return value2; } int num2 = TierForName(text); try { Recipe val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetRecipe(item) : null); if ((Object)(object)val != (Object)null) { if (val.m_resources != null) { Requirement[] resources = val.m_resources; foreach (Requirement val2 in resources) { if (val2 != null && (Object)(object)val2.m_resItem != (Object)null) { num2 = Math.Max(num2, TierForName(((Object)val2.m_resItem).name + " " + val2.m_resItem.m_itemData.m_shared.m_name)); } } } int requiredLevel = Math.Max(1, val.m_minStationLevel) + num - 1; num2 = Math.Max(num2, RequiredStationTier(val.m_craftingStation, requiredLevel)); } } catch { } TierCache[key2] = num2; return num2; } private static int RequiredStationTier(CraftingStation station, int requiredLevel) { if ((Object)(object)station == (Object)null || requiredLevel <= 1) { return 0; } string text = Plugin.NormalizeKey(((Object)station).name + " " + station.m_name).Replace("_", string.Empty).Replace(" ", string.Empty); int[] array; if (text.Contains("workbench")) { array = new int[5] { 0, 0, 0, 1, 3 }; } else if (text.Contains("forge") && !text.Contains("black")) { array = new int[7] { 1, 1, 1, 2, 2, 3, 3 }; } else if (text.Contains("cauldron")) { array = new int[6] { 1, 2, 3, 4, 5, 6 }; } else if (text.Contains("blackforge") || text.Contains("blacksmith")) { array = new int[3] { 5, 5, 6 }; } else if (text.Contains("mage") || text.Contains("galdr")) { array = new int[3] { 5, 5, 6 }; } else { if (!text.Contains("artisan")) { return 0; } array = new int[3] { 4, 5, 6 }; } return array[Math.Min(requiredLevel, array.Length) - 1]; } private static int TierForName(string value) { string text = (value ?? string.Empty).ToLowerInvariant().Replace("$item_", string.Empty).Replace("_", string.Empty) .Replace(" ", string.Empty); int num = 0; for (int i = 0; i < TierTokens.Length; i++) { if (TierTokens[i].Any(text.Contains)) { num = Math.Max(num, i); } } return num; } internal static bool CanSummonBoss(Player player, OfferingBowl bowl) { //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) if (!Enabled || ActiveRun == null || (Object)(object)player == (Object)null || (Object)(object)bowl == (Object)null) { return true; } string text = BossKeyForBowl(bowl); if (!Bosses.Contains(text)) { return true; } bool flag = ValidateInventory(player, report: true); Player[] array = (from candidate in Player.GetAllPlayers() where (Object)(object)candidate != (Object)null && Vector3.Distance(((Component)candidate).transform.position, ((Component)bowl).transform.position) <= 150f select candidate).ToArray(); HashSet registered = new HashSet((ActiveRun.characters ?? new DeathRunCharacter[0]).Select((DeathRunCharacter entry) => entry.id), StringComparer.Ordinal); bool flag2 = array.Length <= 4 && array.All((Player candidate) => registered.Contains(candidate.GetPlayerID().ToString()) && ValidateInventory(candidate, report: true)); if (string.Equals(text, ExpectedBoss, StringComparison.OrdinalIgnoreCase) && flag && flag2) { if (!_bossFightActive) { _bossFightActive = true; _bossFightInvalid = false; _bossFightKey = text; _bossFightCenter = ((Component)bowl).transform.position; BossFightParticipants.Clear(); } Player[] array2 = array; foreach (Player val in array2) { BossFightParticipants.Add(val.GetPlayerID().ToString()); } return true; } string text2 = ((!string.Equals(text, ExpectedBoss, StringComparison.OrdinalIgnoreCase)) ? ("Falsche Bossreihenfolge. Erwartet: " + ExpectedBoss + ", erkannt: " + text + ".") : ((!flag) ? "Nicht freigeschaltete Ausrüstung, Nahrung oder Verbrauchseffekte erkannt." : "Im Umkreis von 150 m befindet sich ein nicht registrierter Run-Charakter.")); Dictionary dictionary = RunFields(); dictionary["boss"] = text; dictionary["inventoryAllowed"] = flag; dictionary["participantCharacterIds"] = array.Select((Player candidate) => candidate.GetPlayerID().ToString()).ToArray(); dictionary["partyAllowed"] = flag2; dictionary["message"] = "Bossbeschwörung blockiert: " + text2; _plugin.SendEvent("deathrun_boss_summon_violation", player, dictionary); ((Character)player).Message((MessageType)2, "DeathRunCounter: " + text2, 0, (Sprite)null); return false; } private static string BossKeyForBowl(OfferingBowl bowl) { if ((Object)(object)bowl == (Object)null) { return string.Empty; } string text = ((Object)bowl).name ?? string.Empty; try { FieldInfo[] fields = ((object)bowl).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { string text2 = fieldInfo.Name.ToLowerInvariant(); if (text2.Contains("boss") || text2.Contains("offer")) { object value = fieldInfo.GetValue(bowl); Object val = (Object)((value is Object) ? value : null); if (val != null) { text = text + " " + val.name; } else if (value != null) { text = text + " " + value; } } } } catch { } return Plugin.CanonicalBossKey(Plugin.NormalizeKey(text)); } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })] internal static class DeathRunInventoryReceiveGatePatch { private static bool Prefix(Inventory __instance, ItemData item, ref bool __result) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || __instance != ((Humanoid)localPlayer).GetInventory() || DeathRunCounterFeature.CanUse(localPlayer, item)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Humanoid), "UseItem", new Type[] { typeof(Inventory), typeof(ItemData), typeof(bool) })] internal static class DeathRunUseItemGatePatch { private static bool Prefix(Humanoid __instance, ItemData item) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer)) { return DeathRunCounterFeature.CanUse(val, item); } return true; } } [HarmonyPatch(typeof(Humanoid), "StartAttack", new Type[] { typeof(Character), typeof(bool) })] internal static class DeathRunCombatAdmissionGatePatch { private static bool Prefix(Humanoid __instance, ref bool __result) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer || DeathRunCounterFeature.GameplayReady) { return true; } __result = false; ((Character)val).Message((MessageType)2, "DeathRunCounter: Charakter und Run werden noch geprüft.", 0, (Sprite)null); return false; } } [HarmonyPatch(typeof(ItemDrop), "Pickup", new Type[] { typeof(Humanoid) })] internal static class DeathRunPickupGatePatch { private static bool Prefix(ItemDrop __instance, Humanoid character) { Player val = (Player)(object)((character is Player) ? character : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer)) { return DeathRunCounterFeature.CanUse(val, __instance?.m_itemData); } return true; } } [HarmonyPatch] internal static class DeathRunProcessingGatePatch { private static IEnumerable TargetMethods() { Type[] array = new Type[3] { typeof(Smelter), typeof(CookingStation), typeof(Fermenter) }; foreach (Type type in array) { MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "UseItem" || methodInfo.Name == "OnAddOre" || methodInfo.Name == "OnAddFuel" || methodInfo.Name == "AddItem") { yield return methodInfo; } } } } private static bool Prefix(object __instance, object[] __args) { Player val = __args?.OfType().FirstOrDefault() ?? Player.m_localPlayer; ItemData val2 = __args?.OfType().FirstOrDefault(); if (!((Object)(object)val == (Object)null) && val2 != null) { return DeathRunCounterFeature.CanProcess(val, __instance, val2); } return true; } } [HarmonyPatch] internal static class DeathRunBossSummonGatePatch { private static IEnumerable TargetMethods() { return from method in typeof(OfferingBowl).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where method.Name == "Interact" || method.Name == "UseItem" select method; } private static bool Prefix(OfferingBowl __instance, object[] __args) { return DeathRunCounterFeature.CanSummonBoss(__args?.OfType().FirstOrDefault() ?? Player.m_localPlayer, __instance); } } [HarmonyPatch(typeof(OfferingBowl), "Awake")] internal static class DeathRunBossAltarRegistrationPatch { private static void Postfix(OfferingBowl __instance) { DeathRunCounterFeature.RegisterBossAltar(__instance); } } [HarmonyPatch(typeof(Humanoid), "EquipItem")] internal static class DeathRunEquipGatePatch { private static bool Prefix(Humanoid __instance, ItemData item) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer)) { return DeathRunCounterFeature.CanUse(val, item); } return true; } } [HarmonyPatch(typeof(Player), "ConsumeItem")] internal static class DeathRunConsumeGatePatch { private static bool Prefix(Player __instance, ItemData item) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { return DeathRunCounterFeature.CanUse(__instance, item); } return true; } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] internal static class DeathRunCraftGatePatch { private static bool Prefix(InventoryGui __instance, Player player) { Recipe value = Traverse.Create((object)__instance).Field("m_craftRecipe").GetValue(); return DeathRunCounterFeature.CanCraft(player, value); } } [HarmonyPatch(typeof(Player), "PlacePiece")] internal static class DeathRunBuildGatePatch { private static bool Prefix(Player __instance, Piece piece) { return DeathRunCounterFeature.CanBuild(__instance, piece); } } internal sealed class DeathRunWorldRulesFeature : MonoBehaviour { internal const float BossAltarRadius = 50f; private const float TrophyReconciliationSeconds = 60f; private const float TrophyRetrySeconds = 5f; private static Plugin _plugin; private static DeathRunWorldRulesFeature _instance; private bool _snapshotPending = true; private float _snapshotNotBefore; private float _nextSnapshotReconciliationAt; private string _snapshotRunKey = string.Empty; private string _lastSnapshot = string.Empty; private string _snapshotInFlight = string.Empty; private ObjectDB _catalogSource; private List> _catalogCache; private static float _lastWarning; internal static void Initialize(Plugin plugin) { _plugin = plugin; _instance = ((Component)plugin).gameObject.GetComponent(); if ((Object)(object)_instance == (Object)null) { _instance = ((Component)plugin).gameObject.AddComponent(); } } private void Update() { Player localPlayer = Player.m_localPlayer; DeathRunCounterFeature.DeathRunState activeRun = DeathRunCounterFeature.ActiveRun; if (!DeathRunCounterFeature.Enabled || (Object)(object)localPlayer == (Object)null || !((Character)localPlayer).IsOwner() || activeRun == null || !CharacterAdmissionFeature.ChallengeScoringAllowed) { _snapshotRunKey = string.Empty; _lastSnapshot = string.Empty; _snapshotInFlight = string.Empty; _snapshotPending = true; return; } string text = localPlayer.GetPlayerID() + ":" + activeRun.id; if (!string.Equals(_snapshotRunKey, text, StringComparison.Ordinal)) { _snapshotRunKey = text; _lastSnapshot = string.Empty; _snapshotInFlight = string.Empty; _snapshotPending = true; _snapshotNotBefore = Time.realtimeSinceStartup; _nextSnapshotReconciliationAt = Time.realtimeSinceStartup + 60f; } if (Time.realtimeSinceStartup >= _nextSnapshotReconciliationAt) { _snapshotPending = true; _snapshotNotBefore = Math.Min(_snapshotNotBefore, Time.realtimeSinceStartup); _nextSnapshotReconciliationAt = Time.realtimeSinceStartup + 60f; } if (_snapshotPending && !(Time.realtimeSinceStartup < _snapshotNotBefore)) { _snapshotPending = false; SendTrophySnapshot(localPlayer); } } internal static void NotifyTrophyChanged(Player player) { if (!((Object)(object)_instance == (Object)null) && !((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer)) { _instance._snapshotPending = true; _instance._snapshotNotBefore = Time.realtimeSinceStartup + 0.5f; _instance._nextSnapshotReconciliationAt = Time.realtimeSinceStartup + 60f; } } private void SendTrophySnapshot(Player player) { if (!string.IsNullOrWhiteSpace(_snapshotInFlight)) { _snapshotPending = true; _snapshotNotBefore = Time.realtimeSinceStartup + 1f; return; } List list = ReadDisplayedTrophies(player); List> list2 = ReadTrophyCatalog(); if (list2.Count == 0) { _snapshotPending = true; _snapshotNotBefore = Time.realtimeSinceStartup + 1f; return; } string text = string.Join("|", list2.Select((Dictionary entry) => Convert.ToString(entry["key"]) + "@" + Convert.ToString(entry["biome"]))); string signature = string.Join("|", list.OrderBy((string value) => value)) + ":" + text; if (signature == _lastSnapshot) { return; } string runKey = _snapshotRunKey; _snapshotInFlight = signature; if (!_plugin.SendEvent("deathrun_trophy_snapshot", player, new Dictionary { { "trophiesFound", list }, { "trophyCatalog", list2 }, { "trophyFoundCount", list.Count }, { "trophyRequiredCount", list2.Count }, { "trophySource", "valheim_trophy_display" }, { "trophySnapshotProtocol", 2 } }, delegate(bool delivered) { CompleteTrophySnapshot(runKey, signature, delivered); })) { _snapshotInFlight = string.Empty; _snapshotPending = true; _snapshotNotBefore = Time.realtimeSinceStartup + 5f; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Blut-Eid Trophäen-Snapshot konnte nicht vorgemerkt werden und wird erneut versucht."); } } else { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Blut-Eid Trophäen-Snapshot zur Übertragung vorgemerkt: " + list.Count + "/" + list2.Count)); } } } private void CompleteTrophySnapshot(string runKey, string signature, bool delivered) { if (!string.Equals(runKey, _snapshotRunKey, StringComparison.Ordinal)) { return; } if (string.Equals(_snapshotInFlight, signature, StringComparison.Ordinal)) { _snapshotInFlight = string.Empty; } if (delivered) { _lastSnapshot = signature; _snapshotPending = true; _snapshotNotBefore = Time.realtimeSinceStartup + 0.25f; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Blut-Eid Trophäen-Snapshot vom ChallengeHub bestätigt."); } } else { _snapshotPending = true; _snapshotNotBefore = Time.realtimeSinceStartup + 5f; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"Blut-Eid Trophäen-Snapshot nicht bestätigt; erneuter Versuch folgt."); } } } private static List ReadDisplayedTrophies(Player player) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); try { if ((Object)(object)player != (Object)null) { AddStrings(hashSet, player.GetTrophies()); } object obj = ((hashSet.Count == 0 && (Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null); if (obj != null) { foreach (MethodInfo item in from method in obj.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where method.Name.IndexOf("Troph", StringComparison.OrdinalIgnoreCase) >= 0 && method.GetParameters().Length == 0 select method) { object value = null; try { value = item.Invoke(obj, null); } catch { } AddStrings(hashSet, value); } foreach (FieldInfo item2 in from field in obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where field.Name.IndexOf("troph", StringComparison.OrdinalIgnoreCase) >= 0 select field) { object value2 = null; try { value2 = item2.GetValue(obj); } catch { } AddStrings(hashSet, value2); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Blut-Eid Trophäenanzeige konnte nicht gelesen werden: " + ex.Message)); } } return (from result in (from value3 in hashSet.Select(CanonicalTrophyKey) where !string.IsNullOrWhiteSpace(value3) select value3).Distinct(StringComparer.OrdinalIgnoreCase) orderby result select result).ToList(); } private static void AddStrings(HashSet target, object value) { if (value == null || value is string) { if (value is string item) { target.Add(item); } } else { if (!(value is IEnumerable enumerable)) { return; } foreach (object item2 in enumerable) { if (item2 != null) { target.Add(Convert.ToString(item2)); } } } } private List> ReadTrophyCatalog() { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Invalid comparison between Unknown and I4 ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null || instance.m_items == null) { return new List>(); } if ((Object)(object)_catalogSource == (Object)(object)instance && _catalogCache != null) { return _catalogCache; } List> list = new List>(); foreach (GameObject item in instance.m_items) { ItemDrop val = (((Object)(object)item != (Object)null) ? item.GetComponent() : null); if (!((Object)(object)val == (Object)null) && val.m_itemData?.m_shared != null && (int)val.m_itemData.m_shared.m_itemType == 13) { string text = CanonicalTrophyKey(((Object)item).name); string text2 = val.m_itemData.m_shared.m_name; try { text2 = QoLSkillRuntimeFeature.Localize(text2); } catch { } list.Add(new Dictionary { { "key", text }, { "label", text2 }, { "biome", TrophyBiome(text) } }); } } _catalogSource = instance; _catalogCache = (from @group in list.GroupBy, string>((Dictionary entry) => Convert.ToString(entry["key"]), StringComparer.OrdinalIgnoreCase) select @group.First() into entry orderby Convert.ToString(entry["biome"]), Convert.ToString(entry["label"]) select entry).ToList(); return _catalogCache; } private static string NormalizeTrophy(string value) { return Plugin.NormalizeKey(value ?? string.Empty).Replace("$item_", string.Empty).Replace("trophy", string.Empty) .Trim(new char[1] { '_' }); } private static string CanonicalTrophyKey(string value) { string text = NormalizeTrophy(value); switch (text) { case "foresttroll": case "frosttroll": return "troll"; case "draugrfem": return "draugr"; case "dragonqueen": return "moder"; case "goblinking": return "yagluth"; case "goblinbrute": return "fulingberserker"; case "goblinshaman": return "fulingshaman"; case "goblin": return "fuling"; case "sgolem": return "stonegolem"; default: return text; } } private static string TrophyBiome(string key) { string text = CanonicalTrophyKey(key); string[][] array = new string[7][] { new string[4] { "boar", "deer", "neck", "eikthyr" }, new string[7] { "greydwarf", "greydwarfbrute", "greydwarfshaman", "troll", "skeleton", "theelder", "elder" }, new string[8] { "blob", "draugr", "draugrelite", "leech", "surtling", "wraith", "abomination", "bonemass" }, new string[7] { "wolf", "fenring", "hatchling", "cultist", "ulv", "stonegolem", "moder" }, new string[7] { "deathsquito", "fuling", "fulingberserker", "fulingshaman", "lox", "growth", "yagluth" }, new string[7] { "hare", "seeker", "seekerbrute", "gjall", "tick", "dvergr", "queen" }, new string[7] { "asksvin", "charred", "morgen", "bonemaw", "fallenvalkyrie", "volture", "fader" } }; string[] array2 = new string[7] { "meadows", "blackforest", "swamp", "mountain", "plains", "mistlands", "ashlands" }; for (int i = 0; i < array.Length; i++) { if (array[i].Any(text.Contains)) { return array2[i]; } } return "unassigned"; } internal static bool IsInsideBossAltar(Vector3 position, out OfferingBowl altar) { //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) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) altar = null; if (!DeathRunCounterFeature.Enabled || DeathRunCounterFeature.ActiveRun == null) { return false; } OfferingBowl[] array; try { array = Object.FindObjectsOfType(); } catch { return false; } OfferingBowl[] array2 = array; foreach (OfferingBowl val in array2) { if (!((Object)(object)val == (Object)null)) { Vector3 val2 = ((Component)val).transform.position - position; val2.y = 0f; if (!(((Vector3)(ref val2)).sqrMagnitude > 2500f)) { altar = val; return true; } } } return false; } internal static bool AllowModification(Player player, Vector3 position, string action) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (!IsInsideBossAltar(position, out var altar)) { return true; } if ((Object)(object)player != (Object)null && Time.realtimeSinceStartup - _lastWarning > 1.5f) { _lastWarning = Time.realtimeSinceStartup; ((Character)player).Message((MessageType)2, "Blut-Eid: Im Umkreis von 50 m um einen Bossaltar darf die Welt nicht verändert werden.", 0, (Sprite)null); _plugin?.SendEvent("deathrun_altar_protection_violation", player, new Dictionary { { "message", "Verbotene Weltveränderung im 50-m-Schutzkreis eines Bossaltars." }, { "operation", action }, { "targetName", ((Object)(object)altar != (Object)null) ? ((Object)altar).name : "boss_altar" }, { "value", 50f } }); } return false; } } [HarmonyPatch(typeof(Player), "PlacePiece", new Type[] { typeof(Piece), typeof(Vector3), typeof(Quaternion), typeof(bool) })] internal static class DeathRunAltarPlacePatch { [HarmonyPrefix] [HarmonyPriority(800)] private static bool Prefix(Player __instance, Vector3 pos) { //IL_0032: 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) if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } GameObject placementGhost = PlayerBuildReflection.GetPlacementGhost(__instance); return DeathRunWorldRulesFeature.AllowModification(__instance, ((Object)(object)placementGhost != (Object)null) ? placementGhost.transform.position : pos, "build_or_terrain"); } } [HarmonyPatch(typeof(Player), "RemovePiece")] internal static class DeathRunAltarRemovePatch { [HarmonyPrefix] [HarmonyPriority(800)] private static bool Prefix(Player __instance) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } Piece val = null; try { val = __instance.GetHoveringPiece(); } catch { } if (!((Object)(object)val == (Object)null)) { return DeathRunWorldRulesFeature.AllowModification(__instance, ((Component)val).transform.position, "remove_piece"); } return true; } } [HarmonyPatch] internal static class DeathRunAltarTerrainOpPatch { private static IEnumerable TargetMethods() { Type type = AccessTools.TypeByName("TerrainOp"); if (type == null) { yield break; } foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type)) { if (declaredMethod.Name == "Awake" || declaredMethod.Name == "OnPlaced") { yield return declaredMethod; } } } [HarmonyPrefix] [HarmonyPriority(800)] private static bool Prefix(MonoBehaviour __instance) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || !DeathRunWorldRulesFeature.IsInsideBossAltar(((Component)__instance).transform.position, out var _)) { return true; } DeathRunWorldRulesFeature.AllowModification(Player.m_localPlayer, ((Component)__instance).transform.position, "terrain_operation"); try { Object.Destroy((Object)(object)((Component)__instance).gameObject); } catch { } return false; } } internal sealed class DungeonLocationResetResult { internal bool Success; internal string Failure = string.Empty; internal StableDungeonContext Context; internal ZDO StateZdo; internal int CandidateCount; internal int DestroyedCount; internal int ProtectedCount; internal int RemainingOldCount; internal int NewObjectCount; internal bool LocationPlaced; internal bool EntranceVerified; internal bool DungeonVerified; internal int Generation; internal string PreflightSummary = string.Empty; internal string VerificationSummary = string.Empty; internal DungeonGenerator RuntimeDungeon; } internal sealed class DungeonResetPreflight { internal readonly List Candidates = new List(); internal readonly List Protected = new List(); internal readonly HashSet CandidateIds = new HashSet(); internal int InteriorCandidates; internal int ExteriorCandidates; internal int UnknownPrefabs; internal int ProtectedInterior; internal string Summary(StableDungeonContext context) { return "DungeonKey=" + (context?.DungeonKey ?? "unknown") + "; Location=" + (context?.LocationPrefabName ?? "unknown") + "; Zone=" + ((context != null) ? ((object)Unsafe.As(ref context.Zone)/*cast due to .constrained prefix*/).ToString() : "unknown") + "; Kandidaten=" + Candidates.Count + "; Innen=" + InteriorCandidates + "; Aussen=" + ExteriorCandidates + "; Geschuetzt=" + Protected.Count + "; GeschuetztInnen=" + ProtectedInterior + "; UnbekanntePrefabs=" + UnknownPrefabs; } } internal static class DungeonLocationResetEngine { private const float InteriorHeight = 4000f; private const int MaxLoadWaitFrames = 600; private const int MaxDestroySettleFrames = 300; private const int MaxVerificationFrames = 600; private const int DestroyBatchLimit = 256; private const long FrameBudgetMilliseconds = 15L; private const float DeferredReleaseRadius = 192f; private const float DeferredReleasePollSeconds = 2f; private const float DeferredReleaseGraceSeconds = 12f; private const float DeferredReleaseStatusSeconds = 900f; private static readonly HashSet PendingZoneReleases = new HashSet(); internal static IEnumerator Execute(DungeonGenerator dungeon, DungeonLocationResetResult result) { if (result == null) { yield break; } result.Success = false; if (!IsServer()) { result.Failure = "Location-Reset darf nur auf dem Server ausgefuehrt werden."; yield break; } StableDungeonContext context = (result.Context = DungeonWorldRegistry.Resolve(dungeon)); result.StateZdo = context?.StateZdo; if (context == null || !context.HasLocation || context.StateZdo == null) { result.Failure = "Keine stabile Location-/Registry-Zuordnung fuer den Dungeon gefunden."; yield break; } if (PlayersInsideContext(context)) { result.Failure = "Mindestens ein Spieler befindet sich noch im Dungeon."; yield break; } ZoneSystem zoneSystem = ZoneSystem.instance; if ((Object)(object)zoneSystem == (Object)null) { result.Failure = "ZoneSystem ist nicht verfuegbar."; yield break; } bool manuallyLoaded = !zoneSystem.IsZoneLoaded(context.Zone); if (manuallyLoaded) { if (!ValheimPrivateAccess.TryPokeLocalZone(zoneSystem, context.Zone, out var error)) { result.Failure = "Dungeon-Zone konnte nicht zum Reset geladen werden: " + error; yield break; } int waitFrames = 0; while (!zoneSystem.IsZoneLoaded(context.Zone) && waitFrames++ < 600) { yield return null; } } if (!zoneSystem.IsZoneLoaded(context.Zone) || !ValheimPrivateAccess.TryGetZoneRoot(zoneSystem, context.Zone, out var zoneRoot)) { result.Failure = "Dungeon-Zone wurde nicht innerhalb des Zeitlimits geladen."; yield break; } if (!zoneSystem.m_locationInstances.TryGetValue(context.Zone, out var location) || !ValheimPrivateAccess.TryGetLocationMetadata(location, out var prefabName, out var exteriorRadius)) { result.Failure = "Location-Instanz ist nach dem Laden der Zone nicht verfuegbar."; yield break; } context.Location = location; context.ExteriorPosition = location.m_position; context.LocationPrefabName = prefabName; context.LocationPrefabHash = StringExtensionMethods.GetStableHashCode(prefabName); context.ExteriorRadius = exteriorRadius; DungeonResetPreflight preflight = BuildPreflight(context); result.CandidateCount = preflight.Candidates.Count; result.ProtectedCount = preflight.Protected.Count; result.PreflightSummary = preflight.Summary(context); DungeonWorldRegistry.Write(context.StateZdo, "ChallengeHub.WorldRegistry.LastPreflight", result.PreflightSummary); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Dungeon-Reset Preflight: " + result.PreflightSummary)); } if (preflight.ProtectedInterior > 0) { result.Failure = "Der Dungeon enthaelt geschuetzte Spielerobjekte oder Grabsteine im Innenraum. Reset abgebrochen."; yield break; } if (preflight.Candidates.Count == 0) { result.Failure = "Der Preflight hat keine resetfaehigen Location-/Dungeon-ZDOs gefunden."; yield break; } HashSet protectedPlayers = ServerAuthoritativeZdoDestroyer.CollectProtectedPlayerZdos(); Stopwatch budget = Stopwatch.StartNew(); int batch = 0; foreach (ZDO candidate in preflight.Candidates) { if (candidate == null || !candidate.IsValid()) { continue; } ServerAuthoritativeZdoDestroyer.DestroyResult destroyResult = ServerAuthoritativeZdoDestroyer.Destroy(candidate, protectedPlayers, "dungeon_location_reset:" + context.DungeonKey); switch (destroyResult) { case ServerAuthoritativeZdoDestroyer.DestroyResult.Destroyed: case ServerAuthoritativeZdoDestroyer.DestroyResult.AlreadyGone: result.DestroyedCount++; batch++; if (batch >= 256 || budget.ElapsedMilliseconds >= 15) { batch = 0; budget.Restart(); ServerAuthoritativeZdoDestroyer.FlushDestroyed("dungeon_location_reset_batch"); yield return null; } break; case ServerAuthoritativeZdoDestroyer.DestroyResult.ProtectedPlayer: result.Failure = "Ein Spieler-ZDO wurde waehrend der Loeschphase erkannt. Reset abgebrochen."; yield break; default: result.Failure = "ZDO-Loeschung fehlgeschlagen: " + destroyResult.ToString() + "."; yield break; } } ServerAuthoritativeZdoDestroyer.FlushDestroyed("dungeon_location_reset_final"); int settle = 0; result.RemainingOldCount = CountRemaining(preflight.CandidateIds); while (result.RemainingOldCount > 0 && settle++ < 300) { yield return null; result.RemainingOldCount = CountRemaining(preflight.CandidateIds); } if (result.RemainingOldCount > 0) { result.Failure = "Alte Dungeon-ZDOs wurden nicht vollstaendig entfernt: " + result.RemainingOldCount + "."; yield break; } HashSet hashSet = new HashSet(); foreach (ZDO item in SnapshotZoneZdos(context.Zone)) { if (item != null && item.IsValid()) { hashSet.Add(item.m_uid); } } try { location.m_placed = false; zoneSystem.m_locationInstances[context.Zone] = location; if (!ValheimPrivateAccess.TryPlaceLocations(zoneSystem, context.Zone, zoneRoot, out var temporaryObjects, out var error2)) { result.Failure = "Location konnte nicht ueber ZoneSystem.PlaceLocations neu platziert werden: " + error2; yield break; } foreach (GameObject item2 in temporaryObjects) { if ((Object)(object)item2 != (Object)null) { Object.Destroy((Object)(object)item2); } } foreach (ZDO item3 in SnapshotZoneZdos(context.Zone).ToList()) { if (item3 != null && item3.IsValid() && !hashSet.Contains(item3.m_uid) && !DungeonWorldRegistry.IsRegistryZdo(item3) && !(item3.GetPosition().y > 4000f)) { ServerAuthoritativeZdoDestroyer.Destroy(item3, protectedPlayers, "dungeon_location_reset_duplicate_exterior"); } } ServerAuthoritativeZdoDestroyer.FlushDestroyed("dungeon_location_reset_duplicate_exterior"); } catch (Exception ex) { result.Failure = "Location konnte nicht ueber ZoneSystem.PlaceLocations neu platziert werden: " + ex.Message; yield break; } int verifyFrames = 0; int num; do { yield return null; Verify(context, preflight.CandidateIds, result); if (result.LocationPlaced && result.NewObjectCount > 0 && result.EntranceVerified && result.DungeonVerified) { break; } num = verifyFrames + 1; verifyFrames = num; } while (num < 600); result.VerificationSummary = "LocationPlaced=" + result.LocationPlaced + "; AlteRestZDOs=" + result.RemainingOldCount + "; NeueZDOs=" + result.NewObjectCount + "; Eingang=" + result.EntranceVerified + "; Dungeon=" + result.DungeonVerified + "; Warteframes=" + verifyFrames; DungeonWorldRegistry.Write(context.StateZdo, "ChallengeHub.WorldRegistry.LastVerification", result.VerificationSummary); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Dungeon-Reset Verifikation: " + context.DungeonKey + "; " + result.VerificationSummary)); } if (!result.LocationPlaced || result.RemainingOldCount > 0 || result.NewObjectCount <= 0 || !result.EntranceVerified || !result.DungeonVerified) { result.Failure = "Location-Neuplatzierung konnte nicht vollstaendig verifiziert werden."; yield break; } result.Generation = DungeonWorldRegistry.IncrementGeneration(context.StateZdo); result.RuntimeDungeon = FindRuntimeDungeon(context); result.Success = true; if (manuallyLoaded) { QueueManuallyLoadedZoneRelease(context); } } private unsafe static DungeonResetPreflight BuildPreflight(StableDungeonContext context) { //IL_000d: 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) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: 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_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) DungeonResetPreflight dungeonResetPreflight = new DungeonResetPreflight(); HashSet protectedPlayers = ServerAuthoritativeZdoDestroyer.CollectProtectedPlayerZdos(); foreach (ZDO item in SnapshotZoneZdos(context.Zone)) { if (item == null || !item.IsValid()) { continue; } Vector3 position; try { position = item.GetPosition(); } catch { continue; } if (ZoneSystem.GetZone(position) != context.Zone) { continue; } bool flag = position.y > 4000f; if (!flag) { continue; } string text = PrefabName(item); if (text.Length == 0) { dungeonResetPreflight.UnknownPrefabs++; List list = dungeonResetPreflight.Protected; string text2 = item.GetPrefab().ToString(); Vector3 val = position; list.Add("unknown:" + text2 + "@" + ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString()); if (flag) { dungeonResetPreflight.ProtectedInterior++; } } else if (IsProtected(item, text, protectedPlayers)) { List list2 = dungeonResetPreflight.Protected; Vector3 val = position; list2.Add(text + "@" + ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString()); if (flag) { dungeonResetPreflight.ProtectedInterior++; } } else { if (flag) { dungeonResetPreflight.InteriorCandidates++; } else { dungeonResetPreflight.ExteriorCandidates++; } dungeonResetPreflight.Candidates.Add(item); dungeonResetPreflight.CandidateIds.Add(item.m_uid); } } dungeonResetPreflight.Candidates.Sort((ZDO left, ZDO right) => right.GetPosition().y.CompareTo(left.GetPosition().y)); return dungeonResetPreflight; } private static bool IsProtected(ZDO zdo, string prefabName, HashSet protectedPlayers) { if (DungeonWorldRegistry.IsRegistryZdo(zdo)) { return true; } if (ServerAuthoritativeZdoDestroyer.IsProtectedPlayerZdo(zdo, protectedPlayers)) { return true; } string text = (prefabName ?? string.Empty).ToLowerInvariant(); if (text == "_zonectrl" || text == "_terraincompiler") { return true; } if (text.Contains("tombstone") || text.Contains("player_tombstone")) { return true; } if (text.Contains("challengehub") && text.Contains("guardian")) { return true; } try { if (zdo.GetLong(ZDOVars.s_creator, 0L) != 0L) { return true; } } catch { } return false; } private static void Verify(StableDungeonContext context, HashSet oldIds, DungeonLocationResetResult result) { //IL_003a: 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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) result.RemainingOldCount = CountRemaining(oldIds); result.NewObjectCount = 0; result.EntranceVerified = false; result.DungeonVerified = false; result.LocationPlaced = (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.m_locationInstances.TryGetValue(context.Zone, out var value) && value.m_placed; foreach (ZDO item in SnapshotZoneZdos(context.Zone)) { if (item == null || !item.IsValid() || oldIds.Contains(item.m_uid) || DungeonWorldRegistry.IsRegistryZdo(item)) { continue; } Vector3 position = item.GetPosition(); bool flag = position.y > 4000f; bool flag2 = Vector2.Distance(new Vector2(position.x, position.z), new Vector2(context.ExteriorPosition.x, context.ExteriorPosition.z)) <= context.ExteriorRadius; if (!flag && !flag2) { continue; } result.NewObjectCount++; GameObject val = null; try { val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(item.GetPrefab()) : null); } catch { } if ((Object)(object)val == (Object)null) { continue; } try { if (flag2 || (Object)(object)val.GetComponentInChildren(true) != (Object)null) { result.EntranceVerified = true; } if (flag || (Object)(object)val.GetComponentInChildren(true) != (Object)null) { result.DungeonVerified = true; } } catch { } } } private static int CountRemaining(IEnumerable ids) { //IL_0018: 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_0023: Unknown result type (might be due to invalid IL or missing references) if (ZDOMan.instance == null || ids == null) { return 0; } int num = 0; foreach (ZDOID id in ids) { try { ZDO zDO = ZDOMan.instance.GetZDO(id); if (zDO != null && zDO.IsValid()) { num++; } } catch { } } return num; } private static List SnapshotZoneZdos(Vector2i zone) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return ValheimPrivateAccess.SnapshotZoneZdos(zone); } private static string PrefabName(ZDO zdo) { try { GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(zdo.GetPrefab()) : null); return ((Object)(object)val != (Object)null) ? ((Object)val).name : string.Empty; } catch { return string.Empty; } } private static bool PlayersInsideContext(StableDungeonContext context) { //IL_0032: 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_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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) Vector2 val2 = default(Vector2); foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer == (Object)null) { continue; } bool flag; try { flag = ((Character)allPlayer).InInterior(); } catch { flag = ((Component)allPlayer).transform.position.y > 4000f; } if (flag) { Vector2 val = new Vector2(((Component)allPlayer).transform.position.x, ((Component)allPlayer).transform.position.z); ((Vector2)(ref val2))..ctor(context.ExteriorPosition.x, context.ExteriorPosition.z); if (Vector2.Distance(val, val2) <= 260f) { return true; } } } return false; } private static bool AnyPlayerNear(Vector3 position, float radius) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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_0049: 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) foreach (Player allPlayer in Player.GetAllPlayers()) { if (!((Object)(object)allPlayer == (Object)null) && Vector2.Distance(new Vector2(((Component)allPlayer).transform.position.x, ((Component)allPlayer).transform.position.z), new Vector2(position.x, position.z)) <= radius) { return true; } } return false; } private static DungeonGenerator FindRuntimeDungeon(StableDungeonContext context) { try { return ((IEnumerable)(from item in Object.FindObjectsByType((FindObjectsSortMode)0) where (Object)(object)item != (Object)null orderby Vector2.Distance(new Vector2(((Component)item).transform.position.x, ((Component)item).transform.position.z), new Vector2(context.ExteriorPosition.x, context.ExteriorPosition.z)) select item)).FirstOrDefault((Func)((DungeonGenerator item) => Vector2.Distance(new Vector2(((Component)item).transform.position.x, ((Component)item).transform.position.z), new Vector2(context.ExteriorPosition.x, context.ExteriorPosition.z)) <= 260f)); } catch { return null; } } private unsafe static void QueueManuallyLoadedZoneRelease(StableDungeonContext context) { //IL_0005: 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_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008b: 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_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) if (context == null) { return; } Vector2i zone = context.Zone; if (!PendingZoneReleases.Add(zone)) { return; } Plugin instance = Plugin.Instance; if ((Object)(object)instance == (Object)null) { PendingZoneReleases.Remove(zone); if (!AnyPlayerNear(context.ExteriorPosition, 192f)) { ReleaseManuallyLoadedZone(zone, context.DungeonKey); return; } ManualLogSource log = Plugin.Log; if (log != null) { string dungeonKey = context.DungeonKey; Vector2i val = zone; log.LogWarning((object)("Manuell geladene Dungeon-Zone konnte nicht zur spaeteren Freigabe vorgemerkt werden, weil die Plugin-Instanz fehlt: " + dungeonKey + "; Zone=" + ((object)(*(Vector2i*)(&val))/*cast due to .constrained prefix*/).ToString())); } } else { ((MonoBehaviour)instance).StartCoroutine(ReleaseManuallyLoadedZoneWhenUnused(zone, context.ExteriorPosition, context.DungeonKey)); } } private unsafe static IEnumerator ReleaseManuallyLoadedZoneWhenUnused(Vector2i zone, Vector3 exteriorPosition, string dungeonKey) { //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) float waitedSeconds = 0f; float clearSeconds = 0f; ManualLogSource log = Plugin.Log; if (log != null) { string[] obj = new string[7] { "Dungeon-Zone zur verzögerten Freigabe vorgemerkt: ", dungeonKey, "; Zone=", null, null, null, null }; Vector2i val = zone; obj[3] = ((object)(*(Vector2i*)(&val))/*cast due to .constrained prefix*/).ToString(); obj[4] = "; Radius="; obj[5] = 192f.ToString("F0"); obj[6] = "m"; log.LogInfo((object)string.Concat(obj)); } while (true) { ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)instance == (Object)null || !instance.IsZoneLoaded(zone)) { PendingZoneReleases.Remove(zone); ManualLogSource log2 = Plugin.Log; if (log2 != null) { Vector2i val = zone; log2.LogInfo((object)("Dungeon-Zone war bereits freigegeben: " + dungeonKey + "; Zone=" + ((object)(*(Vector2i*)(&val))/*cast due to .constrained prefix*/).ToString())); } yield break; } if (AnyPlayerNear(exteriorPosition, 192f)) { clearSeconds = 0f; } else { clearSeconds += 2f; if (clearSeconds >= 12f) { break; } } yield return (object)new WaitForSeconds(2f); waitedSeconds += 2f; if (waitedSeconds >= 900f) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { Vector2i val = zone; log3.LogInfo((object)("Dungeon-Zone wartet weiterhin auf spielerfreie Freigabe: " + dungeonKey + "; Zone=" + ((object)(*(Vector2i*)(&val))/*cast due to .constrained prefix*/).ToString())); } waitedSeconds = 0f; } } ReleaseManuallyLoadedZone(zone, dungeonKey); PendingZoneReleases.Remove(zone); } private unsafe static bool ReleaseManuallyLoadedZone(Vector2i zone, string dungeonKey) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) int num = 0; bool flag = false; try { List list = SnapshotZoneZdos(zone); ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance != (Object)null) { foreach (ZDO item in list) { if (ValheimPrivateAccess.TryGetSceneInstance(instance, item, out var view)) { GameObject val = (((Object)(object)view != (Object)null) ? ((Component)view).gameObject : null); view.ResetZDO(); ValheimPrivateAccess.RemoveSceneInstance(instance, item); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } else { Object.Destroy((Object)(object)view); } num++; } } } if (ValheimPrivateAccess.TryRemoveZoneRoot(ZoneSystem.instance, zone, out var root)) { flag = true; if ((Object)(object)root != (Object)null) { Object.Destroy((Object)(object)root); } } ManualLogSource log = Plugin.Log; if (log != null) { string[] obj = new string[8] { "Manuell geladene Dungeon-Zone freigegeben: ", dungeonKey, "; Zone=", null, null, null, null, null }; Vector2i val2 = zone; obj[3] = ((object)(*(Vector2i*)(&val2))/*cast due to .constrained prefix*/).ToString(); obj[4] = "; Instanzen="; obj[5] = num.ToString(); obj[6] = "; Zonenwurzel="; obj[7] = flag.ToString(); log.LogInfo((object)string.Concat(obj)); } return flag || num > 0; } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { string[] obj2 = new string[6] { "Manuell geladene Dungeon-Zone konnte nicht vollstaendig freigegeben werden: ", dungeonKey, "; Zone=", null, null, null }; Vector2i val2 = zone; obj2[3] = ((object)(*(Vector2i*)(&val2))/*cast due to .constrained prefix*/).ToString(); obj2[4] = " -> "; obj2[5] = ex.Message; log2.LogWarning((object)string.Concat(obj2)); } return false; } } private static bool IsServer() { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } internal static class DungeonResetLifecycleFeature { internal enum GuardianQueueResult { NotDungeonEntrance, Queued, AwaitingServer, EntranceUnresolved } private enum ServerQueueResult { Queued, AlreadyActive, AlreadyPrepared, Failed } internal enum PendingGuardianLifecycleState { Unknown, Active, Completed, Invalid } private sealed class EntranceSnapshot { internal Vector3 EntrancePosition; internal Vector3 TargetPosition; internal Quaternion TargetRotation; internal bool HasTarget; } private sealed class DungeonEntryNotificationState { internal DungeonGenerator Dungeon; internal string DungeonId; } private sealed class PendingClientResetRequest { internal string RequestId; internal Piece Guardian; internal string DungeonId; internal DateTime SentAtUtc; } private sealed class PendingClientRegenerateRequest { internal string RequestId; internal string DungeonId; internal DateTime SentAtUtc; } private const string ResetRequestRpc = "ChallengeHub_DungeonReset_Request_v2230"; private const string ResetAckRpc = "ChallengeHub_DungeonReset_Ack_v2230"; private const string RegenerateRequestRpc = "ChallengeHub_DungeonReset_Regenerate_v2230"; private const string RegenerateAckRpc = "ChallengeHub_DungeonReset_RegenerateAck_v2230"; private const string EntranceRepairRpc = "ChallengeHub_DungeonReset_EntranceRepair_v2230"; private const string StateKey = "ChallengeHub.DungeonLifecycle.State"; private const string NextResetKey = "ChallengeHub.DungeonLifecycle.NextResetUtc"; private const string RequestedAtKey = "ChallengeHub.DungeonLifecycle.RequestedAtUtc"; private const string EmptySinceKey = "ChallengeHub.DungeonLifecycle.EmptySinceUtc"; private const string PreparedAtKey = "ChallengeHub.DungeonLifecycle.PreparedAtUtc"; private const string LastResetKey = "ChallengeHub.DungeonLifecycle.LastResetUtc"; private const string ReasonKey = "ChallengeHub.DungeonLifecycle.Reason"; private const string TierKey = "ChallengeHub.Dungeon.Tier"; private const string LifecycleSchemaKey = "ChallengeHub.DungeonLifecycle.Schema"; private const string CurrentLifecycleSchema = "2.6.4"; private const string StateIdle = "idle"; private const string StateWaitingEmpty = "waiting_empty"; private const string StateUnloading = "unloading"; private const string StatePrepared = "prepared"; private const string StateGenerating = "generating"; private const string StateError = "error"; private static Plugin plugin; private static bool initialized; private static bool rpcRegistered; private static readonly HashSet ActiveUnloads = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly HashSet ActiveGenerations = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly HashSet ImmediateLifecycleWorkers = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary ImmediateLifecycleHeartbeat = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly HashSet ProcessedServerResetRequests = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary PendingClientResetRequests = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary PendingClientRegenerateRequests = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary ClientRegenerateReadyUntil = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> PendingEntranceSnapshots = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary EntranceTargetProxies = new Dictionary(); private static readonly HashSet ActiveEntrancePairRetries = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary KnownDungeons = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary LifecycleAnchors = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly HashSet MissingLifecycleAnchorWarnings = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly HashSet LoggedLifecycleAnchors = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary NextLifecycleAnchorProbeAt = new Dictionary(StringComparer.OrdinalIgnoreCase); private static float NextLocalDungeonProbeAt; private static float NextBrokenEntranceRepairProbeAt; private static bool InvalidNetworkClockWarningLogged; private static readonly HashSet RepairedInvalidTimerWarnings = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly Collider[] NearbyEntranceColliderBuffer = (Collider[])(object)new Collider[64]; private static bool LocalWasInsideDungeon; private static DungeonGenerator LocalInteriorDungeon; private static DungeonGenerator LastExitedDungeon; private static Vector3 LastExitedOutsidePosition; private static DateTime LastExitedAtUtc = DateTime.MinValue; internal static ConfigEntry EnableLifecycle; internal static ConfigEntry EnableAutomaticReset; internal static ConfigEntry EmptyGraceSeconds; internal static ConfigEntry EntranceSearchRadius; internal static ConfigEntry WorkerSeconds; internal static ConfigEntry DungeonBoundsPadding; internal static ConfigEntry RandomizeLayoutOnReset; internal static ConfigEntry EnableBackgroundEntranceRepair; internal static void Initialize(Plugin owner) { if (!initialized && !((Object)(object)owner == (Object)null)) { plugin = owner; EnableLifecycle = ((BaseUnityPlugin)owner).Config.Bind("DungeonLifecycle", "EnableSafeDungeonLifecycle", true, "Echter Dungeon-Reset: warten bis leer, Innenraum entladen, sofort neu erzeugen, Eingang reparieren und Timer neu starten."); EnableAutomaticReset = ((BaseUnityPlugin)owner).Config.Bind("DungeonLifecycle", "EnableAutomaticReset", true, "Automatischer Timer-Reset. Wenn der Countdown faellig ist, wird der sichere Reset beim naechsten Benutzen des Dungeon-Eingangs vor dem Teleport ausgefuehrt."); EmptyGraceSeconds = ((BaseUnityPlugin)owner).Config.Bind("DungeonLifecycle", "EmptyGraceSeconds", 12f, "Wie lange ein Dungeon leer sein muss, bevor der Innenraum entladen wird."); EntranceSearchRadius = ((BaseUnityPlugin)owner).Config.Bind("DungeonLifecycle", "GuardianEntranceSearchRadius", 80f, "Maximale Entfernung zwischen lila Reset-Waechter und dem Eingangskomponent einer Hoehle/Krypte. Der Waechter wird ausserhalb am Eingang gebaut."); WorkerSeconds = ((BaseUnityPlugin)owner).Config.Bind("DungeonLifecycle", "WorkerSeconds", 5f, "Pruefintervall des serverseitigen Dungeon-Reset-Arbeiters."); DungeonBoundsPadding = ((BaseUnityPlugin)owner).Config.Bind("DungeonLifecycle", "BoundsPadding", 18f, "Sicherheitszugabe fuer die erkannten Dungeon-Grenzen."); RandomizeLayoutOnReset = ((BaseUnityPlugin)owner).Config.Bind("DungeonLifecycle", "RandomizeLayoutOnReset", true, "Beim erneuten Betreten einen neuen Generator-Seed verwenden."); EnableBackgroundEntranceRepair = ((BaseUnityPlugin)owner).Config.Bind("DungeonLifecycle", "EnableBackgroundEntranceRepair", false, "Automatische Eingangssuche im Player.Update. Standardmaessig aus, da sie auf grossen Welten kurze Hauptthread-Haenger verursachen kann."); initialized = true; ((MonoBehaviour)owner).StartCoroutine(RegisterRpcWhenReady()); ((MonoBehaviour)owner).StartCoroutine(ServerLifecycleWorker()); ((MonoBehaviour)owner).StartCoroutine(ClientResetRequestTimeoutWorker()); ((MonoBehaviour)owner).StartCoroutine(ClientRegenerateRequestTimeoutWorker()); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"ChallengeHub sicherer Dungeon-Lifecycle 2.6.4 initialisiert: rein serverautoritaer fuer Local Host und Dedicated Server; stabiler Generator/Parent/Eingang-ZDO-Anker aktiv."); } } } internal static void Patch(Harmony harmony) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_007d: Expected O, but got Unknown //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Expected O, but got Unknown //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_0274: Expected O, but got Unknown //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Expected O, but got Unknown if (harmony == null) { return; } try { MethodInfo methodInfo = AccessTools.Method(typeof(DungeonResetLifecycleFeature), "TeleportInteractPrefix", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(DungeonResetLifecycleFeature), "TeleportInteractPostfix", (Type[])null, (Type[])null); List list = FindTeleportInteractMethods(); int num = 0; if (methodInfo != null && methodInfo2 != null) { foreach (MethodInfo item in list) { try { harmony.Patch((MethodBase)item, new HarmonyMethod(methodInfo), new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Dungeon-Lifecycle: optionale Teleport-Interact-Signatur konnte nicht gepatcht werden: " + item?.ToString() + " -> " + ex.Message)); } } } } if (num > 0) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Dungeon-Lifecycle 2.6.4: sicherer Eingangsvorab-Trigger und Eintritts-Timeranzeige aktiv (fail-open); Teleport-Signaturen=" + num + ".")); } } else { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"Dungeon-Lifecycle: keine kompatible Teleport-Interact-Signatur gefunden; Eingang bleibt vollstaendig Vanilla, Waechter-Sofortreset bleibt aktiv."); } } MethodInfo methodInfo3 = AccessTools.Method(typeof(DungeonResetLifecycleFeature), "TeleportHoverTextPostfix", (Type[])null, (Type[])null); int num2 = 0; if (methodInfo3 != null) { foreach (MethodInfo item2 in from method in typeof(Teleport).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where method.Name == "GetHoverText" && method.ReturnType == typeof(string) && method.GetParameters().Length == 0 select method) { try { harmony.Patch((MethodBase)item2, (HarmonyMethod)null, new HarmonyMethod(methodInfo3), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num2++; } catch (Exception ex2) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogDebug((object)("Dungeon-Gedaechtnis-Hover uebersprungen: " + ex2.Message)); } } } } ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)("Dungeon-Gedaechtnis-Hover-Signaturen=" + num2 + ".")); } MethodInfo methodInfo4 = AccessTools.Method(typeof(DungeonGenerator), "Awake", (Type[])null, (Type[])null) ?? AccessTools.Method(typeof(DungeonGenerator), "Start", (Type[])null, (Type[])null); MethodInfo methodInfo5 = AccessTools.Method(typeof(DungeonResetLifecycleFeature), "DungeonGeneratorAwakePostfix", (Type[])null, (Type[])null); if (methodInfo4 != null && methodInfo5 != null) { harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(methodInfo5), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogWarning((object)"Dungeon-Lifecycle: DungeonGenerator.Awake/Start nicht gefunden; direkte Wächter-Zuordnung registriert Generatoren weiterhin."); } } MethodInfo methodInfo6 = AccessTools.Method(typeof(Player), "Update", (Type[])null, (Type[])null); MethodInfo methodInfo7 = AccessTools.Method(typeof(DungeonResetLifecycleFeature), "PlayerUpdatePostfix", (Type[])null, (Type[])null); if (methodInfo6 != null && methodInfo7 != null) { harmony.Patch((MethodBase)methodInfo6, (HarmonyMethod)null, new HarmonyMethod(methodInfo7), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return; } ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogWarning((object)"Dungeon-Lifecycle: Player.Update fuer Ausgangsbindung nicht gefunden."); } } catch (Exception ex3) { ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogWarning((object)("Dungeon-Lifecycle: Laufzeit-Patch fehlgeschlagen: " + ex3.Message)); } } } internal unsafe static GuardianQueueResult TryQueueFromGuardian(Player player, Piece guardian) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) if (!ChallengeHubServerGateFeature.GameplayAllowed) { return GuardianQueueResult.NotDungeonEntrance; } if (!IsEnabled() || (Object)(object)player == (Object)null || (Object)(object)guardian == (Object)null) { return GuardianQueueResult.NotDungeonEntrance; } Vector3 position = ((Component)guardian).transform.position; Teleport val = FindNearestDungeonEntrance(position, EffectiveEntranceSearchRadius()); bool flag = (Object)(object)val != (Object)null; DungeonGenerator val2 = ResolveDungeonNearEntrance(val, position); if ((Object)(object)val2 != (Object)null) { string text = DungeonId(val2); RegisterDungeon(val2); RememberEntranceSnapshot(text, val); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Reset-Waechter hat Dungeon direkt am Eingang zugeordnet: " + (((Object)(object)val != (Object)null) ? EntranceDebugName(val) : ((object)(*(Vector3*)(&position))/*cast due to .constrained prefix*/).ToString()) + " => " + text)); } return RequestReset(val2, player, guardian, "reset_guardian_direct_entrance", val); } DungeonGenerator val3 = FindLearnedDungeonForGuardian(position); if ((Object)(object)val3 != (Object)null) { RegisterDungeon(val3); ManualLogSource log2 = Plugin.Log; if (log2 != null) { Vector3 lastExitedOutsidePosition = LastExitedOutsidePosition; log2.LogInfo((object)("Reset-Waechter nutzt gelernte Dungeon-Ausgangsbindung: Aussenposition=" + ((object)(*(Vector3*)(&lastExitedOutsidePosition))/*cast due to .constrained prefix*/).ToString() + " => " + DungeonId(val3))); } return RequestReset(val3, player, guardian, "reset_guardian_learned_exit_fallback", val); } if (flag) { ((Character)player).Message((MessageType)2, "Konkreter Grabkammer-/Hoehleneingang erkannt, aber die Inneninstanz ist noch nicht geladen. Der Reset-Waechter bleibt aktiv.", 0, (Sprite)null); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"Reset-Waechter: konkreter Teleport-Eingang erkannt, aber keine geladene Dungeoninstanz zugeordnet. Radius-Cleanup wird zum Schutz des Eingangs nicht ausgefuehrt."); } return GuardianQueueResult.EntranceUnresolved; } ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)"Reset-Waechter ohne konkreten Dungeon-Eingang: normaler 30-m-Bereichsreset wird verwendet."); } return GuardianQueueResult.NotDungeonEntrance; } internal static PendingGuardianLifecycleState GetPendingGuardianLifecycleState(string dungeonId, DateTime guardianRequestedAtUtc) { if (string.IsNullOrWhiteSpace(dungeonId)) { return PendingGuardianLifecycleState.Invalid; } try { ZDO val = DungeonWorldRegistry.ResolveStateZdo(dungeonId); DungeonGenerator val2 = FindDungeonById(dungeonId); if (val == null && (Object)(object)val2 == (Object)null) { return PendingGuardianLifecycleState.Unknown; } string text = ((val != null) ? DungeonWorldRegistry.Read(val, "ChallengeHub.DungeonLifecycle.State", "idle") : ReadZdoString((Component)(object)val2, "ChallengeHub.DungeonLifecycle.State")); if (string.IsNullOrWhiteSpace(text)) { text = "idle"; } DateTime dateTime = ParseDate((val != null) ? DungeonWorldRegistry.Read(val, "ChallengeHub.DungeonLifecycle.LastResetUtc", string.Empty) : ReadZdoString((Component)(object)val2, "ChallengeHub.DungeonLifecycle.LastResetUtc")); if (guardianRequestedAtUtc != DateTime.MinValue && dateTime != DateTime.MinValue && dateTime >= guardianRequestedAtUtc.AddSeconds(-1.0)) { return PendingGuardianLifecycleState.Completed; } switch (text) { case "waiting_empty": case "unloading": case "prepared": case "generating": return PendingGuardianLifecycleState.Active; default: if (guardianRequestedAtUtc != DateTime.MinValue && (NetworkUtcNow() - guardianRequestedAtUtc).TotalMinutes < 10.0) { return PendingGuardianLifecycleState.Active; } return PendingGuardianLifecycleState.Invalid; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Pending-Waechterstatus konnte nicht gelesen werden: " + ex.Message)); } return PendingGuardianLifecycleState.Unknown; } } internal static string BuildDungeonStatus(DungeonGenerator dungeon) { if ((Object)(object)dungeon == (Object)null) { return "Dungeonstatus unbekannt"; } EnsureScheduleIfServer(dungeon); string text = ReadZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.State"); if (string.IsNullOrWhiteSpace(text)) { text = "idle"; } switch (text) { case "waiting_empty": if (!PlayersInside(dungeon)) { return "Reset vorgemerkt - Leerphase laeuft"; } return "Reset vorgemerkt - wartet, bis alle Spieler draussen sind"; case "unloading": return "Dungeon wird sicher entladen und sofort neu erzeugt"; case "prepared": return "Reset vorbereitet - sofortige Neuerzeugung laeuft"; case "generating": return "Dungeon wird gerade neu erzeugt"; case "error": return "Dungeon-Reset gestoppt - Fehler im Log pruefen"; default: { if (EnableAutomaticReset == null || !EnableAutomaticReset.Value) { return "Automatischer Reset aus - lila Waechter und Eingangsvorab-Trigger bleiben aktiv"; } DateTime dateTime = NetworkUtcNow(); DateTime dateTime2 = ParseDate(ReadZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.NextResetUtc")); if (!IsPlausibleResetTimestamp(dateTime2, dateTime)) { if (!IsServer() || !EnsureServerLifecycleAuthority(dungeon, "hover_timer_repair")) { return "Automatischer Reset-Termin wird mit dem Server synchronisiert"; } dateTime2 = NextResetUtc(dungeon, dateTime); WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.NextResetUtc", dateTime2.ToString("O", CultureInfo.InvariantCulture)); LogInvalidTimerRepairOnce(dungeon, dateTime2, "hover"); } TimeSpan timeSpan = dateTime2 - dateTime; if (timeSpan <= TimeSpan.Zero) { return "Automatischer Reset faellig - startet sicher vor dem naechsten Eintritt"; } return "Naechster automatischer Reset in " + HumanTime(timeSpan); } } } internal static bool IsDungeonResetEntranceNearby(Vector3 position) { //IL_0000: 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) if (!((Object)(object)FindLearnedDungeonForGuardian(position) != (Object)null)) { return (Object)(object)FindNearestDungeonEntrance(position, EffectiveEntranceSearchRadius()) != (Object)null; } return true; } private static void DungeonGeneratorAwakePostfix(DungeonGenerator __instance) { RegisterDungeon(__instance); ResumeQueuedLifecycleWhenLoaded(__instance); } private unsafe static void PlayerUpdatePostfix(Player __instance) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) try { if (!IsEnabled() || (Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } bool flag = false; try { flag = ((Character)__instance).InInterior(); } catch { } if (flag) { LocalWasInsideDungeon = true; if ((Object)(object)LocalInteriorDungeon == (Object)null || Time.unscaledTime >= NextLocalDungeonProbeAt) { NextLocalDungeonProbeAt = Time.unscaledTime + 2f; DungeonGenerator val = FindNearestDungeonGenerator(((Component)__instance).transform.position, 1200f); if ((Object)(object)val != (Object)null) { LocalInteriorDungeon = val; RegisterDungeon(val); } } return; } if (EnableBackgroundEntranceRepair != null && EnableBackgroundEntranceRepair.Value && Time.unscaledTime >= NextBrokenEntranceRepairProbeAt) { NextBrokenEntranceRepairProbeAt = Time.unscaledTime + 15f; TryRepairBrokenEntranceNearPlayer(((Component)__instance).transform.position); } if (!LocalWasInsideDungeon) { return; } LocalWasInsideDungeon = false; if ((Object)(object)LocalInteriorDungeon != (Object)null) { LastExitedDungeon = LocalInteriorDungeon; RegisterDungeon(LastExitedDungeon); LastExitedOutsidePosition = ((Component)__instance).transform.position; LastExitedAtUtc = DateTime.UtcNow; ManualLogSource log = Plugin.Log; if (log != null) { string text = DungeonId(LastExitedDungeon); Vector3 lastExitedOutsidePosition = LastExitedOutsidePosition; log.LogInfo((object)("Dungeon-Ausgangsbindung gelernt: " + text + " => Aussenposition " + ((object)(*(Vector3*)(&lastExitedOutsidePosition))/*cast due to .constrained prefix*/).ToString())); } } LocalInteriorDungeon = null; } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogDebug((object)("Dungeon-Ausgangsbindung fehlgeschlagen: " + ex.Message)); } } } private static DungeonGenerator FindLearnedDungeonForGuardian(Vector3 guardianPosition) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)LastExitedDungeon == (Object)null || LastExitedAtUtc == DateTime.MinValue) { return null; } if ((DateTime.UtcNow - LastExitedAtUtc).TotalMinutes > 45.0) { return null; } float num = Mathf.Max(120f, EffectiveEntranceSearchRadius() * 1.75f); if (!(Vector3.Distance(guardianPosition, LastExitedOutsidePosition) <= num)) { return null; } return LastExitedDungeon; } private static bool HasDungeonEntranceMarkerNearby(Vector3 position, float radius) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0076: 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) try { if (HasTeleportWithin(Object.FindObjectsByType((FindObjectsSortMode)0), position, radius)) { return true; } } catch { } string csv = ((TargetedResetFeature.DungeonEntranceKeywords != null) ? TargetedResetFeature.DungeonEntranceKeywords.Value : "crypt,sunkencrypt,burial,burialchamber,trollcave,frostcave,cave,hoehle,höhle,infestedmine,mine,dungeon"); try { Collider[] array = Physics.OverlapSphere(position, Mathf.Max(15f, radius)); if (array != null) { Collider[] array2 = array; foreach (Collider val in array2) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null)) { Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).IsValid() && ContainsAny(NormalizeNameChain(((Component)val).gameObject), csv)) { return true; } } } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Dungeon-Eingangsmarker-Collidersuche fehlgeschlagen: " + ex.Message)); } } try { ZNetView[] array3 = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (ZNetView val2 in array3) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((Component)val2).gameObject == (Object)null) && !(HierarchyDistance(position, ((Component)val2).transform, 5) > radius) && ContainsAny(NormalizeNameChain(((Component)val2).gameObject), csv)) { return true; } } } catch { } return false; } private static List FindTeleportInteractMethods() { List list = new List(); try { MethodInfo[] methods = typeof(Teleport).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo == null) && !(methodInfo.ReturnType != typeof(bool)) && (string.Equals(methodInfo.Name, "Interact", StringComparison.Ordinal) || methodInfo.Name.EndsWith(".Interact", StringComparison.Ordinal))) { ParameterInfo[] parameters = methodInfo.GetParameters(); bool flag = parameters.Any((ParameterInfo item) => item.ParameterType == typeof(Humanoid) || typeof(Humanoid).IsAssignableFrom(item.ParameterType) || item.ParameterType.IsAssignableFrom(typeof(Player))); bool flag2 = parameters.Any((ParameterInfo item) => item.ParameterType == typeof(bool)); if (flag && flag2 && !list.Contains(methodInfo)) { list.Add(methodInfo); } } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Teleport-Interact-Signatursuche fehlgeschlagen: " + ex.Message)); } } return list; } private static bool HasTeleportWithin(Teleport[] teleports, Vector3 position, float radius) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (teleports == null) { return false; } foreach (Teleport val in teleports) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null)) { Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).IsValid() && HierarchyDistance(position, ((Component)val).transform, 6) <= radius) { return true; } } } return false; } private static bool TeleportInteractPrefix(Teleport __instance, object[] __args, ref DungeonEntryNotificationState __state) { //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) __state = null; try { Humanoid val = null; bool flag = false; bool flag2 = false; if (__args != null) { foreach (object obj in __args) { if ((Object)(object)val == (Object)null) { Humanoid val2 = (Humanoid)((obj is Humanoid) ? obj : null); if (val2 != null) { val = val2; continue; } } if (!flag2 && obj is bool flag3) { flag = flag3; flag2 = true; } } } if (flag || !IsEnabled() || (Object)(object)__instance == (Object)null) { return true; } Player val3 = (Player)(object)((val is Player) ? val : null); if ((Object)(object)val3 == (Object)null || (Object)(object)val3 != (Object)(object)Player.m_localPlayer) { return true; } bool flag4 = false; try { flag4 = ((Character)val3).InInterior(); } catch { } if (flag4) { return true; } DungeonGenerator val4 = ResolveDungeonNearEntrance(__instance, ((Component)__instance).transform.position) ?? FindLearnedDungeonForGuardian(((Component)__instance).transform.position); if ((Object)(object)val4 == (Object)null) { return true; } RegisterDungeon(val4); string text = DungeonId(val4); RememberEntranceSnapshot(text, __instance); if (IsServer()) { EnsureScheduleIfServer(val4); } __state = new DungeonEntryNotificationState { Dungeon = val4, DungeonId = text }; string text2 = ReadZdoString((Component)(object)val4, "ChallengeHub.DungeonLifecycle.State"); if (string.IsNullOrWhiteSpace(text2)) { text2 = "idle"; } DateTime dateTime = ParseDate(ReadZdoString((Component)(object)val4, "ChallengeHub.DungeonLifecycle.NextResetUtc")); bool flag5 = EnableAutomaticReset != null && EnableAutomaticReset.Value && dateTime != DateTime.MinValue && NetworkUtcNow() >= dateTime; int num; switch (text2) { default: num = ((text2 == "generating") ? 1 : 0); break; case "waiting_empty": case "unloading": case "prepared": num = 1; break; } bool flag6 = (byte)num != 0; if (!flag5 && !flag6) { return true; } if (IsServer()) { if (text2 == "unloading" || text2 == "generating") { LocalMessage("Dungeon-Reset laeuft bereits. Der Eingang wird nach Abschluss automatisch freigegeben."); return false; } string reason = (flag5 ? "automatic_entry_trigger" : "pending_entry_trigger"); if (QueueResetServer(val4, reason) == ServerQueueResult.Failed) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Dungeon-Eingangsvorab-Trigger konnte Reset nicht starten; fail-open: " + text)); } return true; } LocalMessage(PlayersInside(val4) ? "Dungeon-Reset ist vorgemerkt und wartet auf Spieler im Innenraum. Eingang bleibt voruebergehend gesperrt." : "Faelliger Dungeon-Reset startet vor dem Eintritt. Nach Abschluss erneut E druecken."); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Dungeon-Eingang hat Lifecycle vor Teleport geweckt: " + text + "; Zustand=" + text2 + "; AutomatischFaellig=" + flag5)); } return false; } if (!RequestEntranceResetFromServer(val4, __instance, flag5 ? "automatic_entry_trigger" : "pending_entry_trigger")) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("Dungeon-Eingangsvorab-Trigger nicht sendbar; fail-open: " + text)); } return true; } LocalMessage("Dungeon-Reset wird vor dem Eintritt serverseitig ausgefuehrt. Nach Abschluss erneut E druecken."); return false; } catch (Exception ex) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)("Dungeon-Eingangsvorab-Trigger fehlgeschlagen; fail-open: " + ex.Message)); } return true; } } private static void TeleportHoverTextPostfix(Teleport __instance, ref string __result) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || !TalentStore.HasSkill("orientation.dungeon_memory")) { return; } try { DungeonGenerator val = ResolveDungeonNearEntrance(__instance, ((Component)__instance).transform.position) ?? ResolveDungeon(__instance); if (!((Object)(object)val == (Object)null)) { string text = (DungeonTalentRewardFeature.IsDungeonCleared(val) ? "Geleert" : "Aktiv"); __result = __result + "\n" + text + " — " + BuildDungeonStatus(val) + ""; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Dungeon-Gedaechtnis-Hover fehlgeschlagen: " + ex.Message)); } } } private static void TeleportInteractPostfix(bool __result, DungeonEntryNotificationState __state) { if (!__result || __state == null || (Object)(object)__state.Dungeon == (Object)null || !TalentStore.HasSkill("orientation.dungeon_clock")) { return; } try { string text = BuildDungeonStatus(__state.Dungeon); LocalMessage("Dungeon betreten\n" + text); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Dungeon-Eintritt zeigt Reset-Timer: " + (__state.DungeonId ?? DungeonId(__state.Dungeon)) + "; " + text)); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Dungeon-Eintritts-Timer konnte nicht angezeigt werden: " + ex.Message)); } } } private static bool RequestEntranceResetFromServer(DungeonGenerator dungeon, Teleport entrance, string reason) { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)dungeon == (Object)null || ZRoutedRpc.instance == null || !rpcRegistered) { return false; } string dungeonId = DungeonId(dungeon); if (PendingClientResetRequests.Values.Any((PendingClientResetRequest item) => item != null && (Object)(object)item.Guardian == (Object)null && string.Equals(item.DungeonId, dungeonId, StringComparison.OrdinalIgnoreCase))) { return true; } string text = Guid.NewGuid().ToString("N"); PendingClientResetRequests[text] = new PendingClientResetRequest { RequestId = text, Guardian = null, DungeonId = dungeonId, SentAtUtc = DateTime.UtcNow }; string text2 = (((Object)(object)entrance != (Object)null) ? EncodePosition(((Component)entrance).transform.position) : string.Empty); string text3 = text + "|" + EncodePosition(((Component)dungeon).transform.position) + "|" + text2 + "|" + (reason ?? "entry_trigger"); long num = ResolveServerRpcTarget(); ZRoutedRpc.instance.InvokeRoutedRPC(num, "ChallengeHub_DungeonReset_Request_v2230", new object[1] { text3 }); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Dungeon-Eingang hat Reset-Anfrage an Server gesendet: Request=" + text + "; Dungeon=" + dungeonId + "; Target=" + num)); } return true; } private static IEnumerator RegisterRpcWhenReady() { while (ZRoutedRpc.instance == null) { yield return (object)new WaitForSeconds(1f); } if (rpcRegistered) { yield break; } try { ZRoutedRpc.instance.Register("ChallengeHub_DungeonReset_Request_v2230", (Action)RPC_ResetRequest); ZRoutedRpc.instance.Register("ChallengeHub_DungeonReset_Ack_v2230", (Action)RPC_ResetAck); ZRoutedRpc.instance.Register("ChallengeHub_DungeonReset_Regenerate_v2230", (Action)RPC_RegenerateRequest); ZRoutedRpc.instance.Register("ChallengeHub_DungeonReset_RegenerateAck_v2230", (Action)RPC_RegenerateAck); ZRoutedRpc.instance.Register("ChallengeHub_DungeonReset_EntranceRepair_v2230", (Action)RPC_EntranceRepair); rpcRegistered = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Dungeon-Lifecycle RPCs 2.6.4 registriert (Request/Ack/Regenerate/RegenerateAck/EntranceRepair)."); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Dungeon-Lifecycle RPC-Registrierung fehlgeschlagen: " + ex.Message)); } } } private static void ResumeQueuedLifecycleWhenLoaded(DungeonGenerator dungeon) { if (!((Object)(object)dungeon == (Object)null) && !((Object)(object)plugin == (Object)null)) { ((MonoBehaviour)plugin).StartCoroutine(ResumeQueuedLifecycleWhenReady(dungeon)); } } private static IEnumerator ResumeQueuedLifecycleWhenReady(DungeonGenerator dungeon) { for (int attempt = 0; attempt < 20; attempt++) { if ((Object)(object)dungeon == (Object)null) { yield break; } if (IsServer() && HasValidZdo(dungeon)) { break; } yield return (object)new WaitForSeconds(0.5f); } if (!IsEnabled() || !IsServer() || (Object)(object)dungeon == (Object)null || !HasValidZdo(dungeon)) { yield break; } string text = NormalizeLifecycleStateForCurrentBuild(dungeon); if (text == "waiting_empty" || text == "prepared") { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Gespeicherten Dungeon-Reset beim Laden wieder aufgenommen: " + DungeonId(dungeon) + "; Zustand=" + text)); } StartImmediateLifecycle(dungeon, "world_load_resume"); } } private static bool HasValidZdo(DungeonGenerator dungeon) { try { ZDO val = DungeonWorldRegistry.ResolveStateZdo(dungeon, IsServer()); return val != null && val.IsValid(); } catch { return false; } } private static bool IsUsableLifecycleView(ZNetView view) { try { return view != null && (Object)(object)view != (Object)null && (Object)(object)((Component)view).gameObject != (Object)null && view.IsValid() && view.GetZDO() != null; } catch { return false; } } private static ZNetView ViewOnOrAbove(Component component) { if ((Object)(object)component == (Object)null) { return null; } try { ZNetView component2 = component.GetComponent(); if (IsUsableLifecycleView(component2)) { return component2; } ZNetView componentInParent = component.GetComponentInParent(); return IsUsableLifecycleView(componentInParent) ? componentInParent : null; } catch { return null; } } private static void RememberLifecycleAnchor(string dungeonId, Teleport entrance) { if (!string.IsNullOrWhiteSpace(dungeonId) && !((Object)(object)entrance == (Object)null)) { ZNetView val = ViewOnOrAbove((Component)(object)entrance); if (IsUsableLifecycleView(val)) { LifecycleAnchors[dungeonId] = val; MissingLifecycleAnchorWarnings.Remove(dungeonId); NextLifecycleAnchorProbeAt.Remove(dungeonId); } } } private static ZNetView FindEntranceLifecycleAnchor(DungeonGenerator dungeon) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_0080: 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) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)dungeon == (Object)null) { return null; } string text = DungeonId(dungeon); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(((Component)dungeon).transform.position.x, ((Component)dungeon).transform.position.z); Teleport val2 = null; float num = 220f; try { Teleport[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Teleport val3 in array) { if ((Object)(object)val3 == (Object)null || (Object)(object)((Component)val3).gameObject == (Object)null) { continue; } Scene scene = ((Component)val3).gameObject.scene; if (!((Scene)(ref scene)).IsValid() || !IsUsableLifecycleView(ViewOnOrAbove((Component)(object)val3))) { continue; } Vector3 position = ((Component)val3).transform.position; float num2 = Vector2.Distance(val, new Vector2(position.x, position.z)); if (!(num2 >= num)) { DungeonGenerator val4 = ResolveDungeonNearEntrance(val3, position); if (!((Object)(object)val4 == (Object)null) && string.Equals(DungeonId(val4), text, StringComparison.OrdinalIgnoreCase)) { val2 = val3; num = num2; } } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Dungeon-Lifecycle-Ankersuche am Eingang fehlgeschlagen: " + ex.Message)); } } if ((Object)(object)val2 == (Object)null) { return null; } RememberLifecycleAnchor(text, val2); if (!LifecycleAnchors.TryGetValue(text, out var value) || !IsUsableLifecycleView(value)) { return null; } return value; } private static ZNetView ResolveLifecycleView(DungeonGenerator dungeon, bool allowEntranceSearch) { if ((Object)(object)dungeon == (Object)null) { return null; } ZNetView val = ViewOnOrAbove((Component)(object)dungeon); if (IsUsableLifecycleView(val)) { return val; } string key = DungeonId(dungeon); if (LifecycleAnchors.TryGetValue(key, out var value)) { if (IsUsableLifecycleView(value)) { return value; } LifecycleAnchors.Remove(key); } if (!allowEntranceSearch) { return null; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (NextLifecycleAnchorProbeAt.TryGetValue(key, out var value2) && realtimeSinceStartup < value2) { return null; } NextLifecycleAnchorProbeAt[key] = realtimeSinceStartup + 10f; return FindEntranceLifecycleAnchor(dungeon); } private static bool EnsureServerOwnsView(ZNetView view, string context) { if (!IsServer() || (Object)(object)view == (Object)null || !view.IsValid() || view.GetZDO() == null || ZDOMan.instance == null) { return false; } try { return ValheimNetworkCompatibility.TryTakeServerOwnership(view.GetZDO(), context); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("Server-ZDO-Authority Exception: " + (context ?? "unknown") + " -> " + ex.Message)); } return false; } } private static bool EnsureServerLifecycleAuthority(DungeonGenerator dungeon, string context) { if (!IsServer() || (Object)(object)dungeon == (Object)null) { return false; } string text = DungeonId(dungeon); ZDO val = DungeonWorldRegistry.ResolveStateZdo(dungeon); if (val == null || !val.IsValid()) { if (MissingLifecycleAnchorWarnings.Add(text)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("Dungeon-Lifecycle besitzt keinen Welt-Registry-ZDO: " + text + "; Kontext=" + context)); } } return false; } if (!ValheimNetworkCompatibility.TryTakeServerOwnership(val, context ?? "dungeon_lifecycle")) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Server konnte Dungeon-Registry-ZDO nicht uebernehmen: " + text + "; Kontext=" + context)); } return false; } MissingLifecycleAnchorWarnings.Remove(text); if (LoggedLifecycleAnchors.Add(text)) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Dungeon-Weltregistry bereit: " + text + "; ServerAuthority=true")); } } return true; } private static bool IsImmediateResetReason(string reason) { string text = (reason ?? string.Empty).ToLowerInvariant(); if (!text.Contains("guardian") && !text.Contains("manual") && !text.Contains("rpc") && !text.Contains("entry")) { return text.Contains("world_load_resume"); } return true; } private static void StartImmediateLifecycle(DungeonGenerator dungeon, string reason) { if (!IsEnabled() || !IsServer() || (Object)(object)plugin == (Object)null || (Object)(object)dungeon == (Object)null) { return; } RegisterDungeon(dungeon); string text = DungeonId(dungeon); if (string.IsNullOrWhiteSpace(text)) { return; } if (ImmediateLifecycleWorkers.Contains(text)) { if (ImmediateLifecycleHeartbeat.TryGetValue(text, out var value) && Time.realtimeSinceStartup - value < 4f) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Dungeon-Sofortworker laeuft bereits: " + text)); } return; } ImmediateLifecycleWorkers.Remove(text); ImmediateLifecycleHeartbeat.Remove(text); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Verwaisten Dungeon-Sofortworker ersetzt: " + text)); } } ImmediateLifecycleWorkers.Add(text); ImmediateLifecycleHeartbeat[text] = Time.realtimeSinceStartup; ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Dungeon-Sofortworker gestartet: " + text + "; Grund=" + reason)); } ((MonoBehaviour)plugin).StartCoroutine(ImmediateLifecycleWorker(dungeon, text, reason)); } private static IEnumerator ImmediateLifecycleWorker(DungeonGenerator dungeon, string id, string reason) { bool waitingLogged = false; while ((Object)(object)dungeon != (Object)null && IsEnabled() && IsServer()) { ImmediateLifecycleHeartbeat[id] = Time.realtimeSinceStartup; string text = NormalizeLifecycleStateForCurrentBuild(dungeon); if (text == "prepared") { if (PlayersInside(dungeon)) { if (!waitingLogged) { waitingLogged = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Dungeon-Reset wartet weiterhin auf leeren Innenraum: " + id)); } } yield return (object)new WaitForSeconds(1f); continue; } if (!GeneratePreparedDungeon(dungeon, "immediate_prepared_resume")) { TargetedResetFeature.ReleasePendingResetGuardians(id, "Vorbereiteter Dungeon konnte nicht an die Location-Reset-Engine uebergeben werden."); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Vorbereiteter Dungeon konnte nicht an die Location-Reset-Engine uebergeben werden. Dungeon=" + id)); } } break; } if (text != "waiting_empty") { break; } if (PlayersInside(dungeon)) { WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.EmptySinceUtc", string.Empty); if (!waitingLogged) { waitingLogged = true; ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Dungeon-Reset wartet, bis alle Spieler draussen sind: " + id)); } } yield return (object)new WaitForSeconds(1f); continue; } WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.EmptySinceUtc", NetworkUtcNow().ToString("O", CultureInfo.InvariantCulture)); if (!ActiveUnloads.Contains(id)) { ActiveUnloads.Add(id); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)("Dungeon ist leer; sofortiger Reset startet jetzt: " + id + "; Grund=" + reason)); } yield return ((MonoBehaviour)plugin).StartCoroutine(UnloadDungeon(dungeon, id)); } break; } bool num = (Object)(object)dungeon != (Object)null && string.Equals(ReadZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.State"), "waiting_empty", StringComparison.Ordinal); ImmediateLifecycleWorkers.Remove(id); ImmediateLifecycleHeartbeat.Remove(id); if (num) { StartImmediateLifecycle(dungeon, "immediate_retry_after_player_race"); } } private static IEnumerator ServerLifecycleWorker() { while (true) { yield return (object)new WaitForSeconds(Mathf.Max(1f, (WorkerSeconds != null) ? WorkerSeconds.Value : 2f)); if (!ChallengeHubServerGateFeature.GameplayAllowed || !IsEnabled() || !IsServer() || (Object)(object)ZNetScene.instance == (Object)null) { continue; } DungeonGenerator[] array = SnapshotKnownDungeons(); if (array.Length == 0) { continue; } DungeonGenerator[] array2 = array; foreach (DungeonGenerator dungeon in array2) { if ((Object)(object)dungeon == (Object)null) { continue; } string preparedDungeonId = null; try { preparedDungeonId = ProcessDungeonLifecycleStep(dungeon); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Dungeon-Lifecycle Workerfehler: " + ex.Message)); } } if (string.IsNullOrEmpty(preparedDungeonId)) { continue; } yield return (object)new WaitForEndOfFrame(); try { RepairAndBroadcastDungeonEntrances(preparedDungeonId); TargetedResetFeature.CompletePendingResetGuardians(preparedDungeonId, ((Component)dungeon).transform.position); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Dungeon-Reset in derselben Sitzung abgeschlossen: " + preparedDungeonId)); } } catch (Exception ex2) { string text = "Dungeon wurde neu erzeugt, aber die Eingangsreparatur ist fehlgeschlagen: " + ex2.Message; ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)(text + "; Dungeon=" + preparedDungeonId)); } TargetedResetFeature.ReleasePendingResetGuardians(preparedDungeonId, text); } } } } private static string ProcessDungeonLifecycleStep(DungeonGenerator dungeon) { if (!ChallengeHubServerGateFeature.GameplayAllowed) { return null; } RegisterDungeon(dungeon); bool flag = EnableAutomaticReset != null && EnableAutomaticReset.Value; string text = ReadZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.State"); if (!flag && (string.IsNullOrWhiteSpace(text) || text == "idle")) { return null; } EnsureScheduleIfServer(dungeon); string text2 = NormalizeLifecycleStateForCurrentBuild(dungeon); DateTime dateTime = NetworkUtcNow(); if (text2 == "prepared") { string text3 = DungeonId(dungeon); if (!ActiveUnloads.Contains(text3)) { ActiveUnloads.Add(text3); ((MonoBehaviour)plugin).StartCoroutine(UnloadDungeon(dungeon, text3)); } return null; } if (text2 == "idle" && flag) { DateTime dateTime2 = ParseDate(ReadZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.NextResetUtc")); if (dateTime2 != DateTime.MinValue && dateTime >= dateTime2) { return null; } } if (text2 != "waiting_empty") { return null; } if (PlayersInside(dungeon)) { WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.EmptySinceUtc", string.Empty); return null; } DateTime dateTime3 = ParseDate(ReadZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.EmptySinceUtc")); if (dateTime3 == DateTime.MinValue) { WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.EmptySinceUtc", dateTime.ToString("O", CultureInfo.InvariantCulture)); return null; } if ((dateTime - dateTime3).TotalSeconds < (double)EffectiveEmptyGraceSeconds()) { return null; } string text4 = DungeonId(dungeon); if (ActiveUnloads.Contains(text4)) { return null; } ActiveUnloads.Add(text4); ((MonoBehaviour)plugin).StartCoroutine(UnloadDungeon(dungeon, text4)); return null; } private static IEnumerator UnloadDungeon(DungeonGenerator dungeon, string id) { if ((Object)(object)dungeon == (Object)null) { ActiveUnloads.Remove(id); yield break; } StableDungeonContext context = DungeonWorldRegistry.Resolve(dungeon); ZDO stateZdo = context?.StateZdo; if (context == null || stateZdo == null) { TargetedResetFeature.ReleasePendingResetGuardians(id, "Stabile Dungeon-ID oder Welt-Registry konnte nicht aufgeloest werden."); ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("Stabile Dungeon-ID oder Welt-Registry konnte nicht aufgeloest werden. Dungeon=" + id)); } ActiveUnloads.Remove(id); yield break; } id = context.DungeonKey; if (PlayersInside(dungeon)) { DungeonWorldRegistry.Write(stateZdo, "ChallengeHub.DungeonLifecycle.State", "waiting_empty"); ActiveUnloads.Remove(id); yield break; } int tier = DetermineTier(dungeon); Bounds dungeonBounds = GetDungeonBounds(dungeon); List list = CaptureDungeonEntrances(dungeon, dungeonBounds); if (list.Count > 0) { PendingEntranceSnapshots[id] = list; PrepareAndBroadcastEntranceTargets(id, list); } DungeonWorldRegistry.Write(stateZdo, "ChallengeHub.DungeonLifecycle.State", "unloading"); DungeonWorldRegistry.Write(stateZdo, "ChallengeHub.DungeonLifecycle.Schema", "2.6.4"); DungeonLocationResetResult resetResult = new DungeonLocationResetResult(); yield return ((MonoBehaviour)plugin).StartCoroutine(DungeonLocationResetEngine.Execute(dungeon, resetResult)); if (!resetResult.Success) { string text = (string.IsNullOrWhiteSpace(resetResult.Failure) ? "Location-basierter Dungeon-Reset ist fehlgeschlagen." : resetResult.Failure); DungeonWorldRegistry.Write(stateZdo, "ChallengeHub.DungeonLifecycle.State", "error"); DungeonWorldRegistry.Write(stateZdo, "ChallengeHub.DungeonLifecycle.Reason", text); TargetedResetFeature.ReleasePendingResetGuardians(id, text); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Dungeon-Location-Reset fehlgeschlagen: " + id + " -> " + text + "; Preflight=" + resetResult.PreflightSummary + "; Verifikation=" + resetResult.VerificationSummary)); } ActiveUnloads.Remove(id); yield break; } DateTime dateTime = NetworkUtcNow(); DateTime dateTime2 = NextResetUtcForTier(tier, dateTime); DungeonWorldRegistry.Write(stateZdo, "ChallengeHub.DungeonLifecycle.Schema", "2.6.4"); DungeonWorldRegistry.Write(stateZdo, "ChallengeHub.DungeonLifecycle.LastResetUtc", dateTime.ToString("O", CultureInfo.InvariantCulture)); DungeonWorldRegistry.Write(stateZdo, "ChallengeHub.DungeonLifecycle.NextResetUtc", dateTime2.ToString("O", CultureInfo.InvariantCulture)); DungeonWorldRegistry.Write(stateZdo, "ChallengeHub.DungeonLifecycle.RequestedAtUtc", string.Empty); DungeonWorldRegistry.Write(stateZdo, "ChallengeHub.DungeonLifecycle.EmptySinceUtc", string.Empty); DungeonWorldRegistry.Write(stateZdo, "ChallengeHub.DungeonLifecycle.PreparedAtUtc", string.Empty); DungeonWorldRegistry.Write(stateZdo, "ChallengeHub.DungeonLifecycle.Reason", "location_reset_2.6.4"); DungeonWorldRegistry.Write(stateZdo, "ChallengeHub.DungeonLifecycle.State", "idle"); if ((Object)(object)resetResult.RuntimeDungeon != (Object)null) { RegisterDungeon(resetResult.RuntimeDungeon); } if (!RepairAndBroadcastDungeonEntrances(id)) { DungeonWorldRegistry.Write(stateZdo, "ChallengeHub.DungeonLifecycle.State", "error"); DungeonWorldRegistry.Write(stateZdo, "ChallengeHub.DungeonLifecycle.Reason", "entrance_pair_unresolved_2.6.4"); TargetedResetFeature.ReleasePendingResetGuardians(id, "Neuer Dungeon wurde erzeugt, aber Aussen- und Innen-Teleport konnten nicht eindeutig gepaart werden."); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("Neuer Dungeon wurde erzeugt, aber Aussen- und Innen-Teleport konnten nicht eindeutig gepaart werden. Dungeon=" + id + ". Der Reset-Waechter bleibt aktiv und wird nicht verbraucht.")); } ActiveUnloads.Remove(id); yield break; } DungeonTalentRewardFeature.ResetRewardState(stateZdo, id); OrientationMarkerFeature.OnDungeonResetCompleted(id, context.ExteriorPosition, stateZdo); TargetedResetFeature.CompletePendingResetGuardians(id, context.ExteriorPosition); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)("Dungeon-Reset 2.6.4 abgeschlossen: " + id + "; Generation=" + resetResult.Generation + "; Geloescht=" + resetResult.DestroyedCount + "/" + resetResult.CandidateCount + "; NeueZDOs=" + resetResult.NewObjectCount + "; NaechsterReset=" + dateTime2.ToString("O", CultureInfo.InvariantCulture))); } ActiveUnloads.Remove(id); } private unsafe static IEnumerator UnloadDungeonLegacy(DungeonGenerator dungeon, string id) { int networkObjects = 0; int rootsRemoved = 0; int scannedViews = 0; int scannedTransforms = 0; string failure = string.Empty; if ((Object)(object)dungeon == (Object)null) { ActiveUnloads.Remove(id); yield break; } if (PlayersInside(dungeon)) { SetState(dungeon, "waiting_empty"); ActiveUnloads.Remove(id); yield break; } SetState(dungeon, "unloading"); Bounds bounds = GetDungeonBounds(dungeon); List list = CaptureDungeonEntrances(dungeon, bounds); if (list.Count > 0) { PendingEntranceSnapshots[id] = list; PrepareAndBroadcastEntranceTargets(id, list); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Dungeon-Eingangsziele vor Entladung gesichert und auf stabile Proxies umgestellt: " + id + "; Eingaenge=" + list.Count)); } } int num = DisableDungeonSmokeRenderers(bounds); if (num > 0) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Dungeon-Rauchsysteme vor Entladung deaktiviert: " + num + "; Dungeon=" + id)); } yield return (object)new WaitForEndOfFrame(); } List networkViews = new List(); List roomRoots = new List(); HashSet hashSet = new HashSet(); HashSet hashSet2 = new HashSet(); foreach (Transform item in SnapshotPlacedRoomRoots(dungeon)) { if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).gameObject == (Object)null || !IsRuntimeSceneObject(((Component)item).gameObject) || !((Bounds)(ref bounds)).Contains(item.position) || (Object)(object)((Component)item).GetComponentInChildren(true) != (Object)null) { continue; } scannedTransforms++; ZNetView[] componentsInChildren = ((Component)item).GetComponentsInChildren(true); foreach (ZNetView val in componentsInChildren) { scannedViews++; if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && !((Object)(object)((Component)val).GetComponentInParent() != (Object)null) && hashSet.Add(((Object)((Component)val).gameObject).GetInstanceID())) { networkViews.Add(val); } } if (hashSet2.Add(((Object)((Component)item).gameObject).GetInstanceID())) { roomRoots.Add(item); } } try { Scene activeScene = SceneManager.GetActiveScene(); GameObject[] array = (((Scene)(ref activeScene)).IsValid() ? ((Scene)(ref activeScene)).GetRootGameObjects() : Array.Empty()); foreach (GameObject val2 in array) { scannedTransforms++; if ((Object)(object)val2 == (Object)null || !IsRuntimeSceneObject(val2) || string.Equals(((Object)val2).name, "ChallengeHub_DungeonTargetProxy", StringComparison.Ordinal) || (Object)(object)val2 == (Object)(object)((Component)dungeon).gameObject || (Object)(object)val2.GetComponentInChildren(true) != (Object)null || (Object)(object)val2.GetComponentInChildren(true) != (Object)null) { continue; } ZNetView[] componentsInChildren2 = val2.GetComponentsInChildren(true); bool flag = ((Bounds)(ref bounds)).Contains(val2.transform.position); ZNetView[] componentsInChildren = componentsInChildren2; foreach (ZNetView val3 in componentsInChildren) { scannedViews++; if (!((Object)(object)val3 == (Object)null) && !((Object)(object)((Component)val3).gameObject == (Object)null) && ((Bounds)(ref bounds)).Contains(((Component)val3).transform.position) && !((Object)(object)((Component)val3).GetComponentInParent() != (Object)null)) { flag = true; if (hashSet.Add(((Object)((Component)val3).gameObject).GetInstanceID())) { networkViews.Add(val3); } } } if (flag && hashSet2.Add(((Object)val2).GetInstanceID())) { roomRoots.Add(val2.transform); } } for (int k = 0; k < ((Component)dungeon).transform.childCount; k++) { Transform child = ((Component)dungeon).transform.GetChild(k); if ((Object)(object)child == (Object)null || (Object)(object)((Component)child).gameObject == (Object)null || !IsRuntimeSceneObject(((Component)child).gameObject) || (Object)(object)((Component)child).GetComponentInChildren(true) != (Object)null) { continue; } ZNetView[] componentsInChildren = ((Component)child).GetComponentsInChildren(true); foreach (ZNetView val4 in componentsInChildren) { scannedViews++; if (!((Object)(object)val4 == (Object)null) && !((Object)(object)((Component)val4).gameObject == (Object)null) && hashSet.Add(((Object)((Component)val4).gameObject).GetInstanceID())) { networkViews.Add(val4); } } if (hashSet2.Add(((Object)((Component)child).gameObject).GetInstanceID())) { roomRoots.Add(child); } } } catch (Exception ex) { failure = "Dungeon-Innenraum konnte nicht aus den Runtime-Szenenwurzeln gesammelt werden: " + ex.Message; } Vector3 val5 = ReadField(dungeon, "m_zoneCenter", ((Component)dungeon).transform.position); ManualLogSource log3 = Plugin.Log; if (log3 != null) { string[] obj = new string[18] { "Dungeon-Entladeanalyse: ", id, "; GeneratorPosition=", ((object)((Component)dungeon).transform.position/*cast due to .constrained prefix*/).ToString(), "; RawZoneCenter=", null, null, null, null, null, null, null, null, null, null, null, null, null }; Vector3 val6 = val5; obj[5] = ((object)(*(Vector3*)(&val6))/*cast due to .constrained prefix*/).ToString(); obj[6] = "; BoundsCenter="; obj[7] = ((object)((Bounds)(ref bounds)).center/*cast due to .constrained prefix*/).ToString(); obj[8] = "; BoundsSize="; obj[9] = ((object)((Bounds)(ref bounds)).size/*cast due to .constrained prefix*/).ToString(); obj[10] = "; ViewsGescannt="; obj[11] = scannedViews.ToString(); obj[12] = "; SzenenwurzelnGescannt="; obj[13] = scannedTransforms.ToString(); obj[14] = "; NetzwerkKandidaten="; obj[15] = networkViews.Count.ToString(); obj[16] = "; RaumKandidaten="; obj[17] = roomRoots.Count.ToString(); log3.LogInfo((object)string.Concat(obj)); } if (string.IsNullOrEmpty(failure) && networkViews.Count == 0 && roomRoots.Count == 0) { failure = "Keine Innenraumobjekte innerhalb der Dungeon-Grenzen gefunden. Reset wurde NICHT als erfolgreich markiert."; } if (string.IsNullOrEmpty(failure)) { foreach (ZNetView item2 in from item in networkViews where (Object)(object)item != (Object)null orderby TransformDepth(((Component)item).transform) descending select item) { if ((Object)(object)item2 == (Object)null) { continue; } try { if (item2.IsValid()) { if (!EnsureServerOwnsView(item2, "dungeon_unload:" + id)) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)("Dungeon-Entladen: Server konnte ZDO nicht synchron uebernehmen: " + ((Object)((Component)item2).gameObject).name)); } continue; } item2.Destroy(); networkObjects++; } } catch (Exception ex2) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogDebug((object)("Dungeon-Entladen: ZNetView uebersprungen: " + ex2.Message)); } } yield return (object)new WaitForEndOfFrame(); } int settleFrames = 0; int remainingViews = CountLiveViews(networkViews); while (remainingViews > 0 && settleFrames < 120) { settleFrames++; yield return (object)new WaitForEndOfFrame(); remainingViews = CountLiveViews(networkViews); } if (remainingViews > 0) { failure = "Netzwerkabbau wurde nicht vollstaendig bestaetigt; verbleibende ZNetViews=" + remainingViews + ". Lokale Raumwurzeln wurden zum Schutz von ZNetScene nicht zerstoert."; } else { int num2 = ChallengeHubZNetSceneRemoveObjectsNullGuardPatch.RepairNow(); if (num2 > 0) { ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)("Dungeon-ZNetScene-Referenzen vor Raumabbau bereinigt: " + id + "; Entfernt=" + num2)); } } yield return (object)new WaitForEndOfFrame(); int rootsSkippedForNetworkViews = 0; foreach (Transform item3 in roomRoots.Where((Transform item) => (Object)(object)item != (Object)null).OrderByDescending(TransformDepth)) { if ((Object)(object)item3 == (Object)null || (Object)(object)((Component)item3).gameObject == (Object)null) { continue; } try { if (!((Object)(object)((Component)item3).GetComponentInChildren(true) != (Object)null) && !((Object)(object)((Component)item3).GetComponentInChildren(true) != (Object)null)) { if (!HasLiveViewInHierarchy(item3)) { Object.Destroy((Object)(object)((Component)item3).gameObject); rootsRemoved++; goto IL_0ac0; } rootsSkippedForNetworkViews++; } } catch { goto IL_0ac0; } continue; IL_0ac0: yield return (object)new WaitForEndOfFrame(); } ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogInfo((object)("Dungeon-Netzwerkabbau stabilisiert: " + id + "; Warteframes=" + settleFrames + "; VerbleibendeViews=" + remainingViews + "; RaumwurzelnMitNetzreferenzUebersprungen=" + rootsSkippedForNetworkViews)); } if (rootsSkippedForNetworkViews > 0) { failure = "Mindestens eine Raumwurzel besitzt noch einen gueltigen ZNetView; Neuerzeugung wurde zum Schutz der Streamingtabellen abgebrochen."; } else { yield return (object)new WaitForEndOfFrame(); ChallengeHubZNetSceneRemoveObjectsNullGuardPatch.RepairNow(); } } yield return (object)new WaitForEndOfFrame(); yield return (object)new WaitForEndOfFrame(); if (string.IsNullOrEmpty(failure) && networkObjects == 0 && rootsRemoved == 0) { failure = "Es wurde kein einziges Dungeonobjekt entfernt. Reset wurde abgebrochen."; } } if (string.IsNullOrEmpty(failure) && !ClearGeneratorRuntimeState(dungeon)) { failure = "Statische DungeonGenerator-Verbindungslisten konnten nicht vollständig geleert werden."; } if (string.IsNullOrEmpty(failure)) { WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.Schema", "2.6.4"); WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.PreparedAtUtc", NetworkUtcNow().ToString("O", CultureInfo.InvariantCulture)); WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.EmptySinceUtc", string.Empty); SetState(dungeon, "prepared"); ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogInfo((object)("Dungeon sicher entladen und fuer die Neuerzeugung in derselben Sitzung vorbereitet: " + id + "; Netzwerkobjekte=" + networkObjects + "; Raumwurzeln=" + rootsRemoved)); } yield return (object)new WaitForSeconds(1f); if (!PlayersInside(dungeon)) { if (GeneratePreparedDungeon(dungeon, "same_session_after_unload")) { yield return (object)new WaitForEndOfFrame(); RepairAndBroadcastDungeonEntrances(id); TargetedResetFeature.CompletePendingResetGuardians(id, ((Component)dungeon).transform.position); ManualLogSource log9 = Plugin.Log; if (log9 != null) { log9.LogInfo((object)("Dungeon-Reset ohne Neu-Login vollstaendig abgeschlossen: " + id)); } } else { TargetedResetFeature.ReleasePendingResetGuardians(id, "Dungeon konnte nach dem Entladen nicht in derselben Sitzung neu erzeugt werden."); ManualLogSource log10 = Plugin.Log; if (log10 != null) { log10.LogError((object)("Dungeon konnte nach dem Entladen nicht in derselben Sitzung neu erzeugt werden. Dungeon=" + id)); } } } } else { SetState(dungeon, "error"); TargetedResetFeature.ReleasePendingResetGuardians(id, failure); ManualLogSource log11 = Plugin.Log; if (log11 != null) { log11.LogError((object)("Dungeon konnte nicht sicher entladen werden: " + id + " -> " + failure)); } } ActiveUnloads.Remove(id); } private static GuardianQueueResult RequestReset(DungeonGenerator dungeon, Player player, Piece guardian, string reason, Teleport entrance) { //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)dungeon == (Object)null) { return GuardianQueueResult.EntranceUnresolved; } RegisterDungeon(dungeon); string text = DungeonId(dungeon); if ((Object)(object)entrance != (Object)null) { RememberEntranceSnapshot(text, entrance); } if (IsServer()) { DateTime requestedAtUtc = NetworkUtcNow(); switch (QueueResetServer(dungeon, reason)) { case ServerQueueResult.Failed: if (player != null) { ((Character)player).Message((MessageType)2, "Dungeon-Reset konnte serverseitig nicht gestartet werden. Der Waechter bleibt aktiv; Log pruefen.", 0, (Sprite)null); } return GuardianQueueResult.EntranceUnresolved; case ServerQueueResult.AlreadyPrepared: StartImmediateLifecycle(dungeon, reason); break; } TargetedResetFeature.MarkResetGuardianPending(guardian, text, requestedAtUtc); if (player != null) { ((Character)player).Message((MessageType)2, PlayersInside(dungeon) ? "Dungeon-Reset serverseitig vorgemerkt. Er startet, sobald alle Spieler draussen sind." : "Dungeon-Reset gestartet. Der Innenraum wird jetzt entladen und sofort neu erzeugt.", 0, (Sprite)null); } return GuardianQueueResult.Queued; } if (ZRoutedRpc.instance == null || !rpcRegistered) { if (player != null) { ((Character)player).Message((MessageType)2, "Dungeon-Serververbindung noch nicht bereit. Bitte erneut versuchen. Der Reset-Waechter bleibt aktiv.", 0, (Sprite)null); } return GuardianQueueResult.EntranceUnresolved; } string text2 = Guid.NewGuid().ToString("N"); PendingClientResetRequests[text2] = new PendingClientResetRequest { RequestId = text2, Guardian = guardian, DungeonId = text, SentAtUtc = DateTime.UtcNow }; Vector3 position = ((Component)dungeon).transform.position; string text3 = (((Object)(object)entrance != (Object)null) ? EncodePosition(((Component)entrance).transform.position) : string.Empty); string text4 = text2 + "|" + EncodePosition(position) + "|" + text3 + "|" + (reason ?? "manual"); long num = ResolveServerRpcTarget(); ZRoutedRpc.instance.InvokeRoutedRPC(num, "ChallengeHub_DungeonReset_Request_v2230", new object[1] { text4 }); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Dungeon-Reset-Anfrage an Server gesendet: Request=" + text2 + "; Dungeon=" + text + "; Target=" + num)); } if (player != null) { ((Character)player).Message((MessageType)2, "Dungeon-Reset-Anfrage gesendet. Der Waechter bleibt lila, bis der Server die Anfrage bestaetigt.", 0, (Sprite)null); } return GuardianQueueResult.AwaitingServer; } private static void SendRegenerateRequest(DungeonGenerator dungeon) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) if (ZRoutedRpc.instance != null && !((Object)(object)dungeon == (Object)null)) { string text = Guid.NewGuid().ToString("N"); string text2 = DungeonId(dungeon); PendingClientRegenerateRequests[text] = new PendingClientRegenerateRequest { RequestId = text, DungeonId = text2, SentAtUtc = DateTime.UtcNow }; long num = ResolveServerRpcTarget(); string text3 = text + "|" + SanitizeRpcText(text2) + "|" + EncodePosition(((Component)dungeon).transform.position); ZRoutedRpc.instance.InvokeRoutedRPC(num, "ChallengeHub_DungeonReset_Regenerate_v2230", new object[1] { text3 }); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Dungeon-Neugenerierungsanfrage an Server gesendet: Request=" + text + "; Dungeon=" + text2 + "; Target=" + num)); } } } private unsafe static void RPC_ResetRequest(long sender, string payload) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_0095: 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 (!ChallengeHubServerGateFeature.GameplayAllowed || !IsServer()) { return; } if (!TryDecodeResetRequest(payload, out var requestId, out var position, out var entrancePosition, out var hasEntrancePosition, out var reason)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Dungeon-Reset-Serveranfrage konnte nicht gelesen werden. Absender=" + sender)); } return; } if (!ProcessedServerResetRequests.Add(requestId)) { SendResetAck(sender, requestId, accepted: true, string.Empty, "Anfrage wurde bereits verarbeitet."); return; } DungeonGenerator val = FindNearestDungeonGenerator(position, 350f); if ((Object)(object)val == (Object)null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { string[] obj = new string[6] { "Dungeon-Reset-Serveranfrage abgelehnt: Kein Dungeon nahe ", null, null, null, null, null }; Vector3 val2 = position; obj[1] = ((object)(*(Vector3*)(&val2))/*cast due to .constrained prefix*/).ToString(); obj[2] = "; Request="; obj[3] = requestId; obj[4] = "; Absender="; obj[5] = sender.ToString(); log2.LogWarning((object)string.Concat(obj)); } SendResetAck(sender, requestId, accepted: false, string.Empty, "Server konnte die Dungeoninstanz nicht finden. Umgebung auf dem Server laden und erneut versuchen."); return; } string text = DungeonId(val); if (hasEntrancePosition) { Teleport val3 = FindNearestTeleportRaw(entrancePosition, 32f); if ((Object)(object)val3 != (Object)null) { RememberEntranceSnapshot(text, val3); } } ServerQueueResult serverQueueResult = QueueResetServer(val, string.IsNullOrWhiteSpace(reason) ? "rpc_manual" : reason); if (serverQueueResult == ServerQueueResult.Failed) { SendResetAck(sender, requestId, accepted: false, string.Empty, "Server konnte den Dungeon-Lifecycle nicht starten. Server-Log pruefen."); return; } ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Dungeon-Reset-Serveranfrage angenommen: Request=" + requestId + "; Dungeon=" + text + "; Absender=" + sender + "; QueueStatus=" + serverQueueResult)); } SendResetAck(sender, requestId, accepted: true, text, "Server hat den Dungeon-Reset angenommen."); } private static void SendResetAck(long targetPeerId, string requestId, bool accepted, string dungeonId, string message) { if (ZRoutedRpc.instance != null) { string text = requestId + "|" + (accepted ? "1" : "0") + "|" + (dungeonId ?? string.Empty) + "|" + SanitizeRpcText(message); ZRoutedRpc.instance.InvokeRoutedRPC(targetPeerId, "ChallengeHub_DungeonReset_Ack_v2230", new object[1] { text }); } } private static void RPC_ResetAck(long sender, string payload) { if (!TryDecodeResetAck(payload, out var requestId, out var accepted, out var dungeonId, out var message) || !PendingClientResetRequests.TryGetValue(requestId, out var value)) { return; } PendingClientResetRequests.Remove(requestId); if (!accepted) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Dungeon-Reset vom Server abgelehnt: Request=" + requestId + "; Grund=" + message)); } LocalMessage(string.IsNullOrWhiteSpace(message) ? "Der Server hat den Dungeon-Reset abgelehnt. Der Waechter bleibt aktiv." : (message + " Der Waechter bleibt aktiv.")); return; } string text = (string.IsNullOrWhiteSpace(dungeonId) ? value.DungeonId : dungeonId); if ((Object)(object)value.Guardian != (Object)null) { TargetedResetFeature.MarkResetGuardianPending(value.Guardian, text, value.SentAtUtc); } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Dungeon-Reset vom Server bestaetigt: Request=" + requestId + "; Dungeon=" + text + "; Server=" + sender)); } LocalMessage(((Object)(object)value.Guardian != (Object)null) ? "Server hat den Dungeon-Reset angenommen. Der Waechter bleibt lila, bis die Entladung abgeschlossen ist." : "Server hat den Reset vor dem Eintritt angenommen. Nach Abschluss erneut E druecken."); } private static IEnumerator ClientResetRequestTimeoutWorker() { while (true) { yield return (object)new WaitForSeconds(1f); if (IsServer() || PendingClientResetRequests.Count == 0) { continue; } DateTime now = DateTime.UtcNow; foreach (string item in (from entry in PendingClientResetRequests where (now - entry.Value.SentAtUtc).TotalSeconds >= 10.0 select entry.Key).ToList()) { if (PendingClientResetRequests.TryGetValue(item, out var value)) { PendingClientResetRequests.Remove(item); ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("Dungeon-Reset ohne Server-Bestaetigung abgebrochen: Request=" + item + "; Dungeon=" + value.DungeonId + ". Auf dem Server muss ChallengeHub Valheim 2.12.50 laufen.")); } LocalMessage(((Object)(object)value.Guardian != (Object)null) ? "Keine Bestaetigung vom Server. Auf Client UND Server muss ChallengeHub Valheim 2.12.50 laufen. Der Waechter bleibt aktiv." : "Keine Bestaetigung vom Server. Eingang bleibt aus Sicherheitsgruenden Vanilla; erneut versuchen."); } } } } private unsafe static void RPC_RegenerateRequest(long sender, string payload) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) if (!ChallengeHubServerGateFeature.GameplayAllowed || !IsServer()) { return; } if (!TryDecodeRegenerateRequest(payload, out var requestId, out var dungeonId, out var position)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Dungeon-Neugenerierungsanfrage konnte nicht gelesen werden. Absender=" + sender)); } return; } DungeonGenerator val = FindDungeonById(dungeonId) ?? FindNearestDungeonGenerator(position, 700f) ?? FindNearestDungeonGeneratorHorizontal(position, 220f); if ((Object)(object)val == (Object)null) { SendRegenerateAck(sender, requestId, accepted: false, dungeonId, "Server konnte die vorbereitete Dungeoninstanz nicht finden."); ManualLogSource log2 = Plugin.Log; if (log2 != null) { string[] obj = new string[6] { "Dungeon-Neugenerierung abgelehnt: Kein Dungeon fuer Request=", requestId, "; Dungeon=", dungeonId, "; Position=", null }; Vector3 val2 = position; obj[5] = ((object)(*(Vector3*)(&val2))/*cast due to .constrained prefix*/).ToString(); log2.LogWarning((object)string.Concat(obj)); } return; } string text = DungeonId(val); bool flag = NormalizeLifecycleStateForCurrentBuild(val) == "idle" || GeneratePreparedDungeon(val, "remote_entry"); SendRegenerateAck(sender, requestId, flag, text, flag ? "Location-basierter Dungeon-Reset wurde serverseitig angenommen." : "Dungeon konnte serverseitig nicht neu erzeugt werden. Server-Log pruefen."); if (flag) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Dungeon-Location-Reset-Anfrage erfolgreich angenommen: Request=" + requestId + "; Dungeon=" + text + "; Absender=" + sender)); } } } private static void SendRegenerateAck(long targetPeerId, string requestId, bool accepted, string dungeonId, string message) { if (ZRoutedRpc.instance != null) { string text = requestId + "|" + (accepted ? "1" : "0") + "|" + SanitizeRpcText(dungeonId) + "|" + SanitizeRpcText(message); ZRoutedRpc.instance.InvokeRoutedRPC(targetPeerId, "ChallengeHub_DungeonReset_RegenerateAck_v2230", new object[1] { text }); } } private static void RPC_RegenerateAck(long sender, string payload) { if (!TryDecodeRegenerateAck(payload, out var requestId, out var accepted, out var dungeonId, out var message) || !PendingClientRegenerateRequests.TryGetValue(requestId, out var value)) { return; } PendingClientRegenerateRequests.Remove(requestId); string text = (string.IsNullOrWhiteSpace(dungeonId) ? value.DungeonId : dungeonId); if (!accepted) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("Dungeon-Neugenerierung vom Server abgelehnt: Request=" + requestId + "; Dungeon=" + text + "; Grund=" + message)); } LocalMessage(string.IsNullOrWhiteSpace(message) ? "Dungeon konnte nicht neu erzeugt werden. Server-Log pruefen." : message); return; } ClientRegenerateReadyUntil[text] = DateTime.UtcNow.AddSeconds(20.0); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Dungeon-Neugenerierung vom Server bestaetigt: Request=" + requestId + "; Dungeon=" + text + "; Server=" + sender)); } LocalMessage("Dungeon wurde neu erzeugt. Druecke jetzt erneut E, um einzutreten."); } private static IEnumerator ClientRegenerateRequestTimeoutWorker() { while (true) { yield return (object)new WaitForSeconds(1f); if (IsServer() || PendingClientRegenerateRequests.Count == 0) { continue; } DateTime now = DateTime.UtcNow; foreach (string item in (from entry in PendingClientRegenerateRequests where (now - entry.Value.SentAtUtc).TotalSeconds >= 10.0 select entry.Key).ToList()) { if (PendingClientRegenerateRequests.TryGetValue(item, out var value)) { PendingClientRegenerateRequests.Remove(item); ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("Dungeon-Neugenerierung ohne Server-Bestaetigung abgebrochen: Request=" + item + "; Dungeon=" + value.DungeonId + ". Auf Client UND Server muss ChallengeHub Valheim 2.12.50 laufen.")); } LocalMessage("Keine Server-Bestaetigung fuer die Neuerzeugung. Auf Client und Server muss 2.12.50 laufen."); } } } } private static ServerQueueResult QueueResetServer(DungeonGenerator dungeon, string reason) { if (!ChallengeHubServerGateFeature.GameplayAllowed) { return ServerQueueResult.Failed; } if (!IsServer() || (Object)(object)dungeon == (Object)null) { return ServerQueueResult.Failed; } try { RegisterDungeon(dungeon); if (!EnsureServerLifecycleAuthority(dungeon, "queue_reset")) { return ServerQueueResult.Failed; } string text = DungeonId(dungeon); string text2 = NormalizeLifecycleStateForCurrentBuild(dungeon); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Dungeon-Reset Queue-Pruefung: " + text + "; Zustand=" + text2 + "; Schema=" + ReadZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.Schema") + "; Grund=" + reason)); } switch (text2) { case "prepared": { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)("Dungeon ist bereits vorbereitet und wird sofort neu erzeugt: " + text)); } StartImmediateLifecycle(dungeon, reason); return ServerQueueResult.AlreadyPrepared; } case "waiting_empty": { if (!(WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.RequestedAtUtc", NetworkUtcNow().ToString("O", CultureInfo.InvariantCulture)) & WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.EmptySinceUtc", string.Empty) & WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.Reason", reason ?? "manual_retry") & WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.Schema", "2.6.4"))) { ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogError((object)("Bereits vorgemerkter Dungeon-Reset konnte nicht persistent aktualisiert werden: " + text)); } return ServerQueueResult.Failed; } ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogInfo((object)("Bereits vorgemerkter Dungeon-Reset wurde aktualisiert: " + text)); } StartImmediateLifecycle(dungeon, reason); return ServerQueueResult.AlreadyActive; } case "unloading": { ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)("Dungeon-Lifecycle ist bereits aktiv: " + text + "; Zustand=" + text2)); } return ServerQueueResult.AlreadyActive; } case "generating": { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)("Dungeon wird bereits neu erzeugt: " + text)); } return ServerQueueResult.AlreadyActive; } default: { DateTime dateTime = NetworkUtcNow(); bool num = WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.Schema", "2.6.4") & SetState(dungeon, "waiting_empty") & WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.RequestedAtUtc", dateTime.ToString("O", CultureInfo.InvariantCulture)) & WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.EmptySinceUtc", string.Empty) & WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.PreparedAtUtc", string.Empty) & WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.Reason", reason ?? "manual"); string text3 = ReadZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.State"); if (!num || !string.Equals(text3, "waiting_empty", StringComparison.Ordinal)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Dungeon-Reset-ZDO konnte nicht persistent geschrieben werden: " + text + "; Erwartet=waiting_empty; Gelesen=" + (string.IsNullOrWhiteSpace(text3) ? "" : text3))); } return ServerQueueResult.Failed; } ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Dungeon-Reset persistent vorgemerkt: " + text + "; Grund=" + reason)); } if (IsImmediateResetReason(reason)) { StartImmediateLifecycle(dungeon, reason); } return ServerQueueResult.Queued; } } } catch (Exception ex) { ManualLogSource log9 = Plugin.Log; if (log9 != null) { log9.LogError((object)("Dungeon-Reset konnte nicht vorgemerkt werden: " + DungeonId(dungeon) + " -> " + ex)); } return ServerQueueResult.Failed; } } private static string NormalizeLifecycleStateForCurrentBuild(DungeonGenerator dungeon) { if ((Object)(object)dungeon == (Object)null) { return "idle"; } string text = DungeonId(dungeon); string text2 = ReadZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.State"); if (string.IsNullOrWhiteSpace(text2)) { text2 = "idle"; } string text3 = ReadZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.Schema"); bool flag = !string.Equals(text3, "2.6.4", StringComparison.Ordinal); bool flag2 = text2 == "unloading" && !ActiveUnloads.Contains(text); bool flag3 = text2 == "generating" && !ActiveGenerations.Contains(text); bool flag4 = text2 == "error"; if (flag && (text2 == "waiting_empty" || text2 == "prepared")) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Gespeicherten Dungeon-Lifecycle fuer 2.6.4 uebernommen: " + text + "; Zustand=" + text2 + "; AlteSchema=" + (string.IsNullOrWhiteSpace(text3) ? "" : text3))); } WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.Schema", "2.6.4"); return text2; } if (flag2 || flag3) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Unterbrochenen Dungeon-Reset auf wartend zurueckgesetzt und wieder aufgenommen: " + text + "; AlterZustand=" + text2)); } SetState(dungeon, "waiting_empty"); WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.EmptySinceUtc", string.Empty); WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.Schema", "2.6.4"); return "waiting_empty"; } if ((flag && text2 != "idle") || flag4) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("Veralteten oder fehlerhaften Dungeon-Lifecycle-Zustand zurueckgesetzt: " + text + "; AlterZustand=" + text2 + "; AlteSchema=" + (string.IsNullOrWhiteSpace(text3) ? "" : text3))); } ResetLifecycleStateToIdle(dungeon); text2 = "idle"; } WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.Schema", "2.6.4"); return text2; } private static void ResetLifecycleStateToIdle(DungeonGenerator dungeon) { if (!((Object)(object)dungeon == (Object)null)) { SetState(dungeon, "idle"); WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.RequestedAtUtc", string.Empty); WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.EmptySinceUtc", string.Empty); WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.PreparedAtUtc", string.Empty); WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.Reason", "state_recovery_2.6.4"); } } private static long ResolveServerRpcTarget() { if (IsServer()) { return 0L; } try { if ((Object)(object)ZNet.instance != (Object)null) { MethodInfo method = typeof(ZNet).GetMethod("GetServerPeerID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method != null && method.Invoke(ZNet.instance, null) is long num && num != 0L) { return num; } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Server-Peer-ID konnte nicht ueber ZNet bestimmt werden: " + ex.Message)); } } try { if (ZRoutedRpc.instance != null) { MethodInfo method2 = typeof(ZRoutedRpc).GetMethod("GetServerPeerID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method2 != null && method2.Invoke(ZRoutedRpc.instance, null) is long num2 && num2 != 0L) { return num2; } } } catch (Exception ex2) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogDebug((object)("Server-Peer-ID konnte nicht ueber ZRoutedRpc bestimmt werden: " + ex2.Message)); } } return 0L; } private static bool GeneratePreparedDungeon(DungeonGenerator dungeon, string reason) { if (!IsServer() || (Object)(object)dungeon == (Object)null || (Object)(object)plugin == (Object)null) { return false; } if (ReadZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.State") == "idle") { return true; } if (PlayersInside(dungeon)) { return false; } string text = DungeonId(dungeon); if (!ActiveUnloads.Contains(text)) { ActiveUnloads.Add(text); WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.Reason", reason ?? "legacy_prepared_bridge"); ((MonoBehaviour)plugin).StartCoroutine(UnloadDungeon(dungeon, text)); } return true; } private static void EnsureScheduleIfServer(DungeonGenerator dungeon) { if (IsServer() && !((Object)(object)dungeon == (Object)null) && EnsureServerLifecycleAuthority(dungeon, "ensure_schedule")) { if (string.IsNullOrWhiteSpace(ReadZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.State"))) { SetState(dungeon, "idle"); WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.Schema", "2.6.4"); } DateTime dateTime = NetworkUtcNow(); DateTime value = ParseDate(ReadZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.NextResetUtc")); if (!IsPlausibleResetTimestamp(value, dateTime)) { value = NextResetUtc(dungeon, dateTime); WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.NextResetUtc", value.ToString("O", CultureInfo.InvariantCulture)); LogInvalidTimerRepairOnce(dungeon, value, "schedule"); } if (string.IsNullOrWhiteSpace(ReadZdoString((Component)(object)dungeon, "ChallengeHub.Dungeon.Tier"))) { WriteZdoString((Component)(object)dungeon, "ChallengeHub.Dungeon.Tier", DetermineTier(dungeon).ToString(CultureInfo.InvariantCulture)); } } } private static DateTime NextResetUtc(DungeonGenerator dungeon, DateTime from) { float num = ((TargetedResetFeature.DungeonResetCooldownMinMinutes != null) ? TargetedResetFeature.DungeonResetCooldownMinMinutes.Value : 90f); float num2 = ((TargetedResetFeature.DungeonResetCooldownMaxMinutes != null) ? TargetedResetFeature.DungeonResetCooldownMaxMinutes.Value : 240f); if (num <= 0f) { num = ((TargetedResetFeature.DungeonResetCooldownMinutes != null) ? TargetedResetFeature.DungeonResetCooldownMinutes.Value : 120f); } if (num2 < num) { num2 = num; } int num3 = DetermineTier(dungeon); float num4 = Random.value; if (num3 <= 1) { num4 = Mathf.Lerp(0.55f, 1f, num4); } else if (num3 >= 3) { num4 = Mathf.Lerp(0f, 0.45f, num4); } return from.AddMinutes(Mathf.Lerp(num, num2, num4)); } private static DateTime NextResetUtcForTier(int tier, DateTime from) { float num = ((TargetedResetFeature.DungeonResetCooldownMinMinutes != null) ? TargetedResetFeature.DungeonResetCooldownMinMinutes.Value : 90f); float num2 = ((TargetedResetFeature.DungeonResetCooldownMaxMinutes != null) ? TargetedResetFeature.DungeonResetCooldownMaxMinutes.Value : 240f); if (num <= 0f) { num = ((TargetedResetFeature.DungeonResetCooldownMinutes != null) ? TargetedResetFeature.DungeonResetCooldownMinutes.Value : 120f); } if (num2 < num) { num2 = num; } float num3 = Random.value; if (tier <= 1) { num3 = Mathf.Lerp(0.55f, 1f, num3); } else if (tier >= 3) { num3 = Mathf.Lerp(0f, 0.45f, num3); } return from.AddMinutes(Mathf.Lerp(num, num2, num3)); } private static int DetermineTier(DungeonGenerator dungeon) { string haystack = NormalizeNameChain(((Object)(object)dungeon != (Object)null) ? ((Component)dungeon).gameObject : null); if (ContainsAny(haystack, (TargetedResetFeature.DungeonTier3Keywords != null) ? TargetedResetFeature.DungeonTier3Keywords.Value : "")) { return 3; } if (ContainsAny(haystack, (TargetedResetFeature.DungeonTier2Keywords != null) ? TargetedResetFeature.DungeonTier2Keywords.Value : "")) { return 2; } return 1; } private static void RememberEntranceSnapshot(string dungeonId, Teleport entrance) { //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_0059: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrWhiteSpace(dungeonId) && !((Object)(object)entrance == (Object)null)) { RememberLifecycleAnchor(dungeonId, entrance); Teleport teleportTargetComponent = GetTeleportTargetComponent(entrance); PendingEntranceSnapshots[dungeonId] = new List { new EntranceSnapshot { EntrancePosition = ((Component)entrance).transform.position, TargetPosition = (((Object)(object)teleportTargetComponent != (Object)null) ? ((Component)teleportTargetComponent).transform.position : Vector3.zero), TargetRotation = (((Object)(object)teleportTargetComponent != (Object)null) ? ((Component)teleportTargetComponent).transform.rotation : Quaternion.identity), HasTarget = ((Object)(object)teleportTargetComponent != (Object)null) } }; } } private static List CaptureDungeonEntrances(DungeonGenerator dungeon, Bounds dungeonBounds) { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: 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) string text = (((Object)(object)dungeon != (Object)null) ? DungeonId(dungeon) : string.Empty); if (!string.IsNullOrWhiteSpace(text) && PendingEntranceSnapshots.TryGetValue(text, out var value) && value != null && value.Count > 0) { return value.ToList(); } List list = new List(); HashSet hashSet = new HashSet(); Teleport[] array; try { array = Object.FindObjectsByType((FindObjectsSortMode)0); } catch { array = Array.Empty(); } Teleport[] array2 = array; Vector2 val3 = default(Vector2); foreach (Teleport val in array2) { if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null || !IsRuntimeSceneObject(((Component)val).gameObject) || ((Bounds)(ref dungeonBounds)).Contains(((Component)val).transform.position)) { continue; } Teleport teleportTargetComponent = GetTeleportTargetComponent(val); if ((Object)(object)teleportTargetComponent == (Object)null || !((Bounds)(ref dungeonBounds)).Contains(((Component)teleportTargetComponent).transform.position)) { continue; } if ((Object)(object)dungeon != (Object)null) { Vector2 val2 = new Vector2(((Component)val).transform.position.x, ((Component)val).transform.position.z); ((Vector2)(ref val3))..ctor(((Component)dungeon).transform.position.x, ((Component)dungeon).transform.position.z); if (Vector2.Distance(val2, val3) > 260f) { continue; } } if (hashSet.Add(((Object)((Component)val).gameObject).GetInstanceID())) { list.Add(new EntranceSnapshot { EntrancePosition = ((Component)val).transform.position, TargetPosition = ((Component)teleportTargetComponent).transform.position, TargetRotation = ((Component)teleportTargetComponent).transform.rotation, HasTarget = true }); } } if (list.Count == 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Dungeon-Eingangssnapshot ohne eindeutige Zielbindung uebersprungen: " + (string.IsNullOrWhiteSpace(text) ? "" : text) + ". Benachbarte Dungeon-Eingaenge werden nicht mehr per Radius-Fallback veraendert.")); } } return list.OrderBy((EntranceSnapshot snapshot) => (!((Object)(object)dungeon == (Object)null)) ? Vector2.Distance(new Vector2(snapshot.EntrancePosition.x, snapshot.EntrancePosition.z), new Vector2(((Component)dungeon).transform.position.x, ((Component)dungeon).transform.position.z)) : 0f).ToList(); } private static int DisableDungeonSmokeRenderers(Bounds dungeonBounds) { //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_0048: Unknown result type (might be due to invalid IL or missing references) int num = 0; try { Scene activeScene = SceneManager.GetActiveScene(); GameObject[] array = (((Scene)(ref activeScene)).IsValid() ? ((Scene)(ref activeScene)).GetRootGameObjects() : Array.Empty()); foreach (GameObject val in array) { if ((Object)(object)val == (Object)null || !IsRuntimeSceneObject(val) || !((Bounds)(ref dungeonBounds)).Contains(val.transform.position)) { continue; } MonoBehaviour[] componentsInChildren = val.GetComponentsInChildren(true); foreach (MonoBehaviour val2 in componentsInChildren) { if (!((Object)(object)val2 == (Object)null) && string.Equals(((object)val2).GetType().Name, "SmokeRenderer", StringComparison.Ordinal)) { try { ((Behaviour)val2).enabled = false; num++; } catch { } } } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("SmokeRenderer-Vorbereitung nur teilweise moeglich: " + ex.Message)); } } return num; } private static void PrepareAndBroadcastEntranceTargets(string dungeonId, List snapshots) { if (snapshots != null && snapshots.Count != 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Dungeon-Eingangsanker vor Reset gesichert: " + dungeonId + "; Anzahl=" + snapshots.Count + ". Zielbindung erfolgt nach dem neuen Spawn.")); } } } private unsafe static bool RepairAndBroadcastDungeonEntrances(string dungeonId) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(dungeonId)) { return false; } if (!PendingEntranceSnapshots.TryGetValue(dungeonId, out var value) || value == null || value.Count == 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Keine gespeicherten Aussenanker fuer die neue Dungeon-Eingangsbindung vorhanden: " + dungeonId)); } return false; } int num = 0; foreach (EntranceSnapshot item in value) { if (!TryResolveEntrancePair(dungeonId, item, out var resolved, out var failure)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { string[] obj = new string[6] { "Dungeon-Eingang konnte nicht eindeutig mit dem neuen Entrance-Room verbunden werden: ", dungeonId, "; Eingang=", null, null, null }; Vector3 entrancePosition = item.EntrancePosition; obj[3] = ((object)(*(Vector3*)(&entrancePosition))/*cast due to .constrained prefix*/).ToString(); obj[4] = "; Grund="; obj[5] = failure; log2.LogError((object)string.Concat(obj)); } } else { num++; if (IsServer() && ZRoutedRpc.instance != null && rpcRegistered) { string text = EncodeEntranceRepair(dungeonId, resolved); ZRoutedRpc.instance.InvokeRoutedRPC(0L, "ChallengeHub_DungeonReset_EntranceRepair_v2230", new object[1] { text }); } } } if (num != value.Count) { PendingEntranceSnapshots[dungeonId] = value; return false; } PendingEntranceSnapshots.Remove(dungeonId); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Dungeon-Eingangspaare nach Neuerzeugung eindeutig gebunden: " + dungeonId + "; Paare=" + num)); } return num > 0; } private static bool TryResolveEntrancePair(string dungeonId, EntranceSnapshot snapshot, out EntranceSnapshot resolved, out string failure) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) resolved = null; failure = string.Empty; if (snapshot == null) { failure = "Snapshot fehlt."; return false; } Teleport exterior = FindNearestTeleportByPosition(snapshot.EntrancePosition, 18f, interior: false); if ((Object)(object)exterior == (Object)null) { failure = "Aussen-Teleport am gespeicherten Anker nicht gefunden."; return false; } DungeonGenerator val = FindDungeonById(dungeonId); if ((Object)(object)val == (Object)null && snapshot.HasTarget) { val = FindNearestDungeonGenerator(snapshot.TargetPosition, 1200f); } if ((Object)(object)val == (Object)null) { val = FindNearestDungeonGeneratorHorizontal(snapshot.EntrancePosition, 260f); } if ((Object)(object)val == (Object)null) { failure = "Neue Dungeoninstanz nicht gefunden."; return false; } Bounds bounds = GetDungeonBounds(val); List list = (from item in Object.FindObjectsByType((FindObjectsSortMode)0) where (Object)(object)item != (Object)null && (Object)(object)item != (Object)(object)exterior && (Object)(object)((Component)item).gameObject != (Object)null && IsRuntimeSceneObject(((Component)item).gameObject) && ((Bounds)(ref bounds)).Contains(((Component)item).transform.position) select item).ToList(); if (list.Count == 0) { failure = "Im neuen Dungeon wurde kein Teleport gefunden."; return false; } Teleport val2 = SelectInteriorEntranceCandidate(list, exterior, val, snapshot, out failure); if ((Object)(object)val2 == (Object)null) { return false; } if (!SetTeleportTarget(exterior, val2) || !SetTeleportTarget(val2, exterior)) { failure = "m_targetPoint konnte nicht bidirektional gesetzt werden."; return false; } Transform teleportTarget = GetTeleportTarget(exterior); Transform teleportTarget2 = GetTeleportTarget(val2); if ((Object)(object)teleportTarget != (Object)(object)((Component)val2).transform || (Object)(object)teleportTarget2 != (Object)(object)((Component)exterior).transform) { failure = "Bidirektionale Zielreferenzen konnten nicht verifiziert werden."; return false; } resolved = new EntranceSnapshot { EntrancePosition = ((Component)exterior).transform.position, TargetPosition = ((Component)val2).transform.position, TargetRotation = ((Component)val2).transform.rotation, HasTarget = true }; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Dungeon-Eingangspaar gebunden: " + dungeonId + "; Aussen=" + ((object)((Component)exterior).transform.position/*cast due to .constrained prefix*/).ToString() + "; Innen=" + ((object)((Component)val2).transform.position/*cast due to .constrained prefix*/).ToString() + "; EntranceRoom=" + ((Object)(object)((Component)val2).GetComponentInParent() != (Object)null))); } return true; } private static Teleport SelectInteriorEntranceCandidate(List candidates, Teleport exterior, DungeonGenerator dungeon, EntranceSnapshot snapshot, out string failure) { failure = string.Empty; if (candidates == null || candidates.Count == 0) { failure = "Keine Innen-Teleport-Kandidaten vorhanden."; return null; } if (candidates.Count == 1) { return candidates[0]; } List list = candidates.Where((Teleport item) => (Object)(object)GetTeleportTargetComponent(item) == (Object)(object)exterior).ToList(); if (list.Count == 1) { return list[0]; } if (list.Count > 1) { failure = "Mehrere Innen-Teleports zeigen bereits auf denselben Aussen-Teleport."; return null; } if (snapshot.HasTarget) { List> list2 = (from item in candidates select Tuple.Create(item, Vector3.Distance(((Component)item).transform.position, snapshot.TargetPosition)) into item orderby item.Item2 select item).ToList(); if (list2[0].Item2 <= 30f && (list2.Count == 1 || list2[1].Item2 - list2[0].Item2 >= 4f)) { return list2[0].Item1; } } List> list3 = (from item in candidates select Tuple.Create(item, Vector3.Distance(((Component)item).transform.position, ((Component)dungeon).transform.position)) into item orderby item.Item2 select item).ToList(); if (list3[0].Item2 <= 40f && (list3.Count == 1 || list3[1].Item2 - list3[0].Item2 >= 5f)) { return list3[0].Item1; } List list4 = candidates.Where(delegate(Teleport item) { Room componentInParent = ((Component)item).GetComponentInParent(); return (Object)(object)componentInParent != (Object)null && componentInParent.m_entrance; }).ToList(); if (list4.Count == 1) { return list4[0]; } failure = "Innenziel ist nicht eindeutig: Kandidaten=" + candidates.Count + "; GeneratorabstandBest=" + list3[0].Item2.ToString("F1", CultureInfo.InvariantCulture) + "m; EntranceRoomKandidaten=" + list4.Count + "."; return null; } private unsafe static void RPC_EntranceRepair(long sender, string payload) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) if (!TryDecodeEntranceRepair(payload, out var dungeonId, out var snapshot)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Dungeon-Eingangsreparatur konnte nicht gelesen werden. Absender=" + sender)); } } else if (ApplyEntranceRepair(snapshot)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { string[] obj = new string[6] { "Dungeon-Eingangspaar auf lokalem Peer gebunden: ", dungeonId, "; Aussen=", null, null, null }; Vector3 entrancePosition = snapshot.EntrancePosition; obj[3] = ((object)(*(Vector3*)(&entrancePosition))/*cast due to .constrained prefix*/).ToString(); obj[4] = "; Innen="; entrancePosition = snapshot.TargetPosition; obj[5] = ((object)(*(Vector3*)(&entrancePosition))/*cast due to .constrained prefix*/).ToString(); log2.LogInfo((object)string.Concat(obj)); } LocalMessage("Dungeon-Reset abgeschlossen. Der Eingang ist wieder frei; erneut E druecken."); } else if ((Object)(object)plugin != (Object)null && ActiveEntrancePairRetries.Add(dungeonId)) { ((MonoBehaviour)plugin).StartCoroutine(RetryEntranceRepairOnPeer(dungeonId, snapshot)); } } private unsafe static IEnumerator RetryEntranceRepairOnPeer(string dungeonId, EntranceSnapshot snapshot) { for (int attempt = 1; attempt <= 80; attempt++) { yield return (object)new WaitForSeconds(0.25f); if (ApplyEntranceRepair(snapshot)) { ManualLogSource log = Plugin.Log; if (log != null) { string[] obj = new string[8] { "Dungeon-Eingangspaar nach verzögertem Client-Spawn gebunden: ", dungeonId, "; Versuch=", attempt.ToString(), "; Aussen=", null, null, null }; Vector3 entrancePosition = snapshot.EntrancePosition; obj[5] = ((object)(*(Vector3*)(&entrancePosition))/*cast due to .constrained prefix*/).ToString(); obj[6] = "; Innen="; entrancePosition = snapshot.TargetPosition; obj[7] = ((object)(*(Vector3*)(&entrancePosition))/*cast due to .constrained prefix*/).ToString(); log.LogInfo((object)string.Concat(obj)); } ActiveEntrancePairRetries.Remove(dungeonId); LocalMessage("Dungeon-Reset abgeschlossen. Der Eingang ist wieder frei; erneut E druecken."); yield break; } } ActiveEntrancePairRetries.Remove(dungeonId); ManualLogSource log2 = Plugin.Log; if (log2 != null) { string[] obj2 = new string[6] { "Dungeon-Eingangspaar konnte auf diesem Peer auch nach 20 Sekunden nicht gebunden werden: ", dungeonId, "; Aussen=", null, null, null }; Vector3 entrancePosition = snapshot.EntrancePosition; obj2[3] = ((object)(*(Vector3*)(&entrancePosition))/*cast due to .constrained prefix*/).ToString(); obj2[4] = "; Innen="; entrancePosition = snapshot.TargetPosition; obj2[5] = ((object)(*(Vector3*)(&entrancePosition))/*cast due to .constrained prefix*/).ToString(); log2.LogError((object)string.Concat(obj2)); } } private static bool ApplyEntranceRepair(EntranceSnapshot snapshot) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (snapshot == null || !snapshot.HasTarget) { return false; } Teleport val = FindNearestTeleportByPosition(snapshot.EntrancePosition, 18f, interior: false); Teleport val2 = FindNearestTeleportByPosition(snapshot.TargetPosition, 18f, interior: true); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)val == (Object)(object)val2) { return false; } try { int instanceID = ((Object)((Component)val).gameObject).GetInstanceID(); if (EntranceTargetProxies.TryGetValue(instanceID, out var value) && (Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } EntranceTargetProxies.Remove(instanceID); if (!SetTeleportTarget(val, val2) || !SetTeleportTarget(val2, val)) { return false; } return (Object)(object)GetTeleportTarget(val) == (Object)(object)((Component)val2).transform && (Object)(object)GetTeleportTarget(val2) == (Object)(object)((Component)val).transform; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Dungeon-Eingangspaarung fehlgeschlagen: " + ex.Message)); } return false; } } private static Teleport FindNearestTeleportByPosition(Vector3 position, float radius, bool interior) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) Teleport result = null; float num = Mathf.Max(1f, radius); try { Teleport[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Teleport val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && IsRuntimeSceneObject(((Component)val).gameObject) && ((Component)val).transform.position.y > 4000f == interior) { float num2 = Vector3.Distance(position, ((Component)val).transform.position); if (!(num2 >= num)) { result = val; num = num2; } } } } catch { } return result; } private static bool SetTeleportTarget(Teleport source, Teleport target) { if ((Object)(object)source == (Object)null || (Object)(object)target == (Object)null) { return false; } try { FieldInfo fieldInfo = FindField(((object)source).GetType(), "m_targetPoint"); if (fieldInfo == null) { return false; } if (fieldInfo.FieldType.IsAssignableFrom(typeof(Teleport))) { fieldInfo.SetValue(source, target); } else if (fieldInfo.FieldType == typeof(Transform)) { fieldInfo.SetValue(source, ((Component)target).transform); } else { if (!typeof(Component).IsAssignableFrom(fieldInfo.FieldType)) { return false; } Component component = ((Component)target).GetComponent(fieldInfo.FieldType); if ((Object)(object)component == (Object)null) { return false; } fieldInfo.SetValue(source, component); } return true; } catch { return false; } } private static Teleport FindNearestTeleportRaw(Vector3 position, float radius) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) Teleport result = null; float num = Mathf.Max(1f, radius); try { Teleport[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Teleport val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null)) { float num2 = HierarchyDistance(position, ((Component)val).transform, 6); if (!(num2 >= num)) { result = val; num = num2; } } } } catch { } return result; } private static FieldInfo FindField(Type type, string fieldName) { Type type2 = type; while (type2 != null) { FieldInfo field = type2.GetField(fieldName, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field; } type2 = type2.BaseType; } return null; } private static string EncodeEntranceRepair(string dungeonId, EntranceSnapshot snapshot) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) return SanitizeRpcText(dungeonId) + "|" + EncodePosition(snapshot.EntrancePosition) + "|" + EncodePosition(snapshot.TargetPosition) + "|" + snapshot.TargetRotation.x.ToString("R", CultureInfo.InvariantCulture) + "," + snapshot.TargetRotation.y.ToString("R", CultureInfo.InvariantCulture) + "," + snapshot.TargetRotation.z.ToString("R", CultureInfo.InvariantCulture) + "," + snapshot.TargetRotation.w.ToString("R", CultureInfo.InvariantCulture); } private static bool TryDecodeEntranceRepair(string payload, out string dungeonId, out EntranceSnapshot snapshot) { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) dungeonId = string.Empty; snapshot = null; if (string.IsNullOrWhiteSpace(payload)) { return false; } string[] array = payload.Split(new char[1] { '|' }); if (array.Length < 4) { return false; } if (!TryDecodePosition(array[1], out var position, out var suffix)) { return false; } if (!TryDecodePosition(array[2], out var position2, out suffix)) { return false; } string[] array2 = array[3].Split(new char[1] { ',' }); if (array2.Length != 4) { return false; } if (!float.TryParse(array2[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return false; } if (!float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { return false; } if (!float.TryParse(array2[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { return false; } if (!float.TryParse(array2[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var result4)) { return false; } dungeonId = array[0]; snapshot = new EntranceSnapshot { EntrancePosition = position, TargetPosition = position2, TargetRotation = new Quaternion(result, result2, result3, result4), HasTarget = true }; return true; } private static Teleport FindNearestDungeonEntrance(Vector3 position, float radius) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) Teleport nearest = null; float nearestDistance = Mathf.Max(10f, radius); try { FindNearestTeleport(Object.FindObjectsByType((FindObjectsSortMode)0), position, ref nearest, ref nearestDistance); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Dungeon-Eingangssuche fehlgeschlagen: " + ex.Message)); } } return nearest; } private static void FindNearestTeleport(Teleport[] teleports, Vector3 position, ref Teleport nearest, ref float nearestDistance) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) if (teleports == null) { return; } foreach (Teleport val in teleports) { if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null) { continue; } Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { Transform teleportTarget = GetTeleportTarget(val); float num = HierarchyDistance(position, ((Component)val).transform, 4); if (!(num >= nearestDistance) && (!((Object)(object)(FindDungeonInEntranceHierarchy(val) ?? (((Object)(object)teleportTarget != (Object)null) ? FindNearestDungeonGenerator(teleportTarget.position, 700f) : null) ?? FindNearestDungeonGeneratorHorizontal(((Component)val).transform.position, 220f)) == (Object)null) || ContainsAny(NormalizeNameChain(((Component)val).gameObject), (TargetedResetFeature.DungeonEntranceKeywords != null) ? TargetedResetFeature.DungeonEntranceKeywords.Value : "crypt,burial,trollcave,frostcave,cave,mine,dungeon") || (!((Object)(object)teleportTarget == (Object)null) && !(Vector3.Distance(((Component)val).transform.position, teleportTarget.position) < 250f)))) { nearest = val; nearestDistance = num; } } } } private static float HierarchyDistance(Vector3 position, Transform transform, int parentDepth) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) float num = float.MaxValue; Transform val = transform; int num2 = Mathf.Max(0, parentDepth); while ((Object)(object)val != (Object)null && num2-- >= 0) { num = Mathf.Min(num, Vector3.Distance(position, val.position)); val = val.parent; } return num; } private static DungeonGenerator ResolveDungeonNearEntrance(Teleport entrance, Vector3 entrancePosition) { //IL_0012: 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) DungeonGenerator val = ResolveDungeon(entrance); if ((Object)(object)val != (Object)null) { return val; } val = FindNearestDungeonGeneratorHorizontal(entrancePosition, 220f); if ((Object)(object)val != (Object)null) { return val; } Transform teleportTarget = GetTeleportTarget(entrance); if ((Object)(object)teleportTarget != (Object)null) { val = FindNearestDungeonGeneratorHorizontal(teleportTarget.position, 220f); if ((Object)(object)val != (Object)null) { return val; } } return null; } private static DungeonGenerator ResolveDungeon(Teleport entrance) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)entrance == (Object)null) { return null; } DungeonGenerator val = FindDungeonInEntranceHierarchy(entrance); if ((Object)(object)val != (Object)null) { return val; } Transform teleportTarget = GetTeleportTarget(entrance); if ((Object)(object)teleportTarget != (Object)null) { DungeonGenerator val2 = FindNearestDungeonGenerator(teleportTarget.position, 700f); if ((Object)(object)val2 != (Object)null) { return val2; } DungeonGenerator val3 = FindNearestDungeonGeneratorHorizontal(teleportTarget.position, 220f); if ((Object)(object)val3 != (Object)null) { return val3; } } return FindNearestDungeonGeneratorHorizontal(((Component)entrance).transform.position, 220f); } private static DungeonGenerator FindDungeonInEntranceHierarchy(Teleport entrance) { try { Transform val = (((Object)(object)entrance != (Object)null) ? ((Component)entrance).transform : null); int num = 0; while ((Object)(object)val != (Object)null && num < 5) { DungeonGenerator component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } DungeonGenerator componentInChildren = ((Component)val).GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null) { return componentInChildren; } num++; val = val.parent; } } catch { } return null; } private static string EntranceDebugName(Teleport entrance) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)entrance == (Object)null) { return ""; } Transform teleportTarget = GetTeleportTarget(entrance); return ((Object)((Component)entrance).gameObject).name + " @ " + ((object)((Component)entrance).transform.position/*cast due to .constrained prefix*/).ToString() + (((Object)(object)teleportTarget != (Object)null) ? (" -> " + ((object)teleportTarget.position/*cast due to .constrained prefix*/).ToString()) : " -> "); } private static Transform GetTeleportTarget(Teleport teleport) { Teleport teleportTargetComponent = GetTeleportTargetComponent(teleport); if ((Object)(object)teleportTargetComponent != (Object)null) { return ((Component)teleportTargetComponent).transform; } if ((Object)(object)teleport == (Object)null) { return null; } try { FieldInfo fieldInfo = FindField(((object)teleport).GetType(), "m_targetPoint"); object obj = ((fieldInfo != null) ? fieldInfo.GetValue(teleport) : null); Transform val = (Transform)((obj is Transform) ? obj : null); if (val != null) { return val; } Component val2 = (Component)((obj is Component) ? obj : null); if (val2 != null) { return val2.transform; } return null; } catch { return null; } } private static Teleport GetTeleportTargetComponent(Teleport teleport) { if ((Object)(object)teleport == (Object)null) { return null; } try { FieldInfo fieldInfo = FindField(((object)teleport).GetType(), "m_targetPoint"); object obj = ((fieldInfo != null) ? fieldInfo.GetValue(teleport) : null); Teleport val = (Teleport)((obj is Teleport) ? obj : null); if (val != null) { return val; } Component val2 = (Component)((obj is Component) ? obj : null); if (val2 != null) { return val2.GetComponent(); } Transform val3 = (Transform)((obj is Transform) ? obj : null); if (val3 != null) { return ((Component)val3).GetComponent(); } return null; } catch { return null; } } private static void RegisterDungeon(DungeonGenerator dungeon) { if ((Object)(object)dungeon == (Object)null || (Object)(object)((Component)dungeon).gameObject == (Object)null || !IsRuntimeSceneObject(((Component)dungeon).gameObject)) { return; } try { string text = DungeonId(dungeon); if (!string.IsNullOrWhiteSpace(text)) { KnownDungeons[text] = dungeon; } } catch { } } private static DungeonGenerator[] SnapshotKnownDungeons() { foreach (string item in (from entry in KnownDungeons where (Object)(object)entry.Value == (Object)null || (Object)(object)((Component)entry.Value).gameObject == (Object)null || !IsRuntimeSceneObject(((Component)entry.Value).gameObject) select entry.Key).ToList()) { KnownDungeons.Remove(item); } return KnownDungeons.Values.Where((DungeonGenerator item) => (Object)(object)item != (Object)null).Distinct().ToArray(); } private static DungeonGenerator[] CollectRuntimeDungeons(bool includeActiveSceneScan) { Dictionary dictionary = new Dictionary(); DungeonGenerator[] array = SnapshotKnownDungeons(); foreach (DungeonGenerator val in array) { dictionary[((Object)((Component)val).gameObject).GetInstanceID()] = val; } if (includeActiveSceneScan) { try { array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (DungeonGenerator val2 in array) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((Component)val2).gameObject == (Object)null)) { RegisterDungeon(val2); dictionary[((Object)((Component)val2).gameObject).GetInstanceID()] = val2; } } } catch { } } return dictionary.Values.ToArray(); } private static DungeonGenerator FindDungeonById(string dungeonId) { if (string.IsNullOrWhiteSpace(dungeonId)) { return null; } if (KnownDungeons.TryGetValue(dungeonId, out var value) && (Object)(object)value != (Object)null) { return value; } DungeonGenerator[] array = CollectRuntimeDungeons(includeActiveSceneScan: true); foreach (DungeonGenerator val in array) { if (string.Equals(DungeonId(val), dungeonId, StringComparison.OrdinalIgnoreCase)) { return val; } } return null; } private static DungeonGenerator FindNearestDungeonGenerator(Vector3 position, float maxDistance) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) DungeonGenerator result = null; float num = maxDistance; DungeonGenerator[] array = CollectRuntimeDungeons(includeActiveSceneScan: true); foreach (DungeonGenerator val in array) { float num2 = Vector3.Distance(position, ((Component)val).transform.position); if (!(num2 >= num)) { result = val; num = num2; } } return result; } private static DungeonGenerator FindNearestDungeonGeneratorHorizontal(Vector3 position, float maxHorizontalDistance) { //IL_0010: 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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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) //IL_0049: 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) DungeonGenerator result = null; float num = Mathf.Max(1f, maxHorizontalDistance); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(position.x, position.z); DungeonGenerator[] array = CollectRuntimeDungeons(includeActiveSceneScan: true); foreach (DungeonGenerator val2 in array) { Vector3 position2 = ((Component)val2).transform.position; float num2 = Vector2.Distance(val, new Vector2(position2.x, position2.z)); if (!(num2 >= num)) { result = val2; num = num2; } } return result; } private unsafe static void TryRepairBrokenEntranceNearPlayer(Vector3 playerPosition) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: 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) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) try { int num = Physics.OverlapSphereNonAlloc(playerPosition, 28f, NearbyEntranceColliderBuffer, -1, (QueryTriggerInteraction)2); HashSet hashSet = new HashSet(); for (int i = 0; i < num; i++) { Collider val = NearbyEntranceColliderBuffer[i]; NearbyEntranceColliderBuffer[i] = null; if ((Object)(object)val == (Object)null) { continue; } Teleport entrance = ((Component)val).GetComponentInParent(); if ((Object)(object)entrance == (Object)null) { entrance = ((Component)val).GetComponentInChildren(true); } if ((Object)(object)entrance == (Object)null || (Object)(object)((Component)entrance).gameObject == (Object)null || !hashSet.Add(((Object)((Component)entrance).gameObject).GetInstanceID()) || (Object)(object)GetTeleportTarget(entrance) != (Object)null) { continue; } DungeonGenerator val2 = ResolveDungeonNearEntrance(entrance, playerPosition); if ((Object)(object)val2 == (Object)null) { continue; } RegisterDungeon(val2); Bounds bounds = GetDungeonBounds(val2); List list = (from item in Object.FindObjectsByType((FindObjectsSortMode)0) where (Object)(object)item != (Object)null && (Object)(object)item != (Object)(object)entrance && ((Bounds)(ref bounds)).Contains(((Component)item).transform.position) select item).ToList().Where(delegate(Teleport item) { Room componentInParent = ((Component)item).GetComponentInParent(); return (Object)(object)componentInParent != (Object)null && componentInParent.m_entrance; }).ToList(); Teleport val3 = ((list.Count == 1) ? list[0] : null); if ((Object)(object)val3 == (Object)null) { continue; } EntranceSnapshot entranceSnapshot = new EntranceSnapshot { EntrancePosition = ((Component)entrance).transform.position, TargetPosition = ((Component)val3).transform.position, TargetRotation = ((Component)val3).transform.rotation, HasTarget = true }; if (ApplyEntranceRepair(entranceSnapshot)) { ManualLogSource log = Plugin.Log; if (log != null) { string[] obj = new string[6] { "Defekten Dungeon-Eingang lokal ohne Reset repariert: ", DungeonId(val2), "; Eingang=", null, null, null }; Vector3 entrancePosition = entranceSnapshot.EntrancePosition; obj[3] = ((object)(*(Vector3*)(&entrancePosition))/*cast due to .constrained prefix*/).ToString(); obj[4] = "; Ziel="; entrancePosition = entranceSnapshot.TargetPosition; obj[5] = ((object)(*(Vector3*)(&entrancePosition))/*cast due to .constrained prefix*/).ToString(); log.LogWarning((object)string.Concat(obj)); } LocalMessage("Dungeon-Eingang wurde lokal repariert. Druecke erneut E."); } return; } for (int num2 = num; num2 < NearbyEntranceColliderBuffer.Length; num2++) { NearbyEntranceColliderBuffer[num2] = null; } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogDebug((object)("Automatische Reparatur eines defekten Dungeon-Eingangs fehlgeschlagen: " + ex.Message)); } } } private static bool OutsidePlayerNearDungeonEntrance(DungeonGenerator dungeon, float radius) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)dungeon == (Object)null) { return false; } Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(((Component)dungeon).transform.position.x, ((Component)dungeon).transform.position.z); float num = Mathf.Max(8f, radius); try { Player[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Player val2 in array) { if ((Object)(object)val2 == (Object)null) { continue; } bool flag = false; try { flag = ((Character)val2).InInterior(); } catch { } if (!flag) { Vector3 position = ((Component)val2).transform.position; if (Vector2.Distance(val, new Vector2(position.x, position.z)) <= num) { return true; } } } } catch { } return false; } private static bool PlayersInside(DungeonGenerator dungeon) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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_0074: 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_008b: 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_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: 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_00f6: 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) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)dungeon == (Object)null) { return false; } Bounds dungeonBounds = GetDungeonBounds(dungeon); try { Player[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Player val in array) { if ((Object)(object)val == (Object)null) { continue; } Vector3 position = ((Component)val).transform.position; if (((Bounds)(ref dungeonBounds)).Contains(position)) { return true; } bool flag = false; try { flag = ((Character)val).InInterior(); } catch { } if (flag) { float num = Mathf.Abs(position.y - ((Component)dungeon).transform.position.y); float num2 = Vector2.Distance(new Vector2(position.x, position.z), new Vector2(((Component)dungeon).transform.position.x, ((Component)dungeon).transform.position.z)); if (num <= Mathf.Max(160f, ((Bounds)(ref dungeonBounds)).extents.y + 40f) && num2 <= Mathf.Max(180f, Mathf.Max(((Bounds)(ref dungeonBounds)).extents.x, ((Bounds)(ref dungeonBounds)).extents.z) + 30f)) { return true; } } } } catch { } return false; } private static Bounds GetDungeonBounds(DungeonGenerator dungeon) { //IL_0016: 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: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004d: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: 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_00db: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: 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_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) Vector3 val = (((Object)(object)dungeon != (Object)null) ? ((Component)dungeon).transform.position : Vector3.zero); Vector3 val2 = ReadField(dungeon, "m_zoneCenter", val); Vector3 val3 = ReadField(dungeon, "m_zoneSize", new Vector3(256f, 256f, 256f)); Vector3 val4 = val; val3.x = Mathf.Max(IsFinite(val3.x) ? Mathf.Abs(val3.x) : 0f, 256f); val3.y = Mathf.Max(IsFinite(val3.y) ? Mathf.Abs(val3.y) : 0f, 256f); val3.z = Mathf.Max(IsFinite(val3.z) ? Mathf.Abs(val3.z) : 0f, 256f); if (IsFiniteVector(val2)) { float num = Vector2.Distance(new Vector2(val2.x, val2.z), new Vector2(val.x, val.z)); float num2 = Mathf.Max(48f, Mathf.Max(val3.x, val3.z) * 0.6f); if (num <= num2) { val4.x = val2.x; val4.z = val2.z; } else if (Mathf.Abs(val2.x) <= val3.x && Mathf.Abs(val2.z) <= val3.z) { val4.x = val.x + val2.x; val4.z = val.z + val2.z; } float num3 = Mathf.Max(500f, val3.y * 2f); if (Mathf.Abs(val2.y - val.y) <= num3) { val4.y = val2.y; } } Bounds result = default(Bounds); ((Bounds)(ref result))..ctor(val4, val3); float num4 = ((DungeonBoundsPadding != null) ? Mathf.Max(0f, DungeonBoundsPadding.Value) : 18f); ((Bounds)(ref result)).Expand(new Vector3(num4 * 2f, 120f, num4 * 2f)); return result; } private static bool IsFinite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } private static bool IsFiniteVector(Vector3 value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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) if (IsFinite(value.x) && IsFinite(value.y)) { return IsFinite(value.z); } return false; } private static bool IsRuntimeSceneObject(GameObject gameObject) { //IL_000a: 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) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 if ((Object)(object)gameObject != (Object)null) { Scene scene = gameObject.scene; if (((Scene)(ref scene)).IsValid()) { return (int)((Object)gameObject).hideFlags != 61; } } return false; } private static bool HasLiveViewInHierarchy(Transform root) { if ((Object)(object)root == (Object)null || (Object)(object)((Component)root).gameObject == (Object)null) { return false; } try { ZNetView[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); foreach (ZNetView val in componentsInChildren) { if (val == null || (Object)(object)val == (Object)null) { continue; } try { if ((Object)(object)((Component)val).gameObject != (Object)null && val.IsValid() && val.GetZDO() != null) { return true; } } catch { } } } catch { } return false; } private static int CountLiveViews(IEnumerable views) { if (views == null) { return 0; } int num = 0; foreach (ZNetView view in views) { if (view == null) { continue; } try { if ((Object)(object)view != (Object)null && (Object)(object)((Component)view).gameObject != (Object)null && view.IsValid() && view.GetZDO() != null) { num++; } } catch { } } return num; } private static int TransformDepth(Transform transform) { int num = 0; Transform val = transform; while ((Object)(object)val != (Object)null && (Object)(object)val.parent != (Object)null && num < 128) { num++; val = val.parent; } return num; } private static List SnapshotPlacedRoomRoots(DungeonGenerator dungeon) { List list = new List(); if ((Object)(object)dungeon == (Object)null) { return list; } try { FieldInfo fieldInfo = FindGeneratorStateField(((object)dungeon).GetType(), "m_placedRooms"); if (!(((fieldInfo != null) ? fieldInfo.GetValue(fieldInfo.IsStatic ? null : dungeon) : null) is IEnumerable enumerable)) { return list; } HashSet hashSet = new HashSet(); foreach (object item in enumerable) { Transform val = null; Transform val2 = (Transform)((item is Transform) ? item : null); if (val2 != null) { val = val2; } else { Component val3 = (Component)((item is Component) ? item : null); if (val3 != null) { val = val3.transform; } else { GameObject val4 = (GameObject)((item is GameObject) ? item : null); if (val4 != null) { val = val4.transform; } } } if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && hashSet.Add(((Object)((Component)val).gameObject).GetInstanceID())) { list.Add(val); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Gesetzte Dungeonräume konnten nur teilweise erfasst werden: " + ex.Message)); } } return list; } private static FieldInfo FindGeneratorStateField(Type type, string fieldName) { Type type2 = type; while (type2 != null) { FieldInfo field = type2.GetField(fieldName, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field; } type2 = type2.BaseType; } return null; } private static int CollectionCount(object value) { if (value == null) { return 0; } if (value is ICollection collection) { return collection.Count; } try { PropertyInfo property = value.GetType().GetProperty("Count", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { return Convert.ToInt32(property.GetValue(value, null)); } } catch { } return -1; } private static bool ClearGeneratorRuntimeState(DungeonGenerator dungeon) { if ((Object)(object)dungeon == (Object)null) { return false; } bool result = true; List list = new List(); Type type = ((object)dungeon).GetType(); string[] array = new string[3] { "m_placedRooms", "m_openConnections", "m_doorConnections" }; foreach (string text in array) { try { FieldInfo fieldInfo = FindGeneratorStateField(type, text); if (fieldInfo == null) { result = false; list.Add(text + "=nicht gefunden"); continue; } object obj = (fieldInfo.IsStatic ? null : dungeon); object value = fieldInfo.GetValue(obj); int num = CollectionCount(value); if (value is IList list2) { list2.Clear(); goto IL_00fb; } if (value == null) { goto IL_00fb; } MethodInfo method = value.GetType().GetMethod("Clear", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method == null) { result = false; list.Add(text + "=kein Clear()"); continue; } method.Invoke(value, null); goto IL_00fb; IL_00fb: int num2 = CollectionCount(fieldInfo.GetValue(obj)); if (num2 > 0) { result = false; list.Add(text + "=" + num + "->" + num2); } else { list.Add(text + "=" + num + "->0" + (fieldInfo.IsStatic ? "(static)" : "(instance)")); } } catch (Exception ex) { result = false; list.Add(text + "=Fehler:" + ex.GetType().Name); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Dungeon-Generatorliste konnte nicht geleert werden: " + text + " -> " + ex.Message)); } } } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Dungeon-Generatorzustand geleert: " + string.Join("; ", list))); } return result; } private static void InvokeDungeonGenerate(DungeonGenerator dungeon, int seed) { if ((Object)(object)dungeon == (Object)null) { throw new ArgumentNullException("dungeon"); } MethodInfo? methodInfo = ((object)dungeon).GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo method) { if (method.Name != "Generate") { return false; } ParameterInfo[] parameters = method.GetParameters(); return parameters.Length == 2 && parameters[0].ParameterType == typeof(int) && parameters[1].ParameterType.IsEnum; }); if (methodInfo == null) { throw new MissingMethodException(((object)dungeon).GetType().FullName, "Generate(int, SpawnMode)"); } object obj = Enum.Parse(methodInfo.GetParameters()[1].ParameterType, "Full", ignoreCase: true); methodInfo.Invoke(dungeon, new object[2] { seed, obj }); } private static int CreateDungeonSeed(DungeonGenerator dungeon) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.FloorToInt(((Component)dungeon).transform.position.x / 64f); int num2 = Mathf.FloorToInt(((Component)dungeon).transform.position.z / 64f); int num3; if (RandomizeLayoutOnReset != null && RandomizeLayoutOnReset.Value) { num3 = (int)DateTime.UtcNow.Ticks ^ Environment.TickCount; } else { num3 = 12345; try { object instance = WorldGenerator.instance; MethodInfo methodInfo = ((instance != null) ? AccessTools.Method(instance.GetType(), "GetSeed", (Type[])null, (Type[])null) : null); if (methodInfo != null) { num3 = Convert.ToInt32(methodInfo.Invoke(instance, null)); } } catch { } } return num3 + num * 4271 + num2 * 9187; } private static bool SetState(DungeonGenerator dungeon, string state) { return WriteZdoString((Component)(object)dungeon, "ChallengeHub.DungeonLifecycle.State", state ?? "idle"); } private static string DungeonId(DungeonGenerator dungeon) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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) if ((Object)(object)dungeon == (Object)null) { return "unknown"; } try { StableDungeonContext stableDungeonContext = DungeonWorldRegistry.Resolve(dungeon, IsServer()); if (stableDungeonContext != null && !string.IsNullOrWhiteSpace(stableDungeonContext.DungeonKey)) { return stableDungeonContext.DungeonKey; } } catch { } Vector3 position = ((Component)dungeon).transform.position; return Plugin.NormalizeKey(((Object)dungeon).name) + "@" + Mathf.RoundToInt(position.x) + ":" + Mathf.RoundToInt(position.z); } private static bool IsEnabled() { if (initialized) { if (EnableLifecycle != null) { return EnableLifecycle.Value; } return true; } return false; } private static bool IsServer() { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } private static long ServerPeerId() { try { MethodInfo method = typeof(ZRoutedRpc).GetMethod("GetServerPeerID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null && ZRoutedRpc.instance != null) { return Convert.ToInt64(method.Invoke(ZRoutedRpc.instance, null)); } } catch { } return 0L; } private static float EffectiveEmptyGraceSeconds() { if (EmptyGraceSeconds == null) { return 12f; } return Mathf.Max(3f, EmptyGraceSeconds.Value); } private static float EffectiveEntranceSearchRadius() { if (EntranceSearchRadius == null) { return 80f; } return Mathf.Clamp(EntranceSearchRadius.Value, 20f, 180f); } private static void LocalMessage(string text) { try { if ((Object)(object)Player.m_localPlayer != (Object)null) { ((Character)Player.m_localPlayer).Message((MessageType)2, text, 0, (Sprite)null); } } catch { } } private static T ReadField(object instance, string name, T fallback) { try { FieldInfo fieldInfo = ((instance != null) ? AccessTools.Field(instance.GetType(), name) : null); object obj = ((fieldInfo != null) ? fieldInfo.GetValue(instance) : null); if (obj is T) { return (T)obj; } } catch { } return fallback; } private static string ReadZdoString(Component component, string key) { try { DungeonGenerator val = (DungeonGenerator)(object)((component is DungeonGenerator) ? component : null); if (val != null) { ZDO val2 = DungeonWorldRegistry.ResolveStateZdo(val, IsServer()); if (val2 != null && val2.IsValid()) { string text = DungeonWorldRegistry.Read(val2, key, string.Empty); if (!string.IsNullOrEmpty(text)) { return text; } } ZNetView val3 = ResolveLifecycleView(val, allowEntranceSearch: true); if (IsUsableLifecycleView(val3)) { string text2 = val3.GetZDO().GetString(key, string.Empty); if (!string.IsNullOrEmpty(text2) && IsServer() && val2 != null) { DungeonWorldRegistry.Write(val2, key, text2); } return text2; } return string.Empty; } ZNetView val4 = ViewOnOrAbove(component); ZDO val5 = (IsUsableLifecycleView(val4) ? val4.GetZDO() : null); return (val5 != null) ? val5.GetString(key, string.Empty) : string.Empty; } catch { return string.Empty; } } private static bool WriteZdoString(Component component, string key, string value) { try { DungeonGenerator val = (DungeonGenerator)(object)((component is DungeonGenerator) ? component : null); if (val != null) { if (!IsServer()) { return false; } return DungeonWorldRegistry.Write(DungeonWorldRegistry.ResolveStateZdo(val), key, value); } ZNetView val2 = ViewOnOrAbove(component); if (!IsUsableLifecycleView(val2)) { return false; } ZDO zDO = val2.GetZDO(); if (zDO == null) { return false; } zDO.Set(key, value ?? string.Empty); return true; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Dungeon-Lifecycle-ZDO-Schreibfehler: " + key + " -> " + ex.Message)); } return false; } } internal static DateTime NetworkUtcNow() { DateTime utcNow = DateTime.UtcNow; try { if ((Object)(object)ZNet.instance != (Object)null) { MethodInfo methodInfo = AccessTools.Method(((object)ZNet.instance).GetType(), "GetTime", Type.EmptyTypes, (Type[])null); if (((methodInfo != null) ? methodInfo.Invoke(ZNet.instance, null) : null) is DateTime dateTime) { DateTime dateTime2 = ((dateTime.Kind == DateTimeKind.Utc) ? dateTime : ((dateTime.Kind == DateTimeKind.Local) ? dateTime.ToUniversalTime() : DateTime.SpecifyKind(dateTime, DateTimeKind.Utc))); if (dateTime2.Year >= 2020 && dateTime2.Year <= 2100 && Math.Abs((dateTime2 - utcNow).TotalDays) <= 366.0) { return dateTime2; } if (!InvalidNetworkClockWarningLogged) { InvalidNetworkClockWarningLogged = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Ungueltige Valheim-Netzwerkzeit verworfen: " + dateTime2.ToString("O", CultureInfo.InvariantCulture) + "; lokale UTC-Zeit wird verwendet.")); } } } } } catch { } return utcNow; } private static bool IsPlausibleResetTimestamp(DateTime value, DateTime now) { if (value == DateTime.MinValue || value.Year < 2020 || value.Year > 2100) { return false; } float num = ((TargetedResetFeature.DungeonResetCooldownMaxMinutes != null) ? TargetedResetFeature.DungeonResetCooldownMaxMinutes.Value : 240f); double value2 = Math.Max(10080.0, Math.Max(1.0, num) * 4.0); if (value < now.AddDays(-3650.0)) { return false; } if (value > now.AddMinutes(value2)) { return false; } return true; } private static void LogInvalidTimerRepairOnce(DungeonGenerator dungeon, DateTime replacement, string source) { string text = (((Object)(object)dungeon != (Object)null) ? DungeonId(dungeon) : ""); if (RepairedInvalidTimerWarnings.Add(text)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Unplausiblen Dungeon-Reset-Timer automatisch korrigiert: " + text + "; Quelle=" + source + "; NeuerTermin=" + replacement.ToString("O", CultureInfo.InvariantCulture))); } } } private static DateTime ParseDate(string raw) { if (!string.IsNullOrWhiteSpace(raw) && DateTime.TryParse(raw, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out var result)) { return result.ToUniversalTime(); } return DateTime.MinValue; } private static string HumanTime(TimeSpan span) { if (span < TimeSpan.Zero) { span = TimeSpan.Zero; } if (span.TotalMinutes < 1.0) { return Math.Max(1.0, Math.Ceiling(span.TotalSeconds)).ToString("0", CultureInfo.InvariantCulture) + "s"; } int num = Math.Max(1, (int)Math.Ceiling(span.TotalMinutes)); int num2 = num / 1440; int num3 = num % 1440 / 60; int num4 = num % 60; string text = string.Empty; if (num2 > 0) { text = text + num2.ToString(CultureInfo.InvariantCulture) + "d"; } if (num3 > 0) { text = text + num3.ToString(CultureInfo.InvariantCulture) + "h"; } if (num4 > 0 || text.Length == 0) { text = text + num4.ToString(CultureInfo.InvariantCulture) + "m"; } return text; } private static bool TryDecodeResetRequest(string payload, out string requestId, out Vector3 position, out Vector3 entrancePosition, out bool hasEntrancePosition, out string reason) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) requestId = string.Empty; position = Vector3.zero; entrancePosition = Vector3.zero; hasEntrancePosition = false; reason = string.Empty; if (string.IsNullOrWhiteSpace(payload)) { return false; } string[] array = payload.Split(new char[1] { '|' }); if (array.Length < 2) { return false; } requestId = array[0]; if (string.IsNullOrWhiteSpace(requestId)) { return false; } if (!TryDecodePosition(array[1], out position, out var suffix)) { return false; } if (array.Length >= 4) { hasEntrancePosition = TryDecodePosition(array[2], out entrancePosition, out suffix); reason = array[3]; } else { reason = ((array.Length > 2) ? array[2] : string.Empty); } return true; } private static bool TryDecodeResetAck(string payload, out string requestId, out bool accepted, out string dungeonId, out string message) { requestId = string.Empty; accepted = false; dungeonId = string.Empty; message = string.Empty; if (string.IsNullOrWhiteSpace(payload)) { return false; } string[] array = payload.Split(new char[1] { '|' }); if (array.Length < 2) { return false; } requestId = array[0]; accepted = string.Equals(array[1], "1", StringComparison.OrdinalIgnoreCase) || string.Equals(array[1], "true", StringComparison.OrdinalIgnoreCase); dungeonId = ((array.Length > 2) ? array[2] : string.Empty); message = ((array.Length > 3) ? array[3] : string.Empty); return !string.IsNullOrWhiteSpace(requestId); } private static bool TryDecodeRegenerateRequest(string payload, out string requestId, out string dungeonId, out Vector3 position) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) requestId = string.Empty; dungeonId = string.Empty; position = Vector3.zero; if (string.IsNullOrWhiteSpace(payload)) { return false; } string[] array = payload.Split(new char[1] { '|' }); if (array.Length < 3) { return false; } requestId = array[0]; dungeonId = array[1]; string suffix; if (!string.IsNullOrWhiteSpace(requestId)) { return TryDecodePosition(array[2], out position, out suffix); } return false; } private static bool TryDecodeRegenerateAck(string payload, out string requestId, out bool accepted, out string dungeonId, out string message) { requestId = string.Empty; accepted = false; dungeonId = string.Empty; message = string.Empty; if (string.IsNullOrWhiteSpace(payload)) { return false; } string[] array = payload.Split(new char[1] { '|' }); if (array.Length < 2) { return false; } requestId = array[0]; accepted = string.Equals(array[1], "1", StringComparison.OrdinalIgnoreCase) || string.Equals(array[1], "true", StringComparison.OrdinalIgnoreCase); dungeonId = ((array.Length > 2) ? array[2] : string.Empty); message = ((array.Length > 3) ? array[3] : string.Empty); return !string.IsNullOrWhiteSpace(requestId); } private static string SanitizeRpcText(string value) { return (value ?? string.Empty).Replace("|", "/").Replace("\r", " ").Replace("\n", " "); } private static string EncodePosition(Vector3 position) { return position.x.ToString("R", CultureInfo.InvariantCulture) + "," + position.y.ToString("R", CultureInfo.InvariantCulture) + "," + position.z.ToString("R", CultureInfo.InvariantCulture); } private static bool TryDecodePosition(string payload, out Vector3 position, out string suffix) { //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_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; suffix = string.Empty; if (string.IsNullOrWhiteSpace(payload)) { return false; } string[] array = payload.Split(new char[1] { '|' }); string[] array2 = array[0].Split(new char[1] { ',' }); if (array2.Length != 3) { return false; } if (!float.TryParse(array2[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return false; } if (!float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { return false; } if (!float.TryParse(array2[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { return false; } position = new Vector3(result, result2, result3); suffix = ((array.Length > 1) ? array[1] : string.Empty); return true; } private static string NormalizeNameChain(GameObject go) { if ((Object)(object)go == (Object)null) { return string.Empty; } string text = string.Empty; Transform val = go.transform; int num = 0; while ((Object)(object)val != (Object)null && num++ < 5) { text = text + " " + Plugin.NormalizeKey(((Object)val).name); val = val.parent; } return text; } private static bool ContainsAny(string haystack, string csv) { if (string.IsNullOrWhiteSpace(haystack) || string.IsNullOrWhiteSpace(csv)) { return false; } string[] array = csv.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = Plugin.NormalizeKey(array[i].Trim()); if (text.Length > 0 && haystack.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } } internal static class DungeonTalentRewardFeature { internal enum InteractionKind { None, Enter, Exit } internal sealed class TeleportInteractionState { internal InteractionKind Kind; internal Vector3 DungeonPosition; internal long PlayerId; internal bool IsValid { get { if (Kind != InteractionKind.None) { return PlayerId != 0; } return false; } } } internal sealed class DungeonLootScanResult { internal int NonEmptyContainers; internal int AvailablePickables; internal int Count => NonEmptyContainers + AvailablePickables; public override string ToString() { return "Container=" + NonEmptyContainers + "; Pickables=" + AvailablePickables + "; Gesamt=" + Count; } } private sealed class ParticipantRecord { internal long PlayerId; internal long LastKnownPeerId; internal string Serialize() { return PlayerId + "@" + LastKnownPeerId; } } internal const string IsClearedKey = "ChallengeHub_IsCleared"; internal const string ParticipantsKey = "ChallengeHub_Participants"; private const string InteractionRpc = "ChallengeHub_RPC_DungeonTalentInteraction"; private const string GrantRpc = "ChallengeHub_RPC_GrantTalentPoint"; private const string StatusRpc = "ChallengeHub_RPC_DungeonTalentStatus_v250"; private const float InteriorHeightThreshold = 3000f; private static Plugin _plugin; private static bool _rpcRegistrationStarted; private static bool _rpcRegistered; internal static void Initialize(Plugin plugin) { _plugin = plugin; TalentMenuBehaviour.Attach(((Object)(object)plugin != (Object)null) ? ((Component)plugin).gameObject : null); EnsureRpcRegistration(); } internal static void EnsureRpcRegistration() { if (!((Object)(object)_plugin == (Object)null) && !_rpcRegistrationStarted) { _rpcRegistrationStarted = true; ((MonoBehaviour)_plugin).StartCoroutine(RegisterRpcWhenReady()); } } private static IEnumerator RegisterRpcWhenReady() { while (ZRoutedRpc.instance == null) { yield return null; } if (_rpcRegistered) { yield break; } try { ZRoutedRpc.instance.Register("ChallengeHub_RPC_DungeonTalentInteraction", (Action)RPC_DungeonInteraction); ZRoutedRpc.instance.Register("ChallengeHub_RPC_GrantTalentPoint", (Action)RPC_GrantTalentPoint); ZRoutedRpc.instance.Register("ChallengeHub_RPC_DungeonTalentStatus_v250", (Action)RPC_DungeonStatus); _rpcRegistered = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Dungeon-Talent-RPCs registriert."); } } catch (Exception ex) { _rpcRegistrationStarted = false; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Dungeon-Talent-RPC-Registrierung fehlgeschlagen: " + ex)); } } } internal static TeleportInteractionState BuildTeleportState(Teleport teleport, object[] arguments) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) if (!ChallengeHubServerGateFeature.GameplayAllowed) { return null; } if ((Object)(object)teleport == (Object)null || arguments == null) { return null; } Humanoid? obj = arguments.OfType().FirstOrDefault(); Player val = (Player)(object)((obj is Player) ? obj : null); if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer) { return null; } bool[] array = arguments.OfType().ToArray(); if (array.Length != 0 && array[0]) { return null; } bool flag = SafeInInterior(val); Transform val2 = ResolveTeleportTarget(teleport); if ((Object)(object)val2 == (Object)null) { return null; } bool flag2 = IsInteriorPosition(val2.position); InteractionKind interactionKind = InteractionKind.None; if (!flag && flag2) { interactionKind = InteractionKind.Enter; } else if (flag && !flag2) { interactionKind = InteractionKind.Exit; } Vector3 position; switch (interactionKind) { case InteractionKind.None: return null; default: position = ((Component)val).transform.position; break; case InteractionKind.Enter: position = val2.position; break; } Vector3 probePosition = position; DungeonGenerator val3 = ResolveDungeon(teleport, probePosition); if ((Object)(object)val3 == (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)"Dungeon-Talentprüfung: Kein DungeonGenerator zur Teleport-Interaktion gefunden."); } return null; } return new TeleportInteractionState { Kind = interactionKind, DungeonPosition = ((Component)val3).transform.position, PlayerId = TalentStore.SafeGetPlayerId(val) }; } internal static void CommitSuccessfulTeleport(TeleportInteractionState state) { //IL_001b: 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_0065: Expected O, but got Unknown //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (!ChallengeHubServerGateFeature.GameplayAllowed || state == null || !state.IsValid) { return; } OrientationMarkerFeature.HandleSuccessfulTeleport(state.Kind, state.DungeonPosition); if (!_rpcRegistered || ZRoutedRpc.instance == null) { return; } try { long senderPeerId = ValheimNetworkCompatibility.ResolveLocalPeerId(); if (IsServer()) { ProcessDungeonInteraction(senderPeerId, state.Kind, state.DungeonPosition, state.PlayerId); return; } ZPackage val = new ZPackage(); val.Write((int)state.Kind); val.Write(state.DungeonPosition); val.Write(state.PlayerId); long num = ResolveServerPeerId(); if (num == 0L) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Dungeon-Talentinteraktion nicht gesendet: Server-Peer-ID ist unbekannt."); } } else { ZRoutedRpc.instance.InvokeRoutedRPC(num, "ChallengeHub_RPC_DungeonTalentInteraction", new object[1] { val }); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Dungeon-Talentinteraktion konnte nicht gesendet werden: " + ex.Message)); } } } private static void RPC_DungeonInteraction(long sender, ZPackage package) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) if (!IsServer() || package == null) { return; } try { InteractionKind interactionKind = (InteractionKind)package.ReadInt(); Vector3 dungeonPosition = package.ReadVector3(); long num = package.ReadLong(); if ((interactionKind != InteractionKind.Enter && interactionKind != InteractionKind.Exit) || num == 0L) { return; } if (!SenderOwnsPlayerId(sender, num)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Dungeon-Talentinteraktion wegen ungültiger Spielerzuordnung verworfen. Sender=" + sender + "; Spieler=" + num)); } } else { ProcessDungeonInteraction(sender, interactionKind, dungeonPosition, num); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Dungeon-Talentinteraktions-RPC war ungültig: " + ex.Message)); } } } private unsafe static void ProcessDungeonInteraction(long senderPeerId, InteractionKind kind, Vector3 dungeonPosition, long playerId) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) if (!ChallengeHubServerGateFeature.GameplayAllowed || !IsServer()) { return; } DungeonGenerator val = FindNearestDungeon(dungeonPosition, 900f) ?? FindNearestDungeonHorizontal(dungeonPosition, 220f); if ((Object)(object)val == (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { Vector3 val2 = dungeonPosition; log.LogWarning((object)("Dungeon-Talentprüfung: Server fand keinen Dungeon bei " + ((object)(*(Vector3*)(&val2))/*cast due to .constrained prefix*/).ToString())); } return; } ZDO val3 = ResolveDungeonStateZdo(val); if (val3 == null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Dungeon-Talentprüfung: Kein gültiger Welt-Registry-ZDO für " + ((Object)val).name)); } } else { if (!ValheimNetworkCompatibility.TryTakeServerOwnership(val3, "dungeon_talent_" + kind)) { return; } if (ReadZdoBool(val3, "ChallengeHub_IsCleared", fallback: false)) { if (kind == InteractionKind.Exit) { SendDungeonStatus(senderPeerId, 4, 0, 0, ReadParticipants(val3).Count, DungeonResetLifecycleFeature.BuildDungeonStatus(val)); } return; } DungeonLootScanResult dungeonLootScanResult = ScanDungeonLoot(val); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Dungeon-Talentscan " + kind.ToString() + " für " + DungeonIdentity(val) + ": " + dungeonLootScanResult)); } if (kind == InteractionKind.Enter) { if (dungeonLootScanResult.Count > 0) { RegisterParticipant(val3, playerId, senderPeerId); SendDungeonStatus(senderPeerId, 1, dungeonLootScanResult.NonEmptyContainers, dungeonLootScanResult.AvailablePickables, ReadParticipants(val3).Count, string.Empty); } return; } if (dungeonLootScanResult.Count > 0) { SendDungeonStatus(senderPeerId, 2, dungeonLootScanResult.NonEmptyContainers, dungeonLootScanResult.AvailablePickables, ReadParticipants(val3).Count, DungeonResetLifecycleFeature.BuildDungeonStatus(val)); return; } WriteZdoBool(val3, "ChallengeHub_IsCleared", value: true); List list = ReadParticipants(val3); string rewardId = BuildRewardId(val, val3); foreach (ParticipantRecord item in list) { long num = ResolveParticipantPeer(item); if (num == 0L) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)("Talentpunkt konnte nicht zugestellt werden; Spieler ist nicht verbunden: " + item.PlayerId)); } } else { SendTalentPoint(num, rewardId); SendDungeonStatus(num, 3, 0, 0, list.Count, DungeonResetLifecycleFeature.BuildDungeonStatus(val)); } } WriteZdoString(val3, "ChallengeHub_Participants", string.Empty); } } private static void RegisterParticipant(ZDO zdo, long playerId, long peerId) { List list = ReadParticipants(zdo); ParticipantRecord participantRecord = list.FirstOrDefault((ParticipantRecord item) => item.PlayerId == playerId); if (participantRecord == null) { list.Add(new ParticipantRecord { PlayerId = playerId, LastKnownPeerId = peerId }); } else if (peerId != 0L) { participantRecord.LastKnownPeerId = peerId; } string value = string.Join(",", from item in list where item.PlayerId != 0 group item by item.PlayerId into @group select @group.Last() into item select item.Serialize()); WriteZdoString(zdo, "ChallengeHub_Participants", value); } private static List ReadParticipants(ZDO zdo) { List list = new List(); string text = ReadZdoString(zdo, "ChallengeHub_Participants", string.Empty); if (string.IsNullOrWhiteSpace(text)) { return list; } string[] array = text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (string obj in array) { string[] array2 = obj.Trim().Split(new char[1] { '@' }); if (long.TryParse(array2[0], out var playerId) && playerId != 0L) { long result = 0L; if (array2.Length > 1) { long.TryParse(array2[1], out result); } ParticipantRecord participantRecord = list.FirstOrDefault((ParticipantRecord item) => item.PlayerId == playerId); if (participantRecord == null) { list.Add(new ParticipantRecord { PlayerId = playerId, LastKnownPeerId = result }); } else if (result != 0L) { participantRecord.LastKnownPeerId = result; } } } return list; } private static void SendDungeonStatus(long targetPeerId, int kind, int containers, int pickables, int participants, string lifecycleStatus) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown if (targetPeerId != 0L && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(kind); val.Write(Math.Max(0, containers)); val.Write(Math.Max(0, pickables)); val.Write(Math.Max(0, participants)); val.Write(lifecycleStatus ?? string.Empty); ZRoutedRpc.instance.InvokeRoutedRPC(targetPeerId, "ChallengeHub_RPC_DungeonTalentStatus_v250", new object[1] { val }); } } private static void RPC_DungeonStatus(long sender, ZPackage package) { if (package == null || !IsTrustedServerSender(sender)) { return; } try { int num = package.ReadInt(); int num2 = package.ReadInt(); int num3 = package.ReadInt(); int num4 = package.ReadInt(); string text = package.ReadString(); List list = new List(); if (num == 1 && TalentStore.HasSkill("orientation.participant_status")) { list.Add("Für Talentbelohnung registriert"); list.Add("Teilnehmer: " + num4); } else if (num == 2 && TalentStore.HasSkill("orientation.loot_overview")) { list.Add("Dungeon noch nicht geleert"); list.Add(num2 + " gefüllte Behälter"); list.Add(num3 + " ungepflückte Objekte"); } else if (num == 3 && TalentStore.HasSkill("orientation.completion_notice")) { list.Add("Dungeon vollständig geleert"); list.Add(num4 + " Teilnehmer erhalten je 1 Talentpunkt"); } if (num >= 2 && TalentStore.HasSkill("orientation.dungeon_clock") && !string.IsNullOrWhiteSpace(text)) { if (list.Count > 0) { list.Add(string.Empty); } list.Add(text); } if (list.Count > 0) { MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, string.Join("\n", list), 0, (Sprite)null, false); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Dungeon-Status-RPC konnte nicht gelesen werden: " + ex.Message)); } } } private static void SendTalentPoint(long targetPeerId, string rewardId) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown if (ZRoutedRpc.instance != null && targetPeerId != 0L) { ZPackage val = new ZPackage(); val.Write(rewardId ?? string.Empty); val.Write(1); ZRoutedRpc.instance.InvokeRoutedRPC(targetPeerId, "ChallengeHub_RPC_GrantTalentPoint", new object[1] { val }); } } private static void RPC_GrantTalentPoint(long sender, ZPackage package) { if (package == null || !IsTrustedServerSender(sender)) { return; } try { string rewardId = package.ReadString(); int num = package.ReadInt(); if (num > 0 && num <= 10 && TalentStore.GrantTalentPoint(rewardId, num) && !TalentStore.HasSkill("orientation.completion_notice")) { MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, "Dungeon geleert! +" + num + " Talentpunkt" + ((num == 1) ? string.Empty : "e"), 0, (Sprite)null, false); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Talentpunkt-RPC konnte nicht verarbeitet werden: " + ex.Message)); } } } internal static DungeonLootScanResult ScanDungeonLoot(DungeonGenerator dungeon) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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_00dc: Unknown result type (might be due to invalid IL or missing references) DungeonLootScanResult dungeonLootScanResult = new DungeonLootScanResult(); if ((Object)(object)dungeon == (Object)null) { return dungeonLootScanResult; } Bounds dungeonBounds = GetDungeonBounds(dungeon); Container[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Container val in array) { if (!((Object)(object)val == (Object)null) && ((Bounds)(ref dungeonBounds)).Contains(((Component)val).transform.position) && !((Object)(object)((Component)val).GetComponent() != (Object)null) && !((Object)(object)((Component)val).GetComponentInParent() != (Object)null)) { ZNetView component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null && component.IsValid() && val.GetInventory() != null && val.GetInventory().NrOfItems() > 0) { dungeonLootScanResult.NonEmptyContainers++; } } } Pickable[] array2 = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Pickable val2 in array2) { if (!((Object)(object)val2 == (Object)null) && ((Bounds)(ref dungeonBounds)).Contains(((Component)val2).transform.position) && IsAvailableNetworkPickable(val2)) { dungeonLootScanResult.AvailablePickables++; } } return dungeonLootScanResult; } private static ZNetView ResolveContainerNetworkView(Container container) { if ((Object)(object)container == (Object)null) { return null; } try { object? obj = AccessTools.Field(((object)container).GetType(), "m_rootObjectOverride")?.GetValue(container); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if (val != null && (Object)(object)val != (Object)null) { return val.GetComponent(); } } catch { } try { return ((Component)container).GetComponent(); } catch { return null; } } private static bool IsAvailableNetworkPickable(Pickable pickable) { if ((Object)(object)pickable == (Object)null || !((Behaviour)pickable).isActiveAndEnabled || !((Component)pickable).gameObject.activeInHierarchy) { return false; } ZDO validZdo; try { validZdo = GetValidZdo(((Component)pickable).GetComponent()); } catch { return false; } if (validZdo == null) { return false; } if (ReadZdoBool(validZdo, "picked", fallback: false) || !ReadZdoBool(validZdo, "enabled", fallback: true)) { return false; } try { MethodInfo methodInfo = AccessTools.Method(((object)pickable).GetType(), "CanBePicked", Type.EmptyTypes, (Type[])null); if (methodInfo != null && methodInfo.Invoke(pickable, null) is bool result) { return result; } } catch { } try { object obj3 = AccessTools.Field(((object)pickable).GetType(), "m_picked")?.GetValue(pickable); bool flag = default(bool); int num; if (obj3 is bool) { flag = (bool)obj3; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { return false; } if (AccessTools.Field(((object)pickable).GetType(), "m_enabled")?.GetValue(pickable) is int num2 && num2 <= 0) { return false; } if ((Object)(object)pickable.m_hideWhenPicked != (Object)null && !pickable.m_hideWhenPicked.activeInHierarchy) { return false; } } catch { return false; } return true; } internal static void ResetRewardState(DungeonGenerator dungeon) { if (IsServer() && !((Object)(object)dungeon == (Object)null)) { ResetRewardState(ResolveDungeonStateZdo(dungeon), DungeonIdentity(dungeon)); } } internal static void ResetRewardState(ZDO stateZdo, string dungeonKey) { if (IsServer() && stateZdo != null && stateZdo.IsValid() && ValheimNetworkCompatibility.TryTakeServerOwnership(stateZdo, "dungeon_talent_reset:" + (dungeonKey ?? "unknown"))) { WriteZdoBool(stateZdo, "ChallengeHub_IsCleared", value: false); WriteZdoString(stateZdo, "ChallengeHub_Participants", string.Empty); } } internal static DungeonGenerator FindDungeonAt(Vector3 position) { //IL_0000: 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) return FindNearestDungeon(position, 900f) ?? FindNearestDungeonHorizontal(position, 220f); } internal static ZDO ResolveDungeonStateZdo(DungeonGenerator dungeon) { ZDO val = DungeonWorldRegistry.ResolveStateZdo(dungeon, IsServer()); if (val != null && val.IsValid()) { return val; } return GetValidZdo(ResolveDungeonAnchor(dungeon)); } internal static bool IsDungeonCleared(DungeonGenerator dungeon) { return ReadZdoBool(ResolveDungeonStateZdo(dungeon), "ChallengeHub_IsCleared", fallback: false); } internal static bool ValidateSenderOwnsPlayer(long senderPeerId, long playerId) { return SenderOwnsPlayerId(senderPeerId, playerId); } internal static bool IsServerAuthority() { return IsServer(); } internal static long ResolveServerPeerForClient() { return ResolveServerPeerId(); } private static DungeonGenerator ResolveDungeon(Teleport teleport, Vector3 probePosition) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)teleport != (Object)null) { try { Transform val = ((Component)teleport).transform; int num = 0; while ((Object)(object)val != (Object)null && num < 6) { DungeonGenerator component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } DungeonGenerator componentInChildren = ((Component)val).GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null) { return componentInChildren; } num++; val = val.parent; } } catch { } } return FindNearestDungeon(probePosition, 900f) ?? FindNearestDungeonHorizontal(probePosition, 220f); } private static Transform ResolveTeleportTarget(Teleport teleport) { if ((Object)(object)teleport == (Object)null) { return null; } try { object obj = AccessTools.Field(((object)teleport).GetType(), "m_targetPoint")?.GetValue(teleport); Transform val = (Transform)((obj is Transform) ? obj : null); if (val != null) { return val; } Component val2 = (Component)((obj is Component) ? obj : null); if (val2 != null) { return val2.transform; } } catch { } return null; } private static bool SafeInInterior(Player player) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) try { return (Object)(object)player != (Object)null && ((Character)player).InInterior(); } catch { return (Object)(object)player != (Object)null && IsInteriorPosition(((Component)player).transform.position); } } private static bool IsInteriorPosition(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return position.y >= 3000f; } private static DungeonGenerator FindNearestDungeon(Vector3 position, float maxDistance) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) DungeonGenerator result = null; float num = maxDistance; DungeonGenerator[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (DungeonGenerator val in array) { if (!((Object)(object)val == (Object)null)) { float num2 = Vector3.Distance(position, ((Component)val).transform.position); if (num2 < num) { num = num2; result = val; } } } return result; } private static DungeonGenerator FindNearestDungeonHorizontal(Vector3 position, float maxDistance) { //IL_0006: 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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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) //IL_0049: 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) DungeonGenerator result = null; float num = maxDistance; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(position.x, position.z); DungeonGenerator[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (DungeonGenerator val2 in array) { if (!((Object)(object)val2 == (Object)null)) { Vector3 position2 = ((Component)val2).transform.position; float num2 = Vector2.Distance(val, new Vector2(position2.x, position2.z)); if (num2 < num) { num = num2; result = val2; } } } return result; } private static ZNetView ResolveDungeonAnchor(DungeonGenerator dungeon) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005d: 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) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)dungeon == (Object)null) { return null; } ZNetView val = FindValidViewOnHierarchy(((Component)dungeon).transform); if ((Object)(object)val != (Object)null) { return val; } Vector3 position = ((Component)dungeon).transform.position; Teleport[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Teleport val2 in array) { if ((Object)(object)val2 == (Object)null) { continue; } Vector3 position2 = ((Component)val2).transform.position; if (!(Vector2.Distance(new Vector2(position2.x, position2.z), new Vector2(position.x, position.z)) > 220f)) { val = FindValidViewOnHierarchy(((Component)val2).transform); if ((Object)(object)val != (Object)null) { return val; } } } return null; } private static ZNetView FindValidViewOnHierarchy(Transform transform) { try { Transform val = transform; int num = 0; while ((Object)(object)val != (Object)null && num < 8) { ZNetView component = ((Component)val).GetComponent(); if (GetValidZdo(component) != null) { return component; } num++; val = val.parent; } ZNetView componentInChildren = ((Component)transform).GetComponentInChildren(true); return (GetValidZdo(componentInChildren) != null) ? componentInChildren : null; } catch { return null; } } private static ZDO GetValidZdo(ZNetView view) { try { if ((Object)(object)view == (Object)null || !view.IsValid()) { return null; } ZDO zDO = view.GetZDO(); return (zDO != null && zDO.IsValid()) ? zDO : null; } catch { return null; } } private static Bounds GetDungeonBounds(DungeonGenerator dungeon) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009b: 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) //IL_00a6: 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) //IL_00bb: 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_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)dungeon).transform.position; Vector3 val = ReadField(dungeon, "m_zoneCenter", position); Vector3 val2 = ReadField(dungeon, "m_zoneSize", new Vector3(256f, 256f, 256f)); val2.x = Mathf.Max(Mathf.Abs(val2.x), 256f); val2.y = Mathf.Max(Mathf.Abs(val2.y), 256f); val2.z = Mathf.Max(Mathf.Abs(val2.z), 256f); Vector3 val3 = position; if (Vector2.Distance(new Vector2(val.x, val.z), new Vector2(position.x, position.z)) <= Mathf.Max(48f, Mathf.Max(val2.x, val2.z) * 0.6f)) { val3.x = val.x; val3.z = val.z; } else if (Mathf.Abs(val.x) <= val2.x && Mathf.Abs(val.z) <= val2.z) { val3.x += val.x; val3.z += val.z; } if (Mathf.Abs(val.y - position.y) <= Mathf.Max(500f, val2.y * 2f)) { val3.y = val.y; } Bounds result = default(Bounds); ((Bounds)(ref result))..ctor(val3, val2); ((Bounds)(ref result)).Expand(new Vector3(36f, 120f, 36f)); return result; } private static T ReadField(object instance, string fieldName, T fallback) { try { object obj = ((instance != null) ? AccessTools.Field(instance.GetType(), fieldName) : null)?.GetValue(instance); if (obj is T) { return (T)obj; } } catch { } return fallback; } private static bool SenderOwnsPlayerId(long senderPeerId, long playerId) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) if (senderPeerId == 0L || playerId == 0L) { return false; } ZNetPeer val = null; try { ZNet instance = ZNet.instance; val = ((instance != null) ? instance.GetPeer(senderPeerId) : null); } catch { } foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer == (Object)null || TalentStore.SafeGetPlayerId(allPlayer) != playerId) { continue; } if (GetPlayerPeerOwner(allPlayer) == senderPeerId) { return true; } if (val != null && TryReadPeerCharacterId(val, out var result)) { ZDO validZdo = GetValidZdo(((Component)allPlayer).GetComponent()); if (validZdo != null && ((ZDOID)(ref validZdo.m_uid)).Equals(result)) { return true; } } } return false; } private static bool TryReadPeerCharacterId(ZNetPeer peer, out ZDOID result) { //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_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) result = ZDOID.None; if (peer == null) { return false; } string[] array = new string[4] { "m_characterID", "m_characterId", "CharacterID", "CharacterId" }; foreach (string name in array) { try { FieldInfo field = ((object)peer).GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.FieldType == typeof(ZDOID)) { result = (ZDOID)field.GetValue(peer); return !((ZDOID)(ref result)).IsNone(); } PropertyInfo property = ((object)peer).GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.PropertyType == typeof(ZDOID)) { result = (ZDOID)property.GetValue(peer, null); return !((ZDOID)(ref result)).IsNone(); } } catch { } } return false; } private static long ResolveParticipantPeer(ParticipantRecord participant) { if (participant == null) { return 0L; } if (participant.LastKnownPeerId != 0L && IsConnectedPeer(participant.LastKnownPeerId)) { return participant.LastKnownPeerId; } foreach (Player allPlayer in Player.GetAllPlayers()) { if (!((Object)(object)allPlayer == (Object)null) && TalentStore.SafeGetPlayerId(allPlayer) == participant.PlayerId) { long playerPeerOwner = GetPlayerPeerOwner(allPlayer); if (playerPeerOwner != 0L && IsConnectedPeer(playerPeerOwner)) { return playerPeerOwner; } } } return 0L; } private static long GetPlayerPeerOwner(Player player) { try { ZDO validZdo = GetValidZdo(((Object)(object)player != (Object)null) ? ((Component)player).GetComponent() : null); return (validZdo != null) ? validZdo.GetOwner() : 0; } catch { return 0L; } } private static bool IsConnectedPeer(long peerId) { if (peerId == 0L || (Object)(object)ZNet.instance == (Object)null) { return false; } if (peerId == ValheimNetworkCompatibility.ResolveLocalPeerId()) { return true; } try { return ZNet.instance.GetPeer(peerId) != null; } catch { return false; } } private static bool IsTrustedServerSender(long sender) { if (ZRoutedRpc.instance == null) { return false; } if (IsServer() && sender == ValheimNetworkCompatibility.ResolveLocalPeerId()) { return true; } return sender == ResolveServerPeerId(); } private static long ResolveServerPeerId() { try { if (ZRoutedRpc.instance == null) { return 0L; } MethodInfo methodInfo = AccessTools.Method(((object)ZRoutedRpc.instance).GetType(), "GetServerPeerID", (Type[])null, (Type[])null) ?? AccessTools.Method(((object)ZRoutedRpc.instance).GetType(), "GetServerPeerId", (Type[])null, (Type[])null); if (methodInfo != null) { return Convert.ToInt64(methodInfo.Invoke(ZRoutedRpc.instance, null)); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Server-Peer-ID konnte nicht gelesen werden: " + ex.Message)); } } return 0L; } private static bool IsServer() { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } private static string BuildRewardId(DungeonGenerator dungeon, ZDO zdo) { int result = 0; if (zdo != null) { int.TryParse(DungeonWorldRegistry.Read(zdo, "ChallengeHub.WorldRegistry.Generation", "0"), out result); } return "dungeon:" + DungeonIdentity(dungeon) + ":generation:" + result; } private static string DungeonIdentity(DungeonGenerator dungeon) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)dungeon == (Object)null) { return "unknown"; } try { StableDungeonContext stableDungeonContext = DungeonWorldRegistry.Resolve(dungeon, IsServer()); if (stableDungeonContext != null && !string.IsNullOrWhiteSpace(stableDungeonContext.DungeonKey)) { return stableDungeonContext.DungeonKey; } } catch { } Vector3 position = ((Component)dungeon).transform.position; return ((Object)dungeon).name + "@" + Mathf.RoundToInt(position.x) + ":" + Mathf.RoundToInt(position.z); } private static bool ReadZdoBool(ZDO zdo, string key, bool fallback) { try { return (zdo != null) ? zdo.GetBool(key, fallback) : fallback; } catch { return fallback; } } private static void WriteZdoBool(ZDO zdo, string key, bool value) { try { if (zdo != null) { zdo.Set(key, value); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Dungeon-ZDO-Bool konnte nicht geschrieben werden: " + ex.Message)); } } } private static string ReadZdoString(ZDO zdo, string key, string fallback) { try { return (zdo != null) ? zdo.GetString(key, fallback) : fallback; } catch { return fallback; } } private static void WriteZdoString(ZDO zdo, string key, string value) { try { if (zdo != null) { zdo.Set(key, value ?? string.Empty); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Dungeon-ZDO-String konnte nicht geschrieben werden: " + ex.Message)); } } } } [HarmonyPatch] internal static class DungeonTalentTeleportInteractPatch { private static IEnumerable TargetMethods() { return (from method in typeof(Teleport).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where method.ReturnType == typeof(bool) where method.Name == "Interact" || method.Name.EndsWith(".Interact", StringComparison.Ordinal) select method).Where(delegate(MethodInfo method) { ParameterInfo[] parameters = method.GetParameters(); return parameters.Any((ParameterInfo parameter) => typeof(Humanoid).IsAssignableFrom(parameter.ParameterType)) && parameters.Count((ParameterInfo parameter) => parameter.ParameterType == typeof(bool)) >= 1; }); } [HarmonyPrefix] [HarmonyPriority(0)] private static void Prefix(Teleport __instance, object[] __args, ref DungeonTalentRewardFeature.TeleportInteractionState __state) { __state = DungeonTalentRewardFeature.BuildTeleportState(__instance, __args); } [HarmonyPostfix] private static void Postfix(bool __result, DungeonTalentRewardFeature.TeleportInteractionState __state) { if (__result) { DungeonTalentRewardFeature.CommitSuccessfulTeleport(__state); } } } [HarmonyPatch(typeof(Game), "Start")] internal static class DungeonTalentGameStartPatch { [HarmonyPostfix] private static void Postfix() { DungeonTalentRewardFeature.EnsureRpcRegistration(); } } internal sealed class StableDungeonContext { internal string DungeonKey = string.Empty; internal long WorldUid; internal Vector2i Zone; internal Vector3 ExteriorPosition; internal string LocationPrefabName = string.Empty; internal int LocationPrefabHash; internal float ExteriorRadius; internal LocationInstance Location; internal bool HasLocation; internal ZDO StateZdo; } internal static class DungeonWorldRegistry { internal const string RegistryKindKey = "ChallengeHub.WorldRegistry.Kind"; internal const string RegistryRootKind = "root"; internal const string RegistryDungeonKind = "dungeon"; internal const string RegistrySchemaKey = "ChallengeHub.WorldRegistry.Schema"; internal const string RegistrySchema = "2.6.4"; internal const string DungeonKeyField = "ChallengeHub.WorldRegistry.DungeonKey"; internal const string WorldUidField = "ChallengeHub.WorldRegistry.WorldUid"; internal const string ZoneXField = "ChallengeHub.WorldRegistry.ZoneX"; internal const string ZoneYField = "ChallengeHub.WorldRegistry.ZoneY"; internal const string LocationNameField = "ChallengeHub.WorldRegistry.LocationName"; internal const string LocationHashField = "ChallengeHub.WorldRegistry.LocationHash"; internal const string ExteriorPositionField = "ChallengeHub.WorldRegistry.ExteriorPosition"; internal const string RootIndexField = "ChallengeHub.WorldRegistry.Keys"; internal const string GenerationField = "ChallengeHub.WorldRegistry.Generation"; internal const string LastPreflightField = "ChallengeHub.WorldRegistry.LastPreflight"; internal const string LastVerificationField = "ChallengeHub.WorldRegistry.LastVerification"; private static readonly string[] LegacyKeys = new string[12] { "ChallengeHub.DungeonLifecycle.State", "ChallengeHub.DungeonLifecycle.NextResetUtc", "ChallengeHub.DungeonLifecycle.RequestedAtUtc", "ChallengeHub.DungeonLifecycle.EmptySinceUtc", "ChallengeHub.DungeonLifecycle.PreparedAtUtc", "ChallengeHub.DungeonLifecycle.LastResetUtc", "ChallengeHub.DungeonLifecycle.Reason", "ChallengeHub.Dungeon.Tier", "ChallengeHub.DungeonLifecycle.Schema", "ChallengeHub_IsCleared", "ChallengeHub_Participants", "ChallengeHub_OrientationSigns" }; private static ZDO _root; private static long _cachedWorldUid; private static readonly Dictionary DungeonStates = new Dictionary(StringComparer.Ordinal); private static readonly HashSet RegistryIds = new HashSet(); private static bool _cacheBuilt; internal static StableDungeonContext Resolve(DungeonGenerator dungeon, bool createState = true) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)dungeon == (Object)null) { return null; } StableDungeonContext stableDungeonContext = Resolve(((Component)dungeon).transform.position, ((Object)dungeon).name, createState); if (stableDungeonContext == null) { return null; } if (createState && stableDungeonContext.StateZdo != null) { MigrateLegacyState(dungeon, stableDungeonContext.StateZdo); } return stableDungeonContext; } internal static StableDungeonContext Resolve(Vector3 position, string fallbackName, bool createState = true) { //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) //IL_0015: 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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: 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_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: 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) if ((Object)(object)ZoneSystem.instance == (Object)null) { return null; } Vector2i selectedZone; Vector2i requestedZone = (selectedZone = ZoneSystem.GetZone(position)); LocationInstance selectedLocation = default(LocationInstance); bool num = TryResolveLocation(requestedZone, position, out selectedZone, out selectedLocation); string prefabName = string.Empty; float exteriorRadius = 0f; bool flag = num && ValheimPrivateAccess.TryGetLocationMetadata(selectedLocation, out prefabName, out exteriorRadius); Vector3 exteriorPosition = (Vector3)(flag ? selectedLocation.m_position : new Vector3(position.x, 0f, position.z)); string text = (flag ? prefabName : NormalizeFallbackName(fallbackName)); int num2 = ((!string.IsNullOrWhiteSpace(text)) ? StringExtensionMethods.GetStableHashCode(text) : 0); float exteriorRadius2 = (flag ? exteriorRadius : 80f); long worldUid = GetWorldUid(); string dungeonKey = BuildKey(worldUid, selectedZone, num2, text); StableDungeonContext obj = new StableDungeonContext { DungeonKey = dungeonKey, WorldUid = worldUid, Zone = selectedZone, ExteriorPosition = exteriorPosition, LocationPrefabName = text, LocationPrefabHash = num2, ExteriorRadius = exteriorRadius2, Location = selectedLocation, HasLocation = flag }; obj.StateZdo = ResolveStateZdo(obj, createState); return obj; } internal static ZDO ResolveStateZdo(DungeonGenerator dungeon, bool create = true) { return Resolve(dungeon, create)?.StateZdo; } internal static ZDO ResolveStateZdo(string dungeonKey) { if (string.IsNullOrWhiteSpace(dungeonKey)) { return null; } EnsureCache(); if (DungeonStates.TryGetValue(dungeonKey, out var value) && IsValid(value)) { return value; } return FindStateByKey(dungeonKey); } internal static bool IsRegistryZdo(ZDO zdo) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) if (!IsValid(zdo)) { return false; } if (RegistryIds.Contains(zdo.m_uid)) { return true; } try { string a = zdo.GetString("ChallengeHub.WorldRegistry.Kind", string.Empty); int num; if (!string.Equals(a, "root", StringComparison.Ordinal)) { num = (string.Equals(a, "dungeon", StringComparison.Ordinal) ? 1 : 0); if (num == 0) { goto IL_0061; } } else { num = 1; } RegistryIds.Add(zdo.m_uid); goto IL_0061; IL_0061: return (byte)num != 0; } catch { return false; } } internal static string Read(ZDO state, string key, string fallback = "") { if (!IsValid(state) || string.IsNullOrWhiteSpace(key)) { return fallback ?? string.Empty; } try { return state.GetString(key, fallback ?? string.Empty); } catch { return fallback ?? string.Empty; } } internal static bool Write(ZDO state, string key, string value) { if (!IsServer() || !IsValid(state) || string.IsNullOrWhiteSpace(key)) { return false; } try { if (!ValheimNetworkCompatibility.TryTakeServerOwnership(state, "dungeon_registry_write:" + key)) { return false; } state.Set(key, value ?? string.Empty); return true; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Dungeon-Registry konnte nicht geschrieben werden: " + key + " -> " + ex.Message)); } return false; } } internal static bool Write(string dungeonKey, string key, string value) { return Write(ResolveStateZdo(dungeonKey), key, value); } internal static int IncrementGeneration(ZDO state) { if (!IsValid(state)) { return 0; } int result = 0; int.TryParse(Read(state, "ChallengeHub.WorldRegistry.Generation", "0"), NumberStyles.Integer, CultureInfo.InvariantCulture, out result); result = Mathf.Max(0, result) + 1; if (IsServer()) { try { ValheimNetworkCompatibility.TryTakeServerOwnership(state, "dungeon_registry_generation"); state.Set("ChallengeHub.WorldRegistry.Generation", result.ToString(CultureInfo.InvariantCulture)); } catch { } } return result; } internal static IReadOnlyCollection SnapshotRegistryIds() { EnsureCache(); return (IReadOnlyCollection)(object)RegistryIds.ToArray(); } internal static void InvalidateForWorldChange() { _root = null; _cachedWorldUid = 0L; _cacheBuilt = false; DungeonStates.Clear(); RegistryIds.Clear(); } private unsafe static ZDO ResolveStateZdo(StableDungeonContext context, bool create) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) if (context == null || string.IsNullOrWhiteSpace(context.DungeonKey)) { return null; } EnsureCache(); if (DungeonStates.TryGetValue(context.DungeonKey, out var value) && IsValid(value)) { RefreshMetadata(value, context); return value; } value = FindStateByKey(context.DungeonKey); if (IsValid(value)) { RefreshMetadata(value, context); return value; } if (!create || !IsServer()) { return null; } ZDO val = CreatePersistentZdo(new Vector3(context.ExteriorPosition.x, -20000f, context.ExteriorPosition.z), "dungeon"); if (!IsValid(val)) { return null; } val.Set("ChallengeHub.WorldRegistry.DungeonKey", context.DungeonKey); val.Set("ChallengeHub.WorldRegistry.WorldUid", context.WorldUid.ToString(CultureInfo.InvariantCulture)); val.Set("ChallengeHub.WorldRegistry.ZoneX", context.Zone.x.ToString(CultureInfo.InvariantCulture)); val.Set("ChallengeHub.WorldRegistry.ZoneY", context.Zone.y.ToString(CultureInfo.InvariantCulture)); val.Set("ChallengeHub.WorldRegistry.LocationName", context.LocationPrefabName ?? string.Empty); val.Set("ChallengeHub.WorldRegistry.LocationHash", context.LocationPrefabHash.ToString(CultureInfo.InvariantCulture)); val.Set("ChallengeHub.WorldRegistry.ExteriorPosition", SerializeVector(context.ExteriorPosition)); val.Set("ChallengeHub.WorldRegistry.Generation", "0"); DungeonStates[context.DungeonKey] = val; RegistryIds.Add(val.m_uid); UpdateRootIndex(); ManualLogSource log = Plugin.Log; if (log != null) { string[] obj = new string[6] { "Dungeon-Weltregistry angelegt: ", context.DungeonKey, "; Location=", context.LocationPrefabName, "; Zone=", null }; Vector2i zone = context.Zone; obj[5] = ((object)(*(Vector2i*)(&zone))/*cast due to .constrained prefix*/).ToString(); log.LogInfo((object)string.Concat(obj)); } return val; } private static ZDO FindStateByKey(string dungeonKey) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(dungeonKey) || ZDOMan.instance == null) { return null; } try { foreach (ZDO item in ValheimPrivateAccess.SnapshotAllZdos()) { if (!IsValid(item) || !string.Equals(item.GetString("ChallengeHub.WorldRegistry.Kind", string.Empty), "dungeon", StringComparison.Ordinal)) { continue; } string text = item.GetString("ChallengeHub.WorldRegistry.DungeonKey", string.Empty); if (!string.IsNullOrWhiteSpace(text)) { DungeonStates[text] = item; RegistryIds.Add(item.m_uid); if (string.Equals(text, dungeonKey, StringComparison.Ordinal)) { return item; } } } } catch { } return null; } private static void EnsureCache() { //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) long worldUid = GetWorldUid(); if (_cacheBuilt && _cachedWorldUid == worldUid) { return; } _cacheBuilt = true; _cachedWorldUid = worldUid; _root = null; DungeonStates.Clear(); RegistryIds.Clear(); if (ZDOMan.instance != null) { try { foreach (ZDO item in ValheimPrivateAccess.SnapshotAllZdos()) { if (!IsValid(item)) { continue; } string a; try { a = item.GetString("ChallengeHub.WorldRegistry.Kind", string.Empty); } catch { continue; } if (string.Equals(a, "root", StringComparison.Ordinal)) { _root = item; RegistryIds.Add(item.m_uid); } else if (string.Equals(a, "dungeon", StringComparison.Ordinal)) { string text = item.GetString("ChallengeHub.WorldRegistry.DungeonKey", string.Empty); if (!string.IsNullOrWhiteSpace(text)) { DungeonStates[text] = item; RegistryIds.Add(item.m_uid); } } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Dungeon-Weltregistry konnte nur teilweise eingelesen werden: " + ex.Message)); } } } if (_root == null && IsServer()) { _root = CreatePersistentZdo(new Vector3(0f, -20000f, 0f), "root"); } if (IsValid(_root)) { RegistryIds.Add(_root.m_uid); try { _root.Set("ChallengeHub.WorldRegistry.Schema", "2.6.4"); _root.Set("ChallengeHub.WorldRegistry.WorldUid", worldUid.ToString(CultureInfo.InvariantCulture)); } catch { } } } private static ZDO CreatePersistentZdo(Vector3 position, string kind) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (!IsServer() || ZDOMan.instance == null) { return null; } try { ZDO val = ZDOMan.instance.CreateNewZDO(position, 0); if (val == null) { return null; } val.Persistent = true; val.Distant = true; val.SetOwner(ZDOMan.GetSessionID()); val.Set("ChallengeHub.WorldRegistry.Kind", kind ?? string.Empty); val.Set("ChallengeHub.WorldRegistry.Schema", "2.6.4"); return val; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("Persistenter ChallengeHub-Registry-ZDO konnte nicht angelegt werden: " + ex)); } return null; } } private static void RefreshMetadata(ZDO state, StableDungeonContext context) { //IL_00cd: Unknown result type (might be due to invalid IL or missing references) if (!IsServer() || !IsValid(state) || context == null) { return; } try { if (ValheimNetworkCompatibility.TryTakeServerOwnership(state, "dungeon_registry_metadata")) { state.Set("ChallengeHub.WorldRegistry.Schema", "2.6.4"); state.Set("ChallengeHub.WorldRegistry.WorldUid", context.WorldUid.ToString(CultureInfo.InvariantCulture)); state.Set("ChallengeHub.WorldRegistry.ZoneX", context.Zone.x.ToString(CultureInfo.InvariantCulture)); state.Set("ChallengeHub.WorldRegistry.ZoneY", context.Zone.y.ToString(CultureInfo.InvariantCulture)); state.Set("ChallengeHub.WorldRegistry.LocationName", context.LocationPrefabName ?? string.Empty); state.Set("ChallengeHub.WorldRegistry.LocationHash", context.LocationPrefabHash.ToString(CultureInfo.InvariantCulture)); state.Set("ChallengeHub.WorldRegistry.ExteriorPosition", SerializeVector(context.ExteriorPosition)); } } catch { } } private static void UpdateRootIndex() { if (!IsServer() || !IsValid(_root)) { return; } try { if (ValheimNetworkCompatibility.TryTakeServerOwnership(_root, "dungeon_registry_index")) { string text = string.Join("\n", DungeonStates.Keys.OrderBy((string value) => value, StringComparer.Ordinal)); _root.Set("ChallengeHub.WorldRegistry.Keys", text); _root.Set("ChallengeHub.WorldRegistry.Schema", "2.6.4"); } } catch { } } private static void MigrateLegacyState(DungeonGenerator dungeon, ZDO state) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (!IsServer() || (Object)(object)dungeon == (Object)null || !IsValid(state)) { return; } ZDO val = ResolveLegacyAnchorZdo(dungeon); if (!IsValid(val) || ((ZDOID)(ref val.m_uid)).Equals(state.m_uid)) { return; } bool flag = false; string[] legacyKeys = LegacyKeys; foreach (string text in legacyKeys) { try { if (string.IsNullOrEmpty(state.GetString(text, string.Empty))) { string text2 = val.GetString(text, string.Empty); if (!string.IsNullOrEmpty(text2)) { state.Set(text, text2); flag = true; } } } catch { } } try { if (!state.GetBool("ChallengeHub_IsCleared", false) && val.GetBool("ChallengeHub_IsCleared", false)) { state.Set("ChallengeHub_IsCleared", true); flag = true; } } catch { } if (flag) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Legacy-Dungeonstatus in Weltregistry migriert: " + state.GetString("ChallengeHub.WorldRegistry.DungeonKey", string.Empty))); } } } private static ZDO ResolveLegacyAnchorZdo(DungeonGenerator dungeon) { if ((Object)(object)dungeon == (Object)null) { return null; } try { Transform val = ((Component)dungeon).transform; int num = 0; while ((Object)(object)val != (Object)null && num < 8) { ZNetView component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null && component.IsValid() && component.GetZDO() != null) { return component.GetZDO(); } num++; val = val.parent; } ZNetView componentInChildren = ((Component)dungeon).GetComponentInChildren(true); return ((Object)(object)componentInChildren != (Object)null && componentInChildren.IsValid()) ? componentInChildren.GetZDO() : null; } catch { return null; } } private static bool TryResolveLocation(Vector2i requestedZone, Vector3 dungeonPosition, out Vector2i selectedZone, out LocationInstance selectedLocation) { //IL_0001: 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_0008: 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) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0086: 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_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0094: 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_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) selectedZone = requestedZone; selectedLocation = default(LocationInstance); ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)instance == (Object)null) { return false; } if (instance.m_locationInstances.TryGetValue(requestedZone, out selectedLocation)) { return true; } float num = 260f; bool result = false; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(dungeonPosition.x, dungeonPosition.z); Vector2i val2 = default(Vector2i); Vector2 val3 = default(Vector2); for (int i = -2; i <= 2; i++) { for (int j = -2; j <= 2; j++) { ((Vector2i)(ref val2))..ctor(requestedZone.x + i, requestedZone.y + j); if (instance.m_locationInstances.TryGetValue(val2, out var value)) { ((Vector2)(ref val3))..ctor(value.m_position.x, value.m_position.z); float num2 = Vector2.Distance(val, val3); if (!(num2 >= num)) { num = num2; selectedZone = val2; selectedLocation = value; result = true; } } } } return result; } private static string SafeLocationName(LocationInstance location) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (!ValheimPrivateAccess.TryGetLocationMetadata(location, out var prefabName, out var _)) { return string.Empty; } return prefabName; } private static string BuildKey(long worldUid, Vector2i zone, int locationHash, string locationName) { string text = ((locationHash != 0) ? locationHash.ToString(CultureInfo.InvariantCulture) : StringExtensionMethods.GetStableHashCode(NormalizeFallbackName(locationName)).ToString(CultureInfo.InvariantCulture)); return "v1:" + worldUid.ToString(CultureInfo.InvariantCulture) + ":" + zone.x.ToString(CultureInfo.InvariantCulture) + ":" + zone.y.ToString(CultureInfo.InvariantCulture) + ":" + text; } private static long GetWorldUid() { if ((Object)(object)ZNet.instance == (Object)null) { return 0L; } try { MethodInfo methodInfo = AccessTools.Method(((object)ZNet.instance).GetType(), "GetWorldUID", (Type[])null, (Type[])null) ?? AccessTools.Method(((object)ZNet.instance).GetType(), "GetWorldUid", (Type[])null, (Type[])null); if (methodInfo != null) { return Convert.ToInt64(methodInfo.Invoke(ZNet.instance, null), CultureInfo.InvariantCulture); } } catch { } try { object obj2 = AccessTools.Field(((object)ZNet.instance).GetType(), "m_world")?.GetValue(ZNet.instance); FieldInfo fieldInfo = ((obj2 != null) ? AccessTools.Field(obj2.GetType(), "m_uid") : null); if (fieldInfo != null) { return Convert.ToInt64(fieldInfo.GetValue(obj2), CultureInfo.InvariantCulture); } } catch { } return 0L; } private static string NormalizeFallbackName(string value) { string text = (value ?? "Dungeon").Replace("(Clone)", string.Empty).Trim(); if (text.Length != 0) { return text; } return "Dungeon"; } private static string SerializeVector(Vector3 value) { return value.x.ToString("R", CultureInfo.InvariantCulture) + "," + value.y.ToString("R", CultureInfo.InvariantCulture) + "," + value.z.ToString("R", CultureInfo.InvariantCulture); } private static bool IsValid(ZDO zdo) { try { return zdo != null && zdo.IsValid(); } catch { return false; } } private static bool IsServer() { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } internal static class ExplorationPartnerFeature { private static readonly HashSet Reported = new HashSet(StringComparer.OrdinalIgnoreCase); internal static void ReportDiscovery(Plugin plugin, Player player, string signal, string target, string method) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)plugin == (Object)null) && !((Object)(object)player == (Object)null) && PartnershipFeature.TryGetActiveExplorerPartner(player, out var partnerId, out var partnerName)) { Vector3 position = ((Component)player).transform.position; string text = Classify(signal, target); string text2 = text + ":" + Mathf.RoundToInt(position.x / 20f) + ":" + Mathf.RoundToInt(position.z / 20f); string item = player.GetPlayerID() + ":" + partnerId + ":" + text2; if (Reported.Add(item)) { plugin.SendEvent("exploration_partner_discovery", player, new Dictionary { { "partnershipType", "explore" }, { "partnerPlayerId", partnerId }, { "partnerPlayerName", partnerName ?? string.Empty }, { "discoveryType", text }, { "discoveryScope", text2 }, { "signal", Plugin.NormalizeKey(signal) }, { "targetPrefab", Plugin.NormalizeKey(target) }, { "sourceObject", method ?? "exploration_partner" }, { "discoveryPoints", 40 }, { "partnerConfirmationPoints", 10 }, { "biome", Plugin.CurrentBiome(position) }, { "position", Plugin.SerializeVector(position) }, { "attributionMethod", "explorer_partnership_2_9_15" } }); } } } private static string Classify(string signal, string target) { string text = Plugin.NormalizeKey((signal ?? string.Empty) + " " + (target ?? string.Empty)); if (text.Contains("trader")) { return "trader"; } if (text.Contains("altar") || text.Contains("vegvisir") || text.Contains("offeringbowl")) { return "boss_altar"; } if (text.Contains("crypt") || text.Contains("burial") || text.Contains("chamber") || text.Contains("cave") || text.Contains("dungeon") || text.Contains("mine")) { return "dungeon"; } if (text.Contains("resource")) { return "resource_site"; } if (text.Contains("ocean") || text.Contains("ship")) { return "sea_route"; } return "exploration_location"; } } internal static class GoalAutomationFeature { private sealed class AnimalGrowState { internal Vector3 Position; internal string AdultPrefab; } [HarmonyPatch] private static class NaturalAnimalBirthPatch { private static IEnumerable TargetMethods() { Type type = AccessTools.TypeByName("Procreation"); if (type == null) { yield break; } MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name.Equals("Procreate", StringComparison.OrdinalIgnoreCase)) { yield return methodInfo; } } } private static void Postfix(Component __instance) { ObserveAnimalLifecycle(__instance, grown: false); } } [HarmonyPatch] private static class AnimalGrowupPatch { private static IEnumerable TargetMethods() { Type type = AccessTools.TypeByName("Growup"); if (!(type == null)) { MethodInfo methodInfo = AccessTools.DeclaredMethod(type, "GrowUpdate", Type.EmptyTypes, (Type[])null); if (methodInfo != null) { yield return methodInfo; } } } private static void Prefix(Component __instance, out AnimalGrowState __state) { __state = CaptureAnimalGrowState(__instance); } private static void Postfix(AnimalGrowState __state) { ConfirmAnimalGrown(__state); } } [HarmonyPatch] private static class PlantGrowPatch { private static IEnumerable TargetMethods() { MethodInfo[] methods = typeof(Plant).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name.Equals("Grow", StringComparison.OrdinalIgnoreCase)) { yield return methodInfo; } } } private static void Postfix(Component __instance) { ObservePlantGrown(__instance); } } private static readonly Dictionary Counts = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly HashSet Distinct = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly HashSet Sent = new HashSet(StringComparer.OrdinalIgnoreCase); private static bool _emitting; private static long _playerId; private static Vector3 _lastPosition; private static Vector3 _dangerStart; private static Vector3 _oceanStart; private static float _lastTickAt; private static float _biomeEnteredAt; private static float _dangerSeconds; private static float _dangerDistance; private static int _dangerKills; private static float _oceanDistance; private static float _borderWindowStartedAt; private static float _currentBiomeDistance; private static float _oceanSeconds; private static string _oceanAttemptScope = ""; private static int _lastOceanProgressBucket; private static string _biomeAttemptScope = ""; private static string _dangerAttemptScope = ""; private static readonly Dictionary ProgressBuckets = new Dictionary(StringComparer.OrdinalIgnoreCase); private static bool _enteredFromStableBiome; private static float _nextRouteRescan; private static string _currentBiome = ""; private static string _previousLandBiome = ""; private static bool _wasOcean; private static string _lastAltarScope = ""; private static Vector3 _lastAltarPosition; private static readonly Dictionary> RouteMarkers = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> BorderCrossings = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary BorderWindows = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly HashSet VisitedBiomes = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly List KnownLandings = new List(); private static readonly HashSet KnownFoods = new HashSet(new string[29] { "cookedmeat", "necktailgrilled", "fishcooked", "deermeatgrilled", "boarjerky", "carrotsoup", "queensjam", "sausages", "turnipstew", "blackSoup", "onionsoup", "wolfjerky", "wolfmeatskewer", "eyescream", "bread", "loxpie", "fishwraps", "bloodpudding", "salad", "honeyglazedchicken", "meatplatter", "mushroomomelette", "mistharesupreme", "yggdrasilporridge", "cookedegg", "piquantpie", "mashedmeat", "scorchingmedley", "roastedcrustpie" }.Select(Key), StringComparer.OrdinalIgnoreCase); internal static void ObserveAnimalLifecycle(Component animal, bool grown) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)animal == (Object)null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !ServerDropAuthority.IsAuthoritative(animal) || !IsTamedAnimal(animal)) { return; } Piece obj = FindSupplyGuardian(animal.transform.position); long num = GuardianOwnerId(obj); Player val = FindPlayer(num) ?? Player.m_localPlayer ?? Player.GetAllPlayers().FirstOrDefault(); Plugin plugin = Object.FindFirstObjectByType(); if (!((Object)(object)obj == (Object)null) && num != 0L && !((Object)(object)val == (Object)null) && !((Object)(object)plugin == (Object)null)) { string text = SupplySpecies(((Object)animal).name); string evidenceId = (grown ? "grown:" : "birth:") + num + ":" + text + ":" + DateTime.UtcNow.Ticks; ZNetView component = animal.GetComponent(); ZDO val2 = ((component != null) ? component.GetZDO() : null); if (val2 != null) { val2.Set("ChallengeHub.Goal.GuardianOwnerId", num.ToString()); val2.Set("ChallengeHub.Goal.AnimalSpecies", text); val2.Set(grown ? "ChallengeHub.Goal.GrownAt" : "ChallengeHub.Goal.BornAt", DateTime.UtcNow.ToString("O")); } Progress(plugin, val, num, "farmer", grown ? "tamed_combat_support" : "animal_breeding", text, "add", 1, evidenceId); } } private static AnimalGrowState CaptureAnimalGrowState(Component growup) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)growup == (Object)null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !ServerDropAuthority.IsAuthoritative(growup)) { return null; } try { object? obj = AccessTools.Method(((object)growup).GetType(), "GetPrefab", Type.EmptyTypes, (Type[])null)?.Invoke(growup, null); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if ((Object)(object)val == (Object)null) { return null; } return new AnimalGrowState { Position = growup.transform.position, AdultPrefab = Key(((Object)val).name) }; } catch { return null; } } private static void ConfirmAnimalGrown(AnimalGrowState state) { if (state != null && !string.IsNullOrWhiteSpace(state.AdultPrefab)) { Character val = (from candidate in Object.FindObjectsByType((FindObjectsSortMode)0) where (Object)(object)candidate != (Object)null && Key(((Object)candidate).name) == state.AdultPrefab && HorizontalDistance(((Component)candidate).transform.position, state.Position) <= 2.5f orderby HorizontalDistance(((Component)candidate).transform.position, state.Position) select candidate).FirstOrDefault(); if ((Object)(object)val != (Object)null) { ObserveAnimalLifecycle((Component)(object)val, grown: true); } } } internal static void ObservePlantGrown(Component plant) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)plant == (Object)null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !ServerDropAuthority.IsAuthoritative(plant)) { return; } Piece obj = FindSupplyGuardian(plant.transform.position); long num = GuardianOwnerId(obj); Player val = FindPlayer(num) ?? Player.m_localPlayer ?? Player.GetAllPlayers().FirstOrDefault(); Plugin plugin = Object.FindFirstObjectByType(); if (!((Object)(object)obj == (Object)null) && num != 0L && !((Object)(object)val == (Object)null) && !((Object)(object)plugin == (Object)null)) { string text = SupplySpecies(((Object)plant).name); ZNetView component = plant.GetComponent(); ZDO val2 = ((component != null) ? component.GetZDO() : null); string evidenceId = "grownplant:" + num + ":" + text + ":" + DateTime.UtcNow.Ticks; if (val2 != null) { val2.Set("ChallengeHub.Goal.GuardianOwnerId", num.ToString()); val2.Set("ChallengeHub.Goal.PlantSpecies", text); val2.Set("ChallengeHub.Goal.GrownAt", DateTime.UtcNow.ToString("O")); } Progress(plugin, val, num, "farmer", "stable_farm", text, "add", 1, evidenceId); } } internal static void Tick(Plugin plugin, Player player) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_007b: 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) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_0391: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_0519: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_06fb: Unknown result type (might be due to invalid IL or missing references) //IL_0700: Unknown result type (might be due to invalid IL or missing references) //IL_0545: Unknown result type (might be due to invalid IL or missing references) //IL_054a: Unknown result type (might be due to invalid IL or missing references) //IL_0656: Unknown result type (might be due to invalid IL or missing references) //IL_065c: Unknown result type (might be due to invalid IL or missing references) //IL_06c5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)plugin == (Object)null || (Object)(object)player == (Object)null || !((Character)player).IsOwner() || !CharacterAdmissionFeature.ChallengeScoringAllowed) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; long playerID = player.GetPlayerID(); Vector3 position = ((Component)player).transform.position; string text = Key(Plugin.CurrentBiome(position)); if (_playerId != playerID || _lastTickAt <= 0f || realtimeSinceStartup - _lastTickAt > 15f) { ResetSession(playerID, position, text, realtimeSinceStartup); return; } float num = Mathf.Clamp(realtimeSinceStartup - _lastTickAt, 0f, 3f); float num2 = HorizontalDistance(_lastPosition, position); if (num2 > 60f) { num2 = 0f; } bool num3 = text.Contains("ocean"); if (!string.Equals(text, _currentBiome, StringComparison.OrdinalIgnoreCase)) { if (!string.IsNullOrWhiteSpace(_currentBiome) && !string.IsNullOrWhiteSpace(text) && !_currentBiome.Contains("ocean") && !text.Contains("ocean") && num2 <= 60f) { string text2 = PairKey(_currentBiome, text); if (!BorderCrossings.TryGetValue(text2, out var value)) { value = (BorderCrossings[text2] = new List()); } if (!BorderWindows.TryGetValue(text2, out var value2) || realtimeSinceStartup - value2 > 300f) { value.Clear(); BorderWindows[text2] = realtimeSinceStartup; } if (!value.Any((Vector3 x) => HorizontalDistance(x, position) < 40f)) { value.Add(position); } ReportBestProgress(plugin, player, "explorer", "biome_border_mapping", text2, value.Count, 1, 3); if (value.Count >= 3) { Goal(plugin, player, "explorer", "biome_border_mapping", text2); } } _enteredFromStableBiome = realtimeSinceStartup - _biomeEnteredAt >= 60f; _currentBiome = text; _biomeEnteredAt = realtimeSinceStartup; _currentBiomeDistance = 0f; _biomeAttemptScope = "biom-" + text + "-" + DateTime.UtcNow.Ticks; _dangerSeconds = 0f; _dangerDistance = 0f; _dangerAttemptScope = "gefahr-" + text + "-" + DateTime.UtcNow.Ticks; _dangerKills = 0; _dangerStart = position; } _currentBiomeDistance += num2; if (!num3 && _enteredFromStableBiome) { ReportBestProgress(plugin, player, "explorer", "new_biome_route", _biomeAttemptScope, Mathf.RoundToInt(_currentBiomeDistance), 25, 150); } if (!num3 && !string.IsNullOrWhiteSpace(text) && _enteredFromStableBiome && realtimeSinceStartup - _biomeEnteredAt >= 90f && _currentBiomeDistance >= 150f && VisitedBiomes.Add(text)) { Goal(plugin, player, "explorer", "new_biome_route", "global"); } if (Dangerous(text)) { _dangerSeconds += num; _dangerDistance += num2; ReportBestProgress(plugin, player, "explorer", "danger_scout", _dangerAttemptScope, Mathf.RoundToInt(_dangerDistance), 25, 500); if (_dangerSeconds >= 600f && _dangerDistance >= 500f && _dangerKills >= 3) { Goal(plugin, player, "explorer", "danger_scout", text); } } ResourceSiteRecord resourceSiteRecord = ResourceResetFeature.FindNearest(position, 25f); if (resourceSiteRecord != null) { Goal(plugin, player, "explorer", "resource_location", Key(resourceSiteRecord.resourceType) + ":" + Key(resourceSiteRecord.key)); } if (realtimeSinceStartup >= _nextRouteRescan) { _nextRouteRescan = realtimeSinceStartup + 5f; RescanRouteMarkers(player, text); string key = (Dangerous(text) ? text : "general"); if (RouteMarkers.TryGetValue(key, out var value3)) { float num4 = ConnectedLength(value3, 80f); if (text.Contains("mist")) { ReportBestProgress(plugin, player, "explorer", "mistlands_route", text, Mathf.RoundToInt(num4), 10, 250); } if (!string.IsNullOrWhiteSpace(_lastAltarScope)) { ReportBestProgress(plugin, player, "explorer", "safe_boss_path", _lastAltarScope, Mathf.RoundToInt(num4), 10, 300); } if (text.Contains("mist") && value3.Count >= 6 && num4 >= 250f) { Goal(plugin, player, "explorer", "mistlands_route", "global"); } if (!string.IsNullOrWhiteSpace(_lastAltarScope) && value3.Count >= 6 && num4 >= 300f && RouteReachesKnownAltar(value3, 80f) && AnyGuardianNear(value3, 80f)) { Goal(plugin, player, "explorer", "safe_boss_path", "global"); } } } Ship val = StandingShip(player) ?? NearestShip(position, 6f); int num5; if (num3) { num5 = (((Object)(object)val != (Object)null) ? 1 : 0); if (num5 != 0) { if (!_wasOcean) { _oceanStart = position; _oceanDistance = 0f; _oceanAttemptScope = "fahrt-" + player.GetPlayerID() + "-" + DateTime.UtcNow.Ticks; _lastOceanProgressBucket = 0; } _oceanDistance += num2; _oceanSeconds += num; ReportBestProgress(plugin, player, "explorer", "new_island_discovered", _oceanAttemptScope, Mathf.RoundToInt(_oceanDistance), 25, 750); int num6 = Mathf.FloorToInt(_oceanDistance / 25f); if (num6 > _lastOceanProgressBucket) { _lastOceanProgressBucket = num6; Progress(plugin, player, player.GetPlayerID(), "explorer", "sea_route", _oceanAttemptScope, "set", Mathf.RoundToInt(_oceanDistance), "sea-progress:" + _oceanAttemptScope + ":" + num6, 1000); } if (_oceanDistance >= 1000f && HorizontalDistance(_oceanStart, position) >= 700f) { Goal(plugin, player, "explorer", "sea_route", "global"); } goto IL_06f5; } } else { num5 = 0; } if (_wasOcean) { if (_oceanDistance >= 750f && _oceanSeconds >= 90f && !KnownLandings.Any((Vector3 x) => HorizontalDistance(x, position) < 600f)) { KnownLandings.Add(position); Goal(plugin, player, "explorer", "new_island_discovered", "global"); } _previousLandBiome = text; _oceanSeconds = 0f; } goto IL_06f5; IL_06f5: _wasOcean = (byte)num5 != 0; _lastPosition = position; _lastTickAt = realtimeSinceStartup; } internal static void Observe(Plugin plugin, string eventType, Player player, Dictionary data) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) if (_emitting || (Object)(object)plugin == (Object)null || (Object)(object)player == (Object)null || string.IsNullOrWhiteSpace(eventType)) { return; } try { string text = Key(eventType); string text2 = Value(data, "biome", Plugin.CurrentBiome(((Component)player).transform.position)); string text3 = Value(data, "item", Value(data, "piece", Value(data, "bossIdentity", Value(data, "targetPrefab", Value(data, "grownPrefabName", Value(data, "animalPrefab", Value(data, "signal", Value(data, "simulationType", "unknown")))))))); if (text == "death") { ResetSession(player.GetPlayerID(), ((Component)player).transform.position, text2, Time.realtimeSinceStartup); } if (text == "creature_kill" && Dangerous(Key(text2))) { _dangerKills++; } Inc(text); Inc(text + ":" + text2); Distinct.Add(text + ":" + text3); Fighter(plugin, player, text, text2, text3, data); Farmer(plugin, player, text, text2, text3, data); Builder(plugin, player, text, text2, text3); Explorer(plugin, player, text, text2, text3, data); Collector(plugin, player, text, text2, text3); BuilderCollectorGoalFeature.Observe(text, player, text3, text2, data); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Goal-Automation fehlgeschlagen: " + ex.Message)); } } } private static void Fighter(Plugin p, Player player, string type, string biome, string item, Dictionary data) { if (type == "creature_kill") { CombatGoalFeature.NotifyDangerKill(player, biome); } if (type == "portal_violation") { CombatGoalFeature.NotifyPortalViolation(biome); } } private static void Farmer(Plugin p, Player player, string type, string biome, string item, Dictionary data) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) Piece val = FindSupplyGuardian(EventPosition(data, ((Component)player).transform.position)); if ((Object)(object)val == (Object)null) { return; } long num = GuardianOwnerId(val); if (num == 0L) { return; } Player val2 = FindPlayer(num) ?? player; player = val2; string zone = SupplyZoneScope(val); if (!(type == "craft_completed")) { return; } if (Food(item)) { Inc("food_crafts:" + zone); Distinct.Add("foodrecipe:" + zone + ":" + item); Progress(p, val2, num, "farmer", "food_chain_upgrade", item, "confirm", 1, "food:" + zone + ":" + item); if (Distinct.Count((string x) => x.StartsWith("foodrecipe:" + zone + ":")) >= 5) { Goal(p, val2, num, "farmer", "food_chain_upgrade", "global"); } } if (Food(item)) { string text = FoodTier(item); Distinct.Add("foodtier:" + zone + ":" + text); Progress(p, val2, num, "farmer", "biome_food_tier", text, "confirm", 1, "foodtier:" + num + ":" + text + ":" + item); if (Distinct.Count((string x) => x.StartsWith("foodtier:" + zone + ":")) >= 3) { Goal(p, val2, num, "farmer", "biome_food_tier", "global"); } } } private static void Builder(Plugin p, Player player, string type, string biome, string item) { } private static void Explorer(Plugin p, Player player, string type, string biome, string item, Dictionary data) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) string text = Value(data, "signal", item); bool flag = text.Contains("vegvisir") || item.Contains("vegvisir"); if ((type == "mod_signal" || type == "exploration_seen") && !flag && (text.Contains("altar") || text.Contains("offeringbowl") || item.Contains("offeringbowl"))) { _lastAltarScope = BossScope(item + ":" + text); _lastAltarPosition = ((Component)player).transform.position; if (_lastAltarScope != "unknown_boss") { Goal(p, player, "explorer", "boss_altar_found", _lastAltarScope); } } if ((type == "mod_signal" || type == "exploration_seen") && flag) { string text2 = BossScope(item + ":" + text); if (text2 != "unknown_boss") { Goal(p, player, "explorer", "boss_vegvisir_found", text2); } } if (type == "trader_found" || text.Contains("trader")) { Goal(p, player, "explorer", "trader_found", TraderScope(item + ":" + text)); } if (type == "resource_site_registered") { Goal(p, player, "explorer", "resource_location", LocationScope(player, item)); } if (type == "build_completed" && (item.Contains("sign") || item.Contains("path") || item.Contains("torch"))) { string key = (Dangerous(biome) ? biome : "general"); if (!RouteMarkers.TryGetValue(key, out var value)) { value = (RouteMarkers[key] = new List()); } Vector3 position = ((Component)player).transform.position; if (!value.Any((Vector3 marker) => HorizontalDistance(marker, position) < 12f)) { value.Add(position); } float num = ConnectedLength(value, 80f); if (biome.Contains("mist") && value.Count >= 6 && num >= 250f) { Goal(p, player, "explorer", "mistlands_route", "global"); } if (!string.IsNullOrWhiteSpace(_lastAltarScope) && value.Count >= 6 && num >= 300f && RouteReachesKnownAltar(value, 80f) && AnyGuardianNear(value, 80f)) { Goal(p, player, "explorer", "safe_boss_path", "global"); } } } private static void Collector(Plugin p, Player player, string type, string biome, string item) { } private static void Goal(Plugin plugin, Player player, string style, string goal, string scope) { Goal(plugin, player, ((Object)(object)player != (Object)null) ? player.GetPlayerID() : 0, style, goal, scope); } private static void Goal(Plugin plugin, Player reporting, long creditedPlayerId, string style, string goal, string scope) { //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) if (!ScoringEmissionAllowed() || (Object)(object)reporting == (Object)null || creditedPlayerId == 0L) { return; } string item = creditedPlayerId + ":" + goal + ":" + (scope ?? "global"); if (!Sent.Add(item)) { return; } _emitting = true; try { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("ChallengeHub Goal erkannt: " + style + "/" + goal + "; Scope=" + (scope ?? "global"))); } plugin.SendEvent("playstyle_goal", reporting, new Dictionary { { "playstyle", style }, { "goal", goal }, { "scope", scope ?? "global" }, { "creditedPlayerId", creditedPlayerId.ToString() }, { "attributionMethod", "goal_automation_2_9_27" }, { "biome", Plugin.CurrentBiome(((Component)reporting).transform.position) }, { "position", Plugin.SerializeVector(((Component)reporting).transform.position) } }); } finally { _emitting = false; } } private static void Progress(Plugin plugin, Player reporting, long creditedPlayerId, string style, string goal, string scope, string operation, int value, string evidenceId, int target = 0) { if (!((Object)(object)plugin == (Object)null) && !((Object)(object)reporting == (Object)null) && creditedPlayerId != 0L && ScoringEmissionAllowed()) { Dictionary dictionary = new Dictionary { { "playstyle", style }, { "goal", goal }, { "scope", scope ?? "global" }, { "operation", operation }, { "value", value }, { "evidenceId", evidenceId }, { "creditedPlayerId", creditedPlayerId.ToString() }, { "attributionMethod", "goal_progress_best_attempt_2_11_3" } }; if (target > 0) { dictionary["target"] = target; } plugin.SendEvent("goal_progress", reporting, dictionary); } } private static void ReportBestProgress(Plugin plugin, Player player, string style, string goal, string scope, int value, int bucketSize, int target) { if (!((Object)(object)plugin == (Object)null) && !((Object)(object)player == (Object)null) && !string.IsNullOrWhiteSpace(scope) && value > 0) { int num = Mathf.FloorToInt((float)(value / Mathf.Max(1, bucketSize))); string text = goal + ":" + scope; if (!ProgressBuckets.TryGetValue(text, out var value2) || num > value2) { ProgressBuckets[text] = num; Progress(plugin, player, player.GetPlayerID(), style, goal, scope, "set", value, "best-progress:" + text + ":" + num, target); } } } private static void Inc(string key) { Counts[key] = Count(key) + 1; } private static bool ScoringEmissionAllowed() { if (!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer()) { return CharacterAdmissionFeature.ChallengeScoringAllowed; } return true; } private static void IncBy(string key, int amount) { Counts[key] = Count(key) + Math.Max(1, amount); } private static int Count(string key) { if (!Counts.TryGetValue(key, out var value)) { return 0; } return value; } private static int Amount(Dictionary data) { if (data == null) { return 1; } string[] array = new string[4] { "amount", "quantity", "stack", "count" }; foreach (string key in array) { if (data.TryGetValue(key, out var value) && int.TryParse(Convert.ToString(value), out var result)) { return Math.Max(1, result); } } return 1; } private static string Value(Dictionary data, string key, string fallback) { if (data == null || !data.TryGetValue(key, out var value)) { return Key(fallback); } return Key(Convert.ToString(value)); } private static string Key(string value) { return Plugin.NormalizeKey(value ?? string.Empty); } private static bool Dangerous(string b) { if (!b.Contains("swamp") && !b.Contains("mountain") && !b.Contains("plain") && !b.Contains("mist")) { return b.Contains("ash"); } return true; } private static bool Food(string s) { return KnownFoods.Contains(Key(s)); } private static bool PlantItem(string s) { if (!s.Contains("carrot") && !s.Contains("turnip") && !s.Contains("onion") && !s.Contains("barley") && !s.Contains("flax") && !s.Contains("seed")) { return s.Contains("sapling"); } return true; } private static bool Mead(string s) { if (!s.Contains("mead") && !s.Contains("potion")) { return s.Contains("wine"); } return true; } private static bool Metal(string s) { if (!s.Contains("bronze") && !s.Contains("iron") && !s.Contains("silver") && !s.Contains("blackmetal")) { return s.Contains("flametal"); } return true; } private static bool Gear(string s) { if (!s.Contains("sword") && !s.Contains("axe") && !s.Contains("bow") && !s.Contains("helmet") && !s.Contains("armor") && !s.Contains("shield")) { return s.Contains("pickaxe"); } return true; } private static bool Rare(string s) { if (!s.Contains("trophy") && !s.Contains("core") && !s.Contains("egg") && !s.Contains("flametal") && !s.Contains("blackmetal")) { return s.Contains("silver"); } return true; } private static bool BossObject(string s) { if (!s.Contains("altar") && !s.Contains("bosstone") && !s.Contains("eikthyr") && !s.Contains("gdking") && !s.Contains("bonemass") && !s.Contains("goblinking") && !s.Contains("dragonqueen")) { return s.Contains("fader"); } return true; } private static string LocationScope(Player p, string item) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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) //IL_0049: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)p).transform.position; return Key(item) + ":" + Mathf.RoundToInt(position.x / 20f) + ":" + Mathf.RoundToInt(position.z / 20f); } private static Piece FindSupplyGuardian(Vector3 position) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) Piece result = null; float num = float.MaxValue; Piece[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Piece val in array) { string text = GuardianStoneProtectionFeature.DetectGuardianType(((Component)val).gameObject); if (!(text != "farmer") || !(text != "neutral")) { float num2 = HorizontalDistance(position, ((Component)val).transform.position); if (num2 <= GuardianZoneAccessFeature.Radius(val) && num2 < num) { result = val; num = num2; } } } return result; } private static Player FindPlayer(long id) { return ((IEnumerable)Player.GetAllPlayers()).FirstOrDefault((Func)((Player p) => (Object)(object)p != (Object)null && p.GetPlayerID() == id)); } private static long GuardianOwnerId(Piece piece) { if ((Object)(object)piece == (Object)null) { return 0L; } try { long result = piece.GetCreator(); if (result != 0L) { return result; } ZNetView component = ((Component)piece).GetComponent(); object s; if (component == null) { s = null; } else { ZDO zDO = component.GetZDO(); s = ((zDO != null) ? zDO.GetString("ChallengeHub.Guardian.OwnerId", "0") : null); } long.TryParse((string?)s, out result); return result; } catch { return 0L; } } private static string SupplyZoneScope(Piece piece) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) return GuardianOwnerId(piece) + ":" + GridScope(((Component)piece).transform.position, 10f); } private static bool IsTamedAnimal(Component component) { try { Component val = component.GetComponent("Tameable") ?? component.GetComponentInParent(AccessTools.TypeByName("Tameable")) ?? component.GetComponentInChildren(AccessTools.TypeByName("Tameable"), true); if ((Object)(object)val == (Object)null) { return false; } MethodInfo methodInfo = AccessTools.Method(((object)val).GetType(), "IsTamed", (Type[])null, (Type[])null); return methodInfo != null && Convert.ToBoolean(methodInfo.Invoke(val, null)); } catch { return false; } } private static string SupplySpecies(string value) { string text = Key(value); string[] array = new string[4] { "(clone)", "_clone", "_juvenile", "_baby" }; foreach (string oldValue in array) { text = text.Replace(oldValue, ""); } return text; } private static string FoodTier(string item) { item = Key(item); if (new string[4] { "piquantpie", "mashedmeat", "scorchingmedley", "roastedcrustpie" }.Contains(item)) { return "ashlands"; } if (new string[5] { "honeyglazedchicken", "meatplatter", "mushroomomelette", "mistharesupreme", "yggdrasilporridge" }.Contains(item)) { return "mistlands"; } if (new string[5] { "bread", "loxpie", "fishwraps", "bloodpudding", "salad" }.Contains(item)) { return "plains"; } if (new string[4] { "onionsoup", "wolfjerky", "wolfmeatskewer", "eyescream" }.Contains(item)) { return "mountain"; } if (new string[3] { "sausages", "turnipstew", "blacksoup" }.Contains(item)) { return "swamp"; } if (new string[2] { "carrotsoup", "queensjam" }.Contains(item)) { return "blackforest"; } return "meadows"; } private static void TrackExpeditionCategory(string zone, string item, int amount) { string text = (Food(item) ? "food" : ((Mead(item) && !item.Contains("base")) ? "mead" : (Gear(item) ? "gear" : (BuildingMaterial(item) ? "material" : "")))); if (!string.IsNullOrEmpty(text)) { IncBy("expedition:" + zone + ":" + text, amount); } } private static bool ExpeditionReady(string zone) { if (Count("expedition:" + zone + ":food") >= 3 && Count("expedition:" + zone + ":mead") >= 2 && Count("expedition:" + zone + ":gear") >= 1) { return Count("expedition:" + zone + ":material") >= 10; } return false; } private static bool BuildingMaterial(string s) { if (!(s == "wood") && !(s == "stone") && !s.Contains("finewood") && !s.Contains("corewood") && !s.Contains("marble") && !s.Contains("grausten")) { return s.Contains("ironwood"); } return true; } private static Vector3 EventPosition(Dictionary data, Vector3 fallback) { //IL_005d: 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_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) try { if (data == null || !data.TryGetValue("position", out var value)) { return fallback; } if (!(value is IDictionary dictionary)) { return fallback; } return new Vector3(Convert.ToSingle(dictionary["x"]), Convert.ToSingle(dictionary["y"]), Convert.ToSingle(dictionary["z"])); } catch { return fallback; } } private static float HorizontalDistance(Vector3 a, Vector3 b) { //IL_0000: 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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) float num = a.x - b.x; float num2 = a.z - b.z; return Mathf.Sqrt(num * num + num2 * num2); } private static Ship NearestShip(Vector3 position, float radius) { //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) return (from x in Object.FindObjectsByType((FindObjectsSortMode)0) where (Object)(object)x != (Object)null && HorizontalDistance(((Component)x).transform.position, position) <= radius orderby HorizontalDistance(((Component)x).transform.position, position) select x).FirstOrDefault(); } private static Ship StandingShip(Player player) { try { MethodInfo methodInfo = AccessTools.Method(((object)player).GetType(), "GetStandingOnShip", (Type[])null, (Type[])null); return (Ship)((methodInfo != null) ? /*isinst with value type is only supported in some contexts*/: null); } catch { return null; } } private static string PairKey(string a, string b) { string text = Key(a); string text2 = Key(b); if (string.CompareOrdinal(text, text2) > 0) { return text2 + "|" + text; } return text + "|" + text2; } private static float ConnectedLength(List points, float maxGap) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (points == null || points.Count < 2) { return 0f; } float num = 0f; for (int i = 0; i < points.Count; i++) { HashSet hashSet = new HashSet(); Queue queue = new Queue(); hashSet.Add(i); queue.Enqueue(i); float num2 = 0f; while (queue.Count > 0) { int index = queue.Dequeue(); for (int j = 0; j < points.Count; j++) { if (!hashSet.Contains(j)) { float num3 = HorizontalDistance(points[index], points[j]); if (num3 <= maxGap) { hashSet.Add(j); queue.Enqueue(j); num2 += num3; } } } } num = Mathf.Max(num, num2); } return num; } private static bool AnyGuardianNear(List points, float radius) { if (points == null) { return false; } return Object.FindObjectsByType((FindObjectsSortMode)0).Any((Piece piece) => (Object)(object)piece != (Object)null && !string.IsNullOrWhiteSpace(GuardianStoneProtectionFeature.DetectGuardianType(((Component)piece).gameObject)) && points.Any((Vector3 point) => HorizontalDistance(point, ((Component)piece).transform.position) <= radius)); } private static void RescanRouteMarkers(Player player, string biome) { //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return; } string text = (Dangerous(biome) ? biome : "general"); if (!RouteMarkers.TryGetValue(text, out var value)) { value = (RouteMarkers[text] = new List()); } Piece[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Piece piece in array) { if (!((Object)(object)piece == (Object)null) && piece.GetCreator() == player.GetPlayerID()) { string text2 = Key(((Object)piece).name); if ((text2.Contains("sign") || text2.Contains("standingtorch") || text2.Contains("woodtorch") || text2.Contains("woodpole") || text2.Contains("path") || text2.Contains("pavedroad")) && !(HorizontalDistance(((Component)piece).transform.position, ((Component)player).transform.position) > 450f) && (!Dangerous(biome) || Key(Plugin.CurrentBiome(((Component)piece).transform.position)).Equals(text, StringComparison.OrdinalIgnoreCase)) && !value.Any((Vector3 x) => HorizontalDistance(x, ((Component)piece).transform.position) < 8f)) { value.Add(((Component)piece).transform.position); } } } } private static bool RouteReachesKnownAltar(List points, float radius) { if (!string.IsNullOrWhiteSpace(_lastAltarScope) && points != null) { return points.Any((Vector3 position) => HorizontalDistance(position, _lastAltarPosition) <= radius); } return false; } private static string BossScope(string value) { string text = Key(value); if (text.Contains("eikthyr")) { return "eikthyr"; } if (text.Contains("gdking") || text.Contains("elder")) { return "elder"; } if (text.Contains("bonemass")) { return "bonemass"; } if (text.Contains("dragonqueen") || text.Contains("moder")) { return "moder"; } if (text.Contains("goblinking") || text.Contains("yagluth")) { return "yagluth"; } if (text.Contains("queen")) { return "queen"; } if (text.Contains("fader")) { return "fader"; } return "unknown_boss"; } private static string TraderScope(string value) { string text = Key(value); if (text.Contains("hildir")) { return "hildir"; } if (text.Contains("bogwitch") || text.Contains("witch")) { return "bogwitch"; } return "haldor"; } private static string GridScope(Vector3 v, float size) { //IL_0000: 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) return Mathf.RoundToInt(v.x / size) + ":" + Mathf.RoundToInt(v.z / size); } private static string PairScope(string a, string b, Vector3 v) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) return Key(a) + "-" + Key(b) + ":" + GridScope(v, 100f); } private static void ResetSession(long id, Vector3 position, string biome, float now) { //IL_0006: 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_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) _playerId = id; _lastPosition = position; _dangerStart = position; _oceanStart = position; _lastTickAt = now; _biomeEnteredAt = now; _borderWindowStartedAt = now; _dangerSeconds = 0f; _dangerDistance = 0f; _dangerKills = 0; _oceanDistance = 0f; _oceanSeconds = 0f; _currentBiomeDistance = 0f; _currentBiome = biome; _previousLandBiome = (biome.Contains("ocean") ? "" : biome); _wasOcean = false; if (!string.IsNullOrWhiteSpace(biome) && !biome.Contains("ocean")) { VisitedBiomes.Add(biome); } } } internal sealed class GoalProgressFeature : MonoBehaviour { [DataContract] private sealed class GoalProgressEntry { [DataMember] public string goal; [DataMember] public string scope; [DataMember] public float value; [DataMember] public float best; [DataMember] public float target; [DataMember] public string updatedAt; } [DataContract] private sealed class BossProgressEntry { [DataMember] public string key; [DataMember] public bool completed; [DataMember] public int deaths; [DataMember] public string completedAt; } [DataContract] private sealed class BiomeProgressEntry { [DataMember] public string key; [DataMember] public bool visited; [DataMember] public bool completed; [DataMember] public int found; [DataMember] public int required; [DataMember] public string[] trophies; [DataMember] public string[] requiredTrophies; [DataMember] public int deaths; [DataMember] public string completedAt; } [DataContract] private sealed class EventAck { [DataMember] public string eventId; [DataMember] public string eventType; [DataMember] public string status; [DataMember] public bool duplicate; [DataMember] public float scoreBefore; [DataMember] public float scoreAfter; [DataMember] public float scoreDelta; [DataMember] public string[] newlyCompletedGoals; [DataMember] public string receivedAt; } [DataContract] private sealed class CommunityActionResponse { [DataMember] public bool ok; [DataMember] public string error; } [DataContract] private sealed class PointAward { [DataMember] public string id; [DataMember] public int points; [DataMember] public string reason; [DataMember] public string receivedAt; } [DataContract] private sealed class CampaignSystemEntry { [DataMember] public string key; [DataMember] public string label; [DataMember] public int points; [DataMember] public float target; [DataMember] public string unit; [DataMember] public string description; [DataMember] public string[] requirements; [DataMember] public string detection; [DataMember] public int completionBonus; [DataMember] public string repeat; } [DataContract] private sealed class CampaignProgressEntry { [DataMember] public string system; [DataMember] public string scope; [DataMember] public float value; [DataMember] public float best; [DataMember] public float target; [DataMember] public string updatedAt; } [DataContract] private sealed class CampaignCompletionEntry { [DataMember] public string system; [DataMember] public string scope; [DataMember] public string label; [DataMember] public int points; [DataMember] public string completedAt; } [DataContract] private sealed class CampaignTitleEntry { [DataMember] public string key; [DataMember] public string label; [DataMember] public string unlockedAt; } [DataContract] private sealed class ScheduledTaskEntry { [DataMember] public string key; [DataMember] public string label; [DataMember] public float target; [DataMember] public string scope; [DataMember] public int points; } [DataContract] private sealed class WeeklyScheduleEntry { [DataMember] public string weekKey; [DataMember] public string startAt; [DataMember] public string endAt; [DataMember] public ScheduledTaskEntry[] tasks; } [DataContract] private sealed class WorldEventEffectsEntry { [DataMember] public string forcedEnvironment; [DataMember] public bool nightOnly; [DataMember] public bool coldEverywhere; [DataMember] public bool wetEverywhere; [DataMember] public bool sleepBlocked; [DataMember] public float cropGrowthMultiplier = 1f; [DataMember] public float pickableRespawnMultiplier = 1f; [DataMember] public float enemySpawnMultiplier = 1f; [DataMember] public float enemyHealthMultiplier = 1f; [DataMember] public float enemyDamageMultiplier = 1f; [DataMember] public float enemyDropMultiplier = 1f; [DataMember] public float seaCreatureHealthMultiplier = 1f; [DataMember] public float oceanSpawnMultiplier = 1f; [DataMember] public float shipSpeedMultiplier = 1f; [DataMember] public float movementMultiplier = 1f; [DataMember] public float fireDamageMultiplier = 1f; [DataMember] public float staminaRegenMultiplier = 1f; [DataMember] public float skillGainMultiplier = 1f; [DataMember] public bool lightningStrikes; [DataMember] public float lightningIntervalSeconds = 90f; [DataMember] public float lightningDamage = 40f; [DataMember] public float lightningSafeRadius = 12f; [DataMember] public bool hiddenLocation; [DataMember] public float clueIntervalMinutes = 20f; [DataMember] public float discoveryRadius = 20f; [DataMember] public float discoveryPoints = 80f; [DataMember] public bool regional; [DataMember] public float undeadSpawnMultiplier = 1f; [DataMember] public float undeadHealthMultiplier = 1f; [DataMember] public float coastalFloodVisual; [DataMember] public float visibilityMultiplier = 1f; [DataMember] public float craftDurabilityBonus; } [DataContract] private sealed class WorldEventEntry { [DataMember] public string id; [DataMember] public string eventKey; [DataMember] public string label; [DataMember] public string startAt; [DataMember] public string endAt; [DataMember] public int points; [DataMember] public string description; [DataMember] public string[] effectLines; [DataMember] public WorldEventEffectsEntry effects; [DataMember] public ScheduledTaskEntry[] tasks; } [DataContract] private sealed class CommunitySystemDefinition { [DataMember] public string key; [DataMember] public string label; [DataMember] public string kind; [DataMember] public string description; [DataMember] public string verification; [DataMember] public string authority; [DataMember] public string persistence; [DataMember] public int order; [DataMember] public bool independent; [DataMember] public string[] views; } [DataContract] private sealed class CommunityListEntry { [DataMember] public string key; [DataMember] public string label; [DataMember] public string description; [DataMember] public float target; [DataMember] public string unit; } [DataContract] private sealed class CommunityItemTarget { [DataMember] public string item; [DataMember] public float target; [DataMember] public float delivered; [DataMember] public float used; [DataMember] public float remaining; } [DataContract] private sealed class CommunityRecentEntry { [DataMember] public string id; [DataMember] public string type; [DataMember] public string label; [DataMember] public string text; [DataMember] public float delta; [DataMember] public string phase; [DataMember] public string item; [DataMember] public string prefab; [DataMember] public string postId; [DataMember] public string createdAt; } [DataContract] private sealed class CommunitySystemInstance { [DataMember] public string id; [DataMember] public string systemKey; [DataMember] public string title; [DataMember] public string description; [DataMember] public string status; [DataMember] public string startsAt; [DataMember] public string endsAt; [DataMember] public float target; [DataMember] public string unit; [DataMember] public float current; [DataMember] public float personal; [DataMember] public int participantCount; [DataMember] public int entryCount; [DataMember] public string destination; [DataMember] public string meetingPoint; [DataMember] public string biome; [DataMember] public string leaderUserId; [DataMember] public string metric; [DataMember] public string votingMode; [DataMember] public string[] equipment; [DataMember] public string[] allowedPrefabs; [DataMember] public string[] containerZdos; [DataMember] public string[] criteria; [DataMember] public string[] options; [DataMember] public string[] rules; [DataMember] public string[] completedPhases; [DataMember] public CommunityListEntry[] phases; [DataMember] public CommunityListEntry[] tasks; [DataMember] public CommunityItemTarget[] itemTargets; [DataMember] public CommunityRecentEntry[] recentEntries; } [DataContract] private sealed class WorldStationEntry { [DataMember] public string stationId; [DataMember] public string stationType; [DataMember] public string systemKey; [DataMember] public string label; [DataMember] public string ownerPlayerId; [DataMember] public string worldId; [DataMember] public int revision; [DataMember] public string createdAt; [DataMember] public string lastSeenAt; [DataMember] public string[] actions; } [DataContract] private sealed class WorldPlanProgressEntry { [DataMember] public string scope; [DataMember] public float value; [DataMember] public float best; [DataMember] public float target; [DataMember] public string updatedAt; } [DataContract] private sealed class WorldPlanVoteEntry { [DataMember] public string option; [DataMember] public int count; } [DataContract] private sealed class WorldPlanEntry { [DataMember] public string id; [DataMember] public string worldId; [DataMember] public string stationId; [DataMember] public string systemKey; [DataMember] public string status; [DataMember] public int revision; [DataMember] public string title; [DataMember] public string description; [DataMember] public string createdByPlayerId; [DataMember] public string[] participantPlayerIds; [DataMember] public WorldPlanProgressEntry[] progress; [DataMember] public float target; [DataMember] public string[] options; [DataMember] public string[] containerZdos; [DataMember] public WorldPlanVoteEntry[] votes; [DataMember] public string decision; [DataMember] public string createdAt; [DataMember] public string updatedAt; } [DataContract] private sealed class WorldViewRecentEntry { [DataMember] public string id; [DataMember] public string type; [DataMember] public string label; [DataMember] public string text; [DataMember] public float value; [DataMember] public string actorPlayerId; [DataMember] public string observedAt; } [DataContract] private sealed class WorldViewBreakdownEntry { [DataMember] public string label; [DataMember] public float value; } [DataContract] private sealed class WorldSystemViewEntry { [DataMember] public string key; [DataMember] public string label; [DataMember] public string mode; [DataMember] public string trigger; [DataMember] public int eventCount; [DataMember] public float total; [DataMember] public float best; [DataMember] public int actorCount; [DataMember] public WorldViewBreakdownEntry[] breakdown; [DataMember] public string lastActivityAt; [DataMember] public WorldViewRecentEntry[] recent; } [DataContract] private sealed class PartnershipEntry { [DataMember] public string id; [DataMember] public string type; [DataMember] public string biome; [DataMember] public string status; [DataMember] public string userAName; [DataMember] public string userBName; [DataMember] public string structureName; [DataMember] public object structurePosition; [DataMember] public string acceptedAt; [DataMember] public string updatedAt; } [DataContract] private sealed class CompatibilityInfo { [DataMember] public int apiProtocolVersion; [DataMember] public int minimumProtocolVersion; [DataMember] public int goalDetectorVersion; [DataMember] public string clientVersion; [DataMember] public int protocolVersion; [DataMember] public int detectorVersion; [DataMember] public bool compatible; [DataMember] public string[] warnings; } [DataContract] private sealed class GoalResponse { [DataMember] public bool ok; [DataMember] public bool duplicate; [DataMember] public string difficulty; [DataMember] public string[] completedGoals; [DataMember] public GoalProgressEntry[] goalProgress; [DataMember] public PlaystyleWebConfig[] playstyles; [DataMember] public BossProgressEntry[] bossProgress; [DataMember] public BiomeProgressEntry[] biomeProgress; [DataMember] public PointAward[] recentPointAwards; [DataMember] public Dictionary skillCurrent; [DataMember] public Dictionary skillGains; [DataMember] public Dictionary skillAreaGains; [DataMember] public Dictionary skillAreaSteps; [DataMember] public bool newCharacterValid; [DataMember] public PartnershipEntry[] partnerships; [DataMember] public CampaignSystemEntry[] campaignSystems; [DataMember] public CampaignProgressEntry[] campaignProgress; [DataMember] public CampaignCompletionEntry[] campaignCompletions; [DataMember] public CampaignTitleEntry[] campaignTitles; [DataMember] public WeeklyScheduleEntry weeklySchedule; [DataMember] public WorldEventEntry[] worldEvents; [DataMember] public CommunitySystemDefinition[] communitySystemDefinitions; [DataMember] public CommunitySystemInstance[] communitySystemInstances; [DataMember] public WorldStationEntry[] worldStations; [DataMember] public WorldPlanEntry[] worldPlans; [DataMember] public WorldSystemViewEntry[] worldSystemViews; [DataMember] public EventAck eventAck; [DataMember] public CompatibilityInfo compatibility; } private sealed class PartnershipView { internal string Type; internal string Biome; internal string Status; internal string A; internal string B; internal string Id; internal string Structure; internal string AcceptedAt; internal bool Global; } private enum HubTab { Overview, Introduction, Bosses, Biomes, Goals, Campaign, SkillProgress, Skilltree, Partnerships, Diagnostics } private sealed class ReportTarget { internal string Id; internal string Label; } private static readonly string[] TabLabels = new string[10] { "Übersicht", "Einführung", "Bosse", "Biome", "Ziele", "Welt & Gemeinschaft", "Skill-Fortschritt", "QoL-Talente", "Partnerschaften", "Diagnose" }; private static readonly string[] CommunityAreaLabels = new string[6] { "Aktuell", "Abenteuer", "Gemeinschaft", "Entdeckungen", "Mein Wikinger", "Archiv" }; private static readonly string[][] CommunityAreaSystems = new string[6][] { new string[0], new string[4] { "weekly_board", "world_events", "expeditions", "hunt_board" }, new string[5] { "community_projects", "delivery_projects", "community_council", "rivalries", "build_exhibitions" }, new string[3] { "rumors", "trade_network", "hall_of_glory" }, new string[3] { "chronicle", "roles", "titles" }, new string[3] { "world_chronicle", "photo_reports", "season_review" } }; private static GoalProgressFeature _instance; private static Plugin _plugin; private static readonly HashSet Completed = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> Progress = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static PlaystyleWebConfig[] _serverStyles = new PlaystyleWebConfig[0]; private static BossProgressEntry[] _bossProgress = new BossProgressEntry[0]; private static BiomeProgressEntry[] _biomeProgress = new BiomeProgressEntry[0]; private static string _difficulty = "medium"; private static EventAck _lastAck; private static readonly List RecentPointAwards = new List(); private static Dictionary _skillCurrent = new Dictionary(StringComparer.OrdinalIgnoreCase); private static Dictionary _skillGains = new Dictionary(StringComparer.OrdinalIgnoreCase); private static Dictionary _skillAreaGains = new Dictionary(StringComparer.OrdinalIgnoreCase); private static Dictionary _skillAreaSteps = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary SkillAreaTextures = new Dictionary(StringComparer.OrdinalIgnoreCase); private static bool _newCharacterValid = true; private static PartnershipEntry[] _partnerships = new PartnershipEntry[0]; private static CompatibilityInfo _compatibility; private static CampaignSystemEntry[] _campaignSystems = new CampaignSystemEntry[0]; private static CampaignProgressEntry[] _campaignProgress = new CampaignProgressEntry[0]; private static CampaignCompletionEntry[] _campaignCompletions = new CampaignCompletionEntry[0]; private static CampaignTitleEntry[] _campaignTitles = new CampaignTitleEntry[0]; private static WeeklyScheduleEntry _weeklySchedule; private static WorldEventEntry[] _worldEvents = new WorldEventEntry[0]; private static CommunitySystemDefinition[] _communityDefinitions = new CommunitySystemDefinition[0]; private static CommunitySystemInstance[] _communityInstances = new CommunitySystemInstance[0]; private static WorldStationEntry[] _worldStations = new WorldStationEntry[0]; private static WorldPlanEntry[] _worldPlans = new WorldPlanEntry[0]; private static WorldSystemViewEntry[] _worldSystemViews = new WorldSystemViewEntry[0]; private static string _lastResponseAt = string.Empty; private static Font _norseFont; private static Font _bodyFont; private static Texture2D _background; private static Texture2D _panel; private static Texture2D _button; private static Texture2D _buttonHover; private static Texture2D _treeLine; private static GUIStyle _windowStyle; private static GUIStyle _buttonStyle; private static GUIStyle _labelStyle; private static GUIStyle _titleStyle; private bool _visible; private Rect _window = new Rect(35f, 35f, 1120f, 720f); private Vector2 _scroll; private Vector2 _communityNavScroll; private Vector2 _communityDetailScroll; private int _selectedCommunitySystem; private string _selectedCommunitySystemKey = string.Empty; private string _selectedCommunityInstanceId = string.Empty; private bool _communityActionBusy; private string _communityActionMessage = string.Empty; private HubTab _tab = HubTab.Goals; private int _selectedStyle; private SkillBranch _selectedBranch = SkillBranch.Orientation; private float _nextExplorationScanAt; private float _nextGoalTickAt; internal static void Initialize(Plugin plugin) { _plugin = plugin; _instance = ((Component)plugin).gameObject.GetComponent() ?? ((Component)plugin).gameObject.AddComponent(); } internal static void OpenCommunityStation(string systemKey) { //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) if ((Object)(object)_instance == (Object)null || string.IsNullOrWhiteSpace(systemKey)) { return; } _instance._tab = HubTab.Campaign; _instance._selectedCommunitySystemKey = systemKey; _instance._selectedCommunityInstanceId = string.Empty; for (int i = 0; i < CommunityAreaSystems.Length; i++) { if (CommunityAreaSystems[i].Contains(systemKey, StringComparer.OrdinalIgnoreCase)) { _instance._selectedCommunitySystem = i; break; } } _instance._communityDetailScroll = Vector2.zero; _instance.SetVisible(value: true); if ((Object)(object)Player.m_localPlayer != (Object)null) { _plugin?.RequestPlayerStatusNow(Player.m_localPlayer); } } private static WorldEventRuntimeDefinition ToRuntimeWorldEvent(WorldEventEntry entry) { if (entry == null) { return null; } if (!DateTimeOffset.TryParse(entry.startAt, out var result) || !DateTimeOffset.TryParse(entry.endAt, out var result2) || result2 <= result) { return null; } WorldEventEffectsEntry worldEventEffectsEntry = entry.effects ?? new WorldEventEffectsEntry(); return new WorldEventRuntimeDefinition { Id = (entry.id ?? entry.eventKey ?? string.Empty), Key = (entry.eventKey ?? string.Empty), Label = (entry.label ?? entry.eventKey ?? "Welt-Ereignis"), StartUtc = result.UtcDateTime, EndUtc = result2.UtcDateTime, Effects = new WorldEventRuntimeEffects { ForcedEnvironment = worldEventEffectsEntry.forcedEnvironment, NightOnly = worldEventEffectsEntry.nightOnly, ColdEverywhere = worldEventEffectsEntry.coldEverywhere, WetEverywhere = worldEventEffectsEntry.wetEverywhere, SleepBlocked = worldEventEffectsEntry.sleepBlocked, CropGrowthMultiplier = ((worldEventEffectsEntry.cropGrowthMultiplier <= 0f) ? 1f : worldEventEffectsEntry.cropGrowthMultiplier), PickableRespawnMultiplier = ((worldEventEffectsEntry.pickableRespawnMultiplier <= 0f) ? 1f : worldEventEffectsEntry.pickableRespawnMultiplier), EnemyHealthMultiplier = ((worldEventEffectsEntry.enemyHealthMultiplier <= 0f) ? 1f : worldEventEffectsEntry.enemyHealthMultiplier), EnemySpawnMultiplier = ((worldEventEffectsEntry.enemySpawnMultiplier <= 0f) ? 1f : worldEventEffectsEntry.enemySpawnMultiplier), EnemyDamageMultiplier = ((worldEventEffectsEntry.enemyDamageMultiplier <= 0f) ? 1f : worldEventEffectsEntry.enemyDamageMultiplier), EnemyDropMultiplier = ((worldEventEffectsEntry.enemyDropMultiplier <= 0f) ? 1f : worldEventEffectsEntry.enemyDropMultiplier), SeaCreatureHealthMultiplier = ((worldEventEffectsEntry.seaCreatureHealthMultiplier <= 0f) ? 1f : worldEventEffectsEntry.seaCreatureHealthMultiplier), OceanSpawnMultiplier = ((worldEventEffectsEntry.oceanSpawnMultiplier <= 0f) ? 1f : worldEventEffectsEntry.oceanSpawnMultiplier), ShipSpeedMultiplier = ((worldEventEffectsEntry.shipSpeedMultiplier <= 0f) ? 1f : worldEventEffectsEntry.shipSpeedMultiplier), MovementMultiplier = ((worldEventEffectsEntry.movementMultiplier <= 0f) ? 1f : worldEventEffectsEntry.movementMultiplier), FireDamageMultiplier = ((worldEventEffectsEntry.fireDamageMultiplier <= 0f) ? 1f : worldEventEffectsEntry.fireDamageMultiplier), StaminaRegenMultiplier = ((worldEventEffectsEntry.staminaRegenMultiplier <= 0f) ? 1f : worldEventEffectsEntry.staminaRegenMultiplier), SkillGainMultiplier = ((worldEventEffectsEntry.skillGainMultiplier <= 0f) ? 1f : worldEventEffectsEntry.skillGainMultiplier), LightningStrikes = worldEventEffectsEntry.lightningStrikes, LightningIntervalSeconds = ((worldEventEffectsEntry.lightningIntervalSeconds <= 0f) ? 90f : worldEventEffectsEntry.lightningIntervalSeconds), LightningDamage = ((worldEventEffectsEntry.lightningDamage <= 0f) ? 40f : worldEventEffectsEntry.lightningDamage), LightningSafeRadius = ((worldEventEffectsEntry.lightningSafeRadius <= 0f) ? 12f : worldEventEffectsEntry.lightningSafeRadius), HiddenLocation = worldEventEffectsEntry.hiddenLocation, ClueIntervalMinutes = ((worldEventEffectsEntry.clueIntervalMinutes <= 0f) ? 20f : worldEventEffectsEntry.clueIntervalMinutes), DiscoveryRadius = ((worldEventEffectsEntry.discoveryRadius <= 0f) ? 20f : worldEventEffectsEntry.discoveryRadius), DiscoveryPoints = worldEventEffectsEntry.discoveryPoints, Regional = worldEventEffectsEntry.regional, UndeadSpawnMultiplier = ((worldEventEffectsEntry.undeadSpawnMultiplier <= 0f) ? 1f : worldEventEffectsEntry.undeadSpawnMultiplier), UndeadHealthMultiplier = ((worldEventEffectsEntry.undeadHealthMultiplier <= 0f) ? 1f : worldEventEffectsEntry.undeadHealthMultiplier), CoastalFloodVisual = worldEventEffectsEntry.coastalFloodVisual, VisibilityMultiplier = ((worldEventEffectsEntry.visibilityMultiplier <= 0f) ? 1f : worldEventEffectsEntry.visibilityMultiplier), CraftDurabilityBonus = Math.Max(0f, worldEventEffectsEntry.craftDurabilityBonus) } }; } internal static void ApplyServerResponse(string json) { if (string.IsNullOrWhiteSpace(json)) { return; } try { GoalResponse goalResponse = ChallengeHubJson.Deserialize(json); if (goalResponse == null || !goalResponse.ok) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"F6-Statusantwort ist syntaktisch lesbar, aber ok=false oder leer."); } return; } _lastResponseAt = DateTime.UtcNow.ToString("O"); Dictionary previousBiomeFound = (_biomeProgress ?? new BiomeProgressEntry[0]).Where((BiomeProgressEntry entry) => entry != null && !string.IsNullOrWhiteSpace(entry.key)).GroupBy((BiomeProgressEntry entry) => entry.key, StringComparer.OrdinalIgnoreCase).ToDictionary, string, int>((IGrouping group) => group.Key, (IGrouping group) => group.Max((BiomeProgressEntry entry) => entry.found), StringComparer.OrdinalIgnoreCase); if (goalResponse.eventAck != null) { _lastAck = goalResponse.eventAck; } if (goalResponse.recentPointAwards != null) { RecentPointAwards.Clear(); RecentPointAwards.AddRange(goalResponse.recentPointAwards.Where((PointAward entry) => entry != null).Take(8)); } if (goalResponse.skillCurrent != null) { _skillCurrent = goalResponse.skillCurrent; } if (goalResponse.skillGains != null) { _skillGains = goalResponse.skillGains; } if (goalResponse.skillAreaGains != null) { _skillAreaGains = goalResponse.skillAreaGains; } if (goalResponse.skillAreaSteps != null) { _skillAreaSteps = goalResponse.skillAreaSteps; } _newCharacterValid = goalResponse.newCharacterValid; if (goalResponse.partnerships != null) { _partnerships = goalResponse.partnerships; } if (goalResponse.campaignSystems != null) { _campaignSystems = goalResponse.campaignSystems; } if (goalResponse.campaignProgress != null) { _campaignProgress = goalResponse.campaignProgress; } if (goalResponse.campaignCompletions != null) { _campaignCompletions = goalResponse.campaignCompletions; } if (goalResponse.campaignTitles != null) { _campaignTitles = goalResponse.campaignTitles; } if (goalResponse.weeklySchedule != null) { _weeklySchedule = goalResponse.weeklySchedule; } if (goalResponse.worldEvents != null) { _worldEvents = goalResponse.worldEvents; WorldEventGameplayFeature.ApplySchedule(from entry in goalResponse.worldEvents.Select(ToRuntimeWorldEvent) where entry != null select entry); } if (goalResponse.communitySystemDefinitions != null) { _communityDefinitions = goalResponse.communitySystemDefinitions; } if (goalResponse.communitySystemInstances != null) { _communityInstances = goalResponse.communitySystemInstances; } if (goalResponse.worldStations != null) { _worldStations = goalResponse.worldStations; } if (goalResponse.worldPlans != null) { _worldPlans = goalResponse.worldPlans; } if (goalResponse.worldSystemViews != null) { _worldSystemViews = goalResponse.worldSystemViews; } RememberPointAward(goalResponse.eventAck); if (goalResponse.compatibility != null) { _compatibility = goalResponse.compatibility; } if (goalResponse.completedGoals != null) { foreach (string item in goalResponse.completedGoals.Where((string value2) => !string.IsNullOrWhiteSpace(value2))) { Completed.Add(item); } } Progress.Clear(); GoalProgressEntry[] array = goalResponse.goalProgress ?? new GoalProgressEntry[0]; foreach (GoalProgressEntry goalProgressEntry in array) { if (goalProgressEntry != null && !string.IsNullOrWhiteSpace(goalProgressEntry.goal)) { if (!Progress.TryGetValue(goalProgressEntry.goal, out var value)) { value = (Progress[goalProgressEntry.goal] = new List()); } value.Add(goalProgressEntry); } } if (goalResponse.bossProgress != null) { _bossProgress = goalResponse.bossProgress; } if (goalResponse.biomeProgress != null) { _biomeProgress = MergeBiomeProgress(_biomeProgress, goalResponse.biomeProgress); } if (!string.IsNullOrWhiteSpace(goalResponse.difficulty)) { _difficulty = goalResponse.difficulty; } ApplyRemoteConfig(goalResponse.playstyles); ShowImmediateReward(goalResponse, previousBiomeFound); if (goalResponse.biomeProgress != null) { BiomeProgressEntry biomeProgressEntry = goalResponse.biomeProgress.FirstOrDefault((BiomeProgressEntry entry) => entry != null && CanonicalBiomeKey(entry.key) == "meadows"); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("F6-Status synchronisiert: Biome=" + goalResponse.biomeProgress.Length + ((biomeProgressEntry != null) ? ("; Wiesen=" + biomeProgressEntry.found + "/" + biomeProgressEntry.required) : "; Wiesen fehlen"))); } } } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("F6-Statusantwort konnte nicht gelesen werden: " + ex.Message)); } } } private static BiomeProgressEntry[] MergeBiomeProgress(BiomeProgressEntry[] current, BiomeProgressEntry[] incoming) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); BiomeProgressEntry[] array = current ?? new BiomeProgressEntry[0]; foreach (BiomeProgressEntry biomeProgressEntry in array) { if (biomeProgressEntry != null && !string.IsNullOrWhiteSpace(biomeProgressEntry.key)) { dictionary[CanonicalBiomeKey(biomeProgressEntry.key)] = biomeProgressEntry; } } array = incoming ?? new BiomeProgressEntry[0]; foreach (BiomeProgressEntry biomeProgressEntry2 in array) { if (biomeProgressEntry2 == null || string.IsNullOrWhiteSpace(biomeProgressEntry2.key)) { continue; } string key = CanonicalBiomeKey(biomeProgressEntry2.key); if (!dictionary.TryGetValue(key, out var value)) { dictionary[key] = biomeProgressEntry2; continue; } biomeProgressEntry2.visited = biomeProgressEntry2.visited || value.visited; biomeProgressEntry2.completed = biomeProgressEntry2.completed || value.completed; biomeProgressEntry2.trophies = (from value2 in (value.trophies ?? new string[0]).Concat(biomeProgressEntry2.trophies ?? new string[0]) where !string.IsNullOrWhiteSpace(value2) select value2).Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); if (biomeProgressEntry2.requiredTrophies == null || biomeProgressEntry2.requiredTrophies.Length == 0) { biomeProgressEntry2.requiredTrophies = value.requiredTrophies; } biomeProgressEntry2.found = Math.Max(Math.Max(value.found, biomeProgressEntry2.found), biomeProgressEntry2.trophies.Length); biomeProgressEntry2.required = Math.Max(value.required, biomeProgressEntry2.required); if (string.IsNullOrWhiteSpace(biomeProgressEntry2.completedAt)) { biomeProgressEntry2.completedAt = value.completedAt; } dictionary[key] = biomeProgressEntry2; } return dictionary.Values.ToArray(); } private static void ShowImmediateReward(GoalResponse response, Dictionary previousBiomeFound) { if (response == null || response.eventAck == null || response.eventAck.duplicate) { return; } string text = string.Empty; if (response.eventAck.scoreDelta > 0.001f) { text = "ChallengeHub: +" + Mathf.RoundToInt(response.eventAck.scoreDelta) + " Punkte"; if (response.eventAck.newlyCompletedGoals != null && response.eventAck.newlyCompletedGoals.Length != 0) { text = text + "\nZiel erreicht: " + string.Join(", ", response.eventAck.newlyCompletedGoals); } } else if (string.Equals(response.eventAck.eventType, "trophy_found", StringComparison.OrdinalIgnoreCase)) { int value; BiomeProgressEntry biomeProgressEntry = (response.biomeProgress ?? new BiomeProgressEntry[0]).FirstOrDefault((BiomeProgressEntry entry) => entry != null && entry.found > (previousBiomeFound.TryGetValue(entry.key ?? string.Empty, out value) ? value : 0)); if (biomeProgressEntry != null) { text = "ChallengeHub: Trophäe gewertet\n" + biomeProgressEntry.found + " / " + biomeProgressEntry.required + " Trophäen in diesem Biom"; } } if (string.IsNullOrWhiteSpace(text)) { return; } try { MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false); } } catch { } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)text.Replace("\n", " | ")); } } private static void RememberPointAward(EventAck ack) { if (ack == null || ack.duplicate || ack.scoreDelta <= 0.001f) { return; } string id = (string.IsNullOrWhiteSpace(ack.eventId) ? ((ack.eventType ?? "event") + ":" + (ack.receivedAt ?? string.Empty)) : ack.eventId); if (!RecentPointAwards.Any((PointAward entry) => string.Equals(entry.id, id, StringComparison.OrdinalIgnoreCase))) { string reason = ((ack.newlyCompletedGoals != null && ack.newlyCompletedGoals.Length != 0) ? ("Ziel: " + string.Join(", ", ack.newlyCompletedGoals.Select(ReadableCompletionKey).ToArray())) : ReadableEventType(ack.eventType)); RecentPointAwards.Insert(0, new PointAward { id = id, points = Mathf.RoundToInt(ack.scoreDelta), reason = reason, receivedAt = (ack.receivedAt ?? DateTime.UtcNow.ToString("O")) }); if (RecentPointAwards.Count > 8) { RecentPointAwards.RemoveRange(8, RecentPointAwards.Count - 8); } } } internal static void ApplyRemoteConfig(PlaystyleWebConfig[] playstyles) { if (playstyles != null && playstyles.Length != 0) { _serverStyles = playstyles; } } internal static GoalWebConfig[] GoalOptions() { return (from goal in Styles().SelectMany((PlaystyleWebConfig style) => style.goals ?? new GoalWebConfig[0]) orderby goal.label select goal).ToArray(); } internal static string[] CommunityReportTargetLabels() { return (from entry in CommunityReportTargets() select entry.Label).ToArray(); } internal static string[] CommunityReportTargetIds() { return (from entry in CommunityReportTargets() select entry.Id).ToArray(); } private static ReportTarget[] CommunityReportTargets() { HashSet reportable = new HashSet(new string[13] { "chronicle", "world_chronicle", "world_events", "community_projects", "rumors", "rivalries", "expeditions", "hunt_board", "photo_reports", "build_exhibitions", "delivery_projects", "hall_of_glory", "community_council" }, StringComparer.OrdinalIgnoreCase); return (from plan in _worldPlans where plan != null && reportable.Contains(plan.systemKey) && !string.Equals(plan.status, "archived", StringComparison.OrdinalIgnoreCase) select new ReportTarget { Id = plan.id, Label = (plan.title ?? plan.systemKey) + " [" + CommunityStatus(plan.status) + "]" } into entry orderby entry.Label select entry).ToArray(); } internal static void OpenIntroduction() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_instance == (Object)null)) { _instance._tab = HubTab.Introduction; _instance._scroll = Vector2.zero; _instance.SetVisible(value: true); } } private void Update() { if ((Object)(object)Player.m_localPlayer != (Object)null && Time.realtimeSinceStartup >= _nextExplorationScanAt) { _nextExplorationScanAt = Time.realtimeSinceStartup + 2f; ExplorationEvidencePatch.ScanNearby(Player.m_localPlayer); } if ((Object)(object)Player.m_localPlayer != (Object)null && Time.realtimeSinceStartup >= _nextGoalTickAt) { _nextGoalTickAt = Time.realtimeSinceStartup + 1f; GoalAutomationFeature.Tick(_plugin, Player.m_localPlayer); } if ((Object)(object)Player.m_localPlayer != (Object)null && Input.GetKeyDown((KeyCode)287)) { if ((Object)(object)Player.m_localPlayer != (Object)null && !Styles().Any()) { _plugin?.RequestRemoteConfigNow(); } SetVisible(!_visible); } if (_visible && Input.GetKeyDown((KeyCode)27)) { SetVisible(value: false); } if (_visible && Input.GetKeyDown((KeyCode)113)) { ChangeTab(-1); } if (_visible && Input.GetKeyDown((KeyCode)101)) { ChangeTab(1); } if (_visible && (Object)(object)Player.m_localPlayer == (Object)null) { SetVisible(value: false); } } private void SetVisible(bool value) { if (_visible == value) { return; } _visible = value; if (value) { if ((Object)(object)Player.m_localPlayer != (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"F6 geöffnet: aktueller Spielerstatus wird sofort von ChallengeHub geladen."); } _plugin?.RequestPlayerStatusNow(Player.m_localPlayer); } ChallengeHubCursorController.Acquire("f6-hub"); TalentMenuBehaviour.Close(); } else { ChallengeHubCursorController.Release("f6-hub"); } } private void OnGUI() { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected O, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) if (_visible) { InitStyles(); GUI.skin.window = _windowStyle; GUI.skin.button = _buttonStyle; GUI.skin.label = _labelStyle; ((Rect)(ref _window)).width = Mathf.Min(1120f, (float)Screen.width - 30f); ((Rect)(ref _window)).height = Mathf.Min(720f, (float)Screen.height - 30f); _window = GUI.Window("ChallengeHub_Hub_v2".GetHashCode(), _window, new WindowFunction(DrawWindow), "ChallengeHub - F6"); } } private void DrawWindow(int id) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) if (GUI.Button(new Rect(((Rect)(ref _window)).width - 48f, 25f, 30f, 26f), "X")) { SetVisible(value: false); return; } GUI.Label(new Rect(18f, 31f, 270f, 28f), "Q / E: Bereich wechseln", _titleStyle); DrawTabs(); if (GUI.Button(new Rect(((Rect)(ref _window)).width - 168f, 66f, 150f, 24f), "Jetzt aktualisieren") && (Object)(object)Player.m_localPlayer != (Object)null) { _plugin?.RequestPlayerStatusNow(Player.m_localPlayer); } Rect val = default(Rect); ((Rect)(ref val))..ctor(18f, 94f, ((Rect)(ref _window)).width - 36f, ((Rect)(ref _window)).height - 112f); GUI.DrawTexture(val, (Texture)(object)_background); if (_tab == HubTab.Overview) { DrawOverview(val); } else if (_tab == HubTab.Introduction) { WorldIntroductionFeature.DrawF6(val, ref _scroll); } else if (_tab == HubTab.Bosses) { DrawBosses(val); } else if (_tab == HubTab.Biomes) { DrawBiomes(val); } else if (_tab == HubTab.Goals) { DrawGoals(val); } else if (_tab == HubTab.Campaign) { DrawCampaign(val); } else if (_tab == HubTab.SkillProgress) { DrawSkillProgress(val); } else if (_tab == HubTab.Skilltree) { DrawSkilltree(val); } else if (_tab == HubTab.Partnerships) { DrawPartnerships(val); } else { DrawDiagnostics(val); } GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _window)).width, 24f)); } private void DrawTabs() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Min(150f, (((Rect)(ref _window)).width - 310f) / (float)TabLabels.Length); for (int i = 0; i < TabLabels.Length; i++) { Color backgroundColor = GUI.backgroundColor; GUI.backgroundColor = ((i == (int)_tab) ? new Color(0.95f, 0.58f, 0.08f) : new Color(0.48f, 0.48f, 0.5f)); if (GUI.Button(new Rect(280f + (float)i * (num + 5f), 29f, num, 34f), TabLabels[i])) { SelectTab((HubTab)i); } GUI.backgroundColor = backgroundColor; } } private void ChangeTab(int direction) { int num = TabLabels.Length; SelectTab((HubTab)((int)(_tab + direction + num) % num)); } private void SelectTab(HubTab tab) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) _tab = tab; _scroll = Vector2.zero; } private void DrawOverview(Rect box) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_0300: Unknown result type (might be due to invalid IL or missing references) //IL_0307: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Expected O, but got Unknown //IL_0334: Unknown result type (might be due to invalid IL or missing references) //IL_0366: Unknown result type (might be due to invalid IL or missing references) GoalWebConfig[] array = Styles().SelectMany((PlaystyleWebConfig style) => style.goals ?? new GoalWebConfig[0]).ToArray(); int num = array.Count((GoalWebConfig goal) => IsCompleted(goal.key)); TalentData talents = TalentStore.GetLocalData(); int num2 = ((talents != null) ? SkillTreeDefinitions.All.Count((SkillNodeDefinition node) => talents.HasUnlockedSkill(node.Id)) : 0); List source = PartnershipViews(); GUI.Label(new Rect(((Rect)(ref box)).x + 18f, ((Rect)(ref box)).y + 16f, ((Rect)(ref box)).width - 36f, 34f), "ChallengeHub-Zentrale", _titleStyle); GUI.Label(new Rect(((Rect)(ref box)).x + 22f, ((Rect)(ref box)).y + 62f, ((Rect)(ref box)).width - 44f, 90f), "F6: Menü öffnen oder schließen\nQ / E: vorherige oder nächste Seite\nESC: Menü schließen"); DrawStatCard(new Rect(((Rect)(ref box)).x + 22f, ((Rect)(ref box)).y + 170f, 245f, 105f), "ZIELE", num + " / " + array.Length + " erreicht"); DrawStatCard(new Rect(((Rect)(ref box)).x + 285f, ((Rect)(ref box)).y + 170f, 245f, 105f), "FÄHIGKEITEN", num2 + " Knoten freigeschaltet"); DrawStatCard(new Rect(((Rect)(ref box)).x + 548f, ((Rect)(ref box)).y + 170f, 245f, 105f), "PARTNERSCHAFTEN", source.Count((PartnershipView view) => view.Status == "active") + " aktiv"); GUI.Label(new Rect(((Rect)(ref box)).x + 22f, ((Rect)(ref box)).y + 300f, ((Rect)(ref box)).width - 44f, 28f), "Zuletzt erhaltene Punkte", _titleStyle); float num3 = ((Rect)(ref box)).y + 334f; if (RecentPointAwards.Count == 0) { GUI.Label(new Rect(((Rect)(ref box)).x + 22f, num3, ((Rect)(ref box)).width - 44f, 30f), "In dieser Spielsitzung wurden noch keine neuen Punkte bestätigt."); } foreach (PointAward item in RecentPointAwards.Take(6)) { GUI.Label(new Rect(((Rect)(ref box)).x + 22f, num3, 90f, 25f), "+" + item.points + " P", new GUIStyle(_labelStyle) { richText = true, fontStyle = (FontStyle)1 }); GUI.Label(new Rect(((Rect)(ref box)).x + 112f, num3, ((Rect)(ref box)).width - 300f, 25f), item.reason); GUI.Label(new Rect(((Rect)(ref box)).x + ((Rect)(ref box)).width - 180f, num3, 150f, 25f), ShortTime(item.receivedAt)); num3 += 30f; } } private void DrawDiagnostics(Rect box) { //IL_002c: 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_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_0384: Unknown result type (might be due to invalid IL or missing references) GUI.Label(new Rect(((Rect)(ref box)).x + 18f, ((Rect)(ref box)).y + 14f, ((Rect)(ref box)).width - 36f, 32f), "Zustellung und Systemdiagnose", _titleStyle); float num = ((Rect)(ref box)).y + 58f; DrawStatCard(new Rect(((Rect)(ref box)).x + 22f, num, 245f, 105f), "AUSGANG", ChallengeHubEventOutbox.PendingCount + " ausstehend"); DrawStatCard(new Rect(((Rect)(ref box)).x + 285f, num, 245f, 105f), "QUARANTÄNE", ChallengeHubEventOutbox.QuarantineCount + " Ereignisse"); DrawStatCard(new Rect(((Rect)(ref box)).x + 548f, num, 245f, 105f), "LETZTE API-ANTWORT", string.IsNullOrWhiteSpace(_lastResponseAt) ? "noch keine" : _lastResponseAt); num += 130f; string text = ((_compatibility == null) ? "Kompatibilität: noch keine Serverantwort" : ("Kompatibilität: " + (_compatibility.compatible ? "OK" : "NICHT KOMPATIBEL") + " | API " + _compatibility.apiProtocolVersion + " | Client-Protokoll " + _compatibility.protocolVersion + " | Detector Server/Client " + _compatibility.goalDetectorVersion + "/" + _compatibility.detectorVersion + ((_compatibility.warnings != null && _compatibility.warnings.Length != 0) ? ("\nWarnungen: " + string.Join(", ", _compatibility.warnings)) : ""))); GUI.Label(new Rect(((Rect)(ref box)).x + 22f, num, ((Rect)(ref box)).width - 44f, 70f), text); num += 78f; string text2 = ((_lastAck == null) ? "Letzte Ereignisbestätigung: noch keine" : ("Letzte Ereignisbestätigung: " + _lastAck.eventType + " / " + _lastAck.status + (_lastAck.duplicate ? " (bereits verarbeitet)" : "") + "\nPunkte: " + _lastAck.scoreBefore + " -> " + _lastAck.scoreAfter + " (Delta " + _lastAck.scoreDelta + ")" + ((_lastAck.newlyCompletedGoals != null && _lastAck.newlyCompletedGoals.Length != 0) ? ("\nNeue Ziele: " + string.Join(", ", _lastAck.newlyCompletedGoals)) : ""))); GUI.Label(new Rect(((Rect)(ref box)).x + 22f, num, ((Rect)(ref box)).width - 44f, 100f), text2); num += 110f; GUI.Label(new Rect(((Rect)(ref box)).x + 22f, num, ((Rect)(ref box)).width - 44f, 100f), "Ältestes wartendes Ereignis: " + (string.IsNullOrWhiteSpace(ChallengeHubEventOutbox.OldestPendingAt) ? "keines" : ChallengeHubEventOutbox.OldestPendingAt) + "\nLetzte erfolgreiche Zustellung: " + (string.IsNullOrWhiteSpace(ChallengeHubEventOutbox.LastSuccessAt) ? "noch keine" : ChallengeHubEventOutbox.LastSuccessAt) + "\nLetzter Fehler: " + (string.IsNullOrWhiteSpace(ChallengeHubEventOutbox.LastError) ? "keiner" : ChallengeHubEventOutbox.LastError)); } private void DrawBosses(Rect box) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_038b: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) BossWebConfig[] array = _plugin?.RemoteConfig?.bosses ?? new BossWebConfig[0]; GUI.Label(new Rect(((Rect)(ref box)).x + 18f, ((Rect)(ref box)).y + 14f, ((Rect)(ref box)).width - 36f, 32f), "Bosse besiegen", _titleStyle); GUI.Label(new Rect(((Rect)(ref box)).x + 20f, ((Rect)(ref box)).y + 47f, ((Rect)(ref box)).width - 40f, 30f), "Nur der Spieler mit dem bestätigten letzten Treffer erhält Bossabschluss und Boss-Punkte."); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref box)).x + 14f, ((Rect)(ref box)).y + 82f, ((Rect)(ref box)).width - 28f, ((Rect)(ref box)).height - 96f); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(0f, 0f, ((Rect)(ref val)).width - 18f, Mathf.Max(((Rect)(ref val)).height, (float)array.Length * 132f)); _scroll = GUI.BeginScrollView(val, _scroll, val2); float num = 4f; BossWebConfig[] array2 = array; Rect val3 = default(Rect); foreach (BossWebConfig boss in array2) { BossProgressEntry bossProgressEntry = _bossProgress.FirstOrDefault((BossProgressEntry entry) => string.Equals(entry.key, boss.key, StringComparison.OrdinalIgnoreCase)); bool flag = bossProgressEntry?.completed ?? false; int deaths = bossProgressEntry?.deaths ?? 0; float num2 = BonusFactor(deaths); int num3 = Mathf.RoundToInt((float)boss.basePoints * DifficultyMultiplier()); int num4 = Mathf.RoundToInt((float)Mathf.RoundToInt((float)boss.basePoints * BonusMultiplier() * DifficultyMultiplier()) * num2); ((Rect)(ref val3))..ctor(4f, num, ((Rect)(ref val2)).width - 8f, 120f); GUI.DrawTexture(val3, (Texture)(object)_panel); string text = (flag ? "ERREICHT" : "OFFEN"); GUI.Label(new Rect(((Rect)(ref val3)).x + 12f, ((Rect)(ref val3)).y + 8f, ((Rect)(ref val3)).width - 24f, 25f), text + " " + boss.label, RichBold()); GUI.Label(new Rect(((Rect)(ref val3)).x + 12f, ((Rect)(ref val3)).y + 37f, ((Rect)(ref val3)).width - 24f, 24f), "Biom: " + boss.biome + " | Tode seit letztem Boss: " + deaths); GUI.Label(new Rect(((Rect)(ref val3)).x + 12f, ((Rect)(ref val3)).y + 68f, ((Rect)(ref val3)).width - 24f, 42f), "+" + num3 + " Basis +" + num4 + (flag ? " erhaltener Bonus" : " derzeit möglicher Bonus") + " = " + (num3 + num4) + (flag ? " erhalten" : " derzeit möglich")); num += 132f; } if (array.Length == 0) { GUI.Label(new Rect(10f, 10f, 600f, 40f), "Boss-Konfiguration wird mit ChallengeHub synchronisiert ..."); } GUI.EndScrollView(); } private void DrawBiomes(Rect box) { //IL_004c: 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_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_05b0: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_02d1: Unknown result type (might be due to invalid IL or missing references) //IL_0329: Unknown result type (might be due to invalid IL or missing references) //IL_04b6: Unknown result type (might be due to invalid IL or missing references) //IL_04fd: Unknown result type (might be due to invalid IL or missing references) BiomeWebConfig[] source = _plugin?.RemoteConfig?.biomes ?? new BiomeWebConfig[0]; string localBiome = (((Object)(object)Player.m_localPlayer != (Object)null) ? CanonicalBiomeKey(Plugin.CurrentBiome(((Component)Player.m_localPlayer).transform.position)) : string.Empty); BiomeWebConfig[] array = source.Where((BiomeWebConfig biomeWebConfig) => string.Equals(CanonicalBiomeKey(biomeWebConfig.key), localBiome, StringComparison.OrdinalIgnoreCase) || _biomeProgress.Any((BiomeProgressEntry progress) => progress != null && progress.visited && string.Equals(CanonicalBiomeKey(progress.key), CanonicalBiomeKey(biomeWebConfig.key), StringComparison.OrdinalIgnoreCase))).ToArray(); GUI.Label(new Rect(((Rect)(ref box)).x + 18f, ((Rect)(ref box)).y + 14f, ((Rect)(ref box)).width - 36f, 32f), "Trophäen und sauberes Biom-Spiel", _titleStyle); GUI.Label(new Rect(((Rect)(ref box)).x + 20f, ((Rect)(ref box)).y + 47f, ((Rect)(ref box)).width - 40f, 30f), "Nur eigener Last-Hit plus passende physische Trophäe innerhalb von 60 Sekunden zählt."); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref box)).x + 14f, ((Rect)(ref box)).y + 82f, ((Rect)(ref box)).width - 28f, ((Rect)(ref box)).height - 96f); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(0f, 0f, ((Rect)(ref val)).width - 18f, Mathf.Max(((Rect)(ref val)).height, (float)array.Length * 206f)); _scroll = GUI.BeginScrollView(val, _scroll, val2); float num = 4f; BiomeWebConfig[] array2 = array; Rect val3 = default(Rect); foreach (BiomeWebConfig biome in array2) { BiomeProgressEntry biomeProgressEntry = _biomeProgress.FirstOrDefault((BiomeProgressEntry entry) => entry != null && string.Equals(CanonicalBiomeKey(entry.key), CanonicalBiomeKey(biome.key), StringComparison.OrdinalIgnoreCase)); int num3 = biomeProgressEntry?.found ?? 0; int num4 = ((biomeProgressEntry != null && biomeProgressEntry.required > 0) ? biomeProgressEntry.required : Mathf.Max(0, biome.requiredTrophies)); bool flag = biomeProgressEntry?.completed ?? false; int deaths = biomeProgressEntry?.deaths ?? 0; int num5 = Mathf.RoundToInt((float)biome.trophyPoints * DifficultyMultiplier()); int num6 = Mathf.RoundToInt((float)Mathf.RoundToInt((float)biome.trophyPoints * BonusMultiplier() * DifficultyMultiplier()) * TrophyBonusFactor(deaths)); ((Rect)(ref val3))..ctor(4f, num, ((Rect)(ref val2)).width - 8f, 194f); GUI.DrawTexture(val3, (Texture)(object)_panel); string text = (flag ? "ERREICHT" : ((num4 == 0) ? "NOCH NICHT AUTOMATISIERBAR" : "OFFEN")); GUI.Label(new Rect(((Rect)(ref val3)).x + 12f, ((Rect)(ref val3)).y + 8f, ((Rect)(ref val3)).width - 24f, 25f), text + " Alle " + biome.label + "-Trophäen", RichBold()); GUI.Label(new Rect(((Rect)(ref val3)).x + 12f, ((Rect)(ref val3)).y + 38f, ((Rect)(ref val3)).width - 24f, 25f), (num4 > 0) ? ("Trophäenarten: " + num3 + " / " + num4 + " | Ein Biom-Todeszähler: " + deaths + " | Bonus: " + Mathf.RoundToInt(TrophyBonusFactor(deaths) * 100f) + "%") : "Für dieses unfertige Biom existiert noch kein belastbarer Vanilla-Trophäensatz."); HashSet confirmed = new HashSet((biomeProgressEntry?.trophies ?? new string[0]).Select(CanonicalTrophyKey), StringComparer.OrdinalIgnoreCase); string[] array3 = biomeProgressEntry?.requiredTrophies ?? new string[0]; string text2 = ((array3.Length != 0) ? string.Join(" ", array3.Select((string trophy, int index) => (!confirmed.Contains(CanonicalTrophyKey(trophy))) ? ("○ Unbekannte Trophäe " + (index + 1) + "") : ("✓ " + ReadableTrophy(trophy) + "")).ToArray()) : ((confirmed.Count > 0) ? string.Join(", ", confirmed.Select(ReadableTrophy).ToArray()) : "noch keine")); GUI.Label(new Rect(((Rect)(ref val3)).x + 12f, ((Rect)(ref val3)).y + 68f, ((Rect)(ref val3)).width - 24f, 58f), "Bereits bestätigt / noch offen – Trophäenliste: " + text2, RichBold()); GUI.Label(new Rect(((Rect)(ref val3)).x + 12f, ((Rect)(ref val3)).y + 136f, ((Rect)(ref val3)).width - 24f, 42f), "+" + num5 + " Basis +" + num6 + (flag ? " erhaltener Überlebensbonus" : " derzeit möglicher Überlebensbonus") + " = " + (num5 + num6) + (flag ? " erhalten" : " derzeit möglich")); num += 206f; } if (array.Length == 0) { GUI.Label(new Rect(10f, 10f, ((Rect)(ref val)).width - 30f, 50f), "Noch kein Biom-Fortschritt synchronisiert. Biomnamen erscheinen erst nach dem Betreten."); } GUI.EndScrollView(); } private static string ReadableTrophy(string key) { switch ((key ?? string.Empty).Trim().ToLowerInvariant().Replace("_", string.Empty)) { case "boar": return "Wildschwein"; case "deer": return "Hirsch"; case "neck": return "Neck"; case "greydwarf": return "Grauzwerg"; case "greydwarfbrute": return "Grauzwerg-Brut"; case "greydwarfshaman": return "Grauzwerg-Schamane"; case "skeleton": return "Skelett"; case "skeletonpoison": return "Ranziges Skelett"; case "troll": return "Troll"; case "ghost": return "Geist"; case "blob": return "Blob"; case "draugr": return "Draugr"; case "draugrelite": return "Draugr-Elite"; case "leech": return "Blutegel"; case "surtling": return "Surtling"; case "wraith": return "Geisterscheinung"; case "abomination": return "Abscheulichkeit"; case "wolf": return "Wolf"; case "drake": return "Drache"; case "fenring": return "Fenring"; case "golem": return "Steingolem"; case "cultist": return "Kultist"; case "ulv": return "Ulv"; case "fuling": return "Fuling"; case "fulingberserker": return "Fuling-Berserker"; case "fulingshaman": return "Fuling-Schamane"; case "lox": return "Lox"; case "deathsquito": return "Todesmücke"; case "growth": return "Teerwuchs"; case "seeker": return "Sucher"; case "seekerbrute": return "Sucher-Soldat"; case "gjall": return "Gjall"; case "hare": return "Hase"; case "charredwarrior": return "Verkohlter Krieger"; case "charredmarksman": return "Verkohlter Schütze"; case "charredmage": return "Verkohlter Magier"; case "asksvin": return "Asksvin"; case "volture": return "Volture"; case "morgen": return "Morgen"; case "fallenvalkyrie": return "Gefallene Walküre"; case "bonemaw": return "Knochenmaul"; default: if (!string.IsNullOrWhiteSpace(key)) { return key; } return "Unbekannt"; } } private static string CanonicalTrophyKey(string key) { return (key ?? string.Empty).Trim().ToLowerInvariant().Replace("_", string.Empty) .Replace("-", string.Empty) .Replace(" ", string.Empty); } private static string CanonicalBiomeKey(string value) { string text = (value ?? string.Empty).Trim().ToLowerInvariant().Replace(" ", string.Empty) .Replace("_", string.Empty) .Replace("-", string.Empty); switch (text) { case "meadow": case "grassland": case "meadows": return "meadows"; case "blackforest": return "black_forest"; case "swamp": return "swamp"; case "mountains": case "mountain": return "mountains"; case "plains": return "plains"; case "mistlands": return "mistlands"; case "ashlands": return "ashlands"; case "deepnorth": case "north": return "deep_north"; default: return text; } } private static float BonusMultiplier() { if (_plugin?.RemoteConfig?.scoring == null) { return 2f; } return Mathf.Max(0f, _plugin.RemoteConfig.scoring.bonusBaseMultiplier); } private static float DifficultyMultiplier() { DifficultyConfig difficultyConfig = _plugin?.RemoteConfig?.Difficulty(_difficulty); if (difficultyConfig == null) { return 1f; } return Mathf.Max(0f, difficultyConfig.pointsMultiplier); } private static float BonusFactor(int deaths) { if (deaths > 0) { return deaths switch { 3 => 0.1f, 2 => 0.25f, 1 => 0.5f, _ => 0f, }; } return 1f; } private static float TrophyBonusFactor(int deaths) { return Mathf.Clamp01(1f - (float)Mathf.Max(0, deaths) * 0.2f); } private static GUIStyle RichBold() { //IL_0005: 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_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown return new GUIStyle(_labelStyle) { richText = true, fontStyle = (FontStyle)1 }; } private void DrawStatCard(Rect rect, string title, string value) { //IL_0000: 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_0073: Unknown result type (might be due to invalid IL or missing references) GUI.DrawTexture(rect, (Texture)(object)_panel); GUI.Label(new Rect(((Rect)(ref rect)).x + 14f, ((Rect)(ref rect)).y + 12f, ((Rect)(ref rect)).width - 28f, 28f), title, _titleStyle); GUI.Label(new Rect(((Rect)(ref rect)).x + 14f, ((Rect)(ref rect)).y + 52f, ((Rect)(ref rect)).width - 28f, 30f), value); } private void DrawGoals(Rect box) { //IL_0079: 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_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_0357: Unknown result type (might be due to invalid IL or missing references) //IL_035c: Unknown result type (might be due to invalid IL or missing references) //IL_035e: Unknown result type (might be due to invalid IL or missing references) //IL_0363: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_03d4: Unknown result type (might be due to invalid IL or missing references) //IL_0437: Unknown result type (might be due to invalid IL or missing references) //IL_0482: Unknown result type (might be due to invalid IL or missing references) //IL_0487: Unknown result type (might be due to invalid IL or missing references) //IL_048e: Unknown result type (might be due to invalid IL or missing references) //IL_049a: Expected O, but got Unknown //IL_04c6: Unknown result type (might be due to invalid IL or missing references) //IL_04ed: Unknown result type (might be due to invalid IL or missing references) //IL_04f2: Unknown result type (might be due to invalid IL or missing references) //IL_04f9: Unknown result type (might be due to invalid IL or missing references) //IL_0500: Unknown result type (might be due to invalid IL or missing references) //IL_050d: Expected O, but got Unknown //IL_0511: Unknown result type (might be due to invalid IL or missing references) //IL_0525: Unknown result type (might be due to invalid IL or missing references) //IL_0543: Unknown result type (might be due to invalid IL or missing references) PlaystyleWebConfig[] array = Styles().ToArray(); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref box)).x + 10f, ((Rect)(ref box)).y + 10f, 285f, ((Rect)(ref box)).height - 20f); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref box)).x + 307f, ((Rect)(ref box)).y + 10f, ((Rect)(ref box)).width - 317f, ((Rect)(ref box)).height - 20f); GUI.DrawTexture(val, (Texture)(object)_panel); GUI.DrawTexture(val2, (Texture)(object)_panel); GUI.Label(new Rect(((Rect)(ref val)).x + 12f, ((Rect)(ref val)).y + 10f, ((Rect)(ref val)).width - 24f, 30f), "Punktebereiche", _titleStyle); for (int i = 0; i < array.Length; i++) { PlaystyleWebConfig playstyleWebConfig = array[i]; GoalWebConfig[] array2 = playstyleWebConfig.goals ?? new GoalWebConfig[0]; Color backgroundColor = GUI.backgroundColor; GUI.backgroundColor = ((i == _selectedStyle) ? new Color(0.9f, 0.55f, 0.08f) : new Color(0.45f, 0.45f, 0.48f)); if (GUI.Button(new Rect(((Rect)(ref val)).x + 12f, ((Rect)(ref val)).y + 52f + (float)i * 62f, ((Rect)(ref val)).width - 24f, 50f), (playstyleWebConfig.label ?? playstyleWebConfig.key) + "\n" + array2.Count((GoalWebConfig goal) => IsCompleted(goal.key)) + "/" + array2.Length)) { _selectedStyle = i; _scroll = Vector2.zero; } GUI.backgroundColor = backgroundColor; } if (array.Length == 0) { GUI.Label(new Rect(((Rect)(ref val2)).x + 18f, ((Rect)(ref val2)).y + 18f, ((Rect)(ref val2)).width - 36f, 80f), "Ziele werden von ChallengeHub geladen ..."); return; } _selectedStyle = Mathf.Clamp(_selectedStyle, 0, array.Length - 1); PlaystyleWebConfig playstyleWebConfig2 = array[_selectedStyle]; GoalWebConfig[] obj = playstyleWebConfig2.goals ?? new GoalWebConfig[0]; GUI.Label(new Rect(((Rect)(ref val2)).x + 14f, ((Rect)(ref val2)).y + 10f, ((Rect)(ref val2)).width - 28f, 30f), playstyleWebConfig2.label ?? playstyleWebConfig2.key, _titleStyle); Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(((Rect)(ref val2)).x + 10f, ((Rect)(ref val2)).y + 45f, ((Rect)(ref val2)).width - 20f, ((Rect)(ref val2)).height - 55f); float num = ((IEnumerable)obj).Sum((Func)GoalCardHeight); Rect val4 = default(Rect); ((Rect)(ref val4))..ctor(0f, 0f, ((Rect)(ref val3)).width - 18f, Mathf.Max(((Rect)(ref val3)).height, num)); _scroll = GUI.BeginScrollView(val3, _scroll, val4); float num2 = 4f; GoalWebConfig[] array3 = obj; Rect val5 = default(Rect); foreach (GoalWebConfig goalWebConfig in array3) { bool flag = IsBossDiscoveryGoal(goalWebConfig.key); bool flag2 = IsTraderDiscoveryGoal(goalWebConfig.key); float num4 = GoalCardHeight(goalWebConfig) - 12f; bool num5 = IsCompleted(goalWebConfig.key); ((Rect)(ref val5))..ctor(4f, num2, ((Rect)(ref val4)).width - 8f, num4); GUI.DrawTexture(val5, (Texture)(object)_panel); string text = ((!num5) ? "OFFEN" : (string.Equals(goalWebConfig.rewardMode, "per_scope", StringComparison.OrdinalIgnoreCase) ? "ERREICHT – weitere Bereiche möglich" : "ERREICHT")); GUI.Label(new Rect(((Rect)(ref val5)).x + 12f, ((Rect)(ref val5)).y + 8f, ((Rect)(ref val5)).width - 24f, 24f), text + " " + goalWebConfig.label + " (" + goalWebConfig.basePoints + " P)", new GUIStyle(_labelStyle) { richText = true, fontStyle = (FontStyle)1 }); GUI.Label(new Rect(((Rect)(ref val5)).x + 12f, ((Rect)(ref val5)).y + 35f, ((Rect)(ref val5)).width - 24f, 58f), Short(goalWebConfig.description, 230) + ProgressText(goalWebConfig), new GUIStyle(_labelStyle) { wordWrap = true, richText = true, fontSize = 13 }); if (flag) { DrawBossDiscoveryScopes(val5, goalWebConfig.key); } else if (flag2) { DrawTraderDiscoveryScopes(val5, goalWebConfig.key); } else if (IsAnimalBreedingGoal(goalWebConfig.key)) { DrawAnimalBreedingScopes(val5, goalWebConfig); } num2 += GoalCardHeight(goalWebConfig); } GUI.EndScrollView(); } private static bool IsBossDiscoveryGoal(string goalKey) { if (!string.Equals(goalKey, "boss_altar_found", StringComparison.OrdinalIgnoreCase)) { return string.Equals(goalKey, "boss_vegvisir_found", StringComparison.OrdinalIgnoreCase); } return true; } private void DrawCampaign(Rect box) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: 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_0146: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_03de: Unknown result type (might be due to invalid IL or missing references) //IL_0416: Unknown result type (might be due to invalid IL or missing references) //IL_0467: Unknown result type (might be due to invalid IL or missing references) //IL_04fc: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_0597: Unknown result type (might be due to invalid IL or missing references) //IL_0b12: Unknown result type (might be due to invalid IL or missing references) //IL_0b7f: Unknown result type (might be due to invalid IL or missing references) //IL_07ba: Unknown result type (might be due to invalid IL or missing references) //IL_0802: Unknown result type (might be due to invalid IL or missing references) //IL_0888: Unknown result type (might be due to invalid IL or missing references) //IL_08a8: Unknown result type (might be due to invalid IL or missing references) //IL_08ad: Unknown result type (might be due to invalid IL or missing references) //IL_08b4: Unknown result type (might be due to invalid IL or missing references) //IL_08c1: Expected O, but got Unknown //IL_0952: Unknown result type (might be due to invalid IL or missing references) //IL_0972: Unknown result type (might be due to invalid IL or missing references) //IL_0977: Unknown result type (might be due to invalid IL or missing references) //IL_097e: Unknown result type (might be due to invalid IL or missing references) //IL_098b: Expected O, but got Unknown //IL_09b7: Unknown result type (might be due to invalid IL or missing references) //IL_09e1: Unknown result type (might be due to invalid IL or missing references) //IL_09e6: Unknown result type (might be due to invalid IL or missing references) //IL_09ed: Unknown result type (might be due to invalid IL or missing references) //IL_09fa: Expected O, but got Unknown //IL_0a4d: Unknown result type (might be due to invalid IL or missing references) if (_communityDefinitions.Length != 0) { DrawCommunitySystemsWorkbench(box); return; } GUI.Label(new Rect(((Rect)(ref box)).x + 18f, ((Rect)(ref box)).y + 12f, ((Rect)(ref box)).width - 36f, 30f), "Chronik, Gemeinschaft und Ruhmeshalle", _titleStyle); GUI.Label(new Rect(((Rect)(ref box)).x + 20f, ((Rect)(ref box)).y + 45f, ((Rect)(ref box)).width - 40f, 44f), "Bestätigte Kapitel, Wochenaufträge, Projekte, Handel, Fotos, Titel, Abstimmungen und Trophäen. In der Ruhmeshalle zählt jede Trophäenart nur einmal."); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref box)).x + 14f, ((Rect)(ref box)).y + 92f, ((Rect)(ref box)).width - 28f, ((Rect)(ref box)).height - 106f); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(0f, 0f, ((Rect)(ref val)).width - 18f, Mathf.Max(((Rect)(ref val)).height, (float)_campaignSystems.Length * 238f + 410f)); _scroll = GUI.BeginScrollView(val, _scroll, val2); float num = 4f; if (_weeklySchedule != null) { Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(4f, num, ((Rect)(ref val2)).width - 8f, 150f); GUI.DrawTexture(val3, (Texture)(object)_panel); GUI.Label(new Rect(((Rect)(ref val3)).x + 12f, ((Rect)(ref val3)).y + 8f, ((Rect)(ref val3)).width - 24f, 24f), "AKTUELLE WOCHENAUFTRÄGE " + _weeklySchedule.weekKey, RichBold()); GUI.Label(new Rect(((Rect)(ref val3)).x + 12f, ((Rect)(ref val3)).y + 34f, ((Rect)(ref val3)).width - 24f, 22f), ReadableDate(_weeklySchedule.startAt) + " bis " + ReadableDate(_weeklySchedule.endAt)); float num2 = ((Rect)(ref val3)).y + 62f; ScheduledTaskEntry[] array = _weeklySchedule.tasks ?? new ScheduledTaskEntry[0]; foreach (ScheduledTaskEntry task in array) { CampaignProgressEntry campaignProgressEntry = _campaignProgress.FirstOrDefault((CampaignProgressEntry entry) => entry != null && string.Equals(entry.system, "weekly_tasks", StringComparison.OrdinalIgnoreCase) && string.Equals(entry.scope, task.scope, StringComparison.OrdinalIgnoreCase)); GUI.Label(new Rect(((Rect)(ref val3)).x + 18f, num2, ((Rect)(ref val3)).width - 36f, 22f), "• " + task.label + " Aktuell " + Mathf.RoundToInt(campaignProgressEntry?.value ?? 0f) + "/" + Mathf.RoundToInt(task.target) + " Bestwert " + Mathf.RoundToInt(campaignProgressEntry?.best ?? campaignProgressEntry?.value ?? 0f) + "/" + Mathf.RoundToInt(task.target) + " +" + task.points + " P"); num2 += 25f; } num += 162f; } WorldEventEntry[] worldEvents = _worldEvents; Rect val4 = default(Rect); foreach (WorldEventEntry worldEvent in worldEvents) { ((Rect)(ref val4))..ctor(4f, num, ((Rect)(ref val2)).width - 8f, 174f); GUI.DrawTexture(val4, (Texture)(object)_panel); GUI.Label(new Rect(((Rect)(ref val4)).x + 12f, ((Rect)(ref val4)).y + 8f, ((Rect)(ref val4)).width - 24f, 24f), "WELT-EREIGNIS " + worldEvent.label, RichBold()); GUI.Label(new Rect(((Rect)(ref val4)).x + 12f, ((Rect)(ref val4)).y + 34f, ((Rect)(ref val4)).width - 24f, 22f), ReadableDate(worldEvent.startAt) + " bis " + ReadableDate(worldEvent.endAt) + " | +" + worldEvent.points + " P"); GUI.Label(new Rect(((Rect)(ref val4)).x + 12f, ((Rect)(ref val4)).y + 58f, ((Rect)(ref val4)).width - 24f, 35f), worldEvent.description); float num3 = ((Rect)(ref val4)).y + 96f; ScheduledTaskEntry[] array = worldEvent.tasks ?? new ScheduledTaskEntry[0]; foreach (ScheduledTaskEntry task2 in array) { CampaignProgressEntry campaignProgressEntry2 = _campaignProgress.FirstOrDefault((CampaignProgressEntry entry) => entry != null && string.Equals(entry.system, "world_events", StringComparison.OrdinalIgnoreCase) && string.Equals(entry.scope, worldEvent.id + "-" + task2.key, StringComparison.OrdinalIgnoreCase)); GUI.Label(new Rect(((Rect)(ref val4)).x + 18f, num3, ((Rect)(ref val4)).width - 36f, 21f), "• " + task2.label + " Aktuell " + Mathf.RoundToInt(campaignProgressEntry2?.value ?? 0f) + "/" + Mathf.RoundToInt(task2.target) + " Bestwert " + Mathf.RoundToInt(campaignProgressEntry2?.best ?? campaignProgressEntry2?.value ?? 0f) + "/" + Mathf.RoundToInt(task2.target)); num3 += 22f; } num += 186f; } CampaignSystemEntry[] campaignSystems = _campaignSystems; Rect val5 = default(Rect); foreach (CampaignSystemEntry system in campaignSystems) { CampaignProgressEntry[] source = _campaignProgress.Where((CampaignProgressEntry entry) => entry != null && string.Equals(entry.system, system.key, StringComparison.OrdinalIgnoreCase)).ToArray(); bool flag = string.Equals(system.repeat, "per_trophy", StringComparison.OrdinalIgnoreCase); float num5 = (flag ? ((float)_campaignCompletions.Count((CampaignCompletionEntry entry) => entry != null && string.Equals(entry.system, system.key, StringComparison.OrdinalIgnoreCase) && !string.Equals(entry.scope, "complete", StringComparison.OrdinalIgnoreCase))) : source.Sum((CampaignProgressEntry entry) => entry.value)); float num6 = (flag ? num5 : source.Select((CampaignProgressEntry entry) => entry.best).DefaultIfEmpty(0f).Max()); bool num7 = _campaignCompletions.Any((CampaignCompletionEntry entry) => entry != null && string.Equals(entry.system, system.key, StringComparison.OrdinalIgnoreCase)); ((Rect)(ref val5))..ctor(4f, num, ((Rect)(ref val2)).width - 8f, 226f); GUI.DrawTexture(val5, (Texture)(object)_panel); string text = (num7 ? "ERREICHT" : "AKTIV"); GUI.Label(new Rect(((Rect)(ref val5)).x + 12f, ((Rect)(ref val5)).y + 8f, ((Rect)(ref val5)).width - 24f, 24f), text + " " + system.label + " (+" + system.points + " P)", RichBold()); GUI.Label(new Rect(((Rect)(ref val5)).x + 12f, ((Rect)(ref val5)).y + 35f, ((Rect)(ref val5)).width - 24f, 38f), Short(system.description, 220), new GUIStyle(_labelStyle) { wordWrap = true, fontSize = 13 }); string value = ((system.requirements != null && system.requirements.Length != 0) ? string.Join("\n", system.requirements.Select((string entry, int index) => index + 1 + ". " + entry).ToArray()) : "Die genaue Aufgabe wird mit der Serverkonfiguration geladen."); GUI.Label(new Rect(((Rect)(ref val5)).x + 18f, ((Rect)(ref val5)).y + 76f, ((Rect)(ref val5)).width - 36f, 92f), "Was musst du tun?\n" + Short(value, 430), new GUIStyle(_labelStyle) { wordWrap = true, fontSize = 12 }); GUI.Label(new Rect(((Rect)(ref val5)).x + 18f, ((Rect)(ref val5)).y + 169f, ((Rect)(ref val5)).width - 36f, 26f), "Prüfung: " + Short(system.detection, 180), new GUIStyle(_labelStyle) { wordWrap = true, fontSize = 11 }); string text2 = (flag ? "jede Trophäenart" : Mathf.RoundToInt(system.target).ToString()); GUI.Label(new Rect(((Rect)(ref val5)).x + 12f, ((Rect)(ref val5)).y + 199f, ((Rect)(ref val5)).width - 24f, 22f), "Aktuell: " + Mathf.RoundToInt(num5) + " | Bestwert: " + Mathf.RoundToInt(num6) + " | Ziel: " + text2 + ((system.completionBonus > 0) ? (" | Komplettbonus: +" + system.completionBonus) : "")); num += 238f; } if (_campaignTitles.Length != 0) { GUI.Label(new Rect(8f, num, ((Rect)(ref val2)).width - 16f, 50f), "Titel: " + string.Join(", ", _campaignTitles.Select((CampaignTitleEntry entry) => entry.label ?? entry.key).ToArray()), RichBold()); } if (_campaignSystems.Length == 0) { GUI.Label(new Rect(10f, 10f, 700f, 40f), "Kampagnen-Systeme werden von ChallengeHub synchronisiert ..."); } GUI.EndScrollView(); } private void DrawCommunitySystemsWorkbench(Rect box) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0362: Unknown result type (might be due to invalid IL or missing references) //IL_0502: Unknown result type (might be due to invalid IL or missing references) //IL_0549: Unknown result type (might be due to invalid IL or missing references) //IL_055e: Unknown result type (might be due to invalid IL or missing references) //IL_0563: Unknown result type (might be due to invalid IL or missing references) //IL_056a: Unknown result type (might be due to invalid IL or missing references) //IL_0577: Expected O, but got Unknown //IL_05ee: Unknown result type (might be due to invalid IL or missing references) //IL_05f1: Unknown result type (might be due to invalid IL or missing references) //IL_05f6: Unknown result type (might be due to invalid IL or missing references) //IL_05f8: Unknown result type (might be due to invalid IL or missing references) //IL_05fd: Unknown result type (might be due to invalid IL or missing references) //IL_0627: Unknown result type (might be due to invalid IL or missing references) //IL_096b: Unknown result type (might be due to invalid IL or missing references) //IL_09ca: Unknown result type (might be due to invalid IL or missing references) //IL_0a10: Unknown result type (might be due to invalid IL or missing references) //IL_0a2f: Unknown result type (might be due to invalid IL or missing references) //IL_0a34: Unknown result type (might be due to invalid IL or missing references) //IL_0a3b: Unknown result type (might be due to invalid IL or missing references) //IL_0a48: Expected O, but got Unknown //IL_0993: Unknown result type (might be due to invalid IL or missing references) //IL_0998: Unknown result type (might be due to invalid IL or missing references) //IL_0645: Unknown result type (might be due to invalid IL or missing references) //IL_064a: Unknown result type (might be due to invalid IL or missing references) //IL_0651: Unknown result type (might be due to invalid IL or missing references) //IL_065e: Expected O, but got Unknown //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_0a7f: Unknown result type (might be due to invalid IL or missing references) //IL_0700: Unknown result type (might be due to invalid IL or missing references) //IL_0738: Unknown result type (might be due to invalid IL or missing references) //IL_077f: Unknown result type (might be due to invalid IL or missing references) //IL_07a4: Unknown result type (might be due to invalid IL or missing references) //IL_07a9: Unknown result type (might be due to invalid IL or missing references) //IL_07b0: Unknown result type (might be due to invalid IL or missing references) //IL_07bd: Expected O, but got Unknown //IL_07e1: Unknown result type (might be due to invalid IL or missing references) //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_030d: Unknown result type (might be due to invalid IL or missing references) //IL_0b0d: Unknown result type (might be due to invalid IL or missing references) //IL_0828: Unknown result type (might be due to invalid IL or missing references) //IL_082d: Unknown result type (might be due to invalid IL or missing references) //IL_0d69: Unknown result type (might be due to invalid IL or missing references) //IL_0d83: Unknown result type (might be due to invalid IL or missing references) //IL_0d88: Unknown result type (might be due to invalid IL or missing references) //IL_0d8f: Unknown result type (might be due to invalid IL or missing references) //IL_0d9c: Expected O, but got Unknown //IL_0dc0: Unknown result type (might be due to invalid IL or missing references) //IL_0d18: Unknown result type (might be due to invalid IL or missing references) //IL_0bd4: Unknown result type (might be due to invalid IL or missing references) //IL_0c87: Unknown result type (might be due to invalid IL or missing references) //IL_0e9d: Unknown result type (might be due to invalid IL or missing references) //IL_0f50: Unknown result type (might be due to invalid IL or missing references) //IL_0c21: Unknown result type (might be due to invalid IL or missing references) //IL_0c26: Unknown result type (might be due to invalid IL or missing references) //IL_0eea: Unknown result type (might be due to invalid IL or missing references) //IL_0eef: Unknown result type (might be due to invalid IL or missing references) GUI.Label(new Rect(((Rect)(ref box)).x + 18f, ((Rect)(ref box)).y + 12f, ((Rect)(ref box)).width - 36f, 30f), "Dein Leben auf dem Community-Server", _titleStyle); GUI.Label(new Rect(((Rect)(ref box)).x + 20f, ((Rect)(ref box)).y + 45f, ((Rect)(ref box)).width - 40f, 42f), "Aktuelles, Abenteuer und Gemeinschaft auf einen Blick. Wähle zuerst einen verständlichen Bereich und öffne dann nur das, was dich interessiert."); CommunitySystemDefinition[] definitions = (from communitySystemDefinition in _communityDefinitions where communitySystemDefinition != null orderby communitySystemDefinition.order select communitySystemDefinition).ToArray(); if (definitions.Length == 0) { return; } _selectedCommunitySystem = Mathf.Clamp(_selectedCommunitySystem, 0, CommunityAreaLabels.Length - 1); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref box)).x + 14f, ((Rect)(ref box)).y + 92f, 238f, ((Rect)(ref box)).height - 106f); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(0f, 0f, ((Rect)(ref val)).width - 18f, (float)CommunityAreaLabels.Length * 66f + 8f); _communityNavScroll = GUI.BeginScrollView(val, _communityNavScroll, val2); for (int num = 0; num < CommunityAreaLabels.Length; num++) { string[] keys = CommunityAreaSystems[num]; int num2 = ((num == 0) ? _worldPlans.Count((WorldPlanEntry plan) => plan != null && CommunityIsActive(plan.status)) : _worldPlans.Count((WorldPlanEntry plan) => plan != null && keys.Contains(plan.systemKey) && CommunityIsActive(plan.status))); num2 += ((num == 0) ? _worldSystemViews.Count((WorldSystemViewEntry view) => view != null && view.eventCount > 0) : _worldSystemViews.Count((WorldSystemViewEntry view) => view != null && keys.Contains(view.key) && view.eventCount > 0)); if (num2 == 0) { num2 = ((num == 0) ? _communityInstances.Count((CommunitySystemInstance instance) => instance != null && CommunityIsActive(instance.status)) : _communityInstances.Count((CommunitySystemInstance instance) => instance != null && keys.Contains(instance.systemKey) && CommunityIsActive(instance.status))); } GUIStyle val3 = ((num == _selectedCommunitySystem) ? _buttonStyle : GUI.skin.button); if (GUI.Button(new Rect(2f, 4f + (float)num * 66f, ((Rect)(ref val2)).width - 4f, 57f), CommunityAreaLabels[num] + "\n" + num2 + " aktuell", val3)) { _selectedCommunitySystem = num; _selectedCommunitySystemKey = string.Empty; _selectedCommunityInstanceId = string.Empty; _communityDetailScroll = Vector2.zero; } } GUI.EndScrollView(); Rect val4 = default(Rect); ((Rect)(ref val4))..ctor(((Rect)(ref val)).xMax + 12f, ((Rect)(ref val)).y, ((Rect)(ref box)).xMax - ((Rect)(ref val)).xMax - 26f, ((Rect)(ref val)).height); GUI.DrawTexture(val4, (Texture)(object)_panel); if (string.IsNullOrWhiteSpace(_selectedCommunitySystemKey)) { string[] source = CommunityAreaSystems[_selectedCommunitySystem]; if (_selectedCommunitySystem == 0) { source = (from plan in _worldPlans where plan != null && CommunityIsActive(plan.status) select plan.systemKey).Concat(from view in _worldSystemViews where view != null && view.eventCount > 0 select view.key).Concat(from instance in _communityInstances where instance != null && CommunityIsActive(instance.status) select instance.systemKey).Distinct(StringComparer.OrdinalIgnoreCase) .ToArray(); } CommunitySystemDefinition[] array = (from key in source select definitions.FirstOrDefault((CommunitySystemDefinition communitySystemDefinition) => string.Equals(communitySystemDefinition.key, key, StringComparison.OrdinalIgnoreCase)) into communitySystemDefinition where communitySystemDefinition != null select communitySystemDefinition).ToArray(); GUI.Label(new Rect(((Rect)(ref val4)).x + 16f, ((Rect)(ref val4)).y + 12f, ((Rect)(ref val4)).width - 32f, 28f), CommunityAreaLabels[_selectedCommunitySystem], _titleStyle); GUI.Label(new Rect(((Rect)(ref val4)).x + 18f, ((Rect)(ref val4)).y + 43f, ((Rect)(ref val4)).width - 36f, 48f), CommunityAreaDescription(_selectedCommunitySystem), new GUIStyle(_labelStyle) { wordWrap = true, fontSize = 13 }); Rect val5 = default(Rect); ((Rect)(ref val5))..ctor(((Rect)(ref val4)).x + 12f, ((Rect)(ref val4)).y + 98f, ((Rect)(ref val4)).width - 24f, ((Rect)(ref val4)).height - 110f); Rect val6 = default(Rect); ((Rect)(ref val6))..ctor(0f, 0f, ((Rect)(ref val5)).width - 18f, Mathf.Max(((Rect)(ref val5)).height, (float)array.Length * 94f + 8f)); _communityDetailScroll = GUI.BeginScrollView(val5, _communityDetailScroll, val6); float num3 = 4f; if (array.Length == 0) { GUI.Label(new Rect(10f, num3, ((Rect)(ref val6)).width - 20f, 70f), (_selectedCommunitySystem == 0) ? "Gerade ist kein gemeinsames Vorhaben aktiv. Deine automatisch erfasste Geschichte läuft im Hintergrund weiter." : "Dieser Bereich wird sichtbar, sobald eine passende Aktion beginnt.", new GUIStyle(_labelStyle) { wordWrap = true, fontSize = 13 }); } CommunitySystemDefinition[] array2 = array; Rect val7 = default(Rect); foreach (CommunitySystemDefinition entry in array2) { CommunitySystemInstance[] source2 = _communityInstances.Where((CommunitySystemInstance instance) => instance != null && string.Equals(instance.systemKey, entry.key, StringComparison.OrdinalIgnoreCase)).ToArray(); int num5 = _worldPlans.Count((WorldPlanEntry plan) => plan != null && string.Equals(plan.systemKey, entry.key, StringComparison.OrdinalIgnoreCase) && CommunityIsActive(plan.status)); if (num5 == 0) { num5 = source2.Count((CommunitySystemInstance instance) => CommunityIsActive(instance.status)); } ((Rect)(ref val7))..ctor(4f, num3, ((Rect)(ref val6)).width - 8f, 82f); GUI.DrawTexture(val7, (Texture)(object)_button); GUI.Label(new Rect(((Rect)(ref val7)).x + 12f, ((Rect)(ref val7)).y + 8f, ((Rect)(ref val7)).width - 24f, 24f), entry.label, RichBold()); GUI.Label(new Rect(((Rect)(ref val7)).x + 12f, ((Rect)(ref val7)).y + 33f, ((Rect)(ref val7)).width - 150f, 40f), Short(CommunitySource(entry.key), 180), new GUIStyle(_labelStyle) { wordWrap = true, fontSize = 11 }); if (GUI.Button(new Rect(((Rect)(ref val7)).xMax - 128f, ((Rect)(ref val7)).y + 24f, 112f, 36f), (num5 > 0) ? (num5 + " aktuell") : "Ansehen")) { _selectedCommunitySystemKey = entry.key; _selectedCommunityInstanceId = string.Empty; _communityDetailScroll = Vector2.zero; } num3 += 94f; } GUI.EndScrollView(); return; } CommunitySystemDefinition definition = definitions.FirstOrDefault((CommunitySystemDefinition communitySystemDefinition) => string.Equals(communitySystemDefinition.key, _selectedCommunitySystemKey, StringComparison.OrdinalIgnoreCase)); if (definition == null) { _selectedCommunitySystemKey = string.Empty; return; } CommunitySystemInstance[] array3 = (from instance in _communityInstances where instance != null && string.Equals(instance.systemKey, definition.key, StringComparison.OrdinalIgnoreCase) orderby instance.startsAt ?? string.Empty descending select instance).ToArray(); WorldPlanEntry[] array4 = (from plan in _worldPlans where plan != null && string.Equals(plan.systemKey, definition.key, StringComparison.OrdinalIgnoreCase) orderby plan.updatedAt ?? string.Empty descending select plan).ToArray(); WorldStationEntry[] array5 = (from station in _worldStations where station != null && string.Equals(station.systemKey, definition.key, StringComparison.OrdinalIgnoreCase) orderby station.lastSeenAt ?? string.Empty descending select station).ToArray(); if (GUI.Button(new Rect(((Rect)(ref val4)).x + 16f, ((Rect)(ref val4)).y + 10f, 94f, 30f), "← Zurück")) { _selectedCommunitySystemKey = string.Empty; _selectedCommunityInstanceId = string.Empty; _communityDetailScroll = Vector2.zero; return; } GUI.Label(new Rect(((Rect)(ref val4)).x + 122f, ((Rect)(ref val4)).y + 12f, ((Rect)(ref val4)).width - 138f, 28f), definition.label, _titleStyle); GUI.Label(new Rect(((Rect)(ref val4)).x + 18f, ((Rect)(ref val4)).y + 43f, ((Rect)(ref val4)).width - 36f, 42f), Short(definition.description, 260), new GUIStyle(_labelStyle) { wordWrap = true, fontSize = 13 }); if (array5.Length != 0) { WorldStationEntry worldStationEntry = array5[0]; GUI.Label(new Rect(((Rect)(ref val4)).x + 18f, ((Rect)(ref val4)).y + 88f, ((Rect)(ref val4)).width - 36f, 52f), "WELTSTATION AKTIV " + (worldStationEntry.label ?? worldStationEntry.stationType) + "\nZuletzt serverseitig bestätigt: " + ReadableDate(worldStationEntry.lastSeenAt), RichBold()); } if (string.Equals(definition.key, "weekly_board", StringComparison.OrdinalIgnoreCase) && array5.Length != 0) { DrawNoticeBoardDetail(new Rect(((Rect)(ref val4)).x + 12f, ((Rect)(ref val4)).y + 146f, ((Rect)(ref val4)).width - 24f, ((Rect)(ref val4)).height - 158f)); return; } if (array4.Length != 0) { if (string.IsNullOrWhiteSpace(_selectedCommunityInstanceId) || !array4.Any((WorldPlanEntry worldPlanEntry) => worldPlanEntry.id == _selectedCommunityInstanceId)) { _selectedCommunityInstanceId = array4[0].id; } float num6 = ((array5.Length != 0) ? 58f : 0f); float num7 = ((Rect)(ref val4)).x + 16f; float num8 = Mathf.Max(112f, (((Rect)(ref val4)).width - 38f) / (float)Mathf.Min(5, array4.Length)); foreach (WorldPlanEntry item in array4.Take(5)) { if (GUI.Button(new Rect(num7, ((Rect)(ref val4)).y + 91f + num6, num8 - 6f, 36f), Short(item.title, 20), (item.id == _selectedCommunityInstanceId) ? _buttonStyle : GUI.skin.button)) { _selectedCommunityInstanceId = item.id; _communityDetailScroll = Vector2.zero; } num7 += num8; } DrawWorldPlanDetail(new Rect(((Rect)(ref val4)).x + 12f, ((Rect)(ref val4)).y + 136f + num6, ((Rect)(ref val4)).width - 24f, ((Rect)(ref val4)).height - 148f - num6), array4.First((WorldPlanEntry worldPlanEntry) => worldPlanEntry.id == _selectedCommunityInstanceId)); return; } WorldSystemViewEntry worldSystemViewEntry = _worldSystemViews.FirstOrDefault((WorldSystemViewEntry worldSystemViewEntry2) => worldSystemViewEntry2 != null && string.Equals(worldSystemViewEntry2.key, definition.key, StringComparison.OrdinalIgnoreCase)); if (worldSystemViewEntry != null && worldSystemViewEntry.eventCount > 0) { DrawWorldSystemView(new Rect(((Rect)(ref val4)).x + 12f, ((Rect)(ref val4)).y + ((array5.Length != 0) ? 146f : 92f), ((Rect)(ref val4)).width - 24f, ((Rect)(ref val4)).height - ((array5.Length != 0) ? 158f : 104f)), worldSystemViewEntry); return; } if (array3.Length == 0) { float num9 = ((Rect)(ref val4)).y + ((array5.Length != 0) ? 148f : 98f); GUI.Label(new Rect(((Rect)(ref val4)).x + 18f, num9, ((Rect)(ref val4)).width - 36f, 80f), CommunityEmpty(definition.key), new GUIStyle(_labelStyle) { wordWrap = true, fontSize = 13 }); if (array5.Length != 0 && GUI.Button(new Rect(((Rect)(ref val4)).x + 18f, num9 + 86f, 210f, 34f), "Vorhaben starten")) { StartWorldPlanAction("create", null, array5[0], definition); } return; } if (string.IsNullOrWhiteSpace(_selectedCommunityInstanceId) || !array3.Any((CommunitySystemInstance communitySystemInstance) => communitySystemInstance.id == _selectedCommunityInstanceId)) { _selectedCommunityInstanceId = array3[0].id; } float num10 = ((array5.Length != 0) ? 58f : 0f); float num11 = ((Rect)(ref val4)).x + 16f; float num12 = Mathf.Max(112f, (((Rect)(ref val4)).width - 38f) / (float)Mathf.Min(5, array3.Length)); foreach (CommunitySystemInstance item2 in array3.Take(5)) { if (GUI.Button(new Rect(num11, ((Rect)(ref val4)).y + 91f + num10, num12 - 6f, 36f), Short(item2.title, 20), (item2.id == _selectedCommunityInstanceId) ? _buttonStyle : GUI.skin.button)) { _selectedCommunityInstanceId = item2.id; _communityDetailScroll = Vector2.zero; } num11 += num12; } DrawCommunityInstanceDetail(new Rect(((Rect)(ref val4)).x + 12f, ((Rect)(ref val4)).y + 136f + num10, ((Rect)(ref val4)).width - 24f, ((Rect)(ref val4)).height - 148f - num10), array3.First((CommunitySystemInstance communitySystemInstance) => communitySystemInstance.id == _selectedCommunityInstanceId)); } private void DrawNoticeBoardDetail(Rect viewport) { //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_0373: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_03b4: Unknown result type (might be due to invalid IL or missing references) //IL_03fc: Unknown result type (might be due to invalid IL or missing references) //IL_0494: Unknown result type (might be due to invalid IL or missing references) int valueOrDefault = (_weeklySchedule?.tasks?.Length).GetValueOrDefault(); int num = _worldEvents.Sum((WorldEventEntry entry) => (entry?.tasks?.Length).GetValueOrDefault()); int num2 = _worldEvents.Sum((WorldEventEntry entry) => (entry?.effectLines?.Length).GetValueOrDefault()); float num3 = 145f + (float)valueOrDefault * 54f + (float)_worldEvents.Length * 128f + (float)num * 48f + (float)num2 * 24f; Rect val = default(Rect); ((Rect)(ref val))..ctor(0f, 0f, ((Rect)(ref viewport)).width - 18f, Mathf.Max(((Rect)(ref viewport)).height, num3)); _communityDetailScroll = GUI.BeginScrollView(viewport, _communityDetailScroll, val); float y = 4f; DrawCommunityHeading(val, ref y, "ANSCHLAGTAFEL · LIVE VOM SERVER"); if (_weeklySchedule == null) { DrawCommunityText(val, ref y, "Der Wochenplan wurde noch nicht synchronisiert."); } else { DrawCommunityText(val, ref y, "Woche " + _weeklySchedule.weekKey + " · " + ReadableDate(_weeklySchedule.startAt) + " bis " + ReadableDate(_weeklySchedule.endAt)); ScheduledTaskEntry[] array = _weeklySchedule.tasks ?? new ScheduledTaskEntry[0]; foreach (ScheduledTaskEntry task in array) { CampaignProgressEntry? campaignProgressEntry = _campaignProgress.FirstOrDefault((CampaignProgressEntry entry) => entry != null && string.Equals(entry.system, "weekly_tasks", StringComparison.OrdinalIgnoreCase) && string.Equals(entry.scope, task.scope, StringComparison.OrdinalIgnoreCase)); float num5 = campaignProgressEntry?.value ?? 0f; float num6 = campaignProgressEntry?.best ?? num5; DrawCommunityText(val, ref y, ((num5 >= task.target) ? "✓ " : "○ ") + task.label + " · Aktuell " + Mathf.RoundToInt(num5) + "/" + Mathf.RoundToInt(task.target) + " · Bestwert " + Mathf.RoundToInt(num6) + " · +" + task.points + " P"); } } DrawCommunityHeading(val, ref y, "WELT-EREIGNISSE"); if (_worldEvents.Length == 0) { DrawCommunityText(val, ref y, "Derzeit ist kein Welt-Ereignis angekündigt."); } WorldEventEntry[] worldEvents = _worldEvents; foreach (WorldEventEntry worldEvent in worldEvents) { DrawCommunityText(val, ref y, (worldEvent.label ?? "Welt-Ereignis") + " · " + ReadableDate(worldEvent.startAt) + " bis " + ReadableDate(worldEvent.endAt)); DrawCommunityText(val, ref y, worldEvent.description ?? string.Empty); if (DateTimeOffset.TryParse(worldEvent.endAt, out var result) && result > DateTimeOffset.UtcNow) { DrawCommunityText(val, ref y, "Verbleibend: " + FormatRemaining(result - DateTimeOffset.UtcNow)); } string[] array2 = worldEvent.effectLines ?? new string[0]; foreach (string text in array2) { DrawCommunityText(val, ref y, "• " + text); } ScheduledTaskEntry[] array = worldEvent.tasks ?? new ScheduledTaskEntry[0]; foreach (ScheduledTaskEntry task2 in array) { CampaignProgressEntry? campaignProgressEntry2 = _campaignProgress.FirstOrDefault((CampaignProgressEntry entry) => entry != null && string.Equals(entry.system, "world_events", StringComparison.OrdinalIgnoreCase) && string.Equals(entry.scope, worldEvent.id + "-" + task2.key, StringComparison.OrdinalIgnoreCase)); float num8 = campaignProgressEntry2?.value ?? 0f; float num9 = campaignProgressEntry2?.best ?? num8; DrawCommunityText(val, ref y, ((num8 >= task2.target) ? "✓ " : "○ ") + task2.label + " · " + Mathf.RoundToInt(num8) + "/" + Mathf.RoundToInt(task2.target) + " · Bestwert " + Mathf.RoundToInt(num9)); } } GUI.EndScrollView(); } private static string FormatRemaining(TimeSpan remaining) { if (remaining.TotalDays >= 1.0) { return Mathf.FloorToInt((float)remaining.TotalDays) + " T " + remaining.Hours + " Std"; } if (remaining.TotalHours >= 1.0) { return Mathf.FloorToInt((float)remaining.TotalHours) + " Std " + remaining.Minutes + " Min"; } return Math.Max(0, remaining.Minutes) + " Min"; } private void DrawWorldPlanDetail(Rect viewport, WorldPlanEntry plan) { //IL_0065: 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_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: 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_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_02dd: Unknown result type (might be due to invalid IL or missing references) //IL_042d: Unknown result type (might be due to invalid IL or missing references) //IL_045d: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_0492: Unknown result type (might be due to invalid IL or missing references) //IL_03d8: Unknown result type (might be due to invalid IL or missing references) WorldPlanProgressEntry[] array = plan.progress ?? new WorldPlanProgressEntry[0]; float num = 260f + (float)array.Length * 42f; string[] options = plan.options; float num2 = num + (float)((options != null) ? options.Length : 0) * 38f; Rect val = default(Rect); ((Rect)(ref val))..ctor(0f, 0f, ((Rect)(ref viewport)).width - 18f, Mathf.Max(((Rect)(ref viewport)).height, num2)); _communityDetailScroll = GUI.BeginScrollView(viewport, _communityDetailScroll, val); float y = 4f; DrawCommunityHeading(val, ref y, CommunityStatus(plan.status) + " " + (plan.title ?? plan.systemKey)); DrawCommunityText(val, ref y, plan.description ?? string.Empty); Rect content = val; string[] participantPlayerIds = plan.participantPlayerIds; DrawCommunityText(content, ref y, ((participantPlayerIds != null) ? participantPlayerIds.Length : 0) + " Teilnehmer · Revision " + plan.revision + " · serverpersistent"); DrawCommunityHeading(val, ref y, "LIVE-FORTSCHRITT"); if (array.Length == 0) { DrawCommunityText(val, ref y, "Noch kein serverbestätigter Fortschritt."); } WorldPlanProgressEntry[] array2 = array; foreach (WorldPlanProgressEntry worldPlanProgressEntry in array2) { DrawCommunityText(val, ref y, (worldPlanProgressEntry.scope ?? "gesamt") + " · Aktuell " + Mathf.RoundToInt(worldPlanProgressEntry.value) + ((worldPlanProgressEntry.target > 0f) ? ("/" + Mathf.RoundToInt(worldPlanProgressEntry.target)) : "") + " · Bestwert " + Mathf.RoundToInt(worldPlanProgressEntry.best)); } DrawCommunityHeading(val, ref y, "WELTVERTRAG"); DrawCommunityText(val, ref y, "Fortschritt entsteht ausschließlich durch bestätigte Serverereignisse. Reconnect und Neustart ändern den Stand nicht."); if (plan.systemKey == "delivery_projects") { Rect content2 = val; string[] containerZdos = plan.containerZdos; DrawCommunityText(content2, ref y, ((containerZdos != null) ? containerZdos.Length : 0) + " Projektkisten gebunden · STRG+K am Projektlager, danach an der Kiste"); } WorldPlanVoteEntry[] array3 = plan.votes ?? new WorldPlanVoteEntry[0]; foreach (WorldPlanVoteEntry worldPlanVoteEntry in array3) { DrawCommunityText(val, ref y, (worldPlanVoteEntry.option ?? "Option") + " · " + worldPlanVoteEntry.count + " Stimme(n)"); } if (!string.IsNullOrWhiteSpace(plan.decision)) { DrawCommunityText(val, ref y, "Ergebnis: " + plan.decision); } if (plan.systemKey == "rumors") { GUI.Label(new Rect(14f, y, ((Rect)(ref val)).width - 28f, 34f), "Bestätigung erfolgt nur durch echte Interaktion am Entdeckerbrett."); y += 40f; } if (plan.systemKey == "community_council") { string[] array4 = plan.options ?? new string[2] { "Ja", "Nein" }; foreach (string text in array4) { if (GUI.Button(new Rect(14f, y, 260f, 30f), "Stimme: " + text)) { StartWorldPlanAction("vote", plan, null, null, text); } y += 36f; } } if (plan.systemKey == "build_exhibitions" && string.Equals(plan.status, "jury", StringComparison.OrdinalIgnoreCase)) { string[] array4 = plan.options ?? new string[0]; foreach (string text2 in array4) { if (GUI.Button(new Rect(14f, y, 300f, 30f), "Stimme für Beitrag " + Short(text2, 12))) { StartWorldPlanAction("vote", plan, null, null, text2); } y += 36f; } } if (GUI.Button(new Rect(14f, y, 126f, 30f), "Teilnehmen")) { StartWorldPlanAction("join", plan, null, null); } if (GUI.Button(new Rect(148f, y, 126f, 30f), "Verlassen")) { StartWorldPlanAction("leave", plan, null, null); } y += 36f; if (!string.IsNullOrWhiteSpace(_communityActionMessage)) { DrawCommunityText(val, ref y, _communityActionMessage); } GUI.EndScrollView(); } private void DrawWorldSystemView(Rect viewport, WorldSystemViewEntry view) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) WorldViewRecentEntry[] array = view.recent ?? new WorldViewRecentEntry[0]; float num = ((Rect)(ref viewport)).width - 18f; float height = ((Rect)(ref viewport)).height; float num2 = 165f + (float)array.Length * 48f; WorldViewBreakdownEntry[] breakdown = view.breakdown; Rect val = default(Rect); ((Rect)(ref val))..ctor(0f, 0f, num, Mathf.Max(height, num2 + (float)Mathf.Min(8, (breakdown != null) ? breakdown.Length : 0) * 28f)); _communityDetailScroll = GUI.BeginScrollView(viewport, _communityDetailScroll, val); float y = 4f; DrawCommunityHeading(val, ref y, "LIVE-VERLAUF · " + (view.label ?? view.key)); DrawCommunityText(val, ref y, view.eventCount + " Ereignisse · " + view.actorCount + " Spieler · Gesamtwert " + Mathf.RoundToInt(view.total) + " · Bestwert " + Mathf.RoundToInt(view.best)); foreach (WorldViewBreakdownEntry item in (view.breakdown ?? new WorldViewBreakdownEntry[0]).Take(8)) { DrawCommunityText(val, ref y, item.label + " · " + Mathf.RoundToInt(item.value)); } WorldViewRecentEntry[] array2 = array; foreach (WorldViewRecentEntry worldViewRecentEntry in array2) { DrawCommunityText(val, ref y, (worldViewRecentEntry.label ?? worldViewRecentEntry.type) + " · " + (worldViewRecentEntry.text ?? "serverbestätigt")); DrawCommunityText(val, ref y, ReadableDate(worldViewRecentEntry.observedAt)); } GUI.EndScrollView(); } private void StartWorldPlanAction(string action, WorldPlanEntry plan, WorldStationEntry station, CommunitySystemDefinition definition, string option = null) { if (!_communityActionBusy) { ((MonoBehaviour)this).StartCoroutine(PostWorldPlanAction(action, plan, station, definition, option)); } } private IEnumerator PostWorldPlanAction(string action, WorldPlanEntry plan, WorldStationEntry station, CommunitySystemDefinition definition, string option) { _communityActionBusy = true; _communityActionMessage = "ChallengeHub verarbeitet die Weltaktion ..."; Dictionary payload = new Dictionary { { "challengeId", "" }, { "action", action } }; if (plan != null) { payload["planId"] = plan.id; } if (station != null) { payload["stationId"] = station.stationId; } if (!string.IsNullOrWhiteSpace(option)) { payload["option"] = option; } if (action == "create") { payload["title"] = definition?.label ?? "Gemeinsames Vorhaben"; payload["description"] = CommunitySource(definition?.key); payload["data"] = new Dictionary(); } yield return ChallengeHubApiTokenFeature.EnsureAvailable(); byte[] bytes = Encoding.UTF8.GetBytes(Plugin.ToJson(payload)); UnityWebRequest request = new UnityWebRequest(Plugin.ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/api/valheim/world-systems", "POST"); try { request.uploadHandler = (UploadHandler)new UploadHandlerRaw(bytes); request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); request.SetRequestHeader("Content-Type", "application/json"); ChallengeHubApiTokenFeature.ApplyAuthorization(request); request.timeout = 20; yield return request.SendWebRequest(); CommunityActionResponse communityActionResponse = null; try { communityActionResponse = ChallengeHubJson.Deserialize(request.downloadHandler.text ?? string.Empty); } catch { } if ((int)request.result == 1 && communityActionResponse != null && communityActionResponse.ok) { _communityActionMessage = "Weltaktion bestätigt."; _plugin?.RequestPlayerStatusNow(Player.m_localPlayer); } else { _communityActionMessage = "Weltaktion abgelehnt: " + ((communityActionResponse != null && !string.IsNullOrWhiteSpace(communityActionResponse.error)) ? communityActionResponse.error : request.error); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Weltaktion fehlgeschlagen: " + request.responseCode + " / " + request.downloadHandler.text)); } } } finally { ((IDisposable)request)?.Dispose(); } _communityActionBusy = false; } private static bool CommunityIsActive(string status) { return !new string[9] { "completed", "cancelled", "archived", "expired", "rejected", "published", "returned", "fulfilled", "implemented" }.Contains(status ?? string.Empty, StringComparer.OrdinalIgnoreCase); } private static string CommunityAreaDescription(int index) { return (new string[6] { "Was du jetzt tun, unterstützen oder verfolgen kannst.", "Wochenaufträge, Weltereignisse, Expeditionen und Jagdaufträge.", "Bauwerke, Lieferungen, Abstimmungen, Wettbewerbe und Ausstellungen.", "Fundhinweise, bestätigtes Wissen, Handel und die Ruhmeshalle.", "Deine persönliche Geschichte, Rollenentwicklung und Auszeichnungen.", "Weltchronik, Bilder und der dauerhafte Saisonrückblick." })[Mathf.Clamp(index, 0, 5)]; } private static string CommunitySource(string key) { if (!new Dictionary(StringComparer.OrdinalIgnoreCase) { { "weekly_board", "Der Wochenplan öffnet, misst und archiviert Aufgaben automatisch." }, { "world_events", "Die Spielleitung plant das Ereignis; Serverereignisse füllen den Live-Stand." }, { "expeditions", "Gemeinsame Reise mit Treffpunkt, Ausrüstung, Reisephasen und Abschluss." }, { "hunt_board", "Nur serverbestätigte letzte Treffer erhöhen den Stand." }, { "community_projects", "Bauvorhaben mit Ort, Phasen, Beteiligten und bestätigten Berichten." }, { "delivery_projects", "Materialziele und bestätigte Lieferungen bleiben nachvollziehbar." }, { "community_council", "Vorschläge durchlaufen Diskussion, Abstimmung und Beschluss." }, { "rivalries", "Regeln stehen vor Beginn fest; der Server führt den Rangstand." }, { "build_exhibitions", "Thema, Kriterien, Galerie und Auswertung bilden einen klaren Ablauf." }, { "rumors", "Ein zweiter Spieler muss einen gemeldeten Ort unabhängig bestätigen." }, { "trade_network", "Aktive Partnerschaften und echte Transfers bilden das Handelsnetz." }, { "hall_of_glory", "Geprüfte Trophäen auf Ausstellungsständen füllen die Ruhmeshalle." }, { "chronicle", "Bestätigte Spielereignisse und F10-Berichte schreiben deine Geschichte." }, { "roles", "Deine Rolle entsteht aus tatsächlich bestätigter Aktivität." }, { "titles", "Bestätigte Leistungen schalten Titel und Abzeichen frei." }, { "world_chronicle", "Bedeutende Serverereignisse schreiben die Weltgeschichte fort." }, { "photo_reports", "F10-Aufnahmen werden nach Abenteuer und Vorhaben geordnet." }, { "season_review", "Chroniken, Bilder und Statistiken werden zum Saisonbuch gebündelt." } }.TryGetValue(key ?? string.Empty, out var value)) { return "Bestätigte Aktionen füllen diesen Bereich."; } return value; } private static string CommunityEmpty(string key) { return "Hier ist derzeit noch nichts eingetragen. " + CommunitySource(key); } private void DrawCommunityInstanceDetail(Rect viewport, CommunitySystemInstance instance) { //IL_0094: 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) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Expected O, but got Unknown //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_0320: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_0438: Unknown result type (might be due to invalid IL or missing references) //IL_0445: Unknown result type (might be due to invalid IL or missing references) //IL_05ee: Unknown result type (might be due to invalid IL or missing references) //IL_05f7: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_047c: Unknown result type (might be due to invalid IL or missing references) //IL_034c: Unknown result type (might be due to invalid IL or missing references) //IL_0401: Unknown result type (might be due to invalid IL or missing references) //IL_040e: Unknown result type (might be due to invalid IL or missing references) //IL_0618: Unknown result type (might be due to invalid IL or missing references) //IL_04a2: Unknown result type (might be due to invalid IL or missing references) //IL_04c7: Unknown result type (might be due to invalid IL or missing references) //IL_04d4: Unknown result type (might be due to invalid IL or missing references) //IL_04e7: Unknown result type (might be due to invalid IL or missing references) //IL_051f: Unknown result type (might be due to invalid IL or missing references) //IL_052c: Unknown result type (might be due to invalid IL or missing references) //IL_0556: Unknown result type (might be due to invalid IL or missing references) //IL_0563: Unknown result type (might be due to invalid IL or missing references) //IL_063d: Unknown result type (might be due to invalid IL or missing references) //IL_05a1: Unknown result type (might be due to invalid IL or missing references) //IL_057a: Unknown result type (might be due to invalid IL or missing references) //IL_05c7: Unknown result type (might be due to invalid IL or missing references) CommunityItemTarget[] itemTargets = instance.itemTargets; float num = 430f + (float)((itemTargets != null) ? itemTargets.Length : 0) * 34f; CommunityListEntry[] tasks = instance.tasks; float num2 = num + (float)((tasks != null) ? tasks.Length : 0) * 28f; CommunityListEntry[] phases = instance.phases; float num3 = num2 + (float)((phases != null) ? phases.Length : 0) * 27f; CommunityRecentEntry[] recentEntries = instance.recentEntries; float num4 = num3 + (float)Mathf.Min(8, (recentEntries != null) ? recentEntries.Length : 0) * 29f; Rect val = default(Rect); ((Rect)(ref val))..ctor(0f, 0f, ((Rect)(ref viewport)).width - 18f, Mathf.Max(((Rect)(ref viewport)).height, num4)); _communityDetailScroll = GUI.BeginScrollView(viewport, _communityDetailScroll, val); float num5 = 4f; GUI.Label(new Rect(8f, num5, ((Rect)(ref val)).width - 16f, 25f), CommunityStatus(instance.status) + " " + (instance.title ?? instance.systemKey), RichBold()); num5 += 29f; GUI.Label(new Rect(8f, num5, ((Rect)(ref val)).width - 16f, 46f), Short(instance.description, 320), new GUIStyle(_labelStyle) { wordWrap = true, fontSize = 12 }); num5 += 50f; DrawCommunityText(val, ref num5, (instance.target > 0f) ? ("Fortschritt " + Mathf.RoundToInt(instance.current) + "/" + Mathf.RoundToInt(instance.target) + " " + instance.unit + " | Dein Beitrag " + Mathf.RoundToInt(instance.personal)) : "Verlauf ohne Mengenziel"); DrawCommunityText(val, ref num5, instance.participantCount + " Teilnehmer | " + instance.entryCount + " Einträge"); if (instance.systemKey == "expeditions") { DrawCommunityHeading(val, ref num5, "ROUTE UND AUSRÜSTUNG"); DrawCommunityText(val, ref num5, (instance.meetingPoint ?? "Treffpunkt offen") + " → " + (instance.destination ?? "Ziel offen") + " | Leitung: " + (instance.leaderUserId ?? "offen")); DrawCommunityArray(val, ref num5, instance.equipment, "Keine Ausrüstungsliste"); DrawCommunityPhases(val, ref num5, instance); } else if (instance.systemKey == "hunt_board") { DrawCommunityHeading(val, ref num5, "ÖFFENTLICHER JAGDAUFTRAG"); DrawCommunityText(val, ref num5, "Biom: " + (instance.biome ?? "offen") + " | Nur serverbestätigte letzte Treffer"); DrawCommunityArray(val, ref num5, instance.allowedPrefabs, "Keine Kreaturen festgelegt"); } else if (instance.systemKey == "delivery_projects") { DrawCommunityHeading(val, ref num5, "MATERIALLAGER"); CommunityItemTarget[] array = instance.itemTargets ?? new CommunityItemTarget[0]; foreach (CommunityItemTarget communityItemTarget in array) { DrawCommunityText(val, ref num5, (communityItemTarget.item ?? "Material") + ": " + Mathf.RoundToInt(communityItemTarget.delivered) + "/" + Mathf.RoundToInt(communityItemTarget.target) + " | verbraucht " + Mathf.RoundToInt(communityItemTarget.used) + " | offen " + Mathf.RoundToInt(communityItemTarget.remaining)); } DrawCommunityHeading(val, ref num5, "GEBUNDENE KISTEN"); DrawCommunityArray(val, ref num5, instance.containerZdos, "Noch keine Projektkiste gebunden"); } else if (instance.systemKey == "community_projects") { DrawCommunityHeading(val, ref num5, "BAUPHASEN"); DrawCommunityList(val, ref num5, instance.phases); } else if (instance.systemKey == "weekly_board" || instance.systemKey == "world_events") { DrawCommunityHeading(val, ref num5, (instance.systemKey == "weekly_board") ? "WOCHENAUFTRÄGE" : "EREIGNISAUFGABEN"); DrawCommunityList(val, ref num5, instance.tasks); } else if (instance.systemKey == "build_exhibitions") { DrawCommunityHeading(val, ref num5, "BEWERTUNGSKRITERIEN"); DrawCommunityArray(val, ref num5, instance.criteria, "Noch keine Kriterien"); DrawCommunityText(val, ref num5, "Bewertung: " + (instance.votingMode ?? "offen")); } else if (instance.systemKey == "community_council") { DrawCommunityHeading(val, ref num5, "ABSTIMMUNG"); DrawCommunityArray(val, ref num5, instance.options, "Noch keine Antwortmöglichkeiten"); } else if (instance.systemKey == "rivalries") { DrawCommunityHeading(val, ref num5, "DISZIPLIN UND REGELN"); DrawCommunityText(val, ref num5, instance.metric ?? "Messwert offen"); DrawCommunityArray(val, ref num5, instance.rules, "Noch keine Regeln"); } else if (instance.systemKey == "rumors") { DrawCommunityHeading(val, ref num5, (instance.status == "verified") ? "BESTÄTIGTES WISSEN" : "UNBESTÄTIGTES GERÜCHT"); DrawCommunityText(val, ref num5, "Biom: " + (instance.biome ?? "unbekannt") + " | Eine zweite Person muss den Ort bestätigen."); } DrawCommunityActions(val, ref num5, instance); DrawCommunityHeading(val, ref num5, "LETZTER VERLAUF"); CommunityRecentEntry[] obj = instance.recentEntries ?? new CommunityRecentEntry[0]; if (obj.Length == 0) { DrawCommunityText(val, ref num5, "Noch keine bestätigten Ereignisse."); } foreach (CommunityRecentEntry item in obj.Take(8)) { DrawCommunityText(val, ref num5, ReadableDate(item.createdAt) + " | " + (item.label ?? item.type) + (string.IsNullOrWhiteSpace(item.text) ? "" : (" | " + item.text))); } GUI.EndScrollView(); } private static string CommunityStatus(string status) { if (!new Dictionary(StringComparer.OrdinalIgnoreCase) { { "draft", "ENTWURF" }, { "registration", "ANMELDUNG" }, { "prepared", "VORBEREITET" }, { "departed", "AUFGEBROCHEN" }, { "at_target", "AM ZIEL" }, { "objective_done", "AUFTRAG ERFÜLLT" }, { "returned", "ZURÜCKGEKEHRT" }, { "posted", "AUSGEHÄNGT" }, { "submissions", "EINREICHUNG" }, { "discussion", "DISKUSSION" }, { "voting", "ABSTIMMUNG" }, { "open", "OFFEN" }, { "active", "AKTIV" }, { "completed", "ABGESCHLOSSEN" }, { "archived", "ARCHIV" }, { "verified", "BESTÄTIGT" } }.TryGetValue(status ?? "", out var value)) { return (status ?? "OFFEN").ToUpperInvariant(); } return value; } private static void DrawCommunityHeading(Rect content, ref float y, string text) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) GUI.Label(new Rect(8f, y, ((Rect)(ref content)).width - 16f, 24f), text, RichBold()); y += 27f; } private static void DrawCommunityText(Rect content, ref float y, string text) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown GUI.Label(new Rect(14f, y, ((Rect)(ref content)).width - 28f, 25f), Short(text, 190), new GUIStyle(_labelStyle) { wordWrap = true, fontSize = 12 }); y += 28f; } private static void DrawCommunityArray(Rect content, ref float y, string[] values, string empty) { //IL_0007: 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) if (values == null || values.Length == 0) { DrawCommunityText(content, ref y, empty); return; } foreach (string item in values.Take(12)) { DrawCommunityText(content, ref y, "• " + item); } } private static void DrawCommunityList(Rect content, ref float y, CommunityListEntry[] values) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (values == null || values.Length == 0) { DrawCommunityText(content, ref y, "Noch keine Einträge"); return; } foreach (CommunityListEntry item in values.Take(12)) { DrawCommunityText(content, ref y, "• " + (item.label ?? item.key) + ((item.target > 0f) ? (" | Ziel " + Mathf.RoundToInt(item.target) + " " + item.unit) : "")); } } private static void DrawCommunityPhases(Rect content, ref float y, CommunitySystemInstance instance) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) DrawCommunityHeading(content, ref y, "EXPEDITIONSPHASEN"); HashSet hashSet = new HashSet(instance.completedPhases ?? new string[0], StringComparer.OrdinalIgnoreCase); CommunityListEntry[] array = instance.phases ?? new CommunityListEntry[0]; foreach (CommunityListEntry communityListEntry in array) { DrawCommunityText(content, ref y, (hashSet.Contains(communityListEntry.key) ? "✓ " : "○ ") + (communityListEntry.label ?? communityListEntry.key)); } } private void DrawCommunityActions(Rect content, ref float y, CommunitySystemInstance instance) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) DrawCommunityHeading(content, ref y, "AKTIONEN"); float num = 14f; if (new string[6] { "world_events", "community_projects", "rivalries", "expeditions", "hunt_board", "delivery_projects" }.Contains(instance.systemKey)) { if (GUI.Button(new Rect(num, y, 112f, 30f), "Teilnehmen")) { StartCommunityAction(instance, "join"); } num += 118f; if (GUI.Button(new Rect(num, y, 112f, 30f), "Verlassen")) { StartCommunityAction(instance, "leave"); } num += 118f; } if (instance.systemKey == "expeditions") { string text = ExpeditionNextStatus(instance.status); if (!string.IsNullOrWhiteSpace(text) && GUI.Button(new Rect(num, y, 190f, 30f), "Phase: " + CommunityStatus(text))) { StartCommunityAction(instance, "transition", text); } } y += 36f; if (instance.systemKey == "community_council" && instance.status == "voting") { string[] array = instance.options ?? new string[0]; foreach (string text2 in array) { if (GUI.Button(new Rect(14f, y, Mathf.Min(310f, ((Rect)(ref content)).width - 28f), 29f), "Stimme: " + Short(text2, 32))) { StartCommunityAction(instance, "vote", null, text2); } y += 34f; } } if (!string.IsNullOrWhiteSpace(_communityActionMessage)) { DrawCommunityText(content, ref y, _communityActionMessage); } } private static string ExpeditionNextStatus(string status) { if (!new Dictionary(StringComparer.OrdinalIgnoreCase) { { "draft", "registration" }, { "registration", "prepared" }, { "prepared", "departed" }, { "departed", "at_target" }, { "at_target", "objective_done" }, { "objective_done", "returned" }, { "returned", "archived" } }.TryGetValue(status ?? "", out var value)) { return string.Empty; } return value; } private void StartCommunityAction(CommunitySystemInstance instance, string action, string status = null, string option = null) { if (!_communityActionBusy && instance != null) { ((MonoBehaviour)this).StartCoroutine(PostCommunityAction(instance, action, status, option)); } } private IEnumerator PostCommunityAction(CommunitySystemInstance instance, string action, string status, string option) { _communityActionBusy = true; _communityActionMessage = "ChallengeHub verarbeitet die Aktion ..."; Dictionary payload = new Dictionary { { "challengeId", "" }, { "instanceId", instance.id }, { "action", action } }; if (!string.IsNullOrWhiteSpace(status)) { payload["status"] = status; } if (!string.IsNullOrWhiteSpace(option)) { payload["metadata"] = new Dictionary { { "option", option } }; } yield return ChallengeHubApiTokenFeature.EnsureAvailable(); byte[] bytes = Encoding.UTF8.GetBytes(Plugin.ToJson(payload)); UnityWebRequest request = new UnityWebRequest(Plugin.ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/api/valheim/community-systems", "POST"); try { request.uploadHandler = (UploadHandler)new UploadHandlerRaw(bytes); request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); request.SetRequestHeader("Content-Type", "application/json"); ChallengeHubApiTokenFeature.ApplyAuthorization(request); request.timeout = 20; yield return request.SendWebRequest(); CommunityActionResponse communityActionResponse = null; try { communityActionResponse = ChallengeHubJson.Deserialize(request.downloadHandler.text ?? string.Empty); } catch { } if ((int)request.result == 1 && communityActionResponse != null && communityActionResponse.ok) { _communityActionMessage = "Aktion bestätigt. Status wird aktualisiert."; _plugin?.RequestPlayerStatusNow(Player.m_localPlayer); } else { _communityActionMessage = "Aktion abgelehnt: " + ((communityActionResponse != null && !string.IsNullOrWhiteSpace(communityActionResponse.error)) ? communityActionResponse.error : request.error); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Community-Aktion fehlgeschlagen: " + request.responseCode + " / " + request.downloadHandler.text)); } } } finally { ((IDisposable)request)?.Dispose(); } _communityActionBusy = false; } private static string ReadableDate(string value) { string text; if (!DateTime.TryParse(value, out var result)) { text = value; if (text == null) { return "-"; } } else { text = result.ToLocalTime().ToString("dd.MM.yyyy HH:mm"); } return text; } private static bool IsTraderDiscoveryGoal(string goalKey) { return string.Equals(goalKey, "trader_found", StringComparison.OrdinalIgnoreCase); } private static bool IsAnimalBreedingGoal(string goalKey) { return string.Equals(goalKey, "animal_breeding", StringComparison.OrdinalIgnoreCase); } private static float GoalCardHeight(GoalWebConfig goal) { if (!IsBossDiscoveryGoal(goal?.key)) { if (!IsAnimalBreedingGoal(goal?.key)) { if (!IsTraderDiscoveryGoal(goal?.key)) { return 112f; } return 190f; } return 246f; } return 278f; } private static void DrawAnimalBreedingScopes(Rect card, GoalWebConfig goal) { //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Expected O, but got Unknown string[][] obj = new string[5][] { new string[2] { "boar", "Wildschwein" }, new string[2] { "wolf", "Wolf" }, new string[2] { "lox", "Lox" }, new string[2] { "hen", "Huhn" }, new string[2] { "asksvin", "Asksvin" } }; float num = ((Rect)(ref card)).y + 98f; string[][] array = obj; foreach (string[] array2 in array) { bool num2 = IsCompletedScope(goal.key, array2[0]); float num3 = ProgressValue(goal.key, array2[0]); string text = (num2 ? "ERREICHT" : ("" + Mathf.Min(3, Mathf.RoundToInt(num3)) + "/3 Jungtiere")); GUI.Label(new Rect(((Rect)(ref card)).x + 18f, num, ((Rect)(ref card)).width - 36f, 22f), array2[1] + ": " + text + " (" + goal.basePoints + " P je Tierart)", new GUIStyle(_labelStyle) { richText = true, fontSize = 13 }); num += 25f; } } private static void DrawBossDiscoveryScopes(Rect card, string goalKey) { //IL_0086: 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) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown //IL_00fe: Unknown result type (might be due to invalid IL or missing references) BossWebConfig[] source = _plugin?.RemoteConfig?.bosses ?? new BossWebConfig[0]; float num = ((Rect)(ref card)).y + 98f; BossWebConfig[] array = source.Where((BossWebConfig boss) => IsCompletedScope(goalKey, boss.key)).ToArray(); BossWebConfig[] array2 = array; foreach (BossWebConfig bossWebConfig in array2) { GUI.Label(new Rect(((Rect)(ref card)).x + 18f, num, ((Rect)(ref card)).width - 36f, 22f), (bossWebConfig.label ?? bossWebConfig.key) + ": GEFUNDEN", new GUIStyle(_labelStyle) { richText = true, fontSize = 13 }); num += 23f; } if (array.Length == 0) { GUI.Label(new Rect(((Rect)(ref card)).x + 18f, num, ((Rect)(ref card)).width - 36f, 24f), "Noch keine Bossart bestätigt. Namen bleiben bis zum Fund verborgen."); } } private static void DrawTraderDiscoveryScopes(Rect card, string goalKey) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown //IL_0101: Unknown result type (might be due to invalid IL or missing references) string[][] obj = new string[3][] { new string[2] { "haldor", "Haldor" }, new string[2] { "hildir", "Hildir" }, new string[2] { "bogwitch", "Sumpfhexe" } }; float num = ((Rect)(ref card)).y + 98f; int num2 = 0; string[][] array = obj; foreach (string[] array2 in array) { if (IsCompletedScope(goalKey, array2[0])) { num2++; GUI.Label(new Rect(((Rect)(ref card)).x + 18f, num, ((Rect)(ref card)).width - 36f, 22f), array2[1] + ": GEFUNDEN", new GUIStyle(_labelStyle) { richText = true, fontSize = 13 }); num += 23f; } } if (num2 == 0) { GUI.Label(new Rect(((Rect)(ref card)).x + 18f, num, ((Rect)(ref card)).width - 36f, 24f), "Noch keine Händlerart bestätigt. Namen bleiben bis zum Fund verborgen."); } } private void DrawSkillProgress(Rect box) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0595: Unknown result type (might be due to invalid IL or missing references) //IL_0598: Unknown result type (might be due to invalid IL or missing references) //IL_059d: Unknown result type (might be due to invalid IL or missing references) //IL_059f: Unknown result type (might be due to invalid IL or missing references) //IL_05a4: Unknown result type (might be due to invalid IL or missing references) //IL_05c5: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02eb: Unknown result type (might be due to invalid IL or missing references) //IL_0311: Unknown result type (might be due to invalid IL or missing references) //IL_0330: Unknown result type (might be due to invalid IL or missing references) //IL_0363: Unknown result type (might be due to invalid IL or missing references) //IL_039d: Unknown result type (might be due to invalid IL or missing references) //IL_03f0: Unknown result type (might be due to invalid IL or missing references) //IL_0426: Unknown result type (might be due to invalid IL or missing references) //IL_043c: Unknown result type (might be due to invalid IL or missing references) //IL_0441: Unknown result type (might be due to invalid IL or missing references) //IL_044a: Expected O, but got Unknown //IL_0451: Unknown result type (might be due to invalid IL or missing references) //IL_0484: Unknown result type (might be due to invalid IL or missing references) //IL_0645: Unknown result type (might be due to invalid IL or missing references) //IL_083b: Unknown result type (might be due to invalid IL or missing references) //IL_067b: Unknown result type (might be due to invalid IL or missing references) //IL_06ad: Unknown result type (might be due to invalid IL or missing references) //IL_06b2: Unknown result type (might be due to invalid IL or missing references) //IL_06c2: Unknown result type (might be due to invalid IL or missing references) //IL_0707: Unknown result type (might be due to invalid IL or missing references) //IL_0753: Unknown result type (might be due to invalid IL or missing references) //IL_0781: Unknown result type (might be due to invalid IL or missing references) //IL_0786: Unknown result type (might be due to invalid IL or missing references) //IL_078f: Expected O, but got Unknown //IL_0796: Unknown result type (might be due to invalid IL or missing references) //IL_07d1: Unknown result type (might be due to invalid IL or missing references) GUI.Label(new Rect(((Rect)(ref box)).x + 18f, ((Rect)(ref box)).y + 10f, ((Rect)(ref box)).width - 36f, 30f), "Skill-Fortschritt und Punkte", _titleStyle); int num = Mathf.Max(0, _plugin?.RemoteConfig?.skillTracking?.pointsPerStep ?? 25); int num2 = Mathf.Max(1, _plugin?.RemoteConfig?.skillTracking?.maxStepsPerArea ?? 6); GUI.Label(new Rect(((Rect)(ref box)).x + 20f, ((Rect)(ref box)).y + 43f, ((Rect)(ref box)).width - 40f, 44f), "Der Durchschnitt des auf Level 100 normierten Fortschritts ergibt " + num2 + " Bonusstufen mit je " + num + " Punkten. Ab drei gesteigerten Skills entfällt der niedrigste Wert. " + (_newCharacterValid ? "Start-Baseline bestätigt." : "Start-Baseline muss administrativ geprüft werden.")); string[,] array = new string[5, 2] { { "combat", "Kampf" }, { "supply", "Versorgung" }, { "building", "Aufbau" }, { "exploration", "Erkundung" }, { "crafting", "Handwerk / Sammeln" } }; float num3 = (((Rect)(ref box)).width - 64f) / 5f; Rect val = default(Rect); Rect val2 = default(Rect); Rect val4 = default(Rect); for (int i = 0; i < array.GetLength(0); i++) { string key = array[i, 0]; string text = array[i, 1]; float value; float num4 = (_skillAreaGains.TryGetValue(key, out value) ? value : 0f); int value2; int num5 = (_skillAreaSteps.TryGetValue(key, out value2) ? value2 : 0); ((Rect)(ref val))..ctor(((Rect)(ref box)).x + 12f + (float)i * (num3 + 10f), ((Rect)(ref box)).y + 92f, num3, 118f); Color textColor = SkillAreaColor(key); GUI.DrawTexture(val, (Texture)(object)SkillAreaTexture(key)); ((Rect)(ref val2))..ctor(((Rect)(ref val)).x + 3f, ((Rect)(ref val)).y + 3f, ((Rect)(ref val)).width - 6f, ((Rect)(ref val)).height - 6f); GUI.DrawTexture(val2, (Texture)(object)_panel); GUI.DrawTexture(new Rect(((Rect)(ref val2)).x, ((Rect)(ref val2)).y, ((Rect)(ref val2)).width, 5f), (Texture)(object)SkillAreaTexture(key)); GUIStyle val3 = RichBold(); val3.normal.textColor = textColor; GUI.Label(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 12f, ((Rect)(ref val)).width - 20f, 24f), text, val3); GUI.Label(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 40f, ((Rect)(ref val)).width - 20f, 22f), num4.ToString("0.#") + " % Durchschnitt"); ((Rect)(ref val4))..ctor(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 66f, ((Rect)(ref val)).width - 20f, 9f); GUI.DrawTexture(val4, (Texture)(object)_background); GUI.DrawTexture(new Rect(((Rect)(ref val4)).x, ((Rect)(ref val4)).y, ((Rect)(ref val4)).width * Mathf.Clamp01(num4 / 100f), ((Rect)(ref val4)).height), (Texture)(object)SkillAreaTexture(key)); GUIStyle val5 = new GUIStyle(_labelStyle) { fontStyle = (FontStyle)1 }; val5.normal.textColor = textColor; GUI.Label(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 82f, ((Rect)(ref val)).width - 20f, 27f), num5 + "/" + num2 + " Stufen | +" + num5 * num + " P", val5); } Rect val6 = default(Rect); ((Rect)(ref val6))..ctor(((Rect)(ref box)).x + 14f, ((Rect)(ref box)).y + 226f, ((Rect)(ref box)).width - 28f, ((Rect)(ref box)).height - 240f); List> list = _skillCurrent.OrderByDescending((KeyValuePair pair) => pair.Value).ToList(); Rect val7 = default(Rect); ((Rect)(ref val7))..ctor(0f, 0f, ((Rect)(ref val6)).width - 18f, Mathf.Max(((Rect)(ref val6)).height, 42f + (float)list.Count * 34f)); _scroll = GUI.BeginScrollView(val6, _scroll, val7); GUI.Label(new Rect(8f, 4f, ((Rect)(ref val7)).width - 16f, 28f), "Einzelne Skills – aktuelles Level und gewerteter Zuwachs", _titleStyle); float num6 = 38f; Rect val8 = default(Rect); foreach (KeyValuePair item in list) { float value3; float num7 = (_skillGains.TryGetValue(item.Key, out value3) ? value3 : 0f); string[] array2 = SkillAreas(item.Key); ((Rect)(ref val8))..ctor(6f, num6, ((Rect)(ref val7)).width - 12f, 29f); GUI.DrawTexture(val8, (Texture)(object)_panel); float num8 = 5f; for (int num9 = 0; num9 < array2.Length; num9++) { GUI.DrawTexture(new Rect(((Rect)(ref val8)).x + (float)num9 * num8, ((Rect)(ref val8)).y, num8, ((Rect)(ref val8)).height), (Texture)(object)SkillAreaTexture(array2[num9])); } Color textColor2 = SkillAreaColor((array2.Length != 0) ? array2[0] : string.Empty); GUIStyle val9 = RichBold(); val9.normal.textColor = textColor2; GUI.Label(new Rect(((Rect)(ref val8)).x + 10f + (float)array2.Length * num8, ((Rect)(ref val8)).y + 4f, ((Rect)(ref val8)).width * 0.45f - (float)array2.Length * num8, 23f), ReadableSkill(item.Key), val9); GUI.Label(new Rect(((Rect)(ref val8)).x + ((Rect)(ref val8)).width * 0.48f, ((Rect)(ref val8)).y + 4f, ((Rect)(ref val8)).width * 0.25f, 23f), "Level " + item.Value.ToString("0.#")); GUIStyle val10 = new GUIStyle(_labelStyle) { fontStyle = (FontStyle)1 }; val10.normal.textColor = textColor2; GUI.Label(new Rect(((Rect)(ref val8)).x + ((Rect)(ref val8)).width * 0.73f, ((Rect)(ref val8)).y + 4f, ((Rect)(ref val8)).width * 0.25f, 23f), "Zuwachs +" + num7.ToString("0.#"), val10); num6 += 34f; } if (list.Count == 0) { GUI.Label(new Rect(8f, num6, ((Rect)(ref val7)).width - 16f, 30f), "Noch kein Skill-Snapshot synchronisiert."); } GUI.EndScrollView(); } private static Color SkillAreaColor(string key) { //IL_0062: 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_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) return (Color)((key ?? string.Empty).ToLowerInvariant() switch { "combat" => new Color(0.92f, 0.29f, 0.25f), "supply" => new Color(0.34f, 0.78f, 0.36f), "building" => new Color(0.95f, 0.65f, 0.2f), "exploration" => new Color(0.25f, 0.66f, 0.94f), "crafting" => new Color(0.72f, 0.48f, 0.91f), _ => new Color(0.78f, 0.78f, 0.78f), }); } private static Texture2D SkillAreaTexture(string key) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) string key2 = (key ?? string.Empty).ToLowerInvariant(); if (!SkillAreaTextures.TryGetValue(key2, out var value) || (Object)(object)value == (Object)null) { value = Solid(SkillAreaColor(key2)); SkillAreaTextures[key2] = value; } return value; } private static string ReadableSkill(string key) { switch ((key ?? string.Empty).ToLowerInvariant()) { case "running": case "run": return "Laufen"; case "jump": return "Springen"; case "sneak": case "sneaking": return "Schleichen"; case "swim": case "swimming": return "Schwimmen"; case "ride": case "riding": return "Reiten"; case "clubs": return "Keulen"; case "blocking": return "Blocken"; case "unarmed": return "Unbewaffnet"; case "axes": return "Äxte"; case "bows": return "Bögen"; case "swords": return "Schwerter"; case "spears": return "Speere"; case "knives": return "Messer"; case "polearms": return "Stangenwaffen"; case "crafting": return "Handwerk"; case "cooking": return "Kochen"; case "farming": return "Landwirtschaft"; case "woodcutting": return "Holzfällen"; case "pickaxes": return "Spitzhacken"; case "fishing": return "Angeln"; case "elemental_magic": case "elementalmagic": return "Elementarmagie"; case "blood_magic": case "bloodmagic": return "Blutmagie"; default: return (key ?? "Unbekannt").Replace('_', ' '); } } private static string[] SkillAreas(string key) { switch ((key ?? string.Empty).ToLowerInvariant()) { case "axes": case "bows": case "blocking": case "polearms": case "clubs": case "spears": case "swords": case "knives": case "blood_magic": case "unarmed": case "crossbows": case "elemental_magic": case "elementalmagic": case "bloodmagic": return new string[1] { "combat" }; case "cooking": return new string[1] { "supply" }; case "farming": return new string[2] { "supply", "crafting" }; case "crafting": case "woodcutting": return new string[2] { "building", "crafting" }; case "jump": case "ride": case "swim": case "sneaking": case "swimming": case "sneak": case "riding": case "running": case "run": return new string[1] { "exploration" }; case "pickaxes": case "fishing": return new string[1] { "crafting" }; default: return new string[0]; } } private void DrawSkilltree(Rect box) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_011a: 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) TalentData localData = TalentStore.GetLocalData(); if (localData == null) { GUI.Label(new Rect(((Rect)(ref box)).x + 20f, ((Rect)(ref box)).y + 20f, 500f, 40f), "Kein lokaler Spieler geladen."); return; } GUI.Label(new Rect(((Rect)(ref box)).x + 18f, ((Rect)(ref box)).y + 8f, ((Rect)(ref box)).width - 36f, 30f), "QoL-Talente | Talentpunkte: " + localData.AvailablePoints, _titleStyle); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref box)).x + 10f, ((Rect)(ref box)).y + 44f, 405f, ((Rect)(ref box)).height - 54f); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref box)).x + 427f, ((Rect)(ref box)).y + 44f, ((Rect)(ref box)).width - 437f, ((Rect)(ref box)).height - 54f); GUI.DrawTexture(val, (Texture)(object)_panel); GUI.DrawTexture(val2, (Texture)(object)_panel); DrawSkilltreeOverview(val, localData); DrawSkilltreeBranch(val2, localData); } private void DrawSkilltreeOverview(Rect box, TalentData data) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0080: 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) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: 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_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_02ee: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_02d8: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Unknown result type (might be due to invalid IL or missing references) //IL_0365: Unknown result type (might be due to invalid IL or missing references) //IL_035b: Unknown result type (might be due to invalid IL or missing references) //IL_0360: Unknown result type (might be due to invalid IL or missing references) GUI.Label(new Rect(((Rect)(ref box)).x + 14f, ((Rect)(ref box)).y + 8f, ((Rect)(ref box)).width - 28f, 28f), "Sechs QoL-Zweige", _titleStyle); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(((Rect)(ref box)).x + ((Rect)(ref box)).width * 0.5f, ((Rect)(ref box)).y + ((Rect)(ref box)).height * 0.5f); Rect rect = default(Rect); ((Rect)(ref rect))..ctor(val.x - 62f, val.y - 34f, 124f, 68f); Dictionary dictionary = new Dictionary { [SkillBranch.Building] = new Rect(val.x - 72f, ((Rect)(ref box)).y + 48f, 144f, 58f), [SkillBranch.Comfort] = new Rect(((Rect)(ref box)).x + 18f, val.y - 122f, 150f, 64f), [SkillBranch.Orientation] = new Rect(((Rect)(ref box)).x + ((Rect)(ref box)).width - 168f, val.y - 122f, 150f, 64f), [SkillBranch.Storage] = new Rect(((Rect)(ref box)).x + 18f, val.y + 62f, 150f, 64f), [SkillBranch.Travel] = new Rect(((Rect)(ref box)).x + ((Rect)(ref box)).width - 168f, val.y + 62f, 150f, 64f), [SkillBranch.Recovery] = new Rect(val.x - 82f, ((Rect)(ref box)).y + ((Rect)(ref box)).height - 88f, 164f, 64f) }; foreach (KeyValuePair item in dictionary) { Vector2 start = val; Rect value = item.Value; DrawSkilltreeLine(start, ((Rect)(ref value)).center, BranchHasUnlockedSkill(data, item.Key)); } DrawSkilltreeNode(rect, SkillTreeDefinitions.Get("core.pioneer"), data, showHotkey: false); foreach (KeyValuePair item2 in dictionary) { IReadOnlyList readOnlyList = SkillTreeDefinitions.ForBranch(item2.Key); int num = readOnlyList.Count((SkillNodeDefinition node) => data.HasUnlockedSkill(node.Id)); Color backgroundColor = GUI.backgroundColor; GUI.backgroundColor = ((item2.Key == _selectedBranch) ? new Color(1f, 0.72f, 0.18f) : ((num > 0) ? new Color(0.85f, 0.5f, 0.08f) : new Color(0.48f, 0.48f, 0.5f))); if (GUI.Button(item2.Value, SkillTreeDefinitions.BranchTitle(item2.Key) + "\n" + num + "/" + readOnlyList.Count)) { _selectedBranch = item2.Key; _scroll = Vector2.zero; } GUI.backgroundColor = backgroundColor; } } private void DrawSkilltreeBranch(Rect box, TalentData data) { //IL_004a: 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_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) IReadOnlyList nodes = SkillTreeDefinitions.ForBranch(_selectedBranch); GUI.Label(new Rect(((Rect)(ref box)).x + 14f, ((Rect)(ref box)).y + 8f, ((Rect)(ref box)).width - 190f, 28f), SkillTreeDefinitions.BranchTitle(_selectedBranch), _titleStyle); GUI.Label(new Rect(((Rect)(ref box)).x + ((Rect)(ref box)).width - 175f, ((Rect)(ref box)).y + 12f, 160f, 24f), nodes.Count((SkillNodeDefinition node) => data.HasUnlockedSkill(node.Id)) + "/" + nodes.Count + " freigeschaltet"); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref box)).x + 10f, ((Rect)(ref box)).y + 40f, ((Rect)(ref box)).width - 20f, ((Rect)(ref box)).height - 50f); int num = ((nodes.Count != 0) ? nodes.Max((SkillNodeDefinition node) => SkilltreeDepth(node, nodes, new Dictionary(StringComparer.Ordinal))) : 0); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(0f, 0f, ((Rect)(ref val)).width - 18f, Mathf.Max(((Rect)(ref val)).height, 125f + (float)num * 105f)); _scroll = GUI.BeginScrollView(val, _scroll, val2); Dictionary dictionary = SkilltreeNodePositions(nodes, ((Rect)(ref val2)).width); foreach (SkillNodeDefinition item in nodes) { string[] prerequisites = item.Prerequisites; foreach (string text in prerequisites) { if (dictionary.TryGetValue(item.Id, out var value)) { if (dictionary.TryGetValue(text, out var value2)) { DrawSkilltreeLine(((Rect)(ref value2)).center, ((Rect)(ref value)).center, data.IsSkillOperational(item.Id)); } else if (text == "core.pioneer") { DrawSkilltreeLine(new Vector2(((Rect)(ref val2)).width * 0.5f, 12f), ((Rect)(ref value)).center, data.IsSkillOperational(item.Id)); } } } } foreach (SkillNodeDefinition item2 in nodes) { if (dictionary.TryGetValue(item2.Id, out var value3)) { DrawSkilltreeNode(value3, item2, data, showHotkey: true); } } GUI.EndScrollView(); } private static Dictionary SkilltreeNodePositions(IReadOnlyList nodes, float width) { //IL_0171: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = new Dictionary(StringComparer.Ordinal); Dictionary depths = new Dictionary(StringComparer.Ordinal); foreach (SkillNodeDefinition node in nodes) { depths[node.Id] = SkilltreeDepth(node, nodes, depths); } foreach (IGrouping item in from node in nodes group node by depths[node.Id] into @group orderby @group.Key select @group) { List list = item.ToList(); float num = Mathf.Clamp((width - 30f) / (float)Math.Max(1, list.Count) - 12f, 145f, 215f); float num2 = (float)list.Count * num + (float)Math.Max(0, list.Count - 1) * 16f; float num3 = Mathf.Max(8f, (width - num2) * 0.5f); for (int num4 = 0; num4 < list.Count; num4++) { dictionary[list[num4].Id] = new Rect(num3 + (float)num4 * (num + 16f), 42f + (float)item.Key * 105f, num, 76f); } } return dictionary; } private static int SkilltreeDepth(SkillNodeDefinition node, IReadOnlyList nodes, Dictionary cache) { if (cache.TryGetValue(node.Id, out var value)) { return value; } int num = 0; string[] prerequisites = node.Prerequisites; foreach (string prerequisite in prerequisites) { SkillNodeDefinition skillNodeDefinition = nodes.FirstOrDefault((SkillNodeDefinition candidate) => candidate.Id == prerequisite); if (skillNodeDefinition != null) { num = Math.Max(num, SkilltreeDepth(skillNodeDefinition, nodes, cache) + 1); } } cache[node.Id] = num; return num; } private static void DrawSkilltreeNode(Rect rect, SkillNodeDefinition node, TalentData data, bool showHotkey) { //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Expected O, but got Unknown //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) if (node == null || data == null) { return; } bool flag = data.HasUnlockedSkill(node.Id); bool flag2 = data.IsSkillEnabled(node.Id); bool flag3 = data.IsSkillOperational(node.Id); bool flag4 = SkillTreeDefinitions.PrerequisitesMet(data, node); bool flag5 = node.Enabled && !flag && flag4 && data.AvailablePoints >= node.Cost; string text = (flag ? ((!flag2) ? "GEKAUFT - INAKTIV" : (flag3 ? "GEKAUFT - AKTIV" : "VORAUSSETZUNG INAKTIV")) : (flag4 ? (node.Cost + " Punkt") : "GESPERRT")); string text2 = ((showHotkey && !string.IsNullOrWhiteSpace(node.Hotkey)) ? ("\n" + node.Hotkey) : string.Empty); Color backgroundColor = GUI.backgroundColor; GUI.backgroundColor = (flag3 ? new Color(0.9f, 0.52f, 0.06f) : (flag ? new Color(0.62f, 0.46f, 0.18f) : (flag5 ? new Color(0.72f, 0.62f, 0.36f) : new Color(0.4f, 0.4f, 0.43f)))); if (GUI.Button(rect, new GUIContent(node.Title + "\n" + text + text2, node.Description))) { if (flag5) { TalentStore.TryUnlockSkill(node.Id); } else if (flag) { TalentStore.ToggleSkillEnabled(node.Id, out var _); } } GUI.backgroundColor = backgroundColor; } private static bool BranchHasUnlockedSkill(TalentData data, SkillBranch branch) { if (data != null) { return SkillTreeDefinitions.ForBranch(branch).Any((SkillNodeDefinition node) => data.HasUnlockedSkill(node.Id)); } return false; } private static void DrawSkilltreeLine(Vector2 start, Vector2 end, bool active) { //IL_001c: 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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_treeLine == (Object)null) { _treeLine = Solid(Color.white); } Vector2 val = end - start; float magnitude = ((Vector2)(ref val)).magnitude; float num = Mathf.Atan2(val.y, val.x) * 57.29578f; Matrix4x4 matrix = GUI.matrix; Color color = GUI.color; GUI.color = (active ? new Color(1f, 0.65f, 0f, 0.82f) : new Color(0.3f, 0.3f, 0.3f, 0.82f)); GUIUtility.RotateAroundPivot(num, start); GUI.DrawTexture(new Rect(start.x, start.y - 1.5f, magnitude, 3f), (Texture)(object)_treeLine); GUI.matrix = matrix; GUI.color = color; } private static List PartnershipViews() { //IL_0172: Unknown result type (might be due to invalid IL or missing references) List list = (from entry in _partnerships ?? new PartnershipEntry[0] where entry != null select new PartnershipView { Type = entry.type, Biome = entry.biome, Status = entry.status, A = entry.userAName, B = entry.userBName, Id = entry.id, Structure = entry.structureName, AcceptedAt = entry.acceptedAt, Global = true }).ToList(); long num = (((Object)(object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetPlayerID() : 0); Piece[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Piece val in array) { object obj; if (val == null) { obj = null; } else { ZNetView component = ((Component)val).GetComponent(); obj = ((component != null) ? component.GetZDO() : null); } ZDO val2 = (ZDO)obj; if (val2 == null) { continue; } string text = val2.GetString("ChallengeHub.Partnership.Type", ""); if (string.IsNullOrWhiteSpace(text)) { continue; } string text2 = val2.GetString("ChallengeHub.Partnership.UserAId", ""); string text3 = val2.GetString("ChallengeHub.Partnership.UserBId", ""); if (num == 0L || !(text2 != num.ToString()) || !(text3 != num.ToString())) { string id = val2.GetString("ChallengeHub.Partnership.Id", ((Object)val).GetInstanceID().ToString()); if (!list.Any((PartnershipView view) => string.Equals(view.Id, id, StringComparison.OrdinalIgnoreCase))) { list.Add(new PartnershipView { Type = text, Biome = CanonicalBiomeKey(Plugin.CurrentBiome(((Component)val).transform.position)), Status = val2.GetString("ChallengeHub.Partnership.Status", "pending"), A = val2.GetString("ChallengeHub.Partnership.UserAName", text2), B = val2.GetString("ChallengeHub.Partnership.UserBName", text3), Id = id, Structure = val.m_name, Global = false }); } } } return (from view in list group view by view.Id into @group select @group.First() into view orderby view.Status == "active" descending, view.Type select view).ToList(); } private void DrawPartnerships(Rect box) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Expected O, but got Unknown //IL_0273: Unknown result type (might be due to invalid IL or missing references) List list = PartnershipViews(); GUI.Label(new Rect(((Rect)(ref box)).x + 18f, ((Rect)(ref box)).y + 14f, ((Rect)(ref box)).width - 36f, 32f), "Partnerschaften", _titleStyle); GUI.Label(new Rect(((Rect)(ref box)).x + 20f, ((Rect)(ref box)).y + 48f, ((Rect)(ref box)).width - 40f, 36f), "Weltweit gespeichert und überall sichtbar. Die Wirkung gilt im Vertragsbiom. Pro Spielerpaar, Biom und Partnerschaftsart ist genau ein aktiver Vertrag möglich."); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref box)).x + 14f, ((Rect)(ref box)).y + 82f, ((Rect)(ref box)).width - 28f, ((Rect)(ref box)).height - 96f); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(0f, 0f, ((Rect)(ref val)).width - 18f, Mathf.Max(((Rect)(ref val)).height, (float)list.Count * 104f)); _scroll = GUI.BeginScrollView(val, _scroll, val2); if (list.Count == 0) { GUI.Label(new Rect(10f, 10f, ((Rect)(ref val2)).width - 20f, 80f), "Keine Partnerschaft für diesen Spieler gespeichert."); } float num = 4f; Rect val3 = default(Rect); foreach (PartnershipView item in list) { ((Rect)(ref val3))..ctor(4f, num, ((Rect)(ref val2)).width - 8f, 92f); GUI.DrawTexture(val3, (Texture)(object)_panel); string text = ((item.Status == "active") ? "AKTIV" : ((item.Status == "cancelled") ? "BEENDET" : "WARTET")); GUI.Label(new Rect(((Rect)(ref val3)).x + 12f, ((Rect)(ref val3)).y + 10f, ((Rect)(ref val3)).width - 24f, 24f), text + " " + ReadablePartnershipType(item.Type) + " | Biom: " + ReadableBiome(item.Biome), new GUIStyle(_labelStyle) { richText = true, fontStyle = (FontStyle)1 }); GUI.Label(new Rect(((Rect)(ref val3)).x + 12f, ((Rect)(ref val3)).y + 40f, ((Rect)(ref val3)).width - 24f, 42f), (item.A ?? "-") + " ↔ " + (item.B ?? "-") + "\n" + (item.Global ? ("Weltweit sichtbar · im Biom " + ReadableBiome(item.Biome) + " wirksam") : "Lokaler Vertragsort · Web-Synchronisierung ausstehend") + (string.IsNullOrWhiteSpace(item.Structure) ? "" : (" · Ort: " + item.Structure))); num += 104f; } GUI.EndScrollView(); } private static string ReadablePartnershipType(string type) { return (type ?? string.Empty).ToLowerInvariant() switch { "combat" => "Kampfpartnerschaft", "peace" => "Friedenspartnerschaft", "bossfight" => "Bosskampf-Partnerschaft", "farm" => "Farmpartnerschaft", "build" => "Baupartnerschaft", "explore" => "Erkundungspartnerschaft", _ => "Handelspartnerschaft", }; } private static string ReadableBiome(string biome) { return CanonicalBiomeKey(biome) switch { "meadows" => "Wiesen", "black_forest" => "Schwarzwald", "swamp" => "Sumpf", "mountains" => "Gebirge", "plains" => "Ebenen", "mistlands" => "Nebellande", "ashlands" => "Aschlande", "deep_north" => "Tiefer Norden", _ => "noch nicht zugeordnet", }; } private static void InitStyles() { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Expected O, but got Unknown //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Expected O, but got Unknown //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Expected O, but got Unknown //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Expected O, but got Unknown //IL_02a2: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_background != (Object)null)) { _norseFont = ((IEnumerable)Resources.FindObjectsOfTypeAll()).FirstOrDefault((Func)((Font font) => ((Object)font).name == "Norsebold")) ?? GUI.skin.font; _bodyFont = ((IEnumerable)Resources.FindObjectsOfTypeAll()).FirstOrDefault((Func)((Font font) => ((Object)font).name == "AveriaSansLibre-Bold" || ((Object)font).name == "AveriaSerifLibre-Bold")) ?? GUI.skin.font; _background = Solid(new Color(0.075f, 0.075f, 0.075f, 0.99f)); _panel = Solid(new Color(0.12f, 0.12f, 0.12f, 0.98f)); _button = Solid(new Color(0.42f, 0.42f, 0.42f, 1f)); _buttonHover = Solid(new Color(0.58f, 0.58f, 0.58f, 1f)); _windowStyle = new GUIStyle(GUI.skin.window) { font = _norseFont, fontSize = 24 }; _windowStyle.normal.background = _background; _windowStyle.normal.textColor = new Color(1f, 0.65f, 0f); _buttonStyle = new GUIStyle(GUI.skin.button) { font = _bodyFont, fontSize = 14, wordWrap = true }; _buttonStyle.normal.background = _button; _buttonStyle.hover.background = _buttonHover; _buttonStyle.active.background = _buttonHover; _buttonStyle.normal.textColor = Color.white; _buttonStyle.hover.textColor = Color.white; _buttonStyle.active.textColor = Color.white; _labelStyle = new GUIStyle(GUI.skin.label) { font = _bodyFont, fontSize = 15, wordWrap = true }; _labelStyle.normal.textColor = new Color(0.9f, 0.9f, 0.9f); _titleStyle = new GUIStyle(_labelStyle) { font = _norseFont, fontSize = 22 }; _titleStyle.normal.textColor = new Color(1f, 0.65f, 0f); } } private static Texture2D Solid(Color color) { //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_000a: 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) //IL_0017: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, color); val.Apply(); return val; } private static IEnumerable Styles() { PlaystyleWebConfig[] array = _plugin?.RemoteConfig?.playstyles; if (array == null || array.Length == 0) { return _serverStyles; } return array; } private static IEnumerable OpenGoals() { return from goal in Styles().SelectMany((PlaystyleWebConfig style) => style.goals ?? new GoalWebConfig[0]) where !IsCompleted(goal.key) select goal; } private static bool IsCompleted(string goalKey) { if (!string.IsNullOrWhiteSpace(goalKey)) { return Completed.Any((string value) => string.Equals(value, goalKey, StringComparison.OrdinalIgnoreCase) || value.StartsWith(goalKey + "::", StringComparison.OrdinalIgnoreCase) || value.StartsWith(goalKey + ":", StringComparison.OrdinalIgnoreCase)); } return false; } private static bool IsCompletedScope(string goalKey, string scope) { if (string.IsNullOrWhiteSpace(goalKey) || string.IsNullOrWhiteSpace(scope)) { return false; } return Completed.Any((string value) => string.Equals(value, goalKey + "::" + scope, StringComparison.OrdinalIgnoreCase) || string.Equals(value, goalKey + ":" + scope, StringComparison.OrdinalIgnoreCase)); } private static float ProgressValue(string goalKey, string scope) { if (!Progress.TryGetValue(goalKey, out var value)) { return 0f; } return value.FirstOrDefault((GoalProgressEntry goalProgressEntry) => string.Equals(goalProgressEntry.scope, scope, StringComparison.OrdinalIgnoreCase))?.value ?? 0f; } private static string ReadableCompletionKey(string value) { string text = value ?? string.Empty; string[] array = text.Split(new string[1] { "::" }, StringSplitOptions.None); if (array.Length == 2 && string.Equals(array[0], "animal_breeding", StringComparison.OrdinalIgnoreCase)) { return "Tierzucht " + array[1]; } return text.Replace("::", " / ").Replace('_', ' '); } private static string ReadableEventType(string value) { return (value ?? string.Empty).ToLowerInvariant() switch { "boss_kill" => "Boss besiegt", "trophy_found" => "Biom-Trophäe gewertet", "sleep_bed_yes_vote" => "Schlafabstimmung: Ja im Bett", "trade_transfer_completed" => "Handel abgeschlossen", _ => (value ?? "Challenge-Fortschritt").Replace('_', ' '), }; } private static string ShortTime(string value) { if (!DateTime.TryParse(value, out var result)) { return value; } return result.ToLocalTime().ToString("dd.MM. HH:mm"); } private static string ProgressText(GoalWebConfig goal) { string text = goal?.key ?? string.Empty; string text2 = (string.IsNullOrWhiteSpace(goal?.progressUnit) ? string.Empty : (" " + goal.progressUnit)); if (!Progress.TryGetValue(text, out var value)) { value = new List(); } float num = 0f; float num2 = 0f; string a = goal?.progressMode ?? "binary"; if (string.Equals(a, "distinct", StringComparison.OrdinalIgnoreCase)) { float threshold = Mathf.Max(1f, goal?.progressScopeTarget ?? 1f); num = value.Count((GoalProgressEntry entry) => entry.value >= threshold); num2 = value.Count((GoalProgressEntry entry) => Mathf.Max(entry.best, entry.value) >= threshold); } else if (string.Equals(a, "groupedDistinct", StringComparison.OrdinalIgnoreCase)) { IEnumerable> source = from entry in value group entry by (entry.scope ?? "global").Split(new char[1] { ':' })[0]; num = source.Select((IGrouping group) => group.Count((GoalProgressEntry entry) => entry.value >= 1f)).DefaultIfEmpty(0).Max(); num2 = source.Select((IGrouping group) => group.Count((GoalProgressEntry entry) => Mathf.Max(entry.best, entry.value) >= 1f)).DefaultIfEmpty(0).Max(); } else if (string.Equals(a, "max", StringComparison.OrdinalIgnoreCase)) { num = value.OrderByDescending((GoalProgressEntry entry) => ReadableTimestamp(entry.updatedAt)).FirstOrDefault()?.value ?? 0f; num2 = value.Select((GoalProgressEntry entry) => Mathf.Max(entry.best, entry.value)).DefaultIfEmpty(0f).Max(); } else { num = (num2 = (IsCompleted(text) ? 1f : 0f)); } float num3 = Mathf.Max(goal?.progressTarget ?? 0f, value.Select((GoalProgressEntry entry) => entry.target).DefaultIfEmpty(0f).Max()); num3 = Mathf.Max(1f, num3); if (IsCompleted(text)) { num2 = Mathf.Max(num2, num3); } return "\nAktueller Wert: " + Mathf.RoundToInt(IsCompleted(text) ? num3 : num) + " / " + Mathf.RoundToInt(num3) + text2 + " | Persönlicher Bestwert: " + Mathf.RoundToInt(num2) + " / " + Mathf.RoundToInt(num3) + text2; } private static DateTime ReadableTimestamp(string value) { if (!DateTime.TryParse(value, out var result)) { return DateTime.MinValue; } return result.ToUniversalTime(); } private static string Short(string value, int max) { if (!string.IsNullOrWhiteSpace(value)) { if (value.Length > max) { return value.Substring(0, max - 1) + "..."; } return value; } return "Beschreibung in ChallengeHub ansehen."; } private void OnDestroy() { if (_visible) { SetVisible(value: false); } if ((Object)(object)_instance == (Object)(object)this) { _instance = null; } } } internal static class GuardianHoverFeature { internal static ConfigEntry EnableGuardianHoverText; internal static ConfigEntry ShowControls; internal static ConfigEntry ShowFarmerAnimals; internal static ConfigEntry ShowFarmerFood; internal static ConfigEntry ShowFarmerChest; internal static ConfigEntry HoverRefreshSeconds; private static bool patched; internal static void Initialize(Plugin plugin) { if (!((Object)(object)plugin == (Object)null)) { EnableGuardianHoverText = ((BaseUnityPlugin)plugin).Config.Bind("GuardianHover", "EnableGuardianHoverText", true, "Wenn true, zeigen ChallengeHub-Waechtersteine beim Anschauen einen Hover-Text mit Funktion, Radius, Kistenstatus und Farmer-Infos."); ShowControls = ((BaseUnityPlugin)plugin).Config.Bind("GuardianHover", "ShowControls", true, "Wenn true, werden passende Hotkeys wie STRG+K im Waechter-Hover angezeigt."); ShowFarmerAnimals = ((BaseUnityPlugin)plugin).Config.Bind("GuardianHover", "ShowFarmerAnimals", true, "Wenn true, zeigt der Bauer-Waechter im Hover, welche Tiere im Radius erkannt werden."); ShowFarmerFood = ((BaseUnityPlugin)plugin).Config.Bind("GuardianHover", "ShowFarmerFood", true, "Wenn true, zeigt der Bauer-Waechter im Hover die verfuegbare Futtermenge aus Boden/Kiste."); ShowFarmerChest = ((BaseUnityPlugin)plugin).Config.Bind("GuardianHover", "ShowFarmerChest", true, "Wenn true, zeigt der Bauer-Waechter im Hover, ob eine Futterkiste gebunden ist."); HoverRefreshSeconds = ((BaseUnityPlugin)plugin).Config.Bind("GuardianHover", "HoverRefreshSeconds", 1f, "Mindestabstand fuer aufwendige Hover-Aktualisierungen wie Tier-/Futterscan."); ServerSyncBridge.AddSynced(EnableGuardianHoverText, synchronized: true); ServerSyncBridge.AddSynced(ShowControls, synchronized: true); ServerSyncBridge.AddSynced(ShowFarmerAnimals, synchronized: true); ServerSyncBridge.AddSynced(ShowFarmerFood, synchronized: true); ServerSyncBridge.AddSynced(ShowFarmerChest, synchronized: true); ServerSyncBridge.AddSynced(HoverRefreshSeconds, synchronized: true); } } internal static bool IsEnabled() { if (EnableGuardianHoverText != null) { return EnableGuardianHoverText.Value; } return true; } internal static void EnsureComponent(GameObject prefab) { if (!((Object)(object)prefab == (Object)null) && (Object)(object)prefab.GetComponent() == (Object)null) { prefab.AddComponent(); } } internal static void Patch(Harmony harmony) { if (!patched && harmony != null) { patched = true; TryPatchHoverType(harmony, typeof(Piece), "Piece"); Type type = AccessTools.TypeByName("PrivateArea"); if (type != null) { TryPatchHoverType(harmony, type, "PrivateArea"); } Type type2 = AccessTools.TypeByName("Sign"); if (type2 != null) { TryPatchHoverType(harmony, type2, "Sign"); } } } private static void TryPatchHoverType(Harmony harmony, Type type, string label) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown try { MethodInfo methodInfo = FindNoArgStringMethod(type, "GetHoverText"); MethodInfo methodInfo2 = FindNoArgStringMethod(type, "GetHoverName"); MethodInfo methodInfo3 = AccessTools.Method(typeof(GuardianHoverFeature), "GetHoverTextPostfix", (Type[])null, (Type[])null); MethodInfo methodInfo4 = AccessTools.Method(typeof(GuardianHoverFeature), "GetHoverNamePostfix", (Type[])null, (Type[])null); if (methodInfo != null && methodInfo.GetParameters().Length == 0 && methodInfo.ReturnType == typeof(string)) { harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo3), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } if (methodInfo2 != null && methodInfo2.GetParameters().Length == 0 && methodInfo2.ReturnType == typeof(string)) { harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(methodInfo4), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("ChallengeHub Guardian-Hover-Patch geprueft: " + label)); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogDebug((object)("ChallengeHub Guardian-Hover-Patch fuer " + label + " uebersprungen: " + ex.Message)); } } } private static MethodInfo FindNoArgStringMethod(Type type, string methodName) { if (type == null || string.IsNullOrWhiteSpace(methodName)) { return null; } try { return type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); } catch { return null; } } private static void GetHoverTextPostfix(object __instance, ref string __result) { try { if (IsEnabled() && !((Object)(object)Plugin.Instance == (Object)null)) { Component val = (Component)((__instance is Component) ? __instance : null); Piece val2 = (Piece)(((Object)(object)val != (Object)null) ? val.GetComponentInParent() : ((__instance is Piece) ? __instance : null)); if (!((Object)(object)val2 == (Object)null) && Plugin.Instance.IsChallengeHubGuardianPieceForHover(val2)) { __result = Plugin.Instance.BuildChallengeHubGuardianHoverText(val2); } } } catch { } } private static void GetHoverNamePostfix(object __instance, ref string __result) { try { if (IsEnabled() && !((Object)(object)Plugin.Instance == (Object)null)) { Component val = (Component)((__instance is Component) ? __instance : null); Piece val2 = (Piece)(((Object)(object)val != (Object)null) ? val.GetComponentInParent() : ((__instance is Piece) ? __instance : null)); if (!((Object)(object)val2 == (Object)null) && Plugin.Instance.IsChallengeHubGuardianPieceForHover(val2)) { __result = Plugin.Instance.BuildChallengeHubGuardianHoverName(val2); } } } catch { } } } internal sealed class ChallengeHubGuardianHoverText : MonoBehaviour, Hoverable { public string GetHoverText() { try { Piece componentInParent = ((Component)this).GetComponentInParent(); return ((Object)(object)Plugin.Instance != (Object)null) ? Plugin.Instance.BuildChallengeHubGuardianHoverText(componentInParent) : "ChallengeHub-Waechterstein"; } catch { return "ChallengeHub-Waechterstein"; } } public string GetHoverName() { try { Piece componentInParent = ((Component)this).GetComponentInParent(); return ((Object)(object)Plugin.Instance != (Object)null) ? Plugin.Instance.BuildChallengeHubGuardianHoverName(componentInParent) : "ChallengeHub-Waechterstein"; } catch { return "ChallengeHub-Waechterstein"; } } } internal sealed class ChallengeHubGuardianStoneMarker : MonoBehaviour { public string GuardianType = "neutral"; private bool reportedRemoved; private void Awake() { TryWriteTypeToZdo("awake"); } private void Start() { TryWriteTypeToZdo("start"); Piece component = ((Component)this).GetComponent(); if ((Object)(object)component != (Object)null) { Plugin.Instance?.ReportGuardianStoneChanged(component, "guardian_piece_start"); } } private void OnEnable() { TryWriteTypeToZdo("enable"); } private void OnDestroy() { //IL_005d: Unknown result type (might be due to invalid IL or missing references) if (reportedRemoved) { return; } reportedRemoved = true; try { Plugin.Instance?.UnregisterGuardianPieceRuntime(((Component)this).GetComponent()); ZNetView component = ((Component)this).GetComponent(); if (!((Object)(object)component == (Object)null) && component.GetZDO() != null) { Plugin.Instance?.ReportGuardianStoneRemoved(((Object)this).name, GuardianType, ((Component)this).transform.position, "guardian_piece_destroyed"); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Wächterstein-OnDestroy-Meldung fehlgeschlagen: " + ex.Message)); } } } private void TryWriteTypeToZdo(string reason) { try { if (!string.IsNullOrWhiteSpace(GuardianType)) { ZNetView component = ((Component)this).GetComponent(); if (!((Object)(object)component == (Object)null) && component.GetZDO() != null) { component.GetZDO().Set("ChallengeHub.GuardianType", GuardianType); component.GetZDO().Set("ChallengeHub.GuardianPiece", ((Object)this).name ?? string.Empty); component.GetZDO().Set("ChallengeHub.GuardianUpdatedUtc", DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Wächterstein-ZDO konnte noch nicht gesetzt werden (" + reason + "): " + ex.Message)); } } } } internal sealed class GuardianStoneDefinition { public string Type; public string PrefabName; public string DisplayName; public string Description; public Color Color; } internal static class GuardianStonePieces { private static readonly List Definitions = new List { new GuardianStoneDefinition { Type = "farmer", PrefabName = "piece_challengehub_guardian_farmer", DisplayName = "Bauer-Wächterstein", Description = "Grüner Wächterstein: Farm, Versorgung und Zähmen. Hält die Zone aktiv und schützt sie vor Reset.", Color = new Color(0.25f, 1f, 0.35f, 1f) }, new GuardianStoneDefinition { Type = "explorer", PrefabName = "piece_challengehub_guardian_explorer", DisplayName = "Entdecker-Wächterstein", Description = "Blauer Wächterstein: Route, Orientierung und entdeckte Orte. Markiert die Zone für ChallengeHub.", Color = new Color(0.25f, 0.55f, 1f, 1f) }, new GuardianStoneDefinition { Type = "combat", PrefabName = "piece_challengehub_guardian_combat", DisplayName = "Kampf-Wächterstein", Description = "Roter Wächterstein: Schutz- und Kampfzone. Für Boss-/Gefahrenbereiche und Arena-Vorbereitung.", Color = new Color(1f, 0.2f, 0.15f, 1f) }, new GuardianStoneDefinition { Type = "builder", PrefabName = "piece_challengehub_guardian_builder", DisplayName = "Aufbau-Wächterstein", Description = "Gelber Wächterstein: Aufbau, Handwerk, Basis und Werkstattzone. Schützt Bauprojekte vor Reset.", Color = new Color(1f, 0.78f, 0.2f, 1f) }, new GuardianStoneDefinition { Type = "neutral", PrefabName = "piece_challengehub_guardian_neutral", DisplayName = "Neutraler Wächterstein", Description = "Weißer Wächterstein: allgemeiner Zonenanker und Schutzbereich.", Color = new Color(0.92f, 0.92f, 1f, 1f) }, new GuardianStoneDefinition { Type = "reset", PrefabName = "piece_challengehub_guardian_reset", DisplayName = "Reset-Wächterstein", Description = "Lila Einweg-Resetstein ohne Schutzfunktion. Nach erfolgreicher Auslösung wird er grau und dauerhaft deaktiviert. Resetbereich fest auf maximal 30 m begrenzt.", Color = new Color(0.7f, 0.3f, 1f, 1f) } }; private static readonly Dictionary RegisteredPrefabs = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary RegisteredStationPrefabs = new Dictionary(StringComparer.OrdinalIgnoreCase); private static ZNetScene registeredScene; private static ObjectDB registeredObjectDb; private static GameObject persistentPrefabRoot; private static bool addedToHammer; private static string lastRegistrationProblem = string.Empty; private static string lastSuccessfulSource = string.Empty; internal static bool IsRegistrationComplete() { if (addedToHammer && RegisteredPrefabs.Count >= Definitions.Count) { return RegisteredStationPrefabs.Count >= CommunityWorldStationFeature.Definitions.Length; } return false; } internal static Color GetGuardianColor(string guardianType) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) return (Color)(((??)Definitions.FirstOrDefault((GuardianStoneDefinition item) => string.Equals(item.Type, guardianType, StringComparison.OrdinalIgnoreCase))?.Color) ?? new Color(0.92f, 0.92f, 1f, 1f)); } internal static bool IsCustomGuardianPrefab(string prefabName) { string key = Normalize(prefabName); return Definitions.Any((GuardianStoneDefinition definition) => Normalize(definition.PrefabName) == key); } internal static void TryRegisterAll(string reason) { try { if (Plugin.AreCustomGuardianStonePiecesEnabled()) { ZNetScene instance = ZNetScene.instance; ObjectDB instance2 = ObjectDB.instance; if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance2 == (Object)null)) { TryRegisterPieces(instance, instance2, reason); } } } catch (Exception ex) { LogRegistrationProblem("Wächterstein-Registrierung fehlgeschlagen: " + ex); } } internal static void RegisterZNetScene(ZNetScene scene, string reason) { if (!((Object)(object)scene == (Object)null) && Plugin.AreCustomGuardianStonePiecesEnabled() && (Object)(object)ObjectDB.instance != (Object)null) { TryRegisterPieces(scene, ObjectDB.instance, reason); } } internal static void RegisterObjectDb(ObjectDB objectDb, string reason) { if (!((Object)(object)objectDb == (Object)null) && Plugin.AreCustomGuardianStonePiecesEnabled() && (Object)(object)ZNetScene.instance != (Object)null) { TryRegisterPieces(ZNetScene.instance, objectDb, reason); } } private static void TryRegisterPieces(ZNetScene scene, ObjectDB objectDb, string reason) { if ((Object)(object)scene == (Object)null || (Object)(object)objectDb == (Object)null) { return; } if (registeredScene != scene) { registeredScene = scene; RegisteredPrefabs.Clear(); RegisteredStationPrefabs.Clear(); addedToHammer = false; lastRegistrationProblem = string.Empty; } if (registeredObjectDb != objectDb) { registeredObjectDb = objectDb; addedToHammer = false; } PieceTable val = FindHammerPieceTable(objectDb); SanitizePieceTable(val, "vor der Wächterstein-Registrierung"); SanitizeScenePrefabLists(scene); GameObject val2 = FindSourcePrefab(scene, val); if (!IsUsableBuildSource(val2)) { LogMissingSource(scene, val); return; } bool flag = false; foreach (GuardianStoneDefinition definition in Definitions) { GameObject val3 = FindRegisteredPrefab(scene, definition.PrefabName); if (!IsUsableGuardianPrefab(val3, definition)) { RemoveStaleSceneEntries(scene, definition.PrefabName, null); val3 = CreateGuardianPrefab(val2, definition); RegisterNetworkPrefab(scene, val3); flag = true; } else { ConfigurePrefab(val3, definition, objectDb); RegisterNetworkPrefab(scene, val3); } RegisteredPrefabs[definition.PrefabName] = val3; } CommunityWorldStationDefinition[] definitions = CommunityWorldStationFeature.Definitions; foreach (CommunityWorldStationDefinition communityWorldStationDefinition in definitions) { GameObject val4 = FindRegisteredPrefab(scene, communityWorldStationDefinition.PrefabName); ChallengeHubWorldStationMarker challengeHubWorldStationMarker = (((Object)(object)val4 != (Object)null) ? val4.GetComponent() : null); if (!IsUsableBuildSource(val4) || (Object)(object)challengeHubWorldStationMarker == (Object)null || !string.Equals(challengeHubWorldStationMarker.StationType, communityWorldStationDefinition.Type, StringComparison.OrdinalIgnoreCase)) { RemoveStaleSceneEntries(scene, communityWorldStationDefinition.PrefabName, null); val4 = CreateWorldStationPrefab(val2, communityWorldStationDefinition, objectDb); RegisterNetworkPrefab(scene, val4); flag = true; } else { RegisterNetworkPrefab(scene, val4); } RegisteredStationPrefabs[communityWorldStationDefinition.PrefabName] = val4; } if ((Object)(object)val == (Object)null || val.m_pieces == null) { LogRegistrationProblem("Hammer-Baumenü ist noch nicht verfügbar. ChallengeHub versucht die Wächterstein-Registrierung automatisch erneut."); return; } SanitizePieceTable(val, "direkt vor dem Hinzufügen der Wächtersteine"); RemoveStaleHammerEntries(val); bool flag2 = false; foreach (GuardianStoneDefinition definition2 in Definitions) { if (RegisteredPrefabs.TryGetValue(definition2.PrefabName, out var value) && IsUsableGuardianPrefab(value, definition2)) { ConfigurePrefab(value, definition2, objectDb); if (!val.m_pieces.Contains(value)) { val.m_pieces.Add(value); flag2 = true; } } } definitions = CommunityWorldStationFeature.Definitions; foreach (CommunityWorldStationDefinition communityWorldStationDefinition2 in definitions) { if (RegisteredStationPrefabs.TryGetValue(communityWorldStationDefinition2.PrefabName, out var value2) && IsUsableBuildSource(value2) && !val.m_pieces.Contains(value2)) { val.m_pieces.Add(value2); flag2 = true; } } if (!addedToHammer || flag2 || flag || !string.Equals(lastSuccessfulSource, ((Object)val2).name, StringComparison.Ordinal)) { addedToHammer = true; lastRegistrationProblem = string.Empty; lastSuccessfulSource = ((Object)val2).name; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("ChallengeHub-Wächtersteine registriert: Quelle=" + ((Object)val2).name + ", Auslöser=" + reason + ". Standard ist dverger_guardstone; Fallbacks bleiben aktiv. Falls das Hammer-Menü schon offen war, Hammer einmal wegstecken und neu ausrüsten.")); } } } private static GameObject FindSourcePrefab(ZNetScene scene, PieceTable pieceTable) { string configuredName = Plugin.GuardianSourcePiecePrefabFromConfig(); List source = (from prefab in EnumerateBuildCandidates(scene, pieceTable).Where(IsUsableBuildSource) orderby prefab.activeInHierarchy ? 1 : 0 select prefab).ThenBy((GameObject prefab) => ((Object)prefab).name, StringComparer.OrdinalIgnoreCase).ToList(); if (!string.IsNullOrWhiteSpace(configuredName)) { GameObject val = ((IEnumerable)source).FirstOrDefault((Func)((GameObject prefab) => string.Equals(((Object)prefab).name, configuredName, StringComparison.OrdinalIgnoreCase))); if (!IsUsableBuildSource(val)) { val = FindRegisteredPrefab(scene, configuredName); } if (IsUsableBuildSource(val)) { return val; } } string[] array = new string[11] { "dverger_guardstone", "piece_dverger_guardstone", "guard_stone", "piece_guardstone", "piece_ward", "ward", "piece_sign", "sign", "piece_sign_wall", "piece_sign_pole", "piece_sign_notext" }; foreach (string preferredName in array) { GameObject val2 = ((IEnumerable)source).FirstOrDefault((Func)((GameObject prefab) => string.Equals(((Object)prefab).name, preferredName, StringComparison.OrdinalIgnoreCase))); if (!IsUsableBuildSource(val2)) { val2 = FindRegisteredPrefab(scene, preferredName); } if (!IsUsableBuildSource(val2)) { continue; } if (!string.IsNullOrWhiteSpace(configuredName) && !string.Equals(configuredName, preferredName, StringComparison.OrdinalIgnoreCase)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Konfigurierte Wächterstein-Vorlage '" + configuredName + "' wurde nicht gefunden. Verwende Vorlage '" + ((Object)val2).name + "'.")); } } return val2; } GameObject val3 = ((IEnumerable)source).FirstOrDefault((Func)((GameObject prefab) => ((Object)prefab).name.IndexOf("dverger", StringComparison.OrdinalIgnoreCase) >= 0 && ((Object)prefab).name.IndexOf("guard", StringComparison.OrdinalIgnoreCase) >= 0)); if ((Object)(object)val3 != (Object)null) { return val3; } val3 = ((IEnumerable)source).FirstOrDefault((Func)((GameObject prefab) => ((Object)prefab).name.IndexOf("ward", StringComparison.OrdinalIgnoreCase) >= 0 || ((Object)prefab).name.IndexOf("guard", StringComparison.OrdinalIgnoreCase) >= 0)); if ((Object)(object)val3 != (Object)null) { return val3; } return ((IEnumerable)source).FirstOrDefault((Func)((GameObject prefab) => ((Object)prefab).name.IndexOf("sign", StringComparison.OrdinalIgnoreCase) >= 0)); } private static GameObject CreateGuardianPrefab(GameObject source, GuardianStoneDefinition definition) { if (!IsUsableBuildSource(source)) { throw new InvalidOperationException("Die gewählte Wächterstein-Vorlage ist kein gültiges Netzwerk-Bauteil: " + (((Object)(object)source != (Object)null) ? ((Object)source).name : "")); } GameObject val = null; bool activeSelf = source.activeSelf; Transform val2 = GetPersistentPrefabRoot(); try { if (activeSelf) { source.SetActive(false); } val = Object.Instantiate(source, val2, false); ((Object)val).name = definition.PrefabName; Sign[] componentsInChildren = val.GetComponentsInChildren(true); foreach (Sign val3 in componentsInChildren) { if ((Object)(object)val3 != (Object)null) { Object.DestroyImmediate((Object)(object)val3); } } TeleportWorld[] componentsInChildren2 = val.GetComponentsInChildren(true); foreach (TeleportWorld val4 in componentsInChildren2) { if ((Object)(object)val4 != (Object)null) { Object.DestroyImmediate((Object)(object)val4); } } AddGuardianBeacon(val, definition); ConfigurePrefab(val, definition, ObjectDB.instance); val.SetActive(true); return val; } catch { if ((Object)(object)val != (Object)null) { Object.DestroyImmediate((Object)(object)val); } throw; } finally { if ((Object)(object)source != (Object)null && activeSelf) { source.SetActive(true); } } } private static void ConfigurePrefab(GameObject prefab, GuardianStoneDefinition definition, ObjectDB objectDb) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)prefab == (Object)null || definition == null) { return; } Piece component = prefab.GetComponent(); if ((Object)(object)component != (Object)null) { component.m_name = definition.DisplayName; component.m_description = definition.Description + " Wird beim Platzieren sofort an ChallengeHub gemeldet."; try { component.m_category = (PieceCategory)0; } catch { } SetBooleanFieldIfExists(component, "m_canBeRemoved", value: true); Requirement[] array = BuildRequirements(Plugin.GuardianRecipeFromConfig(definition.Type), objectDb); if (array.Length != 0) { component.m_resources = array; } } ChallengeHubGuardianStoneMarker challengeHubGuardianStoneMarker = prefab.GetComponent(); if ((Object)(object)challengeHubGuardianStoneMarker == (Object)null) { challengeHubGuardianStoneMarker = prefab.AddComponent(); } challengeHubGuardianStoneMarker.GuardianType = definition.Type; GuardianHoverFeature.EnsureComponent(prefab); if ((Object)(object)prefab.GetComponent() == (Object)null) { prefab.AddComponent(); } if (string.Equals(definition.Type, "reset", StringComparison.OrdinalIgnoreCase)) { RemoveComponentsByTypeName(prefab, "PrivateArea"); if ((Object)(object)prefab.GetComponent() == (Object)null) { prefab.AddComponent(); } } ApplyColor(prefab, definition.Color); } private static GameObject CreateWorldStationPrefab(GameObject source, CommunityWorldStationDefinition station, ObjectDB objectDb) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) GuardianStoneDefinition definition = new GuardianStoneDefinition { Type = "neutral", PrefabName = station.PrefabName, DisplayName = station.DisplayName, Description = station.Description, Color = station.Color }; GameObject val = CreateGuardianPrefab(source, definition); ChallengeHubGuardianStoneMarker[] components = val.GetComponents(); for (int i = 0; i < components.Length; i++) { Object.DestroyImmediate((Object)(object)components[i]); } ChallengeHubGuardianHoverText[] components2 = val.GetComponents(); for (int i = 0; i < components2.Length; i++) { Object.DestroyImmediate((Object)(object)components2[i]); } GuardianZoneRadiusVisual[] components3 = val.GetComponents(); for (int i = 0; i < components3.Length; i++) { Object.DestroyImmediate((Object)(object)components3[i]); } RemoveComponentsByTypeName(val, "PrivateArea"); ChallengeHubWorldStationMarker challengeHubWorldStationMarker = val.GetComponent(); if ((Object)(object)challengeHubWorldStationMarker == (Object)null) { challengeHubWorldStationMarker = val.AddComponent(); } challengeHubWorldStationMarker.StationType = station.Type; challengeHubWorldStationMarker.SystemKey = station.SystemKey; Piece component = val.GetComponent(); if ((Object)(object)component != (Object)null) { component.m_name = station.DisplayName; component.m_description = station.Description + " Die Station gehört zu den neuen Community-Weltsystemen und erzeugt keine Schutzzone."; Requirement[] array = BuildRequirements("Wood:10,Stone:5,Resin:2", objectDb); if (array.Length != 0) { component.m_resources = array; } } ApplyColor(val, station.Color); return val; } private static void AddGuardianBeacon(GameObject root, GuardianStoneDefinition definition) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)root == (Object)null || (Object)(object)root.transform.Find("ChallengeHubGuardianBeacon") != (Object)null) { return; } GameObject obj = GameObject.CreatePrimitive((PrimitiveType)0); ((Object)obj).name = "ChallengeHubGuardianBeacon"; obj.transform.SetParent(root.transform, false); obj.transform.localPosition = new Vector3(0f, 0.65f, -0.08f); obj.transform.localScale = new Vector3(0.16f, 0.16f, 0.16f); Collider component = obj.GetComponent(); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); } Renderer component2 = obj.GetComponent(); if ((Object)(object)component2 != (Object)null && (Object)(object)component2.sharedMaterial != (Object)null) { Material val = new Material(component2.sharedMaterial); val.color = definition.Color; if (val.HasProperty("_EmissionColor")) { val.EnableKeyword("_EMISSION"); val.SetColor("_EmissionColor", definition.Color * 1.2f); } component2.sharedMaterial = val; } Light obj2 = obj.AddComponent(); obj2.type = (LightType)2; obj2.range = 1.2f; obj2.intensity = 0.7f; obj2.color = definition.Color; } private static Requirement[] BuildRequirements(string recipe, ObjectDB objectDb) { //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: 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_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Expected O, but got Unknown List list = new List(); if ((Object)(object)objectDb == (Object)null) { return list.ToArray(); } string[] array = (recipe ?? string.Empty).Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { ':' }); if (array2.Length < 2) { continue; } string text = array2[0].Trim(); if (string.IsNullOrWhiteSpace(text) || !int.TryParse(array2[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result <= 0) { continue; } GameObject itemPrefab = GetItemPrefab(objectDb, text); ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent() : null); if ((Object)(object)val == (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Wächterstein-Rezeptmaterial nicht gefunden: " + text + ". Eintrag wird übersprungen.")); } } else { list.Add(new Requirement { m_resItem = val, m_amount = result, m_amountPerLevel = 0, m_recover = true }); } } return list.ToArray(); } private static PieceTable FindHammerPieceTable(ObjectDB objectDb) { if ((Object)(object)objectDb == (Object)null) { return null; } string[] array = new string[2] { "Hammer", "hammer" }; foreach (string itemName in array) { GameObject itemPrefab = GetItemPrefab(objectDb, itemName); ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent() : null); PieceTable val2 = (((Object)(object)val != (Object)null && val.m_itemData != null && val.m_itemData.m_shared != null) ? val.m_itemData.m_shared.m_buildPieces : null); if ((Object)(object)val2 != (Object)null) { return val2; } } return null; } private static GameObject GetItemPrefab(ObjectDB objectDb, string itemName) { if ((Object)(object)objectDb == (Object)null || string.IsNullOrWhiteSpace(itemName)) { return null; } try { GameObject itemPrefab = objectDb.GetItemPrefab(itemName.Trim()); if ((Object)(object)itemPrefab != (Object)null) { return itemPrefab; } } catch { } List fieldValue = GetFieldValue>(objectDb, "m_items"); string normalized = Normalize(itemName); return fieldValue?.FirstOrDefault((Func)((GameObject item) => Normalize(((Object)(object)item != (Object)null) ? ((Object)item).name : string.Empty) == normalized)); } private static List EnumerateBuildCandidates(ZNetScene scene, PieceTable pieceTable) { List result = new List(); Action action = delegate(GameObject prefab) { if ((Object)(object)prefab != (Object)null && !result.Contains(prefab)) { result.Add(prefab); } }; if ((Object)(object)pieceTable != (Object)null && pieceTable.m_pieces != null) { foreach (GameObject piece in pieceTable.m_pieces) { action(piece); } } if ((Object)(object)scene != (Object)null) { List fieldValue = GetFieldValue>(scene, "m_prefabs"); if (fieldValue != null) { foreach (GameObject item in fieldValue) { action(item); } } List fieldValue2 = GetFieldValue>(scene, "m_nonNetViewPrefabs"); if (fieldValue2 != null) { foreach (GameObject item2 in fieldValue2) { action(item2); } } } return result; } private static bool IsUsableBuildSource(GameObject prefab) { if ((Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponent() != (Object)null) { return (Object)(object)prefab.GetComponent() != (Object)null; } return false; } private static bool IsUsableGuardianPrefab(GameObject prefab, GuardianStoneDefinition definition) { if (!IsUsableBuildSource(prefab) || definition == null) { return false; } if (!string.Equals(((Object)prefab).name, definition.PrefabName, StringComparison.Ordinal)) { return false; } ChallengeHubGuardianStoneMarker component = prefab.GetComponent(); if ((Object)(object)component != (Object)null) { return string.Equals(Normalize(component.GuardianType), Normalize(definition.Type), StringComparison.Ordinal); } return false; } private static Transform GetPersistentPrefabRoot() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown if ((Object)(object)persistentPrefabRoot == (Object)null) { persistentPrefabRoot = new GameObject("ChallengeHub_GuardianStone_PrefabRoot"); Object.DontDestroyOnLoad((Object)(object)persistentPrefabRoot); persistentPrefabRoot.SetActive(false); } return persistentPrefabRoot.transform; } private static void RegisterNetworkPrefab(ZNetScene scene, GameObject prefab) { if ((Object)(object)scene == (Object)null || (Object)(object)prefab == (Object)null) { return; } if (FindRegisteredPrefab(scene, ((Object)prefab).name) != prefab) { if ((Object)(object)prefab.GetComponent() != (Object)null) { List fieldValue = GetFieldValue>(scene, "m_prefabs"); if (fieldValue != null && !fieldValue.Contains(prefab)) { fieldValue.Add(prefab); } } else { List fieldValue2 = GetFieldValue>(scene, "m_nonNetViewPrefabs"); if (fieldValue2 != null && !fieldValue2.Contains(prefab)) { fieldValue2.Add(prefab); } } } IDictionary namedPrefabMap = GetNamedPrefabMap(scene); if (namedPrefabMap != null) { namedPrefabMap[StableHash(((Object)prefab).name)] = prefab; return; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Interner ZNetScene-Prefabindex wurde nicht gefunden. Wächterstein wurde zur Prefab-Liste hinzugefügt, Netzwerk-Spawns können aber fehlschlagen."); } } private static GameObject FindRegisteredPrefab(ZNetScene scene, string prefabName) { if ((Object)(object)scene == (Object)null || string.IsNullOrWhiteSpace(prefabName)) { return null; } try { GameObject prefab = scene.GetPrefab(prefabName); if ((Object)(object)prefab != (Object)null) { return prefab; } } catch { } string normalized = Normalize(prefabName); List fieldValue = GetFieldValue>(scene, "m_prefabs"); if (fieldValue != null) { GameObject val = ((IEnumerable)fieldValue).FirstOrDefault((Func)((GameObject val3) => Normalize(((Object)(object)val3 != (Object)null) ? ((Object)val3).name : string.Empty) == normalized)); if ((Object)(object)val != (Object)null) { return val; } } List fieldValue2 = GetFieldValue>(scene, "m_nonNetViewPrefabs"); if (fieldValue2 != null) { GameObject val2 = ((IEnumerable)fieldValue2).FirstOrDefault((Func)((GameObject val3) => Normalize(((Object)(object)val3 != (Object)null) ? ((Object)val3).name : string.Empty) == normalized)); if ((Object)(object)val2 != (Object)null) { return val2; } } IDictionary namedPrefabMap = GetNamedPrefabMap(scene); if (namedPrefabMap != null && namedPrefabMap.TryGetValue(StableHash(prefabName), out var value)) { return value; } return null; } private static IDictionary GetNamedPrefabMap(ZNetScene scene) { if ((Object)(object)scene == (Object)null) { return null; } Type typeFromHandle = typeof(ZNetScene); FieldInfo field = typeFromHandle.GetField("m_namedPrefabs", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.GetValue(scene) is IDictionary result) { return result; } FieldInfo[] fields = typeFromHandle.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (typeof(IDictionary).IsAssignableFrom(fieldInfo.FieldType) && fieldInfo.GetValue(scene) is IDictionary result2) { return result2; } } return null; } private static void SanitizePieceTable(PieceTable pieceTable, string reason) { if ((Object)(object)pieceTable == (Object)null || pieceTable.m_pieces == null) { return; } int num = 0; for (int num2 = pieceTable.m_pieces.Count - 1; num2 >= 0; num2--) { if ((Object)(object)pieceTable.m_pieces[num2] == (Object)null) { pieceTable.m_pieces.RemoveAt(num2); num++; } } if (num > 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Aus der Hammer-PieceTable wurden " + num + " zerstörte/null Prefab-Einträge entfernt (" + reason + ").")); } } } private static void SanitizeScenePrefabLists(ZNetScene scene) { if ((Object)(object)scene == (Object)null) { return; } int num = 0; num += RemoveDestroyedEntries(GetFieldValue>(scene, "m_prefabs")); num += RemoveDestroyedEntries(GetFieldValue>(scene, "m_nonNetViewPrefabs")); IDictionary namedPrefabMap = GetNamedPrefabMap(scene); if (namedPrefabMap != null) { int[] array = (from pair in namedPrefabMap where (Object)(object)pair.Value == (Object)null select pair.Key).ToArray(); foreach (int key in array) { namedPrefabMap.Remove(key); num++; } } if (num > 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Aus ZNetScene wurden " + num + " zerstörte/null Prefab-Referenzen entfernt.")); } } } private static int RemoveDestroyedEntries(List prefabs) { if (prefabs == null) { return 0; } int num = 0; for (int num2 = prefabs.Count - 1; num2 >= 0; num2--) { if ((Object)(object)prefabs[num2] == (Object)null) { prefabs.RemoveAt(num2); num++; } } return num; } private static void RemoveStaleSceneEntries(ZNetScene scene, string prefabName, GameObject keep) { if ((Object)(object)scene == (Object)null || string.IsNullOrWhiteSpace(prefabName)) { return; } RemoveStaleFromList(GetFieldValue>(scene, "m_prefabs"), prefabName, keep); RemoveStaleFromList(GetFieldValue>(scene, "m_nonNetViewPrefabs"), prefabName, keep); IDictionary namedPrefabMap = GetNamedPrefabMap(scene); if (namedPrefabMap != null) { int key = StableHash(prefabName); if (namedPrefabMap.TryGetValue(key, out var value) && value != keep) { namedPrefabMap.Remove(key); } } } private static void RemoveStaleFromList(List prefabs, string prefabName, GameObject keep) { if (prefabs == null) { return; } for (int num = prefabs.Count - 1; num >= 0; num--) { GameObject val = prefabs[num]; if ((Object)(object)val == (Object)null || (string.Equals(((Object)val).name, prefabName, StringComparison.Ordinal) && val != keep)) { prefabs.RemoveAt(num); } } } private static void RemoveStaleHammerEntries(PieceTable pieceTable) { if ((Object)(object)pieceTable == (Object)null || pieceTable.m_pieces == null) { return; } HashSet hashSet = new HashSet(Definitions.Select((GuardianStoneDefinition definition) => definition.PrefabName).Concat(CommunityWorldStationFeature.Definitions.Select((CommunityWorldStationDefinition definition) => definition.PrefabName)), StringComparer.Ordinal); for (int num = pieceTable.m_pieces.Count - 1; num >= 0; num--) { GameObject val = pieceTable.m_pieces[num]; if ((Object)(object)val == (Object)null) { pieceTable.m_pieces.RemoveAt(num); } else if (hashSet.Contains(((Object)val).name)) { GameObject value; bool num2 = RegisteredPrefabs.TryGetValue(((Object)val).name, out value) && val == value; GameObject value2; bool flag = RegisteredStationPrefabs.TryGetValue(((Object)val).name, out value2) && val == value2; if (!num2 && !flag) { pieceTable.m_pieces.RemoveAt(num); } } } } private static void ApplyColor(GameObject prefab, Color color) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) try { Renderer[] componentsInChildren = prefab.GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } Material[] materials = val.materials; foreach (Material val2 in materials) { if (!((Object)(object)val2 == (Object)null)) { if (val2.HasProperty("_Color")) { val2.color = Color.Lerp(val2.color, color, 0.45f); } if (val2.HasProperty("_EmissionColor")) { val2.EnableKeyword("_EMISSION"); val2.SetColor("_EmissionColor", color * 0.45f); } } } val.materials = materials; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Wächterstein-Farbe konnte nicht gesetzt werden: " + ex.Message)); } } } private static void LogMissingSource(ZNetScene scene, PieceTable pieceTable) { string text = Plugin.GuardianSourcePiecePrefabFromConfig(); string[] array = (from prefab in EnumerateBuildCandidates(scene, pieceTable) where (Object)(object)prefab != (Object)null && (((Object)prefab).name.IndexOf("sign", StringComparison.OrdinalIgnoreCase) >= 0 || ((Object)prefab).name.IndexOf("ward", StringComparison.OrdinalIgnoreCase) >= 0 || ((Object)prefab).name.IndexOf("guard", StringComparison.OrdinalIgnoreCase) >= 0) select ((Object)prefab).name).Distinct(StringComparer.OrdinalIgnoreCase).OrderBy((string name) => name, StringComparer.OrdinalIgnoreCase).Take(20) .ToArray(); string text2 = ((array.Length != 0) ? (" Gefundene Kandidaten: " + string.Join(", ", array)) : " Es wurden keine Dverger-/Ward-/Guard-/Schild-Kandidaten gefunden."); LogRegistrationProblem("Die Bauteil-Vorlage '" + text + "' wurde nicht gefunden; ChallengeHub-Wächtersteine können noch nicht erstellt werden." + text2); } private static void LogRegistrationProblem(string message) { if (!string.Equals(lastRegistrationProblem, message, StringComparison.Ordinal)) { lastRegistrationProblem = message; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)message); } } } private static void RemoveComponentsByTypeName(GameObject root, string typeName) { if ((Object)(object)root == (Object)null || string.IsNullOrWhiteSpace(typeName)) { return; } try { Component[] componentsInChildren = root.GetComponentsInChildren(true); foreach (Component val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !(((object)val).GetType() == null) && string.Equals(((object)val).GetType().Name, typeName, StringComparison.OrdinalIgnoreCase)) { Object.DestroyImmediate((Object)(object)val); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Wächterstein-Prefab: Komponente " + typeName + " konnte nicht entfernt werden: " + ex.Message)); } } } private static void SetBooleanFieldIfExists(object instance, string fieldName, bool value) { if (instance == null || string.IsNullOrWhiteSpace(fieldName)) { return; } try { FieldInfo fieldInfo = AccessTools.Field(instance.GetType(), fieldName); if (fieldInfo != null && fieldInfo.FieldType == typeof(bool)) { fieldInfo.SetValue(instance, value); return; } PropertyInfo propertyInfo = AccessTools.Property(instance.GetType(), fieldName); if (propertyInfo != null && propertyInfo.CanWrite && propertyInfo.PropertyType == typeof(bool)) { propertyInfo.SetValue(instance, value, null); } } catch { } } private static T GetFieldValue(object instance, string fieldName) where T : class { if (instance == null) { return null; } FieldInfo fieldInfo = AccessTools.Field(instance.GetType(), fieldName); if (!(fieldInfo != null)) { return null; } return fieldInfo.GetValue(instance) as T; } private static string Normalize(string value) { return (value ?? string.Empty).ToLowerInvariant().Replace("(clone)", string.Empty).Trim(); } private static int StableHash(string text) { int num = 5381; int num2 = num; text = text ?? string.Empty; for (int i = 0; i < text.Length && text[i] != 0; i += 2) { num = ((num << 5) + num) ^ text[i]; if (i == text.Length - 1 || text[i + 1] == '\0') { break; } num2 = ((num2 << 5) + num2) ^ text[i + 1]; } return num + num2 * 1566083941; } } internal static class GuardianStoneProtectionFeature { internal sealed class IndestructibleGuardianStoneBehaviour : MonoBehaviour { private float _nextRefresh; private void Awake() { RefreshWearNTearHealth(((Component)this).gameObject); } private void Start() { RefreshWearNTearHealth(((Component)this).gameObject); } private void Update() { if (!(Time.realtimeSinceStartup < _nextRefresh)) { _nextRefresh = Time.realtimeSinceStartup + 3f; if (IsIndestructibleGuardian(((Component)this).gameObject)) { RefreshWearNTearHealth(((Component)this).gameObject); } } } } [HarmonyPatch(typeof(WearNTear), "Damage")] private static class WearNTearDamagePatch { private static bool Prefix(WearNTear __instance) { return PreventIfIndestructible(__instance, "damage"); } } [HarmonyPatch(typeof(WearNTear), "RPC_Damage")] private static class WearNTearRpcDamagePatch { private static bool Prefix(WearNTear __instance) { return PreventIfIndestructible(__instance, "rpc_damage"); } } [HarmonyPatch(typeof(Player), "RemovePiece")] private static class PlayerRemovePiecePatch { private static bool Prefix(Player __instance) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) Piece val = FindLookedAtIndestructibleGuardian(__instance); if ((Object)(object)val != (Object)null && GuardianZoneAccessFeature.CanOwnerRemove(__instance, val)) { return true; } if ((Object)(object)val == (Object)null) { Piece val2 = null; try { val2 = (((Object)(object)__instance != (Object)null) ? __instance.GetHoveringPiece() : null); } catch { } if ((Object)(object)val2 == (Object)null) { return true; } if (GuardianZoneAccessFeature.CanBuildAt(__instance, ((Component)val2).transform.position, out var _)) { CommunityWorldStationFeature.ReportRemoved(val2); return true; } ((Character)__instance).Message((MessageType)2, "Keine Abbaurechte in der Schutzzone dieses weissen Waechters.", 0, (Sprite)null); return false; } RefreshWearNTearHealth(((Component)val).gameObject); NotifyPrevented(((Component)val).gameObject, "remove"); return false; } } private static Plugin _plugin; private static bool _initialized; private static ConfigEntry EnableIndestructibleGuardianStones; private static ConfigEntry IndestructibleGuardianTypes; private static ConfigEntry NotifyOnPreventedDamage; private static ConfigEntry GuardianProtectionScanSeconds; private static readonly Dictionary LastNotifyAt = new Dictionary(); internal static void Initialize(Plugin plugin) { if (!_initialized && !((Object)(object)plugin == (Object)null)) { _initialized = true; _plugin = plugin; EnableIndestructibleGuardianStones = ((BaseUnityPlugin)plugin).Config.Bind("GuardianStones", "EnableIndestructibleGuardianStones", true, "Wenn true, koennen konfigurierte Waechterstein-Typen nicht beschaedigt oder mit dem Hammer entfernt werden."); IndestructibleGuardianTypes = ((BaseUnityPlugin)plugin).Config.Bind("GuardianStones", "IndestructibleGuardianTypes", "all", "Kommagetrennte Waechterstein-Typen, die unzerstoerbar sind. Reset-Waechter sind immer ausgenommen und vom Besitzer abbaubar. Alternativ: farmer,explorer,combat,builder,neutral."); NotifyOnPreventedDamage = ((BaseUnityPlugin)plugin).Config.Bind("GuardianStones", "NotifyOnPreventedDamage", true, "Wenn true, zeigt die Mod eine Ingame-Meldung, wenn Schaden/Abbau an einem unzerstoerbaren Waechterstein verhindert wurde."); GuardianProtectionScanSeconds = ((BaseUnityPlugin)plugin).Config.Bind("GuardianStones", "GuardianProtectionScanSeconds", 0f, "Veralteter Kompatibilitaetswert. Seit 2.2.30 wird kein periodischer Vollscan mehr ausgefuehrt; Piece.Awake weist den Schutz sofort zu."); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("ChallengeHub Waechterstein-Schutz 2.2.30 ereignisgesteuert initialisiert: " + IndestructibleGuardianTypes.Value + "; kein periodischer Piece-Vollscan.")); } } } internal static void HandlePieceAwake(Piece piece) { if ((Object)(object)piece == (Object)null) { return; } try { if (IsIndestructibleGuardian(piece)) { EnsureGuardianBehaviour(((Component)piece).gameObject); RefreshWearNTearHealth(((Component)piece).gameObject); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Waechterstein-Schutz bei Piece.Awake fehlgeschlagen: " + ex.Message)); } } } internal static void EnsureGuardianBehaviour(GameObject obj) { if ((Object)(object)obj == (Object)null) { return; } try { if ((Object)(object)obj.GetComponent() == (Object)null) { obj.AddComponent(); } } catch { } } internal static bool IsIndestructibleGuardian(Piece piece) { if (EnableIndestructibleGuardianStones != null && !EnableIndestructibleGuardianStones.Value) { return false; } if ((Object)(object)piece == (Object)null) { return false; } string value = DetectGuardianType(((Component)piece).gameObject); if (string.IsNullOrWhiteSpace(value)) { return false; } if (string.Equals(Normalize(value), "reset", StringComparison.OrdinalIgnoreCase)) { return false; } HashSet hashSet = SplitTypes((IndestructibleGuardianTypes != null) ? IndestructibleGuardianTypes.Value : "all"); if (hashSet.Contains("all") || hashSet.Contains("*") || hashSet.Contains("alle")) { return true; } return hashSet.Contains(Normalize(value)); } internal static bool IsIndestructibleGuardian(GameObject obj) { if ((Object)(object)obj == (Object)null) { return false; } return IsIndestructibleGuardian(obj.GetComponent() ?? obj.GetComponentInParent()); } internal static string DetectGuardianType(GameObject obj) { if ((Object)(object)obj == (Object)null) { return string.Empty; } try { ChallengeHubGuardianStoneMarker challengeHubGuardianStoneMarker = obj.GetComponent() ?? obj.GetComponentInParent(); if ((Object)(object)challengeHubGuardianStoneMarker != (Object)null && !string.IsNullOrWhiteSpace(challengeHubGuardianStoneMarker.GuardianType)) { return NormalizeGuardianType(challengeHubGuardianStoneMarker.GuardianType); } } catch { } try { ZNetView val = obj.GetComponent() ?? obj.GetComponentInParent(); ZDO val2 = (((Object)(object)val != (Object)null) ? val.GetZDO() : null); if (val2 != null) { string value = val2.GetString("ChallengeHub.GuardianType", string.Empty); if (!string.IsNullOrWhiteSpace(value)) { return NormalizeGuardianType(value); } } } catch { } string text = Normalize(((Object)obj).name); if (text.Contains("guardian_farmer") || text.Contains("bauer")) { return "farmer"; } if (text.Contains("guardian_explorer") || text.Contains("entdecker")) { return "explorer"; } if (text.Contains("guardian_combat")) { return "combat"; } if (text.Contains("guardian_builder")) { return "builder"; } if (text.Contains("guardian_neutral")) { return "neutral"; } if (text.Contains("guardian_reset") || text.Contains("reset_guardian")) { return "reset"; } return string.Empty; } internal static void RefreshWearNTearHealth(GameObject obj) { if ((Object)(object)obj == (Object)null) { return; } try { WearNTear val = obj.GetComponent() ?? obj.GetComponentInParent(); if ((Object)(object)val == (Object)null) { return; } SetFieldIfExists(val, "m_health", 1000000f); SetFieldIfExists(val, "m_noRoofWear", true); SetFieldIfExists(val, "m_noSupportWear", true); SetFieldIfExists(val, "m_ashDamageResist", true); ZNetView val2 = obj.GetComponent() ?? obj.GetComponentInParent(); ZDO val3 = (((Object)(object)val2 != (Object)null) ? val2.GetZDO() : null); if (val3 == null) { return; } try { val3.Set("health", 1000000f); } catch { } try { val3.Set("ChallengeHub.GuardianIndestructible", "true"); } catch { } } catch { } } private static void SetFieldIfExists(object target, string fieldName, object value) { try { FieldInfo field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(field == null)) { if (field.FieldType == typeof(float)) { field.SetValue(target, Convert.ToSingle(value)); } else if (field.FieldType == typeof(bool)) { field.SetValue(target, Convert.ToBoolean(value)); } } } catch { } } internal static bool PreventIfIndestructible(WearNTear wear, string action) { if ((Object)(object)wear == (Object)null) { return true; } Piece val = ((Component)wear).GetComponent() ?? ((Component)wear).GetComponentInParent(); if ((Object)(object)val == (Object)null || !IsIndestructibleGuardian(val)) { return true; } RefreshWearNTearHealth(((Component)val).gameObject); NotifyPrevented(((Component)val).gameObject, action); return false; } internal static void NotifyPrevented(GameObject obj, string action) { //IL_0077: 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) if (NotifyOnPreventedDamage != null && !NotifyOnPreventedDamage.Value) { return; } try { int key = (((Object)(object)obj != (Object)null) ? ((Object)obj).GetInstanceID() : 0); if (!LastNotifyAt.TryGetValue(key, out var value) || !(Time.realtimeSinceStartup - value < 2f)) { LastNotifyAt[key] = Time.realtimeSinceStartup; Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && (!((Object)(object)obj != (Object)null) || !(Vector3.Distance(((Component)localPlayer).transform.position, obj.transform.position) > 12f))) { string text = DisplayType(DetectGuardianType(obj)); ((Character)localPlayer).Message((MessageType)2, text + " ist durch ChallengeHub geschuetzt und kann nicht zerstoert werden.", 0, (Sprite)null); } } } catch { } } private static Piece FindLookedAtIndestructibleGuardian(Player player) { //IL_0087: 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) if ((Object)(object)player == (Object)null) { return null; } GameObject val = null; try { MethodInfo method = ((object)player).GetType().GetMethod("GetHoverObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { object? obj = method.Invoke(player, null); val = (GameObject)((obj is GameObject) ? obj : null); } } catch { } Piece val2 = (((Object)(object)val != (Object)null) ? (val.GetComponentInParent() ?? val.GetComponent()) : null); if ((Object)(object)val2 != (Object)null && IsIndestructibleGuardian(val2)) { return val2; } try { Camera main = Camera.main; if ((Object)(object)main == (Object)null) { return null; } RaycastHit val3 = default(RaycastHit); if (Physics.Raycast(((Component)main).transform.position, ((Component)main).transform.forward, ref val3, 8f)) { val2 = (((Object)(object)((RaycastHit)(ref val3)).collider != (Object)null) ? ((Component)((RaycastHit)(ref val3)).collider).GetComponentInParent() : null); if ((Object)(object)val2 != (Object)null && IsIndestructibleGuardian(val2)) { return val2; } } } catch { } return null; } private static HashSet SplitTypes(string csv) { HashSet hashSet = new HashSet(); string[] array = (csv ?? string.Empty).Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = Normalize(array[i]); if (!string.IsNullOrWhiteSpace(text)) { hashSet.Add(text); } } return hashSet; } private static string NormalizeGuardianType(string value) { string text = Normalize(value); switch (text) { case "bauer": case "farm": case "gruen": case "green": return "farmer"; case "entdecker": case "blue": case "blau": return "explorer"; case "kampf": case "rot": case "red": return "combat"; case "aufbau": case "gelb": case "yellow": return "builder"; case "weiss": case "white": return "neutral"; default: return text; } } private static string DisplayType(string type) { return NormalizeGuardianType(type) switch { "farmer" => "Gruener Bauer-Waechterstein", "explorer" => "Blauer Entdecker-Waechterstein", "combat" => "Roter Kampf-Waechterstein", "builder" => "Gelber Aufbau-Waechterstein", "neutral" => "Weisser Neutral-Waechterstein", _ => "Waechterstein", }; } private static string Normalize(string value) { return (value ?? string.Empty).Trim().ToLowerInvariant().Replace("(clone)", string.Empty) .Trim(); } } internal static class GuardianStoneRuntimeCache { private static readonly Dictionary Guardians = new Dictionary(); private static readonly List StaleIds = new List(); internal static void Register(Piece piece, string reason) { if (!((Object)(object)piece == (Object)null) && !((Object)(object)((Component)piece).gameObject == (Object)null)) { int instanceID = ((Object)((Component)piece).gameObject).GetInstanceID(); if (!IsGuardian(piece)) { Guardians.Remove(instanceID); } else { Guardians[instanceID] = piece; } } } internal static void Unregister(Piece piece) { if (!((Object)(object)piece == (Object)null) && !((Object)(object)((Component)piece).gameObject == (Object)null)) { Guardians.Remove(((Object)((Component)piece).gameObject).GetInstanceID()); } } internal static bool HasGuardianWithin(Vector3 position, float radius) { //IL_007d: 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_0083: 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) float num = radius * radius; bool result = false; StaleIds.Clear(); foreach (KeyValuePair guardian in Guardians) { Piece value = guardian.Value; if ((Object)(object)value == (Object)null || (Object)(object)((Component)value).gameObject == (Object)null) { StaleIds.Add(guardian.Key); continue; } if (!IsGuardian(value)) { StaleIds.Add(guardian.Key); continue; } Vector3 val = ((Component)value).transform.position - position; if (((Vector3)(ref val)).sqrMagnitude <= num) { result = true; } } foreach (int staleId in StaleIds) { Guardians.Remove(staleId); } return result; } internal static int CountWithin(Vector3 position, float radius) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) float num = radius * radius; int num2 = 0; StaleIds.Clear(); foreach (KeyValuePair guardian in Guardians) { Piece value = guardian.Value; if ((Object)(object)value == (Object)null || (Object)(object)((Component)value).gameObject == (Object)null || !IsGuardian(value)) { StaleIds.Add(guardian.Key); continue; } Vector3 val = ((Component)value).transform.position - position; if (((Vector3)(ref val)).sqrMagnitude <= num) { num2++; } } foreach (int staleId in StaleIds) { Guardians.Remove(staleId); } return num2; } private static bool IsGuardian(Piece piece) { if ((Object)(object)piece == (Object)null) { return false; } if (!string.IsNullOrWhiteSpace(GuardianStoneProtectionFeature.DetectGuardianType(((Component)piece).gameObject))) { return true; } string text = Normalize(((Object)piece).name); string[] array = (((Plugin.GuardianStonePrefabNames != null) ? Plugin.GuardianStonePrefabNames.Value : string.Empty) ?? string.Empty).Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text2 = Normalize(array[i]); if (!string.IsNullOrWhiteSpace(text2) && (string.Equals(text, text2, StringComparison.OrdinalIgnoreCase) || text.Contains(text2))) { return true; } } return GuardianStonePieces.IsCustomGuardianPrefab(text); } private static string Normalize(string value) { return (value ?? string.Empty).Trim().ToLowerInvariant().Replace("(clone)", string.Empty) .Trim(); } } [HarmonyPatch] internal static class GuardianStoneRuntimeCacheLifecyclePatch { private static IEnumerable TargetMethods() { foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(Piece))) { if (string.Equals(declaredMethod.Name, "SetCreator", StringComparison.Ordinal) || string.Equals(declaredMethod.Name, "OnPlaced", StringComparison.Ordinal) || string.Equals(declaredMethod.Name, "OnDestroy", StringComparison.Ordinal)) { yield return declaredMethod; } } foreach (MethodInfo declaredMethod2 in AccessTools.GetDeclaredMethods(typeof(WearNTear))) { if (string.Equals(declaredMethod2.Name, "Awake", StringComparison.Ordinal) || string.Equals(declaredMethod2.Name, "OnDestroy", StringComparison.Ordinal)) { yield return declaredMethod2; } } } private static void Postfix(object __instance, MethodBase __originalMethod) { try { string text = ((__originalMethod != null) ? __originalMethod.Name : string.Empty); Piece val = (Piece)((__instance is Piece) ? __instance : null); if ((Object)(object)val == (Object)null) { WearNTear val2 = (WearNTear)((__instance is WearNTear) ? __instance : null); if (val2 != null) { val = ((Component)val2).GetComponent() ?? ((Component)val2).GetComponentInParent(); } } if (!((Object)(object)val == (Object)null)) { if (string.Equals(text, "OnDestroy", StringComparison.Ordinal)) { GuardianStoneRuntimeCache.Unregister(val); } else { GuardianStoneRuntimeCache.Register(val, text); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Waechtercache-Hook fehlgeschlagen: " + ex.Message)); } } } } internal static class GuardianZoneAccessFeature { private sealed class GuardianZoneAccessBehaviour : MonoBehaviour { private readonly Dictionary _rings = new Dictionary(); private float _nextRefresh; private Rect _window = new Rect(0f, 0f, 620f, 470f); private void Update() { //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && (Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)) && Input.GetKeyDown((KeyCode)98)) { Piece val = HoveredNeutral(localPlayer); if ((Object)(object)val == (Object)null) { ((Character)localPlayer).Message((MessageType)2, "Kein weisser Waechter im Blick.", 0, (Sprite)null); } else if (!IsOwner(localPlayer, val)) { ((Character)localPlayer).Message((MessageType)2, "Nur der Besitzer darf Baurechte verwalten.", 0, (Sprite)null); } else { _selectedGuardian = val; _windowOpen = true; ChallengeHubCursorController.Acquire("guardian-build-rights"); } } if ((Object)(object)localPlayer != (Object)null && Time.realtimeSinceStartup >= _nextRefresh) { _nextRefresh = Time.realtimeSinceStartup + 2f; RefreshRings(); } if (_windowOpen && ((Object)(object)_selectedGuardian == (Object)null || (Object)(object)localPlayer == (Object)null || Vector3.Distance(((Component)localPlayer).transform.position, ((Component)_selectedGuardian).transform.position) > Mathf.Max(3f, ManageRange.Value) + 2f)) { Close(); } } private void RefreshRings() { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Expected O, but got Unknown //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) HashSet seen = new HashSet(); foreach (Piece item in LoadedNeutralGuardians()) { int instanceID = ((Object)item).GetInstanceID(); seen.Add(instanceID); if (EnableBoundary != null && EnableBoundary.Value) { if (!_rings.TryGetValue(instanceID, out var value) || (Object)(object)value == (Object)null) { GameObject val = new GameObject("ChallengeHub_NeutralGuardianBoundary"); val.transform.SetParent(((Component)item).transform, false); value = val.AddComponent(); value.loop = true; value.useWorldSpace = true; value.widthMultiplier = 0.0125f; ((Renderer)value).material = new Material(Shader.Find("Sprites/Default")); value.startColor = new Color(0.82f, 0.9f, 1f, 0.2f); value.endColor = value.startColor; _rings[instanceID] = value; } int num = ((BoundarySegments != null) ? BoundarySegments.Value : 96); int num2 = (value.positionCount = Mathf.Clamp(num, 32, 192)); float num4 = Radius(item); for (int i = 0; i < num2; i++) { float num5 = (float)i * (float)Math.PI * 2f / (float)num2; Vector3 val2 = ((Component)item).transform.position + new Vector3(Mathf.Cos(num5) * num4, 0f, Mathf.Sin(num5) * num4); val2.y = GroundHeight(val2, ((Component)item).transform.position.y) + 0.12f; value.SetPosition(i, val2); } } } foreach (int item2 in _rings.Keys.Where((int id) => !seen.Contains(id) || EnableBoundary == null || !EnableBoundary.Value).ToList()) { if ((Object)(object)_rings[item2] != (Object)null) { Object.Destroy((Object)(object)((Component)_rings[item2]).gameObject); } _rings.Remove(item2); } } private static float GroundHeight(Vector3 point, float fallback) { //IL_0012: 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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) try { float result = default(float); if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGroundHeight(point, ref result)) { return result; } } catch { } try { RaycastHit val = (from h in Physics.RaycastAll(new Vector3(point.x, fallback + 200f, point.z), Vector3.down, 500f, -1, (QueryTriggerInteraction)1) where (Object)(object)((RaycastHit)(ref h)).collider != (Object)null && (Object)(object)((Component)((RaycastHit)(ref h)).collider).GetComponentInParent() != (Object)null orderby ((RaycastHit)(ref h)).point.y descending select h).FirstOrDefault(); if ((Object)(object)((RaycastHit)(ref val)).collider != (Object)null) { return ((RaycastHit)(ref val)).point.y; } } catch { } return fallback; } private void OnGUI() { //IL_00a9: 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_00c9: Expected O, but got Unknown //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) if (_windowOpen && !((Object)(object)_selectedGuardian == (Object)null)) { ChallengeHubWindowTheme.Apply(); ((Rect)(ref _window)).width = Mathf.Min(760f, (float)Screen.width - 30f); ((Rect)(ref _window)).height = Mathf.Min(560f, (float)Screen.height - 30f); ((Rect)(ref _window)).x = ((float)Screen.width - ((Rect)(ref _window)).width) * 0.5f; ((Rect)(ref _window)).y = ((float)Screen.height - ((Rect)(ref _window)).height) * 0.5f; _window = GUI.Window(981286, _window, new WindowFunction(DrawWindow), "ChallengeHub - Baurechte", ChallengeHubWindowTheme.Window); } } private void DrawWindow(int id) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Invalid comparison between Unknown and I4 //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Invalid comparison between Unknown and I4 ChallengeHubWindowTheme.DrawPanel(new Rect(16f, 42f, ((Rect)(ref _window)).width - 32f, ((Rect)(ref _window)).height - 58f)); GUILayout.Space(16f); GUILayout.Label("Baurechte des weisen Waechters", ChallengeHubWindowTheme.Title, Array.Empty()); Player local = Player.m_localPlayer; Dictionary dictionary = AccessEntries(_selectedGuardian); GUILayout.Label("Hinzufuegen: Spieler muss online und hoechstens " + Mathf.RoundToInt(NearbyPlayerRange.Value) + " m entfernt sein.", Array.Empty()); foreach (Player item in (Player.GetAllPlayers() ?? new List()).Where((Player p) => (Object)(object)p != (Object)null && (Object)(object)p != (Object)(object)local && Vector3.Distance(((Component)p).transform.position, ((Component)_selectedGuardian).transform.position) <= NearbyPlayerRange.Value)) { string text = SafeId(item); if (!string.IsNullOrWhiteSpace(text) && !dictionary.ContainsKey(text)) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(item.GetPlayerName() + " (" + text + ")", Array.Empty()); if (GUILayout.Button("Hinzufuegen", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) })) { dictionary[text] = item.GetPlayerName(); SaveAccess(_selectedGuardian, dictionary); } GUILayout.EndHorizontal(); } } GUILayout.Space(15f); GUILayout.Label("Dauerhaft berechtigt:", Array.Empty()); foreach (KeyValuePair item2 in dictionary.ToList()) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(item2.Value + " (" + item2.Key + ")", Array.Empty()); if (GUILayout.Button("Entfernen", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) })) { dictionary.Remove(item2.Key); SaveAccess(_selectedGuardian, dictionary); } GUILayout.EndHorizontal(); } GUILayout.FlexibleSpace(); if (GUILayout.Button("Schliessen (ESC)", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(34f) })) { Close(); } if ((int)Event.current.type == 4 && (int)Event.current.keyCode == 27) { Close(); } GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _window)).width, 34f)); } private void OnDestroy() { if (_windowOpen) { Close(); } } private static void Close() { _windowOpen = false; _selectedGuardian = null; ChallengeHubCursorController.Release("guardian-build-rights"); } } [HarmonyPatch(typeof(Player), "PlacePiece", new Type[] { typeof(Piece), typeof(Vector3), typeof(Quaternion), typeof(bool) })] [HarmonyPriority(800)] private static class PlacePieceAccessPatch { private static bool Prefix(Player __instance, Vector3 pos) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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) if ((Object)(object)__instance == (Object)null || !((Character)__instance).IsOwner()) { return true; } GameObject placementGhost = PlayerBuildReflection.GetPlacementGhost(__instance); Vector3 position = (((Object)(object)placementGhost != (Object)null) ? placementGhost.transform.position : pos); if (CanBuildAt(__instance, position, out var _)) { return true; } ((Character)__instance).Message((MessageType)2, "Keine Baurechte in der Schutzzone dieses weissen Waechters.", 0, (Sprite)null); return false; } } [HarmonyPatch(typeof(PrivateArea), "IsPermitted", new Type[] { typeof(long) })] private static class PrivateAreaPermissionPatch { private static void Postfix(PrivateArea __instance, long playerID, ref bool __result) { if (!__result && !((Object)(object)__instance == (Object)null) && playerID != 0L) { Piece piece = ((Component)__instance).GetComponent() ?? ((Component)__instance).GetComponentInParent(); if (IsNeutral(piece) && AccessIds(piece).Contains(playerID.ToString())) { __result = true; } } } } private const string OwnerIdKey = "ChallengeHub.Guardian.OwnerId"; private const string AccessIdsKey = "ChallengeHub.Guardian.BuildAccessIds"; private const string AccessNamesKey = "ChallengeHub.Guardian.BuildAccessNames"; private static Plugin _plugin; private static ConfigEntry EnableBoundary; private static ConfigEntry ManageRange; private static ConfigEntry NearbyPlayerRange; private static ConfigEntry BoundarySegments; private static Piece _selectedGuardian; private static bool _windowOpen; private const string AccessUpdateRpc = "ChallengeHub_GuardianAccessUpdate_v1"; private static ZRoutedRpc _registeredRpc; internal static void Initialize(Plugin plugin) { _plugin = plugin; EnableBoundary = ((BaseUnityPlugin)plugin).Config.Bind("GuardianStone.NeutralAccess", "ShowPermanentBoundary", true, "Zeigt den Rand der weissen Waechter-Schutzzone dauerhaft auf dem Boden."); ManageRange = ((BaseUnityPlugin)plugin).Config.Bind("GuardianStone.NeutralAccess", "ManageRange", 8f, "Maximale Entfernung fuer STRG+B am weissen Waechter."); NearbyPlayerRange = ((BaseUnityPlugin)plugin).Config.Bind("GuardianStone.NeutralAccess", "NearbyPlayerRange", 10f, "Spieler muessen zum Hinzufuegen so nah am weissen Waechter stehen."); BoundarySegments = ((BaseUnityPlugin)plugin).Config.Bind("GuardianStone.NeutralAccess", "BoundarySegments", 96, "Segmentanzahl des Bodenrings."); ((Component)plugin).gameObject.AddComponent(); ((MonoBehaviour)plugin).StartCoroutine(RegisterRpcWhenReady()); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Weisser Waechter: Bodenrand und persistente STRG+B-Baurechte bereit."); } } internal static bool IsNeutral(Piece piece) { if ((Object)(object)piece != (Object)null) { return GuardianStoneProtectionFeature.DetectGuardianType(((Component)piece).gameObject) == "neutral"; } return false; } internal static float Radius(Piece guardian) { float num = 20f; try { PrivateArea val = (((Object)(object)guardian != (Object)null) ? (((Component)guardian).GetComponent() ?? ((Component)guardian).GetComponentInChildren(true)) : null); if ((Object)(object)val != (Object)null && val.m_radius > 0f) { num = val.m_radius; } } catch { } return Mathf.Clamp(num, 8f, 80f); } internal static bool CanOwnerRemove(Player player, Piece piece) { if ((Object)(object)player != (Object)null && IsNeutral(piece)) { return IsOwner(player, piece); } return false; } internal static bool CanBuildAt(Player player, Vector3 position, out Piece blockingGuardian) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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) blockingGuardian = null; if ((Object)(object)player == (Object)null) { return true; } string item = SafeId(player); foreach (Piece item2 in LoadedNeutralGuardians()) { Vector3 val = position - ((Component)item2).transform.position; val.y = 0f; float num = Radius(item2); if (!(((Vector3)(ref val)).sqrMagnitude > num * num) && !IsOwner(player, item2) && !AccessIds(item2).Contains(item)) { blockingGuardian = item2; return false; } } return true; } private static IEnumerable LoadedNeutralGuardians() { return Object.FindObjectsByType((FindObjectsSortMode)0).Where(IsNeutral); } private static string SafeId(Player player) { try { return player.GetPlayerID().ToString(); } catch { return string.Empty; } } private static bool IsOwner(Player player, Piece guardian) { if ((Object)(object)player == (Object)null || (Object)(object)guardian == (Object)null) { return false; } string text = SafeId(player); if (string.IsNullOrWhiteSpace(text)) { return false; } try { long creator = guardian.GetCreator(); if (creator != 0L && creator.ToString() == text) { return true; } } catch { } return string.Equals(Read(guardian, "ChallengeHub.Guardian.OwnerId").Trim(), text, StringComparison.Ordinal); } private static HashSet AccessIds(Piece piece) { return new HashSet(from x in (Read(piece, "ChallengeHub.Guardian.BuildAccessIds") ?? string.Empty).Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries) select x.Trim(), StringComparer.Ordinal); } private static Dictionary AccessEntries(Piece piece) { string[] array = (Read(piece, "ChallengeHub.Guardian.BuildAccessIds") ?? string.Empty).Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = (Read(piece, "ChallengeHub.Guardian.BuildAccessNames") ?? string.Empty).Split(new char[1] { '\u001f' }, StringSplitOptions.None); Dictionary dictionary = new Dictionary(); for (int i = 0; i < array.Length; i++) { dictionary[array[i].Trim()] = ((i < array2.Length) ? array2[i] : array[i].Trim()); } return dictionary; } private static void SaveAccess(Piece piece, Dictionary entries) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_0048: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)piece == (Object)null || entries == null || ZRoutedRpc.instance == null || (Object)(object)Player.m_localPlayer == (Object)null) { return; } ZNetView component = ((Component)piece).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if (val != null && val.IsValid()) { ZPackage val2 = new ZPackage(); val2.Write(val.m_uid); val2.Write(Player.m_localPlayer.GetPlayerID()); val2.Write(string.Join(",", entries.Keys.ToArray())); val2.Write(string.Join("\u001f", entries.Values.Select((string x) => (x ?? string.Empty).Replace("\u001f", " ")).ToArray())); ZRoutedRpc.instance.InvokeRoutedRPC(ServerPeerId(), "ChallengeHub_GuardianAccessUpdate_v1", new object[1] { val2 }); } } private static IEnumerator RegisterRpcWhenReady() { while (ZRoutedRpc.instance == null) { yield return (object)new WaitForSeconds(1f); } if (_registeredRpc != ZRoutedRpc.instance) { _registeredRpc = ZRoutedRpc.instance; _registeredRpc.Register("ChallengeHub_GuardianAccessUpdate_v1", (Action)RPC_AccessUpdate); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Wächter-Baurechte-RPC serverautoritär registriert."); } } } private unsafe static void RPC_AccessUpdate(long sender, ZPackage package) { //IL_0030: 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_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || package == null) { return; } ZDOID guardianId = package.ReadZDOID(); long requestedOwner = package.ReadLong(); string text = package.ReadString() ?? string.Empty; string text2 = package.ReadString() ?? string.Empty; Player val = ((IEnumerable)(Player.GetAllPlayers() ?? new List())).FirstOrDefault((Func)delegate(Player p) { try { object obj2; if (p == null) { obj2 = null; } else { ZNetView component = ((Component)p).GetComponent(); obj2 = ((component != null) ? component.GetZDO() : null); } ZDO val4 = (ZDO)obj2; return (Object)(object)p != (Object)null && p.GetPlayerID() == requestedOwner && val4 != null && val4.GetOwner() == sender; } catch { return false; } }); Piece val2 = LoadedNeutralGuardians().FirstOrDefault((Func)delegate(Piece p) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) object obj2; if (p == null) { obj2 = null; } else { ZNetView component = ((Component)p).GetComponent(); obj2 = ((component != null) ? component.GetZDO() : null); } ZDO val4 = (ZDO)obj2; return val4 != null && val4.IsValid() && ((ZDOID)(ref val4.m_uid)).Equals(guardianId); }); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || !IsOwner(val, val2)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Wächter-Baurechte-Aktualisierung abgelehnt: Besitzer- oder Objektprüfung fehlgeschlagen."); } return; } long result; HashSet hashSet = new HashSet(from x in (text ?? string.Empty).Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries) select x.Trim() into x where long.TryParse(x, out result) && result != 0 select x, StringComparer.Ordinal); string[] array = (text2 ?? string.Empty).Split(new char[1] { '\u001f' }, StringSplitOptions.None); string[] array2 = (from x in (text ?? string.Empty).Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries) select x.Trim()).ToArray(); Dictionary dictionary = new Dictionary(StringComparer.Ordinal); for (int num = 0; num < array2.Length; num++) { if (hashSet.Contains(array2[num])) { dictionary[array2[num]] = ((num < array.Length) ? array[num].Replace("\u001f", " ") : array2[num]); } } Write(val2, "ChallengeHub.Guardian.BuildAccessIds", string.Join(",", dictionary.Keys.ToArray())); Write(val2, "ChallengeHub.Guardian.BuildAccessNames", string.Join("\u001f", dictionary.Values.ToArray())); ManualLogSource log2 = Plugin.Log; if (log2 != null) { string[] obj = new string[7] { "Wächter-Baurechte gespeichert: Wächter=", null, null, null, null, null, null }; ZDOID val3 = guardianId; obj[1] = ((object)(*(ZDOID*)(&val3))/*cast due to .constrained prefix*/).ToString(); obj[2] = "; Besitzer="; obj[3] = requestedOwner.ToString(); obj[4] = "; Berechtigte="; obj[5] = dictionary.Count.ToString(); obj[6] = "."; log2.LogInfo((object)string.Concat(obj)); } } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("Wächter-Baurechte-RPC fehlgeschlagen: " + ex.Message)); } } } private static long ServerPeerId() { try { MethodInfo methodInfo = AccessTools.Method(((object)ZRoutedRpc.instance)?.GetType(), "GetServerPeerID", (Type[])null, (Type[])null) ?? AccessTools.Method(((object)ZRoutedRpc.instance)?.GetType(), "GetServerPeerId", (Type[])null, (Type[])null); return (methodInfo != null) ? Convert.ToInt64(methodInfo.Invoke(ZRoutedRpc.instance, null)) : 0; } catch { return 0L; } } private static string Read(Piece piece, string key) { try { object obj; if (piece == null) { obj = null; } else { ZNetView component = ((Component)piece).GetComponent(); if (component == null) { obj = null; } else { ZDO zDO = component.GetZDO(); obj = ((zDO != null) ? zDO.GetString(key, string.Empty) : null); } } if (obj == null) { obj = string.Empty; } return (string)obj; } catch { return string.Empty; } } private static void Write(Piece piece, string key, string value) { try { if (piece == null) { return; } ZNetView component = ((Component)piece).GetComponent(); if (component != null) { ZDO zDO = component.GetZDO(); if (zDO != null) { zDO.Set(key, value ?? string.Empty); } } } catch { } } private static Piece HoveredNeutral(Player player) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) try { object obj = AccessTools.Method(((object)player).GetType(), "GetHoverObject", (Type[])null, (Type[])null)?.Invoke(player, null); object obj2 = ((obj is GameObject) ? obj : null); if (obj2 == null) { object obj3 = ((obj is Component) ? obj : null); obj2 = ((obj3 != null) ? ((Component)obj3).gameObject : null); } Piece val = ((obj2 != null) ? ((GameObject)obj2).GetComponentInParent() : null); return (IsNeutral(val) && Vector3.Distance(((Component)player).transform.position, ((Component)val).transform.position) <= Mathf.Max(3f, ManageRange.Value)) ? val : null; } catch { return null; } } } internal sealed class GuardianZoneRadiusVisual : MonoBehaviour { private Piece _piece; private ChallengeHubGuardianStoneMarker _marker; private CircleProjector _projector; private MaterialPropertyBlock _propertyBlock; private float _nextRefresh; private float _lastRadius = -1f; private Color _lastColor = new Color(-1f, -1f, -1f, -1f); private bool _requestedVisible = true; private bool _hiddenByChallengeHub; private void Awake() { _piece = ((Component)this).GetComponent() ?? ((Component)this).GetComponentInParent(); _marker = ((Component)this).GetComponent() ?? ((Component)this).GetComponentInParent(); RemoveLegacyFloatingRing(); FindProjector(); } private void Start() { Refresh(force: true); } private void OnEnable() { Refresh(force: true); } private void Update() { if (!(Time.unscaledTime < _nextRefresh)) { _nextRefresh = Time.unscaledTime + 0.5f; Refresh(force: false); } } internal static void SetVisible(GameObject root, bool visible) { if ((Object)(object)root == (Object)null) { return; } GuardianZoneRadiusVisual[] componentsInChildren = root.GetComponentsInChildren(true); foreach (GuardianZoneRadiusVisual guardianZoneRadiusVisual in componentsInChildren) { if (!((Object)(object)guardianZoneRadiusVisual == (Object)null)) { guardianZoneRadiusVisual._requestedVisible = visible; guardianZoneRadiusVisual.Refresh(force: true); } } } private void Refresh(bool force) { //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsDedicated()) { return; } if ((Object)(object)_piece == (Object)null) { _piece = ((Component)this).GetComponent() ?? ((Component)this).GetComponentInParent(); } if ((Object)(object)_marker == (Object)null) { _marker = ((Component)this).GetComponent() ?? ((Component)this).GetComponentInParent(); } if ((Object)(object)_piece == (Object)null) { return; } RemoveLegacyFloatingRing(); if ((Object)(object)_projector == (Object)null) { FindProjector(); } if ((Object)(object)_projector == (Object)null) { return; } if (!_requestedVisible || !ShouldBeVisible()) { if (((Component)_projector).gameObject.activeSelf) { ((Component)_projector).gameObject.SetActive(false); _hiddenByChallengeHub = true; } return; } if (_hiddenByChallengeHub) { ((Component)_projector).gameObject.SetActive(true); _hiddenByChallengeHub = false; } string text = ResolveGuardianType(); float num = ResolveRadius(text); Color guardianColor = GuardianStonePieces.GetGuardianColor(text); guardianColor.a = 0.82f; bool num2 = force || Mathf.Abs(num - _lastRadius) >= 0.1f; bool flag = force || !Approximately(guardianColor, _lastColor); if (num2) { _projector.m_radius = num; _lastRadius = num; } if (flag || force || ((Component)_projector).transform.childCount > 0) { ApplyColorToExistingGroundRing(guardianColor); _lastColor = guardianColor; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Waechter-Bodenradius konnte nicht eingefaerbt werden: " + ex.Message)); } } } private void FindProjector() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown if (!((Object)(object)_piece == (Object)null)) { _projector = ((Component)_piece).GetComponentInChildren(true); if ((Object)(object)_projector != (Object)null) { _propertyBlock = new MaterialPropertyBlock(); } } } private void ApplyColorToExistingGroundRing(Color color) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: 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_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_projector == (Object)null) { return; } if (_propertyBlock == null) { _propertyBlock = new MaterialPropertyBlock(); } Renderer[] componentsInChildren = ((Component)_projector).GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { val.GetPropertyBlock(_propertyBlock); _propertyBlock.SetColor("_Color", color); _propertyBlock.SetColor("_TintColor", color); _propertyBlock.SetColor("_MainColor", color); Color val2 = color * 1.6f; val2.a = color.a; _propertyBlock.SetColor("_EmissionColor", val2); val.SetPropertyBlock(_propertyBlock); } } } private void RemoveLegacyFloatingRing() { Transform val = ((Component)this).transform.Find("ChallengeHubGuardianRadius"); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } } private bool ShouldBeVisible() { if ((Object)(object)_piece == (Object)null || !((Component)_piece).gameObject.activeInHierarchy) { return false; } if (string.Equals(ResolveGuardianType(), "reset", StringComparison.OrdinalIgnoreCase)) { return !TargetedResetFeature.IsResetGuardianConsumed(_piece); } return true; } private string ResolveGuardianType() { string text = (((Object)(object)_marker != (Object)null) ? _marker.GuardianType : string.Empty); if (string.IsNullOrWhiteSpace(text)) { try { ZNetView val = (((Object)(object)_piece != (Object)null) ? ((Component)_piece).GetComponent() : null); if ((Object)(object)val != (Object)null && val.GetZDO() != null) { text = val.GetZDO().GetString("ChallengeHub.GuardianType", string.Empty); } } catch { } } if (!string.IsNullOrWhiteSpace(text)) { return text.Trim().ToLowerInvariant(); } return "neutral"; } private float ResolveRadius(string type) { if (string.Equals(type, "reset", StringComparison.OrdinalIgnoreCase)) { return Mathf.Max(5f, TargetedResetFeature.GetResetGuardianRadius(_piece)); } if (string.Equals(type, "farmer", StringComparison.OrdinalIgnoreCase) && Plugin.GuardianFarmerPresenceRadius != null && Plugin.GuardianFarmerPresenceRadius.Value > 0f) { return Mathf.Max(5f, Plugin.GuardianFarmerPresenceRadius.Value); } if (string.Equals(type, "neutral", StringComparison.OrdinalIgnoreCase) && Plugin.GuardianNeutralPresenceRadius != null && Plugin.GuardianNeutralPresenceRadius.Value > 0f) { return Mathf.Max(5f, Plugin.GuardianNeutralPresenceRadius.Value); } return Mathf.Max(5f, (Plugin.GuardianStoneDefaultRadius != null) ? Plugin.GuardianStoneDefaultRadius.Value : 80f); } private static bool Approximately(Color a, Color b) { //IL_0000: 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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) if (Mathf.Abs(a.r - b.r) < 0.01f && Mathf.Abs(a.g - b.g) < 0.01f && Mathf.Abs(a.b - b.b) < 0.01f) { return Mathf.Abs(a.a - b.a) < 0.01f; } return false; } } [HarmonyPatch(typeof(ItemStand), "Interact")] internal static class HallOfGloryItemStandPatch { private static void Postfix(ItemStand __instance, Humanoid user, bool hold, bool alt, bool __result) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown if (!(!__result || hold) && !((Object)(object)__instance == (Object)null) && !((Object)(object)user == (Object)null) && !((Object)(object)user != (Object)(object)Player.m_localPlayer) && !((Object)(object)Plugin.Instance == (Object)null)) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(ReportAfterVisualUpdate(__instance, (Player)user)); } } private static IEnumerator ReportAfterVisualUpdate(ItemStand stand, Player player) { yield return (object)new WaitForSeconds(0.35f); if ((Object)(object)stand == (Object)null || (Object)(object)player == (Object)null) { yield break; } ZNetView component = ((Component)stand).GetComponent(); ZDO val = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null); if (val == null) { yield break; } string text = val.GetString(StringExtensionMethods.GetStableHashCode("item"), string.Empty); string text2 = Plugin.NormalizeKey(text); if (string.IsNullOrWhiteSpace(text2) || text2.IndexOf("trophy", StringComparison.OrdinalIgnoreCase) < 0) { yield break; } ChallengeHubWorldStationMarker challengeHubWorldStationMarker = (from marker in Object.FindObjectsByType((FindObjectsSortMode)0) where (Object)(object)marker != (Object)null && string.Equals(marker.StationType, "glory_stone", StringComparison.OrdinalIgnoreCase) where Horizontal(((Component)marker).transform.position, ((Component)stand).transform.position) <= 30f orderby Horizontal(((Component)marker).transform.position, ((Component)stand).transform.position) select marker).FirstOrDefault(); if ((Object)(object)challengeHubWorldStationMarker == (Object)null) { MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, "Ruhmeshalle: Trophäen zählen nur auf einem Item-Ständer im Umkreis des Ruhmessteins.", 0, (Sprite)null, false); } yield break; } ZNetView component2 = ((Component)challengeHubWorldStationMarker).GetComponent(); string text3 = (((Object)(object)component2 != (Object)null && component2.IsValid() && component2.GetZDO() != null) ? ((object)Unsafe.As(ref component2.GetZDO().m_uid)/*cast due to .constrained prefix*/).ToString() : ((Object)((Component)challengeHubWorldStationMarker).gameObject).GetInstanceID().ToString()); string text4 = ((object)Unsafe.As(ref val.m_uid)/*cast due to .constrained prefix*/).ToString(); Plugin.Instance.SendEvent("hall_trophy_mounted", player, new Dictionary { { "trophy", text }, { "stationId", text3 }, { "scope", text3 }, { "evidenceId", "hall:" + text3 + ":" + text4 + ":" + text2 }, { "position", Plugin.SerializeVector(((Component)stand).transform.position) }, { "biome", Plugin.CurrentBiome(((Component)stand).transform.position) }, { "attributionMethod", "itemstand_glory_stone_2_12_0" } }); MessageHud instance2 = MessageHud.instance; if (instance2 != null) { instance2.ShowMessage((MessageType)2, "Ruhmeshalle: Trophäe wird geprüft …", 0, (Sprite)null, false); } } private static float Horizontal(Vector3 a, Vector3 b) { //IL_0000: 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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) float num = a.x - b.x; float num2 = a.z - b.z; return Mathf.Sqrt(num * num + num2 * num2); } } internal sealed class IngameCameraFeature : MonoBehaviour { [Serializable] private sealed class PostResponse { public bool ok; public string error; public string postId; public string bugId; public string imageUrl; } private static Plugin _plugin; private bool _visible; private bool _capturing; private bool _uploading; private string _caption = string.Empty; private string _goalKey = string.Empty; private int _goalIndex = -1; private int _communityTargetIndex = -1; private int _cropMode; private int _postType; private bool _includePosition = true; private bool _hideHud = true; private bool _watermark = true; private bool _drawingWatermark; private string _status = string.Empty; private Texture2D _preview; private byte[] _jpeg; private Rect _window = new Rect(80f, 35f, 860f, 730f); private bool _oldCursor; private CursorLockMode _oldLock; private readonly HashSet _automaticChronicleKeys = new HashSet(StringComparer.OrdinalIgnoreCase); private bool _automaticChronicleBusy; internal static void Initialize(Plugin plugin) { _plugin = plugin; if ((Object)(object)((Component)plugin).gameObject.GetComponent() == (Object)null) { ((Component)plugin).gameObject.AddComponent(); } } internal static string AutomaticChronicleEvidenceId(string eventType, string scope, Player player) { if ((Object)(object)player == (Object)null) { return string.Empty; } return ("bluteid-chronicle:" + Plugin.CurrentWorldUidString() + ":" + player.GetPlayerID() + ":" + eventType + ":" + scope).ToLowerInvariant(); } internal static void CaptureAutomaticChronicle(string eventType, string label, string scope, Dictionary eventFields, bool sendEventAfterCapture = true) { if (!DeathRunCounterFeature.Enabled || (Object)(object)_plugin == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null) { return; } IngameCameraFeature component = ((Component)_plugin).gameObject.GetComponent(); if (!((Object)(object)component == (Object)null)) { string text = AutomaticChronicleEvidenceId(eventType, scope, Player.m_localPlayer); if (component._automaticChronicleKeys.Add(text)) { ((MonoBehaviour)component).StartCoroutine(component.UploadAutomaticChronicle(eventType, label, scope, text, eventFields ?? new Dictionary(), sendEventAfterCapture)); } } } private IEnumerator UploadAutomaticChronicle(string eventType, string label, string scope, string evidenceId, Dictionary eventFields, bool sendEventAfterCapture) { while (_automaticChronicleBusy || _capturing || _uploading) { yield return (object)new WaitForSeconds(1f); } _automaticChronicleBusy = true; yield return (object)new WaitForSeconds(1.5f); Player player = Player.m_localPlayer; if ((Object)(object)player == (Object)null) { _automaticChronicleBusy = false; yield break; } yield return (object)new WaitForEndOfFrame(); Texture2D val = ScreenCapture.CaptureScreenshotAsTexture(); byte[] array = null; if ((Object)(object)val != (Object)null) { Texture2D obj = Downscale(val, 1280); if (obj != val) { Object.Destroy((Object)(object)val); } array = EncodeWithinLimit(obj); Object.Destroy((Object)(object)obj); } string imageUrl = string.Empty; string postId = string.Empty; if (array != null) { Dictionary payload = new Dictionary { { "challengeShortCode", Plugin.ChallengeShortCode.Value }, { "playerId", player.GetPlayerID().ToString() }, { "playerName", player.GetPlayerName() }, { "twitchLogin", Plugin.TwitchLogin.Value }, { "linkCode", Plugin.PlayerLinkCode.Value }, { "caption", label }, { "postType", "automatic_chronicle" }, { "chronicleEventType", eventType }, { "chronicleScope", scope }, { "position", string.Empty }, { "imageBase64", Convert.ToBase64String(array) } }; foreach (KeyValuePair item in DeathRunCounterFeature.RunFields()) { payload[item.Key] = item.Value; } yield return ChallengeHubApiTokenFeature.EnsureAvailable(); byte[] bytes = Encoding.UTF8.GetBytes(Plugin.ToJson(payload)); UnityWebRequest request = new UnityWebRequest(Plugin.ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/api/valheim/post", "POST"); try { request.uploadHandler = (UploadHandler)new UploadHandlerRaw(bytes); request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); request.SetRequestHeader("Content-Type", "application/json"); ChallengeHubApiTokenFeature.ApplyAuthorization(request); request.timeout = 30; yield return request.SendWebRequest(); try { PostResponse postResponse = ChallengeHubJson.Deserialize(request.downloadHandler.text ?? string.Empty); if (postResponse != null && postResponse.ok) { imageUrl = postResponse.imageUrl ?? string.Empty; postId = postResponse.postId ?? string.Empty; } } catch { } } finally { ((IDisposable)request)?.Dispose(); } } eventFields["label"] = label; eventFields["scope"] = scope; eventFields["chronicle"] = true; if (!string.IsNullOrWhiteSpace(imageUrl)) { eventFields["screenshotUrl"] = imageUrl; } if (!string.IsNullOrWhiteSpace(postId)) { eventFields["postId"] = postId; } eventFields["evidenceId"] = evidenceId; if (sendEventAfterCapture) { _plugin.SendEvent(eventType, player, eventFields); } ((Character)player).Message((MessageType)1, "Blut-Eid-Chronik: " + label, 0, (Sprite)null); _automaticChronicleBusy = false; } private void Update() { //IL_0029: 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) if (!Enum.TryParse(Plugin.CameraHotkey?.Value ?? "F10", ignoreCase: true, out KeyCode result)) { result = (KeyCode)291; } if ((Object)(object)Player.m_localPlayer != (Object)null && Input.GetKeyDown(result) && !_capturing && !_uploading) { if (_visible) { Close(); } else { ((MonoBehaviour)this).StartCoroutine(Capture()); } } if (_visible && Input.GetKeyDown((KeyCode)27) && !_uploading) { Close(); } if (_visible && (Object)(object)Player.m_localPlayer == (Object)null) { Close(); } } private IEnumerator Capture() { _capturing = true; _status = "Screenshot wird aufgenommen ..."; bool restoreWindow = _visible; _visible = false; GameObject hudObject = (((Object)(object)Hud.instance == (Object)null) ? null : ((Component)Hud.instance).gameObject); bool hudWasActive = (Object)(object)hudObject != (Object)null && hudObject.activeSelf; if (_hideHud && (Object)(object)hudObject != (Object)null) { hudObject.SetActive(false); } _drawingWatermark = _watermark; yield return (object)new WaitForEndOfFrame(); Texture2D val = ScreenCapture.CaptureScreenshotAsTexture(); _drawingWatermark = false; if (_hideHud && (Object)(object)hudObject != (Object)null) { hudObject.SetActive(hudWasActive); } if ((Object)(object)val == (Object)null) { _capturing = false; Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)1, "ChallengeHub-Kamera: Aufnahme fehlgeschlagen.", 0, (Sprite)null); } yield break; } Texture2D val2 = Crop(val, _cropMode); if (val2 != val) { Object.Destroy((Object)(object)val); } Texture2D val3 = Downscale(val2, 1600); if (val3 != val2) { Object.Destroy((Object)(object)val2); } ReplacePreview(val3); _jpeg = EncodeWithinLimit(val3); _capturing = false; if (_jpeg == null) { _status = "Screenshot ist auch nach Komprimierung zu gross."; Open(); yield break; } _status = "Bereit zum Posten (" + Mathf.CeilToInt((float)_jpeg.Length / 1024f) + " KB)."; if (restoreWindow) { _visible = true; } else { Open(); } } private static Texture2D Crop(Texture2D source, int mode) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown if ((Object)(object)source == (Object)null || mode == 0) { return source; } float num = ((mode == 1) ? 1.7777778f : 1f); int num2 = ((Texture)source).width; int num3 = ((Texture)source).height; if ((float)num2 / (float)num3 > num) { num2 = Mathf.RoundToInt((float)num3 * num); } else { num3 = Mathf.RoundToInt((float)num2 / num); } int num4 = (((Texture)source).width - num2) / 2; int num5 = (((Texture)source).height - num3) / 2; Texture2D val = new Texture2D(num2, num3, (TextureFormat)3, false); val.SetPixels(source.GetPixels(num4, num5, num2, num3)); val.Apply(false, false); return val; } private static byte[] EncodeWithinLimit(Texture2D texture) { int[] array = new int[5] { 82, 70, 58, 46, 35 }; foreach (int num in array) { byte[] array2 = ImageConversion.EncodeToJPG(texture, num); if (array2 != null && array2.Length <= 2400000) { return array2; } } return null; } private static Texture2D Downscale(Texture2D source, int maximumWidth) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown if ((Object)(object)source == (Object)null || ((Texture)source).width <= maximumWidth) { return source; } int num = Mathf.Max(1, Mathf.RoundToInt((float)((Texture)source).height * ((float)maximumWidth / (float)((Texture)source).width))); RenderTexture temporary = RenderTexture.GetTemporary(maximumWidth, num, 0, (RenderTextureFormat)0); RenderTexture active = RenderTexture.active; try { Graphics.Blit((Texture)(object)source, temporary); RenderTexture.active = temporary; Texture2D val = new Texture2D(maximumWidth, num, (TextureFormat)3, false); val.ReadPixels(new Rect(0f, 0f, (float)maximumWidth, (float)num), 0, 0); val.Apply(false, false); return val; } finally { RenderTexture.active = active; RenderTexture.ReleaseTemporary(temporary); } } private void Open() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (!_visible) { _visible = true; _oldCursor = Cursor.visible; _oldLock = Cursor.lockState; ChallengeHubCursorController.Acquire("camera-f10"); } } private void Close() { _visible = false; ChallengeHubCursorController.Release("camera-f10"); _caption = string.Empty; _goalKey = string.Empty; _status = string.Empty; _jpeg = null; ReplacePreview(null); } private void ReplacePreview(Texture2D next) { if ((Object)(object)_preview != (Object)null) { Object.Destroy((Object)(object)_preview); } _preview = next; } private void OnGUI() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Expected O, but got Unknown //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) if (_drawingWatermark) { GUI.color = new Color(1f, 1f, 1f, 0.92f); string text = ((DeathRunCounterFeature.Enabled && DeathRunCounterFeature.ActiveRun != null) ? (" | " + DeathRunCounterFeature.ActiveRun.worldCode) : string.Empty); GUI.Label(new Rect(18f, (float)Screen.height - 42f, 760f, 30f), "ChallengeHub | " + Plugin.ChallengeShortCode.Value + text + " | " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); GUI.color = Color.white; } if (_visible) { ((Rect)(ref _window)).width = Mathf.Min(860f, (float)Screen.width - 30f); ((Rect)(ref _window)).height = Mathf.Min(730f, (float)Screen.height - 30f); _window = GUI.Window("ChallengeHub_IngameCamera_v1".GetHashCode(), _window, new WindowFunction(DrawWindow), DeathRunCounterFeature.Enabled ? "ChallengeHub DeathRunCounter - F10" : "ChallengeHub-Kamera - F10"); } } private void DrawWindow(int id) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Unknown result type (might be due to invalid IL or missing references) //IL_030d: Unknown result type (might be due to invalid IL or missing references) //IL_0362: Unknown result type (might be due to invalid IL or missing references) //IL_0391: Unknown result type (might be due to invalid IL or missing references) //IL_03c0: Unknown result type (might be due to invalid IL or missing references) //IL_03f0: Unknown result type (might be due to invalid IL or missing references) //IL_0424: Unknown result type (might be due to invalid IL or missing references) //IL_0485: Unknown result type (might be due to invalid IL or missing references) //IL_04e6: Unknown result type (might be due to invalid IL or missing references) //IL_0513: Unknown result type (might be due to invalid IL or missing references) float width = ((Rect)(ref _window)).width; if (DeathRunCounterFeature.Enabled) { GUI.Box(new Rect(20f, 38f, width - 40f, 30f), DeathRunCounterFeature.F10StatusLine); } if ((Object)(object)_preview != (Object)null) { GUI.DrawTexture(DeathRunCounterFeature.Enabled ? new Rect(20f, 72f, width - 40f, 356f) : new Rect(20f, 42f, width - 40f, 390f), (Texture)(object)_preview, (ScaleMode)2, false); } GUI.Label(new Rect(20f, 440f, 240f, 24f), (_postType == 2) ? "Fehlerbeschreibung (Pflicht):" : "Kurzer Posttext:"); _caption = GUI.TextArea(new Rect(20f, 466f, width - 40f, 62f), _caption ?? string.Empty, 500); GoalWebConfig[] array = GoalProgressFeature.GoalOptions(); GUI.Label(new Rect(20f, 536f, 120f, 24f), "Post-Typ:"); if (GUI.Button(new Rect(140f, 534f, 150f, 28f), (_postType == 0) ? "Community-Post" : ((_postType == 1) ? "Goal-Nachweis" : "Bug melden"))) { _postType = (_postType + 1) % 3; } GUI.Label(new Rect(310f, 536f, 70f, 24f), "Goal:"); string text = ((_goalIndex >= 0 && _goalIndex < array.Length) ? array[_goalIndex].label : "Kein Goal"); GUI.enabled = _postType != 2; if (GUI.Button(new Rect(380f, 534f, width - 400f, 28f), (_postType == 2) ? "Bei Bugs nicht erforderlich" : text) && array.Length != 0) { _goalIndex = (_goalIndex + 1) % array.Length; _goalKey = array[_goalIndex].key; _postType = 1; } GUI.enabled = true; string[] array2 = GoalProgressFeature.CommunityReportTargetLabels(); GUI.Label(new Rect(20f, 570f, 118f, 24f), "Weltsystem:"); string text2 = ((_communityTargetIndex >= 0 && _communityTargetIndex < array2.Length) ? array2[_communityTargetIndex] : "Keiner Instanz zuordnen"); if (GUI.Button(new Rect(140f, 568f, width - 160f, 28f), text2)) { _communityTargetIndex = ((array2.Length == 0 || _communityTargetIndex >= array2.Length - 1) ? (-1) : (_communityTargetIndex + 1)); } GUI.Label(new Rect(20f, 606f, 70f, 24f), "Format:"); if (GUI.Button(new Rect(88f, 604f, 100f, 28f), (_cropMode == 0) ? "Voll" : ((_cropMode == 1) ? "16:9" : "Quadrat"))) { _cropMode = (_cropMode + 1) % 3; } _hideHud = GUI.Toggle(new Rect(205f, 606f, 105f, 24f), _hideHud, "HUD aus"); _watermark = GUI.Toggle(new Rect(315f, 606f, 120f, 24f), _watermark, "Wasserzeichen"); _includePosition = GUI.Toggle(new Rect(440f, 606f, 140f, 24f), _includePosition, "Position senden"); if (GUI.Button(new Rect(width - 150f, 604f, 130f, 28f), "Neu aufnehmen")) { ((MonoBehaviour)this).StartCoroutine(Capture()); } GUI.Label(new Rect(20f, 636f, width - 40f, 24f), _status ?? string.Empty); GUI.enabled = !_uploading && _jpeg != null && (_postType != 2 || !string.IsNullOrWhiteSpace(_caption)); if (GUI.Button(new Rect(width - 330f, 672f, 190f, 34f), _uploading ? "Wird hochgeladen ..." : ((_postType == 2) ? "Bug melden" : "Post veroeffentlichen"))) { ((MonoBehaviour)this).StartCoroutine(Upload()); } GUI.enabled = !_uploading; if (GUI.Button(new Rect(width - 125f, 672f, 105f, 34f), "Abbrechen")) { Close(); } GUI.enabled = true; GUI.DragWindow(new Rect(0f, 0f, width, 30f)); } private IEnumerator Upload() { Player player = Player.m_localPlayer; if ((Object)(object)player == (Object)null || _jpeg == null) { yield break; } _uploading = true; _status = "Upload zu ChallengeHub ..."; Dictionary dictionary = new Dictionary(); dictionary.Add("challengeShortCode", Plugin.ChallengeShortCode.Value); dictionary.Add("playerId", player.GetPlayerID().ToString()); dictionary.Add("playerName", player.GetPlayerName()); dictionary.Add("twitchLogin", Plugin.TwitchLogin.Value); dictionary.Add("linkCode", Plugin.PlayerLinkCode.Value); dictionary.Add("caption", (_caption ?? string.Empty).Trim()); dictionary.Add("goalKey", (_goalKey ?? string.Empty).Trim()); dictionary.Add("postType", (_postType == 0) ? "screenshot" : ((_postType == 1) ? "evidence" : "bug")); dictionary.Add("position", _includePosition ? (((Component)player).transform.position.x + "," + ((Component)player).transform.position.y + "," + ((Component)player).transform.position.z) : string.Empty); dictionary.Add("imageBase64", Convert.ToBase64String(_jpeg)); Dictionary dictionary2 = dictionary; if (DeathRunCounterFeature.Enabled) { foreach (KeyValuePair item in DeathRunCounterFeature.RunFields()) { dictionary2[item.Key] = item.Value; } } string[] array = GoalProgressFeature.CommunityReportTargetIds(); if (_postType != 2 && _communityTargetIndex >= 0 && _communityTargetIndex < array.Length) { dictionary2["worldPlanId"] = array[_communityTargetIndex]; } if (_postType == 2) { dictionary2["logExcerpt"] = ReadSanitizedLogExcerpt(); } byte[] body = Encoding.UTF8.GetBytes(Plugin.ToJson(dictionary2)); yield return ChallengeHubApiTokenFeature.EnsureAvailable(); UnityWebRequest request = new UnityWebRequest(Plugin.ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/api/valheim/post", "POST"); try { request.uploadHandler = (UploadHandler)new UploadHandlerRaw(body); request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); request.SetRequestHeader("Content-Type", "application/json"); ChallengeHubApiTokenFeature.ApplyAuthorization(request); request.timeout = 30; yield return request.SendWebRequest(); PostResponse postResponse = null; try { postResponse = ChallengeHubJson.Deserialize(request.downloadHandler.text ?? string.Empty); } catch { } if ((int)request.result == 1 && postResponse != null && postResponse.ok) { ((Character)player).Message((MessageType)2, (_postType == 2) ? "ChallengeHub: Bug mit Screenshot gemeldet." : "ChallengeHub: Screenshot-Post veroeffentlicht.", 0, (Sprite)null); _uploading = false; Close(); yield break; } _status = "Upload fehlgeschlagen: " + ((postResponse != null && !string.IsNullOrWhiteSpace(postResponse.error)) ? postResponse.error : request.error); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("ChallengeHub Ingame-Kamera Upload fehlgeschlagen: " + request.responseCode + " / " + request.downloadHandler.text)); } } finally { ((IDisposable)request)?.Dispose(); } _uploading = false; } private static string ReadSanitizedLogExcerpt() { try { string path = Path.Combine(Paths.BepInExRootPath, "LogOutput.log"); if (!File.Exists(path)) { return "LogOutput.log wurde nicht gefunden."; } string[] array; using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)) { using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true); List list = new List(); string item; while ((item = streamReader.ReadLine()) != null) { list.Add(item); } array = list.ToArray(); } string input = string.Join("\n", array.Skip(Math.Max(0, array.Length - 180)).ToArray()); input = Regex.Replace(input, "(?im)^(\\s*(?:ApiKey|PlayerLinkCode|RegistrationSecret)\\s*=).*$", "$1 [ENTFERNT]"); input = Regex.Replace(input, "(?i)Bearer\\s+vht_[A-Za-z0-9_-]+", "Bearer [ENTFERNT]"); input = Regex.Replace(input, "vht_[A-Za-z0-9_-]{16,}", "[TOKEN ENTFERNT]"); input = Regex.Replace(input, "(?i)C:\\\\Users\\\\[^\\\\\\r\\n]+\\\\", "%USERPROFILE%\\"); if (input.Length > 16000) { input = input.Substring(input.Length - 16000); } return input; } catch (Exception ex) { return "Logausschnitt konnte nicht gelesen werden: " + ex.GetType().Name; } } } internal sealed class KillAttribution { internal string EventId; internal string CreaturePrefab; internal string CreatureName; internal string KillerPlayerId; internal string KillerPlayerName; internal Player KillerPlayer; internal Vector3 Position; internal string Biome; internal float Time; internal string Method; } internal sealed class KillAttributionTracker { private const float RecentKillWindowSeconds = 60f; private const float DropAttributionRadius = 18f; private readonly Dictionary _lastHits = new Dictionary(); private readonly Dictionary _deathByCharacterId = new Dictionary(); private readonly List _recentKills = new List(); internal void TrackLastHit(Character victim, HitData hit) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)victim == (Object)null) && !victim.IsPlayer() && hit != null) { Player val = TryGetAttackingPlayer(hit); if (!((Object)(object)val == (Object)null)) { _lastHits[((Object)victim).GetInstanceID()] = new KillAttribution { EventId = Guid.NewGuid().ToString("N"), CreaturePrefab = Plugin.NormalizeKey(((Object)victim).name), CreatureName = Plugin.NormalizeKey(victim.m_name), KillerPlayerId = val.GetPlayerID().ToString(), KillerPlayerName = val.GetPlayerName(), KillerPlayer = val, Position = ((Component)victim).transform.position, Biome = Plugin.CurrentBiome(((Component)victim).transform.position), Time = Time.realtimeSinceStartup, Method = "last_hit" }; } } } internal KillAttribution RegisterDeath(Character creature, Player fallbackPlayer) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013e: 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) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)creature == (Object)null) { return null; } int instanceID = ((Object)creature).GetInstanceID(); if (_deathByCharacterId.TryGetValue(instanceID, out var value)) { value.Position = ((Component)creature).transform.position; value.Biome = Plugin.CurrentBiome(((Component)creature).transform.position); return value; } KillAttribution value2 = null; if (_lastHits.TryGetValue(instanceID, out value2)) { _lastHits.Remove(instanceID); } if (value2 == null && (Object)(object)fallbackPlayer != (Object)null) { value2 = new KillAttribution { EventId = Guid.NewGuid().ToString("N"), CreaturePrefab = Plugin.NormalizeKey(((Object)creature).name), CreatureName = Plugin.NormalizeKey(creature.m_name), KillerPlayerId = fallbackPlayer.GetPlayerID().ToString(), KillerPlayerName = fallbackPlayer.GetPlayerName(), KillerPlayer = fallbackPlayer, Position = ((Component)creature).transform.position, Biome = Plugin.CurrentBiome(((Component)creature).transform.position), Time = Time.realtimeSinceStartup, Method = "nearest_player_fallback" }; } if (value2 == null) { return null; } value2.Position = ((Component)creature).transform.position; value2.Biome = Plugin.CurrentBiome(((Component)creature).transform.position); value2.Time = Time.realtimeSinceStartup; _deathByCharacterId[instanceID] = value2; _recentKills.Add(value2); Cleanup(); return value2; } internal KillAttribution FindRecentDropOwner(Vector3 position) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) Cleanup(); KillAttribution result = null; float num = float.MaxValue; foreach (KillAttribution recentKill in _recentKills) { float num2 = Vector3.Distance(position, recentKill.Position); if (num2 <= 18f && num2 < num) { result = recentKill; num = num2; } } return result; } internal KillAttribution FindRecentMatchingTrophyKill(Player player, string trophyName) { if ((Object)(object)player == (Object)null || string.IsNullOrWhiteSpace(trophyName)) { return null; } Cleanup(); string trophySpecies = TrophySpecies(trophyName); if (string.IsNullOrWhiteSpace(trophySpecies)) { return null; } return (from entry in _recentKills where entry != null && entry.KillerPlayerId == player.GetPlayerID().ToString() where string.Equals(entry.Method, "last_hit", StringComparison.OrdinalIgnoreCase) where Time.realtimeSinceStartup - entry.Time >= 0f && Time.realtimeSinceStartup - entry.Time <= 60f where TrophySpecies(entry.CreaturePrefab + " " + entry.CreatureName) == trophySpecies orderby entry.Time descending select entry).FirstOrDefault(); } internal static string TrophySpecies(string value) { string text = Plugin.NormalizeKey(value).Replace("_", "").Replace("trophy", "") .Replace("enemy", ""); string[] array = new string[42] { "greydwarfbrute", "greydwarfshaman", "skeletonpoison", "draugrelite", "fulingberserker", "fulingshaman", "seekerbrute", "charredwarrior", "charredmarksman", "charredmage", "fallenvalkyrie", "bonemawserpent", "deathsquito", "abomination", "greydwarf", "skeleton", "draugr", "fenring", "cultist", "goblin", "fuling", "seeker", "asksvin", "volture", "morgen", "golem", "growth", "surtling", "wraith", "troll", "ghost", "blob", "leech", "wolf", "drake", "boar", "deer", "neck", "lox", "gjall", "hare", "ulv" }; foreach (string text2 in array) { if (text.Contains(text2)) { if (!(text2 == "goblin")) { return text2; } return "fuling"; } } return ""; } private void Cleanup() { float now = Time.realtimeSinceStartup; foreach (int item in (from kv in _lastHits where now - kv.Value.Time > 60f select kv.Key).ToList()) { _lastHits.Remove(item); } foreach (int item2 in (from kv in _deathByCharacterId where now - kv.Value.Time > 60f select kv.Key).ToList()) { _deathByCharacterId.Remove(item2); } _recentKills.RemoveAll((KillAttribution entry) => now - entry.Time > 60f); } private Player TryGetAttackingPlayer(HitData hit) { try { Character attacker = hit.GetAttacker(); Player val = (Player)(object)((attacker is Player) ? attacker : null); if (val != null) { return val; } } catch { } return null; } } [Serializable] internal sealed class MeadowsSettlementRecord { public string key = string.Empty; public string characterId = string.Empty; public string playerId = string.Empty; public string playerName = string.Empty; public string[] authorizedCharacterIds = new string[0]; public float x; public float y; public float z; public float radius = 80f; public string assignedUtc = string.Empty; public string lastActivityUtc = string.Empty; public bool firstArrivalCompleted; internal Vector3 Center => new Vector3(x, y, z); } [Serializable] internal sealed class MeadowsSettlementSnapshot { public MeadowsSettlementRecord[] settlements = new MeadowsSettlementRecord[0]; } internal static class MeadowsSettlementFeature { private const string StateKind = "settlement"; private const string RpcRequest = "ChallengeHub_Settlement_Request_v280"; private const string RpcResponse = "ChallengeHub_Settlement_Response_v280"; private const string RpcSnapshot = "ChallengeHub_Settlement_Snapshot_v280"; private const string RpcActivity = "ChallengeHub_Settlement_Activity_v280"; private static Plugin _plugin; private static ConfigEntry _enabled; private static ConfigEntry _radius; private static ConfigEntry _minimumSpacing; private static ConfigEntry _searchMinRadius; private static ConfigEntry _searchMaxRadius; private static ConfigEntry _candidateAttempts; private static ConfigEntry _maxHeightVariance; private static ConfigEntry _teleportOnFirstActivation; private static ConfigEntry _setSpawn; private static ConfigEntry _ownerOnly; private static ConfigEntry _activityRadius; private static ConfigEntry _activeGraceHours; private static bool _rpcsRegistered; private static readonly Dictionary Records = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary RecordZdos = new Dictionary(StringComparer.Ordinal); private static bool _cacheLoaded; private static MeadowsSettlementRecord _local; private static bool _requestInFlight; private static float _lastRequestAt; private static float _lastActivityEventAt; internal static bool Enabled { get { SettlementWebConfig settlementWebConfig = Effective(); if (settlementWebConfig == null || settlementWebConfig.enabled) { if (_enabled != null) { return _enabled.Value; } return true; } return false; } } internal static float ProtectionRadius { get { SettlementWebConfig settlementWebConfig = Effective(); if (settlementWebConfig == null || !(settlementWebConfig.protectionRadius > 0f)) { return Mathf.Clamp(_radius?.Value ?? 80f, 30f, 180f); } return Mathf.Clamp(Effective().protectionRadius, 30f, 180f); } } internal static float ActivityRadius { get { SettlementWebConfig settlementWebConfig = Effective(); if (settlementWebConfig == null || !(settlementWebConfig.activityRadius > 0f)) { return Mathf.Clamp(_activityRadius?.Value ?? 32f, 10f, 100f); } return Mathf.Clamp(Effective().activityRadius, 10f, 100f); } } internal static float ActiveGraceHours { get { SettlementWebConfig settlementWebConfig = Effective(); if (settlementWebConfig == null || !(settlementWebConfig.activeGraceHours > 0f)) { return Mathf.Clamp(_activeGraceHours?.Value ?? 72f, 1f, 720f); } return Mathf.Clamp(Effective().activeGraceHours, 1f, 720f); } } internal static MeadowsSettlementRecord LocalAssignment => _local; private static SettlementWebConfig Effective() { return _plugin?.RemoteConfig?.settlements; } internal static void Initialize(Plugin plugin) { if (!((Object)(object)plugin == (Object)null)) { _plugin = plugin; _enabled = ((BaseUnityPlugin)plugin).Config.Bind("Settlements", "EnablePersonalMeadowsSettlements", true, "Vergibt jedem freigegebenen Spieler ein eigenes persistentes Meadows-Startgebiet; weitere Charaktere desselben Spielers teilen es."); _radius = ((BaseUnityPlugin)plugin).Config.Bind("Settlements", "ProtectionRadius", 80f, "Radius des persoenlichen Schutzgebiets."); _minimumSpacing = ((BaseUnityPlugin)plugin).Config.Bind("Settlements", "MinimumSpacing", 350f, "Mindestabstand zwischen persoenlichen Startgebieten."); _searchMinRadius = ((BaseUnityPlugin)plugin).Config.Bind("Settlements", "SearchMinRadius", 450f, "Kleinster Suchabstand vom Weltzentrum."); _searchMaxRadius = ((BaseUnityPlugin)plugin).Config.Bind("Settlements", "SearchMaxRadius", 5000f, "Groesster Suchabstand vom Weltzentrum."); _candidateAttempts = ((BaseUnityPlugin)plugin).Config.Bind("Settlements", "CandidateAttempts", 180, "Maximale Anzahl deterministischer Meadows-Kandidaten pro Vergabe."); _maxHeightVariance = ((BaseUnityPlugin)plugin).Config.Bind("Settlements", "MaximumHeightVariance", 8f, "Maximale Hoehendifferenz der Stichproben im Startgebiet."); _teleportOnFirstActivation = ((BaseUnityPlugin)plugin).Config.Bind("Settlements", "TeleportOnFirstActivation", true, "Teleportiert einen frisch freigegebenen Charakter einmalig in sein Startgebiet."); _setSpawn = ((BaseUnityPlugin)plugin).Config.Bind("Settlements", "SetCustomSpawnPoint", true, "Setzt das persoenliche Gebiet als Custom-Spawnpunkt."); _ownerOnly = ((BaseUnityPlugin)plugin).Config.Bind("Settlements", "OwnerOnlyBuilding", true, "Nur der zugewiesene Charakter darf im fremden Schutzkreis bauen oder entfernen."); _activityRadius = ((BaseUnityPlugin)plugin).Config.Bind("Settlements", "ActivityRadius", 32f, "Radius um Bau-/Nutzungsaktivitaet zur Pflege der Siedlung."); _activeGraceHours = ((BaseUnityPlugin)plugin).Config.Bind("Settlements", "ActiveGraceHours", 72f, "Zeit, in der eine Siedlungsaktivitaet Renaturierung/Verfall pausiert."); ((MonoBehaviour)plugin).StartCoroutine(RegisterRpcsWhenReady()); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Persoenliche Meadows-Siedlungen 2.8.0 initialisiert; kein Overworld-Zonenreset."); } } } internal static void RequestAssignmentForLocalPlayer() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown if (ChallengeHubServerGateFeature.GameplayAllowed && Enabled && ZRoutedRpc.instance != null && !((Object)(object)Player.m_localPlayer == (Object)null) && (!_requestInFlight || !(Time.realtimeSinceStartup - _lastRequestAt < 15f))) { _requestInFlight = true; _lastRequestAt = Time.realtimeSinceStartup; Player localPlayer = Player.m_localPlayer; ZPackage val = new ZPackage(); val.Write(localPlayer.GetPlayerID().ToString(CultureInfo.InvariantCulture)); val.Write(ResolveStablePlayerId(localPlayer)); val.Write(localPlayer.GetPlayerName() ?? string.Empty); val.Write(ChallengeHubWorldState.WorldUid.ToString(CultureInfo.InvariantCulture)); ZRoutedRpc.instance.InvokeRoutedRPC(ServerPeerId(), "ChallengeHub_Settlement_Request_v280", new object[1] { val }); } } internal static MeadowsSettlementRecord FindAt(Vector3 position) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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_002e: Unknown result type (might be due to invalid IL or missing references) EnsureCache(); foreach (MeadowsSettlementRecord value in Records.Values) { if (value != null) { Vector3 val = value.Center - position; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude <= value.radius * value.radius) { return value; } } } return null; } private static bool OwnsRecord(MeadowsSettlementRecord record, string characterId, string stablePlayerId) { if (record == null) { return false; } if (!string.IsNullOrWhiteSpace(stablePlayerId) && string.Equals(record.playerId, stablePlayerId, StringComparison.Ordinal)) { return true; } if (!string.IsNullOrWhiteSpace(characterId) && string.Equals(record.characterId, characterId, StringComparison.Ordinal)) { return true; } if (record.authorizedCharacterIds != null && !string.IsNullOrWhiteSpace(characterId)) { return record.authorizedCharacterIds.Contains(characterId, StringComparer.Ordinal); } return false; } private static void AuthorizeCharacter(MeadowsSettlementRecord record, string characterId) { if (record != null && !string.IsNullOrWhiteSpace(characterId)) { List list = (record.authorizedCharacterIds ?? new string[0]).Where((string value) => !string.IsNullOrWhiteSpace(value)).Distinct(StringComparer.Ordinal).ToList(); if (!list.Contains(characterId, StringComparer.Ordinal)) { list.Add(characterId); } record.authorizedCharacterIds = list.Take(16).ToArray(); } } internal static bool CanModify(Player player, Vector3 position, bool showMessage) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (!Enabled || (Object)(object)player == (Object)null || !EffectiveOwnerOnly()) { return true; } MeadowsSettlementRecord meadowsSettlementRecord = FindAt(position); if (meadowsSettlementRecord == null) { return true; } string characterId = player.GetPlayerID().ToString(CultureInfo.InvariantCulture); string stablePlayerId = ResolveStablePlayerId(player); bool num = OwnsRecord(meadowsSettlementRecord, characterId, stablePlayerId); if (!num && showMessage) { ((Character)player).Message((MessageType)2, "Dieses Meadows-Gebiet gehoert " + (meadowsSettlementRecord.playerName ?? "einem anderen Spieler") + ".", 0, (Sprite)null); } return num; } internal static bool CanCreatorModify(long creatorId, Vector3 position) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (!Enabled || creatorId == 0L || !EffectiveOwnerOnly()) { return true; } MeadowsSettlementRecord meadowsSettlementRecord = FindAt(position); if (meadowsSettlementRecord == null) { return true; } string characterId = creatorId.ToString(CultureInfo.InvariantCulture); return OwnsRecord(meadowsSettlementRecord, characterId, string.Empty); } internal static bool IsRecentlyActive(Vector3 position, float additionalGraceHours = 0f) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) MeadowsSettlementRecord meadowsSettlementRecord = FindAt(position); if (meadowsSettlementRecord == null) { return false; } DateTime dateTime = ParseUtc(meadowsSettlementRecord.lastActivityUtc); if (dateTime == DateTime.MinValue) { return false; } return DateTime.UtcNow - dateTime <= TimeSpan.FromHours(Mathf.Max(1f, ActiveGraceHours + additionalGraceHours)); } internal static void TouchLocal(Vector3 position, string reason) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown if (!ChallengeHubServerGateFeature.GameplayAllowed || !Enabled || ZRoutedRpc.instance == null || (Object)(object)Player.m_localPlayer == (Object)null) { return; } MeadowsSettlementRecord meadowsSettlementRecord = FindAt(position); if (meadowsSettlementRecord == null) { return; } string text = Player.m_localPlayer.GetPlayerID().ToString(CultureInfo.InvariantCulture); if (OwnsRecord(meadowsSettlementRecord, text, ResolveStablePlayerId(Player.m_localPlayer))) { ZPackage val = new ZPackage(); val.Write(meadowsSettlementRecord.key ?? string.Empty); val.Write(text); val.Write(reason ?? "activity"); val.Write(meadowsSettlementRecord.firstArrivalCompleted); ZRoutedRpc.instance.InvokeRoutedRPC(ServerPeerId(), "ChallengeHub_Settlement_Activity_v280", new object[1] { val }); if ((Object)(object)_plugin != (Object)null && Time.realtimeSinceStartup - _lastActivityEventAt >= 60f) { _lastActivityEventAt = Time.realtimeSinceStartup; SendWorldEvent("settlement_activity", meadowsSettlementRecord, reason); } } } internal static IEnumerable SnapshotRecords() { EnsureCache(); return Records.Values.Where((MeadowsSettlementRecord record) => record != null).ToArray(); } private static IEnumerator RegisterRpcsWhenReady() { while (ZRoutedRpc.instance == null) { yield return (object)new WaitForSeconds(1f); } if (!_rpcsRegistered) { ZRoutedRpc.instance.Register("ChallengeHub_Settlement_Request_v280", (Action)Rpc_Request); ZRoutedRpc.instance.Register("ChallengeHub_Settlement_Response_v280", (Action)Rpc_Response); ZRoutedRpc.instance.Register("ChallengeHub_Settlement_Snapshot_v280", (Action)Rpc_Snapshot); ZRoutedRpc.instance.Register("ChallengeHub_Settlement_Activity_v280", (Action)Rpc_Activity); _rpcsRegistered = true; if (ChallengeHubWorldState.IsServer) { EnsureCache(); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Meadows-Siedlungs-RPCs registriert."); } } } private static void Rpc_Request(long sender, ZPackage package) { //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) if (!ChallengeHubServerGateFeature.GameplayAllowed || !ChallengeHubWorldState.IsServer || package == null) { return; } string characterId = SafeReadString(package); string fallback = SafeReadString(package); string text = SafeReadString(package); string text2 = SafeReadString(package); if (string.IsNullOrWhiteSpace(characterId) || !long.TryParse(characterId, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || !DungeonTalentRewardFeature.ValidateSenderOwnsPlayer(sender, result)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Meadows-Siedlungsanfrage mit ungueltiger Sender-/Charakterbindung verworfen: Peer=" + sender + "; Charakter=" + characterId)); } return; } string playerId = ResolveStablePlayerIdFromSender(sender, fallback); if (!string.IsNullOrWhiteSpace(text2) && !string.Equals(text2, ChallengeHubWorldState.WorldUid.ToString(CultureInfo.InvariantCulture), StringComparison.Ordinal)) { return; } EnsureCache(); MeadowsSettlementRecord meadowsSettlementRecord = Records.Values.FirstOrDefault((MeadowsSettlementRecord meadowsSettlementRecord3) => meadowsSettlementRecord3 != null && !string.IsNullOrWhiteSpace(playerId) && string.Equals(meadowsSettlementRecord3.playerId, playerId, StringComparison.Ordinal)); if (meadowsSettlementRecord == null) { meadowsSettlementRecord = Records.Values.FirstOrDefault((MeadowsSettlementRecord meadowsSettlementRecord3) => meadowsSettlementRecord3 != null && string.Equals(meadowsSettlementRecord3.characterId, characterId, StringComparison.Ordinal)); } bool created = false; if (meadowsSettlementRecord == null) { Vector3 val = FindCandidate(characterId); string value = ((!string.IsNullOrWhiteSpace(playerId)) ? playerId : characterId); string key = "settlement:" + ChallengeHubWorldState.WorldUid.ToString(CultureInfo.InvariantCulture) + ":" + SanitizeKeyToken(value); MeadowsSettlementRecord meadowsSettlementRecord2 = new MeadowsSettlementRecord(); meadowsSettlementRecord2.key = key; meadowsSettlementRecord2.characterId = characterId; meadowsSettlementRecord2.playerId = playerId; meadowsSettlementRecord2.playerName = text; meadowsSettlementRecord2.authorizedCharacterIds = new string[1] { characterId }; meadowsSettlementRecord2.x = val.x; meadowsSettlementRecord2.y = val.y; meadowsSettlementRecord2.z = val.z; meadowsSettlementRecord2.radius = ProtectionRadius; meadowsSettlementRecord2.assignedUtc = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture); meadowsSettlementRecord2.lastActivityUtc = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture); meadowsSettlementRecord2.firstArrivalCompleted = false; meadowsSettlementRecord = meadowsSettlementRecord2; ZDO val2 = ChallengeHubWorldState.Resolve("settlement", key, new Vector3(val.x, -23000f, val.z), create: true); if (val2 == null || !ChallengeHubWorldState.WritePayload(val2, JsonUtility.ToJson((object)meadowsSettlementRecord), "settlement_create")) { return; } Records[key] = meadowsSettlementRecord; RecordZdos[key] = val2; created = true; SendWorldEvent("settlement_assigned", meadowsSettlementRecord, "created"); } else { meadowsSettlementRecord.characterId = characterId; AuthorizeCharacter(meadowsSettlementRecord, characterId); if (!string.IsNullOrWhiteSpace(playerId)) { meadowsSettlementRecord.playerId = playerId; } if (!string.IsNullOrWhiteSpace(text)) { meadowsSettlementRecord.playerName = text; } meadowsSettlementRecord.radius = ProtectionRadius; Persist(meadowsSettlementRecord, "settlement_refresh"); } SendResponse(sender, meadowsSettlementRecord, created); SendSnapshot(sender); } private static void Rpc_Response(long sender, ZPackage package) { //IL_0083: 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) if (package == null || !TrustedServer(sender)) { return; } string text = SafeReadString(package); bool flag = SafeReadBool(package); MeadowsSettlementRecord meadowsSettlementRecord = null; try { meadowsSettlementRecord = JsonUtility.FromJson(text); } catch { } if (meadowsSettlementRecord == null || string.IsNullOrWhiteSpace(meadowsSettlementRecord.key)) { return; } _requestInFlight = false; _local = meadowsSettlementRecord; Records[meadowsSettlementRecord.key] = meadowsSettlementRecord; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Persoenliches Meadows-Gebiet empfangen: " + meadowsSettlementRecord.key + " @ " + ((object)meadowsSettlementRecord.Center/*cast due to .constrained prefix*/).ToString() + "; Radius=" + meadowsSettlementRecord.radius.ToString("0", CultureInfo.InvariantCulture))); } SendWorldEvent("settlement_assigned", meadowsSettlementRecord, flag ? "created" : "existing"); if (!meadowsSettlementRecord.firstArrivalCompleted && EffectiveTeleportOnFirstActivation()) { Plugin plugin = _plugin; if (plugin != null) { ((MonoBehaviour)plugin).StartCoroutine(MoveLocalPlayerToSettlement(meadowsSettlementRecord)); } } } private static void Rpc_Snapshot(long sender, ZPackage package) { if (package == null || !TrustedServer(sender)) { return; } string text = SafeReadString(package); MeadowsSettlementSnapshot meadowsSettlementSnapshot = null; try { meadowsSettlementSnapshot = JsonUtility.FromJson(text); } catch { } if (meadowsSettlementSnapshot?.settlements == null) { return; } Records.Clear(); MeadowsSettlementRecord[] settlements = meadowsSettlementSnapshot.settlements; foreach (MeadowsSettlementRecord meadowsSettlementRecord in settlements) { if (meadowsSettlementRecord != null && !string.IsNullOrWhiteSpace(meadowsSettlementRecord.key)) { AuthorizeCharacter(meadowsSettlementRecord, meadowsSettlementRecord.characterId); Records[meadowsSettlementRecord.key] = meadowsSettlementRecord; } } } private static void Rpc_Activity(long sender, ZPackage package) { if (!ChallengeHubServerGateFeature.GameplayAllowed || !ChallengeHubWorldState.IsServer || package == null) { return; } string key = SafeReadString(package); string text = SafeReadString(package); string text2 = SafeReadString(package); bool flag = SafeReadBool(package); if (!long.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || !DungeonTalentRewardFeature.ValidateSenderOwnsPlayer(sender, result)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Meadows-Siedlungsaktivitaet mit ungueltiger Sender-/Charakterbindung verworfen: Peer=" + sender + "; Charakter=" + text)); } return; } EnsureCache(); if (Records.TryGetValue(key, out var value) && value != null && OwnsRecord(value, text, ResolveStablePlayerIdFromSender(sender, string.Empty))) { value.lastActivityUtc = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture); if (flag || string.Equals(text2, "first_arrival", StringComparison.Ordinal)) { value.firstArrivalCompleted = true; } Persist(value, "settlement_activity"); SendWorldEvent("settlement_activity", value, text2); } } private static void SendResponse(long peer, MeadowsSettlementRecord record, bool created) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown if (ZRoutedRpc.instance != null && record != null) { ZPackage val = new ZPackage(); val.Write(JsonUtility.ToJson((object)record)); val.Write(created); ZRoutedRpc.instance.InvokeRoutedRPC(peer, "ChallengeHub_Settlement_Response_v280", new object[1] { val }); } } private static void SendSnapshot(long peer) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown if (ZRoutedRpc.instance != null) { EnsureCache(); MeadowsSettlementSnapshot meadowsSettlementSnapshot = new MeadowsSettlementSnapshot { settlements = Records.Values.Where((MeadowsSettlementRecord record) => record != null).ToArray() }; ZPackage val = new ZPackage(); val.Write(JsonUtility.ToJson((object)meadowsSettlementSnapshot)); ZRoutedRpc.instance.InvokeRoutedRPC(peer, "ChallengeHub_Settlement_Snapshot_v280", new object[1] { val }); } } private static IEnumerator MoveLocalPlayerToSettlement(MeadowsSettlementRecord record) { Player player = Player.m_localPlayer; if ((Object)(object)player == (Object)null || record == null) { yield break; } Vector3 center = record.Center; Vector2i zone = ZoneSystem.GetZone(center); ValheimPrivateAccess.TryPokeLocalZone(ZoneSystem.instance, zone, out var _); float deadline = Time.realtimeSinceStartup + 20f; float ground = center.y; while (Time.realtimeSinceStartup < deadline && !Heightmap.GetHeight(center, ref ground)) { yield return (object)new WaitForSeconds(0.25f); } center.y = ground + 1.2f; bool flag = false; try { flag = ((Character)player).TeleportTo(center, Quaternion.identity, true); } catch { } if (!flag) { try { ((Component)player).transform.position = center; } catch { } } if (EffectiveSetSpawn()) { try { Game instance = Game.instance; if (instance != null) { PlayerProfile playerProfile = instance.GetPlayerProfile(); if (playerProfile != null) { playerProfile.SetCustomSpawnPoint(center); } } } catch { } } record.firstArrivalCompleted = true; _local = record; TouchLocal(center, "first_arrival"); ((Character)player).Message((MessageType)2, "Dein persoenliches Meadows-Startgebiet wurde zugewiesen.", 0, (Sprite)null); } private static Vector3 FindCandidate(string characterId) { //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Invalid comparison between Unknown and I4 //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) EnsureCache(); SettlementWebConfig settlementWebConfig = Effective(); float num = ((settlementWebConfig != null && settlementWebConfig.searchMinRadius > 0f) ? settlementWebConfig.searchMinRadius : (_searchMinRadius?.Value ?? 450f)); float num2 = ((settlementWebConfig != null && settlementWebConfig.searchMaxRadius > num) ? settlementWebConfig.searchMaxRadius : (_searchMaxRadius?.Value ?? 5000f)); float num3 = ((settlementWebConfig != null && settlementWebConfig.minimumSpacing > 0f) ? settlementWebConfig.minimumSpacing : (_minimumSpacing?.Value ?? 350f)); int num4 = ((settlementWebConfig != null && settlementWebConfig.candidateAttempts > 0) ? settlementWebConfig.candidateAttempts : (_candidateAttempts?.Value ?? 180)); float num5 = ((settlementWebConfig != null && settlementWebConfig.maximumHeightVariance > 0f) ? settlementWebConfig.maximumHeightVariance : (_maxHeightVariance?.Value ?? 8f)); num = Mathf.Clamp(num, 128f, 8000f); num2 = Mathf.Clamp(num2, num + 128f, 9500f); num4 = Mathf.Clamp(num4, 20, 800); Random random = new Random(StringExtensionMethods.GetStableHashCode(characterId ?? string.Empty) ^ (int)ChallengeHubWorldState.WorldUid); Vector3 val = Vector3.zero; float num6 = float.MinValue; Vector3 val2 = default(Vector3); for (int i = 0; i < num4; i++) { double num7 = random.NextDouble() * Math.PI * 2.0; float num8 = ((num4 <= 1) ? 0f : ((float)i / (float)(num4 - 1))); float num9 = Mathf.Lerp(num, num2, Mathf.Sqrt(Mathf.Clamp01(num8))); num9 += (float)(random.NextDouble() - 0.5) * 120f; float num10 = Mathf.Cos((float)num7) * num9; float num11 = Mathf.Sin((float)num7) * num9; ((Vector3)(ref val2))..ctor(num10, 0f, num11); if (WorldGenerator.instance == null) { continue; } Biome biome; float height; try { biome = WorldGenerator.instance.GetBiome(num10, num11, 0.02f, false); height = WorldGenerator.instance.GetHeight(num10, num11); } catch { continue; } val2.y = height; if ((int)biome == 1 && !(height < 31.5f)) { float num12 = HeightVariance(val2, 14f); float num13 = NearestSettlementDistance(val2); float num14 = NearestLocationDistance(val2); float num15 = Mathf.Min(num13, 1500f) + Mathf.Min(num14, 500f) - num12 * 30f; if (num15 > num6) { num6 = num15; val = val2; } if (num12 <= num5 && num13 >= num3 && num14 >= 80f) { return val2; } } } if (val != Vector3.zero) { return val; } float num16 = ((WorldGenerator.instance != null) ? WorldGenerator.instance.GetHeight(num, 0f) : 35f); return new Vector3(num, num16, 0f); } private static float HeightVariance(Vector3 center, float radius) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (WorldGenerator.instance == null) { return float.MaxValue; } float num = center.y; float num2 = center.y; for (int i = 0; i < 8; i++) { float num3 = (float)i * (float)Math.PI * 0.25f; float num4 = center.x + Mathf.Cos(num3) * radius; float num5 = center.z + Mathf.Sin(num3) * radius; try { float height = WorldGenerator.instance.GetHeight(num4, num5); num = Mathf.Min(num, height); num2 = Mathf.Max(num2, height); } catch { return float.MaxValue; } } return num2 - num; } private static float NearestSettlementDistance(Vector3 candidate) { //IL_0024: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) float num = float.MaxValue; foreach (MeadowsSettlementRecord value in Records.Values) { if (value != null) { Vector3 val = value.Center - candidate; val.y = 0f; num = Mathf.Min(num, ((Vector3)(ref val)).magnitude); } } return num; } private static float NearestLocationDistance(Vector3 candidate) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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) //IL_002a: Unknown result type (might be due to invalid IL or missing references) float num = float.MaxValue; try { foreach (LocationInstance item in ValheimPrivateAccess.SnapshotLocationInstances(ZoneSystem.instance)) { Vector3 val = item.m_position - candidate; val.y = 0f; num = Mathf.Min(num, ((Vector3)(ref val)).magnitude); } } catch { } return num; } private static void EnsureCache() { if (_cacheLoaded) { return; } _cacheLoaded = true; Records.Clear(); RecordZdos.Clear(); foreach (ZDO item in ChallengeHubWorldState.Snapshot("settlement")) { try { MeadowsSettlementRecord meadowsSettlementRecord = JsonUtility.FromJson(ChallengeHubWorldState.ReadPayload(item)); if (meadowsSettlementRecord != null && !string.IsNullOrWhiteSpace(meadowsSettlementRecord.key)) { AuthorizeCharacter(meadowsSettlementRecord, meadowsSettlementRecord.characterId); Records[meadowsSettlementRecord.key] = meadowsSettlementRecord; RecordZdos[meadowsSettlementRecord.key] = item; } } catch { } } } private static void Persist(MeadowsSettlementRecord record, string reason) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) if (ChallengeHubWorldState.IsServer && record != null && !string.IsNullOrWhiteSpace(record.key)) { if (!RecordZdos.TryGetValue(record.key, out var value) || value == null || !value.IsValid()) { value = ChallengeHubWorldState.Resolve("settlement", record.key, new Vector3(record.x, -23000f, record.z), create: true); RecordZdos[record.key] = value; } if (value != null) { ChallengeHubWorldState.WritePayload(value, JsonUtility.ToJson((object)record), reason); } } } private static void SendWorldEvent(string type, MeadowsSettlementRecord record, string reason) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_plugin == (Object)null) && record != null) { Dictionary extra = new Dictionary { { "settlementKey", record.key }, { "characterId", record.characterId }, { "ownerPlayerId", record.playerId }, { "ownerPlayerName", record.playerName }, { "center", ChallengeHubWorldState.Vector(record.Center) }, { "radius", record.radius }, { "reason", reason ?? string.Empty }, { "assignedAt", record.assignedUtc ?? string.Empty }, { "lastActivityAt", record.lastActivityUtc ?? string.Empty } }; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { _plugin.SendEvent(type, localPlayer, extra); } else { _plugin.SendServerEvent(type, extra); } } } private static bool EffectiveOwnerOnly() { SettlementWebConfig settlementWebConfig = Effective(); if (settlementWebConfig == null) { if (_ownerOnly != null) { return _ownerOnly.Value; } return true; } return settlementWebConfig.ownerOnlyBuilding; } internal static string ResolveStablePlayerId(Player player) { if ((Object)(object)player == (Object)null) { return string.Empty; } try { Type type = AccessTools.TypeByName("PrivilegeManager"); string text = Convert.ToString(((type != null) ? AccessTools.Method(type, "GetNetworkUserId", Type.EmptyTypes, (Type[])null) : null)?.Invoke(null, null), CultureInfo.InvariantCulture); if (!string.IsNullOrWhiteSpace(text)) { return text.Trim(); } } catch { } return player.GetPlayerID().ToString(CultureInfo.InvariantCulture); } private static string ResolveStablePlayerIdFromSender(long sender, string fallback) { try { ZNet instance = ZNet.instance; ZNetPeer val = ((instance != null) ? instance.GetPeer(sender) : null); if (val != null) { object obj = null; FieldInfo field = ((object)val).GetType().GetField("m_socket", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { obj = field.GetValue(val); } if (obj == null) { MethodInfo method = ((object)val).GetType().GetMethod("GetSocket", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method != null) { obj = method.Invoke(val, null); } } if (obj != null) { string text = Convert.ToString(obj.GetType().GetMethod("GetHostName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null)?.Invoke(obj, null), CultureInfo.InvariantCulture); if (!string.IsNullOrWhiteSpace(text)) { return text.Trim(); } } } } catch { } if (!string.IsNullOrWhiteSpace(fallback)) { return fallback.Trim(); } return string.Empty; } private static string SanitizeKeyToken(string value) { if (string.IsNullOrWhiteSpace(value)) { return "unknown"; } string text = new string((from character in value.Trim() select (!char.IsLetterOrDigit(character) && character != '-' && character != '_') ? '_' : character).ToArray()); if (text.Length <= 96) { return text; } return text.Substring(0, 96); } private static bool EffectiveTeleportOnFirstActivation() { SettlementWebConfig settlementWebConfig = Effective(); if (settlementWebConfig == null) { if (_teleportOnFirstActivation != null) { return _teleportOnFirstActivation.Value; } return true; } return settlementWebConfig.teleportOnFirstActivation; } private static bool EffectiveSetSpawn() { SettlementWebConfig settlementWebConfig = Effective(); if (settlementWebConfig == null) { if (_setSpawn != null) { return _setSpawn.Value; } return true; } return settlementWebConfig.setCustomSpawnPoint; } private static long ServerPeerId() { try { MethodInfo methodInfo = AccessTools.Method(((object)ZRoutedRpc.instance)?.GetType(), "GetServerPeerID", (Type[])null, (Type[])null) ?? AccessTools.Method(((object)ZRoutedRpc.instance)?.GetType(), "GetServerPeerId", (Type[])null, (Type[])null); if (methodInfo != null) { return Convert.ToInt64(methodInfo.Invoke(ZRoutedRpc.instance, null)); } } catch { } return 0L; } private static bool TrustedServer(long sender) { if (ChallengeHubWorldState.IsServer && sender == ValheimNetworkCompatibility.ResolveLocalPeerId()) { return true; } return sender == ServerPeerId(); } private static string SafeReadString(ZPackage package) { try { return package.ReadString(); } catch { return string.Empty; } } private static bool SafeReadBool(ZPackage package) { try { return package.ReadBool(); } catch { return false; } } private static DateTime ParseUtc(string raw) { if (!DateTime.TryParse(raw, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out var result)) { return DateTime.MinValue; } return result; } } [HarmonyPatch(typeof(Player), "PlacePiece", new Type[] { typeof(Piece), typeof(Vector3), typeof(Quaternion), typeof(bool) })] internal static class SettlementBuildProtectionPatch { [HarmonyPrefix] private static bool Prefix(Player __instance) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } GameObject placementGhost = PlayerBuildReflection.GetPlacementGhost(__instance); if ((Object)(object)placementGhost == (Object)null) { return true; } return MeadowsSettlementFeature.CanModify(__instance, placementGhost.transform.position, showMessage: true); } [HarmonyPostfix] private static void Postfix(Player __instance) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { MeadowsSettlementFeature.TouchLocal(((Component)__instance).transform.position, "piece_placed"); } } } [HarmonyPatch(typeof(Player), "RemovePiece")] internal static class SettlementRemoveProtectionPatch { [HarmonyPrefix] private static bool Prefix(Player __instance, ref bool __result) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } Piece val = RaycastPiece(); if ((Object)(object)val == (Object)null) { return true; } if (MeadowsSettlementFeature.CanModify(__instance, ((Component)val).transform.position, showMessage: true)) { return true; } __result = false; return false; } [HarmonyPostfix] private static void Postfix(Player __instance, bool __result) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (__result && (Object)(object)__instance == (Object)(object)Player.m_localPlayer) { MeadowsSettlementFeature.TouchLocal(((Component)__instance).transform.position, "piece_removed"); } } private static Piece RaycastPiece() { //IL_001e: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)GameCamera.instance == (Object)null) { return null; } foreach (RaycastHit item in from value in Physics.RaycastAll(new Ray(((Component)GameCamera.instance).transform.position, ((Component)GameCamera.instance).transform.forward), 50f, -1, (QueryTriggerInteraction)1) orderby ((RaycastHit)(ref value)).distance select value) { RaycastHit current = item; Piece val = (((Object)(object)((RaycastHit)(ref current)).collider != (Object)null) ? ((Component)((RaycastHit)(ref current)).collider).GetComponentInParent() : null); if ((Object)(object)val != (Object)null) { return val; } } } catch { } return null; } } [HarmonyPatch(typeof(WearNTear), "Damage")] internal static class SettlementDamageProtectionPatch { [HarmonyPrefix] private static bool Prefix(WearNTear __instance, HitData hit) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || hit == null) { return true; } Player val = null; try { object? obj = AccessTools.Method(((object)hit).GetType(), "GetAttacker", (Type[])null, (Type[])null)?.Invoke(hit, null); val = (Player)((obj is Player) ? obj : null); } catch { } if ((Object)(object)val == (Object)null) { return true; } return MeadowsSettlementFeature.CanModify(val, ((Component)__instance).transform.position, (Object)(object)val == (Object)(object)Player.m_localPlayer); } } [HarmonyPatch(typeof(Piece), "SetCreator")] internal static class SettlementServerCreatorEnforcementPatch { [HarmonyPostfix] private static void Postfix(Piece __instance, long uid) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (!ChallengeHubWorldState.IsServer || (Object)(object)__instance == (Object)null || uid == 0L || MeadowsSettlementFeature.CanCreatorModify(uid, ((Component)__instance).transform.position)) { return; } try { ZNetView val = ((Component)__instance).GetComponent() ?? ((Component)__instance).GetComponentInParent(); ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if (val2 != null) { ServerAuthoritativeZdoDestroyer.Destroy(val2, ServerAuthoritativeZdoDestroyer.CollectProtectedPlayerZdos(), "foreign_settlement_build"); ServerAuthoritativeZdoDestroyer.FlushDestroyed("foreign_settlement_build"); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Nicht berechtigtes Bauteil im persoenlichen Meadows-Gebiet serverseitig entfernt: " + Utils.GetPrefabName(((Component)__instance).gameObject))); } } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Siedlungsschutz konnte fremdes Bauteil nicht entfernen: " + ex.Message)); } } } } internal static class NaturalLocationRestorationFeature { private static Plugin _plugin; private static ConfigEntry _enabled; private static ConfigEntry _scanSeconds; private static ConfigEntry _excludedLocations; private static readonly HashSet PendingZoneReleases = new HashSet(); private static bool _processing; private static ZoneRestorationWebConfig Remote => _plugin?.RemoteConfig?.zoneRestoration; internal static void Initialize(Plugin plugin) { _plugin = plugin; _enabled = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.NaturalLocations", "Enabled", true, "Regeneriert natuerliche Locations und deren natuerliche Truhen nach faelliger Zonen-Inaktivitaet."); _scanSeconds = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.NaturalLocations", "ScanSeconds", 60f, "Intervall fuer faellige natuerliche Locations."); _excludedLocations = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.NaturalLocations", "ExcludedLocationKeywords", "start,spawn,altar,boss,offer,trader,haldor,hildir,bogwitch,witch,queen,fader,crypt,burial,cave,mine,dungeon,sunken", "Einzigartige oder progressionkritische Locations, die niemals automatisch regeneriert werden."); ((MonoBehaviour)plugin).StartCoroutine(RestorationLoop()); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Natuerliche Location-Restauration aktiv: serverautoritativ, ohne Spieler-ZDO- oder Terrain-Zonenreset."); } } private static IEnumerator RestorationLoop() { while (true) { ZoneRestorationWebConfig remote = Remote; yield return (object)new WaitForSeconds(Mathf.Clamp((remote != null && remote.naturalLocationScanSeconds > 0f) ? Remote.naturalLocationScanSeconds : (_scanSeconds?.Value ?? 60f), 20f, 1800f)); if (_enabled == null || !_enabled.Value) { continue; } ZoneRestorationWebConfig remote2 = Remote; if ((remote2 == null || remote2.restoreNaturalLocations) && ChallengeHubWorldState.IsServer && ChallengeHubServerGateFeature.GameplayAllowed && !_processing) { Vector2i[] array = ZoneActivityRestorationFeature.DueZones().Take(1).ToArray(); if (array.Length != 0) { _processing = true; yield return RestoreLocation(array[0]); _processing = false; } } } } private unsafe static IEnumerator RestoreLocation(Vector2i zone) { //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) if (ZoneActivityRestorationFeature.IsOccupied(zone)) { yield break; } ZoneSystem system = ZoneSystem.instance; if ((Object)(object)system == (Object)null) { yield break; } bool manuallyLoaded = !system.IsZoneLoaded(zone); if (manuallyLoaded && !ValheimPrivateAccess.TryPokeLocalZone(system, zone, out var error)) { ManualLogSource log = Plugin.Log; if (log != null) { Vector2i val = zone; log.LogWarning((object)("Natuerliche Zone konnte nicht geladen werden: " + ((object)(*(Vector2i*)(&val))/*cast due to .constrained prefix*/).ToString() + " :: " + error)); } yield break; } for (int wait = 0; wait < 90; wait++) { if (system.IsZoneLoaded(zone)) { break; } yield return null; } if (!system.IsZoneLoaded(zone) || !ValheimPrivateAccess.TryGetZoneRoot(system, zone, out var zoneRoot)) { if (manuallyLoaded) { QueueZoneRelease(zone, Vector3.zero, "unvollstaendig"); } yield break; } if (!system.m_locationInstances.TryGetValue(zone, out var location) || !ValheimPrivateAccess.TryGetLocationMetadata(location, out var locationName, out var exteriorRadius)) { if (manuallyLoaded) { QueueZoneRelease(zone, zoneRoot.transform.position, "keine_location"); } yield break; } if (Excluded(locationName)) { ZoneActivityRestorationFeature.MarkReset(zone); if (manuallyLoaded) { QueueZoneRelease(zone, location.m_position, locationName); } yield break; } Vector3 center = location.m_position; float effectiveRadius = Mathf.Max(12f, exteriorRadius + 6f); List source = (from zdo in ValheimPrivateAccess.SnapshotZoneZdos(zone) where zdo != null && zdo.IsValid() select zdo).ToList(); if (source.Any((ZDO zdo) => IsPlayerObject(zdo) && Utils.DistanceXZ(zdo.GetPosition(), center) <= effectiveRadius)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { string text = locationName; Vector2i val = zone; log2.LogInfo((object)("Natuerliche Location-Restauration durch Spielerobjekt blockiert: " + text + " / " + ((object)(*(Vector2i*)(&val))/*cast due to .constrained prefix*/).ToString())); } if (manuallyLoaded) { QueueZoneRelease(zone, center, locationName); } yield break; } if (source.Any((ZDO zdo) => !IsPlayerObject(zdo) && Utils.DistanceXZ(zdo.GetPosition(), center) <= effectiveRadius && HasStoredItems(zdo))) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { string text2 = locationName; Vector2i val = zone; log3.LogInfo((object)("Natuerliche Location-Restauration wegen gefuellter Originaltruhe verschoben: " + text2 + " / " + ((object)(*(Vector2i*)(&val))/*cast due to .constrained prefix*/).ToString())); } if (manuallyLoaded) { QueueZoneRelease(zone, center, locationName); } yield break; } List candidates = source.Where((ZDO zdo) => !IsPlayerObject(zdo) && Utils.DistanceXZ(zdo.GetPosition(), center) <= effectiveRadius && IsNaturalLocationObject(zdo)).ToList(); if (candidates.Count == 0) { if (manuallyLoaded) { QueueZoneRelease(zone, center, locationName); } yield break; } HashSet protectedPlayers = ServerAuthoritativeZdoDestroyer.CollectProtectedPlayerZdos(); int batch = 0; foreach (ZDO item in candidates) { ServerAuthoritativeZdoDestroyer.Destroy(item, protectedPlayers, "natural_location_restore:" + locationName); int num = batch + 1; batch = num; if (num >= 24) { ServerAuthoritativeZdoDestroyer.FlushDestroyed("natural_location_restore_batch"); batch = 0; yield return null; } } ServerAuthoritativeZdoDestroyer.FlushDestroyed("natural_location_restore_final"); for (int wait = 0; wait < 12; wait++) { yield return null; } location.m_placed = false; system.m_locationInstances[zone] = location; if (!ValheimPrivateAccess.TryPlaceLocations(system, zone, zoneRoot, out var temporaryObjects, out var error2)) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)("Natuerliche Location konnte nicht neu platziert werden: " + locationName + " :: " + error2)); } if (manuallyLoaded) { QueueZoneRelease(zone, center, locationName); } yield break; } foreach (GameObject item2 in temporaryObjects) { if ((Object)(object)item2 != (Object)null) { Object.Destroy((Object)(object)item2); } } for (int wait = 0; wait < 12; wait++) { yield return null; } ZoneActivityRestorationFeature.MarkReset(zone); _plugin?.SendServerEvent("natural_location_restored", new Dictionary { { "zone", zone.x + ":" + zone.y }, { "biome", (WorldGenerator.instance != null) ? ((object)WorldGenerator.instance.GetBiome(center)/*cast due to .constrained prefix*/).ToString() : string.Empty }, { "location", locationName }, { "restoredObjects", candidates.Count }, { "restoredAt", DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture) } }); ManualLogSource log5 = Plugin.Log; if (log5 != null) { string[] obj = new string[6] { "Natuerliche Location regeneriert: ", locationName, "; Zone=", null, null, null }; Vector2i val = zone; obj[3] = ((object)(*(Vector2i*)(&val))/*cast due to .constrained prefix*/).ToString(); obj[4] = "; Objekte="; obj[5] = candidates.Count.ToString(); log5.LogInfo((object)string.Concat(obj)); } if (manuallyLoaded) { QueueZoneRelease(zone, center, locationName); } } private static bool HasStoredItems(ZDO zdo) { if (!ValheimPrivateAccess.TryGetPrefab(ZNetScene.instance, zdo.GetPrefab(), out var prefab) || (Object)(object)prefab == (Object)null || (Object)(object)prefab.GetComponentInChildren(true) == (Object)null) { return false; } if (!ValheimPrivateAccess.TryGetSceneInstance(ZNetScene.instance, zdo, out var view) || (Object)(object)view == (Object)null) { return true; } Container componentInChildren = ((Component)view).GetComponentInChildren(true); try { return (Object)(object)componentInChildren == (Object)null || componentInChildren.GetInventory() == null || componentInChildren.GetInventory().NrOfItems() > 0; } catch { return true; } } private static void QueueZoneRelease(Vector2i zone, Vector3 center, string locationName) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_plugin == (Object)null) && PendingZoneReleases.Add(zone)) { ((MonoBehaviour)_plugin).StartCoroutine(ReleaseZoneWhenUnused(zone, center, locationName)); } } private static IEnumerator ReleaseZoneWhenUnused(Vector2i zone, Vector3 center, string locationName) { //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) float clearSeconds = 0f; while ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.IsZoneLoaded(zone)) { if (ZoneActivityRestorationFeature.IsOccupied(zone)) { clearSeconds = 0f; } else { float num; clearSeconds = (num = clearSeconds + 2f); if (num >= 10f) { break; } } yield return (object)new WaitForSeconds(2f); } if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.IsZoneLoaded(zone) && !ZoneActivityRestorationFeature.IsOccupied(zone)) { try { ZNetScene instance = ZNetScene.instance; foreach (ZDO item in ValheimPrivateAccess.SnapshotZoneZdos(zone)) { if (!((Object)(object)instance == (Object)null) && ValheimPrivateAccess.TryGetSceneInstance(instance, item, out var view)) { GameObject val = (((Object)(object)view != (Object)null) ? ((Component)view).gameObject : null); if (view != null) { view.ResetZDO(); } ValheimPrivateAccess.RemoveSceneInstance(instance, item); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } } if (ValheimPrivateAccess.TryRemoveZoneRoot(ZoneSystem.instance, zone, out var root) && (Object)(object)root != (Object)null) { Object.Destroy((Object)(object)root); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Natuerliche Zone konnte nicht freigegeben werden: " + locationName + " -> " + ex.Message)); } } } PendingZoneReleases.Remove(zone); } private static bool IsPlayerObject(ZDO zdo) { try { return zdo.GetLong(ZDOVars.s_creator, 0L) != 0; } catch { return true; } } private static bool IsNaturalLocationObject(ZDO zdo) { if (!ValheimPrivateAccess.TryGetPrefab(ZNetScene.instance, zdo.GetPrefab(), out var prefab) || (Object)(object)prefab == (Object)null) { return false; } if ((Object)(object)prefab.GetComponentInChildren(true) != (Object)null) { return false; } if ((Object)(object)prefab.GetComponentInChildren(true) != (Object)null || (Object)(object)prefab.GetComponentInChildren(true) != (Object)null) { return false; } string text = Utils.GetPrefabName(prefab).ToLowerInvariant(); if (Excluded(text)) { return false; } if (!((Object)(object)prefab.GetComponentInChildren(true) != (Object)null) && !((Object)(object)prefab.GetComponentInChildren(true) != (Object)null) && !((Object)(object)prefab.GetComponentInChildren(true) != (Object)null) && !text.Contains("spawner") && !text.Contains("destructible")) { return text.Contains("locationproxy"); } return true; } private static bool Excluded(string name) { string lower = (name ?? string.Empty).ToLowerInvariant(); return (from value in ((!string.IsNullOrWhiteSpace(Remote?.excludedNaturalLocationKeywords)) ? Remote.excludedNaturalLocationKeywords : (_excludedLocations?.Value ?? string.Empty)).Split(new char[1] { ',' }) select value.Trim().ToLowerInvariant()).Any((string value) => value.Length > 0 && lower.Contains(value)); } } internal static class ObsReplayFeature { private static ConfigEntry _enabled; private static ConfigEntry _address; private static ConfigEntry _port; private static ConfigEntry _password; private static readonly SemaphoreSlim Gate = new SemaphoreSlim(1, 1); private static bool _bossReplayActive; private static string _activeBoss = string.Empty; private static bool Available { get { if (DeathRunCounterFeature.Enabled && _enabled != null) { return _enabled.Value; } return false; } } internal static void Initialize(Plugin plugin) { _enabled = ((BaseUnityPlugin)plugin).Config.Bind("OBSReplay", "Enabled", false, "Startet und speichert den OBS-Replay-Puffer automatisch bei Blut-Eid-Bosskaempfen."); _address = ((BaseUnityPlugin)plugin).Config.Bind("OBSReplay", "Address", "127.0.0.1", "Lokale OBS-WebSocket-Adresse. Aus Sicherheitsgruenden 127.0.0.1 verwenden."); _port = ((BaseUnityPlugin)plugin).Config.Bind("OBSReplay", "Port", 4455, "OBS-WebSocket-Port (OBS 28+ standardmaessig 4455)."); _password = ((BaseUnityPlugin)plugin).Config.Bind("OBSReplay", "Password", string.Empty, "OBS-WebSocket-Passwort. Bleibt ausschliesslich lokal in dieser Profil-Konfiguration."); } internal static void NotifyBossEncounter(Character boss) { if (Available && !((Object)(object)boss == (Object)null) && !_bossReplayActive && IsBoss(boss)) { _bossReplayActive = true; _activeBoss = Plugin.CanonicalBossKey(Plugin.NormalizeKey(boss.m_name) + " " + Plugin.NormalizeKey(((Object)boss).name)); RunRequest("StartReplayBuffer", "Bosskampf erkannt: OBS Replay-Puffer wird gestartet.", saving: false); } } internal static void NotifyBossDefeated(Character boss) { if (Available && _bossReplayActive && !((Object)(object)boss == (Object)null) && IsBoss(boss)) { Save("Boss besiegt"); } } internal static void NotifyPlayerDeath() { if (Available && _bossReplayActive) { Save("Spieler gefallen"); } } private static void Save(string reason) { string activeBoss = _activeBoss; _bossReplayActive = false; _activeBoss = string.Empty; RunRequest("SaveReplayBuffer", reason + ": OBS speichert den Replay-Clip" + (string.IsNullOrWhiteSpace(activeBoss) ? "." : (" fuer " + activeBoss + ".")), saving: true); } private static void RunRequest(string requestType, string message, bool saving) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Blut-Eid OBS: " + message)); } Task.Run(async delegate { await Gate.WaitAsync().ConfigureAwait(continueOnCapturedContext: false); try { await SendRequest(requestType).ConfigureAwait(continueOnCapturedContext: false); } catch (Exception ex) { if (!saving) { _bossReplayActive = false; _activeBoss = string.Empty; } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Blut-Eid OBS-Verbindung fehlgeschlagen: " + ex.Message)); } } finally { Gate.Release(); } }); } private static async Task SendRequest(string requestType) { string text = (string.IsNullOrWhiteSpace(_address?.Value) ? "127.0.0.1" : _address.Value.Trim()); if (!string.Equals(text, "127.0.0.1", StringComparison.OrdinalIgnoreCase) && !string.Equals(text, "localhost", StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("Nur eine lokale OBS-Adresse ist erlaubt."); } using ClientWebSocket socket = new ClientWebSocket(); using CancellationTokenSource timeout = new CancellationTokenSource(TimeSpan.FromSeconds(8.0)); await socket.ConnectAsync(new Uri("ws://" + text + ":" + Math.Max(1, _port.Value)), timeout.Token).ConfigureAwait(continueOnCapturedContext: false); JToken obj = (await Receive(socket, timeout.Token).ConfigureAwait(continueOnCapturedContext: false))["d"]; JToken obj2 = ((JObject)(((object)((obj is JObject) ? obj : null)) ?? ((object)new JObject())))["authentication"]; JObject val = (JObject)(object)((obj2 is JObject) ? obj2 : null); JObject val2 = new JObject { ["rpcVersion"] = JToken.op_Implicit(1) }; if (val != null) { if (string.IsNullOrEmpty(_password.Value)) { throw new InvalidOperationException("OBS verlangt ein WebSocket-Passwort; bitte in der Blut-Eid-Konfiguration eintragen."); } val2["authentication"] = JToken.op_Implicit(Authentication(_password.Value, (string)val["salt"], (string)val["challenge"])); } await Send(socket, new JObject { ["op"] = JToken.op_Implicit(1), ["d"] = (JToken)(object)val2 }, timeout.Token).ConfigureAwait(continueOnCapturedContext: false); if ((int?)(await Receive(socket, timeout.Token).ConfigureAwait(continueOnCapturedContext: false))["op"] != 2) { throw new InvalidOperationException("OBS hat die Anmeldung nicht bestaetigt."); } string text2 = Guid.NewGuid().ToString("N"); await Send(socket, new JObject { ["op"] = JToken.op_Implicit(6), ["d"] = (JToken)new JObject { ["requestType"] = JToken.op_Implicit(requestType), ["requestId"] = JToken.op_Implicit(text2) } }, timeout.Token).ConfigureAwait(continueOnCapturedContext: false); JObject obj3 = await Receive(socket, timeout.Token).ConfigureAwait(continueOnCapturedContext: false); JToken obj4 = obj3["d"]; JToken obj5 = ((obj4 is JObject) ? obj4 : null); JToken obj6 = ((obj5 != null) ? ((JObject)obj5)["requestStatus"] : null); JObject val3 = (JObject)(object)((obj6 is JObject) ? obj6 : null); if ((int?)obj3["op"] != 7 || (bool?)((val3 != null) ? val3["result"] : null) != true) { throw new InvalidOperationException(((string)((val3 != null) ? val3["comment"] : null)) ?? "OBS hat die Replay-Anfrage abgelehnt."); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Blut-Eid OBS: " + requestType + " erfolgreich.")); } try { await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "done", CancellationToken.None).ConfigureAwait(continueOnCapturedContext: false); } catch { } } private static async Task Send(ClientWebSocket socket, JObject payload, CancellationToken token) { byte[] bytes = Encoding.UTF8.GetBytes(((JToken)payload).ToString((Formatting)0, Array.Empty())); await socket.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, endOfMessage: true, token).ConfigureAwait(continueOnCapturedContext: false); } private static async Task Receive(ClientWebSocket socket, CancellationToken token) { byte[] buffer = new byte[32768]; int length = 0; WebSocketReceiveResult webSocketReceiveResult; do { if (length == buffer.Length) { throw new InvalidOperationException("OBS-Antwort ist zu gross."); } webSocketReceiveResult = await socket.ReceiveAsync(new ArraySegment(buffer, length, buffer.Length - length), token).ConfigureAwait(continueOnCapturedContext: false); length += webSocketReceiveResult.Count; } while (!webSocketReceiveResult.EndOfMessage); return JObject.Parse(Encoding.UTF8.GetString(buffer, 0, length)); } private static string Authentication(string password, string salt, string challenge) { using SHA256 sHA = SHA256.Create(); string text = Convert.ToBase64String(sHA.ComputeHash(Encoding.UTF8.GetBytes(password + salt))); return Convert.ToBase64String(sHA.ComputeHash(Encoding.UTF8.GetBytes(text + challenge))); } private static bool IsBoss(Character character) { if ((Object)(object)character == (Object)null || character.IsPlayer()) { return false; } string text = Plugin.CanonicalBossKey(Plugin.NormalizeKey(character.m_name) + " " + Plugin.NormalizeKey(((Object)character).name)); switch (text) { default: return text == "fader"; case "eikthyr": case "elder": case "bonemass": case "moder": case "yagluth": case "queen": return true; } } } internal enum OrientationMarkerKind { PersonalBuoy = 1, PersonalWaypoint, PrivateDungeonSign, SharedDungeonSign } internal enum DungeonSignVisibility { OwnerOnly, SkilledPlayers } internal sealed class SharedDungeonSign { internal string Id = string.Empty; internal long OwnerPlayerId; internal DungeonSignVisibility Visibility; internal int RequiredOrientationLevel; internal string Name = string.Empty; internal Vector3 Position; } internal static class OrientationMarkerFeature { internal enum RequestAction { PlacePrivate = 1, PlaceShared, RemoveNearestOwned, RequestSnapshot } internal const string DungeonSignsKey = "ChallengeHub_OrientationSigns"; private const string RequestRpc = "ChallengeHub_RPC_OrientationSignRequest"; private const string SnapshotRpc = "ChallengeHub_RPC_OrientationSignSnapshot"; private const int PayloadVersion = 1; private const int MaxSignsPerDungeon = 32; private const int MaxSignsPerOwner = 12; private static Plugin _plugin; private static bool _rpcRegistrationStarted; private static bool _rpcRegistered; private static OrientationRuntimeBehaviour _behaviour; internal static void Initialize(Plugin plugin) { _plugin = plugin; if ((Object)(object)plugin != (Object)null && (Object)(object)_behaviour == (Object)null) { _behaviour = ((Component)plugin).gameObject.GetComponent() ?? ((Component)plugin).gameObject.AddComponent(); } EnsureRpcRegistration(); } internal static void EnsureRpcRegistration() { if (!((Object)(object)_plugin == (Object)null) && !_rpcRegistrationStarted) { _rpcRegistrationStarted = true; ((MonoBehaviour)_plugin).StartCoroutine(RegisterRpcWhenReady()); } } private static IEnumerator RegisterRpcWhenReady() { while (ZRoutedRpc.instance == null) { yield return null; } if (_rpcRegistered) { yield break; } try { ZRoutedRpc.instance.Register("ChallengeHub_RPC_OrientationSignRequest", (Action)RPC_Request); ZRoutedRpc.instance.Register("ChallengeHub_RPC_OrientationSignSnapshot", (Action)RPC_Snapshot); _rpcRegistered = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Orientierungs-RPCs registriert."); } } catch (Exception ex) { _rpcRegistrationStarted = false; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Orientierungs-RPC-Registrierung fehlgeschlagen: " + ex)); } } } internal static void OnLocalPlayerSpawned(Player player) { if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer)) { _behaviour?.ReloadPersonalMarkers(); } } internal static void OnLocalPlayerDestroyed(Player player) { if (!((Object)(object)player != (Object)null) || !((Object)(object)player != (Object)(object)Player.m_localPlayer)) { _behaviour?.ClearRuntimeState(); } } internal static void OnTalentDataChanged() { _behaviour?.ReloadPersonalMarkers(); _behaviour?.RefreshCurrentDungeonSigns(); } internal static void HandleSuccessfulTeleport(DungeonTalentRewardFeature.InteractionKind kind, Vector3 dungeonPosition) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) _behaviour?.HandleSuccessfulTeleport(kind, dungeonPosition); } internal static int PersonalMarkerCount() { return TalentStore.GetPersonalMarkersForCurrentWorld().Count; } internal static int VisibleDungeonSignCount() { if (!((Object)(object)_behaviour != (Object)null)) { return 0; } return _behaviour.VisibleDungeonSignCount; } internal static bool PlacePersonalBuoy() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; TalentData localData = TalentStore.GetLocalData(); if ((Object)(object)localPlayer == (Object)null || localData == null || !localData.HasSkill("orientation.personal_buoys")) { return false; } int num = PersonalMarkerCount() + 1; if (!TalentStore.AddPersonalOrientationMarker(((Component)localPlayer).transform.position, "Boje " + num, 1, out var created)) { return false; } OrientationMarkerRenderer.RenderPersonal(created); ShowCenter("Persönliche Boje gesetzt: " + created.Name); return true; } internal static bool PlacePersonalWaypoint() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; TalentData localData = TalentStore.GetLocalData(); if ((Object)(object)localPlayer == (Object)null || localData == null || !localData.HasSkill("orientation.personal_buoys")) { return false; } int num = PersonalMarkerCount() + 1; if (!TalentStore.AddPersonalOrientationMarker(((Component)localPlayer).transform.position, "Wegpunkt " + num, 2, out var created)) { return false; } OrientationMarkerRenderer.RenderPersonal(created); ShowCenter("Persönlicher Wegpunkt gesetzt: " + created.Name); return true; } internal static bool RequestPlaceDungeonSign(bool shared) { //IL_0045: 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_0083: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; TalentData localData = TalentStore.GetLocalData(); if ((Object)(object)localPlayer == (Object)null || localData == null || !(shared ? localData.HasSkill("orientation.shared_signs") : localData.HasSkill("orientation.private_signs")) || !SafeInInterior(localPlayer)) { return false; } DungeonGenerator val = DungeonTalentRewardFeature.FindDungeonAt(((Component)localPlayer).transform.position); if ((Object)(object)val == (Object)null) { return false; } string name = (shared ? "Gemeinsamer Wegweiser" : "Privater Wegweiser"); return SendOrProcessRequest((!shared) ? RequestAction.PlacePrivate : RequestAction.PlaceShared, ((Component)val).transform.position, ((Component)localPlayer).transform.position, TalentStore.SafeGetPlayerId(localPlayer), name); } internal static bool RemoveNearestOwnedMarker() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; TalentData localData = TalentStore.GetLocalData(); if ((Object)(object)localPlayer == (Object)null || localData == null || !localData.HasSkill("orientation.personal_buoys")) { return false; } if (TalentStore.RemoveNearestPersonalOrientationMarker(((Component)localPlayer).transform.position, 12f, out var removedName)) { _behaviour?.ReloadPersonalMarkers(); ShowCenter("Markierung entfernt: " + removedName); return true; } if (!SafeInInterior(localPlayer) || !localData.HasSkill("orientation.private_signs")) { return false; } DungeonGenerator val = DungeonTalentRewardFeature.FindDungeonAt(((Component)localPlayer).transform.position); if ((Object)(object)val == (Object)null) { return false; } return SendOrProcessRequest(RequestAction.RemoveNearestOwned, ((Component)val).transform.position, ((Component)localPlayer).transform.position, TalentStore.SafeGetPlayerId(localPlayer), string.Empty); } internal static bool RequestDungeonSnapshot(Vector3 dungeonPosition) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } return SendOrProcessRequest(RequestAction.RequestSnapshot, dungeonPosition, ((Component)localPlayer).transform.position, TalentStore.SafeGetPlayerId(localPlayer), string.Empty); } private static bool SendOrProcessRequest(RequestAction action, Vector3 dungeonPosition, Vector3 markerPosition, long playerId, string name) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (!_rpcRegistered || ZRoutedRpc.instance == null || playerId == 0L) { return false; } long senderPeerId = ValheimNetworkCompatibility.ResolveLocalPeerId(); if (DungeonTalentRewardFeature.IsServerAuthority()) { ProcessRequest(senderPeerId, action, dungeonPosition, markerPosition, playerId, name); return true; } long num = DungeonTalentRewardFeature.ResolveServerPeerForClient(); if (num == 0L) { return false; } ZPackage val = new ZPackage(); val.Write((int)action); val.Write(dungeonPosition); val.Write(markerPosition); val.Write(playerId); val.Write(name ?? string.Empty); ZRoutedRpc.instance.InvokeRoutedRPC(num, "ChallengeHub_RPC_OrientationSignRequest", new object[1] { val }); return true; } private static void RPC_Request(long sender, ZPackage package) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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_0058: Unknown result type (might be due to invalid IL or missing references) if (!DungeonTalentRewardFeature.IsServerAuthority() || package == null) { return; } try { RequestAction requestAction = (RequestAction)package.ReadInt(); Vector3 dungeonPosition = package.ReadVector3(); Vector3 markerPosition = package.ReadVector3(); long num = package.ReadLong(); string name = package.ReadString(); if (Enum.IsDefined(typeof(RequestAction), requestAction) && num != 0L && DungeonTalentRewardFeature.ValidateSenderOwnsPlayer(sender, num)) { ProcessRequest(sender, requestAction, dungeonPosition, markerPosition, num, name); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Orientierungs-Anfrage war ungültig: " + ex.Message)); } } } private static void ProcessRequest(long senderPeerId, RequestAction action, Vector3 dungeonPosition, Vector3 markerPosition, long playerId, string name) { //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) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) if (!DungeonTalentRewardFeature.IsServerAuthority() || !IsFinite(markerPosition)) { return; } DungeonGenerator val = DungeonTalentRewardFeature.FindDungeonAt(dungeonPosition); if ((Object)(object)val == (Object)null || Vector3.Distance(((Component)val).transform.position, markerPosition) > 950f) { return; } ZDO val2 = DungeonTalentRewardFeature.ResolveDungeonStateZdo(val); if (val2 == null) { return; } Player val3 = FindPlayerById(playerId); TalentData talentData = (((Object)(object)val3 != (Object)null) ? TalentStore.Load(val3, forceReload: false) : null); List list = ReadSigns(val2); if (action == RequestAction.RequestSnapshot) { SendFilteredSnapshot(senderPeerId, ((Component)val).transform.position, list, playerId, talentData); return; } bool flag = talentData?.HasSkill("orientation.private_signs") ?? false; bool flag2 = talentData?.HasSkill("orientation.shared_signs") ?? false; if ((action == RequestAction.PlacePrivate || action == RequestAction.RemoveNearestOwned) && !flag) { SendStatus(senderPeerId, "Talent „Private Wegzeichen“ erforderlich."); } else if (action == RequestAction.PlaceShared && !flag2) { SendStatus(senderPeerId, "Talent „Pfadfinder-Netz“ erforderlich."); } else { if (!ValheimNetworkCompatibility.TryTakeServerOwnership(val2, "orientation_sign")) { return; } bool flag3 = false; string message = string.Empty; if (action == RequestAction.RemoveNearestOwned) { SharedDungeonSign sharedDungeonSign = (from sign in list where sign.OwnerPlayerId == playerId orderby Vector3.Distance(sign.Position, markerPosition) select sign).FirstOrDefault(); if (sharedDungeonSign != null && Vector3.Distance(sharedDungeonSign.Position, markerPosition) <= 12f) { list.Remove(sharedDungeonSign); flag3 = true; message = "Wegzeichen entfernt: " + sharedDungeonSign.Name; } } else { int num = list.Count((SharedDungeonSign sign) => sign.OwnerPlayerId == playerId); if (list.Count >= 32 || num >= 12) { SendStatus(senderPeerId, "Maximale Anzahl an Dungeon-Wegzeichen erreicht."); return; } bool flag4 = action == RequestAction.PlaceShared; SharedDungeonSign item = new SharedDungeonSign { Id = Guid.NewGuid().ToString("N"), OwnerPlayerId = playerId, Visibility = (flag4 ? DungeonSignVisibility.SkilledPlayers : DungeonSignVisibility.OwnerOnly), RequiredOrientationLevel = (flag4 ? 4 : 3), Name = OrientationText.SanitizeLabel(name, flag4 ? "Gemeinsamer Wegweiser" : "Privater Wegweiser"), Position = markerPosition }; list.Add(item); flag3 = true; message = (flag4 ? "Gemeinsames Wegzeichen gesetzt." : "Privates Wegzeichen gesetzt."); } if (!flag3) { SendStatus(senderPeerId, "Kein eigenes Wegzeichen in der Nähe gefunden."); return; } string text = SerializeSigns(list); val2.Set("ChallengeHub_OrientationSigns", text); BroadcastFilteredSnapshots(((Component)val).transform.position, list); SendStatus(senderPeerId, message); } } private static void SendStatus(long targetPeerId, string message) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (targetPeerId != 0L && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(Vector3.zero); val.Write("STATUS:" + (message ?? string.Empty)); ZRoutedRpc.instance.InvokeRoutedRPC(targetPeerId, "ChallengeHub_RPC_OrientationSignSnapshot", new object[1] { val }); } } private static void SendSnapshot(long targetPeerId, Vector3 dungeonPosition, string payload) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (targetPeerId != 0L && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(dungeonPosition); val.Write(payload ?? string.Empty); ZRoutedRpc.instance.InvokeRoutedRPC(targetPeerId, "ChallengeHub_RPC_OrientationSignSnapshot", new object[1] { val }); } } private static void SendFilteredSnapshot(long targetPeerId, Vector3 dungeonPosition, IEnumerable signs, long playerId, TalentData talents) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) bool maySeePrivate = talents?.HasSkill("orientation.private_signs") ?? false; bool maySeeShared = talents?.HasSkill("orientation.shared_signs") ?? false; IEnumerable source = (signs ?? Enumerable.Empty()).Where((SharedDungeonSign sign) => (maySeePrivate && sign.OwnerPlayerId == playerId) || (maySeeShared && sign.Visibility == DungeonSignVisibility.SkilledPlayers)); SendSnapshot(targetPeerId, dungeonPosition, SerializeSigns(source)); } private static void BroadcastFilteredSnapshots(Vector3 dungeonPosition, IEnumerable signs) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) if (ZRoutedRpc.instance == null || !DungeonTalentRewardFeature.IsServerAuthority()) { return; } HashSet hashSet = new HashSet(); foreach (Player allPlayer in Player.GetAllPlayers()) { if (!((Object)(object)allPlayer == (Object)null)) { long num = ResolvePeerId(allPlayer); if (num != 0L && hashSet.Add(num)) { long playerId = TalentStore.SafeGetPlayerId(allPlayer); TalentData talents = TalentStore.Load(allPlayer, forceReload: false); SendFilteredSnapshot(num, dungeonPosition, signs, playerId, talents); } } } } internal static void OnDungeonResetCompleted(string dungeonKey, Vector3 dungeonPosition, ZDO stateZdo) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (DungeonTalentRewardFeature.IsServerAuthority() && stateZdo != null) { List list = ReadSigns(stateZdo); BroadcastFilteredSnapshots(dungeonPosition, list); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Resetfeste Dungeon-Wegzeichen erneut angebunden: " + (dungeonKey ?? "unknown") + "; Anzahl=" + list.Count)); } } } private static Player FindPlayerById(long playerId) { if (playerId == 0L) { return null; } foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer != (Object)null && TalentStore.SafeGetPlayerId(allPlayer) == playerId) { return allPlayer; } } return null; } private static long ResolvePeerId(Player player) { if ((Object)(object)player == (Object)null) { return 0L; } try { ZNetView component = ((Component)player).GetComponent(); ZDO val = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null); long num = ((val != null) ? val.GetOwner() : 0); if (num != 0L) { return num; } } catch { } if (!((Object)(object)player == (Object)(object)Player.m_localPlayer)) { return 0L; } return ValheimNetworkCompatibility.ResolveLocalPeerId(); } private static void RPC_Snapshot(long sender, ZPackage package) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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) if (package == null) { return; } try { Vector3 dungeonPosition = package.ReadVector3(); string text = package.ReadString(); long num = DungeonTalentRewardFeature.ResolveServerPeerForClient(); long num2 = ValheimNetworkCompatibility.ResolveLocalPeerId(); if (sender == num || sender == num2 || DungeonTalentRewardFeature.IsServerAuthority()) { if (text.StartsWith("STATUS:", StringComparison.Ordinal)) { ShowCenter(text.Substring("STATUS:".Length)); } else { _behaviour?.ApplyDungeonSnapshot(dungeonPosition, text); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Orientierungs-Snapshot konnte nicht gelesen werden: " + ex.Message)); } } } internal static List ReadSigns(ZDO zdo) { if (zdo == null) { return new List(); } try { return DeserializeSigns(zdo.GetString("ChallengeHub_OrientationSigns", string.Empty)); } catch { return new List(); } } internal static string SerializeSigns(IEnumerable source) { List list = (source ?? Enumerable.Empty()).Where(IsValidSign).Take(32).ToList(); try { using MemoryStream memoryStream = new MemoryStream(); using BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true); binaryWriter.Write(1); binaryWriter.Write(list.Count); foreach (SharedDungeonSign item in list) { binaryWriter.Write(item.Id ?? string.Empty); binaryWriter.Write(item.OwnerPlayerId); binaryWriter.Write((int)item.Visibility); binaryWriter.Write(Mathf.Clamp(item.RequiredOrientationLevel, 1, 4)); binaryWriter.Write(OrientationText.SanitizeLabel(item.Name, "Wegzeichen")); binaryWriter.Write(item.Position.x); binaryWriter.Write(item.Position.y); binaryWriter.Write(item.Position.z); } binaryWriter.Flush(); return Convert.ToBase64String(memoryStream.ToArray()); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Dungeon-Wegzeichen konnten nicht serialisiert werden: " + ex.Message)); } return string.Empty; } } internal static List DeserializeSigns(string payload) { //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) List list = new List(); if (string.IsNullOrWhiteSpace(payload)) { return list; } try { using MemoryStream input = new MemoryStream(Convert.FromBase64String(payload), writable: false); using BinaryReader binaryReader = new BinaryReader(input, Encoding.UTF8, leaveOpen: true); int num = binaryReader.ReadInt32(); int num2 = binaryReader.ReadInt32(); if (num != 1 || num2 < 0 || num2 > 32) { return list; } for (int i = 0; i < num2; i++) { SharedDungeonSign sharedDungeonSign = new SharedDungeonSign { Id = binaryReader.ReadString(), OwnerPlayerId = binaryReader.ReadInt64(), Visibility = (DungeonSignVisibility)binaryReader.ReadInt32(), RequiredOrientationLevel = binaryReader.ReadInt32(), Name = binaryReader.ReadString(), Position = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()) }; if (IsValidSign(sharedDungeonSign)) { list.Add(sharedDungeonSign); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Dungeon-Wegzeichen konnten nicht gelesen werden: " + ex.Message)); } } return list; } private static bool IsValidSign(SharedDungeonSign sign) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) if (sign != null && !string.IsNullOrWhiteSpace(sign.Id) && sign.OwnerPlayerId != 0L && Enum.IsDefined(typeof(DungeonSignVisibility), sign.Visibility) && sign.RequiredOrientationLevel >= 1 && sign.RequiredOrientationLevel <= 4) { return IsFinite(sign.Position); } return false; } private static bool IsFinite(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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) if (OrientationText.IsFinite(position.x) && OrientationText.IsFinite(position.y)) { return OrientationText.IsFinite(position.z); } return false; } private static bool SafeInInterior(Player player) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) try { return (Object)(object)player != (Object)null && ((Character)player).InInterior(); } catch { return (Object)(object)player != (Object)null && ((Component)player).transform.position.y >= 3000f; } } private static void ShowCenter(string text) { if (!string.IsNullOrWhiteSpace(text)) { MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false); } } } } internal sealed class OrientationRuntimeBehaviour : MonoBehaviour { private Vector3 _currentDungeonPosition; private Vector3 _dungeonExitPosition; private bool _insideDungeon; private Coroutine _refreshCoroutine; private GUIStyle _hudStyle; private static Font _valheimFont; private static Texture2D _bgTexture; internal int VisibleDungeonSignCount { get; private set; } private void Update() { if ((Object)(object)Player.m_localPlayer == (Object)null || TalentMenuBehaviour.IsVisible || (!Input.GetKey((KeyCode)308) && !Input.GetKey((KeyCode)307))) { return; } if (Input.GetKeyDown((KeyCode)98)) { if (!OrientationMarkerFeature.PlacePersonalBuoy()) { MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, "Talent „Persönliche Bojen“ erforderlich.", 0, (Sprite)null, false); } } } else if (Input.GetKeyDown((KeyCode)110)) { if (!OrientationMarkerFeature.PlacePersonalWaypoint()) { MessageHud instance2 = MessageHud.instance; if (instance2 != null) { instance2.ShowMessage((MessageType)2, "Talent „Persönliche Bojen“ erforderlich.", 0, (Sprite)null, false); } } } else if (Input.GetKeyDown((KeyCode)119)) { if (!OrientationMarkerFeature.RequestPlaceDungeonSign(shared: false)) { MessageHud instance3 = MessageHud.instance; if (instance3 != null) { instance3.ShowMessage((MessageType)2, "Im Dungeon ist das Talent „Private Wegzeichen“ erforderlich.", 0, (Sprite)null, false); } } } else if (Input.GetKeyDown((KeyCode)103)) { if (!OrientationMarkerFeature.RequestPlaceDungeonSign(shared: true)) { MessageHud instance4 = MessageHud.instance; if (instance4 != null) { instance4.ShowMessage((MessageType)2, "Im Dungeon ist das Talent „Pfadfinder-Netz“ erforderlich.", 0, (Sprite)null, false); } } } else if (Input.GetKeyDown((KeyCode)127) && !OrientationMarkerFeature.RemoveNearestOwnedMarker()) { MessageHud instance5 = MessageHud.instance; if (instance5 != null) { instance5.ShowMessage((MessageType)2, "Keine eigene Markierung in 12 m Entfernung.", 0, (Sprite)null, false); } } } private void OnGUI() { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Expected O, but got Unknown //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) TalentData localData = TalentStore.GetLocalData(); Player localPlayer = Player.m_localPlayer; if (localData == null || (Object)(object)localPlayer == (Object)null || TalentMenuBehaviour.IsVisible || (!localData.HasSkill("orientation.compass") && !localData.HasSkill("orientation.exit_direction"))) { return; } if (_hudStyle == null || (Object)(object)_bgTexture == (Object)null) { _valheimFont = ((IEnumerable)Resources.FindObjectsOfTypeAll()).FirstOrDefault((Func)((Font x) => ((Object)x).name == "AveriaSansLibre-Bold" || ((Object)x).name == "AveriaSerifLibre-Bold")) ?? GUI.skin.font; _bgTexture = new Texture2D(1, 1); _bgTexture.SetPixel(0, 0, new Color(0.08f, 0.08f, 0.08f, 0.95f)); _bgTexture.Apply(); _hudStyle = new GUIStyle(GUI.skin.box) { alignment = (TextAnchor)4, fontSize = 18, font = _valheimFont, wordWrap = false }; _hudStyle.normal.background = _bgTexture; _hudStyle.normal.textColor = new Color(0.8f, 0.9f, 1f, 1f); } string text = (localData.HasSkill("orientation.compass") ? ("Orientierung: " + CardinalDirection(((Component)localPlayer).transform.forward)) : "Orientierung"); if (_insideDungeon && localData.HasSkill("orientation.exit_direction")) { float num = Vector3.Distance(((Component)localPlayer).transform.position, _dungeonExitPosition); Vector3 direction = _dungeonExitPosition - ((Component)localPlayer).transform.position; text = text + " | Ausgang " + Mathf.RoundToInt(num) + " m " + CardinalDirection(direction); } GUI.Box(new Rect((float)Screen.width * 0.5f - 230f, 85f, 460f, 34f), text, _hudStyle); } internal void HandleSuccessfulTeleport(DungeonTalentRewardFeature.InteractionKind kind, Vector3 dungeonPosition) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (kind == DungeonTalentRewardFeature.InteractionKind.Exit) { _insideDungeon = false; _currentDungeonPosition = Vector3.zero; _dungeonExitPosition = Vector3.zero; VisibleDungeonSignCount = 0; OrientationMarkerRenderer.ClearDungeon(); } else { _currentDungeonPosition = dungeonPosition; if (_refreshCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_refreshCoroutine); } _refreshCoroutine = ((MonoBehaviour)this).StartCoroutine(RefreshAfterTeleport()); } } private IEnumerator RefreshAfterTeleport() { for (int attempt = 0; attempt < 4; attempt++) { yield return (object)new WaitForSeconds((attempt == 0) ? 0.2f : 0.5f); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { break; } bool flag; try { flag = ((Character)localPlayer).InInterior(); } catch { flag = ((Component)localPlayer).transform.position.y >= 3000f; } if (flag) { _insideDungeon = true; _dungeonExitPosition = ((Component)localPlayer).transform.position; RefreshCurrentDungeonSigns(); break; } } } internal void ReloadPersonalMarkers() { OrientationMarkerRenderer.ClearPersonal(); TalentData localData = TalentStore.GetLocalData(); if (localData == null || !localData.HasSkill("orientation.personal_buoys")) { return; } foreach (PersonalOrientationMarkerData item in TalentStore.GetPersonalMarkersForCurrentWorld()) { OrientationMarkerRenderer.RenderPersonal(item); } } internal void RefreshCurrentDungeonSigns() { //IL_0009: 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_0034: Unknown result type (might be due to invalid IL or missing references) if (!_insideDungeon || _currentDungeonPosition == Vector3.zero) { VisibleDungeonSignCount = 0; OrientationMarkerRenderer.ClearDungeon(); } else { VisibleDungeonSignCount = 0; OrientationMarkerRenderer.ClearDungeon(); OrientationMarkerFeature.RequestDungeonSnapshot(_currentDungeonPosition); } } internal void ApplyDungeonSnapshot(Vector3 dungeonPosition, string payload) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (!_insideDungeon || Vector3.Distance(_currentDungeonPosition, dungeonPosition) > 40f) { return; } TalentData data = TalentStore.GetLocalData(); Player localPlayer = Player.m_localPlayer; if (data == null || (Object)(object)localPlayer == (Object)null || !data.HasSkill("orientation.private_signs")) { VisibleDungeonSignCount = 0; OrientationMarkerRenderer.ClearDungeon(); return; } long playerId = TalentStore.SafeGetPlayerId(localPlayer); List list = (from sign in OrientationMarkerFeature.DeserializeSigns(payload) where sign.OwnerPlayerId == playerId || (sign.Visibility == DungeonSignVisibility.SkilledPlayers && data.HasSkill("orientation.shared_signs")) select sign).ToList(); OrientationMarkerRenderer.RenderDungeon(list, playerId); VisibleDungeonSignCount = list.Count; } internal void ClearRuntimeState() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //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) if (_refreshCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_refreshCoroutine); } _refreshCoroutine = null; _insideDungeon = false; _currentDungeonPosition = Vector3.zero; _dungeonExitPosition = Vector3.zero; VisibleDungeonSignCount = 0; OrientationMarkerRenderer.ClearAll(); } private static string CardinalDirection(Vector3 direction) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) direction.y = 0f; if (((Vector3)(ref direction)).sqrMagnitude < 0.001f) { return "-"; } float num = Mathf.Atan2(direction.x, direction.z) * 57.29578f; if (num < 0f) { num += 360f; } string[] array = new string[8] { "N", "NO", "O", "SO", "S", "SW", "W", "NW" }; int num2 = Mathf.RoundToInt(num / 45f) % array.Length; return array[num2]; } } internal static class OrientationMarkerRenderer { private static readonly Dictionary Visuals = new Dictionary(StringComparer.Ordinal); internal static void RenderPersonal(PersonalOrientationMarkerData marker) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (marker != null) { CreateOrReplace("personal:" + marker.Id, marker.Position, marker.Name, (OrientationMarkerKind)Mathf.Clamp(marker.Kind, 1, 2), own: true); } } internal static void RenderDungeon(IEnumerable signs, long localPlayerId) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) ClearDungeon(); foreach (SharedDungeonSign item in signs ?? Enumerable.Empty()) { bool own = item.OwnerPlayerId == localPlayerId; OrientationMarkerKind kind = ((item.Visibility == DungeonSignVisibility.SkilledPlayers) ? OrientationMarkerKind.SharedDungeonSign : OrientationMarkerKind.PrivateDungeonSign); CreateOrReplace("dungeon:" + item.Id, item.Position, item.Name, kind, own); } } internal static void ClearPersonal() { ClearPrefix("personal:"); } internal static void ClearDungeon() { ClearPrefix("dungeon:"); } internal static void ClearAll() { OrientationMarkerVisual[] array = Visuals.Values.ToArray(); foreach (OrientationMarkerVisual orientationMarkerVisual in array) { if ((Object)(object)orientationMarkerVisual != (Object)null) { Object.Destroy((Object)(object)((Component)orientationMarkerVisual).gameObject); } } Visuals.Clear(); } private static void ClearPrefix(string prefix) { string[] array = Visuals.Keys.Where((string text) => text.StartsWith(prefix, StringComparison.Ordinal)).ToArray(); foreach (string key in array) { OrientationMarkerVisual orientationMarkerVisual = Visuals[key]; if ((Object)(object)orientationMarkerVisual != (Object)null) { Object.Destroy((Object)(object)((Component)orientationMarkerVisual).gameObject); } Visuals.Remove(key); } } private static void CreateOrReplace(string key, Vector3 position, string label, OrientationMarkerKind kind, bool own) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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 (Visuals.TryGetValue(key, out var value) && (Object)(object)value != (Object)null) { Object.Destroy((Object)(object)((Component)value).gameObject); } GameObject val = new GameObject("ChallengeHub_LocalOrientation_" + key.Replace(':', '_')); val.transform.position = position; OrientationMarkerVisual orientationMarkerVisual = val.AddComponent(); orientationMarkerVisual.Initialize(label, kind, own); Visuals[key] = orientationMarkerVisual; } } internal sealed class OrientationMarkerVisual : MonoBehaviour { private TextMesh _text; private OrientationMarkerKind _kind; private Vector3 _basePosition; private float _nextRefresh; private string _label; internal void Initialize(string label, OrientationMarkerKind kind, bool own) { //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_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) _label = OrientationText.SanitizeLabel(label, "Markierung"); _kind = kind; _basePosition = ((Component)this).transform.position; Color val = Color.cyan; if (kind == OrientationMarkerKind.PersonalBuoy) { ((Color)(ref val))..ctor(0.1f, 0.75f, 1f, 1f); } if (kind == OrientationMarkerKind.PersonalWaypoint) { ((Color)(ref val))..ctor(0.25f, 1f, 0.55f, 1f); } if (kind == OrientationMarkerKind.PrivateDungeonSign) { ((Color)(ref val))..ctor(1f, 0.75f, 0.15f, 1f); } if (kind == OrientationMarkerKind.SharedDungeonSign) { ((Color)(ref val))..ctor(0.75f, 0.35f, 1f, 1f); } if (!own) { val = Color.Lerp(val, Color.white, 0.25f); } if (kind == OrientationMarkerKind.PersonalBuoy) { BuildBuoy(val); } else { BuildSign(val); } BuildBeam(val); BuildText(val); } private void LateUpdate() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } if (_kind == OrientationMarkerKind.PersonalBuoy) { float num = Mathf.Sin(Time.time * 1.4f + _basePosition.x * 0.05f) * 0.12f; ((Component)this).transform.position = _basePosition + Vector3.up * num; } if (Time.unscaledTime < _nextRefresh) { return; } _nextRefresh = Time.unscaledTime + 0.25f; float num2 = Vector3.Distance(((Component)localPlayer).transform.position, ((Component)this).transform.position); if (!((Object)(object)_text != (Object)null)) { return; } _text.text = _label + "\n" + Mathf.RoundToInt(num2) + " m"; Camera main = Camera.main; if ((Object)(object)main != (Object)null) { Vector3 val = ((Component)_text).transform.position - ((Component)main).transform.position; if (((Vector3)(ref val)).sqrMagnitude > 0.001f) { ((Component)_text).transform.rotation = Quaternion.LookRotation(((Vector3)(ref val)).normalized, Vector3.up); } } } private void BuildBuoy(Color color) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009f: 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) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) GameObject obj = CreatePrimitive((PrimitiveType)2, "BuoyBody", ((Component)this).transform); obj.transform.localPosition = new Vector3(0f, 0.45f, 0f); obj.transform.localScale = new Vector3(0.45f, 0.45f, 0.45f); ApplyColor(obj, color); GameObject obj2 = CreatePrimitive((PrimitiveType)0, "BuoyTop", ((Component)this).transform); obj2.transform.localPosition = new Vector3(0f, 1.05f, 0f); obj2.transform.localScale = Vector3.one * 0.35f; ApplyColor(obj2, Color.Lerp(color, Color.white, 0.2f)); } private void BuildSign(Color color) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //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_005a: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) GameObject obj = CreatePrimitive((PrimitiveType)3, "SignPost", ((Component)this).transform); obj.transform.localPosition = new Vector3(0f, 1f, 0f); obj.transform.localScale = new Vector3(0.12f, 2f, 0.12f); ApplyColor(obj, Color.Lerp(color, Color.black, 0.35f)); GameObject obj2 = CreatePrimitive((PrimitiveType)3, "SignBoard", ((Component)this).transform); obj2.transform.localPosition = new Vector3(0f, 2.05f, 0f); obj2.transform.localScale = new Vector3(1.3f, 0.48f, 0.12f); ApplyColor(obj2, color); } private void BuildBeam(Color color) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: 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) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown LineRenderer val = ((Component)this).gameObject.AddComponent(); val.useWorldSpace = false; val.positionCount = 2; val.SetPosition(0, new Vector3(0f, 0.1f, 0f)); val.SetPosition(1, new Vector3(0f, 5.5f, 0f)); val.startWidth = 0.05f; val.endWidth = 0.015f; val.startColor = new Color(color.r, color.g, color.b, 0.8f); val.endColor = new Color(color.r, color.g, color.b, 0.05f); Shader val2 = Shader.Find("Sprites/Default"); if ((Object)(object)val2 != (Object)null) { ((Renderer)val).material = new Material(val2); } } private void BuildText(Color color) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("MarkerLabel"); val.transform.SetParent(((Component)this).transform, false); val.transform.localPosition = new Vector3(0f, 3.15f, 0f); _text = val.AddComponent(); _text.anchor = (TextAnchor)4; _text.alignment = (TextAlignment)1; _text.fontSize = 40; _text.characterSize = 0.045f; _text.color = color; _text.text = _label; } private static GameObject CreatePrimitive(PrimitiveType type, string name, Transform parent) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) GameObject obj = GameObject.CreatePrimitive(type); ((Object)obj).name = name; obj.transform.SetParent(parent, false); Collider component = obj.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } return obj; } private static void ApplyColor(GameObject obj, Color color) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) Renderer val = (((Object)(object)obj != (Object)null) ? obj.GetComponent() : null); if ((Object)(object)val == (Object)null) { return; } try { val.material.color = color; } catch { } } } [HarmonyPatch(typeof(Game), "Start")] internal static class OrientationGameStartPatch { [HarmonyPostfix] private static void Postfix() { OrientationMarkerFeature.EnsureRpcRegistration(); } } internal static class PartnershipFeature { [HarmonyPatch] private static class PartnershipStructureDestroyedPatch { private static readonly MethodBase CachedTarget = FindTarget(); private static MethodBase FindTarget() { return typeof(Piece).GetMethod("OnDestroyed", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? typeof(Piece).GetMethod("OnDestroy", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? typeof(Piece).GetMethod("OnDeath", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } private static MethodBase TargetMethod() { return CachedTarget; } private static bool Prepare() { bool num = TargetMethod() != null; if (!num) { ManualLogSource log = Plugin.Log; if (log == null) { return num; } log.LogInfo((object)"Partnerschafts-Konstrukt-Zerstoerung: kein passender Piece-Zerstoerungs-Hook gefunden; optionaler Patch wird uebersprungen."); } return num; } private static void Prefix(Piece __instance) { HandlePartnershipStructureDestroyed(__instance); } } private sealed class ProtectedAreaStatus { public bool IsProtected; public bool HasGuardian; public int MatchedCount; public int RequiredCount; public List MatchedLabels = new List(); public List MissingLabels = new List(); public List AllCriteria = new List(); } private sealed class ProtectedStructureGroup { public string Key; public string Label; public HashSet Prefabs = new HashSet(StringComparer.OrdinalIgnoreCase); public bool Matches(string normalizedPrefabName) { if (string.IsNullOrWhiteSpace(normalizedPrefabName)) { return false; } if (Prefabs.Contains(normalizedPrefabName)) { return true; } foreach (string prefab in Prefabs) { if (!string.IsNullOrWhiteSpace(prefab) && normalizedPrefabName.Contains(prefab)) { return true; } } string text = NormalizeKey(Label); if ((text.Contains("werkbank") || text.Contains("workbench")) && normalizedPrefabName.Contains("workbench")) { return true; } if ((text.Contains("bett") || text.Contains("bed")) && normalizedPrefabName.Contains("bed")) { return true; } if ((text.Contains("feuer") || text.Contains("fire") || text.Contains("hearth")) && (normalizedPrefabName.Contains("fire") || normalizedPrefabName.Contains("hearth") || normalizedPrefabName.Contains("bonfire") || normalizedPrefabName.Contains("brazier"))) { return true; } if ((text.Contains("kochen") || text.Contains("cook") || text.Contains("cauldron")) && (normalizedPrefabName.Contains("cauldron") || normalizedPrefabName.Contains("cookingstation") || normalizedPrefabName.Contains("oven"))) { return true; } return false; } } private sealed class PartnershipDialogBehaviour : MonoBehaviour { private Rect _window = new Rect(0f, 0f, 720f, 520f); private bool _cursorChanged; private void Update() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (_partnershipDialogOpen) { if ((Object)(object)_dialogStructure == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || Vector3.Distance(((Component)Player.m_localPlayer).transform.position, ((Component)_dialogStructure).transform.position) > Mathf.Max(8f, (PartnershipStructureRange != null) ? (PartnershipStructureRange.Value + 2f) : 8f)) { Close(); } else if (Input.GetKeyDown((KeyCode)27)) { Close(); } } } private void OnGUI() { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Expected O, but got Unknown //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) if (_partnershipDialogOpen && !((Object)(object)_dialogStructure == (Object)null)) { if (!_cursorChanged) { ChallengeHubCursorController.Acquire("partnership-dialog"); _cursorChanged = true; } ChallengeHubWindowTheme.Apply(); ((Rect)(ref _window)).width = Mathf.Min(720f, (float)Screen.width - 30f); ((Rect)(ref _window)).height = Mathf.Min(520f, (float)Screen.height - 30f); ((Rect)(ref _window)).x = ((float)Screen.width - ((Rect)(ref _window)).width) * 0.5f; ((Rect)(ref _window)).y = ((float)Screen.height - ((Rect)(ref _window)).height) * 0.5f; _window = GUI.Window(981274, _window, new WindowFunction(DrawWindow), "ChallengeHub - Partnerschaften", ChallengeHubWindowTheme.Window); } } private void DrawWindow(int id) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Unknown result type (might be due to invalid IL or missing references) //IL_0364: Unknown result type (might be due to invalid IL or missing references) //IL_03b7: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Unknown result type (might be due to invalid IL or missing references) ChallengeHubWindowTheme.DrawPanel(new Rect(16f, 42f, ((Rect)(ref _window)).width - 32f, ((Rect)(ref _window)).height - 58f)); GUILayout.Space(16f); GUILayout.Label("Partnerschaft am Vertragsort", ChallengeHubWindowTheme.Title, Array.Empty()); Piece dialogStructure = _dialogStructure; ZNetView val = (((Object)(object)dialogStructure != (Object)null) ? ((Component)dialogStructure).GetComponent() : null); ZDO zdo = (((Object)(object)val != (Object)null) ? val.GetZDO() : null); string text = ReadZdoString(zdo, "ChallengeHub.Partnership.Type"); if (string.IsNullOrWhiteSpace(text)) { text = ResolvePartnershipTypeForStructure(dialogStructure); } string text2 = ReadZdoString(zdo, "ChallengeHub.Partnership.Status"); string text3 = SafePlayerId(Player.m_localPlayer).ToString(); string text4 = ReadZdoString(zdo, "ChallengeHub.Partnership.UserAId"); string text5 = ReadZdoString(zdo, "ChallengeHub.Partnership.UserBId"); string text6 = ReadZdoString(zdo, "ChallengeHub.Partnership.UserAName"); string text7 = ReadZdoString(zdo, "ChallengeHub.Partnership.UserBName"); string text8 = ((string.IsNullOrWhiteSpace(text6) && string.IsNullOrWhiteSpace(text7)) ? "Noch keine Vertragspartner" : ((string.IsNullOrWhiteSpace(text6) ? "?" : text6) + " <-> " + (string.IsNullOrWhiteSpace(text7) ? "?" : text7))); GUILayout.Space(8f); GUILayout.Label("Beziehungsstatus: " + DisplayStatus(text2), Array.Empty()); GUILayout.Label("Vertragsart: " + DisplayType(text), Array.Empty()); GUILayout.Label("Vertragspartner: " + text8, Array.Empty()); GUILayout.Space(10f); GUILayout.Label("Vorteile", Array.Empty()); GUILayout.TextArea(PartnershipBenefits(text), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(52f) }); GUILayout.Space(8f); string text9 = "Praesenzintervall: fuer diesen Vertragsort nicht erforderlich"; if ((Object)(object)_plugin != (Object)null && NormalizeKey(((Object)dialogStructure).name).Contains("neutral")) { text9 = _plugin.BuildNeutralGuardianPresenceHover(dialogStructure).Replace("", "").Replace("", ""); } GUILayout.Label(text9, Array.Empty()); GUILayout.FlexibleSpace(); if (text2 == "active" && NormalizePartnershipType(text) == "trade" && (text3 == text4 || text3 == text5)) { GUI.backgroundColor = new Color(0.35f, 0.65f, 0.9f); if (GUILayout.Button("Handelskiste auswaehlen / wechseln", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { Piece dialogStructure2 = _dialogStructure; Close(); PartnershipGameplayFeature.BeginTradeChestBinding(Player.m_localPlayer, dialogStructure2); return; } GUI.backgroundColor = Color.white; } bool flag = text2 == "pending" && text3 == text5 && text3 != text4; string obj = ((text2 == "active" || (text2 == "pending" && !flag)) ? "Vertrag beenden" : (flag ? "Vertrag bestaetigen" : ((text2 == "cancelled") ? "Neuen Vertrag anfragen / bestaetigen" : "Vertrag anfragen / bestaetigen"))); GUI.backgroundColor = ((text2 == "active" || (text2 == "pending" && !flag)) ? new Color(0.9f, 0.45f, 0.3f) : new Color(0.45f, 0.8f, 0.45f)); if (GUILayout.Button(obj, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(42f) })) { Piece dialogStructure3 = _dialogStructure; Close(); TryUsePartnershipStructure(Player.m_localPlayer, dialogStructure3); } GUI.backgroundColor = Color.white; if (GUILayout.Button("Schliessen (ESC)", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(32f) })) { Close(); } GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _window)).width, 34f)); } private void Close() { _partnershipDialogOpen = false; _dialogStructure = null; if (_cursorChanged) { ChallengeHubCursorController.Release("partnership-dialog"); _cursorChanged = false; } } private void OnDestroy() { Close(); } } private static Plugin _plugin; private static bool _initialized; private static ConfigEntry EnablePartnerships; private static ConfigEntry DefaultPartnershipType; private static ConfigEntry PartnershipRange; private static ConfigEntry RequirePartnershipStructure; private static ConfigEntry PartnershipStructureRange; private static ConfigEntry PartnershipStructureTypeMap; private static ConfigEntry RequireProtectedAreaForPartnership; private static ConfigEntry AllowCancelAtStructure; private static ConfigEntry UpdateStructureStatus; private static ConfigEntry AutoCancelWhenStructureDestroyed; private static ConfigEntry NotifyPartnerInGame; private static ConfigEntry EnablePartnershipStructureHud; private static ConfigEntry PartnershipStructureHudSeconds; private static ConfigEntry EnableProtectedAreaHud; private static ConfigEntry ProtectedAreaHudRadius; private static ConfigEntry ProtectedAreaHudIntervalSeconds; private static ConfigEntry ProtectedAreaHudCenterOnEnter; private static ConfigEntry ProtectedAreaRequiredStructureGroups; private static ConfigEntry ProtectedAreaRequiredStructureGroupCount; private static ConfigEntry ProtectedAreaGuardianAlwaysProtects; private static ConfigEntry ProtectedAreaCompactHud; private static ConfigEntry ProtectedAreaShowMissingCriteria; private static ConfigEntry ProtectedAreaShowCriteriaBreakdown; private static ConfigEntry ProtectedAreaCountBaseSetAsOne; private static ConfigEntry EnableMajoritySleepSkip; private static ConfigEntry SleepRequiredSeconds; private static ConfigEntry SleepMajorityPercent; private static ConfigEntry SleepCheckSeconds; private static ConfigEntry EnableSleepVotePopup; private static ConfigEntry SleepVoteResponseSeconds; private static ConfigEntry RequireStrictSleepMajority; private static ConfigEntry ExcludeAfkPlayersFromSleepVote; private static ConfigEntry SleepVoteAfkAfterSeconds; private static ConfigEntry MinimumActiveSleepVoters; private static ConfigEntry SleepNoVoteIsVeto; private static ConfigEntry SleepVoteCooldownSeconds; private static ConfigEntry BlockSleepVoteDuringBossFight; private static ConfigEntry BlockSleepVoteDuringRaid; private static ConfigEntry BlockSleepVoteIfEnemiesNearby; private static ConfigEntry SleepVoteEnemyBlockRadius; private static ConfigEntry ReportSleepVoteEvents; private static ConfigEntry ReportPartnershipEvents; private const string PartnershipStatusKey = "ChallengeHub.Partnership.Status"; private const string PartnershipTypeKey = "ChallengeHub.Partnership.Type"; private const string PartnershipUserAIdKey = "ChallengeHub.Partnership.UserAId"; private const string PartnershipUserANameKey = "ChallengeHub.Partnership.UserAName"; private const string PartnershipUserBIdKey = "ChallengeHub.Partnership.UserBId"; private const string PartnershipUserBNameKey = "ChallengeHub.Partnership.UserBName"; private const string PartnershipUpdatedUtcKey = "ChallengeHub.Partnership.UpdatedUtc"; private const string PartnershipIdKey = "ChallengeHub.Partnership.StructureId"; private static readonly Dictionary SleptThisNight = new Dictionary(); private static readonly Dictionary LastPlayerPositions = new Dictionary(); private static readonly Dictionary LastPlayerActivityAt = new Dictionary(); private static readonly Dictionary SleepVoteResponses = new Dictionary(); private static readonly HashSet SleepVoteBedYesPlayers = new HashSet(); private static bool _nightWasActive; private static bool _sleepVoteActive; private static string _sleepVoteId = string.Empty; private static string _sleepVoteStarterName = string.Empty; private static long _sleepVoteStarterId; private static float _sleepVoteDeadlineAt; private static float _sleepVoteStartedAt; private static float _nextSleepVoteBroadcastAt; private static float _sleepVoteCooldownUntil; private static int _sleepVoteNeededPlayers; private static int _sleepVoteActivePlayers; private static bool _localSleepVotePopupActive; private static string _localSleepVoteId = string.Empty; private static string _localSleepVoteStarterName = string.Empty; private static float _localSleepVoteDeadlineAt; private static float _nextLocalSleepVoteMessageAt; private static float _nextProtectedHudMessage; private static bool _localProtectedAreaActive; private static string _localProtectedAreaReason = string.Empty; private const int ProtectedAreaColliderCapacity = 4096; private static readonly Collider[] ProtectedAreaColliderBuffer = (Collider[])(object)new Collider[4096]; private static readonly HashSet ProtectedAreaSeenPieceIds = new HashSet(); private static bool _protectedAreaColliderOverflowWarned; private static float _localSleepSecondsThisNight; private static bool _localSleepReadySent; private static float _nextLocalSleepProgressMessageAt; private static float _nextPartnershipKeyAt; private static float _nextSleepReportAt; private static float _nextPartnershipHudMessage; private static ZRoutedRpc _sleepVoteRpcInstance; private static Piece _dialogStructure; private static bool _partnershipDialogOpen; private const string SleepVoteRequestRpc = "ChallengeHubSleepVoteRequest"; private const string SleepVoteResponseRpc = "ChallengeHubSleepVoteResponse"; private const string SleepVoteResultRpc = "ChallengeHubSleepVoteResult"; private const string SleepVoteReadyRpc = "ChallengeHubSleepVoteReady"; internal static void Initialize(Plugin plugin) { if (!_initialized && !((Object)(object)plugin == (Object)null)) { _initialized = true; _plugin = plugin; EnablePartnerships = ((BaseUnityPlugin)plugin).Config.Bind("Partnerships", "EnablePartnerships", true, "Aktiviert Partnerschaften: Handel, Kampf, Frieden und Bossfight."); DefaultPartnershipType = ((BaseUnityPlugin)plugin).Config.Bind("Partnerships", "DefaultPartnershipType", "trade", "Fallback-Typ fuer STRG+P: trade, combat, peace, bossfight, farm, build oder explore."); PartnershipRange = ((BaseUnityPlugin)plugin).Config.Bind("Partnerships", "PartnershipRange", 8f, "Maximale Distanz in Metern, in der ein Partner-Spieler fuer eine neue Partnerschaft gesucht wird."); ReportPartnershipEvents = ((BaseUnityPlugin)plugin).Config.Bind("Partnerships", "ReportPartnershipEvents", true, "Wenn true, sendet die Mod partnership_request/partnership_cancelled Events an ChallengeHub."); RequirePartnershipStructure = ((BaseUnityPlugin)plugin).Config.Bind("Partnerships", "RequirePartnershipStructure", true, "Wenn true, muss STRG+P auf ein Partnerschafts-Konstrukt bzw. einen konfigurierten Vertragsort gerichtet sein."); PartnershipStructureRange = ((BaseUnityPlugin)plugin).Config.Bind("Partnerships", "PartnershipStructureRange", 6f, "Maximale Entfernung zum Vertrags-Konstrukt."); PartnershipStructureTypeMap = ((BaseUnityPlugin)plugin).Config.Bind("Partnerships", "StructureTypeMap", "trade:piece_challengehub_trade_post,combat:piece_challengehub_combat_banner,peace:piece_challengehub_peace_stone,bossfight:piece_challengehub_boss_oath_stone,farm:piece_challengehub_guardian_farmer,build:piece_challengehub_guardian_builder,explore:piece_challengehub_guardian_explorer,trade:piece_challengehub_guardian_neutral", "Prefab-Zuordnung fuer Vertragsorte. Format: typ:prefab,typ:prefab. Bis eigene Vertrags-Gebaeude gebaut sind, koennen Guardian-Stones als Ort genutzt werden."); RequireProtectedAreaForPartnership = ((BaseUnityPlugin)plugin).Config.Bind("Partnerships", "RequireProtectedAreaForPartnership", true, "Wenn true, duerfen Partnerschaften nur in einem geschuetzten Bereich abgeschlossen/beendet werden."); AllowCancelAtStructure = ((BaseUnityPlugin)plugin).Config.Bind("Partnerships", "AllowCancelAtStructure", true, "Wenn true, kann ein Spieler eine bestehende Partnerschaft am urspruenglichen Vertragsort mit STRG+P beenden."); UpdateStructureStatus = ((BaseUnityPlugin)plugin).Config.Bind("Partnerships", "UpdateStructureStatus", true, "Wenn true, schreibt die Mod Status/Typ/Spieler in das ZDO des Vertrags-Konstrukts und versucht vorhandene Schildtexte zu aktualisieren."); AutoCancelWhenStructureDestroyed = ((BaseUnityPlugin)plugin).Config.Bind("Partnerships", "AutoCancelWhenStructureDestroyed", true, "Wenn true, wird eine aktive/wartende Partnerschaft automatisch beendet, sobald das Vertrags-Konstrukt zerstoert wird."); NotifyPartnerInGame = ((BaseUnityPlugin)plugin).Config.Bind("Partnerships", "NotifyPartnerInGame", true, "Wenn true, bekommen beteiligte Online-Spieler Partner-Anfragen, Bestätigungen und Beendigungen zusätzlich direkt im Spiel als HUD-Meldung."); EnablePartnershipStructureHud = ((BaseUnityPlugin)plugin).Config.Bind("Partnerships", "EnablePartnershipStructureHud", true, "Wenn true, zeigt die Mod beim Anschauen eines Vertrags-Konstrukts Status, Typ und Partner direkt im Spiel an."); PartnershipStructureHudSeconds = ((BaseUnityPlugin)plugin).Config.Bind("Partnerships", "PartnershipStructureHudSeconds", 2.5f, "Mindestabstand in Sekunden zwischen Vertragsort-HUD-Meldungen."); EnableProtectedAreaHud = ((BaseUnityPlugin)plugin).Config.Bind("ProtectedArea", "EnableProtectedAreaHud", true, "Zeigt im Spiel eine Meldung, wenn der Spieler in einer reset-geschuetzten Zone steht."); ProtectedAreaHudRadius = ((BaseUnityPlugin)plugin).Config.Bind("ProtectedArea", "ProtectedAreaHudRadius", 80f, "Suchradius fuer Schutzobjekte/Waechtersteine um den Spieler."); ProtectedAreaHudIntervalSeconds = ((BaseUnityPlugin)plugin).Config.Bind("ProtectedArea", "ProtectedAreaHudIntervalSeconds", 4f, "Sekunden zwischen Schutzbereich-HUD-Meldungen, solange der Spieler in einem geschuetzten Bereich steht."); ProtectedAreaHudCenterOnEnter = ((BaseUnityPlugin)plugin).Config.Bind("ProtectedArea", "ProtectedAreaHudCenterOnEnter", true, "Wenn true, wird beim Betreten eines geschuetzten Bereichs zusaetzlich eine Center-Meldung angezeigt."); ProtectedAreaRequiredStructureGroups = ((BaseUnityPlugin)plugin).Config.Bind("ProtectedArea", "RequiredStructureGroups", "Werkbank:piece_workbench;Bett:piece_bed,piece_bed02;Feuer:fire_pit,hearth,piece_hearth,bonfire,piece_bonfire,piece_brazierceiling01,piece_brazierfloor01;Kochen:piece_cauldron,piece_cookingstation,piece_cookingstation_iron", "Schutz-Kriterien als Gruppen. Format: Anzeigename:prefab1,prefab2;Anzeigename:prefab3. Es zaehlen unterschiedliche erfuellte Gruppen, nicht mehrere gleiche Werkbaenke."); ProtectedAreaRequiredStructureGroupCount = ((BaseUnityPlugin)plugin).Config.Bind("ProtectedArea", "RequiredStructureGroupCount", 2, "So viele unterschiedliche Schutz-Kriterien muessen im Radius erfuellt sein. 2 bedeutet z.B. Werkbank + Feuer oder Bett + Feuer; Werkbank allein reicht nicht."); ProtectedAreaGuardianAlwaysProtects = ((BaseUnityPlugin)plugin).Config.Bind("ProtectedArea", "GuardianAlwaysProtects", true, "Wenn true, gilt ein ChallengeHub-Waechterstein im Radius immer als geschuetzter Bereich. Ohne Waechter gelten die RequiredStructureGroups."); ProtectedAreaCompactHud = ((BaseUnityPlugin)plugin).Config.Bind("ProtectedArea", "CompactHud", true, "Wenn true, zeigt der Schutzbereich-HUD im aktiven Zustand nur die Anzahl erfuellter Kriterien, z.B. 'Geschuetzter Bereich aktiv: 2'."); ProtectedAreaShowMissingCriteria = ((BaseUnityPlugin)plugin).Config.Bind("ProtectedArea", "ShowMissingCriteria", true, "Wenn true, zeigt der HUD bei teilweise erfuellten Kriterien kurz an, welche Kriterien fehlen. Fehlende Kriterien werden rot markiert."); ProtectedAreaShowCriteriaBreakdown = ((BaseUnityPlugin)plugin).Config.Bind("ProtectedArea", "ShowCriteriaBreakdown", true, "Wenn true, zeigt der Schutzbereich-HUD eine kurze Aufschluesselung untereinander, welche Kriterien erfuellt oder nicht erfuellt sind."); ProtectedAreaCountBaseSetAsOne = ((BaseUnityPlugin)plugin).Config.Bind("ProtectedArea", "CountBaseSetAsOne", true, "Wenn true, zaehlt das komplette Base-Set aus Schutz-Kriterien als 1 Schutzquelle; ein Waechterstein zaehlt zusaetzlich als 1 Schutzquelle."); EnableMajoritySleepSkip = ((BaseUnityPlugin)plugin).Config.Bind("SleepVote", "EnableMajoritySleepSkip", true, "Wenn true, wird die Nacht per Schlaf-Abstimmung uebersprungen."); SleepRequiredSeconds = ((BaseUnityPlugin)plugin).Config.Bind("SleepVote", "SleepRequiredSeconds", 5f, "Sekunden im Bett bis zum Start der Schlaf-Abstimmung. Bei zwei aktiven Spielern startet sie damit, sobald einer im Bett liegt."); if (Mathf.Abs(SleepRequiredSeconds.Value - 60f) < 0.01f) { SleepRequiredSeconds.Value = 5f; } SleepMajorityPercent = ((BaseUnityPlugin)plugin).Config.Bind("SleepVote", "MajorityPercent", 0.5f, "Mehrheitsschwelle. 0.5 bedeutet: mehr als die Haelfte der aktiven Online-Spieler."); SleepCheckSeconds = ((BaseUnityPlugin)plugin).Config.Bind("SleepVote", "SleepCheckSeconds", 2f, "Sekunden zwischen Sleep-Vote-Pruefungen."); EnableSleepVotePopup = ((BaseUnityPlugin)plugin).Config.Bind("SleepVote", "EnableSleepVotePopup", true, "Wenn true, startet nach kurzer Bett-Erkennung eine Ingame-Abstimmung fuer aktive Spieler statt sofort zu skippen."); SleepVoteResponseSeconds = ((BaseUnityPlugin)plugin).Config.Bind("SleepVote", "SleepVoteResponseSeconds", 30f, "Sekunden, die aktive Spieler fuer F7=Ja / F8=Nein Zeit haben."); RequireStrictSleepMajority = ((BaseUnityPlugin)plugin).Config.Bind("SleepVote", "RequireStrictMajority", false, "Kompatibilitaetsoption. Standard ist mindestens 50%: Bei zwei aktiven Spielern reicht eine Ja-Stimme."); ExcludeAfkPlayersFromSleepVote = ((BaseUnityPlugin)plugin).Config.Bind("SleepVote", "ExcludeAfkPlayers", true, "Wenn true, werden Spieler ohne Bewegung/Schlaf nach AfkAfterSeconds nicht in die Mehrheit gerechnet."); SleepVoteAfkAfterSeconds = ((BaseUnityPlugin)plugin).Config.Bind("SleepVote", "AfkAfterSeconds", 180f, "Sekunden ohne Bewegung, nach denen ein Spieler fuer Schlaf-Abstimmungen als AFK gilt."); MinimumActiveSleepVoters = ((BaseUnityPlugin)plugin).Config.Bind("SleepVote", "MinimumActiveVotes", 1, "Mindestanzahl aktiver Spieler, die fuer eine Schlaf-Abstimmung gezaehlt werden. 1 ist sinnvoll fuer Solo-Testserver."); SleepNoVoteIsVeto = ((BaseUnityPlugin)plugin).Config.Bind("SleepVote", "SleepNoVoteIsVeto", false, "Wenn true, blockiert eine einzige Nein-Stimme die laufende Abstimmung."); SleepVoteCooldownSeconds = ((BaseUnityPlugin)plugin).Config.Bind("SleepVote", "SleepVoteCooldownSeconds", 120f, "Cooldown nach einer abgelehnten/abgelaufenen Abstimmung."); BlockSleepVoteDuringBossFight = ((BaseUnityPlugin)plugin).Config.Bind("SleepVote", "BlockSleepVoteDuringBossFight", true, "Wenn true, startet/endet keine Schlaf-Abstimmung, solange ein Boss in Spielernaehe aktiv ist."); BlockSleepVoteDuringRaid = ((BaseUnityPlugin)plugin).Config.Bind("SleepVote", "BlockSleepVoteDuringRaid", true, "Wenn true, startet/endet keine Schlaf-Abstimmung waehrend eines aktiven Raid-/Random-Events."); BlockSleepVoteIfEnemiesNearby = ((BaseUnityPlugin)plugin).Config.Bind("SleepVote", "BlockSleepVoteIfEnemiesNearby", true, "Wenn true, blockieren alarmierte Gegner in Spielernaehe den Nachtsprung."); SleepVoteEnemyBlockRadius = ((BaseUnityPlugin)plugin).Config.Bind("SleepVote", "EnemyBlockRadius", 30f, "Radius in Metern, in dem alarmierte Gegner eine Schlaf-Abstimmung blockieren."); ReportSleepVoteEvents = ((BaseUnityPlugin)plugin).Config.Bind("SleepVote", "ReportSleepVoteEvents", true, "Wenn true, sendet die Mod sleep_vote_started/response/completed/rejected/blocked Events an ChallengeHub."); ((MonoBehaviour)plugin).StartCoroutine(PartnershipInputLoop()); ((MonoBehaviour)plugin).StartCoroutine(PartnershipStructureHudLoop()); ((MonoBehaviour)plugin).StartCoroutine(ProtectedAreaHudLoop()); ((MonoBehaviour)plugin).StartCoroutine(MajoritySleepLoop()); ((MonoBehaviour)plugin).StartCoroutine(LocalSleepProgressLoop()); ((MonoBehaviour)plugin).StartCoroutine(SleepVoteInputLoop()); ((MonoBehaviour)plugin).StartCoroutine(RegisterSleepVoteRpcWhenReady()); ((Component)plugin).gameObject.AddComponent(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"ChallengeHub Partnerschaften/Schutzbereich/SleepVote 2.12.50 initialisiert."); } } } internal static bool TryGetActiveExplorerPartner(Player player, out string partnerId, out string partnerName) { partnerId = string.Empty; partnerName = string.Empty; if ((Object)(object)player == (Object)null) { return false; } string text = SafePlayerId(player).ToString(); Piece[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Piece val in array) { if ((Object)(object)val == (Object)null) { continue; } ZNetView component = ((Component)val).GetComponent(); ZDO val2 = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); if (val2 != null && !(ReadZdoString(val2, "ChallengeHub.Partnership.Status") != "active") && !(NormalizePartnershipType(ReadZdoString(val2, "ChallengeHub.Partnership.Type")) != "explore")) { string text2 = ReadZdoString(val2, "ChallengeHub.Partnership.UserAId"); string text3 = ReadZdoString(val2, "ChallengeHub.Partnership.UserBId"); if (text2 == text) { partnerId = text3; partnerName = ReadZdoString(val2, "ChallengeHub.Partnership.UserBName"); return !string.IsNullOrWhiteSpace(partnerId); } if (text3 == text) { partnerId = text2; partnerName = ReadZdoString(val2, "ChallengeHub.Partnership.UserAName"); return !string.IsNullOrWhiteSpace(partnerId); } } } return false; } private static IEnumerator PartnershipInputLoop() { while (true) { yield return null; try { if (EnablePartnerships != null && EnablePartnerships.Value && !(Time.realtimeSinceStartup < _nextPartnershipKeyAt) && IsCtrlPressed() && Input.GetKeyDown((KeyCode)112)) { _nextPartnershipKeyAt = Time.realtimeSinceStartup + 0.75f; TryUsePartnershipHotkey(); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Partnerschaft-Hotkey fehlgeschlagen: " + ex.Message)); } } } } private static void TryUsePartnershipHotkey() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } if (RequirePartnershipStructure != null && RequirePartnershipStructure.Value) { Piece val = FindLookedAtPartnershipStructure(localPlayer, Mathf.Max(2f, PartnershipStructureRange.Value)); if ((Object)(object)val == (Object)null) { ((Character)localPlayer).Message((MessageType)2, "Kein Partnerschafts-Konstrukt im Blick.", 0, (Sprite)null); } else { OpenPartnershipDialog(val); } } else { TryRequestPartnershipWithoutStructure(localPlayer); } } private static void TryRequestPartnershipWithoutStructure(Player local) { Player val = FindLookedAtPlayer(local, Mathf.Max(2f, PartnershipRange.Value)); if ((Object)(object)val == (Object)null) { ((Character)local).Message((MessageType)2, "Kein Spieler fuer Partnerschaft im Blick.", 0, (Sprite)null); return; } string type = NormalizePartnershipType(DefaultPartnershipType.Value); SendPartnershipRequest(local, val, type, null, "ctrl_p_look_at_player"); } private static void TryUsePartnershipStructure(Player local, Piece structure) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)local == (Object)null || (Object)(object)structure == (Object)null) { return; } string type = ResolvePartnershipTypeForStructure(structure); ZNetView component = ((Component)structure).GetComponent(); ZDO zdo = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); string structureId = StructureId(structure, zdo); string structureName = DisplayStructureName(structure); if (RequireProtectedAreaForPartnership != null && RequireProtectedAreaForPartnership.Value && !IsInsideProtectedArea(((Component)structure).transform.position, out var _)) { ((Character)local).Message((MessageType)2, "Partnerschaften muessen in einem geschuetzten Bereich geschlossen werden.", 0, (Sprite)null); return; } string text = ReadZdoString(zdo, "ChallengeHub.Partnership.Status"); string text2 = ReadZdoString(zdo, "ChallengeHub.Partnership.UserAId"); string text3 = ReadZdoString(zdo, "ChallengeHub.Partnership.UserBId"); string text4 = SafePlayerId(local).ToString(); bool flag = !string.IsNullOrWhiteSpace(text4) && (text2 == text4 || text3 == text4); bool flag2 = text == "pending" && text3 == text4 && text2 != text4; if (AllowCancelAtStructure != null && AllowCancelAtStructure.Value && flag && (text == "active" || (text == "pending" && !flag2))) { string text5 = ((text2 == text4) ? text3 : text2); string partnerName = ((text2 == text4) ? ReadZdoString(zdo, "ChallengeHub.Partnership.UserBName") : ReadZdoString(zdo, "ChallengeHub.Partnership.UserAName")); MarkStructureStatus(zdo, structure, type, "cancelled", text2, ReadZdoString(zdo, "ChallengeHub.Partnership.UserAName"), text3, ReadZdoString(zdo, "ChallengeHub.Partnership.UserBName")); SendPartnershipCancelled(local, type, structure, structureId, structureName, text5, partnerName); ((Character)local).Message((MessageType)2, DisplayType(type) + " am Vertragsort beendet.", 0, (Sprite)null); NotifyPlayerInGame(FindPlayerById(text5), local.GetPlayerName() + " hat eure " + DisplayType(type) + " am Vertragsort beendet.", center: true); return; } Player val = FindBestPartnerNearStructure(local, ((Component)structure).transform.position, Mathf.Max(2f, PartnershipRange.Value)); if ((Object)(object)val == (Object)null) { if (text == "cancelled") { ((Character)local).Message((MessageType)2, "Diese Partnerschaft ist beendet. Fuer eine neue Partnerschaft muss der andere Spieler am Vertragsort sein.", 0, (Sprite)null); } else { ((Character)local).Message((MessageType)2, "Kein Partner-Spieler am Vertragsort gefunden.", 0, (Sprite)null); } return; } string text6 = SafePlayerId(val).ToString(); bool flag3 = text == "pending" && text2 == text6 && text3 == text4; string status = (flag3 ? "active" : "pending"); string userAId = (flag3 ? text6 : text4); string userAName = (flag3 ? val.GetPlayerName() : local.GetPlayerName()); string userBId = (flag3 ? text4 : text6); string userBName = (flag3 ? local.GetPlayerName() : val.GetPlayerName()); MarkStructureStatus(zdo, structure, type, status, userAId, userAName, userBId, userBName); SendPartnershipRequest(local, val, type, structure, flag3 ? "ctrl_p_contract_structure_accept" : "ctrl_p_contract_structure_request"); if (flag3) { ((Character)local).Message((MessageType)2, DisplayType(type) + " geschlossen am Vertragsort.", 0, (Sprite)null); NotifyPlayerInGame(val, local.GetPlayerName() + " hat eure " + DisplayType(type) + " am Vertragsort bestätigt.", center: true); } else { ((Character)local).Message((MessageType)2, "Anfrage am Vertragsort gesendet: " + DisplayType(type) + " an " + val.GetPlayerName(), 0, (Sprite)null); NotifyPlayerInGame(val, local.GetPlayerName() + " möchte eine " + DisplayType(type) + " schließen. Am gleichen Vertragsort STRG+P drücken.", center: true); } } private static void OpenPartnershipDialog(Piece structure) { if (!((Object)(object)structure == (Object)null)) { _dialogStructure = structure; _partnershipDialogOpen = true; } } private static void SendPartnershipRequest(Player local, Player target, string type, Piece structure, string method) { //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)local == (Object)null) && !((Object)(object)target == (Object)null)) { ((Character)local).Message((MessageType)2, "Partnerschaftsanfrage gesendet: " + DisplayType(type) + " an " + target.GetPlayerName(), 0, (Sprite)null); if (ReportPartnershipEvents != null && ReportPartnershipEvents.Value && (Object)(object)_plugin != (Object)null) { Dictionary dictionary = new Dictionary { { "partnershipType", type }, { "partnerPlayerId", target.GetPlayerID().ToString() }, { "partnerPlayerName", target.GetPlayerName() }, { "confirmMethod", method }, { "distance", Math.Round(Vector3.Distance(((Component)local).transform.position, ((Component)target).transform.position), 2) }, { "biome", Plugin.CurrentBiome(((Component)local).transform.position) }, { "position", Plugin.SerializeVector(((Component)local).transform.position) } }; AddStructurePayload(dictionary, structure); _plugin.SendEvent("partnership_request", local, dictionary); } } } private static void SendPartnershipCancelled(Player local, string type, Piece structure, string structureId, string structureName, string partnerId, string partnerName) { //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)local == (Object)null) && !((Object)(object)_plugin == (Object)null) && ReportPartnershipEvents != null && ReportPartnershipEvents.Value) { Dictionary extra = new Dictionary { { "partnershipType", type }, { "partnerPlayerId", partnerId ?? string.Empty }, { "partnerPlayerName", partnerName ?? string.Empty }, { "confirmMethod", "ctrl_p_contract_structure_cancel" }, { "partnershipAction", "cancel" }, { "partnershipStructureId", structureId ?? string.Empty }, { "partnershipStructureName", structureName ?? string.Empty }, { "structurePrefab", ((Object)(object)structure != (Object)null) ? NormalizeKey(((Object)structure).name) : string.Empty }, { "biome", ((Object)(object)structure != (Object)null) ? Plugin.CurrentBiome(((Component)structure).transform.position) : Plugin.CurrentBiome(((Component)local).transform.position) }, { "position", ((Object)(object)structure != (Object)null) ? Plugin.SerializeVector(((Component)structure).transform.position) : Plugin.SerializeVector(((Component)local).transform.position) } }; _plugin.SendEvent("partnership_cancelled", local, extra); } } private static void AddStructurePayload(Dictionary payload, Piece structure) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) if (payload != null && !((Object)(object)structure == (Object)null)) { ZNetView component = ((Component)structure).GetComponent(); ZDO zdo = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); payload["partnershipStructureId"] = StructureId(structure, zdo); payload["partnershipStructureName"] = DisplayStructureName(structure); payload["structurePrefab"] = NormalizeKey(((Object)structure).name); payload["structurePosition"] = Plugin.SerializeVector(((Component)structure).transform.position); } } private static Piece FindLookedAtPartnershipStructure(Player local, float range) { Piece val = FindLookedAtPiece(local, range); if ((Object)(object)val == (Object)null) { return null; } if (!string.IsNullOrWhiteSpace(ResolvePartnershipTypeForStructure(val))) { return val; } return null; } private static Piece FindLookedAtPiece(Player local, float range) { //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)local == (Object)null) { return null; } try { MethodInfo method = ((object)local).GetType().GetMethod("GetHoverObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); object obj = ((method != null) ? method.Invoke(local, null) : null); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if ((Object)(object)val == (Object)null) { Component val2 = (Component)((obj is Component) ? obj : null); if (val2 != null) { val = val2.gameObject; } } Piece val3 = (((Object)(object)val != (Object)null) ? val.GetComponentInParent() : null); if ((Object)(object)val3 != (Object)null && Vector3.Distance(((Component)local).transform.position, ((Component)val3).transform.position) <= range + 1f && !string.IsNullOrWhiteSpace(ResolvePartnershipTypeForStructure(val3))) { return val3; } } catch { } Transform val4 = (((Object)(object)GameCamera.instance != (Object)null) ? ((Component)GameCamera.instance).transform : ((Component)local).transform); Vector3 origin = val4.position; Vector3 forward = val4.forward; foreach (RaycastHit item in from item in Physics.SphereCastAll(origin, 0.35f, forward, range, -1, (QueryTriggerInteraction)2) orderby ((RaycastHit)(ref item)).distance select item) { RaycastHit current = item; if (!((Object)(object)((RaycastHit)(ref current)).collider == (Object)null)) { Piece componentInParent = ((Component)((RaycastHit)(ref current)).collider).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null && !string.IsNullOrWhiteSpace(ResolvePartnershipTypeForStructure(componentInParent))) { return componentInParent; } } } return (from piece in (from piece in Object.FindObjectsByType((FindObjectsSortMode)0) where (Object)(object)piece != (Object)null && !string.IsNullOrWhiteSpace(ResolvePartnershipTypeForStructure(piece)) where Vector3.Distance(origin, ((Component)piece).transform.position) <= range select piece).Where(delegate(Piece piece) { //IL_0001: 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_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) Vector3 val5 = forward; Vector3 val6 = ((Component)piece).transform.position - origin; return Vector3.Dot(val5, ((Vector3)(ref val6)).normalized) >= 0.82f; }) orderby Vector3.Angle(forward, ((Component)piece).transform.position - origin), Vector3.Distance(origin, ((Component)piece).transform.position) select piece).FirstOrDefault(); } private static string ResolvePartnershipTypeForStructure(Piece piece) { if ((Object)(object)piece == (Object)null) { return ""; } string text = NormalizeKey(((Object)piece).name); foreach (KeyValuePair> item in ParseStructureMap((PartnershipStructureTypeMap != null) ? PartnershipStructureTypeMap.Value : string.Empty)) { if (item.Value.Contains(text)) { return item.Key; } } if (text.Contains("trade") || text.Contains("market") || text.Contains("handel")) { return "trade"; } if (text.Contains("combat") || text.Contains("kampf") || text.Contains("banner")) { return "combat"; } if (text.Contains("peace") || text.Contains("frieden")) { return "peace"; } if (text.Contains("boss")) { return "bossfight"; } if (text.Contains("farmer") || text.Contains("farm")) { return "farm"; } if (text.Contains("builder") || text.Contains("build")) { return "build"; } if (text.Contains("explorer") || text.Contains("explore") || text.Contains("map")) { return "explore"; } if (((Object)(object)_plugin != (Object)null && _plugin.IsChallengeHubGuardianPieceForHover(piece)) || text.Contains("piece_challengehub_guardian") || text.Contains("dverger_guardstone") || text.Contains("guardstone")) { return NormalizePartnershipType(DefaultPartnershipType.Value); } return ""; } private static Dictionary> ParseStructureMap(string text) { Dictionary> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); string[] array = (text ?? string.Empty).Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { ':' }); if (array2.Length < 2) { continue; } string text2 = NormalizePartnershipType(array2[0]); string text3 = NormalizeKey(array2[1]); if (!string.IsNullOrWhiteSpace(text2) && !string.IsNullOrWhiteSpace(text3)) { if (!dictionary.ContainsKey(text2)) { dictionary[text2] = new HashSet(StringComparer.OrdinalIgnoreCase); } dictionary[text2].Add(text3); } } return dictionary; } private static Player FindBestPartnerNearStructure(Player local, Vector3 structurePosition, float range) { //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) //IL_004a: 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) List allPlayers = Player.GetAllPlayers(); if (allPlayers == null) { return null; } Player val = FindLookedAtPlayer(local, Mathf.Max(range, PartnershipRange.Value)); if ((Object)(object)val != (Object)null && Vector3.Distance(((Component)val).transform.position, structurePosition) <= Mathf.Max(2f, PartnershipStructureRange.Value + 2f)) { return val; } return (from player in allPlayers where (Object)(object)player != (Object)null && player != local && Vector3.Distance(((Component)player).transform.position, structurePosition) <= Mathf.Max(2f, PartnershipStructureRange.Value + 2f) orderby Vector3.Distance(((Component)player).transform.position, structurePosition) select player).FirstOrDefault(); } private static Player FindPlayerById(string playerId) { if (string.IsNullOrWhiteSpace(playerId)) { return null; } return Player.GetAllPlayers()?.FirstOrDefault((Func)((Player player) => (Object)(object)player != (Object)null && SafePlayerId(player).ToString() == playerId)); } private static void MarkStructureStatus(ZDO zdo, Piece structure, string type, string status, string userAId, string userAName, string userBId, string userBName) { if (UpdateStructureStatus == null || !UpdateStructureStatus.Value) { return; } try { ZNetView val = (((Object)(object)structure != (Object)null) ? ((Component)structure).GetComponent() : null); if ((Object)(object)val != (Object)null && val.IsValid() && !val.IsOwner()) { try { val.ClaimOwnership(); } catch { } } if ((Object)(object)val != (Object)null && val.GetZDO() != null) { zdo = val.GetZDO(); } if (zdo != null) { zdo.Set("ChallengeHub.Partnership.Status", status ?? string.Empty); zdo.Set("ChallengeHub.Partnership.Type", type ?? string.Empty); zdo.Set("ChallengeHub.Partnership.UserAId", userAId ?? string.Empty); zdo.Set("ChallengeHub.Partnership.UserAName", userAName ?? string.Empty); zdo.Set("ChallengeHub.Partnership.UserBId", userBId ?? string.Empty); zdo.Set("ChallengeHub.Partnership.UserBName", userBName ?? string.Empty); zdo.Set("ChallengeHub.Partnership.UpdatedUtc", DateTime.UtcNow.ToString("O")); zdo.Set("ChallengeHub.Partnership.StructureId", StructureId(structure, zdo)); } TryUpdateSignText(structure, type, status, userAName, userBName); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Partnerschafts-Konstruktstatus konnte nicht geschrieben werden: " + ex.Message)); } } } private static void TryUpdateSignText(Piece structure, string type, string status, string userAName, string userBName) { if ((Object)(object)structure == (Object)null) { return; } string text = DisplayType(type) + "\n" + DisplayStatus(status) + "\n" + (userAName ?? "?") + " ↔ " + (userBName ?? "?"); Sign[] componentsInChildren = ((Component)structure).GetComponentsInChildren(true); foreach (Sign val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } try { MethodInfo method = ((object)val).GetType().GetMethod("SetText", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { method.Invoke(val, new object[1] { text }); continue; } FieldInfo field = ((object)val).GetType().GetField("m_text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(val, text); } } catch { } } } private static string StructureId(Piece structure, ZDO zdo) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) try { if (zdo != null) { string text = ReadZdoString(zdo, "ChallengeHub.Partnership.StructureId"); if (!string.IsNullOrWhiteSpace(text)) { return text; } FieldInfo field = ((object)zdo).GetType().GetField("m_uid", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); object obj = ((field != null) ? field.GetValue(zdo) : null); if (obj != null) { return obj.ToString(); } } } catch { } if ((Object)(object)structure == (Object)null) { return ""; } Vector3 position = ((Component)structure).transform.position; return NormalizeKey(((Object)structure).name) + ":" + Mathf.RoundToInt(position.x) + ":" + Mathf.RoundToInt(position.y) + ":" + Mathf.RoundToInt(position.z); } private static string DisplayStructureName(Piece structure) { if ((Object)(object)structure == (Object)null) { return "Vertragsort"; } try { Piece component = ((Component)structure).GetComponent(); if ((Object)(object)component != (Object)null && !string.IsNullOrWhiteSpace(component.m_name)) { return component.m_name; } } catch { } return NormalizeKey(((Object)structure).name).Replace("_", " "); } private static string ReadZdoString(ZDO zdo, string key) { try { return (zdo != null) ? zdo.GetString(key, string.Empty) : string.Empty; } catch { return string.Empty; } } private static Player FindLookedAtPlayer(Player local, float range) { //IL_0011: 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_0020: 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) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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) //IL_006c: 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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: 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_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_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) List allPlayers = Player.GetAllPlayers(); if (allPlayers == null) { return null; } Vector3 val = ((Component)local).transform.position + Vector3.up * 1.6f; Vector3 forward = ((Component)local).transform.forward; Player result = null; float num = -1f; foreach (Player item in allPlayers) { if ((Object)(object)item == (Object)null || item == local) { continue; } Vector3 val2 = ((Component)item).transform.position + Vector3.up * 1.2f - val; float magnitude = ((Vector3)(ref val2)).magnitude; if (magnitude > range || magnitude < 0.1f) { continue; } float num2 = Vector3.Dot(forward, ((Vector3)(ref val2)).normalized); if (!(num2 < 0.65f)) { float num3 = num2 / Mathf.Max(1f, magnitude); if (num3 > num) { num = num3; result = item; } } } return result; } private static IEnumerator PartnershipStructureHudLoop() { while (true) { yield return (object)new WaitForSeconds(0.5f); try { if (EnablePartnershipStructureHud == null || !EnablePartnershipStructureHud.Value) { continue; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || Time.realtimeSinceStartup < _nextPartnershipHudMessage) { continue; } Piece val = FindLookedAtPartnershipStructure(localPlayer, Mathf.Max(2f, (PartnershipStructureRange != null) ? PartnershipStructureRange.Value : 6f)); if (!((Object)(object)val == (Object)null)) { ZNetView component = ((Component)val).GetComponent(); ZDO zdo = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); string status = ReadZdoString(zdo, "ChallengeHub.Partnership.Status"); string text = ReadZdoString(zdo, "ChallengeHub.Partnership.Type"); if (string.IsNullOrWhiteSpace(text)) { text = ResolvePartnershipTypeForStructure(val); } string text2 = ReadZdoString(zdo, "ChallengeHub.Partnership.UserAName"); string text3 = ReadZdoString(zdo, "ChallengeHub.Partnership.UserBName"); string text4 = "Vertragsort: " + DisplayType(text) + " – " + DisplayStatus(status); if (!string.IsNullOrWhiteSpace(text2) || !string.IsNullOrWhiteSpace(text3)) { text4 = text4 + " – " + (text2 ?? "?") + " ↔ " + (text3 ?? "?"); } ((Character)localPlayer).Message((MessageType)1, text4, 0, (Sprite)null); _nextPartnershipHudMessage = Time.realtimeSinceStartup + Mathf.Max(0.5f, (PartnershipStructureHudSeconds != null) ? PartnershipStructureHudSeconds.Value : 2.5f); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Partnerschafts-HUD fehlgeschlagen: " + ex.Message)); } } } } internal static void HandlePartnershipStructureDestroyed(Piece structure) { //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_0301: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)structure == (Object)null) { return; } try { if (AutoCancelWhenStructureDestroyed == null || !AutoCancelWhenStructureDestroyed.Value) { return; } ZNetView component = ((Component)structure).GetComponent(); ZDO val = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); if (val == null) { return; } string text = ReadZdoString(val, "ChallengeHub.Partnership.Status"); if (!(text != "active") || !(text != "pending")) { string text2 = ReadZdoString(val, "ChallengeHub.Partnership.Type"); if (string.IsNullOrWhiteSpace(text2)) { text2 = ResolvePartnershipTypeForStructure(structure); } string text3 = ReadZdoString(val, "ChallengeHub.Partnership.UserAId"); string text4 = ReadZdoString(val, "ChallengeHub.Partnership.UserAName"); string text5 = ReadZdoString(val, "ChallengeHub.Partnership.UserBId"); string text6 = ReadZdoString(val, "ChallengeHub.Partnership.UserBName"); string text7 = StructureId(structure, val); string text8 = DisplayStructureName(structure); val.Set("ChallengeHub.Partnership.Status", "cancelled"); val.Set("ChallengeHub.Partnership.UpdatedUtc", DateTime.UtcNow.ToString("O")); val.Set("ChallengeHub.Partnership.CancelReason", "structure_destroyed"); Player obj = FindPlayerById(text3); Player val2 = FindPlayerById(text5); Player val3 = obj ?? val2 ?? Player.m_localPlayer ?? FindAnyOnlinePlayer(); string message = DisplayType(text2) + " wurde beendet: Vertragsgebäude wurde zerstört."; NotifyPlayerInGame(obj, message, center: true); NotifyPlayerInGame(val2, message, center: true); if ((Object)(object)_plugin != (Object)null && (Object)(object)val3 != (Object)null && ReportPartnershipEvents != null && ReportPartnershipEvents.Value) { string text9 = SafePlayerId(val3).ToString(); string text10 = ((text9 == text3) ? text5 : text3); string text11 = ((text9 == text3) ? text6 : text4); _plugin.SendEvent("partnership_cancelled", val3, new Dictionary { { "partnershipType", text2 }, { "partnershipAction", "cancel" }, { "cancelReason", "structure_destroyed" }, { "partnershipStructureDestroyed", true }, { "partnerPlayerId", text10 ?? string.Empty }, { "partnerPlayerName", text11 ?? string.Empty }, { "userAPlayerId", text3 ?? string.Empty }, { "userAPlayerName", text4 ?? string.Empty }, { "userBPlayerId", text5 ?? string.Empty }, { "userBPlayerName", text6 ?? string.Empty }, { "partnershipStructureId", text7 ?? string.Empty }, { "partnershipStructureName", text8 ?? string.Empty }, { "structurePrefab", NormalizeKey(((Object)structure).name) }, { "biome", Plugin.CurrentBiome(((Component)structure).transform.position) }, { "position", Plugin.SerializeVector(((Component)structure).transform.position) } }); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Partnerschaft konnte beim Zerstoeren des Vertrags-Konstrukts nicht beendet werden: " + ex.Message)); } } } private static Player FindAnyOnlinePlayer() { try { return Player.GetAllPlayers()?.FirstOrDefault((Func)((Player player) => (Object)(object)player != (Object)null)); } catch { return null; } } private static void NotifyPlayerInGame(Player player, string message, bool center) { if ((Object)(object)player == (Object)null || string.IsNullOrWhiteSpace(message) || (NotifyPartnerInGame != null && !NotifyPartnerInGame.Value)) { return; } try { ((Character)player).Message((MessageType)((!center) ? 1 : 2), message, 0, (Sprite)null); } catch { } } private static IEnumerator ProtectedAreaHudLoop() { while (true) { yield return (object)new WaitForSeconds(1f); try { if (EnableProtectedAreaHud == null || !EnableProtectedAreaHud.Value) { continue; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { continue; } ProtectedAreaStatus protectedAreaStatus = GetProtectedAreaStatus(((Component)localPlayer).transform.position); if (protectedAreaStatus.IsProtected) { string text = BuildProtectedAreaActiveHud(protectedAreaStatus); bool num = !_localProtectedAreaActive || !string.Equals(_localProtectedAreaReason, text ?? string.Empty, StringComparison.Ordinal); _localProtectedAreaActive = true; _localProtectedAreaReason = text ?? string.Empty; if (num && (ProtectedAreaHudCenterOnEnter == null || ProtectedAreaHudCenterOnEnter.Value)) { ((Character)localPlayer).Message((MessageType)2, "Geschützter Bereich aktiv: " + text, 0, (Sprite)null); _nextProtectedHudMessage = 0f; } if (Time.realtimeSinceStartup >= _nextProtectedHudMessage) { float num2 = Mathf.Max(2f, (ProtectedAreaHudIntervalSeconds != null) ? ProtectedAreaHudIntervalSeconds.Value : 4f); _nextProtectedHudMessage = Time.realtimeSinceStartup + num2; ((Character)localPlayer).Message((MessageType)1, "Geschuetzter Bereich: " + text, 0, (Sprite)null); } } else { if (_localProtectedAreaActive) { _localProtectedAreaActive = false; _localProtectedAreaReason = string.Empty; ((Character)localPlayer).Message((MessageType)1, "Geschuetzten Bereich verlassen.", 0, (Sprite)null); } if (ProtectedAreaShowMissingCriteria != null && ProtectedAreaShowMissingCriteria.Value && protectedAreaStatus.MatchedCount > 0 && protectedAreaStatus.MatchedCount < protectedAreaStatus.RequiredCount && Time.realtimeSinceStartup >= _nextProtectedHudMessage) { float num3 = Mathf.Max(2f, (ProtectedAreaHudIntervalSeconds != null) ? ProtectedAreaHudIntervalSeconds.Value : 4f); _nextProtectedHudMessage = Time.realtimeSinceStartup + num3; ((Character)localPlayer).Message((MessageType)1, BuildProtectedAreaMissingHud(protectedAreaStatus), 0, (Sprite)null); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Schutzbereich-HUD pruefung fehlgeschlagen: " + ex.Message)); } } } } private static bool IsInsideProtectedArea(Vector3 position, out string reason) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) ProtectedAreaStatus protectedAreaStatus = GetProtectedAreaStatus(position); reason = (protectedAreaStatus.IsProtected ? BuildProtectedAreaActiveHud(protectedAreaStatus) : BuildProtectedAreaMissingHud(protectedAreaStatus)); return protectedAreaStatus.IsProtected; } private static ProtectedAreaStatus GetProtectedAreaStatus(Vector3 position) { //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) ProtectedAreaStatus status = new ProtectedAreaStatus(); float num = Mathf.Max(16f, (ProtectedAreaHudRadius != null) ? ProtectedAreaHudRadius.Value : 80f); float num2 = num * num; List list = ParseProtectedStructureGroups(); HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); status.RequiredCount = Mathf.Max(1, (ProtectedAreaRequiredStructureGroupCount != null) ? ProtectedAreaRequiredStructureGroupCount.Value : 2); status.AllCriteria = (from g in list select g.Label into label where !string.IsNullOrWhiteSpace(label) select label).Distinct(StringComparer.OrdinalIgnoreCase).ToList(); status.HasGuardian = GuardianStoneRuntimeCache.HasGuardianWithin(position, num); ProtectedAreaSeenPieceIds.Clear(); int num3 = Physics.OverlapSphereNonAlloc(position, num, ProtectedAreaColliderBuffer, -1, (QueryTriggerInteraction)2); Collider[] array = null; if (num3 >= ProtectedAreaColliderBuffer.Length) { array = Physics.OverlapSphere(position, num, -1, (QueryTriggerInteraction)2); if (!_protectedAreaColliderOverflowWarned) { _protectedAreaColliderOverflowWarned = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Schutzgrad-HUD: Collider-Puffer ausgelastet; vollstaendige Ueberlaufpruefung mit " + array.Length + " Treffern aktiviert.")); } } } Collider[] array2 = array ?? ProtectedAreaColliderBuffer; int num4 = ((array != null) ? array.Length : Mathf.Min(num3, ProtectedAreaColliderBuffer.Length)); for (int num5 = 0; num5 < num4; num5++) { Collider val = array2[num5]; if (array == null) { ProtectedAreaColliderBuffer[num5] = null; } if ((Object)(object)val == (Object)null) { continue; } Piece componentInParent = ((Component)val).GetComponentInParent(); if ((Object)(object)componentInParent == (Object)null || (Object)(object)((Component)componentInParent).gameObject == (Object)null) { continue; } int instanceID = ((Object)((Component)componentInParent).gameObject).GetInstanceID(); if (ProtectedAreaSeenPieceIds.Add(instanceID)) { Vector3 val2 = ((Component)componentInParent).transform.position - position; if (!(((Vector3)(ref val2)).sqrMagnitude > num2)) { AccumulateProtectedAreaPiece(componentInParent, list, hashSet, status); } } } status.MatchedCount = hashSet.Count; status.MissingLabels = status.AllCriteria.Where((string label) => !status.MatchedLabels.Any((string matched) => string.Equals(matched, label, StringComparison.OrdinalIgnoreCase))).Take(4).ToList(); status.IsProtected = status.MatchedCount >= status.RequiredCount || (status.HasGuardian && (ProtectedAreaGuardianAlwaysProtects == null || ProtectedAreaGuardianAlwaysProtects.Value)); return status; } private static void AccumulateProtectedAreaPiece(Piece piece, List groups, HashSet matchedGroupKeys, ProtectedAreaStatus status) { if ((Object)(object)piece == (Object)null || status == null) { return; } string text = NormalizeKey(((Object)piece).name); if (string.IsNullOrWhiteSpace(text)) { return; } foreach (ProtectedStructureGroup group in groups) { if (group != null && !matchedGroupKeys.Contains(group.Key) && group.Matches(text)) { matchedGroupKeys.Add(group.Key); status.MatchedLabels.Add(group.Label); } } } private static string BuildProtectedAreaActiveHud(ProtectedAreaStatus status) { if (status == null) { return "0"; } if (ProtectedAreaShowCriteriaBreakdown == null || ProtectedAreaShowCriteriaBreakdown.Value) { return BuildProtectedAreaBreakdownHud(status, active: true); } if (ProtectedAreaCompactHud == null || ProtectedAreaCompactHud.Value) { return "Schutzgrad " + GetProtectedAreaSourceCount(status); } bool flag = status.MatchedCount >= status.RequiredCount; bool flag2 = status.HasGuardian && (ProtectedAreaGuardianAlwaysProtects == null || ProtectedAreaGuardianAlwaysProtects.Value); if (flag && flag2) { return "Schutzgrad " + GetProtectedAreaSourceCount(status) + " (Base-Set + Wächter)"; } if (flag) { return "Schutzgrad " + GetProtectedAreaSourceCount(status) + " (Base-Set)"; } if (flag2) { return "Schutzgrad " + GetProtectedAreaSourceCount(status) + " (Wächter)"; } return "Schutzgrad 0 (" + status.MatchedCount + "/" + status.RequiredCount + ")"; } private static string BuildProtectedAreaMissingHud(ProtectedAreaStatus status) { if (status == null) { return "Schutzbereich: 0/0"; } if (ProtectedAreaShowCriteriaBreakdown == null || ProtectedAreaShowCriteriaBreakdown.Value) { return "Schutzbereich: " + BuildProtectedAreaBreakdownHud(status, active: false); } string text = ((status.MissingLabels.Count > 0) ? string.Join(", ", status.MissingLabels.Select((string label) => "" + label + "").ToArray()) : "Kriterium"); return "Schutzbereich: " + status.MatchedCount + "/" + status.RequiredCount + " fehlt: " + text; } private static string BuildProtectedAreaBreakdownHud(ProtectedAreaStatus status, bool active) { if (status == null) { return "0"; } List list = new List(); bool num = status.MatchedCount >= status.RequiredCount; bool flag = status.HasGuardian && (ProtectedAreaGuardianAlwaysProtects == null || ProtectedAreaGuardianAlwaysProtects.Value); if (active) { list.Add("Schutzgrad " + GetProtectedAreaSourceCount(status)); } else { list.Add(status.MatchedCount + "/" + status.RequiredCount); } if (num) { list.Add("OK Base-Set (" + status.MatchedCount + "/" + status.RequiredCount + ")"); } else if (status.MatchedCount > 0) { list.Add("• Base-Set (" + status.MatchedCount + "/" + status.RequiredCount + ")"); } else { list.Add("X Base-Set (0/" + status.RequiredCount + ")"); } foreach (string label in status.AllCriteria.Take(8)) { if (status.MatchedLabels.Any((string m) => string.Equals(m, label, StringComparison.OrdinalIgnoreCase))) { list.Add(" OK " + label + ""); } else { list.Add(" X " + label + ""); } } if (flag) { list.Add("OK Wächterstein"); } else if (status.HasGuardian) { list.Add("• Wächterstein erkannt, aber Schutz per Config aus"); } return string.Join("\n", list.ToArray()); } private static int GetProtectedAreaSourceCount(ProtectedAreaStatus status) { if (status == null) { return 0; } bool num = status.HasGuardian && (ProtectedAreaGuardianAlwaysProtects == null || ProtectedAreaGuardianAlwaysProtects.Value); bool num2 = ProtectedAreaCountBaseSetAsOne == null || ProtectedAreaCountBaseSetAsOne.Value; int num3 = 0; if (num2) { if (status.MatchedCount >= status.RequiredCount) { num3++; } } else { num3 += status.MatchedCount; } if (num) { num3++; } return num3; } private static List ParseProtectedStructureGroups() { object obj = ((ProtectedAreaRequiredStructureGroups != null) ? ProtectedAreaRequiredStructureGroups.Value : ""); List list = new List(); if (obj == null) { obj = ""; } string[] array = ((string)obj).Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0) { continue; } string text2 = text; string text3 = text; int num = text.IndexOf(':'); if (num >= 0) { text2 = text.Substring(0, num).Trim(); text3 = text.Substring(num + 1).Trim(); } ProtectedStructureGroup protectedStructureGroup = new ProtectedStructureGroup(); protectedStructureGroup.Label = (string.IsNullOrWhiteSpace(text2) ? "Schutzobjekt" : text2); protectedStructureGroup.Key = NormalizeKey(protectedStructureGroup.Label); string[] array2 = text3.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (int j = 0; j < array2.Length; j++) { string text4 = NormalizeKey(array2[j]); if (!string.IsNullOrWhiteSpace(text4)) { protectedStructureGroup.Prefabs.Add(text4); } } if (protectedStructureGroup.Prefabs.Count > 0) { list.Add(protectedStructureGroup); } } if (list.Count == 0) { foreach (string item in SplitCsv((Plugin.ProtectedPrefabs != null) ? Plugin.ProtectedPrefabs.Value : "piece_workbench,piece_bed,fire_pit,hearth,piece_cauldron,piece_cookingstation")) { ProtectedStructureGroup protectedStructureGroup2 = new ProtectedStructureGroup(); protectedStructureGroup2.Label = DisplayProtectedReason(item); protectedStructureGroup2.Key = NormalizeKey(protectedStructureGroup2.Label); protectedStructureGroup2.Prefabs.Add(item); list.Add(protectedStructureGroup2); } } return list; } private static IEnumerator MajoritySleepLoop() { while (true) { yield return (object)new WaitForSeconds(Mathf.Max(1f, (SleepCheckSeconds != null) ? SleepCheckSeconds.Value : 2f)); try { if (EnableMajoritySleepSkip == null || !EnableMajoritySleepSkip.Value || !IsServer()) { continue; } if (!IsNight()) { if (_nightWasActive) { ResetSleepVoteState(keepSleptThisNight: false); _nightWasActive = false; } } else { _nightWasActive = true; UpdateSleepVotesAndMaybeSkip(); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("SleepVote-Pruefung fehlgeschlagen: " + ex.Message)); } } } } private static IEnumerator LocalSleepProgressLoop() { while (true) { yield return (object)new WaitForSeconds(1f); try { if (EnableMajoritySleepSkip == null || !EnableMajoritySleepSkip.Value) { continue; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { continue; } if (!IsNight()) { _localSleepSecondsThisNight = 0f; _localSleepReadySent = false; } else if (IsSleeping(localPlayer)) { _localSleepSecondsThisNight += 1f; float num = Mathf.Max(5f, (SleepRequiredSeconds != null) ? SleepRequiredSeconds.Value : 60f); if (Time.realtimeSinceStartup >= _nextLocalSleepProgressMessageAt) { _nextLocalSleepProgressMessageAt = Time.realtimeSinceStartup + 5f; int num2 = Mathf.Min(Mathf.CeilToInt(_localSleepSecondsThisNight), Mathf.CeilToInt(num)); ((Character)localPlayer).Message((MessageType)1, "Schlaf-Abstimmung: " + num2 + "/" + Mathf.CeilToInt(num) + "s im Bett", 0, (Sprite)null); } if (!_localSleepReadySent && _localSleepSecondsThisNight >= num) { _localSleepReadySent = true; ((Character)localPlayer).Message((MessageType)2, "Schlaf-Abstimmung bereit: Deine Stimme zählt als JA.", 0, (Sprite)null); SendLocalSleepReady(localPlayer); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Lokale Schlaf-Abstimmung konnte nicht geprueft werden: " + ex.Message)); } } } } private static void SendLocalSleepReady(Player local) { if ((Object)(object)local == (Object)null) { return; } string text = JoinPayload(SafePlayerId(local).ToString(), SafePlayerName(local)); if (IsServer()) { HandleSleepVoteReadyPayload(text); return; } try { long num = ServerPeerId(); if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(num, "ChallengeHubSleepVoteReady", new object[1] { text }); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Schlaf-Bereitschaft konnte nicht an Server gesendet werden: " + ex.Message)); } } } private static IEnumerator SleepVoteInputLoop() { while (true) { yield return null; try { if (!_localSleepVotePopupActive) { continue; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { continue; } if (!IsNight()) { _localSleepVotePopupActive = false; continue; } if (Time.realtimeSinceStartup >= _nextLocalSleepVoteMessageAt) { _nextLocalSleepVoteMessageAt = Time.realtimeSinceStartup + 4f; int num = Mathf.Max(0, Mathf.CeilToInt(_localSleepVoteDeadlineAt - Time.realtimeSinceStartup)); ((Character)localPlayer).Message((MessageType)2, _localSleepVoteStarterName + " moechte die Nacht ueberspringen. F7 = JA, F8 = NEIN (" + num + "s)", 0, (Sprite)null); ((Character)localPlayer).Message((MessageType)1, "Schlaf-Abstimmung: F7 Ja / F8 Nein", 0, (Sprite)null); } if (Input.GetKeyDown((KeyCode)288)) { SubmitLocalSleepVoteResponse(yes: true); } else if (Input.GetKeyDown((KeyCode)289)) { SubmitLocalSleepVoteResponse(yes: false); } else if (Time.realtimeSinceStartup > _localSleepVoteDeadlineAt + 2f) { _localSleepVotePopupActive = false; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("SleepVote-Eingabe fehlgeschlagen: " + ex.Message)); } } } } private static IEnumerator RegisterSleepVoteRpcWhenReady() { while (true) { yield return (object)new WaitForSeconds(1f); ZRoutedRpc instance = ZRoutedRpc.instance; if (instance == null || instance == _sleepVoteRpcInstance) { continue; } try { instance.Register("ChallengeHubSleepVoteRequest", (Action)RPC_SleepVoteRequest); instance.Register("ChallengeHubSleepVoteResponse", (Action)RPC_SleepVoteResponse); instance.Register("ChallengeHubSleepVoteResult", (Action)RPC_SleepVoteResult); instance.Register("ChallengeHubSleepVoteReady", (Action)RPC_SleepVoteReady); _sleepVoteRpcInstance = instance; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"SleepVote RPC fuer aktuelle Netzwerksitzung registriert."); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("SleepVote RPC konnte nicht registriert werden: " + ex.Message)); } } } } private static void UpdateSleepVotesAndMaybeSkip() { List list = Player.GetAllPlayers(); if (list == null) { list = new List(); } int val = ConnectedSleepVotePeerCount(); if (list.Count == 0 && SleptThisNight.Count == 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"SleepVote wartet: Dedicated Server hat weder Player-Objekte noch Bettbereitschaften."); } return; } float num = Mathf.Max(1f, (SleepCheckSeconds != null) ? SleepCheckSeconds.Value : 2f); UpdatePlayerActivity(list); foreach (Player item in list) { if (!((Object)(object)item == (Object)null)) { long num2 = SafePlayerId(item); if (num2 != 0L && IsSleeping(item)) { float value; float num3 = (SleptThisNight.TryGetValue(num2, out value) ? value : 0f); SleptThisNight[num2] = num3 + num; LastPlayerActivityAt[num2] = Time.realtimeSinceStartup; } } } List list2 = ActiveSleepVotePlayers(list).ToList(); int num4 = Math.Max(list.Count((Player p) => (Object)(object)p != (Object)null), val); int num5 = Math.Max(1, (list2.Count > 0) ? list2.Count : num4); int num6 = RequiredSleepVoteYes(num5); int num7 = SleptThisNight.Count((KeyValuePair pair) => pair.Value >= Mathf.Max(5f, SleepRequiredSeconds.Value)); if (Time.realtimeSinceStartup >= _nextSleepReportAt) { _nextSleepReportAt = Time.realtimeSinceStartup + 20f; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("SleepVote: " + num7 + "/" + num4 + " bereit, " + num5 + " aktiv, benötigt " + num6 + " Ja.")); } } if (_sleepVoteActive) { if (Time.realtimeSinceStartup >= _nextSleepVoteBroadcastAt) { int seconds = Mathf.Max(1, Mathf.CeilToInt(_sleepVoteDeadlineAt - Time.realtimeSinceStartup)); BroadcastSleepVoteRequest(_sleepVoteId, _sleepVoteStarterName, _sleepVoteNeededPlayers, _sleepVoteActivePlayers, seconds); } EvaluateActiveSleepVote(list, list2, num6, justStarted: false); } else { if (Time.realtimeSinceStartup < _sleepVoteCooldownUntil) { return; } KeyValuePair keyValuePair = SleptThisNight.FirstOrDefault((KeyValuePair pair) => pair.Value >= Mathf.Max(5f, SleepRequiredSeconds.Value)); if (keyValuePair.Key != 0L) { Player val2 = FindPlayerById(keyValuePair.Key.ToString()); string starterName = (((Object)(object)val2 != (Object)null) ? SafePlayerName(val2) : "Spieler"); if (EnableSleepVotePopup != null && EnableSleepVotePopup.Value) { StartSleepVote(keyValuePair.Key, starterName, val2, list2, num6, num4, num7); } else if (num7 >= num6) { TryCompleteSleepVote(val2, num4, num5, num7, num6, "legacy_majority_sleep"); } } } } private static void StartSleepVote(long starterId, string starterName, Player starter, List activePlayers, int needed, int online, int ready) { if (starterId == 0L) { return; } if (IsSleepVoteBlocked(out var reason)) { if ((Object)(object)starter != (Object)null) { ((Character)starter).Message((MessageType)2, "Nacht kann gerade nicht uebersprungen werden: " + reason, 0, (Sprite)null); } SendSleepVoteEvent("sleep_vote_blocked", starter, new Dictionary { { "reason", reason }, { "onlinePlayers", online }, { "activePlayers", activePlayers?.Count ?? 0 }, { "readyPlayers", ready } }); _sleepVoteCooldownUntil = Time.realtimeSinceStartup + Mathf.Max(10f, (SleepVoteCooldownSeconds != null) ? SleepVoteCooldownSeconds.Value : 120f); return; } _sleepVoteActive = true; _sleepVoteId = DateTime.UtcNow.ToString("yyyyMMddHHmmss") + "-" + starterId; _sleepVoteStarterId = starterId; _sleepVoteStarterName = (string.IsNullOrWhiteSpace(starterName) ? "Spieler" : starterName); _sleepVoteStartedAt = Time.realtimeSinceStartup; _nextSleepVoteBroadcastAt = 0f; _sleepVoteDeadlineAt = Time.realtimeSinceStartup + Mathf.Max(10f, (SleepVoteResponseSeconds != null) ? SleepVoteResponseSeconds.Value : 30f); _sleepVoteActivePlayers = Math.Max(1, activePlayers?.Count ?? 1); _sleepVoteNeededPlayers = Math.Max(1, needed); SleepVoteResponses.Clear(); SleepVoteBedYesPlayers.Clear(); foreach (Player item in activePlayers ?? new List()) { long num = SafePlayerId(item); if (num != 0L && SleptThisNight.TryGetValue(num, out var value) && value >= Mathf.Max(5f, SleepRequiredSeconds.Value)) { SleepVoteResponses[num] = true; SleepVoteBedYesPlayers.Add(num); } } SleepVoteResponses[_sleepVoteStarterId] = true; SleepVoteBedYesPlayers.Add(_sleepVoteStarterId); BroadcastSleepVoteRequest(_sleepVoteId, _sleepVoteStarterName, _sleepVoteNeededPlayers, _sleepVoteActivePlayers, Mathf.CeilToInt(_sleepVoteDeadlineAt - Time.realtimeSinceStartup)); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("SleepVote gestartet durch " + _sleepVoteStarterName + ": benötigt " + _sleepVoteNeededPlayers + " von " + _sleepVoteActivePlayers + ".")); } SendSleepVoteEvent("sleep_vote_started", starter, new Dictionary { { "voteId", _sleepVoteId }, { "starterPlayerId", _sleepVoteStarterId.ToString() }, { "starterPlayerName", _sleepVoteStarterName }, { "onlinePlayers", online }, { "activePlayers", _sleepVoteActivePlayers }, { "requiredYes", _sleepVoteNeededPlayers }, { "readyPlayers", ready }, { "responseSeconds", Mathf.Max(10f, (SleepVoteResponseSeconds != null) ? SleepVoteResponseSeconds.Value : 30f) } }); EvaluateActiveSleepVote(Player.GetAllPlayers(), activePlayers, _sleepVoteNeededPlayers, justStarted: true); } private static void EvaluateActiveSleepVote(List players, List activePlayers, int needed, bool justStarted) { if (!_sleepVoteActive) { return; } int num = SleepVoteResponses.Count((KeyValuePair pair) => pair.Value); int num2 = SleepVoteResponses.Count((KeyValuePair pair) => !pair.Value); int num3 = Math.Max(1, activePlayers?.Count ?? _sleepVoteActivePlayers); needed = Math.Max(1, needed); bool flag = SleepNoVoteIsVeto != null && SleepNoVoteIsVeto.Value && num2 > 0; bool flag2 = Time.realtimeSinceStartup >= _sleepVoteDeadlineAt; if (!flag && num >= needed) { TryCompleteSleepVote(FindPlayerById(_sleepVoteStarterId.ToString()) ?? players?.FirstOrDefault((Func)((Player p) => (Object)(object)p != (Object)null)), players?.Count((Player p) => (Object)(object)p != (Object)null) ?? num3, num3, num, needed, "popup_majority_yes"); } else if (flag || flag2) { Player player = FindPlayerById(_sleepVoteStarterId.ToString()) ?? players?.FirstOrDefault((Func)((Player p) => (Object)(object)p != (Object)null)); string text = (flag ? "no_vote_veto" : "vote_timeout_no_majority"); BroadcastSleepVoteResult(_sleepVoteId, accepted: false, num, num2, needed, text); SendSleepVoteEvent("sleep_vote_rejected", player, new Dictionary { { "voteId", _sleepVoteId }, { "yesVotes", num }, { "noVotes", num2 }, { "activePlayers", num3 }, { "requiredYes", needed }, { "reason", text } }); _sleepVoteCooldownUntil = Time.realtimeSinceStartup + Mathf.Max(10f, (SleepVoteCooldownSeconds != null) ? SleepVoteCooldownSeconds.Value : 120f); ResetSleepVoteState(keepSleptThisNight: true); } else if (!justStarted && Time.realtimeSinceStartup >= _nextSleepReportAt) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("SleepVote laeuft: Ja " + num + ", Nein " + num2 + ", benötigt " + needed + ".")); } } } private static void TryCompleteSleepVote(Player reportingPlayer, int online, int active, int yes, int needed, string method) { if (IsSleepVoteBlocked(out var reason)) { BroadcastSleepVoteResult(_sleepVoteId, accepted: false, yes, 0, needed, reason); SendSleepVoteEvent("sleep_vote_blocked", reportingPlayer, new Dictionary { { "voteId", _sleepVoteId }, { "reason", reason }, { "onlinePlayers", online }, { "activePlayers", active }, { "yesVotes", yes }, { "requiredYes", needed } }); _sleepVoteCooldownUntil = Time.realtimeSinceStartup + Mathf.Max(10f, (SleepVoteCooldownSeconds != null) ? SleepVoteCooldownSeconds.Value : 120f); ResetSleepVoteState(keepSleptThisNight: true); } else if (TrySkipToMorning()) { BroadcastSleepVoteResult(_sleepVoteId, accepted: true, yes, SleepVoteResponses.Count((KeyValuePair pair) => !pair.Value), needed, method); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"SleepVote: Mehrheit hat zugestimmt, Nacht wird uebersprungen."); } SendSleepVoteEvent("sleep_vote_completed", reportingPlayer, new Dictionary { { "voteId", _sleepVoteId }, { "onlinePlayers", online }, { "activePlayers", active }, { "yesVotes", yes }, { "requiredYes", needed }, { "requiredSeconds", SleepRequiredSeconds.Value }, { "method", method }, { "bedYesPlayerIds", string.Join(",", from id in SleepVoteBedYesPlayers orderby id select id.ToString()) }, { "bedYesBonusPerPlayer", 5 } }); ResetSleepVoteState(keepSleptThisNight: false); } } private static void ResetSleepVoteState(bool keepSleptThisNight) { _sleepVoteActive = false; _sleepVoteId = string.Empty; _sleepVoteStarterName = string.Empty; _sleepVoteStarterId = 0L; _sleepVoteDeadlineAt = 0f; _sleepVoteStartedAt = 0f; _nextSleepVoteBroadcastAt = 0f; _sleepVoteNeededPlayers = 0; _sleepVoteActivePlayers = 0; SleepVoteResponses.Clear(); SleepVoteBedYesPlayers.Clear(); _localSleepVotePopupActive = false; _localSleepVoteId = string.Empty; _localSleepSecondsThisNight = 0f; _localSleepReadySent = false; if (!keepSleptThisNight) { SleptThisNight.Clear(); } } private static IEnumerable ActiveSleepVotePlayers(List players) { if (players == null) { yield break; } int num = Math.Max(1, (MinimumActiveSleepVoters == null) ? 1 : MinimumActiveSleepVoters.Value); List list = new List(); foreach (Player player in players) { if (!((Object)(object)player == (Object)null) && (!ExcludeAfkPlayersFromSleepVote.Value || IsPlayerActiveForSleepVote(player))) { list.Add(player); } } if (list.Count < num) { foreach (Player player2 in players) { if ((Object)(object)player2 != (Object)null && !list.Contains(player2)) { list.Add(player2); } if (list.Count >= num) { break; } } } foreach (Player item in list) { yield return item; } } private static int RequiredSleepVoteYes(int activePlayers) { if (RequireStrictSleepMajority != null && RequireStrictSleepMajority.Value) { return Math.Max(1, activePlayers / 2 + 1); } float num = Mathf.Clamp01((SleepMajorityPercent != null) ? SleepMajorityPercent.Value : 0.5f); return Math.Max(1, Mathf.CeilToInt((float)activePlayers * num)); } private static int ConnectedSleepVotePeerCount() { try { if ((Object)(object)ZNet.instance == (Object)null) { return 0; } return (ZNet.instance.GetPeers() ?? new List()).Count((ZNetPeer peer) => peer != null && peer.m_uid != 0); } catch { return 0; } } private static void UpdatePlayerActivity(List players) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) foreach (Player player in players) { if ((Object)(object)player == (Object)null) { continue; } long num = SafePlayerId(player); if (num != 0L) { Vector3 position = ((Component)player).transform.position; if (!LastPlayerPositions.TryGetValue(num, out var value)) { LastPlayerPositions[num] = position; LastPlayerActivityAt[num] = Time.realtimeSinceStartup; } else if (Vector3.Distance(value, position) > 0.35f || IsSleeping(player)) { LastPlayerPositions[num] = position; LastPlayerActivityAt[num] = Time.realtimeSinceStartup; } } } } private static bool IsPlayerActiveForSleepVote(Player player) { if ((Object)(object)player == (Object)null) { return false; } long num = SafePlayerId(player); if (num == 0L) { return false; } if (IsSleeping(player)) { return true; } float value; float num2 = (LastPlayerActivityAt.TryGetValue(num, out value) ? value : Time.realtimeSinceStartup); return Time.realtimeSinceStartup - num2 <= Mathf.Max(10f, (SleepVoteAfkAfterSeconds != null) ? SleepVoteAfkAfterSeconds.Value : 180f); } private static bool IsSleepVoteBlocked(out string reason) { reason = string.Empty; if (BlockSleepVoteDuringRaid != null && BlockSleepVoteDuringRaid.Value && IsRaidActive()) { reason = "Raid aktiv"; return true; } if (BlockSleepVoteDuringBossFight != null && BlockSleepVoteDuringBossFight.Value && IsBossNearby()) { reason = "Bosskampf aktiv"; return true; } if (BlockSleepVoteIfEnemiesNearby != null && BlockSleepVoteIfEnemiesNearby.Value && HasAlertedEnemyNearPlayer()) { reason = "Gegner in der Naehe"; return true; } return false; } private static bool IsRaidActive() { try { Type type = AccessTools.TypeByName("RandEventSystem"); if (type == null) { return false; } FieldInfo fieldInfo = AccessTools.Field(type, "instance"); object obj = ((fieldInfo != null) ? fieldInfo.GetValue(null) : null); if (obj == null) { return false; } string[] array = new string[2] { "HaveActiveEvent", "IsEventActive" }; foreach (string text in array) { MethodInfo methodInfo = AccessTools.Method(type, text, (Type[])null, (Type[])null); if (methodInfo != null && methodInfo.GetParameters().Length == 0) { return Convert.ToBoolean(methodInfo.Invoke(obj, null)); } } FieldInfo fieldInfo2 = AccessTools.Field(type, "m_randomEvent"); return fieldInfo2 != null && fieldInfo2.GetValue(obj) != null; } catch { return false; } } private static bool IsBossNearby() { try { string[] source = new string[7] { "eikthyr", "gd_king", "bonemass", "dragon", "goblinking", "queen", "fader" }; Character[] array = Object.FindObjectsByType((FindObjectsSortMode)0); List allPlayers = Player.GetAllPlayers(); if (array == null || allPlayers == null) { return false; } Character[] array2 = array; foreach (Character character in array2) { if (!((Object)(object)character == (Object)null) && !IsCharacterDead(character)) { string name = NormalizeKey(((Object)character).name); if (source.Any((string boss) => name.Contains(boss)) && allPlayers.Any((Player player) => (Object)(object)player != (Object)null && Vector3.Distance(((Component)player).transform.position, ((Component)character).transform.position) <= 180f)) { return true; } } } } catch { } return false; } private static bool HasAlertedEnemyNearPlayer() { try { Character[] array = Object.FindObjectsByType((FindObjectsSortMode)0); List allPlayers = Player.GetAllPlayers(); if (array == null || allPlayers == null) { return false; } float radius = Mathf.Max(5f, (SleepVoteEnemyBlockRadius != null) ? SleepVoteEnemyBlockRadius.Value : 30f); Character[] array2 = array; foreach (Character character in array2) { if (!((Object)(object)character == (Object)null) && !IsCharacterDead(character) && !IsCharacterPlayer(character) && !IsCharacterTamed(character) && IsEnemyAlerted(character) && allPlayers.Any((Player player) => (Object)(object)player != (Object)null && Vector3.Distance(((Component)player).transform.position, ((Component)character).transform.position) <= radius)) { return true; } } } catch { } return false; } private static bool IsCharacterDead(Character character) { try { MethodInfo method = ((object)character).GetType().GetMethod("IsDead", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); return method != null && Convert.ToBoolean(method.Invoke(character, null)); } catch { return false; } } private static bool IsCharacterPlayer(Character character) { if ((Object)(object)character != (Object)null) { return (Object)(object)((Component)character).GetComponent() != (Object)null; } return false; } private static bool IsCharacterTamed(Character character) { try { Tameable component = ((Component)character).GetComponent(); if ((Object)(object)component == (Object)null) { return false; } MethodInfo method = ((object)component).GetType().GetMethod("IsTamed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); return method != null && Convert.ToBoolean(method.Invoke(component, null)); } catch { return false; } } private static bool IsEnemyAlerted(Character character) { try { BaseAI component = ((Component)character).GetComponent(); if ((Object)(object)component == (Object)null) { return false; } MethodInfo method = ((object)component).GetType().GetMethod("IsAlerted", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { return Convert.ToBoolean(method.Invoke(component, null)); } } catch { } return false; } private static void BroadcastSleepVoteRequest(string voteId, string starterName, int needed, int activePlayers, int seconds) { try { string text = JoinPayload(voteId, starterName, needed.ToString(), activePlayers.ToString(), seconds.ToString()); if (ZRoutedRpc.instance == null) { return; } int num = 0; if (IsServer() && (Object)(object)ZNet.instance != (Object)null) { foreach (ZNetPeer item in ZNet.instance.GetPeers() ?? new List()) { if (item != null && item.m_uid != 0L) { ZRoutedRpc.instance.InvokeRoutedRPC(item.m_uid, "ChallengeHubSleepVoteRequest", new object[1] { text }); num++; } } } else { ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "ChallengeHubSleepVoteRequest", new object[1] { text }); num = 1; } _nextSleepVoteBroadcastAt = Time.realtimeSinceStartup + 3f; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("SleepVote Einladung gesendet: " + num + " Netzwerkempfaenger, noch " + seconds + "s.")); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("SleepVote Request konnte nicht gesendet werden: " + ex.Message)); } _nextSleepVoteBroadcastAt = Time.realtimeSinceStartup + 1f; } } private static void BroadcastSleepVoteResult(string voteId, bool accepted, int yes, int no, int needed, string reason) { try { string text = JoinPayload(voteId, accepted ? "1" : "0", yes.ToString(), no.ToString(), needed.ToString(), reason ?? string.Empty); if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "ChallengeHubSleepVoteResult", new object[1] { text }); } } catch { } } private static void SubmitLocalSleepVoteResponse(bool yes) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || string.IsNullOrWhiteSpace(_localSleepVoteId)) { return; } string text = JoinPayload(_localSleepVoteId, SafePlayerId(localPlayer).ToString(), SafePlayerName(localPlayer), yes ? "1" : "0"); if (IsServer()) { HandleSleepVoteResponsePayload(text); } else { try { long num = ServerPeerId(); if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(num, "ChallengeHubSleepVoteResponse", new object[1] { text }); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("SleepVote Antwort konnte nicht an Server gesendet werden: " + ex.Message)); } } } ((Character)localPlayer).Message((MessageType)2, yes ? "Schlaf-Abstimmung: JA gesendet." : "Schlaf-Abstimmung: NEIN gesendet.", 0, (Sprite)null); _localSleepVotePopupActive = false; } private static void RPC_SleepVoteReady(long sender, string payload) { if (IsServer()) { HandleSleepVoteReadyPayload(payload); } } private static void HandleSleepVoteReadyPayload(string payload) { try { string[] array = SplitPayload(payload); if (array.Length < 2 || !long.TryParse(array[0], out var result) || result == 0L) { return; } string text = array[1]; float num = Mathf.Max(5f, (SleepRequiredSeconds != null) ? SleepRequiredSeconds.Value : 60f); SleptThisNight[result] = num; LastPlayerActivityAt[result] = Time.realtimeSinceStartup; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("SleepVote: " + text + " hat " + Mathf.CeilToInt(num) + "s Bettzeit erreicht.")); } if (_sleepVoteActive) { SleepVoteResponses[result] = true; SleepVoteBedYesPlayers.Add(result); Player player = FindPlayerById(result.ToString()) ?? Player.m_localPlayer; SendSleepVoteEvent("sleep_vote_response", player, new Dictionary { { "voteId", _sleepVoteId }, { "votePlayerId", result.ToString() }, { "votePlayerName", text }, { "response", "yes" }, { "autoReady", true }, { "yesVotes", SleepVoteResponses.Count((KeyValuePair pair) => pair.Value) }, { "noVotes", SleepVoteResponses.Count((KeyValuePair pair) => !pair.Value) }, { "requiredYes", _sleepVoteNeededPlayers }, { "activePlayers", _sleepVoteActivePlayers } }); } UpdateSleepVotesAndMaybeSkip(); } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("SleepVote Bereitschaft konnte nicht verarbeitet werden: " + ex.Message)); } } } private static void RPC_SleepVoteRequest(long sender, string payload) { try { string[] array = SplitPayload(payload); if (array.Length >= 5) { _localSleepVoteId = array[0]; _localSleepVoteStarterName = array[1]; int num = ToInt(array[4], 30); _localSleepVoteDeadlineAt = Time.realtimeSinceStartup + (float)Mathf.Max(5, num); _localSleepVotePopupActive = true; _nextLocalSleepVoteMessageAt = 0f; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { ((Character)localPlayer).Message((MessageType)2, _localSleepVoteStarterName + " moechte die Nacht ueberspringen. F7 = JA, F8 = NEIN", 0, (Sprite)null); } } } catch { } } private static void RPC_SleepVoteResponse(long sender, string payload) { if (IsServer()) { HandleSleepVoteResponsePayload(payload); } } private static void HandleSleepVoteResponsePayload(string payload) { string[] array = SplitPayload(payload); if (array.Length < 4) { return; } string text = array[0]; if (_sleepVoteActive && !(text != _sleepVoteId) && long.TryParse(array[1], out var result) && result != 0L) { string text2 = array[2]; bool flag = array[3] == "1" || array[3].Equals("true", StringComparison.OrdinalIgnoreCase); SleepVoteResponses[result] = flag; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("SleepVote Antwort: " + text2 + " = " + (flag ? "JA" : "NEIN"))); } Player player = FindPlayerById(result.ToString()) ?? Player.m_localPlayer; SendSleepVoteEvent("sleep_vote_response", player, new Dictionary { { "voteId", _sleepVoteId }, { "votePlayerId", result.ToString() }, { "votePlayerName", text2 }, { "response", flag ? "yes" : "no" }, { "yesVotes", SleepVoteResponses.Count((KeyValuePair pair) => pair.Value) }, { "noVotes", SleepVoteResponses.Count((KeyValuePair pair) => !pair.Value) }, { "requiredYes", _sleepVoteNeededPlayers }, { "activePlayers", _sleepVoteActivePlayers } }); } } private static void RPC_SleepVoteResult(long sender, string payload) { try { string[] array = SplitPayload(payload); if (array.Length >= 6) { bool flag = array[1] == "1"; int num = ToInt(array[2], 0); int num2 = ToInt(array[3], 0); int num3 = ToInt(array[4], 0); string text = array[5]; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { ((Character)localPlayer).Message((MessageType)2, flag ? "Schlaf-Abstimmung angenommen: Nacht wird uebersprungen." : ("Schlaf-Abstimmung abgelehnt: " + text), 0, (Sprite)null); ((Character)localPlayer).Message((MessageType)1, "Schlaf-Abstimmung: Ja " + num + ", Nein " + num2 + ", benötigt " + num3, 0, (Sprite)null); } _localSleepVotePopupActive = false; _localSleepVoteId = string.Empty; } } catch { } } private static long ServerPeerId() { try { MethodInfo method = typeof(ZRoutedRpc).GetMethod("GetServerPeerID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { return Convert.ToInt64(method.Invoke(ZRoutedRpc.instance, null)); } } catch { } return 0L; } private static void SendSleepVoteEvent(string eventType, Player player, Dictionary payload) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_plugin == (Object)null || ReportSleepVoteEvents == null || !ReportSleepVoteEvents.Value) { return; } try { Player val = player ?? Player.m_localPlayer; if (payload == null) { payload = new Dictionary(); } if ((Object)(object)val != (Object)null) { payload["playerName"] = SafePlayerName(val); payload["playerId"] = SafePlayerId(val).ToString(); payload["biome"] = Plugin.CurrentBiome(((Component)val).transform.position); payload["position"] = Plugin.SerializeVector(((Component)val).transform.position); } _plugin.SendServerEvent(eventType, payload); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("SleepVote Event konnte nicht gesendet werden: " + ex.Message)); } } } private static string SafePlayerName(Player player) { try { return ((Object)(object)player != (Object)null) ? player.GetPlayerName() : string.Empty; } catch { return string.Empty; } } private static string JoinPayload(params string[] values) { return string.Join("\u001f", values.Select((string value) => (value ?? string.Empty).Replace("\u001f", " ")).ToArray()); } private static string[] SplitPayload(string payload) { return (payload ?? string.Empty).Split(new string[1] { "\u001f" }, StringSplitOptions.None); } private static int ToInt(string value, int fallback) { if (!int.TryParse(value, out var result)) { return fallback; } return result; } private static bool TrySkipToMorning() { object instance = EnvMan.instance; if (instance == null) { return false; } string[] array = new string[4] { "SkipToMorning", "SkipToDaytime", "SkipTime", "SetForceEnvironment" }; foreach (string name in array) { try { MethodInfo method = instance.GetType().GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(method == null)) { ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length == 0) { method.Invoke(instance, null); return true; } if (parameters.Length == 1 && parameters[0].ParameterType == typeof(float)) { method.Invoke(instance, new object[1] { 0.25f }); return true; } } } catch { } } ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"SleepVote konnte keine passende EnvMan-Methode zum Nachtueberspringen finden. Bitte BepInEx-Log pruefen."); } return false; } private static bool IsNight() { object instance = EnvMan.instance; if (instance == null) { return false; } try { MethodInfo method = instance.GetType().GetMethod("IsNight", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null && method.GetParameters().Length == 0) { return Convert.ToBoolean(method.Invoke(instance, null)); } } catch { } try { MethodInfo method2 = instance.GetType().GetMethod("GetDayFraction", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method2 != null) { float num = Convert.ToSingle(method2.Invoke(instance, null)); return num < 0.25f || num > 0.75f; } } catch { } return false; } private static bool IsSleeping(Player player) { if ((Object)(object)player == (Object)null) { return false; } try { MethodInfo method = ((object)player).GetType().GetMethod("InBed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null && method.GetParameters().Length == 0 && Convert.ToBoolean(method.Invoke(player, null))) { return true; } } catch { } try { MethodInfo method2 = ((object)player).GetType().GetMethod("IsSleeping", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method2 != null) { return Convert.ToBoolean(method2.Invoke(player, null)); } } catch { } try { FieldInfo field = ((object)player).GetType().GetField("m_sleeping", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return Convert.ToBoolean(field.GetValue(player)); } } catch { } return false; } private static bool IsServer() { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } private static bool IsCtrlPressed() { if (!Input.GetKey((KeyCode)306)) { return Input.GetKey((KeyCode)305); } return true; } private static long SafePlayerId(Player player) { try { return player.GetPlayerID(); } catch { return 0L; } } private static string NormalizePartnershipType(string value) { switch (NormalizeKey(value)) { case "combat": case "kampf": case "fighter": return "combat"; case "peace": case "frieden": return "peace"; case "boss": case "bossfight": case "boss_fight": return "bossfight"; case "farm": case "farmer": return "farm"; case "build": case "builder": return "build"; case "explore": case "explorer": return "explore"; default: return "trade"; } } private static string DisplayType(string type) { return NormalizePartnershipType(type) switch { "combat" => "Kampfpartnerschaft", "peace" => "Friedenspartnerschaft", "bossfight" => "Bossfight-Partnerschaft", "farm" => "Farmpartnerschaft", "build" => "Baupartnerschaft", "explore" => "Erkundungspartnerschaft", _ => "Handelspartnerschaft", }; } private static string DisplayStatus(string status) { return (status ?? string.Empty).ToLowerInvariant() switch { "active" => "aktiv", "cancelled" => "beendet", "destroyed" => "zerstört", "pending" => "wartet", _ => "frei", }; } private static string PartnershipBenefits(string type) { return NormalizePartnershipType(type) switch { "combat" => "Gemeinsamer Kampf; Partner werden von den Kampfregeln erkannt.", "peace" => "Gegenseitiger Frieden; Schutz vor versehentlichen Partner-Angriffen.", "bossfight" => "Gemeinsame Bosskaempfe und Zutritt zur beanspruchten Boss-Arena.", "farm" => "Gemeinsame Farm-/Versorgungszone und Nutzung der Bauern-Funktionen.", "build" => "Gemeinsames Bauen und Zugriff auf vereinbarte Bau-Funktionen.", "explore" => "Gemeinsame Erkundung und Karten-/Routen-Unterstuetzung.", _ => "Partnerhandel ueber die gebundene Vertragskiste.", }; } private static string DisplayProtectedReason(string prefab) { if (prefab.Contains("bed")) { return "Bett"; } if (prefab.Contains("fire") || prefab.Contains("hearth")) { return "Feuerstelle"; } if (prefab.Contains("workbench")) { return "Werkbank"; } return prefab; } private static HashSet SplitCsv(string text) { return new HashSet(from v in (text ?? string.Empty).Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(NormalizeKey) where !string.IsNullOrWhiteSpace(v) select v, StringComparer.OrdinalIgnoreCase); } private static string NormalizeKey(string value) { return (value ?? string.Empty).Trim().ToLowerInvariant().Replace("(clone)", "") .Replace(" ", "_"); } } internal static class PartnershipGameplayFeature { private sealed class TradeOffer { internal string Prefab = string.Empty; internal string ItemName = string.Empty; internal int Stack; internal int Quality; internal int Variant; internal float Durability; } private sealed class Ratio { public int Out; public int In; } private sealed class BossClaim { public int BossInstanceId; public string BossName; public Vector3 Center; public string OwnerId; public string OwnerName; public float StartedAt; public readonly Dictionary WarnedAt = new Dictionary(); } private static Plugin _plugin; private static bool _initialized; private const string TradePublishRpc = "ChallengeHub_RPC_TradeChestPublish_v2"; private const string TradeSnapshotRpc = "ChallengeHub_RPC_TradeChestSnapshot_v2"; private const string TradeSettlementRpc = "ChallengeHub_RPC_TradeChestSettlement_v2"; private static ZRoutedRpc _tradeRpcInstance; private static readonly Dictionary>> _serverTradeOffers = new Dictionary>>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> _serverSentMirrors = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary _serverTradeRevisions = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary _serverClientSequences = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary _serverSubscribers = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary _clientAppliedRevisions = new Dictionary(StringComparer.OrdinalIgnoreCase); private static long _clientPublishSequence; private static long _clientLastSentSequence; private static int _suppressTradeInventoryEvents; private static string _lastPublishedFingerprint = string.Empty; private static ConfigEntry EnableTradeChests; private static ConfigEntry TradeChestBindRange; private static ConfigEntry TradeChestScanSeconds; private static ConfigEntry TradeLevelRatios; private static ConfigEntry TradeDefaultLevel; private static ConfigEntry TradeMaxTransfersPerScan; private static ConfigEntry TradeRequireChestNearContract; private static ConfigEntry TradeReportPoints; private static ConfigEntry TradeBlockedItems; private static ConfigEntry EnablePeaceViolation; private static ConfigEntry BlockPeacePartnerDamage; private static ConfigEntry PeaceViolationPenaltyPoints; private static ConfigEntry PeaceViolationCooldownSeconds; private static ConfigEntry EnableBossfightArenaClaim; private static ConfigEntry BossArenaRadius; private static ConfigEntry BossTrespassWarningSeconds; private static ConfigEntry BossTrespassTeleportDistance; private static ConfigEntry AllowCombatPartnersInBossfight; private static ConfigEntry ReportBossfightArenaEvents; private static ConfigEntry EnablePortalBuildViolation; private static ConfigEntry PortalViolationPoints; private static ConfigEntry PortalViolationCooldownSeconds; private const string PStatusKey = "ChallengeHub.Partnership.Status"; private const string PTypeKey = "ChallengeHub.Partnership.Type"; private const string PUserAIdKey = "ChallengeHub.Partnership.UserAId"; private const string PUserANameKey = "ChallengeHub.Partnership.UserAName"; private const string PUserBIdKey = "ChallengeHub.Partnership.UserBId"; private const string PUserBNameKey = "ChallengeHub.Partnership.UserBName"; private const string PStructureIdKey = "ChallengeHub.Partnership.StructureId"; private const string TradeChestPartnershipIdKey = "ChallengeHub.Trade.PartnershipId"; private const string TradeChestStructureIdKey = "ChallengeHub.Trade.StructureId"; private const string TradeChestOwnerIdKey = "ChallengeHub.Trade.OwnerId"; private const string TradeChestOwnerNameKey = "ChallengeHub.Trade.OwnerName"; private const string TradeChestPartnerIdKey = "ChallengeHub.Trade.PartnerId"; private const string TradeChestPartnerNameKey = "ChallengeHub.Trade.PartnerName"; private const string TradeChestBoundUtcKey = "ChallengeHub.Trade.BoundUtc"; private const string TradeReceivedKey = "ChallengeHub.Trade.Received"; private const string TradeTransferIdKey = "ChallengeHub.Trade.TransferId"; private const string TradeMirrorKey = "ChallengeHub.Trade.Mirror"; private const string TradeMirrorSourceOwnerKey = "ChallengeHub.Trade.MirrorSourceOwner"; private const string TradeMirrorItemKey = "ChallengeHub.Trade.MirrorItem"; private static Piece _pendingTradeStructure; private static string _pendingTradePartnershipId = string.Empty; private static float _pendingTradeBindStartedAt; private static Container _recentlyOpenedChest; private static float _recentlyOpenedChestAt; private static float _nextTradeHudAt; private static float _nextBossScanAt; private static float _nextLocalKeyAt; private static readonly Dictionary _lastPeaceViolationAt = new Dictionary(); private static readonly Dictionary _lastPortalViolationAt = new Dictionary(); private static readonly Dictionary _bossClaims = new Dictionary(); private static readonly Dictionary _expectedTradeMirrors = new Dictionary(StringComparer.OrdinalIgnoreCase); internal static void Initialize(Plugin plugin) { if (!_initialized && !((Object)(object)plugin == (Object)null)) { _initialized = true; _plugin = plugin; EnableTradeChests = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Trade", "EnableTradeChests", true, "Aktiviert echte Handelspartner-Kisten mit serverseitigem Item-Transfer."); TradeChestBindRange = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Trade", "TradeChestBindRange", 8f, "Maximale Distanz zum Vertragsort bzw. zur Kiste beim Binden."); TradeChestScanSeconds = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Trade", "TradeChestScanSeconds", 1f, "Sekunden zwischen Live-Synchronisierungen gebundener Handelspartner-Kisten."); TradeLevelRatios = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Trade", "TradeLevelRatios", "1:4,2:4,3:4,4:4", "Tauschverhaeltnisse je Partnerschaftslevel. Format Level1,Level2... z.B. 1:4 bedeutet 4 rein, 1 kommt an."); TradeDefaultLevel = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Trade", "TradeDefaultLevel", 1, "Fallback-Level der Handelspartnerschaft, falls das Vertragsobjekt kein Level gespeichert hat."); TradeMaxTransfersPerScan = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Trade", "TradeMaxTransfersPerScan", 12, "Maximale Itemstapel-Transfers pro Scan, als Schutz gegen Kisten-Spam."); TradeRequireChestNearContract = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Trade", "RequireChestInsideContractRadius", true, "Wenn true, muss die gebundene Kiste innerhalb des Vertragsradius stehen."); TradeReportPoints = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Trade", "ReportTradePoints", true, "Wenn true, sendet die Mod trade_transfer_completed Events fuer Handelspunkte."); TradeBlockedItems = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Trade", "BlockedItems", "DragonEgg,YagluthDrop,QueenDrop,FaderDrop,BossStone", "Kommagetrennte Itemnamen, die nicht automatisch transferiert werden sollen."); EnablePeaceViolation = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Peace", "EnablePeaceViolationDetection", true, "Erkennt Schaden/PvP zwischen Friedenspartnern und sendet peace_violation."); BlockPeacePartnerDamage = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Peace", "BlockPeacePartnerDamage", true, "Wenn true, wird Schaden zwischen aktiven Friedenspartnern auf 0 reduziert."); PeaceViolationPenaltyPoints = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Peace", "PeaceViolationPenaltyPoints", -150, "Punkte fuer Friedensbruch."); PeaceViolationCooldownSeconds = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Peace", "PeaceViolationCooldownSeconds", 60f, "Cooldown je Spielerpaar fuer Friedensbruch-Events."); EnableBossfightArenaClaim = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Bossfight", "EnableBossfightArenaClaim", false, "Bosskaempfe bekommen einen Besitzer. Fremde Spieler ohne Bossfight-/Kampfpartnervertrag werden verwiesen."); BossArenaRadius = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Bossfight", "BossArenaRadius", 120f, "Radius der Bossfight-Arena um den Boss."); BossTrespassWarningSeconds = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Bossfight", "TrespassWarningSeconds", 10f, "Sekunden Warnzeit, bevor unberechtigte Spieler aus der Bossarena teleportiert werden."); BossTrespassTeleportDistance = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Bossfight", "TrespassTeleportDistance", 145f, "Distanz vom Bosszentrum, an die unberechtigte Spieler versetzt werden."); AllowCombatPartnersInBossfight = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Bossfight", "AllowCombatPartners", true, "Wenn true, gelten Kampfpartner auch als erlaubte Bossfight-Helfer."); ReportBossfightArenaEvents = ((BaseUnityPlugin)plugin).Config.Bind("Partnership.Bossfight", "ReportBossfightArenaEvents", true, "Wenn true, meldet die Mod bossfight_started/trespass_removed Events."); EnablePortalBuildViolation = ((BaseUnityPlugin)plugin).Config.Bind("Portals", "EnablePortalBuildViolationEvents", true, "Sendet beim Portalbau sofort portal_built und bei Ueberschreitung portal_violation."); PortalViolationPoints = ((BaseUnityPlugin)plugin).Config.Bind("Portals", "PortalViolationPoints", -100, "Punkte fuer Portal-Verstoss beim Bau."); PortalViolationCooldownSeconds = ((BaseUnityPlugin)plugin).Config.Bind("Portals", "PortalViolationCooldownSeconds", 30f, "Cooldown fuer Portal-Verstoss Events je Biom."); ((MonoBehaviour)plugin).StartCoroutine(RegisterTradeChestRpcs()); } } private static IEnumerator RegisterTradeChestRpcs() { while (ZRoutedRpc.instance == null) { yield return (object)new WaitForSeconds(1f); } if (_tradeRpcInstance == ZRoutedRpc.instance) { yield break; } _tradeRpcInstance = ZRoutedRpc.instance; try { _tradeRpcInstance.Register("ChallengeHub_RPC_TradeChestPublish_v2", (Action)RPC_TradePublish); _tradeRpcInstance.Register("ChallengeHub_RPC_TradeChestSnapshot_v2", (Action)RPC_TradeSnapshot); _tradeRpcInstance.Register("ChallengeHub_RPC_TradeChestSettlement_v2", (Action)RPC_TradeSettlement); if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { LoadTradeOffers(); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Handelskisten-RPCs 2.12.50 registriert (Oeffnen/Schliessen/Partnerzeile)."); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Handelskisten-RPC-Registrierung fehlgeschlagen: " + ex.Message)); } } } internal static void LocalUpdate(Player player) { if (!_initialized || (Object)(object)player == (Object)null || !((Character)player).IsOwner()) { return; } try { if (EnableTradeChests != null && EnableTradeChests.Value && IsCtrlPressed() && Input.GetKeyDown((KeyCode)107) && Time.realtimeSinceStartup >= _nextLocalKeyAt) { _nextLocalKeyAt = Time.realtimeSinceStartup + 0.25f; HandleTradeChestBindKey(player); } ShowTradeChestHud(player); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("PartnershipGameplay LocalUpdate fehlgeschlagen: " + ex.Message)); } } } internal static void ServerUpdate() { if (!_initialized || !IsServer() || EnableBossfightArenaClaim == null || !EnableBossfightArenaClaim.Value || !(Time.realtimeSinceStartup >= _nextBossScanAt)) { return; } _nextBossScanAt = Time.realtimeSinceStartup + 2f; try { ScanBossArenas(); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Bossfight-Arena-Scan fehlgeschlagen: " + ex.Message)); } } } internal static void OnBuildCompleted(Player player, object builtObject, string pieceName) { if (!_initialized || (Object)(object)player == (Object)null || (Object)(object)_plugin == (Object)null) { return; } try { if (EnablePortalBuildViolation != null && EnablePortalBuildViolation.Value) { ReportPortalBuildAndViolation(player, builtObject, pieceName); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Portal-Bau-Verstoss konnte nicht gemeldet werden: " + ex.Message)); } } } private static IEnumerator TradeChestLoop() { while (true) { float num = 1f; yield return (object)new WaitForSeconds(num); if (!_initialized || !IsServer() || EnableTradeChests == null || !EnableTradeChests.Value) { continue; } try { ProcessTradeChests(); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Handelspartner-Kisten-Scan fehlgeschlagen: " + ex.Message)); } } } } internal static void SynchronizeOpenedTradeChest(Container chest) { string text = ReadZdoString(GetZdo((Component)(object)chest), "ChallengeHub.Trade.PartnershipId"); if (!string.IsNullOrWhiteSpace(text)) { _clientAppliedRevisions.Remove(text); } _lastPublishedFingerprint = string.Empty; PublishTradeChest(chest, requestSnapshot: true); } internal static void PublishClosedTradeChest(Container chest) { PublishTradeChest(chest, requestSnapshot: false); } internal static void NotifyTradeInventoryChanged(Inventory changedInventory) { if (_suppressTradeInventoryEvents <= 0 && changedInventory != null && !((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer()) { Container val = TradeChestUiRules.CurrentContainer(InventoryGui.instance); if (!((Object)(object)val == (Object)null) && val.GetInventory() == changedInventory && TradeChestUiRules.IsBound(val) && !((Object)(object)Player.m_localPlayer == (Object)null) && TradeChestUiRules.IsOwner(val, Player.m_localPlayer) && !string.Equals(TradeFingerprint(changedInventory), _lastPublishedFingerprint, StringComparison.Ordinal)) { PublishTradeChest(val, requestSnapshot: false); } } } private static void PublishTradeChest(Container chest, bool requestSnapshot) { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown if ((Object)(object)chest == (Object)null || ZRoutedRpc.instance == null || (Object)(object)Player.m_localPlayer == (Object)null) { return; } ZDO zdo = GetZdo((Component)(object)chest); string text = ReadZdoString(zdo, "ChallengeHub.Trade.PartnershipId"); string text2 = ReadZdoString(zdo, "ChallengeHub.Trade.OwnerId"); string text3 = ReadZdoString(zdo, "ChallengeHub.Trade.PartnerId"); if (string.IsNullOrWhiteSpace(text) || text2 != SafePlayerId(Player.m_localPlayer).ToString()) { return; } Inventory inventory = chest.GetInventory(); if (inventory == null) { return; } TradeChestUiRules.EnsureLayout(chest); List list = (from item in inventory.GetAllItems() where item != null && item.m_gridPos.y < 2 && !IsTradeMirror(item) select item).ToList(); Dictionary dictionary = MirrorAmounts(inventory, text3); ZPackage val = new ZPackage(); val.Write(text); val.Write(text2); val.Write(text3); val.Write(StructureId((Component)(object)chest)); val.Write(requestSnapshot); val.Write(_clientLastSentSequence = ++_clientPublishSequence); val.Write(list.Count); foreach (ItemData item in list) { val.Write(((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : NormalizeItemName(item)); val.Write(NormalizeItemName(item)); val.Write(item.m_stack); val.Write(item.m_quality); val.Write(item.m_variant); val.Write(item.m_durability); } val.Write(dictionary.Count); foreach (KeyValuePair item2 in dictionary) { val.Write(item2.Key); val.Write(item2.Value); } long num = ServerPeerId(); ZRoutedRpc.instance.InvokeRoutedRPC(num, "ChallengeHub_RPC_TradeChestPublish_v2", new object[1] { val }); _lastPublishedFingerprint = TradeFingerprint(inventory); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Handelskiste synchronisiert: " + (requestSnapshot ? "Oeffnen" : "Schliessen") + "; Angebote=" + list.Count + "; Partnerschaft=" + text)); } } private static void RPC_TradePublish(long sender, ZPackage package) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || package == null) { return; } try { string text = package.ReadString(); string text2 = package.ReadString(); string text3 = package.ReadString(); string text4 = package.ReadString(); bool flag = package.ReadBool(); long val = package.ReadLong(); if (string.IsNullOrWhiteSpace(text) || string.IsNullOrWhiteSpace(text2) || string.IsNullOrWhiteSpace(text3) || text2 == text3) { return; } int num = Mathf.Clamp(package.ReadInt(), 0, 100); List list = new List(); for (int i = 0; i < num; i++) { list.Add(new TradeOffer { Prefab = package.ReadString(), ItemName = package.ReadString(), Stack = Math.Max(0, package.ReadInt()), Quality = Math.Max(1, package.ReadInt()), Variant = package.ReadInt(), Durability = package.ReadSingle() }); } int num2 = Mathf.Clamp(package.ReadInt(), 0, 100); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); for (int j = 0; j < num2; j++) { string key = package.ReadString(); dictionary[key] = Math.Max(0, package.ReadInt()); } if (!_serverTradeOffers.TryGetValue(text, out var value)) { value = (_serverTradeOffers[text] = new Dictionary>(StringComparer.OrdinalIgnoreCase)); } string key2 = text + "|" + text2; _serverSubscribers[key2] = sender; _serverClientSequences[key2] = Math.Max(val, _serverClientSequences.TryGetValue(key2, out var value2) ? value2 : 0); bool flag2 = false; List list2 = list.Where((TradeOffer x) => x.Stack > 0).ToList(); if (!flag || !value.ContainsKey(text2)) { if (!value.TryGetValue(text2, out var value3) || !SameOffers(value3, list2)) { flag2 = true; } value[text2] = list2; } string key3 = text + "|" + text2; if (_serverSentMirrors.TryGetValue(key3, out var value4) && value.TryGetValue(text3, out var value5)) { foreach (KeyValuePair item in value4) { int value6; int num3 = (dictionary.TryGetValue(item.Key, out value6) ? value6 : 0); int num4 = Math.Max(0, item.Value - num3); if (num4 > 0) { int num5 = ConsumeCachedOffer(value5, item.Key, num4); if (num5 > 0) { flag2 = true; SendTradeSettlement(sender, text, text3, text2, item.Key, num5); } } } _serverSentMirrors[key3] = new Dictionary(dictionary, StringComparer.OrdinalIgnoreCase); } if (flag2) { _serverTradeRevisions[text] = CurrentRevision(text) + 1; } SaveTradeOffers(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Handelskisten-RPC empfangen: Besitzer=" + text2 + "; Angebote=" + list.Count + "; Snapshot=" + flag + "; Kiste=" + text4)); } BroadcastTradeSnapshots(text, value); } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Handelskisten-Publish-RPC fehlgeschlagen: " + ex.Message)); } } } private static int ConsumeCachedOffer(List offers, string itemName, int quantity) { int num = quantity; foreach (TradeOffer item in offers.Where((TradeOffer x) => x.ItemName == itemName).ToList()) { int num2 = Math.Min(num, item.Stack); item.Stack -= num2; num -= num2; if (num <= 0) { break; } } offers.RemoveAll((TradeOffer x) => x.Stack <= 0); return quantity - num; } private static void SendTradeSettlement(long peer, string partnershipId, string providerId, string receiverId, string itemName, int quantity) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown if (ZRoutedRpc.instance != null && quantity > 0) { ZPackage val = new ZPackage(); val.Write(partnershipId); val.Write(providerId); val.Write(receiverId); val.Write(itemName); val.Write(quantity); ZRoutedRpc.instance.InvokeRoutedRPC(peer, "ChallengeHub_RPC_TradeChestSettlement_v2", new object[1] { val }); } } private static void RPC_TradeSettlement(long sender, ZPackage package) { if (package == null || (Object)(object)Player.m_localPlayer == (Object)null || ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())) { return; } try { string value = package.ReadString(); string value2 = package.ReadString(); string text = package.ReadString(); string text2 = package.ReadString(); int num = Math.Max(0, package.ReadInt()); if (num > 0 && !(SafePlayerId(Player.m_localPlayer).ToString() != text)) { _plugin.SendEvent("trade_transfer_completed", Player.m_localPlayer, new Dictionary { { "partnershipType", "trade" }, { "partnershipId", value }, { "tradeTransferId", DateTime.UtcNow.ToString("yyyyMMddHHmmssfff") + "-rpc-" + text2 }, { "item", text2 }, { "quantity", num }, { "sourceQuantity", num }, { "points", num }, { "creditedPlayerId", value2 }, { "sourcePlayerId", value2 }, { "partnerPlayerId", text }, { "tradeItemValue", 1 }, { "rarityMultiplier", 1f }, { "attributionMethod", "trade_chest_partner_row_take_2_9_17" } }); ((Character)Player.m_localPlayer).Message((MessageType)2, "Handel abgeschlossen: " + text2 + " x" + num + "; " + num + " Punkt(e) fuer den Anbieter.", 0, (Sprite)null); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Handelskisten-Abrechnung fehlgeschlagen: " + ex.Message)); } } } private static string TradeOfferFile() { return Path.Combine(Paths.ConfigPath, "ChallengeHubValheim", "trade-chest-offers-v2.tsv"); } private static string EncodeField(string value) { return Convert.ToBase64String(Encoding.UTF8.GetBytes(value ?? string.Empty)); } private static string DecodeField(string value) { try { return Encoding.UTF8.GetString(Convert.FromBase64String(value ?? string.Empty)); } catch { return string.Empty; } } private static void SaveTradeOffers() { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } try { string text = TradeOfferFile(); Directory.CreateDirectory(Path.GetDirectoryName(text)); List list = new List(); foreach (KeyValuePair>> serverTradeOffer in _serverTradeOffers) { foreach (KeyValuePair> item in serverTradeOffer.Value) { foreach (TradeOffer item2 in item.Value.Where((TradeOffer x) => x.Stack > 0)) { list.Add(string.Join("\t", EncodeField(serverTradeOffer.Key), EncodeField(item.Key), EncodeField(item2.Prefab), EncodeField(item2.ItemName), item2.Stack.ToString(), item2.Quality.ToString(), item2.Variant.ToString(), item2.Durability.ToString(CultureInfo.InvariantCulture))); } } } string text2 = text + ".tmp"; File.WriteAllLines(text2, list.ToArray()); if (File.Exists(text)) { File.Delete(text); } File.Move(text2, text); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Handelskisten-Speicherung fehlgeschlagen: " + ex.Message)); } } } private static void LoadTradeOffers() { try { string path = TradeOfferFile(); if (!File.Exists(path)) { return; } _serverTradeOffers.Clear(); int num = 0; string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { '\t' }); if (array2.Length == 8 && int.TryParse(array2[4], out var result) && result > 0) { string key = DecodeField(array2[0]); string key2 = DecodeField(array2[1]); if (!_serverTradeOffers.TryGetValue(key, out var value)) { value = (_serverTradeOffers[key] = new Dictionary>(StringComparer.OrdinalIgnoreCase)); } if (!value.TryGetValue(key2, out var value2)) { value2 = (value[key2] = new List()); } int.TryParse(array2[5], out var result2); int.TryParse(array2[6], out var result3); float.TryParse(array2[7], NumberStyles.Float, CultureInfo.InvariantCulture, out var result4); value2.Add(new TradeOffer { Prefab = DecodeField(array2[2]), ItemName = DecodeField(array2[3]), Stack = result, Quality = Math.Max(1, result2), Variant = result3, Durability = result4 }); num++; } } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Persistente Handelskisten-Angebote geladen: " + num + " Stapel.")); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Handelskisten-Angebote konnten nicht geladen werden: " + ex.Message)); } } } private static void BroadcastTradeSnapshots(string partnershipId, Dictionary> owners) { foreach (KeyValuePair item in _serverSubscribers.Where((KeyValuePair pair) => pair.Key.StartsWith(partnershipId + "|", StringComparison.OrdinalIgnoreCase)).ToList()) { string ownerId = item.Key.Substring((partnershipId + "|").Length); string text = owners.Keys.FirstOrDefault((string id) => !string.Equals(id, ownerId, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrWhiteSpace(text)) { SendTradeSnapshot(item.Value, partnershipId, ownerId, text, owners.TryGetValue(ownerId, out var value) ? value : new List(), owners.TryGetValue(text, out var value2) ? value2 : new List()); } } } private static void SendTradeSnapshot(long peer, string partnershipId, string ownerId, string partnerId, List ownOffers, List partnerOffers) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown if (ZRoutedRpc.instance == null) { return; } ZPackage val = new ZPackage(); val.Write(partnershipId); val.Write(ownerId); val.Write(partnerId); val.Write(CurrentRevision(partnershipId)); val.Write(_serverClientSequences.TryGetValue(partnershipId + "|" + ownerId, out var value) ? value : 0); val.Write(ownOffers.Count); foreach (TradeOffer ownOffer in ownOffers) { val.Write(ownOffer.Prefab); val.Write(ownOffer.ItemName); val.Write(ownOffer.Stack); val.Write(ownOffer.Quality); val.Write(ownOffer.Variant); val.Write(ownOffer.Durability); } val.Write(partnerOffers.Count); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (TradeOffer partnerOffer in partnerOffers) { val.Write(partnerOffer.Prefab); val.Write(partnerOffer.ItemName); val.Write(partnerOffer.Stack); val.Write(partnerOffer.Quality); val.Write(partnerOffer.Variant); val.Write(partnerOffer.Durability); dictionary[partnerOffer.ItemName] = (dictionary.TryGetValue(partnerOffer.ItemName, out var value2) ? value2 : 0) + partnerOffer.Stack; } _serverSentMirrors[partnershipId + "|" + ownerId] = dictionary; ZRoutedRpc.instance.InvokeRoutedRPC(peer, "ChallengeHub_RPC_TradeChestSnapshot_v2", new object[1] { val }); } private static void RPC_TradeSnapshot(long sender, ZPackage package) { if (package == null || (Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer()) { return; } try { string text = package.ReadString(); string text2 = package.ReadString(); string value = package.ReadString(); long num = package.ReadLong(); long num2 = package.ReadLong(); if (_clientAppliedRevisions.TryGetValue(text, out var value2) && num <= value2) { return; } int num3 = Mathf.Clamp(package.ReadInt(), 0, 100); List list = new List(); for (int i = 0; i < num3; i++) { list.Add(new TradeOffer { Prefab = package.ReadString(), ItemName = package.ReadString(), Stack = package.ReadInt(), Quality = package.ReadInt(), Variant = package.ReadInt(), Durability = package.ReadSingle() }); } int num4 = Mathf.Clamp(package.ReadInt(), 0, 100); List list2 = new List(); for (int j = 0; j < num4; j++) { list2.Add(new TradeOffer { Prefab = package.ReadString(), ItemName = package.ReadString(), Stack = package.ReadInt(), Quality = package.ReadInt(), Variant = package.ReadInt(), Durability = package.ReadSingle() }); } Container val = TradeChestUiRules.CurrentContainer(InventoryGui.instance); if ((Object)(object)val == (Object)null || ReadZdoString(GetZdo((Component)(object)val), "ChallengeHub.Trade.PartnershipId") != text || ReadZdoString(GetZdo((Component)(object)val), "ChallengeHub.Trade.OwnerId") != text2) { return; } Inventory inventory = val.GetInventory(); TradeChestUiRules.EnsureLayout(val); _suppressTradeInventoryEvents++; foreach (ItemData item in inventory.GetAllItems().Where(IsTradeMirror).ToList()) { inventory.RemoveItem(item); } int num5 = InventorySize(inventory, "GetWidth", 5); int num6 = num5 * 2; int num7 = 0; if (num2 >= _clientLastSentSequence) { foreach (ItemData item2 in (from item in inventory.GetAllItems() where item != null && item.m_gridPos.y < 2 && !IsTradeMirror(item) select item).ToList()) { inventory.RemoveItem(item2); } foreach (TradeOffer item3 in list.Where((TradeOffer x) => x.Stack > 0)) { if (num7 < num6) { ItemData val2 = CreateTradeItem(item3); if (val2 != null) { AddAt(inventory, val2, num7 % num5, num7 / num5); num7++; } continue; } break; } } num7 = 0; foreach (TradeOffer item4 in list2.Where((TradeOffer x) => x.Stack > 0)) { if (num7 >= num6) { break; } ItemData val3 = CreateTradeItem(item4); if (val3 != null) { if (val3.m_customData == null) { val3.m_customData = new Dictionary(); } val3.m_customData["ChallengeHub.Trade.Mirror"] = "1"; val3.m_customData["ChallengeHub.Trade.MirrorSourceOwner"] = value; val3.m_customData["ChallengeHub.Trade.MirrorItem"] = item4.ItemName; AddAt(inventory, val3, num7 % num5, 2 + num7 / num5); num7++; } } MarkContainerChanged(val); _suppressTradeInventoryEvents = Math.Max(0, _suppressTradeInventoryEvents - 1); _clientAppliedRevisions[text] = num; _lastPublishedFingerprint = TradeFingerprint(inventory); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, (num4 > 0) ? "Partnerangebot aktualisiert." : "Der Partner bietet derzeit keine Waren an.", 0, (Sprite)null); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Handelskisten-Snapshot angewendet: Partnerangebote=" + num4 + "; Partnerschaft=" + text)); } } catch (Exception ex) { _suppressTradeInventoryEvents = Math.Max(0, _suppressTradeInventoryEvents - 1); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Handelskisten-Snapshot fehlgeschlagen: " + ex.Message)); } } } private static long CurrentRevision(string partnershipId) { if (!_serverTradeRevisions.TryGetValue(partnershipId, out var value)) { return 0L; } return value; } private static bool SameOffers(List left, List right) { return string.Equals(Fingerprint(left), Fingerprint(right), StringComparison.Ordinal); static string Fingerprint(List offers) { return string.Join(";", from offer in offers ?? new List() where offer != null && offer.Stack > 0 orderby offer.ItemName, offer.Quality, offer.Variant select offer.Prefab + "|" + offer.ItemName + "|" + offer.Stack + "|" + offer.Quality + "|" + offer.Variant + "|" + Mathf.RoundToInt(offer.Durability * 100f)); } } private static string TradeFingerprint(Inventory inventory) { if (inventory == null) { return string.Empty; } return string.Join(";", from item in inventory.GetAllItems() where item != null && item.m_stack > 0 orderby item.m_gridPos.y, item.m_gridPos.x select item.m_gridPos.x + "," + item.m_gridPos.y + "|" + NormalizeItemName(item) + "|" + item.m_stack + "|" + (IsTradeMirror(item) ? "M" : "O")); } private static ItemData CreateTradeItem(TradeOffer offer) { GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(offer.Prefab) : null); ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if ((Object)(object)val2 == (Object)null) { return null; } ItemData obj = val2.m_itemData.Clone(); obj.m_dropPrefab = val; obj.m_stack = offer.Stack; obj.m_quality = offer.Quality; obj.m_variant = offer.Variant; obj.m_durability = offer.Durability; return obj; } private static long ServerPeerId() { try { MethodInfo methodInfo = AccessTools.Method(((object)ZRoutedRpc.instance)?.GetType(), "GetServerPeerID", (Type[])null, (Type[])null) ?? AccessTools.Method(((object)ZRoutedRpc.instance)?.GetType(), "GetServerPeerId", (Type[])null, (Type[])null); return (methodInfo != null) ? Convert.ToInt64(methodInfo.Invoke(ZRoutedRpc.instance, null)) : 0; } catch { return 0L; } } private static void ProcessTradeChests() { Container[] array = Object.FindObjectsByType((FindObjectsSortMode)0); Dictionary> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); Container[] array2 = array; foreach (Container val in array2) { if ((Object)(object)val == (Object)null) { continue; } string text = ReadZdoString(GetZdo((Component)(object)val), "ChallengeHub.Trade.PartnershipId"); if (!string.IsNullOrWhiteSpace(text)) { if (!dictionary.ContainsKey(text)) { dictionary[text] = new List(); } dictionary[text].Add(val); } } int num = Math.Max(1, (TradeMaxTransfersPerScan != null) ? TradeMaxTransfersPerScan.Value : 12); foreach (KeyValuePair> item in dictionary) { if (num <= 0) { break; } List list = (from c in item.Value where (Object)(object)c != (Object)null orderby ReadZdoString(GetZdo((Component)(object)c), "ChallengeHub.Trade.BoundUtc") descending group c by ReadZdoString(GetZdo((Component)(object)c), "ChallengeHub.Trade.OwnerId") into @group select @group.First()).Take(2).ToList(); if (list.Count >= 2) { Container a = list[0]; Container b = ((IEnumerable)list).FirstOrDefault((Func)((Container c) => (Object)(object)c != (Object)(object)a && ReadZdoString(GetZdo((Component)(object)c), "ChallengeHub.Trade.OwnerId") != ReadZdoString(GetZdo((Component)(object)a), "ChallengeHub.Trade.OwnerId"))) ?? list[1]; SyncTradePair(a, b); num--; } } } private static void SyncTradePair(Container a, Container b) { if (!((Object)(object)a == (Object)null) && !((Object)(object)b == (Object)null)) { string text = ReadZdoString(GetZdo((Component)(object)a), "ChallengeHub.Trade.OwnerId"); string text2 = ReadZdoString(GetZdo((Component)(object)b), "ChallengeHub.Trade.OwnerId"); if (!string.IsNullOrWhiteSpace(text) && !string.IsNullOrWhiteSpace(text2) && !(text == text2)) { SettleMirrorWithdrawals(a, b); SettleMirrorWithdrawals(b, a); RebuildPartnerRow(a, b); RebuildPartnerRow(b, a); } } } private static void SettleMirrorWithdrawals(Container target, Container source) { Inventory inventory = target.GetInventory(); Inventory inventory2 = source.GetInventory(); if (inventory == null || inventory2 == null) { return; } string targetId = StructureId((Component)(object)target); string sourceOwner = ReadZdoString(GetZdo((Component)(object)source), "ChallengeHub.Trade.OwnerId"); Dictionary dictionary = MirrorAmounts(inventory, sourceOwner); foreach (KeyValuePair item in _expectedTradeMirrors.Where((KeyValuePair x) => x.Key.StartsWith(targetId + "|", StringComparison.OrdinalIgnoreCase)).ToList()) { string text = item.Key.Substring((targetId + "|").Length); int value; int num = (dictionary.TryGetValue(text, out value) ? value : 0); int num2 = Math.Max(0, item.Value - num); if (num2 > 0) { int num3 = ConsumeOffer(inventory2, text, num2); if (num3 > 0) { MarkContainerChanged(source); ReportTradeWithdrawal(source, target, text, num3); } } } } private static void RebuildPartnerRow(Container target, Container source) { //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = target.GetInventory(); Inventory inventory2 = source.GetInventory(); if (inventory == null || inventory2 == null) { return; } string value = ReadZdoString(GetZdo((Component)(object)source), "ChallengeHub.Trade.OwnerId"); foreach (ItemData item in inventory.GetAllItems().Where(IsTradeMirror).ToList()) { inventory.RemoveItem(item); } string targetId = StructureId((Component)(object)target); foreach (string item2 in _expectedTradeMirrors.Keys.Where((string x) => x.StartsWith(targetId + "|", StringComparison.OrdinalIgnoreCase)).ToList()) { _expectedTradeMirrors.Remove(item2); } int num = InventorySize(inventory, "GetWidth", 5); int num2 = num * 2; int num3 = 0; foreach (ItemData item3 in (from i in inventory2.GetAllItems() where i != null && i.m_gridPos.y < 2 && !IsTradeMirror(i) select i).ToList()) { if (num3 >= num2) { break; } string text = NormalizeItemName(item3); if (!SplitCsv((TradeBlockedItems != null) ? TradeBlockedItems.Value : string.Empty).Contains(text)) { ItemData val = item3.Clone(); val.m_gridPos = new Vector2i(num3 % num, 2 + num3 / num); num3++; if (val.m_customData == null) { val.m_customData = new Dictionary(); } val.m_customData["ChallengeHub.Trade.Mirror"] = "1"; val.m_customData["ChallengeHub.Trade.MirrorSourceOwner"] = value; val.m_customData["ChallengeHub.Trade.MirrorItem"] = text; if (AddAt(inventory, val, val.m_gridPos.x, val.m_gridPos.y)) { string key = targetId + "|" + text; _expectedTradeMirrors[key] = (_expectedTradeMirrors.TryGetValue(key, out var value2) ? value2 : 0) + val.m_stack; } } } MarkContainerChanged(target); } private static Dictionary MirrorAmounts(Inventory inventory, string sourceOwner) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (ItemData item in inventory.GetAllItems().Where(IsTradeMirror)) { if (item.m_customData.TryGetValue("ChallengeHub.Trade.MirrorSourceOwner", out var value) && !(value != sourceOwner)) { string value2; string key = (item.m_customData.TryGetValue("ChallengeHub.Trade.MirrorItem", out value2) ? value2 : NormalizeItemName(item)); dictionary[key] = (dictionary.TryGetValue(key, out var value3) ? value3 : 0) + Math.Max(0, item.m_stack); } } return dictionary; } private static int ConsumeOffer(Inventory inventory, string itemName, int quantity) { int num = quantity; foreach (ItemData item in (from i in inventory.GetAllItems() where i != null && i.m_gridPos.y < 2 && !IsTradeMirror(i) && NormalizeItemName(i) == itemName select i).ToList()) { int num2 = Math.Min(num, Math.Max(0, item.m_stack)); if (num2 > 0) { inventory.RemoveItem(item, num2); num -= num2; if (num <= 0) { break; } } } return quantity - num; } private static void ReportTradeWithdrawal(Container source, Container target, string itemName, int quantity) { //IL_0041: 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) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) if (TradeReportPoints == null || TradeReportPoints.Value) { ZDO zdo = GetZdo((Component)(object)source); ZDO zdo2 = GetZdo((Component)(object)target); Player val = FindPlayerById(ReadZdoString(zdo, "ChallengeHub.Trade.OwnerId")) ?? NearestPlayer(((Component)target).transform.position); if (!((Object)(object)val == (Object)null)) { string value = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff") + "-take-" + itemName; _plugin.SendEvent("trade_transfer_completed", val, new Dictionary { { "partnershipType", "trade" }, { "partnershipId", ReadZdoString(zdo, "ChallengeHub.Trade.PartnershipId") }, { "tradeTransferId", value }, { "item", itemName }, { "quantity", quantity }, { "sourceQuantity", quantity }, { "points", quantity }, { "tradeItemValue", 1 }, { "rarityMultiplier", 1f }, { "sourceChestId", StructureId((Component)(object)source) }, { "targetChestId", StructureId((Component)(object)target) }, { "sourcePlayerId", ReadZdoString(zdo, "ChallengeHub.Trade.OwnerId") }, { "sourcePlayerName", ReadZdoString(zdo, "ChallengeHub.Trade.OwnerName") }, { "partnerPlayerId", ReadZdoString(zdo2, "ChallengeHub.Trade.OwnerId") }, { "partnerPlayerName", ReadZdoString(zdo2, "ChallengeHub.Trade.OwnerName") }, { "biome", Plugin.CurrentBiome(((Component)source).transform.position) }, { "position", Plugin.SerializeVector(((Component)source).transform.position) }, { "attributionMethod", "trade_chest_partner_row_take_2_9_17" } }); } } } private static bool IsTradeMirror(ItemData item) { if (item != null && item.m_customData != null && item.m_customData.TryGetValue("ChallengeHub.Trade.Mirror", out var value)) { return value == "1"; } return false; } private static int InventorySize(Inventory inventory, string methodName, int fallback) { try { return (AccessTools.Method(((object)inventory).GetType(), methodName, (Type[])null, (Type[])null)?.Invoke(inventory, null) is int num) ? num : fallback; } catch { return fallback; } } private static bool AddAt(Inventory inventory, ItemData item, int x, int y) { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) try { MethodInfo methodInfo = AccessTools.Method(((object)inventory).GetType(), "AddItem", new Type[4] { typeof(ItemData), typeof(int), typeof(int), typeof(int) }, (Type[])null); if (methodInfo != null && methodInfo.Invoke(inventory, new object[4] { item, item.m_stack, x, y }) is bool result) { return result; } item.m_gridPos = new Vector2i(x, y); return inventory.AddItem(item); } catch { return false; } } private static int TransferBetweenTradeChests(Container source, Container target, int maxTransfers) { //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_047e: Unknown result type (might be due to invalid IL or missing references) //IL_0499: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)source == (Object)null || (Object)(object)target == (Object)null || maxTransfers <= 0) { return 0; } Inventory inventory = source.GetInventory(); Inventory inventory2 = target.GetInventory(); if (inventory == null || inventory2 == null) { return 0; } ZDO zdo = GetZdo((Component)(object)source); ZDO zdo2 = GetZdo((Component)(object)target); string text = ReadZdoString(zdo, "ChallengeHub.Trade.PartnershipId"); if (string.IsNullOrWhiteSpace(text)) { return 0; } string text2 = ReadZdoString(zdo, "ChallengeHub.Trade.OwnerId"); string text3 = ReadZdoString(zdo2, "ChallengeHub.Trade.OwnerId"); if (string.IsNullOrWhiteSpace(text2) || string.IsNullOrWhiteSpace(text3) || text2 == text3 || !HaveActiveOwnedGuardianPair(text2, text3)) { return 0; } int num = ReadZdoInt(zdo, "ChallengeHub.Partnership.Level", (TradeDefaultLevel == null) ? 1 : TradeDefaultLevel.Value); Ratio ratio = RatioForLevel(num); if (ratio.In <= 0 || ratio.Out <= 0) { return 0; } HashSet hashSet = SplitCsv((TradeBlockedItems != null) ? TradeBlockedItems.Value : string.Empty); int num2 = 0; foreach (ItemData item in (from i in inventory.GetAllItems() where i != null && i.m_shared != null select i).ToList()) { if (num2 >= maxTransfers) { break; } string text4 = NormalizeItemName(item); if (hashSet.Contains(text4) || (item.m_customData != null && item.m_customData.TryGetValue("ChallengeHub.Trade.Received", out var value) && value == "1")) { continue; } int num3 = Math.Max(0, item.m_stack); int num4 = num3 / ratio.In * ratio.Out; if (num4 <= 0) { continue; } int num5 = Mathf.Min(num3, Mathf.CeilToInt((float)num4 * (float)ratio.In / (float)Math.Max(1, ratio.Out))); if (num5 <= 0) { continue; } ItemData val = item.Clone(); val.m_stack = num4; if (val.m_customData == null) { val.m_customData = new Dictionary(); } string value2 = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff") + "-" + text + "-" + text4; val.m_customData["ChallengeHub.Trade.Received"] = "1"; val.m_customData["ChallengeHub.Trade.TransferId"] = value2; if (!inventory2.AddItem(val)) { continue; } inventory.RemoveItem(item, num5); MarkContainerChanged(source); MarkContainerChanged(target); num2++; if (TradeReportPoints == null || TradeReportPoints.Value) { Player val2 = NearestPlayer(((Component)source).transform.position) ?? NearestPlayer(((Component)target).transform.position); if ((Object)(object)val2 != (Object)null) { _plugin.SendEvent("trade_transfer_completed", val2, new Dictionary { { "partnershipType", "trade" }, { "partnershipId", text }, { "tradeTransferId", value2 }, { "item", text4 }, { "quantity", num4 }, { "sourceQuantity", num5 }, { "tradeRatio", ratio.Out + ":" + ratio.In }, { "tradeLevel", num }, { "tradeItemValue", BaseItemValue(text4) }, { "rarityMultiplier", 1f }, { "sourceChestId", ReadZdoString(zdo, "ChallengeHub.Trade.StructureId") }, { "targetChestId", ReadZdoString(zdo2, "ChallengeHub.Trade.StructureId") }, { "sourcePlayerId", ReadZdoString(zdo, "ChallengeHub.Trade.OwnerId") }, { "sourcePlayerName", ReadZdoString(zdo, "ChallengeHub.Trade.OwnerName") }, { "partnerPlayerId", ReadZdoString(zdo2, "ChallengeHub.Trade.OwnerId") }, { "partnerPlayerName", ReadZdoString(zdo2, "ChallengeHub.Trade.OwnerName") }, { "biome", Plugin.CurrentBiome(((Component)source).transform.position) }, { "position", Plugin.SerializeVector(((Component)source).transform.position) } }); } } } return num2; } private static void HandleTradeChestBindKey(Player player) { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_04af: Unknown result type (might be due to invalid IL or missing references) //IL_04ca: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) GameObject val = HoverObject(player, Mathf.Max(3f, (TradeChestBindRange != null) ? TradeChestBindRange.Value : 8f)); Container val2 = (((Object)(object)val != (Object)null) ? val.GetComponentInParent() : null); float num = Mathf.Max(3f, (TradeChestBindRange != null) ? TradeChestBindRange.Value : 8f); if ((Object)(object)val2 == (Object)null && (Object)(object)_recentlyOpenedChest != (Object)null && Time.realtimeSinceStartup - _recentlyOpenedChestAt <= 15f && Vector3.Distance(((Component)player).transform.position, ((Component)_recentlyOpenedChest).transform.position) <= num + 2f) { val2 = _recentlyOpenedChest; val = ((Component)val2).gameObject; } if ((Object)(object)val2 != (Object)null && (Object)(object)Plugin.Instance != (Object)null && Plugin.Instance.FarmerChestBindingOwnsCurrentKey) { return; } if ((Object)(object)val == (Object)null) { ((Character)player).Message((MessageType)2, "Kein Vertragsort oder keine Kiste im Blick. STRG+K am Handel-Vertragsort, danach an der Kiste.", 0, (Sprite)null); return; } Piece componentInParent = val.GetComponentInParent(); if ((Object)(object)_pendingTradeStructure != (Object)null && Time.realtimeSinceStartup - _pendingTradeBindStartedAt > 45f) { _pendingTradeStructure = null; _pendingTradePartnershipId = string.Empty; } if ((Object)(object)_pendingTradeStructure == (Object)null) { if ((Object)(object)componentInParent == (Object)null || !IsActivePartnershipStructure(componentInParent, "trade")) { ((Character)player).Message((MessageType)2, "Zuerst den aktiven Handel-Vertragsort anschauen und STRG+K druecken.", 0, (Sprite)null); return; } _pendingTradeStructure = componentInParent; _pendingTradePartnershipId = StructureId((Component)(object)componentInParent); _pendingTradeBindStartedAt = Time.realtimeSinceStartup; ((Character)player).Message((MessageType)2, "Handel-Vertragsort gewaehlt. Jetzt Kiste anschauen und STRG+K druecken.", 0, (Sprite)null); return; } if ((Object)(object)val2 == (Object)null) { ((Character)player).Message((MessageType)2, "Keine Kiste im Blick. Handel-Kistenbindung abgebrochen.", 0, (Sprite)null); _pendingTradeStructure = null; _pendingTradePartnershipId = string.Empty; return; } if (TradeRequireChestNearContract == null || TradeRequireChestNearContract.Value) { float num2 = Mathf.Max(3f, (TradeChestBindRange != null) ? TradeChestBindRange.Value : 8f); if (Vector3.Distance(((Component)val2).transform.position, ((Component)_pendingTradeStructure).transform.position) > num2) { ((Character)player).Message((MessageType)2, "Die Handelspartner-Kiste muss innerhalb des Vertragsradius stehen.", 0, (Sprite)null); return; } } ZDO zdo = GetZdo((Component)(object)val2); try { ZNetView val3 = ((Component)val2).GetComponent() ?? ((Component)val2).GetComponentInParent(); if ((Object)(object)val3 != (Object)null && val3.IsValid() && !val3.IsOwner()) { val3.ClaimOwnership(); zdo = val3.GetZDO(); } } catch { } ZDO zdo2 = GetZdo((Component)(object)_pendingTradeStructure); string text = ReadZdoString(zdo2, "ChallengeHub.Partnership.UserAId"); string text2 = ReadZdoString(zdo2, "ChallengeHub.Partnership.UserBId"); string text3 = ReadZdoString(zdo2, "ChallengeHub.Partnership.UserAName"); string text4 = ReadZdoString(zdo2, "ChallengeHub.Partnership.UserBName"); string text5 = SafePlayerId(player).ToString(); string text6 = ((text == text5) ? text2 : text); string text7 = ((text == text5) ? text4 : text3); if (!string.IsNullOrWhiteSpace(text) && !string.IsNullOrWhiteSpace(text2) && text5 != text && text5 != text2) { ((Character)player).Message((MessageType)2, "Du bist kein Partner dieses Handel-Vertragsorts.", 0, (Sprite)null); return; } if (!IsGuardianOwnedBy(_pendingTradeStructure, text5)) { ((Character)player).Message((MessageType)2, "Die Handelskiste kann nur am eigenen Waechter gebunden werden.", 0, (Sprite)null); return; } if ((Object)(object)FindOwnedTradeGuardian(text6, text5, _pendingTradeStructure) == (Object)null) { ((Character)player).Message((MessageType)2, "Der Handelspartner braucht einen eigenen Waechter mit derselben aktiven Handelspartnerschaft.", 0, (Sprite)null); return; } string text8 = CanonicalTradePartnershipId(text5, text6); ClearOtherBoundTradeChests(text8, text5, val2); WriteZdoString(zdo, "ChallengeHub.Trade.PartnershipId", text8); WriteZdoString(zdo, "ChallengeHub.Trade.StructureId", StructureId((Component)(object)_pendingTradeStructure)); WriteZdoString(zdo, "ChallengeHub.Trade.OwnerId", text5); WriteZdoString(zdo, "ChallengeHub.Trade.OwnerName", player.GetPlayerName()); WriteZdoString(zdo, "ChallengeHub.Trade.PartnerId", text6); WriteZdoString(zdo, "ChallengeHub.Trade.PartnerName", text7); WriteZdoString(zdo, "ChallengeHub.Trade.BoundUtc", DateTime.UtcNow.ToString("o")); TradeChestUiRules.EnsureLayout(val2); bool flag = HasBoundTradeChest(text8, text6); ((Character)player).Message((MessageType)2, flag ? "Handelspartner-Kiste gebunden. Beide Kisten sind bereit; der Transfer ist aktiv." : "Handelspartner-Kiste gebunden. Warte auf die Kiste des Partners.", 0, (Sprite)null); _plugin.SendEvent("trade_chest_bound", player, new Dictionary { { "partnershipType", "trade" }, { "partnershipId", text8 }, { "partnerPlayerId", text6 ?? string.Empty }, { "partnerPlayerName", text7 ?? string.Empty }, { "chestId", StructureId((Component)(object)val2) }, { "structureId", StructureId((Component)(object)_pendingTradeStructure) }, { "biome", Plugin.CurrentBiome(((Component)val2).transform.position) }, { "position", Plugin.SerializeVector(((Component)val2).transform.position) } }); _pendingTradeStructure = null; _pendingTradePartnershipId = string.Empty; } internal static void RememberOpenedChest(Container chest, Humanoid character, bool interacted) { if (interacted && !((Object)(object)chest == (Object)null) && !((Object)(object)character == (Object)null) && !((Object)(object)character != (Object)(object)Player.m_localPlayer)) { _recentlyOpenedChest = chest; _recentlyOpenedChestAt = Time.realtimeSinceStartup; } } internal static void BeginTradeChestBinding(Player player, Piece structure) { if ((Object)(object)player == (Object)null || (Object)(object)structure == (Object)null || EnableTradeChests == null || !EnableTradeChests.Value) { return; } if (!IsActivePartnershipStructure(structure, "trade")) { ((Character)player).Message((MessageType)2, "Dieser Handelsvertrag ist noch nicht aktiv.", 0, (Sprite)null); return; } ZDO zdo = GetZdo((Component)(object)structure); string text = SafePlayerId(player).ToString(); string text2 = ReadZdoString(zdo, "ChallengeHub.Partnership.UserAId"); string text3 = ReadZdoString(zdo, "ChallengeHub.Partnership.UserBId"); if (text != text2 && text != text3) { ((Character)player).Message((MessageType)2, "Du bist kein Partner dieses Handelsvertrags.", 0, (Sprite)null); return; } string ownerId = ((text2 == text) ? text3 : text2); if (!IsGuardianOwnedBy(structure, text)) { ((Character)player).Message((MessageType)2, "Waehle deinen eigenen Handelswaechter aus.", 0, (Sprite)null); return; } if ((Object)(object)FindOwnedTradeGuardian(ownerId, text, structure) == (Object)null) { ((Character)player).Message((MessageType)2, "Der Partner braucht zuerst einen eigenen Waechter mit aktiver Handelspartnerschaft.", 0, (Sprite)null); return; } _pendingTradeStructure = structure; _pendingTradePartnershipId = StructureId((Component)(object)structure); _pendingTradeBindStartedAt = Time.realtimeSinceStartup; ((Character)player).Message((MessageType)2, "Handelsvertrag gewaehlt. Jetzt Kiste anschauen und STRG+K druecken.", 0, (Sprite)null); } private static bool IsGuardianOwnedBy(Piece guardian, string playerId) { if ((Object)(object)guardian == (Object)null || string.IsNullOrWhiteSpace(playerId) || !long.TryParse(playerId, out var result)) { return false; } try { return guardian.GetCreator() == result; } catch { return false; } } private static Piece FindOwnedTradeGuardian(string ownerId, string partnerId, Piece except) { if (string.IsNullOrWhiteSpace(ownerId) || string.IsNullOrWhiteSpace(partnerId)) { return null; } Piece[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Piece val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)except) && IsActivePartnershipStructure(val, "trade") && IsGuardianOwnedBy(val, ownerId)) { ZDO zdo = GetZdo((Component)(object)val); string text = ReadZdoString(zdo, "ChallengeHub.Partnership.UserAId"); string text2 = ReadZdoString(zdo, "ChallengeHub.Partnership.UserBId"); if ((text == ownerId && text2 == partnerId) || (text == partnerId && text2 == ownerId)) { return val; } } } return null; } private static string CanonicalTradePartnershipId(string playerA, string playerB) { string[] array = new string[2] { playerA ?? string.Empty, playerB ?? string.Empty }; Array.Sort(array, (IComparer?)StringComparer.Ordinal); return "trade:" + array[0] + ":" + array[1]; } private static bool HasBoundTradeChest(string partnershipId, string ownerId) { Container[] array = Object.FindObjectsByType((FindObjectsSortMode)0); for (int i = 0; i < array.Length; i++) { ZDO zdo = GetZdo((Component)(object)array[i]); if (ReadZdoString(zdo, "ChallengeHub.Trade.PartnershipId") == partnershipId && ReadZdoString(zdo, "ChallengeHub.Trade.OwnerId") == ownerId) { return true; } } return false; } internal static string BuildGuardianTradeChestHover(Piece guardian) { if ((Object)(object)guardian == (Object)null || !IsActivePartnershipStructure(guardian, "trade")) { return "Handelskiste: kein aktiver Handelsvertrag"; } ZDO zdo = GetZdo((Component)(object)guardian); string text; try { text = guardian.GetCreator().ToString(); } catch { text = string.Empty; } string text2 = ReadZdoString(zdo, "ChallengeHub.Partnership.UserAId"); string text3 = ReadZdoString(zdo, "ChallengeHub.Partnership.UserBId"); string text4 = ((text2 == text) ? text3 : text2); string partnershipId = CanonicalTradePartnershipId(text, text4); if (!HasBoundTradeChest(partnershipId, text)) { return "Handelskiste: noch nicht verbunden (STRG+K)"; } if (!HasBoundTradeChest(partnershipId, text4)) { return "Handelskiste: verbunden · wartet auf Partnerkiste"; } return "Handelskiste: verbunden · Transfer aktiv"; } private static void ClearOtherBoundTradeChests(string partnershipId, string ownerId, Container selected) { Container[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Container val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)selected)) { ZDO zdo = GetZdo((Component)(object)val); if (!(ReadZdoString(zdo, "ChallengeHub.Trade.PartnershipId") != partnershipId) && !(ReadZdoString(zdo, "ChallengeHub.Trade.OwnerId") != ownerId)) { WriteZdoString(zdo, "ChallengeHub.Trade.PartnershipId", string.Empty); WriteZdoString(zdo, "ChallengeHub.Trade.PartnerId", string.Empty); } } } } private static bool HaveActiveOwnedGuardianPair(string ownerA, string ownerB) { Piece val = FindOwnedTradeGuardian(ownerA, ownerB, null); if ((Object)(object)val == (Object)null) { return false; } return (Object)(object)FindOwnedTradeGuardian(ownerB, ownerA, val) != (Object)null; } private static void ShowTradeChestHud(Player player) { if (Time.realtimeSinceStartup < _nextTradeHudAt) { return; } GameObject val = HoverObject(player, 5f); if ((Object)(object)val == (Object)null) { return; } Container componentInParent = val.GetComponentInParent(); if (!((Object)(object)componentInParent == (Object)null)) { ZDO zdo = GetZdo((Component)(object)componentInParent); if (!string.IsNullOrWhiteSpace(ReadZdoString(zdo, "ChallengeHub.Trade.PartnershipId"))) { _nextTradeHudAt = Time.realtimeSinceStartup + 2.5f; ((Character)player).Message((MessageType)1, "Handelspartner-Kiste: " + ReadZdoString(zdo, "ChallengeHub.Trade.OwnerName") + " ↔ " + ReadZdoString(zdo, "ChallengeHub.Trade.PartnerName"), 0, (Sprite)null); } } } private static void ReportPortalBuildAndViolation(Player player, object builtObject, string pieceName) { //IL_0017: 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_01e9: Unknown result type (might be due to invalid IL or missing references) string text = NormalizePieceName(pieceName, builtObject); if (!IsPortalName(text)) { return; } string text2 = Plugin.CurrentBiome(((Component)player).transform.position); int num = CountPortalsInBiome(text2); int num2 = Math.Max(0, (Plugin.AllowedActivePortalsPerBiome == null) ? 1 : Plugin.AllowedActivePortalsPerBiome.Value); _plugin.SendEvent("portal_built", player, new Dictionary { { "piece", text }, { "item", text }, { "biome", text2 }, { "activePortals", num }, { "allowedActivePortals", num2 }, { "isViolation", num > num2 }, { "position", Plugin.SerializeVector(((Component)player).transform.position) }, { "attributionMethod", "portal_build_direct" } }); if (num > num2) { string key = text2 + ":" + text; float value; float num3 = (_lastPortalViolationAt.TryGetValue(key, out value) ? value : 0f); if (!(Time.realtimeSinceStartup - num3 < Mathf.Max(1f, (PortalViolationCooldownSeconds != null) ? PortalViolationCooldownSeconds.Value : 30f))) { _lastPortalViolationAt[key] = Time.realtimeSinceStartup; _plugin.SendEvent("portal_violation", player, new Dictionary { { "label", "Portal-Verstoß: zu viele aktive Portale in " + text2 }, { "points", (PortalViolationPoints != null) ? PortalViolationPoints.Value : (-100) }, { "piece", text }, { "item", text }, { "biome", text2 }, { "activePortals", num }, { "allowedActivePortals", num2 }, { "position", Plugin.SerializeVector(((Component)player).transform.position) }, { "attributionMethod", "portal_build_direct" } }); } } } internal static bool TryHandlePeaceDamage(Player victim, HitData hit) { //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) if (!_initialized || EnablePeaceViolation == null || !EnablePeaceViolation.Value || (Object)(object)victim == (Object)null || hit == null) { return false; } try { Character attacker = hit.GetAttacker(); Player val = (Player)(object)((attacker is Player) ? attacker : null); if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)victim) { return false; } if (!HasActivePartnershipBetween("peace", val, victim, out var structure)) { return false; } string key = SafePlayerId(val) + ":" + SafePlayerId(victim); float value; float num = (_lastPeaceViolationAt.TryGetValue(key, out value) ? value : 0f); if (Time.realtimeSinceStartup - num >= Mathf.Max(1f, (PeaceViolationCooldownSeconds != null) ? PeaceViolationCooldownSeconds.Value : 60f)) { _lastPeaceViolationAt[key] = Time.realtimeSinceStartup; _plugin.SendEvent("peace_violation", val, new Dictionary { { "partnershipType", "peace" }, { "partnerPlayerId", SafePlayerId(victim).ToString() }, { "partnerPlayerName", victim.GetPlayerName() }, { "label", "Friedensbruch: Angriff auf Friedenspartner" }, { "points", (PeaceViolationPenaltyPoints != null) ? PeaceViolationPenaltyPoints.Value : (-150) }, { "structureId", ((Object)(object)structure != (Object)null) ? StructureId((Component)(object)structure) : string.Empty }, { "biome", Plugin.CurrentBiome(((Component)victim).transform.position) }, { "position", Plugin.SerializeVector(((Component)victim).transform.position) } }); ((Character)val).Message((MessageType)2, "Friedensbruch! Angriff auf Friedenspartner wurde gewertet.", 0, (Sprite)null); ((Character)victim).Message((MessageType)2, "Friedensbruch: " + val.GetPlayerName() + " hat dich angegriffen.", 0, (Sprite)null); } if (BlockPeacePartnerDamage != null && BlockPeacePartnerDamage.Value) { ((DamageTypes)(ref hit.m_damage)).Modify(0f); return true; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Friedensbruch-Erkennung fehlgeschlagen: " + ex.Message)); } } return false; } private static void ScanBossArenas() { //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) List allPlayers = Player.GetAllPlayers(); if (allPlayers == null || allPlayers.Count == 0) { return; } Character[] array = Object.FindObjectsByType((FindObjectsSortMode)0); HashSet hashSet = new HashSet(); Character[] array2 = array; foreach (Character val in array2) { if ((Object)(object)val == (Object)null || val.IsPlayer() || !IsBoss(val)) { continue; } int instanceID = ((Object)val).GetInstanceID(); hashSet.Add(instanceID); if (!_bossClaims.TryGetValue(instanceID, out var value)) { Player val2 = NearestPlayer(((Component)val).transform.position); if ((Object)(object)val2 == (Object)null) { continue; } value = new BossClaim { BossInstanceId = instanceID, BossName = NormalizePieceName(((Object)val).name, val), Center = ((Component)val).transform.position, OwnerId = SafePlayerId(val2).ToString(), OwnerName = val2.GetPlayerName(), StartedAt = Time.realtimeSinceStartup }; _bossClaims[instanceID] = value; if (ReportBossfightArenaEvents != null && ReportBossfightArenaEvents.Value) { _plugin.SendEvent("bossfight_started", val2, new Dictionary { { "boss", value.BossName }, { "ownerPlayerId", value.OwnerId }, { "ownerPlayerName", value.OwnerName }, { "bossArenaRadius", (BossArenaRadius != null) ? BossArenaRadius.Value : 120f }, { "biome", Plugin.CurrentBiome(value.Center) }, { "position", Plugin.SerializeVector(value.Center) } }); } } value.Center = ((Component)val).transform.position; EnforceBossClaim(value, allPlayers); } foreach (int item in _bossClaims.Keys.ToList()) { if (!hashSet.Contains(item)) { _bossClaims.Remove(item); } } } private static void EnforceBossClaim(BossClaim claim, List players) { //IL_0049: 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_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(30f, (BossArenaRadius != null) ? BossArenaRadius.Value : 120f); foreach (Player player2 in players) { if ((Object)(object)player2 == (Object)null || Vector3.Distance(((Component)player2).transform.position, claim.Center) > num || IsPlayerAllowedInBossClaim(claim, player2)) { continue; } long key = SafePlayerId(player2); float value; float num2 = (claim.WarnedAt.TryGetValue(key, out value) ? value : 0f); if (num2 <= 0f) { claim.WarnedAt[key] = Time.realtimeSinceStartup; ((Character)player2).Message((MessageType)2, "Dieser Bosskampf gehoert " + claim.OwnerName + ". Kein Bossfight-Partner: Arena verlassen!", 0, (Sprite)null); } else if (!(Time.realtimeSinceStartup - num2 < Mathf.Max(3f, (BossTrespassWarningSeconds != null) ? BossTrespassWarningSeconds.Value : 10f))) { Vector3 val = ((Component)player2).transform.position - claim.Center; if (((Vector3)(ref val)).sqrMagnitude < 1f) { val = Vector3.forward; } Vector3 target = claim.Center + ((Vector3)(ref val)).normalized * Mathf.Max(num + 5f, (BossTrespassTeleportDistance != null) ? BossTrespassTeleportDistance.Value : 145f); target.y = ((Component)player2).transform.position.y + 0.5f; TryTeleportPlayer(player2, target); ((Character)player2).Message((MessageType)2, "Du wurdest aus der Boss-Arena verwiesen.", 0, (Sprite)null); claim.WarnedAt[key] = Time.realtimeSinceStartup + 9999f; if (ReportBossfightArenaEvents != null && ReportBossfightArenaEvents.Value) { Player player = FindPlayerById(claim.OwnerId) ?? player2; _plugin.SendEvent("bossfight_trespass_removed", player, new Dictionary { { "boss", claim.BossName }, { "ownerPlayerId", claim.OwnerId }, { "ownerPlayerName", claim.OwnerName }, { "trespassPlayerId", key.ToString() }, { "trespassPlayerName", player2.GetPlayerName() }, { "bossArenaRadius", num }, { "biome", Plugin.CurrentBiome(claim.Center) }, { "position", Plugin.SerializeVector(claim.Center) } }); } } } } private static bool IsPlayerAllowedInBossClaim(BossClaim claim, Player player) { if (SafePlayerId(player).ToString() == claim.OwnerId) { return true; } Player val = FindPlayerById(claim.OwnerId); if ((Object)(object)val == (Object)null) { return false; } if (HasActivePartnershipBetween("bossfight", val, player, out var structure)) { return true; } if (AllowCombatPartnersInBossfight != null && AllowCombatPartnersInBossfight.Value) { return HasActivePartnershipBetween("combat", val, player, out structure); } return false; } private static bool HasActivePartnershipBetween(string type, Player a, Player b, out Piece structure) { structure = null; if ((Object)(object)a == (Object)null || (Object)(object)b == (Object)null) { return false; } string text = SafePlayerId(a).ToString(); string text2 = SafePlayerId(b).ToString(); Piece[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Piece val in array) { if ((Object)(object)val == (Object)null) { continue; } ZDO zdo = GetZdo((Component)(object)val); if (!(ReadZdoString(zdo, "ChallengeHub.Partnership.Status") != "active") && !(NormalizeType(ReadZdoString(zdo, "ChallengeHub.Partnership.Type")) != NormalizeType(type))) { string text3 = ReadZdoString(zdo, "ChallengeHub.Partnership.UserAId"); string text4 = ReadZdoString(zdo, "ChallengeHub.Partnership.UserBId"); if ((text3 == text && text4 == text2) || (text3 == text2 && text4 == text)) { structure = val; return true; } } } return false; } private static bool IsActivePartnershipStructure(Piece piece, string expectedType) { if ((Object)(object)piece == (Object)null) { return false; } ZDO zdo = GetZdo((Component)(object)piece); if (ReadZdoString(zdo, "ChallengeHub.Partnership.Status") != "active") { return false; } return NormalizeType(ReadZdoString(zdo, "ChallengeHub.Partnership.Type")) == NormalizeType(expectedType); } private static bool IsBoss(Character character) { try { if (character.m_boss) { return true; } } catch { } string text = NormalizePieceName(((Object)(object)character != (Object)null) ? ((Object)character).name : string.Empty, character); if (!text.Contains("eikthyr") && !text.Contains("gd_king") && !text.Contains("bonemass") && !text.Contains("dragon") && !text.Contains("goblinking") && !text.Contains("queen")) { return text.Contains("fader"); } return true; } private static int CountPortalsInBiome(string biome) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) int num = 0; TeleportWorld[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (TeleportWorld val in array) { if (!((Object)(object)val == (Object)null) && ((Behaviour)val).isActiveAndEnabled && Plugin.CurrentBiome(((Component)val).transform.position) == biome) { num++; } } return num; } private static bool IsPortalName(string normalized) { if (string.IsNullOrWhiteSpace(normalized)) { return false; } if (!normalized.Contains("portal")) { return normalized.Contains("teleport"); } return true; } private static string NormalizePieceName(string fallback, object instance) { try { GameObject val = (GameObject)((instance is GameObject) ? instance : null); if (val != null) { return Plugin.NormalizeKey(((Object)val).name); } Component val2 = (Component)((instance is Component) ? instance : null); if (val2 != null) { return Plugin.NormalizeKey(((Object)val2).name); } if (instance != null) { return Plugin.NormalizeKey(instance.ToString()); } } catch { } return Plugin.NormalizeKey(fallback ?? "unknown"); } private static string NormalizeItemName(ItemData item) { try { return Plugin.NormalizeKey(((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : item.m_shared.m_name); } catch { return "unknown"; } } private static int BaseItemValue(string item) { string text = Plugin.NormalizeKey(item); if (text.Contains("trophy") || text.Contains("dragon_egg") || text.Contains("queen") || text.Contains("fader")) { return 25; } if (text.Contains("surtling") || text.Contains("core") || text.Contains("black_core")) { return 12; } if (text.Contains("iron") || text.Contains("silver") || text.Contains("blackmetal") || text.Contains("flametal")) { return 8; } if (text.Contains("bronze") || text.Contains("copper") || text.Contains("tin")) { return 5; } if (text.Contains("seed") || text.Contains("carrot") || text.Contains("turnip") || text.Contains("onion")) { return 3; } if (text.Contains("wood") || text.Contains("stone")) { return 1; } return 2; } private static Ratio RatioForLevel(int level) { string[] array = ((TradeLevelRatios != null) ? TradeLevelRatios.Value : "1:4,2:4,3:4,4:4").Split(new char[1] { ',' }); int num = Math.Max(0, Math.Min(array.Length - 1, level - 1)); string[] array2 = ((array.Length != 0) ? array[num] : "1:4").Split(new char[1] { ':' }); if (array2.Length == 2 && int.TryParse(array2[0], out var result) && int.TryParse(array2[1], out var result2)) { return new Ratio { Out = Math.Max(1, result), In = Math.Max(1, result2) }; } return new Ratio { Out = 1, In = 4 }; } private static HashSet SplitCsv(string text) { return new HashSet(from s in (text ?? "").Split(new char[1] { ',' }).Select(Plugin.NormalizeKey) where !string.IsNullOrWhiteSpace(s) select s, StringComparer.OrdinalIgnoreCase); } private static GameObject HoverObject(Player player, float range) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_0025: Unknown result type (might be due to invalid IL or missing references) try { RaycastHit val = default(RaycastHit); if (Physics.Raycast(((Component)player).transform.position + Vector3.up * 1.6f, ((Component)player).transform.forward, ref val, range, -1, (QueryTriggerInteraction)1)) { return ((Object)(object)((RaycastHit)(ref val)).collider != (Object)null) ? ((Component)((RaycastHit)(ref val)).collider).gameObject : null; } } catch { } return null; } private static ZDO GetZdo(Component component) { try { ZNetView val = (((Object)(object)component != (Object)null) ? component.GetComponent() : null); return ((Object)(object)val != (Object)null) ? val.GetZDO() : null; } catch { return null; } } private static string ReadZdoString(ZDO zdo, string key, string fallback = "") { try { return (zdo != null) ? zdo.GetString(key, fallback) : fallback; } catch { return fallback; } } private static int ReadZdoInt(ZDO zdo, string key, int fallback = 0) { try { return (zdo != null) ? zdo.GetInt(key, fallback) : fallback; } catch { return fallback; } } private static void WriteZdoString(ZDO zdo, string key, string value) { try { if (zdo != null) { zdo.Set(key, value ?? string.Empty); } } catch { } } private static string StructureId(Component component) { //IL_006c: 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_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) try { ZDO zdo = GetZdo(component); string text = ReadZdoString(zdo, "ChallengeHub.Partnership.StructureId"); if (!string.IsNullOrWhiteSpace(text)) { return text; } if (zdo != null) { FieldInfo field = ((object)zdo).GetType().GetField("m_uid", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); object obj = ((field != null) ? field.GetValue(zdo) : null); if (obj != null) { return obj.ToString(); } } Vector3 position = component.transform.position; return Plugin.NormalizeKey(((Object)component).name) + ":" + Mathf.RoundToInt(position.x) + ":" + Mathf.RoundToInt(position.y) + ":" + Mathf.RoundToInt(position.z); } catch { return ((Object)(object)component != (Object)null) ? ((Object)component).GetInstanceID().ToString() : string.Empty; } } private static string NormalizeType(string value) { string text = Plugin.NormalizeKey(value); if (text.Contains("boss")) { return "bossfight"; } if (text.Contains("combat") || text.Contains("kampf")) { return "combat"; } if (text.Contains("peace") || text.Contains("frieden")) { return "peace"; } if (text.Contains("build") || text.Contains("builder")) { return "build"; } if (text.Contains("explore") || text.Contains("explorer")) { return "explore"; } if (text.Contains("farm")) { return "farm"; } return "trade"; } private static void MarkContainerChanged(Container container) { if ((Object)(object)container == (Object)null) { return; } try { Inventory inventory = container.GetInventory(); if (inventory != null) { MethodInfo methodInfo = AccessTools.Method(((object)inventory).GetType(), "Changed", (Type[])null, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(inventory, null); } } } catch { } try { string[] array = new string[3] { "Save", "OnContainerChanged", "OnInventoryChanged" }; foreach (string text in array) { MethodInfo methodInfo2 = AccessTools.Method(((object)container).GetType(), text, (Type[])null, (Type[])null); if (!(methodInfo2 == null) && methodInfo2.GetParameters().Length == 0) { methodInfo2.Invoke(container, null); break; } } } catch { } } private static bool IsCtrlPressed() { if (!Input.GetKey((KeyCode)306)) { return Input.GetKey((KeyCode)305); } return true; } private static bool IsServer() { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } private static long SafePlayerId(Player player) { try { return ((Object)(object)player != (Object)null) ? player.GetPlayerID() : 0; } catch { return 0L; } } private static Player NearestPlayer(Vector3 position) { //IL_0026: 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) List allPlayers = Player.GetAllPlayers(); Player result = null; float num = float.MaxValue; foreach (Player item in allPlayers) { if (!((Object)(object)item == (Object)null)) { float num2 = Vector3.Distance(position, ((Component)item).transform.position); if (num2 < num) { result = item; num = num2; } } } return result; } private static Player FindPlayerById(string playerId) { foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer != (Object)null && SafePlayerId(allPlayer).ToString() == playerId) { return allPlayer; } } return null; } private static void TryTeleportPlayer(Player player, Vector3 target) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) try { MethodInfo methodInfo = AccessTools.Method(typeof(Player), "TeleportTo", new Type[3] { typeof(Vector3), typeof(Quaternion), typeof(bool) }, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(player, new object[3] { target, ((Component)player).transform.rotation, true }); return; } } catch { } try { ((Component)player).transform.position = target; } catch { } } } [HarmonyPatch(typeof(Player), "Update")] internal static class ChallengeHubPartnershipGameplayPlayerUpdatePatch { private static void Postfix(Player __instance) { PartnershipGameplayFeature.LocalUpdate(__instance); PartnershipGameplayFeature.ServerUpdate(); } } [HarmonyPatch(typeof(Container), "Interact")] internal static class ChallengeHubTradeChestOpenedPatch { [HarmonyPostfix] private static void Postfix(Container __instance, Humanoid character, bool __result) { PartnershipGameplayFeature.RememberOpenedChest(__instance, character, __result); } } [HarmonyPatch(typeof(Inventory), "Changed")] internal static class ChallengeHubTradeChestInventoryChangedPatch { [HarmonyPostfix] private static void Postfix(Inventory __instance) { PartnershipGameplayFeature.NotifyTradeInventoryChanged(__instance); } } [HarmonyPatch(typeof(Character), "Damage")] internal static class ChallengeHubPeaceViolationDamagePatch { private static void Prefix(Character __instance, HitData hit) { try { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (!((Object)(object)val == (Object)null)) { PartnershipGameplayFeature.TryHandlePeaceDamage(val, hit); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("PeaceViolationDamagePatch fehlgeschlagen: " + ex.Message)); } } } } [HarmonyPatch(typeof(Player), "OnDeath")] internal static class PlayerOnDeathPatch { private static void Prefix(Player __instance, out DeathInventoryIntegrityFeature.DeathSnapshot __state) { __state = DeathInventoryIntegrityFeature.Capture(__instance); try { if (!((Object)(object)__instance == (Object)null) && ((Character)__instance).IsOwner()) { CharacterAdmissionFeature.NotifyLocalPlayerDeath(__instance); if (!DeathRunCounterFeature.Enabled) { CombatGoalFeature.NotifyPlayerDeath(__instance); } Plugin.Instance?.ReportDeath(__instance); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Death-Patch fehlgeschlagen: " + ex.Message)); } } } private static void Postfix(Player __instance, DeathInventoryIntegrityFeature.DeathSnapshot __state) { DeathInventoryIntegrityFeature.VerifyAfterVanillaDeath(__instance, __state); } } [HarmonyPatch(typeof(Character), "Damage")] internal static class CharacterDamagePatch { private static void Prefix(Character __instance, HitData hit) { try { if ((Object)(object)__instance == (Object)null || hit == null) { return; } Plugin.Instance?.TrackLastHit(__instance, hit); Character attacker = hit.GetAttacker(); if (DeathRunCounterFeature.Enabled) { Player val = (Player)(object)((attacker is Player) ? attacker : null); if (val != null && ((Character)val).IsOwner()) { ObsReplayFeature.NotifyBossEncounter(__instance); } } if (!DeathRunCounterFeature.Enabled) { Player val2 = (Player)(object)((attacker is Player) ? attacker : null); if (val2 != null) { CombatGoalFeature.NotifyBossDamage(__instance, val2); } } if (Plugin.IsDamageScalingEnabled()) { Character attacker2 = hit.GetAttacker(); bool flag = __instance.IsPlayer(); bool flag2 = (Object)(object)attacker2 != (Object)null && attacker2.IsPlayer(); if (flag && !flag2) { ((DamageTypes)(ref hit.m_damage)).Modify(Plugin.Instance.IncomingDamageMultiplier()); } else if (!flag && flag2) { ((DamageTypes)(ref hit.m_damage)).Modify(Plugin.Instance.OutgoingDamageMultiplier()); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Damage-Scaling fehlgeschlagen: " + ex.Message)); } } } } [HarmonyPatch(typeof(Character), "OnDeath")] internal static class CharacterOnDeathPatch { private static void Postfix(Character __instance) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)__instance == (Object)null || __instance.IsPlayer()) { return; } if (DeathRunCounterFeature.Enabled) { ObsReplayFeature.NotifyBossDefeated(__instance); } if (!DeathRunCounterFeature.Enabled && IsBoss(__instance)) { CombatGoalFeature.NotifyBossDeath(__instance); } Player val = FindNearestPlayer(((Component)__instance).transform.position); KillAttribution killAttribution = Plugin.Instance?.RegisterCreatureDeath(__instance, val); Player val2 = killAttribution?.KillerPlayer ?? val; if (!((Object)(object)val2 != (Object)null) || (!((Character)val2).IsOwner() && (!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer()))) { return; } if (IsBoss(__instance)) { if (DeathRunCounterFeature.Enabled && (Object)(object)val2 != (Object)null) { Plugin.Instance?.ReportBossKill(val2, __instance, killAttribution); return; } if (killAttribution != null && string.Equals(killAttribution.Method, "last_hit", StringComparison.OrdinalIgnoreCase) && (Object)(object)killAttribution.KillerPlayer != (Object)null) { Plugin.Instance?.ReportBossKill(killAttribution.KillerPlayer, __instance, killAttribution); return; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Bossabschluss nicht gewertet: kein serverseitig bestätigter Spieler-Last-Hit."); } } else { Plugin.Instance?.ReportCreatureKill(val2, __instance, killAttribution); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Boss-Kill-Patch fehlgeschlagen: " + ex.Message)); } } } private static bool IsBoss(Character character) { try { if (character.m_boss) { return true; } } catch { } string text = (((Object)character).name ?? "").ToLowerInvariant(); if (!text.Contains("eikthyr") && !text.Contains("gd_king") && !text.Contains("bonemass") && !text.Contains("dragon") && !text.Contains("goblinking") && !text.Contains("queen")) { return text.Contains("fader"); } return true; } internal static Player FindNearestPlayer(Vector3 position) { //IL_0026: 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) List allPlayers = Player.GetAllPlayers(); Player result = null; float num = float.MaxValue; foreach (Player item in allPlayers) { if (!((Object)(object)item == (Object)null)) { float num2 = Vector3.Distance(position, ((Component)item).transform.position); if (num2 < num) { result = item; num = num2; } } } return result; } } [HarmonyPatch] internal static class FarmerGuardianPlayerInRangePatch { internal static IEnumerable TargetMethods() { foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(Player))) { if (!(declaredMethod == null) && !(declaredMethod.Name != "IsPlayerInRange") && !(declaredMethod.ReturnType != typeof(bool)) && declaredMethod.GetParameters().Any((ParameterInfo parameter) => parameter.ParameterType == typeof(Vector3))) { yield return declaredMethod; } } } private static void Postfix(ref bool __result, object[] __args) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) try { if (__result || (Object)(object)Plugin.Instance == (Object)null || __args == null) { return; } Vector3 position = Vector3.zero; bool flag = false; for (int i = 0; i < __args.Length; i++) { if (__args[i] is Vector3 val) { position = val; flag = true; break; } } if (flag && (Plugin.Instance.IsFarmerPresenceActiveAt(position) || Plugin.Instance.IsNeutralPresencePulseActiveAt(position))) { __result = true; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Bauer-Wächter Player.IsPlayerInRange-Patch fehlgeschlagen: " + ex.Message)); } } } } internal static class ServerDropAuthority { internal static bool IsAuthoritative(Component component) { try { if ((Object)(object)component == (Object)null) { return false; } if ((Object)(object)ZNet.instance == (Object)null) { return true; } if (ZNet.instance.IsServer()) { return true; } ZNetView component2 = component.GetComponent(); return (Object)(object)component2 != (Object)null && component2.IsOwner(); } catch { return false; } } } [HarmonyPatch] internal static class CharacterDropItemsPatch { internal static IEnumerable TargetMethods() { Type type = AccessTools.TypeByName("CharacterDrop"); if (!(type == null)) { MethodInfo methodInfo = AccessTools.Method(type, "DropItems", (Type[])null, (Type[])null); if (methodInfo != null) { yield return methodInfo; } } } private static void Prefix(object __instance) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) try { Component val = (Component)((__instance is Component) ? __instance : null); if (ServerDropAuthority.IsAuthoritative(val)) { Character val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if (!((Object)(object)val2 == (Object)null) && !val2.IsPlayer()) { Player fallbackPlayer = CharacterOnDeathPatch.FindNearestPlayer(((Component)val2).transform.position); Plugin.Instance?.RegisterCreatureDeath(val2, fallbackPlayer); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Drop-Spawn-Kontext konnte nicht vorbereitet werden: " + ex.Message)); } } } } [HarmonyPatch] internal static class ItemDropSpawnPatch { internal static IEnumerable TargetMethods() { MethodInfo methodInfo = AccessTools.Method(typeof(ItemDrop), "Awake", (Type[])null, (Type[])null); if (methodInfo != null) { yield return methodInfo; } MethodInfo methodInfo2 = AccessTools.Method(typeof(ItemDrop), "Start", (Type[])null, (Type[])null); if (methodInfo2 != null) { yield return methodInfo2; } } private static void Postfix(ItemDrop __instance) { try { if (ServerDropAuthority.IsAuthoritative((Component)(object)__instance)) { Plugin.Instance?.ReportDropSpawn(__instance); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Drop-Spawn-Patch fehlgeschlagen: " + ex.Message)); } } } } internal static class PhysicalItemPickupContext { [ThreadStatic] private static int _depth; [ThreadStatic] private static ItemDrop _drop; [ThreadStatic] private static Player _player; internal static void Enter(Humanoid character, GameObject groundObject) { Player val = (Player)(object)((character is Player) ? character : null); ItemDrop val2 = (((Object)(object)groundObject != (Object)null) ? groundObject.GetComponent() : null); ZNetView val3 = (((Object)(object)groundObject != (Object)null) ? groundObject.GetComponent() : null); bool flag = (Object)(object)val != (Object)null && (Object)(object)val2 != (Object)null && (Object)(object)val3 != (Object)null; if (flag) { try { flag = val3.IsValid() && val3.GetZDO() != null; } catch { flag = false; } } _depth++; if (_depth == 1) { _drop = (flag ? val2 : null); _player = (flag ? val : null); } } internal static void Exit() { if (_depth > 0) { _depth--; } if (_depth == 0) { _drop = null; _player = null; } } internal static bool IsPhysicalPickup(Player player, ItemData item) { if (_depth <= 0 || (Object)(object)_drop == (Object)null || (Object)(object)_player == (Object)null || (Object)(object)player == (Object)null || item == null) { return false; } if ((Object)(object)_player != (Object)(object)player) { return false; } try { ItemData itemData = _drop.m_itemData; if (itemData == null || itemData.m_shared == null || item.m_shared == null) { return true; } if (itemData == item) { return true; } object obj = (((Object)(object)itemData.m_dropPrefab != (Object)null) ? ((Object)itemData.m_dropPrefab).name : itemData.m_shared.m_name); string text = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : item.m_shared.m_name); if (obj == null) { obj = string.Empty; } return string.Equals((string?)obj, text ?? string.Empty, StringComparison.OrdinalIgnoreCase); } catch { return true; } } } internal static class HumanoidPhysicalPickupContextPatch { internal static void Patch(Harmony harmony) { //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Expected O, but got Unknown //IL_01b3: Expected O, but got Unknown //IL_01b3: Expected O, but got Unknown if (harmony == null) { return; } MethodInfo methodInfo = AccessTools.Method(typeof(HumanoidPhysicalPickupContextPatch), "Prefix", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(HumanoidPhysicalPickupContextPatch), "Postfix", (Type[])null, (Type[])null); MethodInfo methodInfo3 = AccessTools.Method(typeof(HumanoidPhysicalPickupContextPatch), "Finalizer", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null || methodInfo3 == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Physischer Pickup-Kontext: Patchmethoden nicht gefunden; item_pickup bleibt sicher deaktiviert."); } return; } HashSet hashSet = new HashSet(); Type[] array = new Type[2] { typeof(Humanoid), typeof(Player) }; foreach (Type type in array) { MethodInfo[] methods; try { methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } catch { continue; } MethodInfo[] array2 = methods; foreach (MethodInfo methodInfo4 in array2) { if (!(methodInfo4 == null) && !(methodInfo4.ReturnType != typeof(bool)) && (string.Equals(methodInfo4.Name, "Pickup", StringComparison.Ordinal) || methodInfo4.Name.EndsWith(".Pickup", StringComparison.Ordinal))) { ParameterInfo[] parameters = methodInfo4.GetParameters(); if (parameters.Length != 0 && parameters.Any((ParameterInfo item) => item.ParameterType == typeof(GameObject))) { hashSet.Add(methodInfo4); } } } } int num = 0; foreach (MethodBase item in hashSet) { try { harmony.Patch(item, new HarmonyMethod(methodInfo), new HarmonyMethod(methodInfo2), (HarmonyMethod)null, new HarmonyMethod(methodInfo3), (HarmonyMethod)null); num++; } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Physischer Pickup-Kontext: optionale Signatur konnte nicht gepatcht werden: " + item?.ToString() + " -> " + ex.Message)); } } } if (num > 0) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Physischer Pickup-Kontext 2.2.30 kompatibel registriert: " + num + " Methode(n).")); } } else { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)"Physischer Pickup-Kontext: keine kompatible Pickup(GameObject)-Signatur gefunden; item_pickup-POSTs werden nicht aus internen Inventarwegen erzeugt, restliche Mod-Patches bleiben aktiv."); } } } private static void Prefix(Humanoid __instance, object[] __args) { GameObject groundObject = null; if (__args != null) { foreach (object obj in __args) { GameObject val = (GameObject)((obj is GameObject) ? obj : null); if (val != null) { groundObject = val; break; } } } PhysicalItemPickupContext.Enter(__instance, groundObject); } private static void Postfix() { PhysicalItemPickupContext.Exit(); } private static Exception Finalizer(Exception __exception) { if (__exception != null) { PhysicalItemPickupContext.Exit(); } return __exception; } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })] internal static class InventoryAddItemPatch { private static void Postfix(ItemData item, bool __result) { try { if (!__result || item == null || item.m_shared == null) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && (((Character)localPlayer).IsOwner() || ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())) && PhysicalItemPickupContext.IsPhysicalPickup(localPlayer, item)) { Plugin.Instance?.ReportItemPickup(localPlayer, item); if (((((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : item.m_shared.m_name) ?? "").ToLowerInvariant().Contains("trophy")) { Plugin.Instance?.ReportTrophy(localPlayer, item); DeathRunWorldRulesFeature.NotifyTrophyChanged(localPlayer); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Physischer Item-Pickup-Patch fehlgeschlagen: " + ex.Message)); } } } } [HarmonyPatch(typeof(ZNetScene), "Awake")] internal static class ChallengeHubGuardianZNetScenePatch { private static void Postfix(ZNetScene __instance) { try { GuardianStonePieces.RegisterZNetScene(__instance, "ZNetScene.Awake"); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Guardian-ZNetScene-Patch fehlgeschlagen: " + ex.Message)); } } } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class ChallengeHubGuardianObjectDbPatch { private static void Postfix(ObjectDB __instance) { try { GuardianStonePieces.RegisterObjectDb(__instance, "ObjectDB.Awake"); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Guardian-ObjectDB-Patch fehlgeschlagen: " + ex.Message)); } } } } [HarmonyPatch(typeof(Piece), "Awake")] internal static class ChallengeHubGuardianPieceAwakePatch { private static void Postfix(Piece __instance) { try { GuardianStoneProtectionFeature.HandlePieceAwake(__instance); GuardianStoneRuntimeCache.Register(__instance, "Piece.Awake"); if (!string.IsNullOrWhiteSpace(GuardianStoneProtectionFeature.DetectGuardianType(((Component)__instance).gameObject)) && (Object)(object)((Component)__instance).GetComponent() == (Object)null) { ((Component)__instance).gameObject.AddComponent(); } Plugin.Instance?.RegisterGuardianPieceRuntime(__instance); Plugin.Instance?.ReportGuardianStoneChanged(__instance, "piece_awake"); CommunityWorldStationFeature.ReportPlaced(__instance); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Guardian-Piece-Awake-Patch fehlgeschlagen: " + ex.Message)); } } } } internal static class ChallengeHubGuardianPieceStartPatchDisabled { internal static void Disabled(Piece __instance) { try { Plugin.Instance?.ReportGuardianStoneChanged(__instance, "piece_start_disabled"); } catch { } } } [HarmonyPatch(typeof(Player), "Update")] internal static class FarmerGuardianChestBindHotkeyPatch { private static void Postfix(Player __instance) { try { if (!((Object)(object)__instance == (Object)null) && ((Character)__instance).IsOwner()) { Plugin.Instance?.TryHandleFarmerBindingHotkey(__instance); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Bauer-Wächter STRG+K Patch fehlgeschlagen: " + ex.Message)); } } } } internal static class ChallengeHubZNetSceneRemoveObjectsNullGuardPatch { private static float _lastWarningTime; private static int _suppressedWarnings; internal static void Patch(Harmony harmony) { //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown //IL_00d1: Expected O, but got Unknown if (harmony == null) { return; } MethodInfo methodInfo = AccessTools.Method(typeof(ChallengeHubZNetSceneRemoveObjectsNullGuardPatch), "Prefix", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(ChallengeHubZNetSceneRemoveObjectsNullGuardPatch), "Finalizer", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"ZNetScene.RemoveObjects-Schutz konnte nicht vorbereitet werden: Prefix oder Finalizer fehlt."); } return; } MethodInfo[] array = (from method in typeof(ZNetScene).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where string.Equals(method.Name, "RemoveObjects", StringComparison.Ordinal) && method.GetParameters().Length == 2 select method).ToArray(); if (array.Length == 0) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"ZNetScene.RemoveObjects-Schutz uebersprungen: keine passende Methode gefunden."); } return; } MethodInfo[] array2 = array; foreach (MethodInfo methodInfo3 in array2) { harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null); } ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("ZNetScene.RemoveObjects-Schutz aktiv: " + array.Length + " Methode(n), proaktive ZDO-Listenbereinigung und gezielter NRE-Instanz-Finalizer.")); } } private static void Prefix(object[] __args) { try { CleanupArgumentLists(__args); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("ZNetScene.RemoveObjects-Prefix Argumentbereinigung fehlgeschlagen: " + ex.Message)); } } } internal static int RepairNow(ZNetScene scene = null) { try { return CleanupSceneInstanceTables(((Object)(object)scene != (Object)null) ? scene : ZNetScene.instance); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("ZNetScene-Sofortreparatur fehlgeschlagen: " + ex.Message)); } return 0; } } private static Exception Finalizer(ZNetScene __instance, object[] __args, Exception __exception) { if (__exception == null) { return null; } if (!(__exception is NullReferenceException)) { return __exception; } int num = 0; try { num += CleanupArgumentLists(__args); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("ZNetScene.RemoveObjects-Argumentbereinigung fehlgeschlagen: " + ex.Message)); } } try { num += CleanupSceneInstanceTables(__instance); } catch (Exception ex2) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogDebug((object)("ZNetScene.RemoveObjects-Tabellenbereinigung fehlgeschlagen: " + ex2.Message)); } } LogCleanup(num, "NullReferenceException abgefangen; defekte Streaming-Referenzen entfernt"); return null; } private static int CleanupArgumentLists(object[] args) { if (args == null) { return 0; } int num = 0; for (int i = 0; i < args.Length; i++) { if (!(args[i] is IList list)) { continue; } for (int num2 = list.Count - 1; num2 >= 0; num2--) { object obj = null; try { obj = list[num2]; } catch { try { list.RemoveAt(num2); num++; } catch { } continue; } if (IsBrokenSceneInstance(obj)) { try { list.RemoveAt(num2); num++; } catch { } } } } return num; } private static int CleanupSceneInstanceTables(ZNetScene scene) { if ((Object)(object)scene == (Object)null) { return 0; } int num = 0; FieldInfo[] fields = typeof(ZNetScene).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.Name.IndexOf("instance", StringComparison.OrdinalIgnoreCase) < 0) { continue; } object obj = null; try { obj = fieldInfo.GetValue(scene); } catch { continue; } if (!(obj is IDictionary dictionary)) { continue; } List list = null; foreach (DictionaryEntry item in dictionary) { if (IsBrokenSceneInstance(item.Key) || IsBrokenSceneInstance(item.Value)) { if (list == null) { list = new List(); } list.Add(item.Key); } } if (list == null) { continue; } foreach (object item2 in list) { try { dictionary.Remove(item2); num++; } catch { } } } return num; } private static bool IsBrokenSceneInstance(object value) { if (value == null) { return true; } Object val = (Object)((value is Object) ? value : null); if (val != null) { try { if (val == (Object)null) { return true; } } catch { return true; } } ZNetView val2 = (ZNetView)((value is ZNetView) ? value : null); if (val2 != null) { try { if ((Object)(object)val2 == (Object)null || (Object)(object)((Component)val2).gameObject == (Object)null || !val2.IsValid() || val2.GetZDO() == null) { return true; } } catch { return true; } } Component val3 = (Component)((value is Component) ? value : null); if (val3 != null) { try { if ((Object)(object)val3 == (Object)null || (Object)(object)val3.gameObject == (Object)null) { return true; } } catch { return true; } } GameObject val4 = (GameObject)((value is GameObject) ? value : null); if (val4 != null) { try { if ((Object)(object)val4 == (Object)null) { return true; } } catch { return true; } } return false; } private static void LogCleanup(int removed, string reason) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup - _lastWarningTime < 5f) { _suppressedWarnings++; return; } string text = ((_suppressedWarnings > 0) ? (" (" + _suppressedWarnings + " weitere Meldungen gebuendelt)") : string.Empty); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("ZNetScene.RemoveObjects-Notfallschutz: " + reason + "; entfernt=" + removed + text + ".")); } _suppressedWarnings = 0; _lastWarningTime = realtimeSinceStartup; } } [HarmonyPatch(typeof(Player), "PlacePiece", new Type[] { typeof(Piece), typeof(Vector3), typeof(Quaternion), typeof(bool) })] [HarmonyPriority(800)] internal static class PlacementMaterialTransactionFeature { internal sealed class PlacementSnapshot { internal Player Player; internal long PlayerId; internal Vector3 Position; internal string PieceName; internal int CandidateInstanceId; internal bool TerrainAction; internal bool OriginalRan; internal readonly List TerrainSamples = new List(); internal readonly List TerrainHeights = new List(); internal readonly HashSet ExistingPieces = new HashSet(); internal readonly Dictionary Amounts = new Dictionary(StringComparer.Ordinal); internal readonly Dictionary Templates = new Dictionary(StringComparer.Ordinal); } private static void Prefix(Player __instance, Piece piece, Vector3 pos, out PlacementSnapshot __state) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) __state = null; try { if ((Object)(object)__instance == (Object)null || !((Character)__instance).IsOwner() || (Object)(object)piece == (Object)null) { return; } GameObject placementGhost = PlayerBuildReflection.GetPlacementGhost(__instance); Vector3 position = (((Object)(object)placementGhost != (Object)null) ? placementGhost.transform.position : pos); PlacementSnapshot placementSnapshot = new PlacementSnapshot { Player = __instance, PlayerId = __instance.GetPlayerID(), Position = position, PieceName = CanonicalPieceName(((Object)((Component)piece).gameObject).name) }; if (string.Equals(placementSnapshot.PieceName, "raise_v2", StringComparison.OrdinalIgnoreCase)) { return; } placementSnapshot.TerrainAction = (Object)(object)((Component)piece).GetComponent() != (Object)null || (Object)(object)((Component)piece).GetComponentInChildren(true) != (Object)null; if (placementSnapshot.TerrainAction) { return; } HashSet hashSet = new HashSet(from requirement in piece.m_resources ?? Array.Empty() where requirement != null && requirement.m_amount > 0 && requirement.m_resItem?.m_itemData != null select ItemKey(requirement.m_resItem.m_itemData), StringComparer.Ordinal); if (hashSet.Count == 0) { return; } foreach (Piece item in NearbyPieces(position, 5f)) { placementSnapshot.ExistingPieces.Add(((Object)item).GetInstanceID()); } Inventory inventory = ((Humanoid)__instance).GetInventory(); foreach (ItemData item2 in ((inventory == null) ? null : inventory.GetAllItems()?.ToList()) ?? new List()) { if (item2 == null || item2.m_stack <= 0) { continue; } string text = ItemKey(item2); if (hashSet.Contains(text)) { placementSnapshot.Amounts[text] = (placementSnapshot.Amounts.TryGetValue(text, out var value) ? value : 0) + item2.m_stack; if (!placementSnapshot.Templates.ContainsKey(text)) { placementSnapshot.Templates[text] = item2.Clone(); } } } __state = placementSnapshot; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Bau-Transaktion konnte nicht gestartet werden: " + ex.Message)); } } } private static void Postfix(PlacementSnapshot __state, bool __runOriginal) { if (!((Object)(object)__state?.Player == (Object)null) && !((Object)(object)Plugin.Instance == (Object)null)) { __state.OriginalRan = __runOriginal; ((MonoBehaviour)Plugin.Instance).StartCoroutine(VerifyPlacement(__state)); } } private static IEnumerator VerifyPlacement(PlacementSnapshot snapshot) { float deadline = Time.realtimeSinceStartup + 3f; while (Time.realtimeSinceStartup < deadline) { if (snapshot.CandidateInstanceId == 0) { BindCreatedCandidate(snapshot); } yield return (object)new WaitForSeconds(0.15f); } if (WasPlaced(snapshot)) { yield break; } Inventory val = (((Object)(object)snapshot.Player != (Object)null) ? ((Humanoid)snapshot.Player).GetInventory() : null); if (val == null) { yield break; } Dictionary dictionary = Counts(val); int num = 0; foreach (KeyValuePair amount in snapshot.Amounts) { int value; int num2 = (dictionary.TryGetValue(amount.Key, out value) ? value : 0); int num3 = amount.Value - num2; if (num3 <= 0 || !snapshot.Templates.TryGetValue(amount.Key, out var value2)) { continue; } int num4 = num3; int val2 = Math.Max(1, value2.m_shared?.m_maxStackSize ?? num3); while (num4 > 0) { int num5 = Math.Min(num4, val2); ItemData val3 = value2.Clone(); val3.m_stack = num5; val3.m_equipped = false; if (!val.AddItem(val3)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("Bau-Transaktion: Rückerstattung konnte nicht ins Inventar gelegt werden: " + amount.Key + " x" + num4 + ".")); } break; } num += num5; num4 -= num5; } } if (num > 0) { try { AccessTools.Method(((object)val).GetType(), "Changed", (Type[])null, (Type[])null)?.Invoke(val, null); } catch { } try { ((Character)snapshot.Player).Message((MessageType)2, "Bau fehlgeschlagen – " + num + " Materialeinheiten wurden zurückerstattet.", 0, (Sprite)null); } catch { } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Bau-Transaktion zurückgerollt: Teil=" + snapshot.PieceName + "; Einheiten=" + num + ".")); } } } private static bool WasPlaced(PlacementSnapshot snapshot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) foreach (Piece item in NearbyPieces(snapshot.Position, 5f)) { if (IsMatchingFusionProxy(item, snapshot)) { return true; } if ((snapshot.CandidateInstanceId != 0 && ((Object)item).GetInstanceID() != snapshot.CandidateInstanceId) || snapshot.CandidateInstanceId == 0 || snapshot.ExistingPieces.Contains(((Object)item).GetInstanceID()) || !string.Equals(CanonicalPieceName(((Object)((Component)item).gameObject).name), snapshot.PieceName, StringComparison.OrdinalIgnoreCase)) { continue; } try { long creator = item.GetCreator(); if (creator != 0L && snapshot.PlayerId != 0L && creator != snapshot.PlayerId) { continue; } } catch { } ZNetView component = ((Component)item).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if (val != null && val.IsValid()) { return true; } } return false; } private static bool IsMatchingFusionProxy(Piece candidate, PlacementSnapshot snapshot) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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_0022: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)candidate == (Object)null) && snapshot != null) { Vector3 val = ((Component)candidate).transform.position - snapshot.Position; if (!(((Vector3)(ref val)).sqrMagnitude > 4f)) { if (!((Component)candidate).GetComponents().Any((Component component) => (Object)(object)component != (Object)null && string.Equals(((object)component).GetType().FullName, "Zeitsurfer.AutoConstructFusion.VirtualPartProxy", StringComparison.Ordinal))) { return false; } string text = ((Object)((Component)candidate).gameObject).name ?? string.Empty; int num = text.IndexOf("_Virtual_", StringComparison.OrdinalIgnoreCase); if (num >= 0) { text = text.Substring(0, num); } return string.Equals(CanonicalPieceName(text), snapshot.PieceName, StringComparison.OrdinalIgnoreCase); } } return false; } private static void CaptureTerrain(PlacementSnapshot snapshot) { //IL_0005: 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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //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_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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_0095: 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) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) if (snapshot != null) { Vector3 position = snapshot.Position; Vector3[] array = (Vector3[])(object)new Vector3[5] { Vector3.zero, new Vector3(0.75f, 0f, 0f), new Vector3(-0.75f, 0f, 0f), new Vector3(0f, 0f, 0.75f), new Vector3(0f, 0f, -0.75f) }; foreach (Vector3 val in array) { Vector3 val2 = position + val; snapshot.TerrainSamples.Add(val2); snapshot.TerrainHeights.Add(GroundHeight(val2)); } } } private static bool TerrainChanged(PlacementSnapshot snapshot) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (snapshot == null || snapshot.TerrainSamples.Count == 0 || snapshot.TerrainSamples.Count != snapshot.TerrainHeights.Count) { return false; } for (int i = 0; i < snapshot.TerrainSamples.Count; i++) { float num = snapshot.TerrainHeights[i]; float num2 = GroundHeight(snapshot.TerrainSamples[i]); if (!float.IsNaN(num) && !float.IsNaN(num2) && Mathf.Abs(num2 - num) >= 0.025f) { return true; } } return false; } private static float GroundHeight(Vector3 position) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) try { float result = default(float); if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGroundHeight(position, ref result)) { return result; } } catch { } return float.NaN; } private static void BindCreatedCandidate(PlacementSnapshot snapshot) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) Piece val = (from piece in NearbyPieces(snapshot.Position, 5f) where !snapshot.ExistingPieces.Contains(((Object)piece).GetInstanceID()) && string.Equals(CanonicalPieceName(((Object)((Component)piece).gameObject).name), snapshot.PieceName, StringComparison.OrdinalIgnoreCase) orderby Vector3.Distance(((Component)piece).transform.position, snapshot.Position) select piece).FirstOrDefault(); if ((Object)(object)val != (Object)null) { snapshot.CandidateInstanceId = ((Object)val).GetInstanceID(); } } private static IEnumerable NearbyPieces(Vector3 position, float radius) { //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) float squared = radius * radius; return Object.FindObjectsByType((FindObjectsSortMode)0).Where(delegate(Piece piece) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_001f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)piece != (Object)null) { Vector3 val = ((Component)piece).transform.position - position; return ((Vector3)(ref val)).sqrMagnitude <= squared; } return false; }); } private static Dictionary Counts(Inventory inventory) { Dictionary dictionary = new Dictionary(StringComparer.Ordinal); foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem != null && allItem.m_stack > 0) { string key = ItemKey(allItem); dictionary[key] = (dictionary.TryGetValue(key, out var value) ? value : 0) + allItem.m_stack; } } return dictionary; } private static string ItemKey(ItemData item) { string text = (((Object)(object)item?.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : (item?.m_shared?.m_name ?? "unknown")); return text + "|" + (item?.m_quality ?? 0) + "|" + (item?.m_variant ?? 0); } private static string CanonicalPieceName(string value) { string text = value ?? string.Empty; int num = text.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase); if (num >= 0) { text = text.Substring(0, num); } return text.Trim(); } } internal sealed class PlayerLinkSetupFeature : MonoBehaviour { [Serializable] private sealed class BootstrapResponse { public bool ok; public string apiBaseUrl; public string challengeShortCode; public string apiKey; } private const int WindowId = 28102; private Plugin _plugin; private string _playerLinkCode = string.Empty; private string _apiBaseUrl = string.Empty; private string _challengeShortCode = string.Empty; private string _apiKey = string.Empty; private string _message = string.Empty; private bool _dismissedForSession; private bool _loadingChallenge; private Rect _window = new Rect(0f, 0f, 560f, 245f); internal static void Initialize(Plugin plugin) { if (!((Object)(object)plugin == (Object)null) && !((Object)(object)((Component)plugin).GetComponent() != (Object)null)) { PlayerLinkSetupFeature playerLinkSetupFeature = ((Component)plugin).gameObject.AddComponent(); playerLinkSetupFeature._plugin = plugin; playerLinkSetupFeature.LoadValues(); } } private void LoadValues() { _playerLinkCode = ((Plugin.PlayerLinkCode != null) ? (Plugin.PlayerLinkCode.Value ?? string.Empty) : string.Empty); _apiBaseUrl = ((Plugin.ApiBaseUrl != null) ? (Plugin.ApiBaseUrl.Value ?? string.Empty) : string.Empty); _challengeShortCode = ((Plugin.ChallengeShortCode != null) ? (Plugin.ChallengeShortCode.Value ?? string.Empty) : string.Empty); _apiKey = ((Plugin.ApiKey != null) ? (Plugin.ApiKey.Value ?? string.Empty) : string.Empty); } private bool ShouldShow() { if (_dismissedForSession || (Object)(object)_plugin == (Object)null || IsHeadlessServer()) { return false; } try { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsDedicated()) { return false; } } catch { return false; } if (HasRequiredGateConfiguration() && Plugin.PlayerLinkCode != null) { return string.IsNullOrWhiteSpace(Plugin.PlayerLinkCode.Value); } return true; } internal static bool HasRequiredGateConfiguration() { bool num = Plugin.ApiBaseUrl == null || string.IsNullOrWhiteSpace(Plugin.ApiBaseUrl.Value) || Plugin.ApiBaseUrl.Value.IndexOf("localhost", StringComparison.OrdinalIgnoreCase) >= 0; bool flag = Plugin.ChallengeShortCode == null || string.IsNullOrWhiteSpace(Plugin.ChallengeShortCode.Value); bool flag2 = Plugin.ApiKey == null || string.IsNullOrWhiteSpace(Plugin.ApiKey.Value) || string.Equals(Plugin.ApiKey.Value.Trim(), "CHANGE_ME", StringComparison.OrdinalIgnoreCase); if (!num && !flag) { return !flag2; } return false; } private static bool IsHeadlessServer() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 try { return Application.isBatchMode || (int)SystemInfo.graphicsDeviceType == 4; } catch { return false; } } private void OnGUI() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_006c: 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) if (ShouldShow()) { ((Rect)(ref _window)).x = ((float)Screen.width - ((Rect)(ref _window)).width) * 0.5f; ((Rect)(ref _window)).y = ((float)Screen.height - ((Rect)(ref _window)).height) * 0.5f; _window = GUI.Window(28102, _window, new WindowFunction(DrawWindow), "ChallengeHub – Spieler verknüpfen"); } } private void DrawWindow(int id) { //IL_010c: Unknown result type (might be due to invalid IL or missing references) GUILayout.Space(8f); GUILayout.Label("Dein VH-Code verbindet automatisch dein ChallengeHub-Konto und deinen Twitch-Namen.", Array.Empty()); GUILayout.Space(10f); GUILayout.Label("Die Challenge waehlst du ueber die Bilder rechts im Hauptmenue.", Array.Empty()); GUILayout.Space(8f); GUILayout.Label("Persoenlicher Player-Link-Code (z. B. VH-CRKPGMKJ)", Array.Empty()); _playerLinkCode = GUILayout.TextField(_playerLinkCode ?? string.Empty, 64, Array.Empty()).Trim().ToUpperInvariant(); if (!string.IsNullOrWhiteSpace(_message)) { GUILayout.Label(_message, Array.Empty()); } GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Speichern und verbinden", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(34f) })) { Save(); } if (GUILayout.Button("Diese Sitzung später", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(34f) })) { _dismissedForSession = true; } GUILayout.EndHorizontal(); GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _window)).width, 24f)); } private void Save() { string text = (_playerLinkCode ?? string.Empty).Trim().ToUpperInvariant(); if (string.IsNullOrWhiteSpace(text) || !text.StartsWith("VH-", StringComparison.OrdinalIgnoreCase)) { _message = "Bitte einen gültigen Player-Link-Code mit VH- eintragen."; return; } if (!HasRequiredGateConfiguration()) { _message = "BLUTEID-Verbindung wird automatisch eingerichtet ..."; if (!_loadingChallenge) { ((MonoBehaviour)this).StartCoroutine(SelectChallenge("BLUTEID", delegate { Save(); })); } return; } string text2 = (_apiBaseUrl ?? string.Empty).Trim().TrimEnd(new char[1] { '/' }); string value = (_challengeShortCode ?? string.Empty).Trim().ToUpperInvariant(); string text3 = (_apiKey ?? string.Empty).Trim(); if (!Uri.TryCreate(text2, UriKind.Absolute, out Uri result) || (result.Scheme != Uri.UriSchemeHttps && result.Scheme != Uri.UriSchemeHttp)) { _message = "Bitte eine gültige ChallengeHub-Webadresse eintragen."; return; } if (string.IsNullOrWhiteSpace(value)) { _message = "Bitte das Challenge-Kürzel eintragen."; return; } if (string.IsNullOrWhiteSpace(text3) || string.Equals(text3, "CHANGE_ME", StringComparison.OrdinalIgnoreCase)) { _message = "Bitte den API-Key dieser Challenge eintragen."; return; } if (string.IsNullOrWhiteSpace(text) || !text.StartsWith("VH-", StringComparison.OrdinalIgnoreCase)) { _message = "Bitte einen gültigen Player-Link-Code mit VH- eintragen."; return; } Plugin.PlayerLinkCode.Value = text; Plugin.ApiBaseUrl.Value = text2; Plugin.ChallengeShortCode.Value = value; Plugin.ApiKey.Value = text3; ((BaseUnityPlugin)_plugin).Config.Save(); _message = string.Empty; _dismissedForSession = true; Plugin.Log.LogInfo((object)"Persönliche PlayerLink-Konfiguration wurde im Spiel gespeichert."); try { MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, "ChallengeHub-Profil wurde lokal verknüpft.", 0, (Sprite)null, false); } } catch { } } internal void SelectDeathRunFromMainMenu(Action completed = null) { if (!_loadingChallenge) { ((MonoBehaviour)this).StartCoroutine(SelectChallenge("BLUTEID", completed)); } } private IEnumerator SelectChallenge(string challengeCode, Action completed = null) { _loadingChallenge = true; _message = string.Empty; UnityWebRequest request = UnityWebRequest.Get("https://challenge.zeitsurfer.de/api/valheim/bootstrap?challenge=" + UnityWebRequest.EscapeURL(challengeCode)); try { request.timeout = 15; yield return request.SendWebRequest(); if ((int)request.result != 1) { _message = "ChallengeHub konnte nicht erreicht werden. Bitte erneut versuchen."; } else { BootstrapResponse bootstrapResponse = JsonUtility.FromJson(request.downloadHandler.text); if (bootstrapResponse == null || !bootstrapResponse.ok || string.IsNullOrWhiteSpace(bootstrapResponse.apiKey)) { _message = "Challenge-Konfiguration konnte nicht geladen werden."; } else { _apiBaseUrl = bootstrapResponse.apiBaseUrl; _challengeShortCode = bootstrapResponse.challengeShortCode; _apiKey = bootstrapResponse.apiKey; Plugin.ApiBaseUrl.Value = _apiBaseUrl; Plugin.ChallengeShortCode.Value = _challengeShortCode; Plugin.ApiKey.Value = _apiKey; ((BaseUnityPlugin)_plugin).Config.Save(); _message = _challengeShortCode + " wurde automatisch eingerichtet."; } } } finally { ((IDisposable)request)?.Dispose(); } _loadingChallenge = false; completed?.Invoke(_message); } } [BepInPlugin("de.challengehub.valheim.bluteid", "ChallengeHub Valheim - Der Blut-Eid: Vorzeichen des Nordens", "2.12.50")] public sealed class Plugin : BaseUnityPlugin { private sealed class ZoneRuntimeState { public string ZoneId; public string ZoneName; public string Biome; public Vector3 Center; public DateTime LastSeenAt; public DateTime? LastResetAt; public string LastStatus; public DateTime LastReportedAt; public string ProtectedReason; public bool Protected; public string ZoneType; public string GuardianType; public string GuardianColor; public string GuardianRecipe; public DateTime? GuardianRemovedAt; } private sealed class FarmerPresenceAnchor { public string ZoneId; public Vector3 Center; public DateTime LastSeenAt; public string GuardianType; public string BoundChestId; public Vector3? BoundChestPosition; } private sealed class NeutralPresenceAnchor { public string ZoneId; public Vector3 Center; public DateTime LastSeenAt; public DateTime NextPulseAt; public DateTime PulseUntil; public DateTime LastPulseAt; } private sealed class FarmerFoodStack { public ItemDrop GroundItem; public object Inventory; public ItemData Item; public int Available; public string Source; } private sealed class FarmerFoodPool { public int Available; public readonly List Stacks = new List(); } public const string PluginGuid = "de.challengehub.valheim.bluteid"; public const string PluginName = "ChallengeHub Valheim - Der Blut-Eid: Vorzeichen des Nordens"; public const string PluginVersion = "2.12.50"; public const int ApiProtocolVersion = 2; public const int GoalDetectorVersion = 5; internal static Plugin Instance; internal static ManualLogSource Log; internal static Harmony HarmonyInstance; internal static ConfigEntry ApiBaseUrl; internal static ConfigEntry ApiKey; internal static ConfigEntry ChallengeShortCode; internal static ConfigEntry DefaultDifficulty; internal static ConfigEntry TwitchLogin; internal static ConfigEntry PlayerLinkCode; internal static ConfigEntry ServerId; internal static ConfigEntry WorldName; internal static ConfigEntry RequireValidChallengeHubServer; internal static ConfigEntry ServerGateFailClosed; internal static ConfigEntry ServerGateProtocolVersion; internal static ConfigEntry ServerGateVerificationTimeoutSeconds; internal static ConfigEntry ServerGateDisconnectDelaySeconds; internal static ConfigEntry ServerGateHeartbeatSeconds; internal static ConfigEntry ServerGateRegistrationSecret; internal static ConfigEntry BoundCharacterHardLock; internal static ConfigEntry WrongWorldWarningLimit; internal static ConfigEntry WrongWorldRequireAdminAfter; internal static ConfigEntry EnableCharacterAdmission; internal static ConfigEntry AdmissionWaitingPositionX; internal static ConfigEntry AdmissionWaitingPositionY; internal static ConfigEntry AdmissionWaitingPositionZ; internal static ConfigEntry AdmissionStartPositionX; internal static ConfigEntry AdmissionStartPositionY; internal static ConfigEntry AdmissionStartPositionZ; internal static ConfigEntry AdmissionWaitingRadius; internal static ConfigEntry AdmissionPollSeconds; internal static ConfigEntry CharacterCheckpointSeconds; internal static ConfigEntry AdmissionFailClosed; internal static ConfigEntry MapEnableLargeMap; internal static ConfigEntry MapEnableMinimap; internal static ConfigEntry MapRevealFogWhileWalking; internal static ConfigEntry MapRevealFogAtCartographyTable; internal static ConfigEntry EnableNoMap; internal static ConfigEntry EnableIngameChat; internal static ConfigEntry RequireExplorerPartnershipForCartographyShare; internal static ConfigEntry CartographyPartnerShareLevelPercentages; internal static ConfigEntry CartographyPartnerShareBasePoints; internal static ConfigEntry ReportCartographyPartnerShareEvents; internal static ConfigEntry DisableBossVegvisir; internal static ConfigEntry EnableDamageScaling; internal static ConfigEntry EnableZoneReset; internal static ConfigEntry EnableGuardianStones; internal static ConfigEntry EnableCustomGuardianStonePieces; internal static ConfigEntry GuardianStoneSourcePiecePrefab; internal static ConfigEntry GuardianStonePrefabNames; internal static ConfigEntry GuardianStoneDefaultRadius; internal static ConfigEntry GuardianStoneDefaultType; internal static ConfigEntry GuardianStoneTypeByPrefab; internal static ConfigEntry GuardianFarmerRecipe; internal static ConfigEntry GuardianExplorerRecipe; internal static ConfigEntry GuardianCombatRecipe; internal static ConfigEntry GuardianBuilderRecipe; internal static ConfigEntry GuardianNeutralRecipe; internal static ConfigEntry GuardianResetRecipe; internal static ConfigEntry GuardianFarmerPreventReset; internal static ConfigEntry GuardianExplorerPreventReset; internal static ConfigEntry GuardianCombatPreventReset; internal static ConfigEntry GuardianBuilderPreventReset; internal static ConfigEntry GuardianNeutralPreventReset; internal static ConfigEntry GuardianResetPreventReset; internal static ConfigEntry GuardianNeutralSimulatePlayerPresencePulse; internal static ConfigEntry GuardianNeutralPresencePulseIntervalMinutes; internal static ConfigEntry GuardianNeutralPresencePulseDurationSeconds; internal static ConfigEntry GuardianNeutralPresenceRadius; internal static ConfigEntry GuardianNeutralPresenceScanSeconds; internal static ConfigEntry GuardianNeutralPresencePulseOnFirstScan; internal static ConfigEntry GuardianNeutralReportPresencePulseEvents; internal static ConfigEntry GuardianFarmerSimulatePlayerPresence; internal static ConfigEntry GuardianFarmerPresenceRadius; internal static ConfigEntry GuardianFarmerPresenceScanSeconds; internal static ConfigEntry GuardianFarmerCatchupEnabled; internal static ConfigEntry GuardianFarmerMaxOfflineSimulationHours; internal static ConfigEntry GuardianFarmerTamingCatchupEnabled; internal static ConfigEntry GuardianFarmerBreedCatchupEnabled; internal static ConfigEntry GuardianFarmerBreedIntervalMinutes; internal static ConfigEntry GuardianFarmerMaxBabiesPerCatchup; internal static ConfigEntry GuardianFarmerMaxAnimalsPerZone; internal static ConfigEntry GuardianFarmerGrowthCatchupEnabled; internal static ConfigEntry GuardianFarmerFoodItems; internal static ConfigEntry GuardianFarmerTamingMinutesPerFood; internal static ConfigEntry GuardianFarmerFoodPerBaby; internal static ConfigEntry GuardianFarmerFoodPerGrowth; internal static ConfigEntry GuardianFarmerAllowBoundChest; internal static ConfigEntry GuardianFarmerReportSimulationEvents; internal static ConfigEntry EnableSkillTracking; internal static ConfigEntry EnableEvidenceTracking; internal static ConfigEntry EnableBuildEvents; internal static ConfigEntry EnableCraftEvents; internal static ConfigEntry EnableGatherEvents; internal static ConfigEntry EnableExplorationEvents; internal static ConfigEntry EnableMapViolationPenalty; internal static ConfigEntry HeartbeatSeconds; internal static ConfigEntry SkillScanSeconds; internal static ConfigEntry PortalScanSeconds; internal static ConfigEntry ZoneResetScanSeconds; internal static ConfigEntry AllowedActivePortalsPerBiome; internal static ConfigEntry ProtectedPrefabs; internal static ConfigEntry ResetPrefabKeywords; internal static ConfigEntry PersistRemoteConfigToLocalFile; internal static ConfigEntry ConfigManagerSafeMode; internal static ConfigEntry CameraHotkey; internal WebConfig RemoteConfig = WebConfig.Default(); private float _lastConfigPull; private readonly Dictionary> _seenTrophiesByBiome = new Dictionary>(); private readonly Dictionary _lastPortalReportByBiome = new Dictionary(); private readonly Dictionary _lastSkillLevels = new Dictionary(); private bool _sentStartSkills; private long _skillSnapshotPlayerId; private readonly KillAttributionTracker _killAttributionTracker = new KillAttributionTracker(); private readonly HashSet _reportedDropSpawnIds = new HashSet(); private readonly Dictionary _lastEventSentAt = new Dictionary(); private readonly Dictionary _zoneStates = new Dictionary(); private readonly Dictionary _farmerPresenceAnchors = new Dictionary(); private readonly Dictionary _neutralPresenceAnchors = new Dictionary(); private readonly Dictionary _runtimeGuardianPieces = new Dictionary(); private Piece _pendingFarmerChestBindPiece; private string _pendingFarmerChestBindZoneId = string.Empty; private float _pendingFarmerChestBindStartedAt; private int _lastFarmerChestBindingFrame = -1; private int _inflightPosts; private float _lastPostThrottleWarningAt; private int _suppressedPostThrottleWarnings; private bool _remoteConfigLoaded; private bool _remoteConfigRequestInFlight; private float _lastRemoteConfigCompletedAt = -9999f; private const float RemoteConfigOnDemandCooldownSeconds = 30f; private bool _playerStatusRequestInFlight; private float _lastPlayerStatusCompletedAt = -9999f; private const float PlayerStatusOnDemandCooldownSeconds = 1f; private bool _startupConfigDirty; private const bool IsDeathRunOnlyBuild = true; private const string ItemMetaPrefix = "ChallengeHub."; internal bool HasPendingFarmerChestBinding { get { if ((Object)(object)_pendingFarmerChestBindPiece != (Object)null) { return Time.realtimeSinceStartup - _pendingFarmerChestBindStartedAt <= 60f; } return false; } } internal bool FarmerChestBindingOwnsCurrentKey { get { if (!HasPendingFarmerChestBinding) { return _lastFarmerChestBindingFrame == Time.frameCount; } return true; } } internal string EventEndpoint => ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/api/valheim/event"; internal int InflightPostCount => _inflightPosts; private void Awake() { //IL_1075: Unknown result type (might be due to invalid IL or missing references) //IL_107f: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; ((BaseUnityPlugin)this).Logger.LogInfo((object)"ChallengeHub Startphase 1/5: ServerSync pruefen."); ServerSyncBridge.Initialize(((BaseUnityPlugin)this).Logger); bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; bool flag = IsDeathRunModeConfigured(); ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; ((BaseUnityPlugin)this).Logger.LogInfo((object)"ChallengeHub Startphase 2/5: Konfiguration gebuendelt laden."); try { ApiBaseUrl = BindSynced("ChallengeHub", "ApiBaseUrl", "http://localhost:3000", "Basis-URL deiner Webseite ohne Slash am Ende.", synchronized: true); ApiKey = BindSynced("ChallengeHub", "ApiKey", "CHANGE_ME", "Challenge/API-Key; wird als x-challengehub-key gesendet.", synchronized: true); ChallengeShortCode = BindSynced("ChallengeHub", "ChallengeShortCode", "BLUTEID", "Festes Kuerzel der Blut-Eid-Challenge.", synchronized: true); ChallengeShortCode.Value = "BLUTEID"; DefaultDifficulty = BindSynced("ChallengeHub", "DefaultDifficulty", "medium", "easy, medium oder hard, falls ein Spieler noch keine Kategorie auf der Webseite gewählt hat.", synchronized: true); TwitchLogin = BindSynced("PlayerLink", "TwitchLogin", "", "Dein Twitch-Login exakt wie in ChallengeHub. Wird zur Profil-Verknüpfung gesendet. Wird NICHT per ServerSync verteilt.", synchronized: false); PlayerLinkCode = BindSynced("PlayerLink", "PlayerLinkCode", "", "Persönlicher Valheim-Link-Code aus deinem ChallengeHub-Profil. Wird NICHT per ServerSync verteilt.", synchronized: false); ServerId = BindSynced("Server", "ServerId", "", "Eindeutige Server-ID/Name. Wird mit jedem Drop-/Kill-/Status-Event gesendet.", synchronized: true); WorldName = BindSynced("Server", "WorldName", "", "Name der Valheim-Welt. Wird mit jedem Server-Event gesendet.", synchronized: true); RequireValidChallengeHubServer = BindSynced("ServerGate", "RequireValidChallengeHubServer", defaultValue: true, "Wenn true, darf die Mod nur administrativ freigegebene ChallengeHub-Server/Welt-UID-Kombinationen betreten.", synchronized: true); ServerGateFailClosed = BindSynced("ServerGate", "FailClosed", defaultValue: true, "Wenn Server- oder Webnachweis fehlt, wird der Weltbeitritt verweigert.", synchronized: true); ServerGateProtocolVersion = BindSynced("ServerGate", "ProtocolVersion", 1, "ChallengeHub Server-Gate-Protokollversion. Nur bei gemeinsamem Client-/Serverupdate aendern.", synchronized: true); ServerGateVerificationTimeoutSeconds = BindSynced("ServerGate", "VerificationTimeoutSeconds", 10f, "Sekunden bis ein fehlender Servernachweis den Weltbeitritt verweigert.", synchronized: true); ServerGateDisconnectDelaySeconds = BindSynced("ServerGate", "DisconnectDelaySeconds", 3f, "Sekunden zwischen Fehlermeldung und sicherer Rueckkehr ins Hauptmenue.", synchronized: true); ServerGateHeartbeatSeconds = BindSynced("ServerGate", "ServerHeartbeatSeconds", 30f, "Sekunden zwischen Serverregistrierung/Lease-Erneuerung bei der Web-App.", synchronized: true); ServerGateRegistrationSecret = BindSynced("ServerGate", "RegistrationSecret", "", "Serverseitiges Gate-Geheimnis. Nicht an Clients verteilen. Leer verwendet vorlaeufig den Challenge-API-Key als Legacy-Fallback.", synchronized: false); BoundCharacterHardLock = BindSynced("ServerGate", "BoundCharacterHardLock", defaultValue: true, "Aktive Charaktere duerfen nur ihren gebundenen ChallengeHub-Server und die gebundene Welt-UID betreten.", synchronized: true); WrongWorldWarningLimit = BindSynced("ServerGate", "WrongWorldWarningLimit", 2, "Anzahl falscher Weltbeitritte, ab der deutlich verwarnt wird.", synchronized: true); WrongWorldRequireAdminAfter = BindSynced("ServerGate", "WrongWorldRequireAdminAfter", 3, "Anzahl falscher Weltbeitritte bis eine administrative Server-Gate-Pruefung erforderlich wird. 0 deaktiviert.", synchronized: true); EnableCharacterAdmission = BindSynced("Admission", "EnableCharacterAdmission", defaultValue: true, "Spieler- und Charakterfreigabe ueber ChallengeHub aktivieren.", synchronized: true); AdmissionWaitingPositionX = BindSynced("Admission", "WaitingPositionX", 0f, "Veraltet seit 2.8.0: nur fuer alte JSON-/CFG-Kompatibilitaet; beeinflusst weder Position noch Bewegung.", synchronized: true); AdmissionWaitingPositionY = BindSynced("Admission", "WaitingPositionY", 0f, "Veraltet seit 2.8.0: nur fuer alte JSON-/CFG-Kompatibilitaet; beeinflusst weder Position noch Bewegung.", synchronized: true); AdmissionWaitingPositionZ = BindSynced("Admission", "WaitingPositionZ", 0f, "Veraltet seit 2.8.0: nur fuer alte JSON-/CFG-Kompatibilitaet; beeinflusst weder Position noch Bewegung.", synchronized: true); AdmissionStartPositionX = BindSynced("Admission", "StartPositionX", 0f, "Veraltet seit 2.8.0: Freigabe teleportiert den Charakter nicht mehr.", synchronized: true); AdmissionStartPositionY = BindSynced("Admission", "StartPositionY", 0f, "Veraltet seit 2.8.0: Freigabe teleportiert den Charakter nicht mehr.", synchronized: true); AdmissionStartPositionZ = BindSynced("Admission", "StartPositionZ", 0f, "Veraltet seit 2.8.0: Freigabe teleportiert den Charakter nicht mehr.", synchronized: true); AdmissionWaitingRadius = BindSynced("Admission", "WaitingRadius", 0f, "Veraltet seit 2.8.0: wartende, gesperrte und abgelehnte Charaktere duerfen sich frei bewegen.", synchronized: true); AdmissionPollSeconds = BindSynced("Admission", "AdmissionPollSeconds", 8f, "Sekunden zwischen Freigabepruefungen in der Web-App.", synchronized: true); CharacterCheckpointSeconds = BindSynced("Admission", "CharacterCheckpointSeconds", 30f, "Sekunden zwischen serverseitigen Charakter-Snapshots.", synchronized: true); AdmissionFailClosed = BindSynced("Admission", "FailClosed", defaultValue: true, "Bei nicht erreichbarer Web-App bleibt nur die ChallengeHub-Wertung gesperrt; der Weltzugang und die Bewegung bleiben erlaubt.", synchronized: true); EnableNoMap = BindSynced("Gameplay", "EnableNoMap", defaultValue: false, "Vanilla-NoMap/ChallengeHub-NoMap. Fuer die neue vereinfachte Kartenlogik normalerweise false lassen.", synchronized: true); MapEnableLargeMap = BindSynced("Map", "EnableLargeMap", defaultValue: true, "Grosse Karte erlauben.", synchronized: true); MapEnableMinimap = BindSynced("Map", "EnableMinimap", defaultValue: false, "Kleine Minimap erlauben. Fuer den ChallengeHub-Kartentisch-Modus false.", synchronized: true); MapRevealFogWhileWalking = BindSynced("Map", "RevealFogWhileWalking", defaultValue: false, "Kartennebel beim normalen Laufen sichtbar aktualisieren.", synchronized: true); MapRevealFogAtCartographyTable = BindSynced("Map", "RevealFogAtCartographyTable", defaultValue: true, "Gepufferte Erkundung beim Lesen am Kartentisch auf die Karte uebertragen.", synchronized: true); EnableIngameChat = BindSynced("Gameplay", "EnableIngameChat", defaultValue: true, "Kompatibilitaetsschalter fuer Chat-Regeln: true laesst alle Slash-Commands zu; false blockiert nur gesperrte Slash-Commands (aktuell /printseeds). Normaler Player-Chat bleibt immer aktiv.", synchronized: true); RequireExplorerPartnershipForCartographyShare = BindSynced("Gameplay.CartographyPartnerShare", "RequireExplorerPartnership", defaultValue: true, "Wenn true, darf Kartenwissen am Kartentisch nur mit aktiver Entdecker-/Explorer-Partnerschaft geteilt werden.", synchronized: true); CartographyPartnerShareLevelPercentages = BindSynced("Gameplay.CartographyPartnerShare", "LevelRevealPercentages", "25,50,75,100", "Kartenteilungs-Anteil je Entdecker-Partnerschaftslevel. Beispiel: 25,50,75,100.", synchronized: true); CartographyPartnerShareBasePoints = BindSynced("Gameplay.CartographyPartnerShare", "BasePoints", 40, "Basispunkte fuer das Teilen von Kartenwissen am Kartentisch. Der effektive Wert wird mit dem Level-Anteil multipliziert.", synchronized: true); ReportCartographyPartnerShareEvents = BindSynced("Gameplay.CartographyPartnerShare", "ReportEvents", defaultValue: true, "Wenn true, sendet die Mod cartography_partner_share Events fuer App-Punkte und Partnerwertung.", synchronized: true); DisableBossVegvisir = BindSynced("Gameplay", "DisableBossVegvisir", defaultValue: true, "Boss-Vegvisir-Regel: Eikthyr blockiert auch die Richtungsanzeige; alle Boss-Orte werden nicht automatisch auf der Karte eingetragen.", synchronized: true); EnableDamageScaling = BindSynced("Gameplay", "EnableDamageScaling", defaultValue: false, "Schaden anhand der Kategorie skalieren.", synchronized: true); EnableZoneReset = BindSynced("WorldReset", "EnableZoneReset", defaultValue: false, "Veraltet: generelle Overworld-Zonenresets sind seit 2.8.0 dauerhaft deaktiviert.", synchronized: true); EnableZoneReset.Value = false; EnableGuardianStones = BindSynced("GuardianStones", "EnableGuardianStones", defaultValue: true, "Wächtersteine als Welt-Zonenanker erkennen. Ein platzierter Wächterstein erzeugt automatisch eine ChallengeHub-Zone.", synchronized: true); EnableCustomGuardianStonePieces = BindSynced("GuardianStones", "EnableCustomGuardianStonePieces", defaultValue: true, "Eigene ChallengeHub-Wächtersteine im Hammer-Menü registrieren. Freischaltung erfolgt über die jeweiligen Rezept-Materialien/Boss-/Biom-Fortschritt.", synchronized: true); GuardianStoneSourcePiecePrefab = BindSynced("GuardianStones", "SourcePiecePrefab", "dverger_guardstone", "Vanilla-Bauteil, das als Vorlage fuer die ChallengeHub-Waechtersteine geklont wird. Standard: dverger_guardstone. Fallbacks: Guard-/Ward-Stone, danach Schilder.", synchronized: true); MigrateGuardianStoneSourcePiecePrefabDefault(); GuardianStonePrefabNames = BindSynced("GuardianStones", "GuardianStonePrefabNames", "guard_stone,piece_guardstone,piece_ward,ward,piece_challengehub_guardian_farmer,piece_challengehub_guardian_explorer,piece_challengehub_guardian_combat,piece_challengehub_guardian_builder,piece_challengehub_guardian_neutral,piece_challengehub_guardian_reset", "Kommagetrennte Prefab-Namen, die als Wächterstein gelten. Standard ist Valheims Wächterstein/Ward.", synchronized: true); GuardianStoneDefaultRadius = BindSynced("GuardianStones", "DefaultRadius", 80f, "Standard-Radius eines Wächtersteins in Metern.", synchronized: true); GuardianStoneDefaultType = BindSynced("GuardianStones", "DefaultType", "farmer", "Standard-Typ für normale Valheim-Wächtersteine: farmer, explorer, combat, builder oder neutral.", synchronized: true); GuardianStoneTypeByPrefab = BindSynced("GuardianStones", "TypeByPrefab", "piece_challengehub_guardian_farmer:farmer,piece_challengehub_guardian_explorer:explorer,piece_challengehub_guardian_combat:combat,piece_challengehub_guardian_builder:builder,piece_challengehub_guardian_neutral:neutral,piece_challengehub_guardian_reset:reset", "Optionale Prefab:Typ-Zuordnung für spätere eigene Wächterstein-Prefabs.", synchronized: true); GuardianFarmerRecipe = BindSynced("GuardianStone.Farmer", "Recipe", "Stone:20,Wood:10,Resin:5,CarrotSeeds:1", "Materialdefinition für grünen Bauer-/Versorgungs-Wächterstein.", synchronized: true); GuardianExplorerRecipe = BindSynced("GuardianStone.Explorer", "Recipe", "Stone:15,Wood:10,Resin:5,Amber:1,Feathers:1", "Materialdefinition für blauen Entdecker-Wächterstein.", synchronized: true); GuardianCombatRecipe = BindSynced("GuardianStone.Combat", "Recipe", "Stone:25,Wood:10,Resin:5,BoneFragments:2,TrophyEikthyr:1", "Materialdefinition für roten Schutz-/Kampf-Wächterstein.", synchronized: true); GuardianBuilderRecipe = BindSynced("GuardianStone.Builder", "Recipe", "Stone:20,Wood:15,FineWood:5,Resin:2,BronzeNails:5", "Materialdefinition für gelben Aufbau-/Handwerk-Wächterstein.", synchronized: true); GuardianNeutralRecipe = BindSynced("GuardianStone.Neutral", "Recipe", "Stone:10,Wood:10,Resin:2", "Materialdefinition für neutralen Wächterstein.", synchronized: true); GuardianResetRecipe = BindSynced("GuardianStone.Reset", "Recipe", "Stone:20,Wood:10,Resin:5,SurtlingCore:1", "Materialdefinition für lila Reset-Wächterstein.", synchronized: true); GuardianFarmerPreventReset = BindSynced("GuardianStone.Farmer", "PreventReset", defaultValue: true, "Grün/Bauer schützt Farm- und Zähm-Zonen vor Reset.", synchronized: true); GuardianExplorerPreventReset = BindSynced("GuardianStone.Explorer", "PreventReset", defaultValue: false, "Blau/Entdecker markiert Routen/Orte. StandardmäÃÅÂÂ\u00b8ig wird Reset nicht dauerhaft blockiert.", synchronized: true); GuardianCombatPreventReset = BindSynced("GuardianStone.Combat", "PreventReset", defaultValue: true, "Rot/Schutz & Kampf schützt Kampf-/Bossvorbereitungszonen vor Reset.", synchronized: true); GuardianBuilderPreventReset = BindSynced("GuardianStone.Builder", "PreventReset", defaultValue: true, "Gelb/Aufbau & Handwerk schützt Bau-/Werkstattzonen vor Reset.", synchronized: true); GuardianNeutralPreventReset = BindSynced("GuardianStone.Neutral", "PreventReset", defaultValue: true, "WeiÃÅÂÂ\u00b8/Neutral schützt allgemeine KeepAlive-Zonen vor Reset.", synchronized: true); GuardianResetPreventReset = BindSynced("GuardianStone.Reset", "PreventReset", defaultValue: false, "Lila/Reset markiert einen gezielten Reset-Bereich; schützt selbst nicht dauerhaft vor Reset.", synchronized: true); GuardianResetPreventReset.Value = false; GuardianNeutralSimulatePlayerPresencePulse = BindSynced("GuardianStone.NeutralPresence", "EnableNeutralPresencePulse", defaultValue: true, "Wenn true, simuliert der neutrale Wächterstein alle X Minuten kurz Spieler-Präsenz im Radius. Kein echter Spieler wird gespawnt; nur Valheim-Checks wie PlayerInRange bekommen kurz true.", synchronized: true); GuardianNeutralPresencePulseIntervalMinutes = BindSynced("GuardianStone.NeutralPresence", "PulseIntervalMinutes", 15f, "Minuten zwischen zwei Neutral-Wächter Präsenz-Pulsen.", synchronized: true); GuardianNeutralPresencePulseDurationSeconds = BindSynced("GuardianStone.NeutralPresence", "PulseDurationSeconds", 5f, "Sekunden, die ein Neutral-Wächter pro Puls Spieler-Präsenz simuliert.", synchronized: true); GuardianNeutralPresenceRadius = BindSynced("GuardianStone.NeutralPresence", "PresenceRadius", 0f, "Radius in Metern für Neutral-Wächter-Präsenz. 0 nutzt GuardianStones.DefaultRadius.", synchronized: true); GuardianNeutralPresenceScanSeconds = BindSynced("GuardianStone.NeutralPresence", "ScanSeconds", 2f, "Sekunden zwischen Neutral-Wächter-Präsenzprüfungen. Niedrig halten, damit 5s-Pulse zuverlässig getroffen werden.", synchronized: true); GuardianNeutralPresencePulseOnFirstScan = BindSynced("GuardianStone.NeutralPresence", "PulseOnFirstScan", defaultValue: true, "Wenn true, startet ein neutraler Wächter nach Server-/Zonenladen sofort einen kurzen Präsenzpuls statt erst nach dem ersten Intervall.", synchronized: true); GuardianNeutralReportPresencePulseEvents = BindSynced("GuardianStone.NeutralPresence", "ReportPresencePulseEvents", defaultValue: true, "Wenn true, sendet die Mod neutral_guardian_presence_pulse Events an ChallengeHub.", synchronized: true); GuardianFarmerSimulatePlayerPresence = BindSynced("GuardianStone.Farmer", "SimulatePlayerPresence", defaultValue: true, "Wenn true, simuliert ein grüner Bauer-Wächter eine Spieler-Präsenz im Radius. Ziel: Farm-/Zähmzone aktiv halten, ohne einen echten Spieler zu spawnen.", synchronized: true); GuardianFarmerPresenceRadius = BindSynced("GuardianStone.Farmer", "PresenceRadius", 80f, "Radius in Metern, in dem der Bauer-Wächter Spieler-Präsenz für Farm-/Zähm-Logik simuliert. 0 nutzt DefaultRadius.", synchronized: true); GuardianFarmerPresenceScanSeconds = BindSynced("GuardianStone.Farmer", "PresenceScanSeconds", 5f, "Sekunden zwischen Bauer-Wächter-Präsenz-Scans. Niedriger = schneller, aber mehr Serverarbeit.", synchronized: true); GuardianFarmerCatchupEnabled = BindSynced("GuardianStone.FarmerSimulation", "EnableFarmerCatchup", defaultValue: true, "Wenn true, rechnet der Bauer-Wächter Zähmen/Nachwuchs beim Wiederladen der Zone nach.", synchronized: true); GuardianFarmerMaxOfflineSimulationHours = BindSynced("GuardianStone.FarmerSimulation", "MaxOfflineSimulationHours", 6f, "Maximal nachgerechnete Offline-/Abwesenheitsstunden pro Catch-up.", synchronized: true); GuardianFarmerTamingCatchupEnabled = BindSynced("GuardianStone.FarmerSimulation", "EnableTamingCatchup", defaultValue: true, "Wenn true, wird Zähmfortschritt in Bauer-Zonen nachgetragen.", synchronized: true); GuardianFarmerBreedCatchupEnabled = BindSynced("GuardianStone.FarmerSimulation", "EnableBreedCatchup", defaultValue: true, "Wenn true, versucht die Mod beim Wiederkommen verpassten Nachwuchs nachzuholen.", synchronized: true); GuardianFarmerBreedIntervalMinutes = BindSynced("GuardianStone.FarmerSimulation", "BreedIntervalMinutes", 30f, "Abstand, der fuer einen theoretischen Nachwuchs-Wurf gerechnet wird.", synchronized: true); GuardianFarmerMaxBabiesPerCatchup = BindSynced("GuardianStone.FarmerSimulation", "MaxBabiesPerCatchup", 3, "Maximale Anzahl Nachwuchs-Spawns pro Catch-up und Tier/Procreation-Komponente.", synchronized: true); GuardianFarmerMaxAnimalsPerZone = BindSynced("GuardianStone.FarmerSimulation", "MaxAnimalsPerFarmerZone", 12, "Maximale Tieranzahl im Bauer-Waechter-Radius, bevor Nachwuchs-Catch-up blockiert wird.", synchronized: true); GuardianFarmerGrowthCatchupEnabled = BindSynced("GuardianStone.FarmerSimulation", "EnableGrowthCatchup", defaultValue: true, "Wenn true, werden Babies/Jungtiere beim Wiederkommen zu ausgewachsenen Tieren nachgerechnet, sofern genug Nahrung vorhanden ist.", synchronized: true); GuardianFarmerFoodItems = BindSynced("GuardianStone.FarmerSimulation", "FoodItems", "Carrot,Turnip,Onion,Barley,Cloudberry,Raspberry,Blueberries,Mushroom,MushroomYellow,Dandelion,SeedsCarrot,CarrotSeeds,TurnipSeeds,OnionSeeds", "Kommagetrennte Item-Prefabs, die der Bauer-Wächter als Tierfutter fuer Zähmen/Nachwuchs/Wachstum verbrauchen darf.", synchronized: true); GuardianFarmerTamingMinutesPerFood = BindSynced("GuardianStone.FarmerSimulation", "TamingMinutesPerFood", 10f, "Wie viele Minuten Offline-Zähmfortschritt ein Futter-Item maximal nachtragen darf.", synchronized: true); GuardianFarmerFoodPerBaby = BindSynced("GuardianStone.FarmerSimulation", "FoodPerBaby", 1, "Futter-Verbrauch pro nachgeholtem Nachwuchs-Spawn.", synchronized: true); GuardianFarmerFoodPerGrowth = BindSynced("GuardianStone.FarmerSimulation", "FoodPerGrowth", 1, "Futter-Verbrauch pro nachgeholtem Erwachsenwerden eines Babies/Jungtiers.", synchronized: true); GuardianFarmerAllowBoundChest = BindSynced("GuardianStone.FarmerSimulation", "AllowBoundChest", defaultValue: true, "Wenn true, kann per STRG+K ein Farmer-Wächter und danach eine Kiste innerhalb seines Radius gebunden werden. Futter aus dieser Kiste zählt für Catch-up.", synchronized: true); GuardianFarmerReportSimulationEvents = BindSynced("GuardianStone.FarmerSimulation", "ReportFarmerSimulationEvents", defaultValue: true, "Wenn true, sendet die Mod farmer_simulation Events an ChallengeHub.", synchronized: true); EnableSkillTracking = BindSynced("Skills", "EnableSkillTracking", defaultValue: true, "Start-Skills und Skill-Veränderungen an ChallengeHub melden. Für die Challenge müssen Start-Skills 0 sein.", synchronized: true); EnableEvidenceTracking = BindSynced("Evidence", "EnableEvidenceTracking", defaultValue: true, "Zusätzliche Nachweis-Events für Goals senden: Bauen, Crafting, Sammeln, Erkunden.", synchronized: true); EnableBuildEvents = BindSynced("Evidence", "EnableBuildEvents", defaultValue: true, "Bau-Events an ChallengeHub senden.", synchronized: true); EnableCraftEvents = BindSynced("Evidence", "EnableCraftEvents", defaultValue: true, "Crafting-/Rezept-Events an ChallengeHub senden.", synchronized: true); EnableGatherEvents = BindSynced("Evidence", "EnableGatherEvents", defaultValue: true, "Sammel-/Rohstoff-/Pickable-Events an ChallengeHub senden.", synchronized: true); EnableExplorationEvents = BindSynced("Evidence", "EnableExplorationEvents", defaultValue: true, "Erkundungs-Nachweise wie Altäre/Händler/Biome an ChallengeHub senden, sofern erkennbar.", synchronized: true); EnableMapViolationPenalty = BindSynced("Evidence", "EnableMapViolationPenalty", defaultValue: false, "No-Map-VerstöÃÅÂÂ\u00b8e als Penalty-Event melden, wenn die Karte geöffnet wird.", synchronized: true); HeartbeatSeconds = BindSynced("Intervals", "HeartbeatSeconds", 20f, "Sekunden zwischen Live-Score-Heartbeats.", synchronized: true); SkillScanSeconds = BindSynced("Intervals", "SkillScanSeconds", 60f, "Sekunden zwischen Skill-Prüfungen.", synchronized: true); PortalScanSeconds = BindSynced("Intervals", "PortalScanSeconds", 120f, "Sekunden zwischen Portal-Scans.", synchronized: true); ZoneResetScanSeconds = BindSynced("Intervals", "ZoneResetScanSeconds", 3600f, "Veraltet seit 2.8.0: generelle Oberwelt-Zonenresets laufen nicht mehr; Feld bleibt nur fuer alte Konfigurationen.", synchronized: true); AllowedActivePortalsPerBiome = BindSynced("Rules", "AllowedActivePortalsPerBiome", 1, "Aktive Portale pro Biom, bevor Strafpunkte gemeldet werden.", synchronized: true); ProtectedPrefabs = BindSynced("WorldReset", "ProtectedPrefabs", "", "Veraltet seit 2.8.0: Oberwelt-Zonen werden nicht mehr zurueckgesetzt.", synchronized: true); ResetPrefabKeywords = BindSynced("WorldReset", "ResetPrefabKeywords", "", "Veraltet seit 2.8.0: nur Dungeons und exakt registrierte Ressourcen besitzen einen Lifecycle.", synchronized: true); PersistRemoteConfigToLocalFile = BindSynced("Advanced", "PersistRemoteConfigToLocalFile", defaultValue: false, "Wenn false, werden App/Admin-Werte nur im Speicher genutzt und nicht zurück in diese lokale .cfg geschrieben. Verhindert Auto-Änderungen der Datei durch den Config-Pull.", synchronized: false); ConfigManagerSafeMode = BindSynced("Advanced", "ConfigManagerSafeMode", defaultValue: true, "Zeigt im F1 Config Manager nur die wichtigsten ChallengeHub-Einstellungen. Alle erweiterten Werte bleiben in de.challengehub.valheim.cfg vorhanden. Verhindert Abstürze beim Öffnen des sehr groÃÅÂÂ\u00b8en Mod-Panels.", synchronized: false); CameraHotkey = BindSynced("Camera", "Hotkey", "F10", "Taste fuer die ChallengeHub-Ingame-Kamera.", synchronized: false); flag = true; if (flag) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"ChallengeHub DeathRunCounter-Profil erkannt: Community- und Kampagnenfunktionen bleiben deaktiviert."); } InitializeFeatureSafe("ServerGate", delegate { ChallengeHubServerGateFeature.Initialize(this); }); InitializeFeatureSafe("CursorController", delegate { ChallengeHubCursorController.Initialize(this); }); InitializeFeatureSafe("EventOutbox", delegate { ChallengeHubEventOutbox.Initialize(this); }); InitializeFeatureSafe("ApiToken", delegate { ChallengeHubApiTokenFeature.Initialize(this); }); InitializeFeatureSafe("IngameCamera", delegate { IngameCameraFeature.Initialize(this); }); InitializeFeatureSafe("CharacterAdmission", delegate { CharacterAdmissionFeature.Initialize(this); }); InitializeFeatureSafe("BlutEid", delegate { DeathRunCounterFeature.Initialize(this); }); InitializeFeatureSafe("BlutEidChronicle", delegate { DeathRunChronicleFeature.Initialize(this); }); InitializeFeatureSafe("BlutEidWorldRules", delegate { DeathRunWorldRulesFeature.Initialize(this); }); InitializeFeatureSafe("OBSReplay", delegate { ObsReplayFeature.Initialize(this); }); InitializeFeatureSafe("PlayerLinkSetup", delegate { PlayerLinkSetupFeature.Initialize(this); }); InitializeFeatureSafe("BlutEidStartup", delegate { StartupWelcomeFeature.Initialize(this); }); InitializeFeatureSafe("ConfigManagerCompatibility", delegate { ConfigManagerCompatibilityFeature.Apply(((BaseUnityPlugin)this).Config, ConfigManagerSafeMode == null || ConfigManagerSafeMode.Value); }); if (!flag) { InitializeFeatureSafe("Partnerships/SleepVote/ProtectedArea", delegate { PartnershipFeature.Initialize(this); }); InitializeFeatureSafe("PartnershipGameplay", delegate { PartnershipGameplayFeature.Initialize(this); }); InitializeFeatureSafe("GuardianStoneProtection", delegate { GuardianStoneProtectionFeature.Initialize(this); }); InitializeFeatureSafe("GuardianZoneAccess", delegate { GuardianZoneAccessFeature.Initialize(this); }); InitializeFeatureSafe("GuardianHover", delegate { GuardianHoverFeature.Initialize(this); }); InitializeFeatureSafe("TargetedReset", delegate { TargetedResetFeature.Initialize(this); }); InitializeFeatureSafe("DungeonResetLifecycle", delegate { DungeonResetLifecycleFeature.Initialize(this); }); InitializeFeatureSafe("DungeonTalentReward", delegate { DungeonTalentRewardFeature.Initialize(this); }); InitializeFeatureSafe("OrientationMarkers", delegate { OrientationMarkerFeature.Initialize(this); }); InitializeFeatureSafe("QoLSkillRuntime", delegate { QoLSkillRuntimeFeature.Initialize(this); }); InitializeFeatureSafe("AdvancedQoL", delegate { AdvancedQoLFeature.Initialize(this); }); InitializeFeatureSafe("GoalProgress", delegate { GoalProgressFeature.Initialize(this); }); InitializeFeatureSafe("WorldEvents", delegate { WorldEventGameplayFeature.Initialize(this); }); InitializeFeatureSafe("WorldEventConsole", delegate { WorldEventConsoleFeature.Initialize(this); }); InitializeFeatureSafe("AdminConsole", delegate { ChallengeHubAdminConsoleFeature.Initialize(this); }); InitializeFeatureSafe("CommunityWorldStations", delegate { CommunityWorldStationFeature.Initialize(this); }); InitializeFeatureSafe("StartupWelcome", delegate { StartupWelcomeFeature.Initialize(this); }); InitializeFeatureSafe("WorldIntroduction", delegate { WorldIntroductionFeature.Initialize(this); }); InitializeFeatureSafe("CombatGoals", delegate { CombatGoalFeature.Initialize(this); }); InitializeFeatureSafe("BossArenaReset", delegate { BossArenaResetFeature.Initialize(this); }); InitializeFeatureSafe("BuilderCollectorGoals", delegate { BuilderCollectorGoalFeature.Initialize(this); }); InitializeFeatureSafe("MeadowsSettlements", delegate { MeadowsSettlementFeature.Initialize(this); }); InitializeFeatureSafe("ZoneActivityRestoration", delegate { ZoneActivityRestorationFeature.Initialize(this); }); InitializeFeatureSafe("NaturalLocationRestoration", delegate { NaturalLocationRestorationFeature.Initialize(this); }); InitializeFeatureSafe("WorldRenaturation", delegate { WorldRenaturationFeature.Initialize(this); }); InitializeFeatureSafe("ResourceReset", delegate { ResourceResetFeature.Initialize(this); }); InitializeFeatureSafe("CartographyMapMode", delegate { CartographyMapModeFeature.Initialize(this); }); InitializeFeatureSafe("TombstoneMap", delegate { TombstoneMapFeature.Initialize(this); }); } } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } ((BaseUnityPlugin)this).Logger.LogInfo((object)"ChallengeHub Startphase 3/5: Konfiguration und Feature-Werte geladen."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"ChallengeHub Startphase 4/5: Harmony-Patches installieren."); HarmonyInstance = new Harmony("de.challengehub.valheim.bluteid"); PatchAttributedClassesSafely(HarmonyInstance); InitializeFeatureSafe("CommandRestrictionPatch", delegate { ChatRestrictionFeature.Patch(HarmonyInstance); }); if (!flag) { InitializeFeatureSafe("PhysicalPickupContextPatch", delegate { HumanoidPhysicalPickupContextPatch.Patch(HarmonyInstance); }); InitializeFeatureSafe("GuardianHoverPatch", delegate { GuardianHoverFeature.Patch(HarmonyInstance); }); InitializeFeatureSafe("TargetedResetPatch", delegate { TargetedResetFeature.Patch(HarmonyInstance); }); InitializeFeatureSafe("DungeonResetLifecyclePatch", delegate { DungeonResetLifecycleFeature.Patch(HarmonyInstance); }); InitializeFeatureSafe("TombstoneMapPatch", delegate { TombstoneMapFeature.Patch(HarmonyInstance); }); } InitializeFeatureSafe("ZNetSceneRemoveObjectsGuard", delegate { ChallengeHubZNetSceneRemoveObjectsNullGuardPatch.Patch(HarmonyInstance); }); ((BaseUnityPlugin)this).Logger.LogInfo((object)"ChallengeHub Startphase 5/5: Laufzeitdienste starten."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"ChallengeHub Valheim - Der Blut-Eid: Vorzeichen des Nordens 2.12.50 geladen."); ((MonoBehaviour)this).StartCoroutine(SaveConfigAfterStartup()); ((MonoBehaviour)this).StartCoroutine(PullConfigLoop()); ((MonoBehaviour)this).StartCoroutine(HeartbeatLoop()); ((MonoBehaviour)this).StartCoroutine(SkillLoop()); if (!flag) { ((MonoBehaviour)this).StartCoroutine(PortalLoop()); ((MonoBehaviour)this).StartCoroutine(GuardianStoneRegistrationLoop()); ((MonoBehaviour)this).StartCoroutine(FarmerGuardianPresenceLoop()); ((MonoBehaviour)this).StartCoroutine(NeutralGuardianPresencePulseLoop()); } } private IEnumerator SaveConfigAfterStartup() { yield return null; yield return null; try { ((BaseUnityPlugin)this).Config.Save(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("ChallengeHub Konfiguration nach dem Start einmalig gespeichert" + (_startupConfigDirty ? " (inklusive Migration)." : "."))); _startupConfigDirty = false; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("ChallengeHub Konfiguration konnte nach dem Start nicht gespeichert werden: " + ex.Message)); } } private void PatchAttributedClassesSafely(Harmony harmony) { if (harmony == null) { return; } int num = 0; int num2 = 0; Type[] array; try { array = Assembly.GetExecutingAssembly().GetTypes(); } catch (ReflectionTypeLoadException ex) { array = ex.Types.Where((Type item) => item != null).ToArray(); } Type[] array2 = array; foreach (Type type in array2) { if (type == null) { continue; } bool flag = type.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length != 0; if (!flag) { try { flag = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Any((MethodInfo method) => method.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length != 0); } catch { } } if (flag && (!IsDeathRunModeConfigured() || IsDeathRunHarmonyType(type))) { try { harmony.CreateClassProcessor(type).Patch(); num++; } catch (Exception ex2) { num2++; ((BaseUnityPlugin)this).Logger.LogError((object)("ChallengeHub Harmony-Klasse isoliert uebersprungen: " + type.FullName + " :: " + ex2.Message)); } } } ((BaseUnityPlugin)this).Logger.LogInfo((object)("ChallengeHub Harmony-Klassen einzeln verarbeitet: erfolgreich=" + num + "; uebersprungen=" + num2 + ". Ein optionaler Signaturfehler kann die restliche Mod nicht mehr stoppen.")); } private static bool IsDeathRunModeConfigured() { return string.Equals((ChallengeShortCode != null) ? ChallengeShortCode.Value : string.Empty, "BLUTEID", StringComparison.OrdinalIgnoreCase); } private static bool IsDeathRunHarmonyType(Type type) { if (type == null) { return false; } string text = type.Name ?? string.Empty; if (!text.StartsWith("DeathRun", StringComparison.Ordinal) && !text.StartsWith("BlutEid", StringComparison.Ordinal) && !text.StartsWith("CharacterAdmission", StringComparison.Ordinal) && !text.StartsWith("ChallengeHubServerGate", StringComparison.Ordinal) && !(text == "PlayerOnDeathPatch") && !(text == "CharacterDamagePatch")) { return text == "CharacterOnDeathPatch"; } return true; } private void InitializeFeatureSafe(string name, Action initialize) { try { ((BaseUnityPlugin)this).Logger.LogDebug((object)("ChallengeHub initialisiert Feature: " + name)); initialize?.Invoke(); ((BaseUnityPlugin)this).Logger.LogDebug((object)("ChallengeHub Feature bereit: " + name)); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("ChallengeHub Feature-Initialisierung fehlgeschlagen: " + name + " :: " + ex)); } } private ConfigEntry BindSynced(string section, string key, T defaultValue, string description, bool synchronized) { ConfigEntry obj = ((BaseUnityPlugin)this).Config.Bind(section, key, defaultValue, description); ServerSyncBridge.AddSynced(obj, synchronized); return obj; } private void MigrateGuardianStoneSourcePiecePrefabDefault() { try { if (GuardianStoneSourcePiecePrefab != null) { string text = (GuardianStoneSourcePiecePrefab.Value ?? string.Empty).Trim(); if (string.IsNullOrWhiteSpace(text) || string.Equals(text, "piece_sign", StringComparison.OrdinalIgnoreCase)) { GuardianStoneSourcePiecePrefab.Value = "dverger_guardstone"; _startupConfigDirty = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"GuardianStones.SourcePiecePrefab auf dverger_guardstone migriert; Speicherung erfolgt gebuendelt nach dem Start."); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("GuardianStones.SourcePiecePrefab konnte nicht migriert werden: " + ex.Message)); } } private bool HasRemoteConfig() { if (_remoteConfigLoaded) { return RemoteConfig != null; } return false; } private string EffectiveChallengeShortCode() { if (ChallengeShortCode == null || string.IsNullOrWhiteSpace(ChallengeShortCode.Value)) { if (!HasRemoteConfig() || string.IsNullOrWhiteSpace(RemoteConfig.challengeShortCode)) { return string.Empty; } return RemoteConfig.challengeShortCode; } return ChallengeShortCode.Value; } private string EffectiveServerId() { if (ServerId == null || string.IsNullOrWhiteSpace(ServerId.Value)) { if (!HasRemoteConfig() || string.IsNullOrWhiteSpace(RemoteConfig.serverId)) { return string.Empty; } return RemoteConfig.serverId; } return ServerId.Value; } private string EffectiveDefaultDifficulty() { if (DeathRunCounterFeature.ActiveRun != null && !string.IsNullOrWhiteSpace(DeathRunCounterFeature.ActiveRun.difficulty)) { return DeathRunCounterFeature.ActiveRun.difficulty; } if (!HasRemoteConfig() || string.IsNullOrWhiteSpace(RemoteConfig.defaultDifficulty)) { return DefaultDifficulty.Value; } return RemoteConfig.defaultDifficulty; } internal string CurrentChallengeShortCode() { return EffectiveChallengeShortCode(); } internal string CurrentServerId() { return EffectiveServerId(); } internal string CurrentWorldName() { return EffectiveWorldName(); } internal bool EffectiveServerGateEnabled() { return false; } internal bool EffectiveServerGateFailClosed() { if (!HasRemoteConfig() || RemoteConfig.serverGate == null) { if (ServerGateFailClosed != null) { return ServerGateFailClosed.Value; } return true; } return RemoteConfig.serverGate.failClosed; } internal int EffectiveServerGateProtocolVersion() { if (!HasRemoteConfig() || RemoteConfig.serverGate == null || RemoteConfig.serverGate.protocolVersion <= 0) { return Math.Max(1, (ServerGateProtocolVersion == null) ? 1 : ServerGateProtocolVersion.Value); } return Math.Max(1, RemoteConfig.serverGate.protocolVersion); } internal float EffectiveServerGateVerificationTimeoutSeconds() { if (!HasRemoteConfig() || RemoteConfig.serverGate == null || !(RemoteConfig.serverGate.verificationTimeoutSeconds > 0f)) { return Mathf.Max(3f, (ServerGateVerificationTimeoutSeconds != null) ? ServerGateVerificationTimeoutSeconds.Value : 10f); } return Mathf.Max(3f, RemoteConfig.serverGate.verificationTimeoutSeconds); } internal float EffectiveServerGateDisconnectDelaySeconds() { if (!HasRemoteConfig() || RemoteConfig.serverGate == null) { return Mathf.Max(0f, (ServerGateDisconnectDelaySeconds != null) ? ServerGateDisconnectDelaySeconds.Value : 3f); } return Mathf.Max(0f, RemoteConfig.serverGate.disconnectDelaySeconds); } internal float EffectiveServerGateHeartbeatSeconds() { if (!HasRemoteConfig() || RemoteConfig.serverGate == null || !(RemoteConfig.serverGate.serverHeartbeatSeconds > 0f)) { return Mathf.Max(10f, (ServerGateHeartbeatSeconds != null) ? ServerGateHeartbeatSeconds.Value : 30f); } return Mathf.Max(10f, RemoteConfig.serverGate.serverHeartbeatSeconds); } internal bool EffectiveBoundCharacterHardLock() { if (!HasRemoteConfig() || RemoteConfig.serverGate == null) { if (BoundCharacterHardLock != null) { return BoundCharacterHardLock.Value; } return true; } return RemoteConfig.serverGate.boundCharacterHardLock; } internal int EffectiveWrongWorldWarningLimit() { if (!HasRemoteConfig() || RemoteConfig.serverGate == null || RemoteConfig.serverGate.wrongWorldWarningLimit <= 0) { return Math.Max(1, (WrongWorldWarningLimit != null) ? WrongWorldWarningLimit.Value : 2); } return Math.Max(1, RemoteConfig.serverGate.wrongWorldWarningLimit); } internal int EffectiveWrongWorldRequireAdminAfter() { if (!HasRemoteConfig() || RemoteConfig.serverGate == null) { return Math.Max(0, (WrongWorldRequireAdminAfter != null) ? WrongWorldRequireAdminAfter.Value : 3); } return Math.Max(0, RemoteConfig.serverGate.wrongWorldRequireAdminAfter); } internal string CurrentServerGateRegistrationSecret() { string text = ((ServerGateRegistrationSecret != null) ? ServerGateRegistrationSecret.Value : string.Empty); if (!string.IsNullOrWhiteSpace(text)) { return text.Trim(); } if (ApiKey == null) { return string.Empty; } return ApiKey.Value; } internal bool EffectiveCharacterAdmissionEnabled() { if (!HasRemoteConfig() || RemoteConfig.admission == null) { if (EnableCharacterAdmission != null) { return EnableCharacterAdmission.Value; } return true; } return RemoteConfig.admission.enabled; } internal float EffectiveAdmissionPollSeconds() { if (!HasRemoteConfig() || RemoteConfig.admission == null || !(RemoteConfig.admission.pollSeconds > 0f)) { return Mathf.Max(2f, (AdmissionPollSeconds != null) ? AdmissionPollSeconds.Value : 8f); } return Mathf.Max(2f, RemoteConfig.admission.pollSeconds); } internal float EffectiveCharacterCheckpointSeconds() { if (!HasRemoteConfig() || RemoteConfig.admission == null || !(RemoteConfig.admission.checkpointSeconds > 0f)) { return Mathf.Max(10f, (CharacterCheckpointSeconds != null) ? CharacterCheckpointSeconds.Value : 30f); } return Mathf.Max(10f, RemoteConfig.admission.checkpointSeconds); } internal bool EffectiveAdmissionFailClosed() { if (!HasRemoteConfig() || RemoteConfig.admission == null) { if (AdmissionFailClosed != null) { return AdmissionFailClosed.Value; } return true; } return RemoteConfig.admission.failClosed; } internal Vector3 EffectiveAdmissionWaitingPosition() { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) if (HasRemoteConfig() && RemoteConfig.admission != null && RemoteConfig.admission.waitingPosition != null) { return new Vector3(RemoteConfig.admission.waitingPosition.x, RemoteConfig.admission.waitingPosition.y, RemoteConfig.admission.waitingPosition.z); } return new Vector3((AdmissionWaitingPositionX != null) ? AdmissionWaitingPositionX.Value : 0f, (AdmissionWaitingPositionY != null) ? AdmissionWaitingPositionY.Value : 0f, (AdmissionWaitingPositionZ != null) ? AdmissionWaitingPositionZ.Value : 0f); } internal Vector3 EffectiveAdmissionStartPosition() { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) if (HasRemoteConfig() && RemoteConfig.admission != null && RemoteConfig.admission.startPosition != null) { return new Vector3(RemoteConfig.admission.startPosition.x, RemoteConfig.admission.startPosition.y, RemoteConfig.admission.startPosition.z); } return new Vector3((AdmissionStartPositionX != null) ? AdmissionStartPositionX.Value : 0f, (AdmissionStartPositionY != null) ? AdmissionStartPositionY.Value : 0f, (AdmissionStartPositionZ != null) ? AdmissionStartPositionZ.Value : 0f); } internal float EffectiveAdmissionWaitingRadius() { return 0f; } private bool EffectiveNoMap() { if (!HasRemoteConfig()) { if (EnableNoMap != null) { return EnableNoMap.Value; } return false; } return RemoteConfig.noMap; } private bool EffectiveIngameChatEnabled() { if (EnableIngameChat != null) { return EnableIngameChat.Value; } return true; } internal bool EffectiveMapEnableLargeMap() { if (MapEnableLargeMap != null) { return MapEnableLargeMap.Value; } return true; } internal bool EffectiveMapEnableMinimap() { if (MapEnableMinimap != null) { return MapEnableMinimap.Value; } return false; } internal bool EffectiveMapRevealFogWhileWalking() { if (MapRevealFogWhileWalking != null) { return MapRevealFogWhileWalking.Value; } return false; } internal bool EffectiveMapRevealFogAtCartographyTable() { if (MapRevealFogAtCartographyTable != null) { return MapRevealFogAtCartographyTable.Value; } return true; } internal bool EffectiveCartographyOnlyMapMode() { if (EffectiveMapEnableLargeMap() && EffectiveMapEnableMinimap()) { return !EffectiveMapRevealFogWhileWalking(); } return true; } internal bool EffectiveRequireExplorerPartnershipForCartographyShare() { if (RequireExplorerPartnershipForCartographyShare != null) { return RequireExplorerPartnershipForCartographyShare.Value; } return true; } internal string EffectiveCartographyPartnerShareLevelPercentages() { if (CartographyPartnerShareLevelPercentages == null || string.IsNullOrWhiteSpace(CartographyPartnerShareLevelPercentages.Value)) { return "25,50,75,100"; } return CartographyPartnerShareLevelPercentages.Value; } internal int EffectiveCartographyPartnerShareBasePoints() { if (CartographyPartnerShareBasePoints == null || CartographyPartnerShareBasePoints.Value <= 0) { return 40; } return CartographyPartnerShareBasePoints.Value; } internal bool EffectiveReportCartographyPartnerShareEvents() { if (ReportCartographyPartnerShareEvents != null) { return ReportCartographyPartnerShareEvents.Value; } return true; } internal bool EffectiveDisableBossVegvisir() { if (!HasRemoteConfig()) { return DisableBossVegvisir.Value; } return RemoteConfig.disableBossVegvisir; } private bool EffectiveDamageScalingEnabled() { if (!HasRemoteConfig()) { return EnableDamageScaling.Value; } return RemoteConfig.damageScalingEnabled; } private bool EffectiveSkillTrackingEnabled() { if (!HasRemoteConfig() || RemoteConfig.skillTracking == null) { return EnableSkillTracking.Value; } return RemoteConfig.skillTracking.enabled; } private bool EffectiveEvidenceTrackingEnabled() { if (!HasRemoteConfig() || RemoteConfig.evidenceTracking == null) { return EnableEvidenceTracking.Value; } return RemoteConfig.evidenceTracking.enabled; } private bool EffectiveBuildEventsEnabled() { if (!HasRemoteConfig() || RemoteConfig.evidenceTracking == null) { return EnableBuildEvents.Value; } return RemoteConfig.evidenceTracking.buildEvents; } private bool EffectiveCraftEventsEnabled() { if (!HasRemoteConfig() || RemoteConfig.evidenceTracking == null) { return EnableCraftEvents.Value; } return RemoteConfig.evidenceTracking.craftEvents; } private bool EffectiveGatherEventsEnabled() { if (!HasRemoteConfig() || RemoteConfig.evidenceTracking == null) { return EnableGatherEvents.Value; } return RemoteConfig.evidenceTracking.gatherEvents; } private bool EffectiveExplorationEventsEnabled() { if (!HasRemoteConfig() || RemoteConfig.evidenceTracking == null) { return EnableExplorationEvents.Value; } return RemoteConfig.evidenceTracking.explorationEvents; } private bool EffectiveMapViolationPenaltyEnabled() { if (!HasRemoteConfig() || RemoteConfig.evidenceTracking == null) { return EnableMapViolationPenalty.Value; } return RemoteConfig.evidenceTracking.mapViolationPenalty; } private bool EffectiveZoneResetEnabled() { return false; } private bool EffectiveGuardianStonesEnabled() { if (!HasRemoteConfig() || RemoteConfig.guardianStones == null) { return EnableGuardianStones.Value; } return RemoteConfig.guardianStones.enabled; } private bool EffectiveCustomGuardianStonePiecesEnabled() { if (!HasRemoteConfig() || RemoteConfig.guardianStones == null) { return EnableCustomGuardianStonePieces.Value; } return RemoteConfig.guardianStones.customPiecesEnabled; } private string EffectiveGuardianStonePrefabNames() { if (!HasRemoteConfig() || RemoteConfig.guardianStones == null || string.IsNullOrWhiteSpace(RemoteConfig.guardianStones.prefabNames)) { return GuardianStonePrefabNames.Value; } return RemoteConfig.guardianStones.prefabNames; } private float EffectiveGuardianStoneDefaultRadius() { if (!HasRemoteConfig() || RemoteConfig.guardianStones == null || !(RemoteConfig.guardianStones.defaultRadius > 0f)) { return GuardianStoneDefaultRadius.Value; } return RemoteConfig.guardianStones.defaultRadius; } private string EffectiveGuardianStoneDefaultType() { if (!HasRemoteConfig() || RemoteConfig.guardianStones == null || string.IsNullOrWhiteSpace(RemoteConfig.guardianStones.defaultType)) { return GuardianStoneDefaultType.Value; } return RemoteConfig.guardianStones.defaultType; } private string EffectiveGuardianStoneTypeByPrefab() { if (!HasRemoteConfig() || RemoteConfig.guardianStones == null || string.IsNullOrWhiteSpace(RemoteConfig.guardianStones.typeByPrefab)) { return GuardianStoneTypeByPrefab.Value; } return RemoteConfig.guardianStones.typeByPrefab; } private string EffectiveGuardianRecipe(string type) { string text = NormalizeGuardianType(type); if (HasRemoteConfig() && RemoteConfig.guardianStones != null) { switch (text) { case "farmer": if (!string.IsNullOrWhiteSpace(RemoteConfig.guardianStones.farmerRecipe)) { return RemoteConfig.guardianStones.farmerRecipe; } break; case "explorer": if (!string.IsNullOrWhiteSpace(RemoteConfig.guardianStones.explorerRecipe)) { return RemoteConfig.guardianStones.explorerRecipe; } break; case "combat": if (!string.IsNullOrWhiteSpace(RemoteConfig.guardianStones.combatRecipe)) { return RemoteConfig.guardianStones.combatRecipe; } break; case "builder": if (!string.IsNullOrWhiteSpace(RemoteConfig.guardianStones.builderRecipe)) { return RemoteConfig.guardianStones.builderRecipe; } break; default: if (!string.IsNullOrWhiteSpace(RemoteConfig.guardianStones.neutralRecipe)) { return RemoteConfig.guardianStones.neutralRecipe; } break; case "reset": break; } } switch (text) { case "farmer": return GuardianFarmerRecipe.Value; case "explorer": return GuardianExplorerRecipe.Value; case "combat": return GuardianCombatRecipe.Value; case "builder": return GuardianBuilderRecipe.Value; case "reset": if (GuardianResetRecipe == null) { return "Stone:20,Wood:10,Resin:5,SurtlingCore:1"; } return GuardianResetRecipe.Value; default: return GuardianNeutralRecipe.Value; } } private bool EffectiveGuardianPreventReset(string type) { string text = NormalizeGuardianType(type); if (HasRemoteConfig() && RemoteConfig.guardianStones != null) { return text switch { "farmer" => RemoteConfig.guardianStones.farmerPreventReset, "explorer" => RemoteConfig.guardianStones.explorerPreventReset, "combat" => RemoteConfig.guardianStones.combatPreventReset, "builder" => RemoteConfig.guardianStones.builderPreventReset, "reset" => false, _ => RemoteConfig.guardianStones.neutralPreventReset, }; } return text switch { "farmer" => GuardianFarmerPreventReset.Value, "explorer" => GuardianExplorerPreventReset.Value, "combat" => GuardianCombatPreventReset.Value, "builder" => GuardianBuilderPreventReset.Value, "reset" => false, _ => GuardianNeutralPreventReset.Value, }; } private bool EffectiveNeutralPresencePulseEnabled() { if (GuardianNeutralSimulatePlayerPresencePulse != null) { return GuardianNeutralSimulatePlayerPresencePulse.Value; } return true; } private float EffectiveNeutralPresencePulseIntervalMinutes() { if (GuardianNeutralPresencePulseIntervalMinutes == null || !(GuardianNeutralPresencePulseIntervalMinutes.Value > 0f)) { return 15f; } return GuardianNeutralPresencePulseIntervalMinutes.Value; } private float EffectiveNeutralPresencePulseDurationSeconds() { if (GuardianNeutralPresencePulseDurationSeconds == null || !(GuardianNeutralPresencePulseDurationSeconds.Value > 0f)) { return 5f; } return GuardianNeutralPresencePulseDurationSeconds.Value; } private float EffectiveNeutralPresenceRadius() { if (GuardianNeutralPresenceRadius != null && GuardianNeutralPresenceRadius.Value > 0f) { return GuardianNeutralPresenceRadius.Value; } return EffectiveGuardianStoneDefaultRadius(); } private float EffectiveNeutralPresenceScanSeconds() { if (GuardianNeutralPresenceScanSeconds == null || !(GuardianNeutralPresenceScanSeconds.Value > 0f)) { return 2f; } return GuardianNeutralPresenceScanSeconds.Value; } private bool EffectiveNeutralPresencePulseOnFirstScan() { if (GuardianNeutralPresencePulseOnFirstScan != null) { return GuardianNeutralPresencePulseOnFirstScan.Value; } return true; } private bool EffectiveNeutralReportPresencePulseEvents() { if (GuardianNeutralReportPresencePulseEvents != null) { return GuardianNeutralReportPresencePulseEvents.Value; } return true; } private float EffectiveHeartbeatSeconds() { if (!HasRemoteConfig() || RemoteConfig.intervals == null || !(RemoteConfig.intervals.heartbeatSeconds > 0f)) { return HeartbeatSeconds.Value; } return RemoteConfig.intervals.heartbeatSeconds; } private float EffectiveSkillScanSeconds() { if (!HasRemoteConfig() || RemoteConfig.intervals == null || !(RemoteConfig.intervals.skillScanSeconds > 0f)) { return SkillScanSeconds.Value; } return RemoteConfig.intervals.skillScanSeconds; } private float EffectivePortalScanSeconds() { if (!HasRemoteConfig() || RemoteConfig.intervals == null || !(RemoteConfig.intervals.portalScanSeconds > 0f)) { return PortalScanSeconds.Value; } return RemoteConfig.intervals.portalScanSeconds; } private float EffectiveZoneResetScanSeconds() { if (!HasRemoteConfig() || RemoteConfig.intervals == null || !(RemoteConfig.intervals.zoneResetScanSeconds > 0f)) { return ZoneResetScanSeconds.Value; } return RemoteConfig.intervals.zoneResetScanSeconds; } private int EffectiveAllowedActivePortalsPerBiome() { if (!HasRemoteConfig() || RemoteConfig.portals == null || RemoteConfig.portals.allowedActivePerBiome < 0) { return AllowedActivePortalsPerBiome.Value; } return RemoteConfig.portals.allowedActivePerBiome; } private string[] EffectiveProtectedPrefabs() { if (!HasRemoteConfig() || RemoteConfig.zoneReset == null || RemoteConfig.zoneReset.protectedByAny == null || RemoteConfig.zoneReset.protectedByAny.Length == 0) { return SplitCsv(ProtectedPrefabs.Value).ToArray(); } return RemoteConfig.zoneReset.protectedByAny; } private string[] EffectiveResetPrefabKeywords() { if (!HasRemoteConfig() || RemoteConfig.zoneReset == null || RemoteConfig.zoneReset.resetObjects == null || RemoteConfig.zoneReset.resetObjects.Length == 0) { return SplitCsv(ResetPrefabKeywords.Value).ToArray(); } return RemoteConfig.zoneReset.resetObjects; } internal static bool IsNoMapEnabled() { if (!((Object)(object)Instance != (Object)null)) { if (EnableNoMap != null) { return EnableNoMap.Value; } return false; } return Instance.EffectiveNoMap(); } internal static bool IsIngameChatEnabled() { if (!((Object)(object)Instance != (Object)null)) { if (EnableIngameChat != null) { return EnableIngameChat.Value; } return true; } return Instance.EffectiveIngameChatEnabled(); } internal static bool MapLargeMapEnabled() { if (!((Object)(object)Instance == (Object)null)) { return Instance.EffectiveMapEnableLargeMap(); } return true; } internal static bool MapMinimapEnabled() { if ((Object)(object)Instance != (Object)null) { return Instance.EffectiveMapEnableMinimap(); } return false; } internal static bool MapRevealFogWhileWalkingEnabled() { if ((Object)(object)Instance != (Object)null) { return Instance.EffectiveMapRevealFogWhileWalking(); } return false; } internal static bool MapRevealFogAtCartographyTableEnabled() { if (!((Object)(object)Instance == (Object)null)) { return Instance.EffectiveMapRevealFogAtCartographyTable(); } return true; } internal static bool IsCartographyOnlyMapModeEnabled() { if ((Object)(object)Instance != (Object)null) { return Instance.EffectiveCartographyOnlyMapMode(); } return false; } internal static bool ShouldRequireExplorerPartnershipForCartographyShare() { if (!((Object)(object)Instance == (Object)null)) { return Instance.EffectiveRequireExplorerPartnershipForCartographyShare(); } return true; } internal static int CartographyPartnerSharePercentForLevel(int level) { string text = (((Object)(object)Instance != (Object)null) ? Instance.EffectiveCartographyPartnerShareLevelPercentages() : "25,50,75,100"); int num = Math.Max(1, Math.Min(4, level)); try { string[] array = (text ?? string.Empty).Split(new char[1] { ',' }); if (array.Length >= num && int.TryParse(array[num - 1].Trim(), out var result)) { return Math.Max(0, Math.Min(100, result)); } } catch { } return num * 25; } internal static int CartographyPartnerShareBasePointsValue() { if (!((Object)(object)Instance != (Object)null)) { return 40; } return Instance.EffectiveCartographyPartnerShareBasePoints(); } internal static bool ShouldReportCartographyPartnerShareEvents() { if (!((Object)(object)Instance == (Object)null)) { return Instance.EffectiveReportCartographyPartnerShareEvents(); } return true; } internal static bool IsDamageScalingEnabled() { if (!((Object)(object)Instance != (Object)null)) { if (EnableDamageScaling != null) { return EnableDamageScaling.Value; } return false; } return Instance.EffectiveDamageScalingEnabled(); } internal static bool IsBossVegvisirDisabled() { if (!((Object)(object)Instance != (Object)null)) { if (DisableBossVegvisir != null) { return DisableBossVegvisir.Value; } return false; } return Instance.EffectiveDisableBossVegvisir(); } internal static bool AreCustomGuardianStonePiecesEnabled() { if (!((Object)(object)Instance == (Object)null) && EnableCustomGuardianStonePieces != null) { return Instance.EffectiveCustomGuardianStonePiecesEnabled(); } return true; } internal static string GuardianRecipeFromConfig(string type) { if (!((Object)(object)Instance != (Object)null)) { return ""; } return Instance.EffectiveGuardianRecipe(type); } internal static string GuardianSourcePiecePrefabFromConfig() { if (GuardianStoneSourcePiecePrefab == null || string.IsNullOrWhiteSpace(GuardianStoneSourcePiecePrefab.Value)) { return "dverger_guardstone"; } return GuardianStoneSourcePiecePrefab.Value.Trim(); } private void OnDestroy() { ChallengeHubServerGateFeature.Shutdown(); CharacterAdmissionFeature.Shutdown(); Harmony harmonyInstance = HarmonyInstance; if (harmonyInstance != null) { harmonyInstance.UnpatchSelf(); } } private IEnumerator GuardianStoneRegistrationLoop() { for (int attempt = 1; attempt <= 20; attempt++) { if (GuardianStonePieces.IsRegistrationComplete()) { break; } yield return (object)new WaitForSeconds(2f); if (!AreCustomGuardianStonePiecesEnabled()) { yield break; } try { GuardianStonePieces.TryRegisterAll("startup_fallback_" + attempt); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("Wächterstein-Registrierungsfallback fehlgeschlagen: " + ex.Message)); } } } if (GuardianStonePieces.IsRegistrationComplete()) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)"Wächterstein-Registrierungsfallback beendet; Awake-Hooks übernehmen weitere Szenenwechsel."); } } else { ManualLogSource log3 = Log; if (log3 != null) { log3.LogWarning((object)"Wächterstein-Registrierungsfallback nach 40 Sekunden beendet; kein weiterer Poll-Loop aktiv."); } } } internal static bool IsSmallMinimapMode(object mode) { try { string a = ((mode != null) ? mode.ToString() : string.Empty); return string.Equals(a, "Small", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "SmallMap", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "MiniMap", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "Minimap", StringComparison.OrdinalIgnoreCase); } catch { return false; } } internal static bool IsLargeMapMode(object mode) { try { string a = ((mode != null) ? mode.ToString() : string.Empty); return string.Equals(a, "Large", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "LargeMap", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "Map", StringComparison.OrdinalIgnoreCase); } catch { return false; } } private IEnumerator PullConfigLoop() { while (true) { if (Time.realtimeSinceStartup - _lastConfigPull > 120f) { _lastConfigPull = Time.realtimeSinceStartup; yield return PullRemoteConfig(); } yield return (object)new WaitForSeconds(5f); } } private IEnumerator HeartbeatLoop() { while (true) { yield return (object)new WaitForSeconds(Mathf.Max(5f, EffectiveHeartbeatSeconds())); Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { SendEvent("heartbeat", localPlayer, new Dictionary { { "biome", CurrentBiome(((Component)localPlayer).transform.position) }, { "position", SerializeVector(((Component)localPlayer).transform.position) } }); } } } private IEnumerator SkillLoop() { while (true) { yield return (object)new WaitForSeconds(Mathf.Max(5f, EffectiveSkillScanSeconds())); if (EffectiveSkillTrackingEnabled()) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && ((Character)localPlayer).IsOwner()) { ReportSkillSnapshot(localPlayer); } } } } private IEnumerator PortalLoop() { while (true) { yield return (object)new WaitForSeconds(Mathf.Max(10f, EffectivePortalScanSeconds())); Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { ReportPortalCounts(localPlayer); } } } private IEnumerator ZoneResetLoop() { while (true) { yield return (object)new WaitForSeconds(Mathf.Max(60f, EffectiveZoneResetScanSeconds())); if (EffectiveZoneResetEnabled() && IsServer()) { TryResetInactiveZones(); } } } internal IEnumerator RefreshRemoteConfigAfterToken() { RequestRemoteConfigNow(); yield break; } private IEnumerator PullRemoteConfig() { if (!_remoteConfigRequestInFlight) { _remoteConfigRequestInFlight = true; try { yield return PullRemoteConfigCore(); } finally { Plugin plugin = this; plugin._remoteConfigRequestInFlight = false; plugin._lastRemoteConfigCompletedAt = Time.realtimeSinceStartup; } } } private IEnumerator PullRemoteConfigCore() { string text = ((ChallengeShortCode != null) ? (ChallengeShortCode.Value ?? string.Empty).Trim() : string.Empty); string text2 = ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/api/valheim/config" + (string.IsNullOrWhiteSpace(text) ? string.Empty : ("?challengeShortCode=" + UnityWebRequest.EscapeURL(text))); UnityWebRequest req = UnityWebRequest.Get(text2); try { ChallengeHubApiTokenFeature.ApplyAuthorization(req); yield return req.SendWebRequest(); if ((int)req.result != 1) { if (req.responseCode == 401) { ChallengeHubApiTokenFeature.Invalidate("Konfigurationsabruf: HTTP 401"); yield return ChallengeHubApiTokenFeature.EnsureAvailable(); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Valheim config konnte nicht geladen werden: " + req.error)); } yield break; } try { WebConfigEnvelope webConfigEnvelope = ChallengeHubJson.Deserialize(req.downloadHandler.text); WebConfig remoteConfig = RemoteConfig; RemoteConfig = ((webConfigEnvelope != null && webConfigEnvelope.config != null) ? webConfigEnvelope.config : WebConfig.Default()); if ((RemoteConfig.playstyles == null || RemoteConfig.playstyles.Length == 0) && webConfigEnvelope != null && webConfigEnvelope.playstyles != null && webConfigEnvelope.playstyles.Length != 0) { RemoteConfig.playstyles = webConfigEnvelope.playstyles; } if ((RemoteConfig.bosses == null || RemoteConfig.bosses.Length == 0) && webConfigEnvelope != null && webConfigEnvelope.bosses != null && webConfigEnvelope.bosses.Length != 0) { RemoteConfig.bosses = webConfigEnvelope.bosses; } if ((RemoteConfig.biomes == null || RemoteConfig.biomes.Length == 0) && webConfigEnvelope != null && webConfigEnvelope.biomes != null && webConfigEnvelope.biomes.Length != 0) { RemoteConfig.biomes = webConfigEnvelope.biomes; } if ((RemoteConfig.playstyles == null || RemoteConfig.playstyles.Length == 0) && HasCompleteGoalCatalog(remoteConfig)) { RemoteConfig.playstyles = remoteConfig.playstyles; } if ((RemoteConfig.bosses == null || RemoteConfig.bosses.Length == 0) && remoteConfig != null && remoteConfig.bosses != null && remoteConfig.bosses.Length != 0) { RemoteConfig.bosses = remoteConfig.bosses; } if ((RemoteConfig.biomes == null || RemoteConfig.biomes.Length == 0) && remoteConfig != null && remoteConfig.biomes != null && remoteConfig.biomes.Length != 0) { RemoteConfig.biomes = remoteConfig.biomes; } _remoteConfigLoaded = true; if (PersistRemoteConfigToLocalFile.Value) { ChallengeShortCode.Value = (string.IsNullOrWhiteSpace(RemoteConfig.challengeShortCode) ? ChallengeShortCode.Value : RemoteConfig.challengeShortCode); DefaultDifficulty.Value = (string.IsNullOrWhiteSpace(RemoteConfig.defaultDifficulty) ? DefaultDifficulty.Value : RemoteConfig.defaultDifficulty); EnableNoMap.Value = RemoteConfig.noMap; DisableBossVegvisir.Value = RemoteConfig.disableBossVegvisir; EnableDamageScaling.Value = RemoteConfig.damageScalingEnabled; if (RemoteConfig.skillTracking != null) { EnableSkillTracking.Value = RemoteConfig.skillTracking.enabled; } if (RemoteConfig.admission != null) { EnableCharacterAdmission.Value = RemoteConfig.admission.enabled; AdmissionFailClosed.Value = RemoteConfig.admission.failClosed; if (RemoteConfig.admission.waitingPosition != null) { AdmissionWaitingPositionX.Value = RemoteConfig.admission.waitingPosition.x; AdmissionWaitingPositionY.Value = RemoteConfig.admission.waitingPosition.y; AdmissionWaitingPositionZ.Value = RemoteConfig.admission.waitingPosition.z; } if (RemoteConfig.admission.startPosition != null) { AdmissionStartPositionX.Value = RemoteConfig.admission.startPosition.x; AdmissionStartPositionY.Value = RemoteConfig.admission.startPosition.y; AdmissionStartPositionZ.Value = RemoteConfig.admission.startPosition.z; } AdmissionWaitingRadius.Value = 0f; if (RemoteConfig.admission.pollSeconds > 0f) { AdmissionPollSeconds.Value = RemoteConfig.admission.pollSeconds; } if (RemoteConfig.admission.checkpointSeconds > 0f) { CharacterCheckpointSeconds.Value = RemoteConfig.admission.checkpointSeconds; } } if (RemoteConfig.serverGate != null) { RequireValidChallengeHubServer.Value = RemoteConfig.serverGate.enabled; ServerGateFailClosed.Value = RemoteConfig.serverGate.failClosed; if (RemoteConfig.serverGate.protocolVersion > 0) { ServerGateProtocolVersion.Value = RemoteConfig.serverGate.protocolVersion; } if (RemoteConfig.serverGate.verificationTimeoutSeconds > 0f) { ServerGateVerificationTimeoutSeconds.Value = RemoteConfig.serverGate.verificationTimeoutSeconds; } ServerGateDisconnectDelaySeconds.Value = Mathf.Max(0f, RemoteConfig.serverGate.disconnectDelaySeconds); if (RemoteConfig.serverGate.serverHeartbeatSeconds > 0f) { ServerGateHeartbeatSeconds.Value = RemoteConfig.serverGate.serverHeartbeatSeconds; } BoundCharacterHardLock.Value = RemoteConfig.serverGate.boundCharacterHardLock; if (RemoteConfig.serverGate.wrongWorldWarningLimit > 0) { WrongWorldWarningLimit.Value = RemoteConfig.serverGate.wrongWorldWarningLimit; } WrongWorldRequireAdminAfter.Value = Mathf.Max(0, RemoteConfig.serverGate.wrongWorldRequireAdminAfter); } if (RemoteConfig.evidenceTracking != null) { EnableEvidenceTracking.Value = RemoteConfig.evidenceTracking.enabled; EnableBuildEvents.Value = RemoteConfig.evidenceTracking.buildEvents; EnableCraftEvents.Value = RemoteConfig.evidenceTracking.craftEvents; EnableGatherEvents.Value = RemoteConfig.evidenceTracking.gatherEvents; EnableExplorationEvents.Value = RemoteConfig.evidenceTracking.explorationEvents; EnableMapViolationPenalty.Value = RemoteConfig.evidenceTracking.mapViolationPenalty; } if (RemoteConfig.intervals != null) { if (RemoteConfig.intervals.heartbeatSeconds > 0f) { HeartbeatSeconds.Value = RemoteConfig.intervals.heartbeatSeconds; } if (RemoteConfig.intervals.skillScanSeconds > 0f) { SkillScanSeconds.Value = RemoteConfig.intervals.skillScanSeconds; } if (RemoteConfig.intervals.portalScanSeconds > 0f) { PortalScanSeconds.Value = RemoteConfig.intervals.portalScanSeconds; } if (RemoteConfig.intervals.zoneResetScanSeconds > 0f) { ZoneResetScanSeconds.Value = RemoteConfig.intervals.zoneResetScanSeconds; } } EnableZoneReset.Value = false; if (RemoteConfig.guardianStones != null) { EnableGuardianStones.Value = RemoteConfig.guardianStones.enabled; EnableCustomGuardianStonePieces.Value = RemoteConfig.guardianStones.customPiecesEnabled; if (!string.IsNullOrWhiteSpace(RemoteConfig.guardianStones.prefabNames)) { GuardianStonePrefabNames.Value = RemoteConfig.guardianStones.prefabNames; } if (RemoteConfig.guardianStones.defaultRadius > 0f) { GuardianStoneDefaultRadius.Value = RemoteConfig.guardianStones.defaultRadius; } if (!string.IsNullOrWhiteSpace(RemoteConfig.guardianStones.defaultType)) { GuardianStoneDefaultType.Value = RemoteConfig.guardianStones.defaultType; } if (!string.IsNullOrWhiteSpace(RemoteConfig.guardianStones.typeByPrefab)) { GuardianStoneTypeByPrefab.Value = RemoteConfig.guardianStones.typeByPrefab; } if (!string.IsNullOrWhiteSpace(RemoteConfig.guardianStones.farmerRecipe)) { GuardianFarmerRecipe.Value = RemoteConfig.guardianStones.farmerRecipe; } if (!string.IsNullOrWhiteSpace(RemoteConfig.guardianStones.explorerRecipe)) { GuardianExplorerRecipe.Value = RemoteConfig.guardianStones.explorerRecipe; } if (!string.IsNullOrWhiteSpace(RemoteConfig.guardianStones.combatRecipe)) { GuardianCombatRecipe.Value = RemoteConfig.guardianStones.combatRecipe; } if (!string.IsNullOrWhiteSpace(RemoteConfig.guardianStones.builderRecipe)) { GuardianBuilderRecipe.Value = RemoteConfig.guardianStones.builderRecipe; } if (!string.IsNullOrWhiteSpace(RemoteConfig.guardianStones.neutralRecipe)) { GuardianNeutralRecipe.Value = RemoteConfig.guardianStones.neutralRecipe; } GuardianFarmerPreventReset.Value = RemoteConfig.guardianStones.farmerPreventReset; GuardianExplorerPreventReset.Value = RemoteConfig.guardianStones.explorerPreventReset; GuardianCombatPreventReset.Value = RemoteConfig.guardianStones.combatPreventReset; GuardianBuilderPreventReset.Value = RemoteConfig.guardianStones.builderPreventReset; GuardianNeutralPreventReset.Value = RemoteConfig.guardianStones.neutralPreventReset; } if (RemoteConfig.portals != null) { AllowedActivePortalsPerBiome.Value = RemoteConfig.portals.allowedActivePerBiome; } } ((BaseUnityPlugin)this).Logger.LogInfo((object)("Valheim ChallengeHub-Konfiguration aus App geladen" + (PersistRemoteConfigToLocalFile.Value ? " und in lokale .cfg geschrieben." : " (nur im Speicher; lokale .cfg bleibt unverändert)."))); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Valheim config JSON konnte nicht gelesen werden: " + ex.Message)); } } finally { ((IDisposable)req)?.Dispose(); } } private IEnumerator PullRemoteGoalCatalog(string challengeCode) { string baseUrl = ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/api/valheim/goals?challengeShortCode=" + UnityWebRequest.EscapeURL(challengeCode ?? string.Empty); List loadedStyles = new List(); BossWebConfig[] loadedBosses = null; BiomeWebConfig[] loadedBiomes = null; int totalBytes = 0; string[] array = new string[6] { "core", "fighter", "farmer", "builder", "explorer", "collector" }; string[] array2 = array; foreach (string part in array2) { string text = baseUrl + ((part == "core") ? "&part=core" : ("&style=" + UnityWebRequest.EscapeURL(part))); UnityWebRequest req = UnityWebRequest.Get(text); try { req.timeout = 15; ChallengeHubApiTokenFeature.ApplyAuthorization(req); yield return req.SendWebRequest(); if ((int)req.result != 1) { if (req.responseCode == 401) { ChallengeHubApiTokenFeature.Invalidate("Goal-Katalog: HTTP 401"); yield return ChallengeHubApiTokenFeature.EnsureAvailable(); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)("ChallengeHub Goal-Katalogteil '" + part + "' konnte nicht geladen werden: " + req.error)); } yield break; } try { string text2 = req.downloadHandler.text ?? string.Empty; totalBytes += text2.Length; GoalCatalogEnvelope goalCatalogEnvelope = ParseGoalCatalog(text2); if (goalCatalogEnvelope == null || !goalCatalogEnvelope.ok) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("ChallengeHub Goal-Katalogteil '" + part + "' ist ungueltig (" + text2.Length + " Bytes).")); yield break; } if (goalCatalogEnvelope.playstyles != null) { loadedStyles.AddRange(goalCatalogEnvelope.playstyles.Where((PlaystyleWebConfig style) => style != null)); } if (goalCatalogEnvelope.bosses != null && goalCatalogEnvelope.bosses.Length != 0) { loadedBosses = goalCatalogEnvelope.bosses; } if (goalCatalogEnvelope.biomes != null && goalCatalogEnvelope.biomes.Length != 0) { loadedBiomes = goalCatalogEnvelope.biomes; } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("ChallengeHub Goal-Katalogteil '" + part + "' konnte nicht gelesen werden: " + ex.Message)); yield break; } } finally { ((IDisposable)req)?.Dispose(); } } int num = loadedStyles.Sum((PlaystyleWebConfig style) => (style.goals != null) ? style.goals.Length : 0); if (loadedStyles.Count != 5 || num == 0 || loadedBosses == null || loadedBiomes == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("ChallengeHub Goal-Katalog unvollstaendig: " + totalBytes + " Bytes, " + loadedStyles.Count + " Spielstile, " + num + " Goals.")); yield break; } RemoteConfig.playstyles = loadedStyles.ToArray(); RemoteConfig.bosses = loadedBosses; RemoteConfig.biomes = loadedBiomes; SaveLastKnownGoodGoalCatalog(); GoalProgressFeature.ApplyRemoteConfig(RemoteConfig.playstyles); ((BaseUnityPlugin)this).Logger.LogInfo((object)("ChallengeHub geteilter Goal-Katalog geladen: " + totalBytes + " Bytes, " + loadedStyles.Count + " Spielstile, " + num + " Goals, " + loadedBosses.Length + " Bosse, " + loadedBiomes.Length + " Biome.")); } private static GoalCatalogEnvelope ParseGoalCatalog(string json) { GoalCatalogEnvelope result = null; try { result = ChallengeHubJson.Deserialize(json); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("ChallengeHub Goal-Katalog-Parser fehlgeschlagen: " + ex.Message)); } } return result; } private string GoalCatalogCachePath() { string text = ((ChallengeShortCode != null) ? NormalizeKey(ChallengeShortCode.Value) : "default"); if (string.IsNullOrWhiteSpace(text)) { text = "default"; } return Path.Combine(Paths.ConfigPath, "ChallengeHubValheim", "goal-catalog-" + text + ".json"); } private static bool HasCompleteGoalCatalog(WebConfig config) { if (config == null || config.playstyles == null || config.playstyles.Length != 5) { return false; } if (config.bosses == null || config.bosses.Length != 7 || config.biomes == null || config.biomes.Length != 8) { return false; } if (config.playstyles.All((PlaystyleWebConfig style) => style != null && style.goals != null && style.goals.Length != 0)) { return config.playstyles.Sum((PlaystyleWebConfig style) => style.goals.Length) >= 42; } return false; } private void LoadLastKnownGoodGoalCatalog() { if (HasCompleteGoalCatalog(RemoteConfig)) { return; } try { string text = GoalCatalogCachePath(); if (!File.Exists(text)) { return; } WebConfig webConfig = ChallengeHubJson.Deserialize(File.ReadAllText(text)); if (!HasCompleteGoalCatalog(webConfig)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Gespeicherter Goal-Katalog ist unvollstaendig und wird ignoriert: " + text)); return; } if (RemoteConfig == null) { RemoteConfig = WebConfig.Default(); } RemoteConfig.playstyles = webConfig.playstyles; RemoteConfig.bosses = webConfig.bosses; RemoteConfig.biomes = webConfig.biomes; GoalProgressFeature.ApplyRemoteConfig(RemoteConfig.playstyles); ((BaseUnityPlugin)this).Logger.LogInfo((object)("ChallengeHub Last-known-good Goal-Katalog geladen: 5 Spielstile, " + RemoteConfig.playstyles.Sum((PlaystyleWebConfig style) => style.goals.Length) + " Goals, 7 Bosse, 8 Biome.")); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Gespeicherter Goal-Katalog konnte nicht geladen werden: " + ex.Message)); } } private void SaveLastKnownGoodGoalCatalog() { if (!HasCompleteGoalCatalog(RemoteConfig)) { return; } try { string text = GoalCatalogCachePath(); Directory.CreateDirectory(Path.GetDirectoryName(text)); string text2 = text + ".tmp"; File.WriteAllText(text2, JsonConvert.SerializeObject((object)RemoteConfig)); if (File.Exists(text)) { File.Replace(text2, text, null); } else { File.Move(text2, text); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Goal-Katalog konnte nicht atomar gespeichert werden: " + ex.Message)); } } internal void RequestRemoteConfigNow() { if (!_remoteConfigRequestInFlight && !(Time.realtimeSinceStartup - _lastRemoteConfigCompletedAt < 30f)) { _lastConfigPull = Time.realtimeSinceStartup; ((MonoBehaviour)this).StartCoroutine(PullRemoteConfig()); } } internal void RequestPlayerStatusNow(Player player) { if (!((Object)(object)player == (Object)null) && !_playerStatusRequestInFlight && !(Time.realtimeSinceStartup - _lastPlayerStatusCompletedAt < 1f)) { _playerStatusRequestInFlight = true; ((MonoBehaviour)this).StartCoroutine(PullPlayerStatus()); } } private IEnumerator PullPlayerStatus() { yield return ChallengeHubApiTokenFeature.EnsureAvailable(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"F6-Statusabruf: GET /api/valheim/status wird gesendet."); for (int attempt = 0; attempt < 2; attempt++) { UnityWebRequest req = UnityWebRequest.Get(ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/api/valheim/status"); try { req.timeout = 15; ChallengeHubApiTokenFeature.ApplyAuthorization(req); yield return req.SendWebRequest(); if ((int)req.result == 1) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("F6-Statusabruf erfolgreich: " + (req.downloadHandler.text ?? string.Empty).Length + " Bytes empfangen.")); GoalProgressFeature.ApplyServerResponse(req.downloadHandler.text); _playerStatusRequestInFlight = false; _lastPlayerStatusCompletedAt = Time.realtimeSinceStartup; yield break; } if (req.responseCode == 401 && attempt == 0) { ChallengeHubApiTokenFeature.Invalidate("F6-Status: HTTP 401"); yield return ChallengeHubApiTokenFeature.EnsureAvailable(); continue; } ((BaseUnityPlugin)this).Logger.LogWarning((object)("F6-Status konnte nicht geladen werden: " + req.responseCode + " / " + req.error)); _playerStatusRequestInFlight = false; _lastPlayerStatusCompletedAt = Time.realtimeSinceStartup; yield break; } finally { ((IDisposable)req)?.Dispose(); } } _playerStatusRequestInFlight = false; _lastPlayerStatusCompletedAt = Time.realtimeSinceStartup; } internal static string CurrentWorldUidString() { try { return ((Object)(object)ZNet.instance == (Object)null) ? string.Empty : ZNet.instance.GetWorldUID().ToString(CultureInfo.InvariantCulture); } catch { return string.Empty; } } internal void SendServerEvent(string eventType, Dictionary extra = null) { if (string.IsNullOrWhiteSpace(eventType) || !ChallengeHubServerGateFeature.GameplayAllowed) { return; } Dictionary dictionary = new Dictionary { { "apiKey", ApiKey.Value }, { "eventType", eventType }, { "challengeShortCode", EffectiveChallengeShortCode() }, { "playerName", "ChallengeHub Server" }, { "playerId", string.Empty }, { "twitchLogin", string.Empty }, { "linkCode", string.Empty }, { "serverId", EffectiveServerId() }, { "worldName", EffectiveWorldName() }, { "worldUid", CurrentWorldUidString() }, { "difficulty", EffectiveDefaultDifficulty() }, { "occurredAt", DateTime.UtcNow.ToString("O") } }; if (extra != null) { foreach (KeyValuePair item in extra) { dictionary[item.Key] = item.Value; } } if (ShouldDropEvent(eventType, dictionary)) { return; } AddEventIdentity(dictionary); string text = ToJson(dictionary); if (text.Length > 8192) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("ChallengeHub Server-Event zu groÃÅÂÂ\u00b8 und wurde verworfen: " + eventType + " (" + text.Length + " Zeichen)")); } else { string text2 = ChallengeHubEventOutbox.Enqueue(text, Convert.ToString(dictionary["evidenceId"])); if (_inflightPosts >= 4) { LogPostThrottle(eventType); return; } ChallengeHubEventOutbox.BeginDelivery(text2); ((MonoBehaviour)this).StartCoroutine(PostJson(EventEndpoint, text, text2)); } } internal bool SendEvent(string eventType, Player player, Dictionary extra = null, Action deliveryCallback = null) { if ((Object)(object)player == (Object)null) { return false; } bool flag = extra?.ContainsKey("goalLocallyObserved") ?? false; if (!DeathRunCounterFeature.Enabled && !string.Equals(eventType, "playstyle_goal", StringComparison.OrdinalIgnoreCase) && !flag) { GoalAutomationFeature.Observe(this, eventType, player, extra); } if (!ChallengeHubServerGateFeature.GameplayAllowed) { return false; } if ((!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer()) && !CharacterAdmissionFeature.ChallengeScoringAllowed && !IsAdmissionDiagnosticEvent(eventType)) { return false; } Dictionary dictionary = new Dictionary { { "apiKey", ApiKey.Value }, { "eventType", eventType }, { "challengeShortCode", EffectiveChallengeShortCode() }, { "playerName", player.GetPlayerName() }, { "playerId", player.GetPlayerID().ToString() }, { "twitchLogin", TwitchLogin.Value }, { "linkCode", PlayerLinkCode.Value }, { "serverId", EffectiveServerId() }, { "worldName", EffectiveWorldName() }, { "worldUid", CurrentWorldUidString() }, { "difficulty", EffectiveDefaultDifficulty() }, { "occurredAt", DateTime.UtcNow.ToString("O") } }; if (extra != null) { foreach (KeyValuePair item in extra) { dictionary[item.Key] = item.Value; } } if (ShouldDropEvent(eventType, dictionary)) { return false; } AddEventIdentity(dictionary); string text = ToJson(dictionary); int num = (string.Equals(eventType, "deathrun_trophy_snapshot", StringComparison.OrdinalIgnoreCase) ? 49152 : 8192); if (Encoding.UTF8.GetByteCount(text) > num) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("ChallengeHub Event zu groÃÅÂÂ\u00b8 und wurde verworfen: " + eventType + " (" + text.Length + " Zeichen)")); return false; } string text2 = ChallengeHubEventOutbox.Enqueue(text, Convert.ToString(dictionary["evidenceId"])); if (string.IsNullOrWhiteSpace(text2)) { return false; } ChallengeHubEventOutbox.RegisterDeliveryCallback(text2, deliveryCallback); if (_inflightPosts >= 4) { LogPostThrottle(eventType); return true; } ChallengeHubEventOutbox.BeginDelivery(text2); ((MonoBehaviour)this).StartCoroutine(PostJson(EventEndpoint, text, text2)); return true; } private static void AddEventIdentity(Dictionary payload) { string value = (payload.ContainsKey("evidenceId") ? Convert.ToString(payload["evidenceId"]) : string.Empty); if (string.IsNullOrWhiteSpace(value)) { value = "evt-" + Guid.NewGuid().ToString("N"); } payload["evidenceId"] = value; payload["eventId"] = value; payload["clientVersion"] = "2.12.50"; payload["protocolVersion"] = 2; payload["detectorVersion"] = 5; } private void LogPostThrottle(string eventType) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup - _lastPostThrottleWarningAt < 30f) { _suppressedPostThrottleWarnings++; return; } string text = ((_suppressedPostThrottleWarnings > 0) ? (" (" + _suppressedPostThrottleWarnings + " weitere Meldungen gebuendelt)") : string.Empty); ((BaseUnityPlugin)this).Logger.LogWarning((object)("ChallengeHub Event gedrosselt, zu viele offene POSTs: " + eventType + text)); _suppressedPostThrottleWarnings = 0; _lastPostThrottleWarningAt = realtimeSinceStartup; } private bool ShouldDropEvent(string eventType, Dictionary payload) { float now = Time.realtimeSinceStartup; string text = (payload.ContainsKey("item") ? Convert.ToString(payload["item"]) : ""); string text2 = (payload.ContainsKey("piece") ? Convert.ToString(payload["piece"]) : ""); string text3 = (payload.ContainsKey("sourcePrefab") ? Convert.ToString(payload["sourcePrefab"]) : ""); string text4 = (payload.ContainsKey("biome") ? Convert.ToString(payload["biome"]) : ""); string text5 = (payload.ContainsKey("goal") ? Convert.ToString(payload["goal"]) : ""); string text6 = (payload.ContainsKey("scope") ? Convert.ToString(payload["scope"]) : ""); string text7 = (payload.ContainsKey("evidenceId") ? Convert.ToString(payload["evidenceId"]) : ""); string key = eventType + "|" + payload["playerId"]?.ToString() + "|" + text5 + "|" + text6 + "|" + text7 + "|" + text + "|" + text2 + "|" + text3 + "|" + text4; float num = ((eventType == "map_violation") ? 15f : (IsLowPriorityEvent(eventType) ? 2.5f : 0.25f)); if (_lastEventSentAt.TryGetValue(key, out var value) && now - value < num) { return true; } _lastEventSentAt[key] = now; if (_lastEventSentAt.Count > 500) { foreach (string item in (from kv in _lastEventSentAt where now - kv.Value > 60f select kv.Key).ToList()) { _lastEventSentAt.Remove(item); } } return false; } private static bool IsAdmissionDiagnosticEvent(string eventType) { switch ((eventType ?? string.Empty).Trim().ToLowerInvariant()) { case "settlement_activity": case "settlement_assigned": case "heartbeat": case "zone_status": case "zone_reset": case "resource_site_registered": case "resource_depleted": case "resource_reset": case "renaturation_stage": case "renaturation_completed": return true; default: return false; } } private static bool IsLowPriorityEvent(string eventType) { switch (eventType) { case "item_pickup": case "zone_status": case "mine_hit": case "tree_cut": case "build_completed": case "craft_completed": case "heartbeat": case "skill_change": case "pickable_collected": case "mod_signal": case "map_violation": return true; default: return false; } } internal IEnumerator PostJson(string url, string json, string outboxPath = null) { byte[] body = Encoding.UTF8.GetBytes(json); _inflightPosts++; try { for (int attempt = 1; attempt <= 5; attempt++) { UnityWebRequest req = new UnityWebRequest(url, "POST"); try { req.uploadHandler = (UploadHandler)new UploadHandlerRaw(body); req.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); req.timeout = 10; req.SetRequestHeader("Content-Type", "application/json"); ChallengeHubApiTokenFeature.ApplyAuthorization(req); yield return req.SendWebRequest(); if ((int)req.result == 1) { ChallengeHubEventOutbox.Delivered(outboxPath); yield break; } if (req.responseCode == 401) { ChallengeHubApiTokenFeature.Invalidate("Ereignis-POST: HTTP 401"); yield return ChallengeHubApiTokenFeature.EnsureAvailable(); continue; } if (req.responseCode == 423) { ChallengeHubEventOutbox.AdmissionRejected(outboxPath, "ChallengeHub-Zulassung abgelehnt: " + req.downloadHandler.text); ((BaseUnityPlugin)this).Logger.LogWarning((object)"ChallengeHub Ereignis wegen fehlender Charakterzulassung zurückgestellt; neuere Ereignisse werden weiter zugestellt."); yield break; } ((BaseUnityPlugin)this).Logger.LogWarning((object)("ChallengeHub POST fehlgeschlagen (Versuch " + attempt + "/5): " + req.error + " / " + req.downloadHandler.text)); } finally { ((IDisposable)req)?.Dispose(); } if (attempt < 5) { yield return (object)new WaitForSeconds(Mathf.Pow(2f, (float)(attempt - 1))); } } ChallengeHubEventOutbox.Failed(outboxPath, "Ereignis nach fuenf Versuchen nicht zugestellt; bleibt persistent in der Outbox."); ((BaseUnityPlugin)this).Logger.LogError((object)"ChallengeHub Event nach fuenf Versuchen nicht zugestellt; es bleibt persistent in der Outbox."); } finally { Plugin plugin = this; plugin._inflightPosts = Math.Max(0, plugin._inflightPosts - 1); } } internal void ReportSkillSnapshot(Player player) { long num = (((Object)(object)player != (Object)null) ? player.GetPlayerID() : 0); if (num != 0L && _skillSnapshotPlayerId != num) { _skillSnapshotPlayerId = num; _sentStartSkills = false; _lastSkillLevels.Clear(); } Dictionary dictionary = ReadSkillSnapshot(player); if (dictionary == null || dictionary.Count == 0) { if (!_sentStartSkills) { _sentStartSkills = true; SendEvent("start_skills", player, new Dictionary { { "skills", new Dictionary() }, { "newCharacterRequired", true } }); } return; } if (!_sentStartSkills) { _sentStartSkills = true; _lastSkillLevels.Clear(); foreach (KeyValuePair item in dictionary) { _lastSkillLevels[item.Key] = item.Value; } if (DeathRunCounterFeature.Enabled) { SendEvent("skills_snapshot", player, new Dictionary { { "skills", ToObjectDictionary(dictionary) }, { "snapshotKind", "deathrun_current" } }); } else { SendEvent("start_skills", player, new Dictionary { { "skills", ToObjectDictionary(dictionary) }, { "newCharacterRequired", true } }); } return; } SendEvent("skills_snapshot", player, new Dictionary { { "skills", ToObjectDictionary(dictionary) }, { "snapshotKind", "periodic_full" } }); foreach (KeyValuePair item2 in dictionary) { float value; float num2 = (_lastSkillLevels.TryGetValue(item2.Key, out value) ? value : 0f); if (item2.Value > num2 + 0.05f) { SendEvent("skill_change", player, new Dictionary { { "skill", item2.Key }, { "oldLevel", Math.Round(num2, 2) }, { "newLevel", Math.Round(item2.Value, 2) } }); } _lastSkillLevels[item2.Key] = Math.Max(num2, item2.Value); } } private Dictionary ToObjectDictionary(Dictionary values) { return ((IEnumerable>)values).ToDictionary((Func, string>)((KeyValuePair kv) => kv.Key), (Func, object>)((KeyValuePair kv) => Math.Round(kv.Value, 2))); } internal Dictionary ReadSkillSnapshot(Player player) { Dictionary dictionary = new Dictionary(); try { object obj = null; MethodInfo methodInfo = AccessTools.Method(typeof(Player), "GetSkills", (Type[])null, (Type[])null); if (methodInfo != null) { obj = methodInfo.Invoke(player, null); } if (obj == null) { FieldInfo fieldInfo = AccessTools.Field(typeof(Player), "m_skills"); if (fieldInfo != null) { obj = fieldInfo.GetValue(player); } } if (obj == null) { return dictionary; } MethodInfo methodInfo2 = AccessTools.Method(obj.GetType(), "GetSkillList", (Type[])null, (Type[])null); IEnumerable enumerable = ((methodInfo2 != null) ? (methodInfo2.Invoke(obj, null) as IEnumerable) : null); if (enumerable != null) { foreach (object item in enumerable) { if (item != null) { string text = ReadSkillName(item); float num = ReadSkillLevel(item); if (!string.IsNullOrWhiteSpace(text)) { dictionary[NormalizeKey(text)] = Mathf.Max(0f, num); } } } } FieldInfo fieldInfo2 = AccessTools.Field(obj.GetType(), "m_skillData"); IEnumerable enumerable2 = ((fieldInfo2 != null) ? (fieldInfo2.GetValue(obj) as IEnumerable) : null); if (enumerable2 != null) { foreach (object item2 in enumerable2) { if (item2 != null) { Type type = item2.GetType(); PropertyInfo property = type.GetProperty("Key"); PropertyInfo property2 = type.GetProperty("Value"); object obj2 = ((property != null) ? property.GetValue(item2, null) : null); object skill = ((property2 != null) ? property2.GetValue(item2, null) : null); string text2 = ((obj2 != null) ? NormalizeKey(obj2.ToString()) : ReadSkillName(skill)); float num2 = ReadSkillLevel(skill); if (!string.IsNullOrWhiteSpace(text2)) { dictionary[text2] = Mathf.Max(0f, num2); } } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Skill-Snapshot konnte nicht gelesen werden: " + ex.Message)); } return dictionary; } private string ReadSkillName(object skill) { if (skill == null) { return ""; } try { FieldInfo fieldInfo = AccessTools.Field(skill.GetType(), "m_info"); object obj = ((fieldInfo != null) ? fieldInfo.GetValue(skill) : null); if (obj != null) { FieldInfo fieldInfo2 = AccessTools.Field(obj.GetType(), "m_skill"); object obj2 = ((fieldInfo2 != null) ? fieldInfo2.GetValue(obj) : null); if (obj2 != null) { return obj2.ToString(); } } FieldInfo fieldInfo3 = AccessTools.Field(skill.GetType(), "m_name"); object obj3 = ((fieldInfo3 != null) ? fieldInfo3.GetValue(skill) : null); return (obj3 != null) ? obj3.ToString() : ""; } catch { return ""; } } private float ReadSkillLevel(object skill) { if (skill == null) { return 0f; } try { FieldInfo fieldInfo = AccessTools.Field(skill.GetType(), "m_level"); if (fieldInfo != null) { return Convert.ToSingle(fieldInfo.GetValue(skill), CultureInfo.InvariantCulture); } MethodInfo methodInfo = AccessTools.Method(skill.GetType(), "GetLevel", (Type[])null, (Type[])null); if (methodInfo != null) { return Convert.ToSingle(methodInfo.Invoke(skill, null), CultureInfo.InvariantCulture); } } catch { } return 0f; } internal void TrackLastHit(Character victim, HitData hit) { try { _killAttributionTracker.TrackLastHit(victim, hit); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Last-Hit konnte nicht gespeichert werden: " + ex.Message)); } } internal KillAttribution RegisterCreatureDeath(Character creature, Player fallbackPlayer) { return _killAttributionTracker.RegisterDeath(creature, fallbackPlayer); } internal KillAttribution FindRecentDropOwner(Vector3 position) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return _killAttributionTracker.FindRecentDropOwner(position); } internal void ReportDeath(Player player) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) ObsReplayFeature.NotifyPlayerDeath(); Dictionary dictionary = new Dictionary { { "biome", CurrentBiome(((Component)player).transform.position) }, { "position", SerializeVector(((Component)player).transform.position) } }; if (!DeathRunCounterFeature.Enabled) { GoalAutomationFeature.Observe(this, "death", player, dictionary); } SendDeathRunChronicleEvent("death", "Im Kampf gefallen", "death:" + DateTime.UtcNow.Ticks, player, dictionary); } internal void ReportBossKill(Player player, Character boss, KillAttribution attribution = null) { //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)boss == (Object)null) { return; } string text = CanonicalBossKey(NormalizeKey(boss.m_name) + " " + NormalizeKey(((Object)boss).name)); Player[] array = (Player[])(DeathRunCounterFeature.Enabled ? ((Array)(from candidate in Player.GetAllPlayers() where (Object)(object)candidate != (Object)null && Vector3.Distance(((Component)candidate).transform.position, ((Component)boss).transform.position) <= 150f select candidate).ToArray()) : ((Array)new Player[1] { player })); bool flag = !DeathRunCounterFeature.Enabled || DeathRunCounterFeature.FinishBossFight(text, array); Dictionary dictionary = new Dictionary { { "boss", text }, { "sourceObject", "boss" }, { "sourcePrefab", NormalizeKey(((Object)boss).name) }, { "killerPlayerId", attribution?.KillerPlayerId ?? player.GetPlayerID().ToString() }, { "killerPlayerName", attribution?.KillerPlayerName ?? player.GetPlayerName() }, { "attributionMethod", attribution?.Method ?? "nearest_player_fallback" }, { "killEventId", attribution?.EventId ?? "" }, { "biome", CurrentBiome(((Component)boss).transform.position) }, { "position", SerializeVector(((Component)boss).transform.position) }, { "progressionAllowed", flag }, { "inventoryAllowed", flag }, { "participantCharacterIds", array.Select((Player candidate) => candidate.GetPlayerID().ToString()).ToArray() }, { "expectedBoss", DeathRunCounterFeature.ExpectedBoss } }; foreach (KeyValuePair item in DeathRunCounterFeature.RunFields()) { dictionary[item.Key] = item.Value; } SendDeathRunChronicleEvent("boss_kill", "Boss bezwungen: " + text, "boss:" + text, player, dictionary); } internal void ReportBossCompletion(Player player, string bossKey, Vector3 position) { //IL_004d: 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) if (!((Object)(object)player == (Object)null)) { SendEvent("boss_kill", player, new Dictionary { { "boss", CanonicalBossKey(bossKey) }, { "sourceObject", "boss_phase" }, { "attributionMethod", "server_authoritative_boss_participation" }, { "biome", CurrentBiome(position) }, { "position", SerializeVector(position) } }); } } internal static string CanonicalBossKey(string value) { string text = NormalizeKey(value).Replace("-", "_"); if (text.Contains("eikthyr")) { return "eikthyr"; } if (text.Contains("gd_king") || text.Contains("gdking") || text.Contains("elder")) { return "elder"; } if (text.Contains("bonemass")) { return "bonemass"; } if (text.Contains("dragonqueen") || text.Contains("moder")) { return "moder"; } if (text.Contains("goblinking") || text.Contains("yagluth")) { return "yagluth"; } if (text.Contains("seekerqueen") || text.Contains("thequeen") || text.Contains("queen")) { return "queen"; } if (text.Contains("fader")) { return "fader"; } return text; } internal void ReportCreatureKill(Player player, Character creature, KillAttribution attribution = null) { //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)player == (Object)null) && !((Object)(object)creature == (Object)null)) { string value = NormalizeKey(creature.m_name); SendEvent("creature_kill", player, new Dictionary { { "sourceObject", "creature" }, { "sourcePrefab", NormalizeKey(((Object)creature).name) }, { "creature", value }, { "killerPlayerId", attribution?.KillerPlayerId ?? player.GetPlayerID().ToString() }, { "killerPlayerName", attribution?.KillerPlayerName ?? player.GetPlayerName() }, { "attributionMethod", attribution?.Method ?? "nearest_player_fallback" }, { "killEventId", attribution?.EventId ?? "" }, { "dropSourceEventId", attribution?.EventId ?? "" }, { "biome", CurrentBiome(((Component)creature).transform.position) }, { "position", SerializeVector(((Component)creature).transform.position) } }); } } private static IDictionary GetItemCustomData(ItemData item) { if (item == null) { return null; } try { FieldInfo field = typeof(ItemData).GetField("m_customData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { return null; } IDictionary dictionary = field.GetValue(item) as IDictionary; if (dictionary == null) { dictionary = new Dictionary(); field.SetValue(item, dictionary); } return dictionary; } catch { return null; } } private static void SetItemMeta(ItemData item, string key, string value) { if (item != null && !string.IsNullOrWhiteSpace(key) && !string.IsNullOrWhiteSpace(value)) { IDictionary itemCustomData = GetItemCustomData(item); if (itemCustomData != null) { itemCustomData["ChallengeHub." + key] = value; } } } private static string GetItemMeta(ItemData item, string key) { if (item == null || string.IsNullOrWhiteSpace(key)) { return ""; } IDictionary itemCustomData = GetItemCustomData(item); if (itemCustomData == null) { return ""; } if (!itemCustomData.TryGetValue("ChallengeHub." + key, out var value)) { return ""; } return value; } internal static string EffectiveWorldName() { try { if (WorldName != null && !string.IsNullOrWhiteSpace(WorldName.Value)) { return WorldName.Value; } if ((Object)(object)Instance != (Object)null && Instance.HasRemoteConfig() && Instance.RemoteConfig != null && !string.IsNullOrWhiteSpace(Instance.RemoteConfig.worldName)) { return Instance.RemoteConfig.worldName; } } catch { } return string.Empty; } private static void SetZdoString(ItemDrop itemDrop, string key, string value) { if ((Object)(object)itemDrop == (Object)null || string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(value)) { return; } try { ZNetView component = ((Component)itemDrop).GetComponent(); if (!((Object)(object)component == (Object)null)) { object zDO = component.GetZDO(); zDO?.GetType().GetMethod("Set", new Type[2] { typeof(string), typeof(string) })?.Invoke(zDO, new object[2] { "ChallengeHub." + key, value }); } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogDebug((object)("Item-ZDO-Metadaten konnten nicht geschrieben werden: " + ex.Message)); } } } private void WriteAttributionIntoItem(ItemDrop itemDrop, KillAttribution attribution, string itemName, int quantity) { if ((Object)(object)itemDrop == (Object)null || itemDrop.m_itemData == null || attribution == null) { return; } foreach (KeyValuePair item in new Dictionary { { "item", itemName }, { "quantity", quantity.ToString() }, { "creditedPlayerId", attribution.KillerPlayerId }, { "creditedPlayerName", attribution.KillerPlayerName }, { "killerPlayerId", attribution.KillerPlayerId }, { "killerPlayerName", attribution.KillerPlayerName }, { "dropSourcePrefab", attribution.CreaturePrefab }, { "dropSourceName", attribution.CreatureName }, { "dropSourceEventId", attribution.EventId }, { "attributionMethod", attribution.Method + "_drop_spawn" }, { "serverId", EffectiveServerId() ?? "" }, { "worldName", EffectiveWorldName() }, { "challengeShortCode", EffectiveChallengeShortCode() ?? "" }, { "createdAt", DateTime.UtcNow.ToString("o") } }) { SetItemMeta(itemDrop.m_itemData, item.Key, item.Value); SetZdoString(itemDrop, item.Key, item.Value); } } private static Dictionary ReadAttributionFromItem(ItemData item) { Dictionary dictionary = new Dictionary(); if (item == null) { return dictionary; } string[] array = new string[14] { "item", "quantity", "creditedPlayerId", "creditedPlayerName", "killerPlayerId", "killerPlayerName", "dropSourcePrefab", "dropSourceName", "dropSourceEventId", "attributionMethod", "serverId", "worldName", "challengeShortCode", "createdAt" }; foreach (string key in array) { string itemMeta = GetItemMeta(item, key); if (!string.IsNullOrWhiteSpace(itemMeta)) { dictionary[key] = itemMeta; } } return dictionary; } internal void ReportDropSpawn(ItemDrop itemDrop) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)itemDrop == (Object)null || itemDrop.m_itemData == null || itemDrop.m_itemData.m_shared == null) { return; } KillAttribution killAttribution = FindRecentDropOwner(((Component)itemDrop).transform.position); if (killAttribution == null || (Object)(object)killAttribution.KillerPlayer == (Object)null) { return; } int instanceID = ((Object)itemDrop).GetInstanceID(); if (!_reportedDropSpawnIds.Contains(instanceID)) { _reportedDropSpawnIds.Add(instanceID); if (_reportedDropSpawnIds.Count > 2000) { _reportedDropSpawnIds.Clear(); } ItemData itemData = itemDrop.m_itemData; string text = NormalizeKey(((Object)(object)itemData.m_dropPrefab != (Object)null) ? ((Object)itemData.m_dropPrefab).name : (((Object)itemDrop).name ?? itemData.m_shared.m_name)); string input = ((object)Unsafe.As(ref itemData.m_shared.m_itemType)/*cast due to .constrained prefix*/).ToString(); int num = Math.Max(1, itemData.m_stack); WriteAttributionIntoItem(itemDrop, killAttribution, text, num); Dictionary extra = new Dictionary { { "item", text }, { "itemType", NormalizeKey(input) }, { "quantity", num }, { "sourceObject", killAttribution.CreaturePrefab }, { "sourcePrefab", killAttribution.CreaturePrefab }, { "biome", CurrentBiome(((Component)itemDrop).transform.position) }, { "position", SerializeVector(((Component)itemDrop).transform.position) }, { "creditedPlayerId", killAttribution.KillerPlayerId }, { "creditedPlayerName", killAttribution.KillerPlayerName }, { "killerPlayerId", killAttribution.KillerPlayerId }, { "killerPlayerName", killAttribution.KillerPlayerName }, { "dropSourcePrefab", killAttribution.CreaturePrefab }, { "dropSourceName", killAttribution.CreatureName }, { "dropSourceEventId", killAttribution.EventId }, { "attributionMethod", killAttribution.Method + "_drop_spawn" } }; SendEvent("item_drop_spawned", killAttribution.KillerPlayer, extra); } } internal void ReportItemPickup(Player player, ItemData item) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || item == null || item.m_shared == null) { return; } string text = NormalizeKey(((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : item.m_shared.m_name); string input = ((object)Unsafe.As(ref item.m_shared.m_itemType)/*cast due to .constrained prefix*/).ToString(); int num = Math.Max(1, item.m_stack); Dictionary dictionary = ReadAttributionFromItem(item); KillAttribution killAttribution = ((dictionary.Count > 0) ? null : FindRecentDropOwner(((Component)player).transform.position)); string value; string value2; int result; string value3; string value4; Dictionary dictionary2 = new Dictionary { { "item", dictionary.TryGetValue("item", out value) ? value : text }, { "itemType", NormalizeKey(input) }, { "quantity", (dictionary.TryGetValue("quantity", out value2) && int.TryParse(value2, out result)) ? result : num }, { "sourceObject", dictionary.TryGetValue("dropSourcePrefab", out value3) ? value3 : ((killAttribution != null) ? killAttribution.CreaturePrefab : "inventory_add") }, { "sourcePrefab", dictionary.TryGetValue("dropSourcePrefab", out value4) ? value4 : ((killAttribution != null) ? killAttribution.CreaturePrefab : "") }, { "biome", CurrentBiome(((Component)player).transform.position) }, { "position", SerializeVector(((Component)player).transform.position) }, { "collectorPlayerId", player.GetPlayerID().ToString() }, { "collectorPlayerName", player.GetPlayerName() } }; if (dictionary.Count > 0) { foreach (KeyValuePair item2 in dictionary) { dictionary2[item2.Key] = item2.Value; } dictionary2["attributionMethod"] = (dictionary.TryGetValue("attributionMethod", out var value5) ? (value5 + "_pickup_from_item_meta") : "item_custom_data_pickup"); dictionary2["dropAlreadyCredited"] = true; dictionary2["itemAttributionStoredInItem"] = true; } else if (killAttribution != null) { dictionary2["creditedPlayerId"] = killAttribution.KillerPlayerId; dictionary2["creditedPlayerName"] = killAttribution.KillerPlayerName; dictionary2["killerPlayerId"] = killAttribution.KillerPlayerId; dictionary2["killerPlayerName"] = killAttribution.KillerPlayerName; dictionary2["dropSourcePrefab"] = killAttribution.CreaturePrefab; dictionary2["dropSourceName"] = killAttribution.CreatureName; dictionary2["dropSourceEventId"] = killAttribution.EventId; dictionary2["attributionMethod"] = killAttribution.Method + "_drop_pickup"; dictionary2["dropAlreadyCredited"] = true; } SendEvent("item_pickup", player, dictionary2); } internal void ReportTrophy(Player player, ItemData item) { //IL_0254: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || item == null) { return; } string text = NormalizeKey(((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : item.m_shared.m_name); Dictionary dictionary = ReadAttributionFromItem(item); string b = player.GetPlayerID().ToString(); string value; string text2 = (dictionary.TryGetValue("dropSourceEventId", out value) ? value : string.Empty); string value2; string text3 = (dictionary.TryGetValue("killerPlayerId", out value2) ? value2 : string.Empty); string value3; string text4 = (dictionary.TryGetValue("dropSourcePrefab", out value3) ? value3 : string.Empty); string value4; string text5 = (dictionary.TryGetValue("dropSourceName", out value4) ? value4 : string.Empty); string value5; string text6 = (dictionary.TryGetValue("attributionMethod", out value5) ? value5 : string.Empty); string text7 = KillAttributionTracker.TrophySpecies(text); string b2 = KillAttributionTracker.TrophySpecies(text4 + " " + text5); bool flag = !string.IsNullOrWhiteSpace(text2) && string.Equals(text3, b, StringComparison.Ordinal) && text6.IndexOf("last_hit", StringComparison.OrdinalIgnoreCase) >= 0 && !string.IsNullOrWhiteSpace(text7) && string.Equals(text7, b2, StringComparison.OrdinalIgnoreCase); KillAttribution killAttribution = (flag ? null : _killAttributionTracker.FindRecentMatchingTrophyKill(player, text)); if (!flag && killAttribution == null) { if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { CombatGoalFeature.RequestServerTrophyValidation(player, text); ((BaseUnityPlugin)this).Logger.LogInfo((object)("ChallengeHub Trophäe wird serverseitig gegen den eigenen Last-Hit geprüft: " + text)); } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)("ChallengeHub Trophäe nicht gewertet: kein passender eigener Kill innerhalb von 60 Sekunden; Trophäe=" + text)); } return; } if (!_seenTrophiesByBiome.ContainsKey("catalog")) { _seenTrophiesByBiome["catalog"] = new HashSet(); } if (_seenTrophiesByBiome["catalog"].Add(text)) { string value6 = (flag ? text2 : killAttribution.EventId); string value7 = (flag ? text4 : killAttribution.CreaturePrefab); string value8 = (flag ? text5 : killAttribution.CreatureName); string value9 = (flag ? text3 : killAttribution.KillerPlayerId); string value11; string value10 = ((!flag) ? killAttribution.KillerPlayerName : (dictionary.TryGetValue("killerPlayerName", out value11) ? value11 : player.GetPlayerName())); SendDeathRunAwareTrophy(player, text, new Dictionary { { "biome", CurrentBiome(((Component)player).transform.position) }, { "trophy", text }, { "creditedPlayerId", value9 }, { "creditedPlayerName", value10 }, { "dropSourcePrefab", value7 }, { "dropSourceName", value8 }, { "dropSourceEventId", value6 }, { "killEventId", value6 }, { "killAgeSeconds", flag ? (-1f) : Mathf.Max(0f, Time.realtimeSinceStartup - killAttribution.Time) }, { "attributionMethod", flag ? "verified_server_drop_meta_then_trophy_pickup_60s" : "verified_kill_then_trophy_pickup_60s" } }); } } private void SendDeathRunAwareTrophy(Player player, string trophyName, Dictionary fields) { SendDeathRunChronicleEvent("trophy_found", "Fehlende Trophäe gefunden: " + trophyName, "trophy:" + trophyName, player, fields); } private void SendDeathRunChronicleEvent(string eventType, string label, string scope, Player player, Dictionary fields) { if (DeathRunCounterFeature.Enabled && (Object)(object)player == (Object)(object)Player.m_localPlayer) { fields = fields ?? new Dictionary(); fields["evidenceId"] = IngameCameraFeature.AutomaticChronicleEvidenceId(eventType, scope, player); SendEvent(eventType, player, fields); IngameCameraFeature.CaptureAutomaticChronicle(eventType, label, scope, fields, sendEventAfterCapture: false); } else { SendEvent(eventType, player, fields); } } internal void ReportServerValidatedTrophy(Player player, string trophyName) { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || string.IsNullOrWhiteSpace(trophyName) || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } string text = NormalizeKey(trophyName); KillAttribution killAttribution = _killAttributionTracker.FindRecentMatchingTrophyKill(player, text); if (killAttribution == null) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Server-Trophäenprüfung abgelehnt: kein passender eigener Last-Hit innerhalb von 60 Sekunden; Trophäe=" + text)); return; } if (!_seenTrophiesByBiome.ContainsKey("catalog")) { _seenTrophiesByBiome["catalog"] = new HashSet(); } if (_seenTrophiesByBiome["catalog"].Add(text)) { SendDeathRunAwareTrophy(player, text, new Dictionary { { "biome", CurrentBiome(((Component)player).transform.position) }, { "trophy", text }, { "creditedPlayerId", killAttribution.KillerPlayerId }, { "creditedPlayerName", killAttribution.KillerPlayerName }, { "dropSourcePrefab", killAttribution.CreaturePrefab }, { "dropSourceName", killAttribution.CreatureName }, { "dropSourceEventId", killAttribution.EventId }, { "killEventId", killAttribution.EventId }, { "killAgeSeconds", Mathf.Max(0f, Time.realtimeSinceStartup - killAttribution.Time) }, { "attributionMethod", "server_verified_last_hit_then_trophy_pickup_rpc_60s" } }); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Trophäe serverseitig verifiziert und gemeldet: " + text + " nach " + killAttribution.CreaturePrefab + ".")); } } internal void ReportBuildCompleted(Player player, object builtObject, string method = "build_completed") { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)player == (Object)null)) { string text = ExtractName(builtObject); PartnershipGameplayFeature.OnBuildCompleted(player, builtObject, text); Dictionary dictionary = new Dictionary { { "piece", text }, { "item", text }, { "sourceObject", method }, { "biome", CurrentBiome(((Component)player).transform.position) }, { "position", SerializeVector(((Component)player).transform.position) }, { "attributionMethod", "mod_build_patch" }, { "goalLocallyObserved", true } }; GoalAutomationFeature.Observe(this, "build_completed", player, dictionary); if (EffectiveEvidenceTrackingEnabled() && EffectiveBuildEventsEnabled()) { SendEvent("build_completed", player, dictionary); } } } internal void ReportCraftCompleted(Player player, object craftedObject, string method = "craft_completed") { //IL_0046: 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) if (!((Object)(object)player == (Object)null)) { string value = ExtractName(craftedObject); Dictionary dictionary = new Dictionary { { "item", value }, { "recipe", value }, { "sourceObject", method }, { "biome", CurrentBiome(((Component)player).transform.position) }, { "position", SerializeVector(((Component)player).transform.position) }, { "attributionMethod", "mod_craft_patch" }, { "goalLocallyObserved", true } }; GoalAutomationFeature.Observe(this, "craft_completed", player, dictionary); if (EffectiveEvidenceTrackingEnabled() && EffectiveCraftEventsEnabled()) { SendEvent("craft_completed", player, dictionary); } } } internal void ReportGatherSignal(Player player, string eventType, object targetObject, string method = "gather_signal") { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)player == (Object)null)) { string value = ExtractName(targetObject); Dictionary dictionary = new Dictionary { { "item", value }, { "targetPrefab", value }, { "sourceObject", method }, { "biome", CurrentBiome(((Component)player).transform.position) }, { "position", SerializeVector(((Component)player).transform.position) }, { "attributionMethod", "mod_gather_patch" }, { "goalLocallyObserved", true } }; GoalAutomationFeature.Observe(this, eventType, player, dictionary); if (EffectiveEvidenceTrackingEnabled() && EffectiveGatherEventsEnabled()) { SendEvent(eventType, player, dictionary); } } } internal void ReportExplorationSignal(Player player, string signal, object targetObject, string method = "exploration_signal") { //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return; } string text = ExtractName(targetObject); string text2 = NormalizeKey(BossVegvisirDisablePatch.Describe(targetObject, null)); string text3 = NormalizeKey(signal); string text4 = NormalizeKey(text + " " + text2); if (text3.Contains("altar") || text3.Contains("vegvisir") || text3.Contains("trader") || text4.Contains("offeringbowl") || text4.Contains("bosstone") || text4.Contains("eikthyr") || text4.Contains("gdking") || text4.Contains("bonemass") || text4.Contains("goblinking") || text4.Contains("dragonqueen") || text4.Contains("fader") || (EffectiveEvidenceTrackingEnabled() && EffectiveExplorationEventsEnabled())) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("ChallengeHub Erkundungsfund erkannt: Signal=" + text3 + "; Ziel=" + text4 + "; Quelle=" + method)); ExplorationPartnerFeature.ReportDiscovery(this, player, signal, text, method); SendEvent("mod_signal", player, new Dictionary { { "signal", signal }, { "targetPrefab", text4 }, { "targetName", text }, { "bossIdentity", text2 }, { "sourceObject", method }, { "biome", CurrentBiome(((Component)player).transform.position) }, { "position", SerializeVector(((Component)player).transform.position) }, { "attributionMethod", "mod_exploration_patch" } }); string text5 = (text3.Contains("altar") ? "boss_altar_found" : (text3.Contains("vegvisir") ? "boss_vegvisir_seen" : (text3.Contains("trader") ? "trader_found" : string.Empty))); if (!string.IsNullOrWhiteSpace(text5)) { Vector3 position = ((Component)player).transform.position; string scope = text5 + ":" + text4 + ":" + Mathf.RoundToInt(position.x / 32f) + ":" + Mathf.RoundToInt(position.z / 32f); string label = ((text5 == "boss_altar_found") ? "Bossaltar entdeckt" : ((text5 == "boss_vegvisir_seen") ? "Boss-Runenstein entdeckt" : "Händler entdeckt")); IngameCameraFeature.CaptureAutomaticChronicle(text5, label, scope, new Dictionary { { "targetPrefab", text4 }, { "targetName", text }, { "bossIdentity", text2 }, { "biome", CurrentBiome(position) }, { "discoveryType", text5 } }); } } } internal void ReportMapViolation(Player player) { //IL_004c: 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 (!((Object)(object)player == (Object)null) && EffectiveMapViolationPenaltyEnabled()) { SendEvent("map_violation", player, new Dictionary { { "label", "Karte geöffnet / No-Map-VerstoÃÅÂÂ\u00b8" }, { "points", -100 }, { "biome", CurrentBiome(((Component)player).transform.position) }, { "position", SerializeVector(((Component)player).transform.position) }, { "attributionMethod", "mod_minimap_patch" } }); } } private static string ExtractName(object value) { try { if (value == null) { return "unknown"; } GameObject val = (GameObject)((value is GameObject) ? value : null); if (val != null) { return NormalizeKey(((Object)val).name); } Component val2 = (Component)((value is Component) ? value : null); if (val2 != null) { return NormalizeKey(((Object)val2).name); } Type type = value.GetType(); string[] array = new string[4] { "m_name", "m_itemName", "m_prefab", "name" }; foreach (string text in array) { FieldInfo fieldInfo = AccessTools.Field(type, text); if (!(fieldInfo == null)) { object value2 = fieldInfo.GetValue(value); GameObject val3 = (GameObject)((value2 is GameObject) ? value2 : null); if (val3 != null) { return NormalizeKey(((Object)val3).name); } string text2 = Convert.ToString(value2); if (!string.IsNullOrWhiteSpace(text2)) { return NormalizeKey(text2); } } } return NormalizeKey(value.ToString()); } catch { return "unknown"; } } private void ReportPortalCounts(Player player) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) TeleportWorld[] array = Object.FindObjectsByType((FindObjectsSortMode)0); Dictionary dictionary = new Dictionary(); TeleportWorld[] array2 = array; foreach (TeleportWorld val in array2) { if (!((Object)(object)val == (Object)null) && ((Behaviour)val).isActiveAndEnabled) { string key = CurrentBiome(((Component)val).transform.position); dictionary[key] = ((!dictionary.TryGetValue(key, out var value)) ? 1 : (value + 1)); } } foreach (KeyValuePair item in dictionary) { float value2; float num = (_lastPortalReportByBiome.TryGetValue(item.Key, out value2) ? value2 : 0f); if (!(Time.realtimeSinceStartup - num < 25f)) { _lastPortalReportByBiome[item.Key] = Time.realtimeSinceStartup; SendEvent("portal_count", player, new Dictionary { { "biome", item.Key }, { "activePortals", item.Value } }); } } } internal float IncomingDamageMultiplier() { return DifficultyMultiplier(EffectiveDefaultDifficulty(), incoming: true); } internal float OutgoingDamageMultiplier() { return DifficultyMultiplier(EffectiveDefaultDifficulty(), incoming: false); } private float DifficultyMultiplier(string difficulty, bool incoming) { string text = (difficulty ?? "medium").ToLowerInvariant(); if (!(text == "easy")) { if (text == "hard") { if (!incoming) { return RemoteConfig.Difficulty("hard").playerDamageMultiplier; } return RemoteConfig.Difficulty("hard").enemyDamageMultiplier; } if (!incoming) { return RemoteConfig.Difficulty("medium").playerDamageMultiplier; } return RemoteConfig.Difficulty("medium").enemyDamageMultiplier; } if (!incoming) { return RemoteConfig.Difficulty("easy").playerDamageMultiplier; } return RemoteConfig.Difficulty("easy").enemyDamageMultiplier; } private bool IsServer() { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } private void TryResetInactiveZones() { //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_03dd: Unknown result type (might be due to invalid IL or missing references) //IL_04a8: Unknown result type (might be due to invalid IL or missing references) try { List allPlayers = Player.GetAllPlayers(); if (allPlayers == null || allPlayers.Count == 0) { return; } Player val = ((IEnumerable)allPlayers).FirstOrDefault((Func)((Player player) => (Object)(object)player != (Object)null)); if ((Object)(object)val == (Object)null) { return; } HashSet protectedNames = SplitCsv(string.Join(",", EffectiveProtectedPrefabs())); HashSet resetKeywords = SplitCsv(string.Join(",", EffectiveResetPrefabKeywords())); Piece[] array = Object.FindObjectsByType((FindObjectsSortMode)0); ZNetView[] allZNetViews = Object.FindObjectsByType((FindObjectsSortMode)0); float radius = ((HasRemoteConfig() && RemoteConfig.zoneReset != null && RemoteConfig.zoneReset.zoneRadiusMeters > 0f) ? RemoteConfig.zoneReset.zoneRadiusMeters : 64f); int num = Math.Max(5, (HasRemoteConfig() && RemoteConfig.zoneReset != null && RemoteConfig.zoneReset.minInactiveMinutes > 0) ? RemoteConfig.zoneReset.minInactiveMinutes : 120); DateTime utcNow = DateTime.UtcNow; HashSet hashSet = new HashSet(); ReportGuardianStoneZones(val, array, utcNow, num, hashSet); foreach (Player item in allPlayers) { if (!((Object)(object)item == (Object)null)) { Vector3 center = ((Component)item).transform.position; string text = ZoneIdFor(center, radius); hashSet.Add(text); ZoneRuntimeState zoneRuntimeState = EnsureZoneState(text, center); zoneRuntimeState.Center = center; zoneRuntimeState.LastSeenAt = utcNow; string reason; bool flag = IsAlwaysProtectedZone(center, radius, allZNetViews, out reason) || array.Any((Piece piece) => (Object)(object)piece != (Object)null && Vector3.Distance(((Component)piece).transform.position, center) <= radius && protectedNames.Contains(NormalizeKey(((Object)piece).name))); if (flag && string.IsNullOrWhiteSpace(reason)) { reason = "Spielerbasis/Schutz-Prefab"; } zoneRuntimeState.Protected = flag; zoneRuntimeState.ProtectedReason = reason; if (flag) { MaybeSendZoneStatus(item, zoneRuntimeState, "geschützt", null, reason); } else { MaybeSendZoneStatus(item, zoneRuntimeState, "aktiv", utcNow.AddMinutes(num), "Spieler in Zone; Reset frühestens nach Inaktivität"); } } } foreach (ZoneRuntimeState item2 in _zoneStates.Values.ToList()) { if (hashSet.Contains(item2.ZoneId)) { continue; } bool flag2 = IsGuardianRuntimeZone(item2); if (!item2.Protected || flag2) { if (flag2 && item2.Protected) { item2.Protected = false; item2.GuardianRemovedAt = item2.GuardianRemovedAt ?? utcNow; item2.ProtectedReason = "Wächterstein entfernt; Reset-Timer läuft wieder."; item2.LastStatus = ""; item2.LastReportedAt = DateTime.MinValue; } TimeSpan timeSpan = utcNow - item2.LastSeenAt; DateTime value = item2.LastSeenAt.AddMinutes(num); if (timeSpan.TotalMinutes < (double)num) { MaybeSendZoneStatus(val, item2, "inaktiv", value, $"Seit {Math.Round(timeSpan.TotalMinutes)} Minuten inaktiv"); continue; } if (IsAlwaysProtectedZone(item2.Center, radius, allZNetViews, out var reason2)) { item2.Protected = true; item2.ProtectedReason = reason2; MaybeSendZoneStatus(val, item2, "geschützt", null, reason2); continue; } int num2 = ResetObjectsInZone(item2.Center, radius, allZNetViews, resetKeywords); item2.LastResetAt = utcNow; item2.LastSeenAt = utcNow; SendEvent("zone_reset", val, new Dictionary { { "zoneId", item2.ZoneId }, { "zoneName", item2.ZoneName }, { "zoneStatus", "reset_done" }, { "biome", item2.Biome }, { "removedObjects", num2 }, { "nextResetAt", utcNow.AddMinutes(num).ToString("O") }, { "protectedReason", "" }, { "position", SerializeVector(item2.Center) }, { "label", "Zone-Reset" }, { "points", 0 } }); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"ChallengeHub Zone-Reset {item2.ZoneName}: {num2} Objekte entfernt."); } } if (_zoneStates.Count <= 120) { return; } foreach (string item3 in (from zone in _zoneStates.Values.OrderBy((ZoneRuntimeState zone) => zone.LastSeenAt).Take(_zoneStates.Count - 120) select zone.ZoneId).ToList()) { _zoneStates.Remove(item3); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Zone-Reset konnte nicht ausgeführt werden: " + ex.Message)); } } private ZoneRuntimeState EnsureZoneState(string zoneId, Vector3 center) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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 (!_zoneStates.TryGetValue(zoneId, out var value)) { string biome = CurrentBiome(center); value = new ZoneRuntimeState { ZoneId = zoneId, ZoneName = ZoneNameFor(biome, center), Biome = biome, Center = center, LastSeenAt = DateTime.UtcNow, LastStatus = "neu", LastReportedAt = DateTime.MinValue }; _zoneStates[zoneId] = value; } return value; } private string ZoneIdFor(Vector3 center, float zoneSize) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(32f, zoneSize); string arg = CurrentBiome(center); int num2 = Mathf.FloorToInt(center.x / num); int num3 = Mathf.FloorToInt(center.z / num); return $"{arg}_{num2}_{num3}"; } private string ZoneNameFor(string biome, Vector3 center) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) string text = DisplayBiomeName(biome); int gx = Mathf.FloorToInt(center.x / 64f); int gz = Mathf.FloorToInt(center.z / 64f); return text + " " + ZoneLetterForGrid(gx, gz); } private static string ZoneLetterForGrid(int gx, int gz) { int num = Math.Abs((gx * 73856093) ^ (gz * 19349663)) % 26; return ((char)(65 + num)).ToString(); } private static string DisplayBiomeName(string biome) { switch (NormalizeKey(biome)) { case "meadows": return "Wiese"; case "black_forest": return "Schwarzwald"; case "swamp": return "Sumpf"; case "mountains": return "Berge"; case "plains": return "Ebene"; case "mistlands": return "Nebelande"; case "ashlands": return "Aschlande"; case "ocean": return "Ozean"; default: if (!string.IsNullOrWhiteSpace(biome)) { return biome.Replace("_", " "); } return "Welt"; } } private void MaybeSendZoneStatus(Player player, ZoneRuntimeState zone, string status, DateTime? nextResetAt, string reason) { //IL_019e: Unknown result type (might be due to invalid IL or missing references) DateTime utcNow = DateTime.UtcNow; if (!(zone.LastStatus == status) || !((utcNow - zone.LastReportedAt).TotalSeconds < 60.0)) { zone.LastStatus = status; zone.LastReportedAt = utcNow; SendEvent("zone_status", player, new Dictionary { { "zoneId", zone.ZoneId }, { "zoneName", zone.ZoneName }, { "zoneStatus", status }, { "zoneType", string.IsNullOrWhiteSpace(zone.ZoneType) ? "world" : zone.ZoneType }, { "guardianType", zone.GuardianType ?? "" }, { "guardianColor", zone.GuardianColor ?? "" }, { "guardianRecipe", zone.GuardianRecipe ?? "" }, { "biome", zone.Biome }, { "nextResetAt", nextResetAt.HasValue ? nextResetAt.Value.ToString("O") : "" }, { "lastSeenAt", zone.LastSeenAt.ToString("O") }, { "lastResetAt", zone.LastResetAt.HasValue ? zone.LastResetAt.Value.ToString("O") : "" }, { "protectedReason", reason ?? "" }, { "position", SerializeVector(zone.Center) } }); } } private IEnumerator FarmerGuardianPresenceLoop() { while (true) { yield return (object)new WaitForSeconds(EffectiveFarmerPresenceScanSeconds()); if (!ChallengeHubServerGateFeature.GameplayAllowed) { _farmerPresenceAnchors.Clear(); continue; } try { if (!EffectiveGuardianStonesEnabled() || !EffectiveFarmerSimulatePlayerPresence()) { _farmerPresenceAnchors.Clear(); } else if (IsServer()) { ScanFarmerPresenceAnchors(); ProcessFarmerCatchup(DateTime.UtcNow); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Bauer-Wächter-Präsenzscan fehlgeschlagen: " + ex.Message)); } } } private void ScanFarmerPresenceAnchors() { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) Piece[] array = SnapshotRuntimeGuardianPieces(); if (array.Length == 0) { _farmerPresenceAnchors.Clear(); return; } DateTime utcNow = DateTime.UtcNow; HashSet seenZoneIds = new HashSet(); HashSet guardianPrefabs = SplitCsv(EffectiveGuardianStonePrefabNames()); Piece[] array2 = array; foreach (Piece val in array2) { if ((Object)(object)val == (Object)null) { continue; } string prefab = NormalizeKey(((Object)val).name); if (IsGuardianStonePrefab(prefab, guardianPrefabs)) { string text = GuardianTypeForPiece(val, prefab); if (!(NormalizeGuardianType(text) != "farmer") && GuardianPreventsReset(text)) { string text2 = GuardianZoneIdFor(text, ((Component)val).transform.position); seenZoneIds.Add(text2); RegisterFarmerPresenceAnchor(text2, val, utcNow, text); } } } foreach (string item in _farmerPresenceAnchors.Keys.Where((string key) => !seenZoneIds.Contains(key)).ToList()) { _farmerPresenceAnchors.Remove(item); } } private void RegisterFarmerPresenceAnchor(string zoneId, Piece farmerPiece, DateTime now, string guardianType) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrWhiteSpace(zoneId) && !((Object)(object)farmerPiece == (Object)null)) { string boundChestId = ReadZdoString((Component)(object)farmerPiece, "ChallengeHub.Farmer.BoundChestId"); Vector3? boundChestPosition = TryParseVector(ReadZdoString((Component)(object)farmerPiece, "ChallengeHub.Farmer.BoundChestPosition")); _farmerPresenceAnchors[zoneId] = new FarmerPresenceAnchor { ZoneId = zoneId, Center = ((Component)farmerPiece).transform.position, LastSeenAt = now, GuardianType = NormalizeGuardianType(guardianType), BoundChestId = boundChestId, BoundChestPosition = boundChestPosition }; } } private void RemoveFarmerPresenceAnchor(string zoneId) { if (!string.IsNullOrWhiteSpace(zoneId)) { _farmerPresenceAnchors.Remove(zoneId); } } internal bool IsFarmerPresenceActiveAt(Vector3 position) { //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) try { if (!EffectiveGuardianStonesEnabled() || !EffectiveFarmerSimulatePlayerPresence()) { return false; } if (_farmerPresenceAnchors.Count == 0) { return false; } DateTime utcNow = DateTime.UtcNow; double num = Math.Max(15.0, (double)EffectiveFarmerPresenceScanSeconds() * 4.0); float num2 = Mathf.Max(8f, EffectiveFarmerPresenceRadius()); foreach (FarmerPresenceAnchor item in _farmerPresenceAnchors.Values.ToList()) { if (item != null) { if ((utcNow - item.LastSeenAt).TotalSeconds > num) { _farmerPresenceAnchors.Remove(item.ZoneId); } else if (Vector3.Distance(position, item.Center) <= num2) { return true; } } } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogDebug((object)("Bauer-Wächter-Präsenzprüfung fehlgeschlagen: " + ex.Message)); } } return false; } private IEnumerator NeutralGuardianPresencePulseLoop() { while (true) { yield return (object)new WaitForSeconds(EffectiveNeutralPresenceScanSeconds()); if (!ChallengeHubServerGateFeature.GameplayAllowed) { _neutralPresenceAnchors.Clear(); continue; } try { if (!EffectiveGuardianStonesEnabled() || !EffectiveNeutralPresencePulseEnabled()) { _neutralPresenceAnchors.Clear(); } else if (IsServer()) { DateTime utcNow = DateTime.UtcNow; ScanNeutralPresenceAnchors(utcNow); UpdateNeutralPresencePulses(utcNow); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Neutral-Wächter-Präsenzpuls fehlgeschlagen: " + ex.Message)); } } } private void ScanNeutralPresenceAnchors(DateTime now) { //IL_009a: Unknown result type (might be due to invalid IL or missing references) Piece[] array = SnapshotRuntimeGuardianPieces(); if (array.Length == 0) { _neutralPresenceAnchors.Clear(); return; } HashSet seenZoneIds = new HashSet(); HashSet guardianPrefabs = SplitCsv(EffectiveGuardianStonePrefabNames()); Piece[] array2 = array; foreach (Piece val in array2) { if ((Object)(object)val == (Object)null) { continue; } string prefab = NormalizeKey(((Object)val).name); if (IsGuardianStonePrefab(prefab, guardianPrefabs)) { string text = GuardianTypeForPiece(val, prefab); if (!(NormalizeGuardianType(text) != "neutral") && GuardianPreventsReset(text)) { string text2 = GuardianZoneIdFor(text, ((Component)val).transform.position); seenZoneIds.Add(text2); RegisterNeutralPresenceAnchor(text2, val, now); } } } foreach (string item in _neutralPresenceAnchors.Keys.Where((string key) => !seenZoneIds.Contains(key)).ToList()) { _neutralPresenceAnchors.Remove(item); } } private void RegisterNeutralPresenceAnchor(string zoneId, Piece neutralPiece, DateTime now) { //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_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrWhiteSpace(zoneId) && !((Object)(object)neutralPiece == (Object)null)) { if (!_neutralPresenceAnchors.TryGetValue(zoneId, out var value) || value == null) { value = new NeutralPresenceAnchor { ZoneId = zoneId, Center = ((Component)neutralPiece).transform.position, LastSeenAt = now, NextPulseAt = (EffectiveNeutralPresencePulseOnFirstScan() ? now : now.AddMinutes(EffectiveNeutralPresencePulseIntervalMinutes())), PulseUntil = DateTime.MinValue, LastPulseAt = DateTime.MinValue }; _neutralPresenceAnchors[zoneId] = value; } value.Center = ((Component)neutralPiece).transform.position; value.LastSeenAt = now; } } private void UpdateNeutralPresencePulses(DateTime now) { foreach (NeutralPresenceAnchor item in _neutralPresenceAnchors.Values.ToList()) { if (item != null && !(now < item.NextPulseAt)) { float num = EffectiveNeutralPresencePulseDurationSeconds(); float num2 = EffectiveNeutralPresencePulseIntervalMinutes(); item.LastPulseAt = now; item.PulseUntil = now.AddSeconds(num); item.NextPulseAt = now.AddMinutes(num2); ManualLogSource log = Log; if (log != null) { log.LogInfo((object)$"Neutral-Wächter simuliert Spieler-Präsenz: {num:0.#}s aktiv, nächster Puls in {num2:0.#}min ({item.ZoneId})."); } ReportNeutralPresencePulse(item, now, num, num2); } } } internal bool IsNeutralPresencePulseActiveAt(Vector3 position) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) try { if (!EffectiveGuardianStonesEnabled() || !EffectiveNeutralPresencePulseEnabled()) { return false; } if (_neutralPresenceAnchors.Count == 0) { return false; } DateTime utcNow = DateTime.UtcNow; float num = Mathf.Max(8f, EffectiveNeutralPresenceRadius()); foreach (NeutralPresenceAnchor item in _neutralPresenceAnchors.Values.ToList()) { if (item != null && !(utcNow > item.PulseUntil) && Vector3.Distance(position, item.Center) <= num) { return true; } } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogDebug((object)("Neutral-Wächter-Präsenzprüfung fehlgeschlagen: " + ex.Message)); } } return false; } private void ReportNeutralPresencePulse(NeutralPresenceAnchor anchor, DateTime now, float durationSeconds, float intervalMinutes) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) try { if (EffectiveNeutralReportPresencePulseEvents() && anchor != null) { Player val = CharacterOnDeathPatch.FindNearestPlayer(anchor.Center) ?? ((IEnumerable)Player.GetAllPlayers()).FirstOrDefault((Func)((Player player) => (Object)(object)player != (Object)null)) ?? Player.m_localPlayer; if (!((Object)(object)val == (Object)null)) { SendEvent("neutral_guardian_presence_pulse", val, new Dictionary { { "guardianType", "neutral" }, { "guardianColor", "weiÃÅÂÂ\u00b8" }, { "zoneId", anchor.ZoneId }, { "zoneName", ZoneNameFor(CurrentBiome(anchor.Center), anchor.Center) }, { "biome", CurrentBiome(anchor.Center) }, { "durationSeconds", durationSeconds }, { "intervalMinutes", intervalMinutes }, { "pulseUntil", anchor.PulseUntil.ToString("O") }, { "nextPulseAt", anchor.NextPulseAt.ToString("O") }, { "position", SerializeVector(anchor.Center) }, { "label", "Neutral-Wächter Präsenzpuls" }, { "points", 0 } }); } } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogDebug((object)("Neutral-Wächter Präsenzpuls-Event fehlgeschlagen: " + ex.Message)); } } } internal string BuildNeutralGuardianPresenceHover(Piece piece) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)piece == (Object)null || !EffectiveNeutralPresencePulseEnabled()) { return "Präsenzpuls: aus"; } string key = GuardianZoneIdFor("neutral", ((Component)piece).transform.position); DateTime utcNow = DateTime.UtcNow; if (_neutralPresenceAnchors.TryGetValue(key, out var value) && value != null) { if (utcNow <= value.PulseUntil) { double num = Math.Max(0.0, (value.PulseUntil - utcNow).TotalSeconds); return $"Präsenzpuls: aktiv ({num:0}s)"; } double seconds = Math.Max(0.0, (value.NextPulseAt - utcNow).TotalSeconds); return $"Präsenzpuls: nächster in {FormatShortTime(seconds)} · Dauer {EffectiveNeutralPresencePulseDurationSeconds():0.#}s"; } return $"Präsenzpuls: alle {EffectiveNeutralPresencePulseIntervalMinutes():0.#}min für {EffectiveNeutralPresencePulseDurationSeconds():0.#}s"; } catch { return "Präsenzpuls: unbekannt"; } } private static string FormatShortTime(double seconds) { if (seconds < 60.0) { return Math.Ceiling(seconds) + "s"; } return Math.Ceiling(seconds / 60.0) + "min"; } private void ProcessFarmerCatchup(DateTime now) { if (!EffectiveFarmerCatchupEnabled() || _farmerPresenceAnchors.Count == 0) { return; } try { if (EffectiveFarmerTamingCatchupEnabled()) { ProcessFarmerTamingCatchup(now); } if (EffectiveFarmerBreedCatchupEnabled()) { ProcessFarmerBreedCatchup(now); } if (EffectiveFarmerGrowthCatchupEnabled()) { ProcessFarmerGrowthCatchup(now); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Bauer-Wächter-Catch-up fehlgeschlagen: " + ex.Message)); } } private void ProcessFarmerTamingCatchup(DateTime now) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) Type type = AccessTools.TypeByName("Tameable"); if (type == null) { return; } Object[] array = Object.FindObjectsByType(type, (FindObjectsSortMode)0); foreach (Object obj in array) { Component val = (Component)(object)((obj is Component) ? obj : null); if (!((Object)(object)val == (Object)null) && ServerDropAuthority.IsAuthoritative(val) && TryFindFarmerPresenceAnchor(val.transform.position, out var bestAnchor)) { ProcessOneFarmerTamingCatchup(val, bestAnchor, now); } } } private void ProcessOneFarmerTamingCatchup(Component tameable, FarmerPresenceAnchor anchor, DateTime now) { //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) if (!DateTime.TryParse(ReadZdoString(tameable, "ChallengeHub.Farmer.TameLastProcessedUtc"), null, DateTimeStyles.RoundtripKind, out var result)) { WriteZdoString(tameable, "ChallengeHub.Farmer.TameLastProcessedUtc", now.ToString("O")); return; } double num = Math.Min(Math.Max(0.0, (now - result).TotalSeconds), (double)Math.Max(0.25f, EffectiveFarmerMaxOfflineSimulationHours()) * 3600.0); if (num < 60.0) { return; } Type type = ((object)tameable).GetType(); if (ReadBoolMember(tameable, type, new string[3] { "m_tamed", "m_isTamed", "m_tame" }, fallback: false) || InvokeBoolMethod(tameable, type, new string[2] { "IsTamed", "GetTamed" }, fallback: false)) { WriteZdoString(tameable, "ChallengeHub.Farmer.TameLastProcessedUtc", now.ToString("O")); return; } FieldInfo fieldInfo = FindFloatField(type, new string[3] { "m_tamingProgress", "m_tameProgress", "m_tamingTimeDone" }); if (fieldInfo == null) { WriteZdoString(tameable, "ChallengeHub.Farmer.TameLastProcessedUtc", now.ToString("O")); return; } FarmerFoodPool farmerFoodPool = BuildFarmerFoodPool(anchor); float num2 = Mathf.Max(60f, EffectiveFarmerTamingMinutesPerFood() * 60f); int num3 = Math.Max(1, (int)Math.Ceiling(num / (double)num2)); int num4 = Math.Min(num3, farmerFoodPool.Available); if (num4 <= 0) { WriteZdoString(tameable, "ChallengeHub.Farmer.TameLastProcessedUtc", now.ToString("O")); ReportFarmerSimulation(tameable.transform.position, anchor, "taming_blocked_no_food", new Dictionary { { "elapsedSeconds", Math.Round(num, 1) }, { "foodAvailable", farmerFoodPool.Available }, { "targetPrefab", NormalizeKey(((Object)tameable).name) } }); return; } float num5 = (float)num4 * num2; float num6 = Mathf.Min((float)num, num5); float num7 = ReadFloatMember(tameable, type, new string[3] { "m_tamingTime", "m_tameTime", "m_tamingDuration" }, 1800f); num7 = Mathf.Max(60f, num7); float num8 = ReadFloatField(tameable, fieldInfo, 0f); float num9 = Mathf.Min(num7, num8 + num6); int num10 = ConsumeFarmerFood(farmerFoodPool, num4); WriteFloatField(tameable, fieldInfo, num9); WriteZdoString(tameable, "ChallengeHub.Farmer.TameLastProcessedUtc", now.ToString("O")); bool flag = num9 >= num7 - 0.01f; if (flag) { TrySetTamed(tameable, type); } ReportFarmerSimulation(tameable.transform.position, anchor, "taming", new Dictionary { { "addedSeconds", Math.Round(num9 - num8, 1) }, { "oldProgress", Math.Round(num8, 1) }, { "newProgress", Math.Round(num9, 1) }, { "requiredSeconds", Math.Round(num7, 1) }, { "becameTamed", flag }, { "foodAvailable", farmerFoodPool.Available }, { "foodUsed", num10 }, { "foodNeededForFullElapsed", num3 }, { "targetPrefab", NormalizeKey(((Object)tameable).name) } }); } private void ProcessFarmerBreedCatchup(DateTime now) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) Type type = AccessTools.TypeByName("Procreation"); if (type == null) { return; } Object[] array = Object.FindObjectsByType(type, (FindObjectsSortMode)0); foreach (Object obj in array) { Component val = (Component)(object)((obj is Component) ? obj : null); if (!((Object)(object)val == (Object)null) && ServerDropAuthority.IsAuthoritative(val) && TryFindFarmerPresenceAnchor(val.transform.position, out var bestAnchor)) { ProcessOneFarmerBreedCatchup(val, bestAnchor, now); } } } private void ProcessOneFarmerBreedCatchup(Component procreation, FarmerPresenceAnchor anchor, DateTime now) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) if (!DateTime.TryParse(ReadZdoString(procreation, "ChallengeHub.Farmer.BreedLastProcessedUtc"), null, DateTimeStyles.RoundtripKind, out var result)) { WriteZdoString(procreation, "ChallengeHub.Farmer.BreedLastProcessedUtc", now.ToString("O")); return; } double num = Math.Min(Math.Max(0.0, (now - result).TotalMinutes), (double)Math.Max(0.25f, EffectiveFarmerMaxOfflineSimulationHours()) * 60.0); float num2 = Mathf.Max(5f, EffectiveFarmerBreedIntervalMinutes()); int num3 = Math.Min(EffectiveFarmerMaxBabiesPerCatchup(), (int)Math.Floor(num / (double)num2)); if (num3 <= 0) { return; } int num4 = CountAnimalsNear(anchor.Center, EffectiveFarmerPresenceRadius()); int num5 = Math.Max(0, EffectiveFarmerMaxAnimalsPerZone() - num4); if (num5 <= 0) { WriteZdoString(procreation, "ChallengeHub.Farmer.BreedLastProcessedUtc", now.ToString("O")); return; } FarmerFoodPool farmerFoodPool = BuildFarmerFoodPool(anchor); int num6 = EffectiveFarmerFoodPerBaby(); int num7 = ((num6 <= 0) ? num3 : (farmerFoodPool.Available / num6)); int num8 = Math.Min(num3, Math.Min(num5, num7)); if (num8 <= 0) { WriteZdoString(procreation, "ChallengeHub.Farmer.BreedLastProcessedUtc", now.ToString("O")); ReportFarmerSimulation(procreation.transform.position, anchor, "breeding_blocked_no_food", new Dictionary { { "possibleBirthsByTime", num3 }, { "animalCountBefore", num4 }, { "foodAvailable", farmerFoodPool.Available }, { "foodPerBaby", num6 }, { "targetPrefab", NormalizeKey(((Object)procreation).name) } }); return; } int num9 = 0; for (int i = 0; i < num8; i++) { if (num4 + num9 >= EffectiveFarmerMaxAnimalsPerZone()) { break; } if (!TryInvokeBreedSpawn(procreation)) { break; } num9++; } int num10 = ((num9 > 0) ? ConsumeFarmerFood(farmerFoodPool, num9 * num6) : 0); WriteZdoString(procreation, "ChallengeHub.Farmer.BreedLastProcessedUtc", now.ToString("O")); if (num9 > 0) { ReportFarmerSimulation(procreation.transform.position, anchor, "breeding", new Dictionary { { "possibleBirthsByTime", num3 }, { "possibleBirthsByFood", num7 }, { "spawnedBabies", num9 }, { "foodAvailable", farmerFoodPool.Available }, { "foodUsed", num10 }, { "animalCountBefore", num4 }, { "maxAnimalsPerZone", EffectiveFarmerMaxAnimalsPerZone() }, { "targetPrefab", NormalizeKey(((Object)procreation).name) } }); } } private void ProcessFarmerGrowthCatchup(DateTime now) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) Type type = AccessTools.TypeByName("Growup"); if (type == null) { return; } Object[] array = Object.FindObjectsByType(type, (FindObjectsSortMode)0); foreach (Object obj in array) { Component val = (Component)(object)((obj is Component) ? obj : null); if (!((Object)(object)val == (Object)null) && ServerDropAuthority.IsAuthoritative(val) && TryFindFarmerPresenceAnchor(val.transform.position, out var bestAnchor)) { ProcessOneFarmerGrowthCatchup(val, bestAnchor, now); } } } private void ProcessOneFarmerGrowthCatchup(Component growup, FarmerPresenceAnchor anchor, DateTime now) { //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) if (!DateTime.TryParse(ReadZdoString(growup, "ChallengeHub.Farmer.GrowthLastProcessedUtc"), null, DateTimeStyles.RoundtripKind, out var result)) { WriteZdoString(growup, "ChallengeHub.Farmer.GrowthLastProcessedUtc", now.ToString("O")); return; } double num = Math.Min(Math.Max(0.0, (now - result).TotalSeconds), (double)Math.Max(0.25f, EffectiveFarmerMaxOfflineSimulationHours()) * 3600.0); Type type = ((object)growup).GetType(); float num2 = ReadFloatMember(growup, type, new string[3] { "m_growTime", "m_growupTime", "m_growDuration" }, 3000f); num2 = Mathf.Max(60f, num2); if (num < (double)num2) { return; } FarmerFoodPool farmerFoodPool = BuildFarmerFoodPool(anchor); int num3 = EffectiveFarmerFoodPerGrowth(); if (num3 > 0 && farmerFoodPool.Available < num3) { WriteZdoString(growup, "ChallengeHub.Farmer.GrowthLastProcessedUtc", now.ToString("O")); ReportFarmerSimulation(growup.transform.position, anchor, "growth_blocked_no_food", new Dictionary { { "elapsedSeconds", Math.Round(num, 1) }, { "growSeconds", Math.Round(num2, 1) }, { "foodAvailable", farmerFoodPool.Available }, { "foodPerGrowth", num3 }, { "targetPrefab", NormalizeKey(((Object)growup).name) } }); return; } string grownPrefabName; bool num4 = TryInvokeGrowup(growup, out grownPrefabName); int num5 = (num4 ? ConsumeFarmerFood(farmerFoodPool, num3) : 0); WriteZdoString(growup, "ChallengeHub.Farmer.GrowthLastProcessedUtc", now.ToString("O")); if (num4) { ReportFarmerSimulation(growup.transform.position, anchor, "growth", new Dictionary { { "elapsedSeconds", Math.Round(num, 1) }, { "growSeconds", Math.Round(num2, 1) }, { "foodAvailable", farmerFoodPool.Available }, { "foodUsed", num5 }, { "grownPrefab", grownPrefabName ?? "" }, { "targetPrefab", NormalizeKey(((Object)growup).name) } }); } } private bool TryFindFarmerPresenceAnchor(Vector3 position, out FarmerPresenceAnchor bestAnchor) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) bestAnchor = null; if (_farmerPresenceAnchors.Count == 0) { return false; } DateTime utcNow = DateTime.UtcNow; double num = Math.Max(15.0, (double)EffectiveFarmerPresenceScanSeconds() * 4.0); float num2 = Mathf.Max(8f, EffectiveFarmerPresenceRadius()); float num3 = float.MaxValue; foreach (FarmerPresenceAnchor item in _farmerPresenceAnchors.Values.ToList()) { if (item != null && !((utcNow - item.LastSeenAt).TotalSeconds > num)) { float num4 = Vector3.Distance(position, item.Center); if (num4 <= num2 && num4 < num3) { bestAnchor = item; num3 = num4; } } } return bestAnchor != null; } private int CountAnimalsNear(Vector3 center, float radius) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) Type type = AccessTools.TypeByName("Tameable"); if (type == null) { return 0; } int num = 0; Object[] array = Object.FindObjectsByType(type, (FindObjectsSortMode)0); foreach (Object obj in array) { Component val = (Component)(object)((obj is Component) ? obj : null); if ((Object)(object)val != (Object)null && Vector3.Distance(val.transform.position, center) <= radius) { num++; } } return num; } private FarmerFoodPool BuildFarmerFoodPool(FarmerPresenceAnchor anchor) { //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) FarmerFoodPool farmerFoodPool = new FarmerFoodPool(); if (anchor == null) { return farmerFoodPool; } HashSet hashSet = SplitCsv(EffectiveFarmerFoodItems()); if (hashSet.Count == 0) { return farmerFoodPool; } float num = Mathf.Max(8f, EffectiveFarmerPresenceRadius()); ItemDrop[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (ItemDrop val in array) { try { if ((Object)(object)val == (Object)null || val.m_itemData == null || val.m_itemData.m_shared == null || Vector3.Distance(((Component)val).transform.position, anchor.Center) > num) { continue; } string item = NormalizeFoodItemName(((Object)(object)val.m_itemData.m_dropPrefab != (Object)null) ? ((Object)val.m_itemData.m_dropPrefab).name : (((Object)val).name ?? val.m_itemData.m_shared.m_name)); if (hashSet.Contains(item)) { int num2 = Math.Max(0, val.m_itemData.m_stack); if (num2 > 0) { farmerFoodPool.Available += num2; farmerFoodPool.Stacks.Add(new FarmerFoodStack { GroundItem = val, Item = val.m_itemData, Available = num2, Source = "ground" }); } } } catch { } } if (EffectiveFarmerAllowBoundChest() && !string.IsNullOrWhiteSpace(anchor.BoundChestId)) { Container val2 = FindBoundFarmerChest(anchor); if ((Object)(object)val2 != (Object)null && Vector3.Distance(((Component)val2).transform.position, anchor.Center) <= num) { object containerInventory = GetContainerInventory(val2); foreach (ItemData inventoryItem in GetInventoryItems(containerInventory)) { if (inventoryItem == null || inventoryItem.m_shared == null) { continue; } string item2 = NormalizeFoodItemName(((Object)(object)inventoryItem.m_dropPrefab != (Object)null) ? ((Object)inventoryItem.m_dropPrefab).name : inventoryItem.m_shared.m_name); if (hashSet.Contains(item2)) { int num3 = Math.Max(0, inventoryItem.m_stack); if (num3 > 0) { farmerFoodPool.Available += num3; farmerFoodPool.Stacks.Add(new FarmerFoodStack { Inventory = containerInventory, Item = inventoryItem, Available = num3, Source = "bound_chest" }); } } } } } return farmerFoodPool; } private int ConsumeFarmerFood(FarmerFoodPool pool, int amount) { if (pool == null || amount <= 0) { return 0; } int num = amount; int num2 = 0; foreach (FarmerFoodStack item2 in pool.Stacks.ToList()) { if (num <= 0) { break; } if (item2 == null || item2.Item == null || item2.Available <= 0) { continue; } int num3 = Math.Min(num, (item2.Item.m_stack > 0) ? item2.Item.m_stack : item2.Available); if (num3 <= 0) { continue; } try { ItemData item = item2.Item; item.m_stack -= num3; if (item2.Item.m_stack <= 0) { if ((Object)(object)item2.GroundItem != (Object)null) { ZNetView component = ((Component)item2.GroundItem).GetComponent(); if ((Object)(object)component != (Object)null && component.IsOwner() && (Object)(object)ZNetScene.instance != (Object)null) { ZNetScene.instance.Destroy(((Component)item2.GroundItem).gameObject); } else { Object.Destroy((Object)(object)((Component)item2.GroundItem).gameObject); } } else if (item2.Inventory != null) { TryRemoveInventoryItem(item2.Inventory, item2.Item); } } if (item2.Inventory != null) { TryMarkInventoryChanged(item2.Inventory); } num -= num3; num2 += num3; } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogDebug((object)("Bauer-Wächter Futter konnte nicht verbraucht werden: " + ex.Message)); } } } return num2; } private Container FindBoundFarmerChest(FarmerPresenceAnchor anchor) { if (anchor == null || string.IsNullOrWhiteSpace(anchor.BoundChestId)) { return null; } Container[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Container val in array) { if (!((Object)(object)val == (Object)null) && string.Equals(GetZdoIdString((Component)(object)val), anchor.BoundChestId, StringComparison.Ordinal)) { return val; } } return null; } private static object GetContainerInventory(Container container) { if ((Object)(object)container == (Object)null) { return null; } MethodInfo methodInfo = AccessTools.Method(((object)container).GetType(), "GetInventory", (Type[])null, (Type[])null); if (methodInfo != null) { try { return methodInfo.Invoke(container, null); } catch { } } FieldInfo fieldInfo = AccessTools.Field(((object)container).GetType(), "m_inventory"); try { return (fieldInfo != null) ? fieldInfo.GetValue(container) : null; } catch { return null; } } private static List GetInventoryItems(object inventory) { List list = new List(); if (inventory == null) { return list; } MethodInfo methodInfo = AccessTools.Method(inventory.GetType(), "GetAllItems", (Type[])null, (Type[])null); if (methodInfo != null) { try { if (methodInfo.Invoke(inventory, null) is IEnumerable enumerable) { foreach (object item in enumerable) { ItemData val = (ItemData)((item is ItemData) ? item : null); if (val != null) { list.Add(val); } } return list; } } catch { } } FieldInfo fieldInfo = AccessTools.Field(inventory.GetType(), "m_inventory"); try { if (((fieldInfo != null) ? fieldInfo.GetValue(inventory) : null) is IEnumerable enumerable2) { foreach (object item2 in enumerable2) { ItemData val2 = (ItemData)((item2 is ItemData) ? item2 : null); if (val2 != null) { list.Add(val2); } } } } catch { } return list; } private static void TryRemoveInventoryItem(object inventory, ItemData item) { if (inventory == null || item == null) { return; } string[] array = new string[1] { "RemoveItem" }; foreach (string methodName in array) { foreach (MethodInfo item2 in from m in inventory.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where m.Name == methodName select m) { ParameterInfo[] parameters = item2.GetParameters(); try { if (parameters.Length == 1 && parameters[0].ParameterType.IsAssignableFrom(typeof(ItemData))) { item2.Invoke(inventory, new object[1] { item }); return; } } catch { } } } try { FieldInfo fieldInfo = AccessTools.Field(inventory.GetType(), "m_inventory"); if (((fieldInfo != null) ? fieldInfo.GetValue(inventory) : null) is IList list) { list.Remove(item); } } catch { } } private static void TryMarkInventoryChanged(object inventory) { if (inventory == null) { return; } string[] array = new string[2] { "Changed", "Save" }; foreach (string text in array) { MethodInfo methodInfo = AccessTools.Method(inventory.GetType(), text, (Type[])null, (Type[])null); if (methodInfo != null && methodInfo.GetParameters().Length == 0) { try { methodInfo.Invoke(inventory, null); break; } catch { break; } } } } private bool TryInvokeGrowup(Component growup, out string grownPrefabName) { //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) grownPrefabName = string.Empty; if ((Object)(object)growup == (Object)null) { return false; } Type type = ((object)growup).GetType(); string[] array = new string[4] { "GrowUpdate", "GrowUp", "Grow", "DoGrow" }; foreach (string text in array) { MethodInfo methodInfo = AccessTools.Method(type, text, (Type[])null, (Type[])null); if (methodInfo == null || methodInfo.GetParameters().Length != 0) { continue; } try { methodInfo.Invoke(growup, null); grownPrefabName = "method:" + text; return true; } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogDebug((object)("Bauer-Wächter Growup-Methode " + text + " fehlgeschlagen: " + ex.Message)); } } } GameObject val = ReadGameObjectMember(growup, type, new string[3] { "m_grownPrefab", "m_grownupPrefab", "m_adultPrefab" }); if ((Object)(object)val == (Object)null) { return false; } grownPrefabName = ((Object)val).name; Vector3 position = growup.transform.position; Quaternion rotation = growup.transform.rotation; GameObject val2 = null; try { MethodInfo methodInfo2 = AccessTools.Method(typeof(ZNetScene), "Instantiate", new Type[3] { typeof(GameObject), typeof(Vector3), typeof(Quaternion) }, (Type[])null); if ((Object)(object)ZNetScene.instance != (Object)null && methodInfo2 != null) { object? obj = methodInfo2.Invoke(ZNetScene.instance, new object[3] { val, position, rotation }); val2 = (GameObject)((obj is GameObject) ? obj : null); } if ((Object)(object)val2 == (Object)null) { val2 = Object.Instantiate(val, position, rotation); } ZNetView component = growup.GetComponent(); if ((Object)(object)component != (Object)null && component.IsOwner() && (Object)(object)ZNetScene.instance != (Object)null) { ZNetScene.instance.Destroy(growup.gameObject); } else { Object.Destroy((Object)(object)growup.gameObject); } return (Object)(object)val2 != (Object)null; } catch (Exception ex2) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogDebug((object)("Bauer-Wächter Growup-Spawn fehlgeschlagen: " + ex2.Message)); } return false; } } private static GameObject ReadGameObjectMember(object target, Type type, string[] names) { foreach (string name in names) { FieldInfo fieldInfo = FindFieldQuiet(type, name); if (fieldInfo != null) { try { object? value = fieldInfo.GetValue(target); return (GameObject)((value is GameObject) ? value : null); } catch { } } } return null; } private bool TryInvokeBreedSpawn(Component procreation) { if ((Object)(object)procreation == (Object)null) { return false; } Type type = ((object)procreation).GetType(); string[] array = new string[5] { "Procreate", "SpawnOffspring", "SpawnBaby", "TryProcreate", "Breed" }; bool flag = default(bool); foreach (string text in array) { MethodInfo methodInfo = AccessTools.Method(type, text, (Type[])null, (Type[])null); if (methodInfo == null || methodInfo.GetParameters().Length != 0) { continue; } try { object obj = methodInfo.Invoke(procreation, null); int num; if (obj is bool) { flag = (bool)obj; num = ((1 == 0) ? 1 : 0); } else { num = 1; } return (byte)((uint)num | (flag ? 1u : 0u)) != 0; } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogDebug((object)("Bauer-Wächter Nachwuchs-Catch-up Methode " + text + " fehlgeschlagen: " + ex.Message)); } } } return false; } private void TrySetTamed(Component tameable, Type type) { string[] array = new string[2] { "SetTamed", "Tame" }; foreach (string text in array) { MethodInfo methodInfo = AccessTools.Method(type, text, (Type[])null, (Type[])null); if (methodInfo == null) { continue; } ParameterInfo[] parameters = methodInfo.GetParameters(); try { if (parameters.Length == 0) { methodInfo.Invoke(tameable, null); return; } if (parameters.Length == 1 && parameters[0].ParameterType == typeof(bool)) { methodInfo.Invoke(tameable, new object[1] { true }); return; } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogDebug((object)("Bauer-Wächter SetTamed/Tame fehlgeschlagen: " + ex.Message)); } } } FieldInfo fieldInfo = FindFieldQuiet(type, "m_tamed") ?? FindFieldQuiet(type, "m_isTamed"); if (fieldInfo != null && fieldInfo.FieldType == typeof(bool)) { fieldInfo.SetValue(tameable, true); } } private static FieldInfo FindFieldQuiet(Type type, string name) { Type type2 = type; while (type2 != null) { try { FieldInfo field = type2.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field; } } catch { } type2 = type2.BaseType; } return null; } private static FieldInfo FindFloatField(Type type, string[] names) { foreach (string name in names) { FieldInfo fieldInfo = FindFieldQuiet(type, name); if (fieldInfo != null && (fieldInfo.FieldType == typeof(float) || fieldInfo.FieldType == typeof(double) || fieldInfo.FieldType == typeof(int))) { return fieldInfo; } } return null; } private static float ReadFloatMember(object target, Type type, string[] names, float fallback) { foreach (string name in names) { FieldInfo fieldInfo = FindFieldQuiet(type, name); if (fieldInfo != null) { try { return Convert.ToSingle(fieldInfo.GetValue(target)); } catch { } } } return fallback; } private static bool ReadBoolMember(object target, Type type, string[] names, bool fallback) { foreach (string name in names) { FieldInfo fieldInfo = FindFieldQuiet(type, name); if (fieldInfo != null && fieldInfo.FieldType == typeof(bool)) { try { return Convert.ToBoolean(fieldInfo.GetValue(target)); } catch { } } } return fallback; } private static bool InvokeBoolMethod(object target, Type type, string[] names, bool fallback) { foreach (string text in names) { MethodInfo methodInfo = AccessTools.Method(type, text, (Type[])null, (Type[])null); if (!(methodInfo == null) && !(methodInfo.ReturnType != typeof(bool)) && methodInfo.GetParameters().Length == 0) { try { return Convert.ToBoolean(methodInfo.Invoke(target, null)); } catch { } } } return fallback; } private static float ReadFloatField(object target, FieldInfo field, float fallback) { try { return Convert.ToSingle(field.GetValue(target)); } catch { return fallback; } } private static void WriteFloatField(object target, FieldInfo field, float value) { try { if (field.FieldType == typeof(float)) { field.SetValue(target, value); } else if (field.FieldType == typeof(double)) { field.SetValue(target, (double)value); } else if (field.FieldType == typeof(int)) { field.SetValue(target, (int)Math.Round(value)); } } catch { } } private void ReportFarmerSimulation(Vector3 position, FarmerPresenceAnchor anchor, string simulationType, Dictionary extra) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: 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) if (!EffectiveFarmerReportSimulationEvents()) { return; } Player val = CharacterOnDeathPatch.FindNearestPlayer(position) ?? Player.m_localPlayer; if ((Object)(object)val == (Object)null) { return; } Dictionary dictionary = new Dictionary { { "simulationType", simulationType }, { "guardianType", "farmer" }, { "guardianColor", "grün" }, { "zoneId", (anchor != null) ? anchor.ZoneId : "" }, { "zoneName", (anchor != null) ? ZoneNameFor(CurrentBiome(anchor.Center), anchor.Center) : ZoneNameFor(CurrentBiome(position), position) }, { "biome", CurrentBiome(position) }, { "position", SerializeVector(position) } }; if (extra != null) { foreach (KeyValuePair item in extra) { dictionary[item.Key] = item.Value; } } SendEvent("farmer_simulation", val, dictionary); } private bool EffectiveFarmerCatchupEnabled() { if (GuardianFarmerCatchupEnabled != null) { return GuardianFarmerCatchupEnabled.Value; } return true; } private float EffectiveFarmerMaxOfflineSimulationHours() { return Mathf.Clamp((GuardianFarmerMaxOfflineSimulationHours != null) ? GuardianFarmerMaxOfflineSimulationHours.Value : 6f, 0.25f, 24f); } private bool EffectiveFarmerTamingCatchupEnabled() { if (GuardianFarmerTamingCatchupEnabled != null) { return GuardianFarmerTamingCatchupEnabled.Value; } return true; } private bool EffectiveFarmerBreedCatchupEnabled() { if (GuardianFarmerBreedCatchupEnabled != null) { return GuardianFarmerBreedCatchupEnabled.Value; } return true; } private float EffectiveFarmerBreedIntervalMinutes() { float num = ((GuardianFarmerBreedIntervalMinutes != null) ? GuardianFarmerBreedIntervalMinutes.Value : 30f); return Mathf.Max(5f, num); } private int EffectiveFarmerMaxBabiesPerCatchup() { int val = ((GuardianFarmerMaxBabiesPerCatchup != null) ? GuardianFarmerMaxBabiesPerCatchup.Value : 3); return Math.Max(0, Math.Min(12, val)); } private int EffectiveFarmerMaxAnimalsPerZone() { int val = ((GuardianFarmerMaxAnimalsPerZone != null) ? GuardianFarmerMaxAnimalsPerZone.Value : 12); return Math.Max(2, Math.Min(100, val)); } private bool EffectiveFarmerGrowthCatchupEnabled() { if (GuardianFarmerGrowthCatchupEnabled != null) { return GuardianFarmerGrowthCatchupEnabled.Value; } return true; } private string EffectiveFarmerFoodItems() { if (GuardianFarmerFoodItems == null) { return "Carrot,Turnip,Onion,Barley,Cloudberry,Raspberry,Blueberries,Mushroom,MushroomYellow,Dandelion,SeedsCarrot,CarrotSeeds,TurnipSeeds,OnionSeeds"; } return GuardianFarmerFoodItems.Value; } private float EffectiveFarmerTamingMinutesPerFood() { return Mathf.Clamp((GuardianFarmerTamingMinutesPerFood != null) ? GuardianFarmerTamingMinutesPerFood.Value : 10f, 1f, 120f); } private int EffectiveFarmerFoodPerBaby() { int val = ((GuardianFarmerFoodPerBaby == null) ? 1 : GuardianFarmerFoodPerBaby.Value); return Math.Max(0, Math.Min(50, val)); } private int EffectiveFarmerFoodPerGrowth() { int val = ((GuardianFarmerFoodPerGrowth == null) ? 1 : GuardianFarmerFoodPerGrowth.Value); return Math.Max(0, Math.Min(50, val)); } private bool EffectiveFarmerAllowBoundChest() { if (GuardianFarmerAllowBoundChest != null) { return GuardianFarmerAllowBoundChest.Value; } return true; } private bool EffectiveFarmerReportSimulationEvents() { if (GuardianFarmerReportSimulationEvents != null) { return GuardianFarmerReportSimulationEvents.Value; } return true; } internal void TryHandleFarmerBindingHotkey(Player player) { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) try { if (CommunityProjectContainerFeature.TryHandleHotkey(player) || !EffectiveFarmerAllowBoundChest() || (Object)(object)player == (Object)null || !((Character)player).IsOwner() || (!Input.GetKey((KeyCode)306) && !Input.GetKey((KeyCode)305)) || !Input.GetKeyDown((KeyCode)107)) { return; } GameObject val = FindPlayerHoverObject(player); if ((Object)(object)val == (Object)null) { ((Character)player).Message((MessageType)2, "Kein Ziel für STRG+K gefunden.", 0, (Sprite)null); return; } Piece componentInParent = val.GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null && IsFarmerGuardianPiece(componentInParent)) { string guardianType = GuardianTypeForPiece(componentInParent, NormalizeKey(((Object)componentInParent).name)); string pendingFarmerChestBindZoneId = GuardianZoneIdFor(guardianType, ((Component)componentInParent).transform.position); _pendingFarmerChestBindPiece = componentInParent; _pendingFarmerChestBindZoneId = pendingFarmerChestBindZoneId; _pendingFarmerChestBindStartedAt = Time.realtimeSinceStartup; ((Character)player).Message((MessageType)2, "Bauer-Wächter ausgewählt. Jetzt innerhalb des Radius eine Kiste ansehen und STRG+K drücken.", 0, (Sprite)null); } else { Container componentInParent2 = val.GetComponentInParent(); if ((Object)(object)componentInParent2 != (Object)null && HasPendingFarmerChestBinding) { TryBindChestToPendingFarmer(player, componentInParent2); } } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("Bauer-Wächter Kistenbindung fehlgeschlagen: " + ex.Message)); } } } private void TryBindChestToPendingFarmer(Player player, Container chest) { //IL_005f: 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_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)chest == (Object)null) { return; } if ((Object)(object)_pendingFarmerChestBindPiece == (Object)null || Time.realtimeSinceStartup - _pendingFarmerChestBindStartedAt > 60f) { ((Character)player).Message((MessageType)2, "Keine Bauer-Wächter-Auswahl aktiv. Erst Bauer-Wächter ansehen und STRG+K drücken.", 0, (Sprite)null); return; } float num = Mathf.Max(8f, EffectiveFarmerPresenceRadius()); float num2 = Vector3.Distance(((Component)_pendingFarmerChestBindPiece).transform.position, ((Component)chest).transform.position); if (num2 > num) { ((Character)player).Message((MessageType)2, "Kiste ist auÃÅÂÂ\u00b8erhalb des Bauer-Wächter-Radius.", 0, (Sprite)null); return; } string zdoIdString = GetZdoIdString((Component)(object)chest); if (string.IsNullOrWhiteSpace(zdoIdString)) { ((Character)player).Message((MessageType)2, "Diese Kiste konnte nicht eindeutig gebunden werden.", 0, (Sprite)null); return; } WriteZdoString((Component)(object)_pendingFarmerChestBindPiece, "ChallengeHub.Farmer.BoundChestId", zdoIdString); WriteZdoString((Component)(object)_pendingFarmerChestBindPiece, "ChallengeHub.Farmer.BoundChestPosition", VectorToString(((Component)chest).transform.position)); WriteZdoString((Component)(object)_pendingFarmerChestBindPiece, "ChallengeHub.Farmer.BoundChestName", NormalizeKey(((Object)chest).name)); string guardianType = GuardianTypeForPiece(_pendingFarmerChestBindPiece, NormalizeKey(((Object)_pendingFarmerChestBindPiece).name)); string text = (string.IsNullOrWhiteSpace(_pendingFarmerChestBindZoneId) ? GuardianZoneIdFor(guardianType, ((Component)_pendingFarmerChestBindPiece).transform.position) : _pendingFarmerChestBindZoneId); RegisterFarmerPresenceAnchor(text, _pendingFarmerChestBindPiece, DateTime.UtcNow, guardianType); ((Character)player).Message((MessageType)2, "Kiste an Bauer-Wächter gebunden. Futter aus dieser Kiste zählt für Offline-Farm.", 0, (Sprite)null); ReportFarmerSimulation(((Component)chest).transform.position, _farmerPresenceAnchors.ContainsKey(text) ? _farmerPresenceAnchors[text] : null, "chest_bound", new Dictionary { { "boundChestId", zdoIdString }, { "boundChestName", NormalizeKey(((Object)chest).name) }, { "distance", Math.Round(num2, 1) } }); _lastFarmerChestBindingFrame = Time.frameCount; _pendingFarmerChestBindPiece = null; _pendingFarmerChestBindZoneId = string.Empty; } private bool IsFarmerGuardianPiece(Piece piece) { if ((Object)(object)piece == (Object)null) { return false; } HashSet guardianPrefabs = SplitCsv(EffectiveGuardianStonePrefabNames()); string prefab = NormalizeKey(((Object)piece).name); if (!IsGuardianStonePrefab(prefab, guardianPrefabs)) { return false; } return NormalizeGuardianType(GuardianTypeForPiece(piece, prefab)) == "farmer"; } internal bool IsChallengeHubGuardianPieceForHover(Piece piece) { try { if ((Object)(object)piece == (Object)null || !EffectiveGuardianStonesEnabled()) { return false; } HashSet guardianPrefabs = SplitCsv(EffectiveGuardianStonePrefabNames()); string text = NormalizeKey(((Object)piece).name); return GuardianStonePieces.IsCustomGuardianPrefab(text) || IsGuardianStonePrefab(text, guardianPrefabs); } catch { return false; } } internal string BuildChallengeHubGuardianHoverName(Piece piece) { try { return NormalizeGuardianType(((Object)(object)piece != (Object)null) ? GuardianTypeForPiece(piece, NormalizeKey(((Object)piece).name)) : "neutral") switch { "farmer" => "Bauer-Wächterstein", "explorer" => "Entdecker-Wächterstein", "combat" => "Kampf-Wächterstein", "builder" => "Aufbau-Wächterstein", "reset" => "Reset-Wächterstein", _ => "Neutraler Wächterstein", }; } catch { return "ChallengeHub-Wächterstein"; } } internal string BuildChallengeHubGuardianHoverText(Piece piece) { try { if ((Object)(object)piece == (Object)null || !IsChallengeHubGuardianPieceForHover(piece)) { return "ChallengeHub-Wächterstein"; } string text = NormalizeGuardianType(GuardianTypeForPiece(piece, NormalizeKey(((Object)piece).name))); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("").Append(BuildChallengeHubGuardianHoverName(piece)).Append(""); stringBuilder.Append("\n"); stringBuilder.Append("Typ: ").Append(GuardianZoneTypeLabel(text)); float num = ((text == "farmer") ? EffectiveFarmerPresenceRadius() : ((text == "reset") ? TargetedResetFeature.GetResetGuardianRadius(piece) : EffectiveGuardianStoneDefaultRadius())); stringBuilder.Append(" · Radius: ").Append(Math.Round(num, 0)).Append("m"); stringBuilder.Append("\n"); stringBuilder.Append("Besitzer: ").Append(BuildGuardianOwnerLabel(piece)); stringBuilder.Append("\n"); stringBuilder.Append((text == "reset") ? "Schutz: keiner (nur Reset-Ausloeser)" : (GuardianPreventsReset(text) ? "Schutz: aktiv gegen Zonen-Reset" : "Schutz: markiert Zone")); switch (text) { case "farmer": AppendFarmerGuardianHover(piece, stringBuilder); break; case "neutral": AppendGenericGuardianHover(text, stringBuilder); stringBuilder.Append("\n").Append(BuildNeutralGuardianPresenceHover(piece)); stringBuilder.Append("\n").Append(PartnershipGameplayFeature.BuildGuardianTradeChestHover(piece)); stringBuilder.Append("\nSTRG+K: Handelsort wählen, danach Kiste wählen"); break; case "reset": TargetedResetFeature.AppendResetGuardianHover(piece, stringBuilder); break; default: AppendGenericGuardianHover(text, stringBuilder); break; } if (GuardianHoverFeature.ShowControls == null || GuardianHoverFeature.ShowControls.Value) { stringBuilder.Append("\nSTRG+P: Partnerschaft am Vertragsort"); if (text == "neutral") { stringBuilder.Append("\nSTRG+B: Baurechte verwalten (Spieler in der Nähe)"); } } return stringBuilder.ToString(); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogDebug((object)("Wächter-Hover konnte nicht gebaut werden: " + ex.Message)); } return "ChallengeHub-Wächterstein"; } } private void AppendGenericGuardianHover(string type, StringBuilder builder) { switch (NormalizeGuardianType(type)) { case "explorer": builder.Append("\nAufgabe: Erkundung / Routen / Orte"); break; case "combat": builder.Append("\nAufgabe: Kampfzone / Bossvorbereitung"); break; case "builder": builder.Append("\nAufgabe: Bauzone / Base-Aufbau"); break; default: builder.Append("\nAufgabe: neutraler Zonenanker"); break; } } private void AppendFarmerGuardianHover(Piece piece, StringBuilder builder) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(8f, EffectiveFarmerPresenceRadius()); string text = ReadZdoString((Component)(object)piece, "ChallengeHub.Farmer.BoundChestId"); Vector3? boundChestPosition = TryParseVector(ReadZdoString((Component)(object)piece, "ChallengeHub.Farmer.BoundChestPosition")); FarmerPresenceAnchor anchor = new FarmerPresenceAnchor { ZoneId = GuardianZoneIdFor("farmer", ((Component)piece).transform.position), Center = ((Component)piece).transform.position, LastSeenAt = DateTime.UtcNow, GuardianType = "farmer", BoundChestId = text, BoundChestPosition = boundChestPosition }; builder.Append("\nAufgabe: Zähmen, Nachwuchs, Wachstum"); builder.Append("\nOffline: ").Append(EffectiveFarmerCatchupEnabled() ? "an" : "aus").Append(" · Tierlimit: ") .Append(EffectiveFarmerMaxAnimalsPerZone()); if (GuardianHoverFeature.ShowFarmerChest == null || GuardianHoverFeature.ShowFarmerChest.Value) { bool num2 = !string.IsNullOrWhiteSpace(text); Container val = (num2 ? FindBoundFarmerChest(anchor) : null); string value = ((!num2) ? "keine Futterkiste" : (((Object)(object)val != (Object)null && Vector3.Distance(((Component)val).transform.position, ((Component)piece).transform.position) <= num) ? "Futterkiste gebunden" : "Kiste gebunden, aber nicht geladen/auÃÅÂÂ\u00b8er Radius")); builder.Append("\nKiste: ").Append(value); } if (GuardianHoverFeature.ShowFarmerFood == null || GuardianHoverFeature.ShowFarmerFood.Value) { FarmerFoodPool farmerFoodPool = BuildFarmerFoodPool(anchor); builder.Append("\nFutter verfügbar: ").Append(farmerFoodPool.Available); } if (GuardianHoverFeature.ShowFarmerAnimals == null || GuardianHoverFeature.ShowFarmerAnimals.Value) { string text2 = BuildFarmerAnimalSummary(((Component)piece).transform.position, num); builder.Append("\nTiere im Radius: ").Append(string.IsNullOrWhiteSpace(text2) ? "keine erkannt" : text2); } if (GuardianHoverFeature.ShowControls == null || GuardianHoverFeature.ShowControls.Value) { builder.Append("\nSTRG+K: Farmer/Kiste binden"); } } private string BuildFarmerAnimalSummary(Vector3 center, float radius) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); Dictionary tamed = new Dictionary(StringComparer.OrdinalIgnoreCase); Type type = AccessTools.TypeByName("Tameable"); Type type2 = AccessTools.TypeByName("Procreation"); Type type3 = AccessTools.TypeByName("Growup"); Character[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Character val in array) { try { if ((Object)(object)val == (Object)null || val.IsPlayer() || Vector3.Distance(((Component)val).transform.position, center) > radius) { continue; } Component val2 = ((type != null) ? ((Component)val).GetComponent(type) : null); Component val3 = ((type2 != null) ? ((Component)val).GetComponent(type2) : null); Component val4 = ((type3 != null) ? ((Component)val).GetComponent(type3) : null); if (!((Object)(object)val2 == (Object)null) || !((Object)(object)val3 == (Object)null) || !((Object)(object)val4 == (Object)null)) { string text = CleanHoverName(ReadStringMemberQuiet(val, "m_name", ((Object)val).name)); if (string.IsNullOrWhiteSpace(text)) { text = CleanHoverName(((Object)val).name); } if (string.IsNullOrWhiteSpace(text)) { text = "Tier"; } dictionary[text] = ((!dictionary.ContainsKey(text)) ? 1 : (dictionary[text] + 1)); if ((Object)(object)val2 != (Object)null && IsTamedQuiet(val2)) { tamed[text] = ((!tamed.ContainsKey(text)) ? 1 : (tamed[text] + 1)); } } } catch { } } if (dictionary.Count == 0) { return string.Empty; } return string.Join(", ", dictionary.OrderBy((KeyValuePair pair) => pair.Key).Take(5).Select(delegate(KeyValuePair pair) { int num = (tamed.ContainsKey(pair.Key) ? tamed[pair.Key] : 0); return (num <= 0 || num >= pair.Value) ? ((num != pair.Value) ? (pair.Key + " x" + pair.Value) : (pair.Key + " x" + pair.Value + " (gezähmt)")) : (pair.Key + " x" + pair.Value + " (gezähmt " + num + ")"); }) .ToArray()) + ((dictionary.Count > 5) ? ", ..." : string.Empty); } private static bool IsTamedQuiet(Component tameable) { if ((Object)(object)tameable == (Object)null) { return false; } Type type = ((object)tameable).GetType(); string[] array = new string[2] { "IsTamed", "GetTamed" }; foreach (string name in array) { MethodInfo method = type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (!(method == null) && !(method.ReturnType != typeof(bool))) { try { return (bool)method.Invoke(tameable, null); } catch { } } } array = new string[3] { "m_tamed", "m_isTamed", "m_tame" }; foreach (string name2 in array) { FieldInfo field = type.GetField(name2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(field == null) && !(field.FieldType != typeof(bool))) { try { return (bool)field.GetValue(tameable); } catch { } } } return false; } private static string ReadStringMemberQuiet(object target, string fieldName, string fallback) { try { if (target == null || string.IsNullOrWhiteSpace(fieldName)) { return fallback; } FieldInfo field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); object obj = ((field != null) ? field.GetValue(target) : null); return (obj != null) ? Convert.ToString(obj) : fallback; } catch { return fallback; } } private static string CleanHoverName(string input) { if (string.IsNullOrWhiteSpace(input)) { return string.Empty; } string text = input.Trim(); if (text.StartsWith("$", StringComparison.Ordinal)) { text = text.Substring(1); } int num = text.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase); if (num >= 0) { text = text.Substring(0, num); } text = text.Replace("char_", "").Replace("_", " ").Trim(); if (text.Length == 0) { return text; } return char.ToUpperInvariant(text[0]) + ((text.Length > 1) ? text.Substring(1) : string.Empty); } internal GameObject FindPlayerHoverObject(Player player) { //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return null; } string[] array = new string[5] { "m_hovering", "m_hoveringPiece", "m_hoveringCreature", "m_hoveringItem", "m_hoveringObject" }; foreach (string name in array) { FieldInfo field = ((object)player).GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { continue; } try { object value = field.GetValue(player); GameObject val = (GameObject)((value is GameObject) ? value : null); if (val != null) { return val; } Component val2 = (Component)((value is Component) ? value : null); if (val2 != null) { return val2.gameObject; } } catch { } } Camera main = Camera.main; RaycastHit val3 = default(RaycastHit); if ((Object)(object)main != (Object)null && Physics.Raycast(((Component)main).transform.position, ((Component)main).transform.forward, ref val3, 6f, -1, (QueryTriggerInteraction)1)) { if (!((Object)(object)((RaycastHit)(ref val3)).collider != (Object)null)) { return null; } return ((Component)((RaycastHit)(ref val3)).collider).gameObject; } return null; } internal static string GetZdoIdString(Component component) { try { ZNetView val = (((Object)(object)component != (Object)null) ? component.GetComponent() : null); object obj = (((Object)(object)val != (Object)null) ? val.GetZDO() : null); if (obj == null) { return string.Empty; } string[] array = new string[3] { "m_uid", "m_zdoID", "m_id" }; foreach (string name in array) { FieldInfo field = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(field == null)) { object value = field.GetValue(obj); if (value != null) { return value.ToString(); } } } return obj.ToString(); } catch { return string.Empty; } } private static string VectorToString(Vector3 value) { return value.x.ToString(CultureInfo.InvariantCulture) + "|" + value.y.ToString(CultureInfo.InvariantCulture) + "|" + value.z.ToString(CultureInfo.InvariantCulture); } private static Vector3? TryParseVector(string text) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) try { if (string.IsNullOrWhiteSpace(text)) { return null; } string[] array = text.Split(new char[1] { '|' }); if (array.Length != 3) { return null; } float num = Convert.ToSingle(array[0], CultureInfo.InvariantCulture); float num2 = Convert.ToSingle(array[1], CultureInfo.InvariantCulture); float num3 = Convert.ToSingle(array[2], CultureInfo.InvariantCulture); return new Vector3(num, num2, num3); } catch { return null; } } private static string NormalizeFoodItemName(string input) { return NormalizeKey(input); } private static bool IsGuardianRuntimeZone(ZoneRuntimeState zone) { if (zone == null) { return false; } if (!string.IsNullOrWhiteSpace(zone.GuardianType)) { return true; } if (!string.IsNullOrWhiteSpace(zone.ZoneId) && zone.ZoneId.StartsWith("guardian_", StringComparison.OrdinalIgnoreCase)) { return true; } if (!string.IsNullOrWhiteSpace(zone.ZoneType) && zone.ZoneType.IndexOf("Wächter", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } return false; } private void ReportGuardianStoneZones(Player reportingPlayer, Piece[] allPieces, DateTime now, int inactiveMinutes, HashSet activeZoneIds) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) if (!EffectiveGuardianStonesEnabled() || (Object)(object)reportingPlayer == (Object)null || allPieces == null) { return; } HashSet guardianPrefabs = SplitCsv(EffectiveGuardianStonePrefabNames()); foreach (Piece val in allPieces) { if ((Object)(object)val == (Object)null) { continue; } string prefab = NormalizeKey(((Object)val).name); if (IsGuardianStonePrefab(prefab, guardianPrefabs)) { Vector3 position = ((Component)val).transform.position; string text = GuardianTypeForPiece(val, prefab); string text2 = GuardianZoneIdFor(text, position); activeZoneIds.Add(text2); ZoneRuntimeState zoneRuntimeState = EnsureZoneState(text2, position); zoneRuntimeState.Center = position; zoneRuntimeState.LastSeenAt = now; zoneRuntimeState.ZoneType = GuardianZoneTypeLabel(text); zoneRuntimeState.GuardianType = text; zoneRuntimeState.GuardianColor = GuardianColorForType(text); zoneRuntimeState.GuardianRecipe = GuardianRecipeForType(text); zoneRuntimeState.GuardianRemovedAt = null; if (NormalizeGuardianType(text) == "farmer") { RegisterFarmerPresenceAnchor(text2, val, now, text); } bool flag = (zoneRuntimeState.Protected = GuardianPreventsReset(text)); zoneRuntimeState.ProtectedReason = (flag ? ("Wächterstein " + GuardianColorForType(text) + " / " + GuardianZoneTypeLabel(text) + " schützt diese Zone") : ("Wächterstein " + GuardianColorForType(text) + " / " + GuardianZoneTypeLabel(text) + " markiert diese Zone")); string status = (flag ? "aktiv gehalten" : "markiert"); DateTime? nextResetAt = (flag ? ((DateTime?)null) : new DateTime?(now.AddMinutes(inactiveMinutes))); MaybeSendZoneStatus(reportingPlayer, zoneRuntimeState, status, nextResetAt, zoneRuntimeState.ProtectedReason); } } } internal void RegisterGuardianPieceRuntime(Piece piece) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)piece == (Object)null || (Object)(object)((Component)piece).gameObject == (Object)null) { return; } Scene scene = ((Component)piece).gameObject.scene; if (!((Scene)(ref scene)).IsValid()) { return; } try { string prefab = NormalizeKey(((Object)piece).name); HashSet guardianPrefabs = SplitCsv(EffectiveGuardianStonePrefabNames()); if (!IsGuardianStonePrefab(prefab, guardianPrefabs)) { return; } _runtimeGuardianPieces[((Object)((Component)piece).gameObject).GetInstanceID()] = piece; if (!IsServer()) { return; } ZNetView component = ((Component)piece).GetComponent(); if (!((Object)(object)component == (Object)null) && component.GetZDO() != null) { DateTime utcNow = DateTime.UtcNow; string text = GuardianTypeForPiece(piece, prefab); string zoneId = GuardianZoneIdFor(text, ((Component)piece).transform.position); string text2 = NormalizeGuardianType(text); if (text2 == "farmer" && GuardianPreventsReset(text)) { RegisterFarmerPresenceAnchor(zoneId, piece, utcNow, text); } else if (text2 == "neutral" && GuardianPreventsReset(text)) { RegisterNeutralPresenceAnchor(zoneId, piece, utcNow); } } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogDebug((object)("Wächterstein-Laufzeitcache konnte Piece nicht registrieren: " + ex.Message)); } } } internal void UnregisterGuardianPieceRuntime(Piece piece) { if ((Object)(object)piece == (Object)null || (Object)(object)((Component)piece).gameObject == (Object)null) { return; } try { _runtimeGuardianPieces.Remove(((Object)((Component)piece).gameObject).GetInstanceID()); } catch { } } internal Piece[] RuntimeGuardianPiecesSnapshot() { return SnapshotRuntimeGuardianPieces(); } private Piece[] SnapshotRuntimeGuardianPieces() { foreach (int item in (from entry in _runtimeGuardianPieces where (Object)(object)entry.Value == (Object)null || (Object)(object)((Component)entry.Value).gameObject == (Object)null select entry.Key).ToList()) { _runtimeGuardianPieces.Remove(item); } return _runtimeGuardianPieces.Values.Where(delegate(Piece piece) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)piece != (Object)null && (Object)(object)((Component)piece).gameObject != (Object)null) { Scene scene = ((Component)piece).gameObject.scene; return ((Scene)(ref scene)).IsValid(); } return false; }).Where(delegate(Piece piece) { try { ZNetView component = ((Component)piece).GetComponent(); return (Object)(object)component != (Object)null && component.GetZDO() != null; } catch { return false; } }).ToArray(); } internal void ReportGuardianStoneChanged(Piece piece, string action) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_039b: Unknown result type (might be due to invalid IL or missing references) try { if (!EffectiveGuardianStonesEnabled() || (Object)(object)piece == (Object)null) { return; } RegisterGuardianPieceRuntime(piece); if (!ServerDropAuthority.IsAuthoritative((Component)(object)piece)) { return; } string prefab = NormalizeKey(((Object)piece).name); HashSet guardianPrefabs = SplitCsv(EffectiveGuardianStonePrefabNames()); if (!IsGuardianStonePrefab(prefab, guardianPrefabs)) { return; } Player val = CharacterOnDeathPatch.FindNearestPlayer(((Component)piece).transform.position) ?? Player.m_localPlayer; if (!((Object)(object)val == (Object)null)) { WriteGuardianOwnerIfMissing(piece, val); DateTime utcNow = DateTime.UtcNow; int num = Math.Max(5, (HasRemoteConfig() && RemoteConfig.zoneReset != null && RemoteConfig.zoneReset.minInactiveMinutes > 0) ? RemoteConfig.zoneReset.minInactiveMinutes : 120); Vector3 position = ((Component)piece).transform.position; string text = GuardianTypeForPiece(piece, prefab); string zoneId = GuardianZoneIdFor(text, position); ZoneRuntimeState zoneRuntimeState = EnsureZoneState(zoneId, position); zoneRuntimeState.Center = position; zoneRuntimeState.LastSeenAt = utcNow; zoneRuntimeState.ZoneType = GuardianZoneTypeLabel(text); zoneRuntimeState.GuardianType = text; zoneRuntimeState.GuardianColor = GuardianColorForType(text); zoneRuntimeState.GuardianRecipe = GuardianRecipeForType(text); zoneRuntimeState.GuardianRemovedAt = null; if (NormalizeGuardianType(text) == "farmer") { RegisterFarmerPresenceAnchor(zoneId, piece, utcNow, text); } zoneRuntimeState.Protected = GuardianPreventsReset(text); zoneRuntimeState.ProtectedReason = (zoneRuntimeState.Protected ? ("Wächterstein " + zoneRuntimeState.GuardianColor + " / " + zoneRuntimeState.ZoneType + " schützt diese Zone") : ("Wächterstein " + zoneRuntimeState.GuardianColor + " / " + zoneRuntimeState.ZoneType + " markiert diese Zone")); zoneRuntimeState.LastStatus = ""; zoneRuntimeState.LastReportedAt = DateTime.MinValue; string value = (zoneRuntimeState.Protected ? "aktiv gehalten" : "markiert"); DateTime? dateTime = (zoneRuntimeState.Protected ? ((DateTime?)null) : new DateTime?(utcNow.AddMinutes(num))); SendEvent("zone_status", val, new Dictionary { { "zoneId", zoneRuntimeState.ZoneId }, { "zoneName", zoneRuntimeState.ZoneName }, { "zoneStatus", value }, { "zoneType", zoneRuntimeState.ZoneType }, { "guardianType", zoneRuntimeState.GuardianType }, { "guardianColor", zoneRuntimeState.GuardianColor }, { "guardianRecipe", zoneRuntimeState.GuardianRecipe }, { "biome", zoneRuntimeState.Biome }, { "nextResetAt", dateTime.HasValue ? dateTime.Value.ToString("O") : "" }, { "lastSeenAt", zoneRuntimeState.LastSeenAt.ToString("O") }, { "lastResetAt", zoneRuntimeState.LastResetAt.HasValue ? zoneRuntimeState.LastResetAt.Value.ToString("O") : "" }, { "protectedReason", zoneRuntimeState.ProtectedReason }, { "guardianAction", action ?? "guardian_stone_changed" }, { "position", SerializeVector(zoneRuntimeState.Center) } }); ManualLogSource log = Log; if (log != null) { log.LogInfo((object)("Wächterstein-Zone sofort gemeldet: " + zoneRuntimeState.ZoneName + " (" + zoneRuntimeState.GuardianColor + "/" + zoneRuntimeState.ZoneType + ")")); } } } catch (Exception ex) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)("Wächterstein-Sofortmeldung fehlgeschlagen: " + ex.Message)); } } } internal void ReportGuardianStoneRemoved(Piece piece, string action) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) try { if (!((Object)(object)piece == (Object)null)) { string prefab = NormalizeKey(((Object)piece).name); string guardianType = GuardianTypeForPiece(piece, prefab); ReportGuardianStoneRemoved(prefab, guardianType, ((Component)piece).transform.position, action); } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogDebug((object)("Wächterstein-Entfernung konnte nicht aus Piece gelesen werden: " + ex.Message)); } } } internal void ReportGuardianStoneRemoved(string prefab, string guardianType, Vector3 center, string action) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) try { if (!EffectiveGuardianStonesEnabled() || !IsServer()) { return; } HashSet guardianPrefabs = SplitCsv(EffectiveGuardianStonePrefabNames()); string text = NormalizeKey(prefab); string text2 = NormalizeGuardianType(guardianType); if (!GuardianStonePieces.IsCustomGuardianPrefab(text) && !IsGuardianStonePrefab(text, guardianPrefabs)) { return; } Player val = CharacterOnDeathPatch.FindNearestPlayer(center) ?? Player.m_localPlayer; if (!((Object)(object)val == (Object)null)) { DateTime utcNow = DateTime.UtcNow; int num = Math.Max(5, (HasRemoteConfig() && RemoteConfig.zoneReset != null && RemoteConfig.zoneReset.minInactiveMinutes > 0) ? RemoteConfig.zoneReset.minInactiveMinutes : 120); string zoneId = GuardianZoneIdFor(text2, center); if (text2 == "farmer") { RemoveFarmerPresenceAnchor(zoneId); } ZoneRuntimeState zoneRuntimeState = EnsureZoneState(zoneId, center); zoneRuntimeState.Center = center; zoneRuntimeState.LastSeenAt = utcNow; zoneRuntimeState.ZoneType = GuardianZoneTypeLabel(text2); zoneRuntimeState.GuardianType = text2; zoneRuntimeState.GuardianColor = GuardianColorForType(text2); zoneRuntimeState.GuardianRecipe = GuardianRecipeForType(text2); zoneRuntimeState.GuardianRemovedAt = utcNow; zoneRuntimeState.Protected = false; zoneRuntimeState.ProtectedReason = "Wächterstein entfernt; Reset-Timer läuft wieder."; zoneRuntimeState.LastStatus = ""; zoneRuntimeState.LastReportedAt = DateTime.MinValue; DateTime dateTime = utcNow.AddMinutes(num); SendEvent("zone_status", val, new Dictionary { { "zoneId", zoneRuntimeState.ZoneId }, { "zoneName", zoneRuntimeState.ZoneName }, { "zoneStatus", "inaktiv" }, { "zoneType", zoneRuntimeState.ZoneType }, { "guardianType", zoneRuntimeState.GuardianType }, { "guardianColor", zoneRuntimeState.GuardianColor }, { "guardianRecipe", zoneRuntimeState.GuardianRecipe }, { "biome", zoneRuntimeState.Biome }, { "nextResetAt", dateTime.ToString("O") }, { "lastSeenAt", zoneRuntimeState.LastSeenAt.ToString("O") }, { "lastResetAt", zoneRuntimeState.LastResetAt.HasValue ? zoneRuntimeState.LastResetAt.Value.ToString("O") : "" }, { "protectedReason", zoneRuntimeState.ProtectedReason }, { "guardianAction", action ?? "guardian_stone_removed" }, { "position", SerializeVector(zoneRuntimeState.Center) } }); ManualLogSource log = Log; if (log != null) { log.LogInfo((object)$"Wächterstein entfernt: {zoneRuntimeState.ZoneName} ({zoneRuntimeState.GuardianColor}/{zoneRuntimeState.ZoneType}) ist wieder resetbar ab {dateTime:O}."); } } } catch (Exception ex) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)("Wächterstein-Entfernungsmeldung fehlgeschlagen: " + ex.Message)); } } } private bool IsGuardianStonePrefab(string prefab, HashSet guardianPrefabs) { if (string.IsNullOrWhiteSpace(prefab)) { return false; } if (GuardianStonePieces.IsCustomGuardianPrefab(prefab)) { return true; } if (guardianPrefabs.Contains(prefab)) { return true; } if (!prefab.Contains("guard_stone") && !prefab.Contains("guardstone")) { if (prefab.Contains("ward")) { return !prefab.Contains("dvergr"); } return false; } return true; } private string GuardianZoneIdFor(string guardianType, Vector3 center) { //IL_0000: 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_0024: Unknown result type (might be due to invalid IL or missing references) string text = CurrentBiome(center); int num = Mathf.FloorToInt(center.x / Mathf.Max(32f, EffectiveGuardianStoneDefaultRadius())); int num2 = Mathf.FloorToInt(center.z / Mathf.Max(32f, EffectiveGuardianStoneDefaultRadius())); return $"guardian_{NormalizeKey(guardianType)}_{text}_{num}_{num2}"; } private string GuardianTypeForPiece(Piece piece, string prefab) { ChallengeHubGuardianStoneMarker challengeHubGuardianStoneMarker = (((Object)(object)piece != (Object)null) ? ((Component)piece).GetComponent() : null); if ((Object)(object)challengeHubGuardianStoneMarker != (Object)null && !string.IsNullOrWhiteSpace(challengeHubGuardianStoneMarker.GuardianType)) { return NormalizeGuardianType(challengeHubGuardianStoneMarker.GuardianType); } string text = ReadZdoString((Component)(object)piece, "ChallengeHub.GuardianType"); if (!string.IsNullOrWhiteSpace(text)) { return NormalizeGuardianType(text); } foreach (KeyValuePair item in ParseGuardianTypeMap(EffectiveGuardianStoneTypeByPrefab())) { if (prefab == item.Key || prefab.Contains(item.Key)) { return NormalizeGuardianType(item.Value); } } return NormalizeGuardianType(EffectiveGuardianStoneDefaultType()); } private static Dictionary ParseGuardianTypeMap(string input) { Dictionary dictionary = new Dictionary(); string[] array = (input ?? "").Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { ':' }); if (array2.Length == 2) { string text = NormalizeKey(array2[0]); string value = NormalizeGuardianType(array2[1]); if (!string.IsNullOrWhiteSpace(text) && !dictionary.ContainsKey(text)) { dictionary[text] = value; } } } return dictionary; } private static string NormalizeGuardianType(string input) { switch (NormalizeKey(input)) { case "farmer": case "bauer": case "farm": case "farming": case "green": case "gruen": case "grün": case "supply": case "versorgung": case "taming": return "farmer"; case "explorer": case "entdecker": case "exploration": case "route": case "scout": case "blue": case "blau": return "explorer"; case "combat": case "kampf": case "schutz": case "fighter": case "protection": case "battle": case "red": case "rot": return "combat"; case "builder": case "aufbau": case "handwerk": case "crafting": case "build": case "building": case "yellow": case "gelb": return "builder"; case "reset": case "resetter": case "purple": case "lila": case "violett": return "reset"; case "neutral": case "white": case "weiss": case "weiÃÅÂÂ\u00b8": return "neutral"; default: return "farmer"; } } private static string GuardianZoneTypeLabel(string guardianType) { return NormalizeGuardianType(guardianType) switch { "farmer" => "Bauer / Versorgung / Zähmen", "explorer" => "Entdecker / Route", "combat" => "Schutz & Kampf", "builder" => "Aufbau & Handwerk", "reset" => "Gezielter Reset", _ => "Neutral / Allgemein", }; } private static string GuardianColorForType(string guardianType) { return NormalizeGuardianType(guardianType) switch { "farmer" => "grün", "explorer" => "blau", "combat" => "rot", "builder" => "gelb", "reset" => "lila", _ => "weiÃÅÂÂ\u00b8", }; } private bool EffectiveFarmerSimulatePlayerPresence() { if (GuardianFarmerSimulatePlayerPresence != null) { return GuardianFarmerSimulatePlayerPresence.Value; } return true; } private float EffectiveFarmerPresenceRadius() { float num = ((GuardianFarmerPresenceRadius != null) ? GuardianFarmerPresenceRadius.Value : 0f); if (!(num > 0f)) { return EffectiveGuardianStoneDefaultRadius(); } return num; } private float EffectiveFarmerPresenceScanSeconds() { float num = ((GuardianFarmerPresenceScanSeconds != null) ? GuardianFarmerPresenceScanSeconds.Value : 5f); return Mathf.Max(2f, num); } private bool GuardianPreventsReset(string guardianType) { return EffectiveGuardianPreventReset(guardianType); } private string GuardianRecipeForType(string guardianType) { return EffectiveGuardianRecipe(guardianType); } private string BuildGuardianOwnerLabel(Piece piece) { try { string text = ReadZdoString((Component)(object)piece, "ChallengeHub.Guardian.OwnerName"); string text2 = ReadZdoString((Component)(object)piece, "ChallengeHub.Guardian.OwnerId"); if (!string.IsNullOrWhiteSpace(text)) { return text; } if (!string.IsNullOrWhiteSpace(text2)) { return "ID " + text2; } return "unbekannt"; } catch { return "unbekannt"; } } private void WriteGuardianOwnerIfMissing(Piece piece, Player player) { try { if (!((Object)(object)piece == (Object)null) && !((Object)(object)player == (Object)null) && string.IsNullOrWhiteSpace(ReadZdoString((Component)(object)piece, "ChallengeHub.Guardian.OwnerId"))) { WriteZdoString((Component)(object)piece, "ChallengeHub.Guardian.OwnerId", player.GetPlayerID().ToString()); WriteZdoString((Component)(object)piece, "ChallengeHub.Guardian.OwnerName", player.GetPlayerName()); WriteZdoString((Component)(object)piece, "ChallengeHub.Guardian.OwnerSetUtc", DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)); } } catch { } } private static void WriteZdoString(Component component, string key, string value) { try { if (!((Object)(object)component == (Object)null) && !string.IsNullOrWhiteSpace(key)) { ZNetView component2 = component.GetComponent(); object obj = (((Object)(object)component2 != (Object)null) ? component2.GetZDO() : null); obj?.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Set" && m.GetParameters().Length == 2 && m.GetParameters()[0].ParameterType == typeof(string) && m.GetParameters()[1].ParameterType == typeof(string))?.Invoke(obj, new object[2] { key, value ?? string.Empty }); } } catch { } } private static string ReadZdoString(Component component, string key) { try { ZNetView val = (((Object)(object)component != (Object)null) ? component.GetComponent() : null); object obj = (((Object)(object)val != (Object)null) ? val.GetZDO() : null); if (obj == null) { return ""; } MethodInfo methodInfo = obj.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "GetString" && m.GetParameters().Length >= 1 && m.GetParameters()[0].ParameterType == typeof(string)); if (methodInfo == null) { return ""; } object obj2 = ((methodInfo.GetParameters().Length >= 2) ? methodInfo.Invoke(obj, new object[2] { key, "" }) : methodInfo.Invoke(obj, new object[1] { key })); return (obj2 != null) ? Convert.ToString(obj2) : ""; } catch { return ""; } } private int ResetObjectsInZone(Vector3 center, float radius, ZNetView[] allZNetViews, HashSet resetKeywords) { //IL_0033: 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) int num = 0; ZNetView[] array = allZNetViews ?? Array.Empty(); foreach (ZNetView val in array) { if (!((Object)(object)val == (Object)null) && val.GetZDO() != null && !(Vector3.Distance(((Component)val).transform.position, center) > radius)) { string prefab = NormalizeKey(((Object)val).name); if (resetKeywords.Any((string keyword) => prefab.Contains(keyword)) && val.IsOwner()) { ZNetScene.instance.Destroy(((Component)val).gameObject); num++; } } } return num; } private bool IsAlwaysProtectedZone(Vector3 center, float radius, ZNetView[] allZNetViews, out string reason) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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_0064: Unknown result type (might be due to invalid IL or missing references) reason = null; float num = Math.Max(radius, 180f); if (Vector3.Distance(new Vector3(center.x, 0f, center.z), Vector3.zero) <= num) { reason = "Steinkreis/Spawn-Kreis ist immer geschützt"; return true; } ZNetView[] array = allZNetViews ?? Array.Empty(); foreach (ZNetView val in array) { if (!((Object)(object)val == (Object)null) && !(Vector3.Distance(((Component)val).transform.position, center) > num)) { string prefab = NormalizeKey(((Object)val).name); if (IsStartCirclePrefab(prefab)) { reason = "Steinkreis/Spawn-Kreis ist immer geschützt"; return true; } if (IsEikthyrCirclePrefab(prefab)) { reason = "Eikthyr-Kreis ist immer geschützt"; return true; } } } return false; } private static bool IsStartCirclePrefab(string prefab) { prefab = NormalizeKey(prefab); if (!prefab.Contains("starttemple") && !prefab.Contains("start_temple") && (!prefab.Contains("spawn") || !prefab.Contains("temple")) && !prefab.Contains("trophy_stone") && !prefab.Contains("location_start")) { return prefab.Contains("locationstone_start"); } return true; } private static bool IsEikthyrCirclePrefab(string prefab) { prefab = NormalizeKey(prefab); if (!prefab.Contains("eikthyr") && !prefab.Contains("eiktyr") && !prefab.Contains("eikthyrnir") && !prefab.Contains("bossstone_eikthyr") && !prefab.Contains("locationstone_eikthyr") && !prefab.Contains("vegvisir_eikthyr") && !prefab.Contains("altar_eikthyr")) { return prefab.Contains("eikthyrnirnir"); } return true; } private static HashSet SplitCsv(string input) { return new HashSet(from s in (input ?? "").Split(new char[1] { ',' }).Select(NormalizeKey) where !string.IsNullOrWhiteSpace(s) select s); } internal static string CurrentBiome(Vector3 position) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_0008: 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) try { return NormalizeKey(((object)((WorldGenerator.instance != null) ? WorldGenerator.instance.GetBiome(position) : Heightmap.FindBiome(position))/*cast due to .constrained prefix*/).ToString()); } catch { return "unknown"; } } private static int EstimatedTrophyGoalForBiome(string biome) { return biome switch { "meadows" => 5, "black_forest" => 7, "swamp" => 7, "mountains" => 6, "plains" => 6, "mistlands" => 8, "ashlands" => 8, _ => 5, }; } internal static string NormalizeKey(string input) { return (input ?? "").Replace("(Clone)", "").Replace("$", "").Trim() .ToLowerInvariant() .Replace(" ", "_") .Replace("-", "_"); } internal static Dictionary SerializeVector(Vector3 v) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) return new Dictionary { { "x", Math.Round(v.x, 2) }, { "y", Math.Round(v.y, 2) }, { "z", Math.Round(v.z, 2) } }; } internal static string ToJson(Dictionary data) { if (data == null) { return "{}"; } IEnumerable values = data.Select((KeyValuePair kv) => "\"" + Escape(kv.Key) + "\":" + JsonValue(kv.Value)); return "{" + string.Join(",", values) + "}"; } private static string JsonValue(object value) { if (value == null) { return "null"; } if (value is bool) { if (!(bool)value) { return "false"; } return "true"; } if (value is int || value is long || value is float || value is double || value is decimal) { return Convert.ToString(value, CultureInfo.InvariantCulture); } if (value is Dictionary data) { return ToJson(data); } if (value is IDictionary dictionary) { List list = new List(); foreach (DictionaryEntry item in dictionary) { list.Add("\"" + Escape(Convert.ToString(item.Key)) + "\":" + JsonValue(item.Value)); } return "{" + string.Join(",", list) + "}"; } if (value is IEnumerable enumerable && !(value is string)) { List list2 = new List(); foreach (object item2 in enumerable) { list2.Add(JsonValue(item2)); } return "[" + string.Join(",", list2) + "]"; } return "\"" + Escape(Convert.ToString(value)) + "\""; } private static string Escape(string input) { return (input ?? "").Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n") .Replace("\r", "\\r"); } } internal static class PatchUtil { internal static Player LocalOrInstancePlayer(object instance) { try { Player val = (Player)((instance is Player) ? instance : null); if (val != null) { return val; } return Player.m_localPlayer; } catch { return null; } } internal static object FirstUsefulArg(object[] args) { if (args == null) { return null; } foreach (object obj in args) { if (obj != null) { if (obj is GameObject || obj is Component) { return obj; } string text = obj.GetType().Name.ToLowerInvariant(); if (text.Contains("item") || text.Contains("recipe") || text.Contains("piece")) { return obj; } } } return args.FirstOrDefault((object arg) => arg != null); } } [HarmonyPatch] internal static class BuildEvidencePatch { internal static IEnumerable TargetMethods() { Type player = AccessTools.TypeByName("Player"); if (player == null) { yield break; } string[] array = new string[2] { "PlacePiece", "RPC_PlacePiece" }; foreach (string methodName in array) { foreach (MethodInfo item in from m in AccessTools.GetDeclaredMethods(player) where m.Name == methodName select m) { yield return item; } } } private static void Postfix(object __instance, object[] __args) { try { Player val = PatchUtil.LocalOrInstancePlayer(__instance); if (!((Object)(object)val == (Object)null) && ((Character)val).IsOwner()) { Plugin.Instance?.ReportBuildCompleted(val, PatchUtil.FirstUsefulArg(__args), "player_place_piece"); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Build-Evidence-Patch fehlgeschlagen: " + ex.Message)); } } } } [HarmonyPatch] internal static class CraftEvidencePatch { internal static IEnumerable TargetMethods() { string[] array = new string[2] { "InventoryGui", "Player" }; foreach (string text in array) { Type type = AccessTools.TypeByName(text); if (type == null) { continue; } string[] array2 = new string[3] { "DoCrafting", "CraftItem", "RPC_CraftItem" }; foreach (string methodName in array2) { foreach (MethodInfo item in from m in AccessTools.GetDeclaredMethods(type) where m.Name == methodName select m) { yield return item; } } } } private static void Postfix(object __instance, object[] __args) { try { Player val = PatchUtil.LocalOrInstancePlayer(__instance); if (!((Object)(object)val == (Object)null) && ((Character)val).IsOwner()) { Plugin.Instance?.ReportCraftCompleted(val, PatchUtil.FirstUsefulArg(__args), "crafting_patch"); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Craft-Evidence-Patch fehlgeschlagen: " + ex.Message)); } } } } [HarmonyPatch] internal static class GatherEvidencePatch { internal static IEnumerable TargetMethods() { string[] array = new string[5] { "Pickable", "TreeBase", "TreeLog", "MineRock", "MineRock5" }; foreach (string text in array) { Type type = AccessTools.TypeByName(text); if (type == null) { continue; } string[] array2 = new string[3] { "Interact", "Pick", "Destroy" }; foreach (string methodName in array2) { foreach (MethodInfo item in from m in AccessTools.GetDeclaredMethods(type) where m.Name == methodName select m) { yield return item; } } } } private static void Postfix(object __instance, object[] __args) { try { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && ((Character)localPlayer).IsOwner()) { string text = ((__instance != null) ? __instance.GetType().Name.ToLowerInvariant() : "gather"); string eventType = (text.Contains("tree") ? "tree_cut" : (text.Contains("mine") ? "mine_hit" : "pickable_collected")); Plugin.Instance?.ReportGatherSignal(localPlayer, eventType, __instance, text); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Gather-Evidence-Patch fehlgeschlagen: " + ex.Message)); } } } } [HarmonyPatch] internal static class AnimalEvidencePatch { internal static IEnumerable TargetMethods() { Type tameable = AccessTools.TypeByName("Tameable"); if (tameable == null) { yield break; } string[] array = new string[3] { "Tame", "SetTamed", "Command" }; foreach (string methodName in array) { foreach (MethodInfo item in from m in AccessTools.GetDeclaredMethods(tameable) where m.Name == methodName select m) { yield return item; } } } private static void Postfix(object __instance, object[] __args) { try { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && ((Character)localPlayer).IsOwner()) { Plugin.Instance?.ReportGatherSignal(localPlayer, "animal_tamed", __instance, "tameable_patch"); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Animal-Evidence-Patch fehlgeschlagen: " + ex.Message)); } } } } [HarmonyPatch] internal static class BossVegvisirDisablePatch { internal static IEnumerable TargetMethods() { Type vegvisir = AccessTools.TypeByName("Vegvisir"); if (vegvisir == null) { yield break; } string[] array = new string[10] { "Interact", "Use", "OnUse", "RegisterLocation", "FindClosestSpawn", "DiscoverClosestLocation", "DiscoverLocation", "FindClosestLocation", "OnActivate", "Activate" }; foreach (string methodName in array) { foreach (MethodInfo item in from m in AccessTools.GetDeclaredMethods(vegvisir) where m.Name == methodName select m) { yield return item; } } } private static bool Prefix(MethodBase __originalMethod, object __instance, object[] __args) { try { if (!Plugin.IsBossVegvisirDisabled()) { return true; } string text = ((__originalMethod != null) ? __originalMethod.Name : string.Empty); string text2 = Describe(__instance, __args).ToLowerInvariant(); bool num = LooksLikeEikthyrLocation(text2); bool flag = LooksLikeBossLocation(text2) || text2.Contains("vegvisir"); if (num && IsDirectionOrInteractMethod(text)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Eikthyr-Vegvisir blockiert: Richtung und Kartenmarkierung verhindert."); } try { MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, "Eikthyr-Wegzeichen sind in dieser Challenge deaktiviert.", 0, (Sprite)null, false); } } catch { } return false; } if (flag && IsMapMarkerMethod(text)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Boss-Vegvisir-Kartenmarkierung blockiert: " + text)); } try { MessageHud instance2 = MessageHud.instance; if (instance2 != null) { instance2.ShowMessage((MessageType)2, "Boss-Orte werden nicht automatisch auf der Karte markiert.", 0, (Sprite)null, false); } } catch { } return false; } return true; } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("Boss-Vegvisir-Disable-Patch fehlgeschlagen: " + ex.Message)); } return true; } } internal static bool IsEikthyrVegvisir(object instance) { return LooksLikeEikthyrLocation(Describe(instance, null).ToLowerInvariant()); } internal static bool IsBossOrGenericVegvisir(object instance) { string text = Describe(instance, null).ToLowerInvariant(); if (!LooksLikeBossLocation(text)) { return text.Contains("vegvisir"); } return true; } internal static bool LooksLikeBossLocation(string value) { if (string.IsNullOrWhiteSpace(value)) { return false; } value = value.ToLowerInvariant(); if (!value.Contains("eikthyr") && !value.Contains("eiktyr") && !value.Contains("eikthyrnir") && !value.Contains("elder") && !value.Contains("gd_king") && !value.Contains("gdk") && !value.Contains("bonemass") && !value.Contains("dragon") && !value.Contains("moder") && !value.Contains("goblinking") && !value.Contains("yagluth") && !value.Contains("seekerqueen") && !value.Contains("queen") && !value.Contains("fader") && !value.Contains("boss_") && !value.Contains("bossstone_") && !value.Contains("locationstone_")) { return value.Contains("boss location"); } return true; } internal static bool LooksLikeEikthyrLocation(string value) { if (string.IsNullOrWhiteSpace(value)) { return false; } value = value.ToLowerInvariant(); if (!value.Contains("eikthyr") && !value.Contains("eiktyr")) { return value.Contains("eikthyrnir"); } return true; } internal static bool IsDirectionOrInteractMethod(string methodName) { if (string.IsNullOrWhiteSpace(methodName)) { return true; } methodName = methodName.ToLowerInvariant(); if (!methodName.Contains("interact") && !(methodName == "use") && !methodName.Contains("onuse") && !methodName.Contains("activate") && !methodName.Contains("findclosest")) { return methodName.Contains("discoverclosest"); } return true; } internal static bool IsMapMarkerMethod(string methodName) { if (string.IsNullOrWhiteSpace(methodName)) { return false; } methodName = methodName.ToLowerInvariant(); if (!methodName.Contains("registerlocation") && !methodName.Contains("discoverlocation") && !methodName.Contains("addpin") && !methodName.Contains("pin")) { return methodName.Contains("map"); } return true; } internal static string Describe(object instance, object[] args) { List list = new List(); Append(list, instance); if (args != null) { foreach (object value in args) { Append(list, value); } } return string.Join(" ", list.Where((string part) => !string.IsNullOrWhiteSpace(part)).ToArray()); } internal static void Append(List parts, object value) { if (value == null) { return; } try { parts.Add(Convert.ToString(value)); } catch { } try { parts.Add(value.GetType().Name); } catch { } try { Component val = (Component)((value is Component) ? value : null); GameObject val2 = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val != (Object)null) { val2 = val.gameObject; } if ((Object)(object)val2 != (Object)null) { parts.Add(((Object)val2).name); Transform val3 = val2.transform; int num = 0; while ((Object)(object)val3 != (Object)null && num++ < 8) { parts.Add(((Object)val3).name); val3 = val3.parent; } } } catch { } try { Type type = value.GetType(); string[] array = new string[15] { "m_name", "m_label", "m_text", "m_topic", "m_hoverText", "m_locationName", "m_pinName", "m_boss", "m_bossName", "m_target", "m_prefabName", "m_vegvisirName", "m_location", "m_locationType", "m_locationNameHash" }; foreach (string name in array) { try { object obj4 = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(value); if (obj4 != null) { parts.Add(Convert.ToString(obj4)); } } catch { } try { PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.GetIndexParameters().Length == 0) { object value2 = property.GetValue(value, null); if (value2 != null) { parts.Add(Convert.ToString(value2)); } } } catch { } } } catch { } } } [HarmonyPatch] internal static class BossVegvisirMapMarkerBlockPatch { internal static IEnumerable TargetMethods() { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { Type[] array; try { array = assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { array = ex.Types.Where((Type t) => t != null).ToArray(); } catch { continue; } Type[] array2 = array; foreach (Type type in array2) { if (type == null) { continue; } string text = type.Name.ToLowerInvariant(); if (!text.Contains("vegvisir") && !text.Contains("minimap") && !text.Contains("pin") && !text.Contains("map") && !text.Contains("location")) { continue; } MethodInfo[] array3; try { array3 = AccessTools.GetDeclaredMethods(type).ToArray(); } catch { continue; } MethodInfo[] array4 = array3; foreach (MethodInfo methodInfo in array4) { if (methodInfo == null || methodInfo.IsAbstract || methodInfo.ContainsGenericParameters) { continue; } string text2 = methodInfo.Name.ToLowerInvariant(); switch (text2) { default: if (!text2.Contains("addpin") && !text2.Contains("discoverlocation")) { continue; } break; case "discoverclosestlocation": case "findclosestlocation": case "findclosestspawn": case "discoverlocation": case "registerlocation": case "addpin": break; } yield return methodInfo; } } } } private static bool Prefix(MethodBase __originalMethod, object __instance, object[] __args) { try { if (!Plugin.IsBossVegvisirDisabled()) { return true; } string text = ((__originalMethod != null) ? __originalMethod.Name : string.Empty); string text2 = BossVegvisirDisablePatch.Describe(__instance, __args).ToLowerInvariant(); bool flag = BossVegvisirDisablePatch.LooksLikeEikthyrLocation(text2); bool flag2 = BossVegvisirDisablePatch.LooksLikeBossLocation(text2); bool flag3 = text2.Contains("vegvisir"); string text3 = text.ToLowerInvariant(); if ((text3.Contains("findclosest") || text3.Contains("discoverclosest")) && !flag) { return true; } if ((flag2 || flag3) && (BossVegvisirDisablePatch.IsMapMarkerMethod(text) || text3.Contains("discover") || text3.Contains("pin") || text3.Contains("register"))) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Boss-Vegvisir-Kartenmarkierung blockiert: " + ((__originalMethod != null) ? (__originalMethod.DeclaringType?.Name + "." + __originalMethod.Name) : "MapMarker"))); } try { MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, "Boss-Orte werden nicht automatisch auf der Karte markiert.", 0, (Sprite)null, false); } } catch { } return false; } return true; } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Boss-Vegvisir-Kartenmarkierungs-Block fehlgeschlagen: " + ex.Message)); } return true; } } } [HarmonyPatch] internal static class ExplorationEvidencePatch { private static readonly HashSet NearbyReported = new HashSet(); private static readonly Collider[] NearbyBuffer = (Collider[])(object)new Collider[128]; internal static void ScanNearby(Player player) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)Plugin.Instance == (Object)null || !CharacterAdmissionFeature.ChallengeScoringAllowed) { return; } try { int num = Physics.OverlapSphereNonAlloc(((Component)player).transform.position, 16f, NearbyBuffer, -1, (QueryTriggerInteraction)2); for (int i = 0; i < num; i++) { Collider val = NearbyBuffer[i]; if ((Object)(object)val == (Object)null) { continue; } MonoBehaviour[] componentsInParent = ((Component)val).GetComponentsInParent(true); foreach (MonoBehaviour val2 in componentsInParent) { if ((Object)(object)val2 == (Object)null) { continue; } string text = ((object)val2).GetType().Name.ToLowerInvariant(); string text2 = Plugin.NormalizeKey(((Object)val2).name); bool flag = text.Contains("offeringbowl") || text2.Contains("offeringbowl") || text2.Contains("bossaltar") || text2.Contains("boss_altar"); bool flag2 = text.Contains("vegvisir") || text2.Contains("vegvisir"); if (flag || flag2) { int instanceID = ((Object)val2).GetInstanceID(); if (NearbyReported.Add(instanceID)) { Plugin.Instance.ReportExplorationSignal(player, flag ? "boss_altar_seen" : "boss_vegvisir_seen", val2, "physical_proximity"); } } } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Bossaltar-Naehepruefung fehlgeschlagen: " + ex.Message)); } } } internal static IEnumerable TargetMethods() { string[] array = new string[5] { "TeleportWorld", "LocationProxy", "Vegvisir", "Trader", "OfferingBowl" }; foreach (string text in array) { Type type = AccessTools.TypeByName(text); if (type == null) { continue; } string[] array2 = new string[4] { "Interact", "Use", "OnTriggerEnter", "Awake" }; foreach (string methodName in array2) { foreach (MethodInfo item in from m in AccessTools.GetDeclaredMethods(type) where m.Name == methodName select m) { yield return item; } } } } private static void Postfix(object __instance, object[] __args) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) try { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && ((Character)localPlayer).IsOwner()) { string text = ((__instance != null) ? __instance.GetType().Name.ToLowerInvariant() : "exploration"); Component val = (Component)((__instance is Component) ? __instance : null); if (!((Object)(object)val != (Object)null) || !(Vector3.Distance(((Component)localPlayer).transform.position, val.transform.position) > 35f)) { string signal = (text.Contains("trader") ? "trader_found" : (text.Contains("vegvisir") ? "boss_vegvisir_seen" : (text.Contains("offeringbowl") ? "boss_altar_seen" : "exploration_seen"))); Plugin.Instance?.ReportExplorationSignal(localPlayer, signal, __instance, text); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Exploration-Evidence-Patch fehlgeschlagen: " + ex.Message)); } } } } [HarmonyPatch] internal static class MapViolationEvidencePatch { internal static IEnumerable TargetMethods() { Type minimap = AccessTools.TypeByName("Minimap"); if (minimap == null) { yield break; } string[] array = new string[1] { "SetMapMode" }; foreach (string methodName in array) { foreach (MethodInfo item in from m in AccessTools.GetDeclaredMethods(minimap) where m.Name == methodName select m) { yield return item; } } } private static void Prefix(object __instance, object[] __args) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) try { if (Plugin.IsNoMapEnabled()) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && ((Character)localPlayer).IsOwner() && (Object)(object)Minimap.instance != (Object)null && (int)Minimap.instance.m_mode != 0) { Plugin.Instance?.ReportMapViolation(localPlayer); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Map-Violation-Patch fehlgeschlagen: " + ex.Message)); } } } } internal static class QoLSkillRuntimeFeature { private static readonly Collider[] NearbyColliderBuffer = (Collider[])(object)new Collider[160]; private static Plugin _plugin; private static QoLSkillRuntimeBehaviour _behaviour; private static ConfigEntry _containerRadius; internal static float ContainerRadius { get { Plugin plugin = _plugin; if (plugin == null || !(plugin.RemoteConfig?.qol?.containerRadius > 0f)) { if (_containerRadius == null) { return 12f; } return Mathf.Clamp(_containerRadius.Value, 2f, 30f); } return Mathf.Clamp(_plugin.RemoteConfig.qol.containerRadius, 2f, 30f); } } internal static void Initialize(Plugin plugin) { if (!((Object)(object)plugin == (Object)null)) { _plugin = plugin; _containerRadius = ((BaseUnityPlugin)plugin).Config.Bind("QoLSkilltree", "NearbyContainerRadius", 12f, "Reichweite fuer bewusst ausgeloeste Lager- und Materialabfragen. Es gibt keinen Hintergrundscan."); _behaviour = ((Component)plugin).gameObject.GetComponent() ?? ((Component)plugin).gameObject.AddComponent(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"QoL-Skilltree-Laufzeit bereit: sechs unabhaengige Zweige, interaktionsgebundene Scans."); } } } internal static void OnTalentDataChanged() { _behaviour?.OnTalentDataChanged(); } internal static string ItemKey(ItemData item) { if (item == null) { return string.Empty; } try { if ((Object)(object)item.m_dropPrefab != (Object)null && !string.IsNullOrWhiteSpace(((Object)item.m_dropPrefab).name)) { return ((Object)item.m_dropPrefab).name; } } catch { } try { return (item.m_shared != null) ? (item.m_shared.m_name ?? string.Empty) : string.Empty; } catch { return string.Empty; } } internal static bool IsReservedItem(TalentData data, Inventory inventory, ItemData item) { if (data == null || inventory == null || item == null) { return false; } string item2 = ItemKey(item); if (data.HasSkill("storage.favorites") && data.FavoriteItemKeys.Contains(item2)) { return true; } int num = InventoryReflection.Width(inventory); int item3 = ((num > 0) ? (item.m_gridPos.y * num + item.m_gridPos.x) : (-1)); if (data.HasSkill("storage.protected_slots") && data.ProtectedInventorySlots.Contains(item3)) { return true; } if (data.HasSkill("recovery.recovery_reserve") && (item.m_gridPos.y == 0 || item.m_equipped)) { return true; } return false; } internal static int SortInventory() { //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; TalentData localData = TalentStore.GetLocalData(); if ((Object)(object)localPlayer == (Object)null || localData == null || !localData.HasSkill("comfort.order_keeper")) { return 0; } Inventory inventory = ((Humanoid)localPlayer).GetInventory(); if (inventory == null) { return 0; } int num = InventoryReflection.Width(inventory); int num2 = InventoryReflection.Height(inventory); if (num <= 0 || num2 <= 1) { return 0; } List list = (from val2 in inventory.GetAllItems() where val2 != null select val2).ToList(); HashSet hashSet = new HashSet(); List list2 = new List(); foreach (ItemData item2 in list) { int item = item2.m_gridPos.y * num + item2.m_gridPos.x; if (item2.m_gridPos.y == 0 || IsReservedItem(localData, inventory, item2)) { hashSet.Add(item); } else { list2.Add(item2); } } list2 = list2.OrderBy((ItemData item2) => ItemCategory(item2)).ThenByDescending((ItemData val2) => val2.m_quality).ThenBy((ItemData item2) => LocalizedName(item2), StringComparer.CurrentCultureIgnoreCase) .ThenByDescending((ItemData val2) => val2.m_stack) .ToList(); int num3 = 0; int num4 = num; Vector2i val = default(Vector2i); foreach (ItemData item3 in list2) { for (; num4 < num * num2 && hashSet.Contains(num4); num4++) { } if (num4 >= num * num2) { break; } ((Vector2i)(ref val))..ctor(num4 % num, num4 / num); if (item3.m_gridPos.x != val.x || item3.m_gridPos.y != val.y) { item3.m_gridPos = val; num3++; } hashSet.Add(num4); num4++; } if (num3 > 0) { InventoryReflection.NotifyChanged(inventory); } return num3; } internal static int ConsolidateStacks() { Player localPlayer = Player.m_localPlayer; TalentData data = TalentStore.GetLocalData(); if ((Object)(object)localPlayer == (Object)null || data == null || !data.HasSkill("comfort.stack_master")) { return 0; } Inventory inventory = ((Humanoid)localPlayer).GetInventory(); if (inventory == null) { return 0; } List source = (from item in inventory.GetAllItems() where item != null && item.m_shared != null select item).ToList(); int num = 0; foreach (IGrouping item in source.GroupBy(StackKey, StringComparer.Ordinal)) { List list = (from item in item where !IsReservedItem(data, inventory, item) orderby item.m_gridPos.y, item.m_gridPos.x select item).ToList(); for (int num2 = 0; num2 < list.Count; num2++) { ItemData val = list[num2]; int num3 = Math.Max(1, val.m_shared.m_maxStackSize); if (val.m_stack >= num3) { continue; } int num4 = list.Count - 1; while (num4 > num2 && val.m_stack < num3) { ItemData val2 = list[num4]; if (val2 != null && val2.m_stack > 0) { int num5 = Math.Min(num3 - val.m_stack, val2.m_stack); if (num5 > 0) { val.m_stack += num5; val2.m_stack -= num5; num += num5; } } num4--; } } } ItemData[] array = source.Where((ItemData item) => item.m_stack <= 0).ToArray(); foreach (ItemData val3 in array) { inventory.RemoveItem(val3); } if (num > 0) { InventoryReflection.NotifyChanged(inventory); } return num; } internal static IEnumerator QuickStoreNearby() { IEnumerator routine = AdvancedQoLFeature.IntelligentStoreNearby(); while (routine.MoveNext()) { yield return routine.Current; } } internal static int RepairAllAtCurrentStation() { TalentData localData = TalentStore.GetLocalData(); Player localPlayer = Player.m_localPlayer; if (localData == null || (Object)(object)localPlayer == (Object)null || !localData.HasSkill("comfort.tool_care")) { return 0; } try { if ((Object)(object)localPlayer.GetCurrentCraftingStation() == (Object)null) { return 0; } } catch { return 0; } InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance == (Object)null) { return 0; } MethodInfo methodInfo = AccessTools.Method(((object)instance).GetType(), "RepairOneItem", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(((object)instance).GetType(), "HaveRepairableItems", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { return 0; } int num = 0; for (int i = 0; i < 128; i++) { try { object obj2 = methodInfo2.Invoke(instance, Array.Empty()); if (!(obj2 is bool) || !(bool)obj2) { break; } methodInfo.Invoke(instance, Array.Empty()); num++; continue; } catch { } break; } return num; } internal static string BuildMaterialOverview() { Player localPlayer = Player.m_localPlayer; TalentData localData = TalentStore.GetLocalData(); if ((Object)(object)localPlayer == (Object)null || localData == null || !localData.HasSkill("building.material_overview")) { return string.Empty; } Piece selectedPiece = PlayerBuildReflection.GetSelectedPiece(localPlayer); if ((Object)(object)selectedPiece == (Object)null) { return "Kein Hammerbauteil ausgewaehlt."; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(Localize(selectedPiece.m_name)); Requirement[] array = selectedPiece.m_resources ?? Array.Empty(); foreach (Requirement val in array) { if (val != null && !((Object)(object)val.m_resItem == (Object)null) && val.m_resItem.m_itemData != null) { string name = val.m_resItem.m_itemData.m_shared.m_name; int num = Math.Max(0, val.m_amount); int num2 = ((Humanoid)localPlayer).GetInventory().CountItems(name, -1, true); int num3 = Math.Max(0, num - num2); string value = ((num2 >= num) ? "#75e075" : ((num2 > 0) ? "#ffd35a" : "#ff6868")); stringBuilder.Append("\n") .Append(Localize(name)) .Append(": ") .Append(num2) .Append("/") .Append(num); if (num3 > 0) { stringBuilder.Append(" - fehlt ").Append(num3); } else { stringBuilder.Append(" - vollstaendig"); } stringBuilder.Append(""); } } return stringBuilder.ToString(); } internal static bool IsInBuildPlacementMode(Player player) { if ((Object)(object)player == (Object)null) { return false; } try { MethodInfo methodInfo = AccessTools.Method(((object)player).GetType(), "InPlaceMode", (Type[])null, (Type[])null); if (methodInfo != null && methodInfo.Invoke(player, null) is bool result) { return result; } } catch { } try { FieldInfo fieldInfo = AccessTools.Field(((object)player).GetType(), "m_placementGhost"); return fieldInfo != null && fieldInfo.GetValue(player) != null; } catch { return false; } } internal static bool ToggleFavoriteEquipped(out string name, out bool enabled) { name = string.Empty; enabled = false; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } ItemData representativeEquippedItem = PlayerItemReflection.GetRepresentativeEquippedItem(localPlayer); if (representativeEquippedItem == null) { return false; } name = LocalizedName(representativeEquippedItem); return TalentStore.ToggleFavoriteItem(ItemKey(representativeEquippedItem), out enabled); } internal static bool ToggleProtectedCurrentHotbar(out int slot, out bool enabled) { slot = -1; enabled = false; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } slot = PlayerItemReflection.GetCurrentHotbarSlot(localPlayer); if (slot >= 0) { return TalentStore.ToggleProtectedSlot(slot, out enabled); } return false; } internal static bool ToggleLookedAtPickupFilter(out string name, out bool blocked) { name = string.Empty; blocked = false; ItemDrop val = LookTarget.FindComponent(6f); if ((Object)(object)val == (Object)null || val.m_itemData == null) { return false; } name = LocalizedName(val.m_itemData); return TalentStore.TogglePickupFilter(ItemKey(val.m_itemData), out blocked); } internal static string TravelBlockers() { Player localPlayer = Player.m_localPlayer; TalentData localData = TalentStore.GetLocalData(); if ((Object)(object)localPlayer == (Object)null || localData == null || !localData.HasSkill("travel.travel_check")) { return string.Empty; } List list = (from item in ((Humanoid)localPlayer).GetInventory().GetAllItems() where item != null && item.m_shared != null && !item.m_shared.m_teleportable select item).Select(LocalizedName).Distinct(StringComparer.CurrentCultureIgnoreCase).Take(8) .ToList(); if (list.Count != 0) { return "Nicht teleportierbar: " + string.Join(", ", list.ToArray()); } return "Reisepruefung: alle Gegenstaende sind teleportierbar."; } internal static string PortalOverviewText() { //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) TalentData localData = TalentStore.GetLocalData(); Player localPlayer = Player.m_localPlayer; if (localData == null || (Object)(object)localPlayer == (Object)null || !localData.HasSkill("travel.portal_overview")) { return string.Empty; } string world = CartographyMapModeFeature.CurrentWorldStorageToken(); List list = (from entry in localData.PortalHistory where entry != null && entry.WorldToken == world orderby entry.SeenUtcTicks descending select entry).ToList(); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); TeleportWorld[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (TeleportWorld val in array) { if (!((Object)(object)val == (Object)null)) { string value; try { value = val.GetText(); } catch { value = "Portal"; } string safe = OrientationText.SanitizeLabel(value, "Portal"); if (TryGetPortalTarget(val, out var connected, out var targetLoaded, out var targetPosition)) { dictionary[safe] = ((!connected) ? "unverbunden" : (targetLoaded ? ("verbunden; Ziel " + Mathf.RoundToInt(targetPosition.x) + ", " + Mathf.RoundToInt(targetPosition.z)) : "verbunden; Ziel wird geladen")); } if (!list.Any((PortalHistoryEntry entry) => string.Equals(entry.Name, safe, StringComparison.OrdinalIgnoreCase))) { list.Add(new PortalHistoryEntry { WorldToken = world, Name = safe, X = ((Component)val).transform.position.x, Y = ((Component)val).transform.position.y, Z = ((Component)val).transform.position.z, SeenUtcTicks = 0L }); } } } if (list.Count == 0) { return "Keine bekannten Portale."; } StringBuilder stringBuilder = new StringBuilder("Portalübersicht"); foreach (PortalHistoryEntry item in list.Take(14)) { int value2 = Mathf.RoundToInt(Vector3.Distance(((Component)localPlayer).transform.position, item.Position)); string value4; string value3 = (dictionary.TryGetValue(item.Name, out value4) ? value4 : "Verlauf; derzeit nicht geladen"); stringBuilder.Append("\n").Append(item.Name).Append(" — ") .Append(value3) .Append(" — ") .Append(value2) .Append(" m") .Append(" (") .Append(Mathf.RoundToInt(item.X)) .Append(", ") .Append(Mathf.RoundToInt(item.Z)) .Append(")"); } return stringBuilder.ToString(); } internal static bool TryGetPortalTarget(TeleportWorld portal, out bool connected, out bool targetLoaded, out Vector3 targetPosition) { //IL_0007: 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_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_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) connected = false; targetLoaded = false; targetPosition = Vector3.zero; if ((Object)(object)portal == (Object)null) { return false; } try { MethodInfo methodInfo = AccessTools.Method(((object)portal).GetType(), "HaveTarget", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(((object)portal).GetType(), "TargetFound", (Type[])null, (Type[])null); connected = methodInfo != null && Convert.ToBoolean(methodInfo.Invoke(portal, Array.Empty())); targetLoaded = methodInfo2 != null && Convert.ToBoolean(methodInfo2.Invoke(portal, Array.Empty())); if (!targetLoaded) { return true; } ZNetView component = ((Component)portal).GetComponent(); ZDO val = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); if (val == null || ZDOMan.instance == null) { return true; } ZDOID connectionZDOID = val.GetConnectionZDOID((ConnectionType)1); ZDO zDO = ZDOMan.instance.GetZDO(connectionZDOID); if (zDO != null) { targetPosition = zDO.GetPosition(); } else { targetLoaded = false; } return true; } catch { return false; } } internal static List FindNearbyContainers(Vector3 center, float radius) { //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_001a: Unknown result type (might be due to invalid IL or missing references) List list = new List(); HashSet hashSet = new HashSet(); int num; try { num = Physics.OverlapSphereNonAlloc(center, radius, NearbyColliderBuffer); } catch { return list; } for (int i = 0; i < num; i++) { Collider val = NearbyColliderBuffer[i]; NearbyColliderBuffer[i] = null; if (!((Object)(object)val == (Object)null)) { Container val2 = ((Component)val).GetComponentInParent() ?? ((Component)val).GetComponentInChildren(); if (!((Object)(object)val2 == (Object)null) && hashSet.Add(((Object)val2).GetInstanceID())) { list.Add(val2); } } } return list.OrderBy((Container container) => Vector3.Distance(center, ((Component)container).transform.position)).ToList(); } internal static string LocalizedName(ItemData item) { if (item == null || item.m_shared == null) { return "Gegenstand"; } return Localize(item.m_shared.m_name); } internal static void RememberBuildSelection(Player player) { TalentData localData = TalentStore.GetLocalData(); if ((Object)(object)player == (Object)null || localData == null || !localData.HasSkill("comfort.builder_memory")) { return; } Piece selectedPiece = PlayerBuildReflection.GetSelectedPiece(player); if (!((Object)(object)selectedPiece == (Object)null)) { string text = Utils.GetPrefabName(((Component)selectedPiece).gameObject); if (string.IsNullOrWhiteSpace(text)) { text = ((Object)((Component)selectedPiece).gameObject).name; } localData.LastBuildPiece = text; localData.LastBuildRotation = PlayerBuildReflection.GetRotationStep(player); localData.LastBuildRotationIndex = PlayerBuildReflection.GetRotationIndex(player); TalentStore.Save(player, localData, flushProfile: true); } } internal static string Localize(string text) { string text2 = text ?? string.Empty; try { Type type = AccessTools.TypeByName("Localization") ?? AccessTools.TypeByName("GUIFramework.Localization"); if (type == null) { return text2; } object obj = type.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null) ?? type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null, null) ?? type.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null, null); if (obj == null) { return text2; } MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo method) { if (!string.Equals(method.Name, "Localize", StringComparison.Ordinal)) { return false; } ParameterInfo[] parameters = method.GetParameters(); return parameters.Length == 1 && parameters[0].ParameterType == typeof(string); }); if (methodInfo == null) { return text2; } return (methodInfo.Invoke(obj, new object[1] { text2 }) as string) ?? text2; } catch { return text2; } } private static int ItemCategory(ItemData item) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) try { return (item == null || item.m_shared == null) ? int.MaxValue : ((int)item.m_shared.m_itemType); } catch { return int.MaxValue; } } private static string StackKey(ItemData item) { if (item == null || item.m_shared == null) { return Guid.NewGuid().ToString("N"); } return (item.m_shared.m_name ?? string.Empty) + "|" + item.m_quality.ToString(CultureInfo.InvariantCulture) + "|" + item.m_worldLevel.ToString(CultureInfo.InvariantCulture) + "|" + item.m_variant.ToString(CultureInfo.InvariantCulture); } internal static string BuildRefundPreview(Piece piece) { if ((Object)(object)piece == (Object)null || piece.m_resources == null || piece.m_resources.Length == 0) { return string.Empty; } string[] array = (from req in piece.m_resources where req != null && req.m_recover && (Object)(object)req.m_resItem != (Object)null && req.m_resItem.m_itemData != null && req.m_amount > 0 select req.m_amount + "× " + Localize(req.m_resItem.m_itemData.m_shared.m_name)).ToArray(); if (array.Length == 0) { return string.Empty; } return "Rueckgabe: " + string.Join(", ", array); } internal static void ShowCenter(string text) { if (!string.IsNullOrWhiteSpace(text)) { MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false); } } } } internal sealed class QoLSkillRuntimeBehaviour : MonoBehaviour { private bool _showPortalOverview; private string _portalOverviewText = string.Empty; private float _rotationStep = 5f; private GUIStyle _overviewStyle; private long _memoryPlayerId; private bool _memoryRestored; private int _memoryRestoreAttempts; private float _nextMemoryRestoreAt; private static Font _valheimFont; private static Texture2D _bgTexture; private static GUIStyle _compassStyle; private void Update() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || TalentMenuBehaviour.IsVisible) { return; } long num = TalentStore.SafeGetPlayerId(localPlayer); if (_memoryPlayerId != num) { _memoryPlayerId = num; _memoryRestored = false; _memoryRestoreAttempts = 0; _nextMemoryRestoreAt = 0f; } if (!_memoryRestored && TalentStore.HasSkill("comfort.builder_memory") && Time.unscaledTime >= _nextMemoryRestoreAt) { _nextMemoryRestoreAt = Time.unscaledTime + 1f; _memoryRestoreAttempts++; TalentData localData = TalentStore.GetLocalData(); if (localData == null || string.IsNullOrWhiteSpace(localData.LastBuildPiece)) { _memoryRestored = true; } else if (PlayerBuildReflection.TryRestoreRememberedPiece(localPlayer)) { if (localData.LastBuildRotation > 0f) { _rotationStep = PlayerBuildReflection.NormalizeRotationStep(localData.LastBuildRotation); PlayerBuildReflection.SetRotationStep(localPlayer, _rotationStep); } PlayerBuildReflection.SetRotationIndex(localPlayer, localData.LastBuildRotationIndex); _memoryRestored = true; } else if (_memoryRestoreAttempts >= 60) { _memoryRestored = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)"Gespeichertes Hammerbauteil konnte nach 60 Versuchen nicht wiederhergestellt werden."); } } } if (!Input.GetKey((KeyCode)308) && !Input.GetKey((KeyCode)307)) { return; } if (Input.GetKeyDown((KeyCode)115)) { int num2 = QoLSkillRuntimeFeature.SortInventory(); QoLSkillRuntimeFeature.ShowCenter((num2 > 0) ? (num2 + " Inventarplaetze sortiert.") : "Inventar bereits geordnet oder Talent fehlt."); } else if (Input.GetKeyDown((KeyCode)99)) { int num3 = QoLSkillRuntimeFeature.ConsolidateStacks(); QoLSkillRuntimeFeature.ShowCenter((num3 > 0) ? (num3 + " Gegenstaende gestapelt.") : "Keine Stapel zusammenzufuehren."); } else if (Input.GetKeyDown((KeyCode)113)) { ((MonoBehaviour)this).StartCoroutine(QoLSkillRuntimeFeature.QuickStoreNearby()); } else if (Input.GetKeyDown((KeyCode)114)) { int num4 = QoLSkillRuntimeFeature.RepairAllAtCurrentStation(); QoLSkillRuntimeFeature.ShowCenter((num4 > 0) ? (num4 + " Reparaturschritte ausgefuehrt.") : "Keine Reparatur moeglich oder Station fehlt."); } else if (Input.GetKeyDown((KeyCode)102)) { if (QoLSkillRuntimeFeature.ToggleFavoriteEquipped(out var name, out var enabled)) { QoLSkillRuntimeFeature.ShowCenter(name + (enabled ? " als Favorit markiert." : " ist kein Favorit mehr.")); } else { QoLSkillRuntimeFeature.ShowCenter("Kein ausgeruesteter Gegenstand oder Favoriten-Talent fehlt."); } } else if (Input.GetKeyDown((KeyCode)108)) { if (QoLSkillRuntimeFeature.ToggleProtectedCurrentHotbar(out var slot, out var enabled2)) { QoLSkillRuntimeFeature.ShowCenter("Schnellleistenplatz " + (slot + 1) + (enabled2 ? " geschuetzt." : " freigegeben.")); } else { QoLSkillRuntimeFeature.ShowCenter("Geschuetzte-Plaetze-Talent fehlt."); } } else if (Input.GetKeyDown((KeyCode)112)) { if (QoLSkillRuntimeFeature.ToggleLookedAtPickupFilter(out var name2, out var blocked)) { QoLSkillRuntimeFeature.ShowCenter(name2 + (blocked ? " vom Auto-Pickup ausgeschlossen." : " wieder fuer Auto-Pickup erlaubt.")); } else { QoLSkillRuntimeFeature.ShowCenter("Kein Bodengegenstand anvisiert oder Aufnahmefilter fehlt."); } } else if (Input.GetKeyDown((KeyCode)101)) { if (!TalentStore.HasSkill("building.smart_copy")) { QoLSkillRuntimeFeature.ShowCenter("Intelligentes Nachbauen ist nicht freigeschaltet."); return; } Piece val = LookTarget.FindComponent(8f); QoLSkillRuntimeFeature.ShowCenter(((Object)(object)val != (Object)null && PlayerBuildReflection.TrySelectPiece(localPlayer, val)) ? ("Bauteil ausgewaehlt: " + QoLSkillRuntimeFeature.Localize(val.m_name)) : "Kein passendes bekanntes Bauteil anvisiert."); } else if (Input.GetKeyDown((KeyCode)120)) { string state; if (!TalentStore.HasSkill("building.snap_switch")) { QoLSkillRuntimeFeature.ShowCenter("Snap-Wechsel ist nicht freigeschaltet."); } else if (PlayerBuildReflection.TryToggleSnapMode(localPlayer, out state)) { QoLSkillRuntimeFeature.ShowCenter("Snap-Modus: " + state); } else { QoLSkillRuntimeFeature.ShowCenter("Dieser Valheim-Build stellt keinen kompatiblen Snap-Schalter bereit."); } } else if (Input.GetKeyDown((KeyCode)109)) { QoLSkillRuntimeFeature.ShowCenter(QoLSkillRuntimeFeature.BuildMaterialOverview()); } else if (Input.GetKeyDown((KeyCode)116)) { QoLSkillRuntimeFeature.ShowCenter(QoLSkillRuntimeFeature.TravelBlockers()); } else if (Input.GetKeyDown((KeyCode)111)) { _portalOverviewText = QoLSkillRuntimeFeature.PortalOverviewText(); _showPortalOverview = !string.IsNullOrWhiteSpace(_portalOverviewText) && !_showPortalOverview; } } private void OnGUI() { //IL_0395: 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_0068: Expected O, but got Unknown //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: 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_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Expected O, but got Unknown //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02c4: Unknown result type (might be due to invalid IL or missing references) //IL_02d8: Unknown result type (might be due to invalid IL or missing references) //IL_02dd: Unknown result type (might be due to invalid IL or missing references) //IL_0307: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) if (TalentMenuBehaviour.IsVisible) { return; } if (_overviewStyle == null || (Object)(object)_bgTexture == (Object)null) { _valheimFont = ((IEnumerable)Resources.FindObjectsOfTypeAll()).FirstOrDefault((Func)((Font x) => ((Object)x).name == "AveriaSansLibre-Bold" || ((Object)x).name == "AveriaSerifLibre-Bold")) ?? GUI.skin.font; _bgTexture = new Texture2D(1, 1); _bgTexture.SetPixel(0, 0, new Color(0.08f, 0.08f, 0.08f, 0.95f)); _bgTexture.Apply(); _overviewStyle = new GUIStyle(GUI.skin.box) { alignment = (TextAnchor)0, wordWrap = true, fontSize = 15, font = _valheimFont }; _overviewStyle.normal.background = _bgTexture; _overviewStyle.normal.textColor = new Color(0.9f, 0.9f, 0.9f, 1f); _compassStyle = new GUIStyle(GUI.skin.box) { alignment = (TextAnchor)4, wordWrap = false, fontSize = 18, font = _valheimFont }; _compassStyle.normal.background = _bgTexture; _compassStyle.normal.textColor = new Color(1f, 0.65f, 0f, 1f); } if (_showPortalOverview && !string.IsNullOrWhiteSpace(_portalOverviewText)) { GUI.Box(new Rect((float)Screen.width - 430f, 55f, 400f, Mathf.Min(500f, 75f + (float)_portalOverviewText.Split(new char[1] { '\n' }).Length * 22f)), _portalOverviewText + "\n\nAlt+O schliesst die Liste.", _overviewStyle); } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && TalentStore.HasSkill("building.material_overview") && QoLSkillRuntimeFeature.IsInBuildPlacementMode(localPlayer)) { string text = QoLSkillRuntimeFeature.BuildMaterialOverview(); if (!string.IsNullOrWhiteSpace(text)) { int num = text.Split(new char[1] { '\n' }).Length; GUI.Box(new Rect((float)Screen.width - 390f, (float)Screen.height * 0.5f - 70f, 360f, 42f + (float)num * 23f), text, _overviewStyle); } } if ((Object)(object)localPlayer != (Object)null && TalentStore.HasSkill("recovery.grave_compass") && TombstoneMapFeature.TryGetNearestOwnTombstone(((Component)localPlayer).transform.position, out var position, out var distance)) { Vector3 target = position - ((Component)localPlayer).transform.position; target.y = 0f; string text2 = DirectionLabel(((Component)localPlayer).transform.forward, target); GUI.Box(new Rect((float)Screen.width * 0.5f - 155f, 45f, 310f, 34f), "Eigener Grabstein: " + Mathf.RoundToInt(distance) + " m " + text2, _compassStyle); } if ((Object)(object)localPlayer != (Object)null && TalentStore.HasSkill("building.refund_preview")) { Piece piece = null; try { piece = localPlayer.GetHoveringPiece(); } catch { } string text3 = QoLSkillRuntimeFeature.BuildRefundPreview(piece); if (!string.IsNullOrWhiteSpace(text3)) { GUI.Box(new Rect((float)Screen.width * 0.5f - 260f, (float)Screen.height - 138f, 520f, 38f), text3, _compassStyle); } } } private static string DirectionLabel(Vector3 forward, Vector3 target) { //IL_002e: 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 (((Vector3)(ref target)).sqrMagnitude < 0.01f) { return "hier"; } forward.y = 0f; target.y = 0f; float num = Vector3.SignedAngle(((Vector3)(ref forward)).normalized, ((Vector3)(ref target)).normalized, Vector3.up); if (Mathf.Abs(num) < 22.5f) { return "vorn"; } if (num >= 22.5f && num < 67.5f) { return "rechts-vorn"; } if (num >= 67.5f && num < 112.5f) { return "rechts"; } if (num >= 112.5f && num < 157.5f) { return "rechts-hinten"; } if (num <= -22.5f && num > -67.5f) { return "links-vorn"; } if (num <= -67.5f && num > -112.5f) { return "links"; } if (num <= -112.5f && num > -157.5f) { return "links-hinten"; } return "hinten"; } internal void OnTalentDataChanged() { if (!TalentStore.HasSkill("travel.portal_overview")) { _showPortalOverview = false; } } } internal static class InventoryReflection { private static readonly FieldInfo InventoryField = AccessTools.Field(typeof(Inventory), "m_inventory"); private static readonly FieldInfo WidthField = AccessTools.Field(typeof(Inventory), "m_width"); private static readonly FieldInfo HeightField = AccessTools.Field(typeof(Inventory), "m_height"); private static readonly MethodInfo ChangedMethod = AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null); internal static int Width(Inventory inventory) { try { return (inventory != null && WidthField != null) ? Convert.ToInt32(WidthField.GetValue(inventory)) : 0; } catch { return 0; } } internal static int Height(Inventory inventory) { try { return (inventory != null && HeightField != null) ? Convert.ToInt32(HeightField.GetValue(inventory)) : 0; } catch { return 0; } } internal static List RawItems(Inventory inventory) { try { return InventoryField?.GetValue(inventory) as List; } catch { return null; } } internal static void NotifyChanged(Inventory inventory) { try { ChangedMethod?.Invoke(inventory, Array.Empty()); } catch { } } } internal static class PlayerItemReflection { internal static ItemData GetRepresentativeEquippedItem(Player player) { if ((Object)(object)player == (Object)null) { return null; } string[] array = new string[3] { "GetCurrentWeapon", "GetRightItem", "GetLeftItem" }; foreach (string text in array) { try { object? obj = AccessTools.Method(((object)player).GetType(), text, (Type[])null, (Type[])null)?.Invoke(player, Array.Empty()); ItemData val = (ItemData)((obj is ItemData) ? obj : null); if (val != null) { return val; } } catch { } } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return null; } return ((IEnumerable)inventory.GetAllItems()).FirstOrDefault((Func)((ItemData item) => item?.m_equipped ?? false)); } internal static int GetCurrentHotbarSlot(Player player) { ItemData representativeEquippedItem = GetRepresentativeEquippedItem(player); if (representativeEquippedItem != null && representativeEquippedItem.m_gridPos.y == 0) { return representativeEquippedItem.m_gridPos.x; } return -1; } } internal static class PlayerBuildReflection { internal static Piece GetSelectedPiece(Player player) { if ((Object)(object)player == (Object)null) { return null; } string[] array = new string[2] { "GetSelectedPiece", "GetPiece" }; foreach (string text in array) { try { MethodInfo methodInfo = AccessTools.Method(((object)player).GetType(), text, (Type[])null, (Type[])null); if (methodInfo != null && methodInfo.GetParameters().Length == 0) { object? obj = methodInfo.Invoke(player, Array.Empty()); Piece val = (Piece)((obj is Piece) ? obj : null); if (val != null) { return val; } } } catch { } } try { object obj3 = AccessTools.Field(((object)player).GetType(), "m_buildPieces")?.GetValue(player); object? obj4 = ((obj3 != null) ? AccessTools.Method(obj3.GetType(), "GetSelectedPiece", (Type[])null, (Type[])null) : null)?.Invoke(obj3, Array.Empty()); return (Piece)((obj4 is Piece) ? obj4 : null); } catch { return null; } } internal static GameObject GetPlacementGhost(Player player) { if ((Object)(object)player == (Object)null) { return null; } try { object? obj = AccessTools.Field(((object)player).GetType(), "m_placementGhost")?.GetValue(player); return (GameObject)((obj is GameObject) ? obj : null); } catch { return null; } } internal static PieceTable GetPieceTable(Player player) { if ((Object)(object)player == (Object)null) { return null; } try { object? obj = AccessTools.Field(((object)player).GetType(), "m_buildPieces")?.GetValue(player); return (PieceTable)((obj is PieceTable) ? obj : null); } catch { return null; } } internal static bool TrySelectPiece(Player player, Piece target) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) PieceTable pieceTable = GetPieceTable(player); if ((Object)(object)pieceTable == (Object)null || (Object)(object)target == (Object)null) { return false; } try { Vector2Int selected = default(Vector2Int); int category = default(int); if (!pieceTable.GetPieceIndex(target, ref selected, ref category)) { return false; } pieceTable.SetCategory(category); pieceTable.SetSelected(selected); return true; } catch { return false; } } internal static bool TryRestoreRememberedPiece(Player player) { TalentData localData = TalentStore.GetLocalData(); PieceTable pieceTable = GetPieceTable(player); if (localData == null || (Object)(object)pieceTable == (Object)null || string.IsNullOrWhiteSpace(localData.LastBuildPiece)) { return false; } try { foreach (GameObject piece in pieceTable.m_pieces) { if (!((Object)(object)piece == (Object)null) && (string.Equals(Utils.GetPrefabName(piece), localData.LastBuildPiece, StringComparison.Ordinal) || string.Equals(((Object)piece).name, localData.LastBuildPiece, StringComparison.Ordinal))) { Piece component = piece.GetComponent(); return (Object)(object)component != (Object)null && TrySelectPiece(player, component); } } } catch { } return false; } internal static bool TryToggleSnapMode(Player player, out string state) { state = string.Empty; if ((Object)(object)player == (Object)null) { return false; } try { FieldInfo fieldInfo = AccessTools.Field(((object)player).GetType(), "m_manualSnapPoint"); if (fieldInfo == null || fieldInfo.FieldType != typeof(int)) { return false; } int num = Convert.ToInt32(fieldInfo.GetValue(player)) + 1; fieldInfo.SetValue(player, num); state = ((num < 0) ? "automatisch" : ("Punkt " + (num + 1))); return true; } catch { return false; } } internal static float NormalizeRotationStep(float value) { if (Mathf.Approximately(value, 1f)) { return 1f; } if (Mathf.Approximately(value, 5f)) { return 5f; } if (Mathf.Approximately(value, 15f)) { return 15f; } return 22.5f; } internal static float GetRotationStep(Player player) { if ((Object)(object)player == (Object)null) { return 22.5f; } try { FieldInfo fieldInfo = AccessTools.Field(((object)player).GetType(), "m_placeRotationDegrees"); return (fieldInfo != null) ? NormalizeRotationStep(Convert.ToSingle(fieldInfo.GetValue(player), CultureInfo.InvariantCulture)) : 22.5f; } catch { return 22.5f; } } internal static int GetRotationIndex(Player player) { if ((Object)(object)player == (Object)null) { return 0; } try { FieldInfo fieldInfo = AccessTools.Field(((object)player).GetType(), "m_placeRotation"); return (fieldInfo != null && fieldInfo.FieldType == typeof(int)) ? Convert.ToInt32(fieldInfo.GetValue(player)) : 0; } catch { return 0; } } internal static bool SetRotationIndex(Player player, int value) { if ((Object)(object)player == (Object)null) { return false; } try { FieldInfo fieldInfo = AccessTools.Field(((object)player).GetType(), "m_placeRotation"); if (fieldInfo == null || fieldInfo.FieldType != typeof(int)) { return false; } fieldInfo.SetValue(player, Mathf.Clamp(value, -4096, 4096)); return true; } catch { return false; } } internal static bool SetRotationStep(Player player, float value) { if ((Object)(object)player == (Object)null) { return false; } try { FieldInfo fieldInfo = AccessTools.Field(((object)player).GetType(), "m_placeRotationDegrees"); if (fieldInfo == null || fieldInfo.FieldType != typeof(float)) { return false; } fieldInfo.SetValue(player, NormalizeRotationStep(value)); return true; } catch { return false; } } } internal static class LookTarget { internal static T FindComponent(float distance) where T : Component { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) Camera main = Camera.main; if ((Object)(object)main == (Object)null) { return default(T); } RaycastHit val = default(RaycastHit); if (!Physics.Raycast(((Component)main).transform.position, ((Component)main).transform.forward, ref val, distance)) { return default(T); } if (!((Object)(object)((RaycastHit)(ref val)).collider != (Object)null)) { return default(T); } return ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent() ?? ((Component)((RaycastHit)(ref val)).collider).GetComponentInChildren(); } } [HarmonyPatch(typeof(Player), "PlacePiece", new Type[] { typeof(Piece), typeof(Vector3), typeof(Quaternion), typeof(bool) })] internal static class QoLBuilderMemoryPatch { [HarmonyPostfix] private static void Postfix(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { QoLSkillRuntimeFeature.RememberBuildSelection(__instance); } } } [HarmonyPatch(typeof(ItemDrop), "GetHoverText")] internal static class QoLItemDropHoverPatch { [HarmonyPostfix] private static void Postfix(ItemDrop __instance, ref string __result) { TalentData localData = TalentStore.GetLocalData(); Player localPlayer = Player.m_localPlayer; ItemData val = (((Object)(object)__instance != (Object)null) ? __instance.m_itemData : null); if (localData != null && !((Object)(object)localPlayer == (Object)null) && val != null && val.m_shared != null && (localData.HasSkill("comfort.collector_sight") || localData.HasSkill("storage.weight_preview"))) { StringBuilder stringBuilder = new StringBuilder(); float num = ((Humanoid)localPlayer).GetInventory().GetTotalWeight() + val.m_shared.m_weight * (float)Math.Max(1, val.m_stack); float maxCarryWeight = localPlayer.GetMaxCarryWeight(); if (localData.HasSkill("comfort.collector_sight")) { int value = ((Humanoid)localPlayer).GetInventory().CountItems(val.m_shared.m_name, -1, true); stringBuilder.Append("\nInventar: ").Append(value).Append(" | Stapel: ") .Append(val.m_shared.m_maxStackSize) .Append(" | Gewicht danach: ") .Append(num.ToString("0.0", CultureInfo.InvariantCulture)) .Append(""); } if (localData.HasSkill("storage.weight_preview")) { stringBuilder.Append("\n maxCarryWeight) ? "#ff8080" : "#a8ff9a").Append(">Tragkraft: ") .Append(num.ToString("0.0", CultureInfo.InvariantCulture)) .Append(" / ") .Append(maxCarryWeight.ToString("0.0", CultureInfo.InvariantCulture)) .Append((num > maxCarryWeight) ? " — UEBERLADEN" : " — OK") .Append(""); } __result += stringBuilder.ToString(); } } } internal static class QoLAutoPickupScope { [ThreadStatic] internal static bool Active; } [HarmonyPatch(typeof(Player), "AutoPickup")] internal static class QoLAutoPickupScopePatch { [HarmonyPrefix] private static void Prefix(Player __instance) { QoLAutoPickupScope.Active = (Object)(object)__instance != (Object)null && (Object)(object)__instance == (Object)(object)Player.m_localPlayer; } [HarmonyPostfix] private static void Postfix() { QoLAutoPickupScope.Active = false; } [HarmonyFinalizer] private static Exception Finalizer(Exception __exception) { QoLAutoPickupScope.Active = false; return __exception; } } [HarmonyPatch(typeof(ItemDrop), "CanPickup")] internal static class QoLAutoPickupFilterPatch { [HarmonyPrefix] private static bool Prefix(ItemDrop __instance, ref bool __result) { if (!QoLAutoPickupScope.Active || (Object)(object)__instance == (Object)null) { return true; } TalentData localData = TalentStore.GetLocalData(); if (localData == null || !localData.HasSkill("storage.pickup_filter")) { return true; } string item = QoLSkillRuntimeFeature.ItemKey(__instance.m_itemData); if (!localData.PickupFilterItemKeys.Contains(item)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Inventory), "StackAll")] internal static class QoLProtectedStackAllPatch { internal sealed class State { internal Inventory Source; internal List Hidden; } [HarmonyPrefix] private static void Prefix(Inventory fromInventory, out State __state) { __state = null; Player localPlayer = Player.m_localPlayer; TalentData data = TalentStore.GetLocalData(); if ((Object)(object)localPlayer == (Object)null || data == null || fromInventory == null || fromInventory != ((Humanoid)localPlayer).GetInventory() || (!data.HasSkill("storage.protected_slots") && !data.HasSkill("storage.favorites") && !data.HasSkill("recovery.recovery_reserve"))) { return; } List list = InventoryReflection.RawItems(fromInventory); if (list == null) { return; } List list2 = list.Where((ItemData item) => QoLSkillRuntimeFeature.IsReservedItem(data, fromInventory, item)).ToList(); if (list2.Count == 0) { return; } foreach (ItemData item in list2) { list.Remove(item); } __state = new State { Source = fromInventory, Hidden = list2 }; } [HarmonyPostfix] private static void Postfix(State __state) { Restore(__state); } [HarmonyFinalizer] private static Exception Finalizer(Exception __exception, State __state) { Restore(__state); return __exception; } private static void Restore(State state) { if (state?.Source == null || state.Hidden == null) { return; } List list = InventoryReflection.RawItems(state.Source); if (list == null) { return; } foreach (ItemData item in state.Hidden) { if (item != null && !list.Contains(item)) { list.Add(item); } } InventoryReflection.NotifyChanged(state.Source); state.Hidden.Clear(); } } [HarmonyPatch(typeof(TeleportWorld), "GetHoverText")] internal static class QoLPortalHoverPatch { [HarmonyPostfix] private static void Postfix(TeleportWorld __instance, ref string __result) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) TalentData localData = TalentStore.GetLocalData(); Player localPlayer = Player.m_localPlayer; if (localData == null || (Object)(object)localPlayer == (Object)null || (Object)(object)__instance == (Object)null) { return; } if (localData.HasSkill("travel.portal_target")) { string value; try { value = __instance.GetText(); } catch { value = "Portal"; } string text = "unverbunden"; if (QoLSkillRuntimeFeature.TryGetPortalTarget(__instance, out var connected, out var targetLoaded, out var targetPosition)) { text = ((!connected) ? "unverbunden" : (targetLoaded ? ("verbunden; Ziel bei " + Mathf.RoundToInt(targetPosition.x) + ", " + Mathf.RoundToInt(targetPosition.z)) : "verbunden; Ziel wird geladen")); } __result = __result + "\nZielkennung: " + OrientationText.SanitizeLabel(value, "ohne Namen") + " | " + text + ""; } if (localData.HasSkill("travel.travel_check")) { string text2 = QoLSkillRuntimeFeature.TravelBlockers(); if (!string.IsNullOrWhiteSpace(text2)) { __result = __result + "\n" + text2 + ""; } } } } [HarmonyPatch(typeof(TeleportWorld), "SetText")] internal static class QoLPortalRenameWarningPatch { [HarmonyPrefix] private static void Prefix(TeleportWorld __instance, string text) { if (TalentStore.HasSkill("travel.portal_name_warning") && !((Object)(object)__instance == (Object)null)) { string text2; try { text2 = __instance.GetText(); } catch { text2 = string.Empty; } if (!string.Equals(text2 ?? string.Empty, text ?? string.Empty, StringComparison.Ordinal)) { QoLSkillRuntimeFeature.ShowCenter("Portalname wird geaendert. Die Verbindung bleibt getrennt, bis ein Gegenportal denselben Namen traegt."); } } } } [HarmonyPatch(typeof(TeleportWorld), "Teleport")] internal static class QoLPortalHistoryPatch { [HarmonyPrefix] private static void Prefix(TeleportWorld __instance, Player player) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer) && TalentStore.HasSkill("travel.portal_history")) { string name; try { name = __instance.GetText(); } catch { name = "Portal"; } TalentStore.RememberPortal(name, ((Component)__instance).transform.position); } } } internal sealed class ResetGuardianProtectionStrippedMarker : MonoBehaviour { } internal sealed class ConsumedResetGuardianVisualMarker : MonoBehaviour { } internal sealed class ResetGuardianSingleUseBehaviour : MonoBehaviour { private Piece _piece; private float _nextRefresh; private void Awake() { _piece = ((Component)this).GetComponent() ?? ((Component)this).GetComponentInParent(); Refresh(); } private void Start() { Refresh(); } private void OnEnable() { Refresh(); } private void Update() { if (!(Time.realtimeSinceStartup < _nextRefresh)) { _nextRefresh = Time.realtimeSinceStartup + 2f; Refresh(); } } private void Refresh() { try { if ((Object)(object)_piece == (Object)null) { _piece = ((Component)this).GetComponent() ?? ((Component)this).GetComponentInParent(); } if (!((Object)(object)_piece == (Object)null)) { TargetedResetFeature.RefreshResetGuardianState(_piece); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Reset-Waechter-Status konnte nicht aktualisiert werden: " + ex.Message)); } } } } [Serializable] internal sealed class ResourceMemberRecord { public string prefabName = string.Empty; public int prefabHash; public float x; public float y; public float z; public float rx; public float ry; public float rz; public float rw = 1f; public float sx = 1f; public float sy = 1f; public float sz = 1f; public float initialLiquidQuantity; public int initialLiquidByteSum; internal Vector3 Position => new Vector3(x, y, z); internal Quaternion Rotation => new Quaternion(rx, ry, rz, rw); internal Vector3 Scale => new Vector3(sx, sy, sz); } [Serializable] internal sealed class ResourceSiteRecord { public string key = string.Empty; public string resourceType = string.Empty; public float centerX; public float centerY; public float centerZ; public string state = "active"; public string registeredUtc = string.Empty; public string depletedUtc = string.Empty; public string nextResetUtc = string.Empty; public string lastResetUtc = string.Empty; public int generation; public ResourceMemberRecord[] members = new ResourceMemberRecord[0]; internal Vector3 Center => new Vector3(centerX, centerY, centerZ); } internal static class ResourceResetFeature { internal enum GuardianRequestResult { NotFound, Queued, AwaitingServer } private const string StateKind = "resource_site"; private const string RpcGuardianRequest = "ChallengeHub_ResourceReset_Request_v280"; private const string RpcGuardianAck = "ChallengeHub_ResourceReset_Ack_v280"; private static Plugin _plugin; private static ConfigEntry _enabled; private static ConfigEntry _scanSeconds; private static ConfigEntry _maxSitesPerPass; private static ConfigEntry _playerSafetyRadius; private static ConfigEntry _playerBuildSafetyRadius; private static ConfigEntry _restoreAtDepletionPercent; private static ConfigEntry _defaultCooldown; private static ConfigEntry _copperCooldown; private static ConfigEntry _silverCooldown; private static ConfigEntry _tarCooldown; private static ConfigEntry _tinCooldown; private static ConfigEntry _obsidianCooldown; private static ConfigEntry _flametalCooldown; private static ConfigEntry _ironCooldown; private static ConfigEntry _softTissueCooldown; private static ConfigEntry _patterns; private static readonly Dictionary Sites = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary SiteZdos = new Dictionary(StringComparer.Ordinal); private static bool _loaded; private static bool _processing; private static bool _rpcsRegistered; private static readonly Dictionary PendingGuardians = new Dictionary(StringComparer.Ordinal); private static readonly HashSet PendingZoneReleases = new HashSet(); private static ResourceResetWebConfig Remote => _plugin?.RemoteConfig?.resourceReset; internal static bool Enabled { get { ResourceResetWebConfig remote = Remote; if (remote == null || remote.enabled) { if (_enabled != null) { return _enabled.Value; } return true; } return false; } } internal static void Initialize(Plugin plugin) { if (!((Object)(object)plugin == (Object)null)) { _plugin = plugin; _enabled = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Resources", "Enabled", true, "Regeneriert registrierte endliche Ressourcen, ohne die umgebende Zone zu resetten."); _scanSeconds = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Resources", "ScanSeconds", 120f, "Intervall der Ressourcen-Lifecycle-Pruefung."); _maxSitesPerPass = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Resources", "MaxSitesPerPass", 8, "Maximal bearbeitete Ressourcenstandorte pro Batch."); _playerSafetyRadius = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Resources", "PlayerSafetyRadius", 50f, "Kein Respawn, solange ein Spieler direkt am Vorkommen steht."); _playerBuildSafetyRadius = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Resources", "PlayerBuildSafetyRadius", 6f, "Fehlende Ressourcenteile werden uebersprungen, wenn genau dort ein Spielerbau steht."); _restoreAtDepletionPercent = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Resources", "RestoreAtDepletionPercent", 50f, "Ab diesem prozentualen Abbau startet der Cooldown zur Ergaenzung fehlender Ressourcenteile."); _defaultCooldown = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Resources", "DefaultCooldownHours", 24f, "Standard-Cooldown."); _copperCooldown = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Resources", "CopperCooldownHours", 24f, "Kupfer-Cooldown."); _silverCooldown = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Resources", "SilverCooldownHours", 36f, "Silber-Cooldown."); _tarCooldown = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Resources", "TarCooldownHours", 12f, "Teer-Cooldown."); _tinCooldown = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Resources", "TinCooldownHours", 12f, "Zinn-Cooldown."); _obsidianCooldown = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Resources", "ObsidianCooldownHours", 24f, "Obsidian-Cooldown."); _flametalCooldown = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Resources", "FlametalCooldownHours", 72f, "Flametal-Cooldown."); _ironCooldown = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Resources", "IronCooldownHours", 24f, "Eisen-/Schrott-Cooldown."); _softTissueCooldown = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Resources", "SoftTissueCooldownHours", 48f, "Weichgewebe-Cooldown."); _patterns = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Resources", "ResourcePrefabPatterns", "rock4_copper,rock4_copper_frac,copper,rock3_silver,silver,tar,tarliquid,tarpit,tin,obsidian,flametal,mudpile,iron_scrap,giant_brain,softtissue", "Kommagetrennte Ressourcennamen. Nur Weltobjekte ohne Spieler-Creator werden registriert."); ((MonoBehaviour)plugin).StartCoroutine(RegisterRpcsWhenReady()); ((MonoBehaviour)plugin).StartCoroutine(ResourceLifecycleLoop()); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"ResourceReset 2.8.0 aktiv: Kupfer/Silber/Teer usw. werden exakt regeneriert; keine Zone wird resettet."); } } } internal static void RegisterObject(GameObject obj) { //IL_02dc: Unknown result type (might be due to invalid IL or missing references) //IL_02e9: Unknown result type (might be due to invalid IL or missing references) //IL_02f6: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_0310: Unknown result type (might be due to invalid IL or missing references) //IL_031d: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) if (!Enabled || !ChallengeHubWorldState.IsServer || (Object)(object)obj == (Object)null) { return; } string prefabName = Utils.GetPrefabName(obj); string type = ResourceType(prefabName); if (string.IsNullOrWhiteSpace(type)) { return; } ZNetView val = obj.GetComponent() ?? obj.GetComponentInParent(); if ((Object)(object)val == (Object)null || !val.IsValid() || val.GetZDO() == null) { return; } ZDO objectZdo = val.GetZDO(); try { if (objectZdo.GetLong(ZDOVars.s_creator, 0L) != 0L) { return; } } catch { } if (!LooksLikeWorldResource(obj)) { return; } EnsureLoaded(); Vector3 position = obj.transform.position; float cluster = ((type == "tar") ? 38f : ((type == "copper") ? 18f : 10f)); ResourceSiteRecord resourceSiteRecord = Sites.Values.FirstOrDefault((ResourceSiteRecord candidate) => candidate != null && candidate.resourceType == type && Utils.DistanceXZ(candidate.Center, position) <= cluster); if (resourceSiteRecord == null) { string key = "resource:" + ChallengeHubWorldState.WorldUid.ToString(CultureInfo.InvariantCulture) + ":" + type + ":" + Mathf.RoundToInt(position.x) + ":" + Mathf.RoundToInt(position.z); resourceSiteRecord = new ResourceSiteRecord { key = key, resourceType = type, centerX = position.x, centerY = position.y, centerZ = position.z, state = "active", registeredUtc = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture), members = new ResourceMemberRecord[0] }; Sites[key] = resourceSiteRecord; } List list = (resourceSiteRecord.members ?? new ResourceMemberRecord[0]).ToList(); if (list.FirstOrDefault((ResourceMemberRecord member) => member != null && member.prefabHash == objectZdo.GetPrefab() && Vector3.Distance(member.Position, position) <= 1.5f) == null) { Quaternion rotation = obj.transform.rotation; Vector3 localScale = obj.transform.localScale; byte[] data = null; try { objectZdo.GetByteArray(ZDOVars.s_liquidData, ref data); } catch { } list.Add(new ResourceMemberRecord { prefabName = prefabName, prefabHash = objectZdo.GetPrefab(), x = position.x, y = position.y, z = position.z, rx = rotation.x, ry = rotation.y, rz = rotation.z, rw = rotation.w, sx = localScale.x, sy = localScale.y, sz = localScale.z, initialLiquidQuantity = ReadLiquidQuantity(obj), initialLiquidByteSum = ByteSum(data) }); resourceSiteRecord.members = list.ToArray(); RecalculateCenter(resourceSiteRecord); Persist(resourceSiteRecord, "resource_member_registered"); SendEvent("resource_site_registered", resourceSiteRecord, "member_registered", 0); } } internal static ResourceSiteRecord FindNearest(Vector3 position, float radius) { //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) EnsureLoaded(); return (from site in Sites.Values where site != null && Utils.DistanceXZ(site.Center, position) <= radius orderby Utils.DistanceXZ(site.Center, position) select site).FirstOrDefault(); } internal static GuardianRequestResult RequestFromGuardian(Piece guardian, Player player) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) if (!Enabled || (Object)(object)guardian == (Object)null || (Object)(object)player == (Object)null) { return GuardianRequestResult.NotFound; } ZNetView val = ((Component)guardian).GetComponent() ?? ((Component)guardian).GetComponentInParent(); ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if (val2 == null) { return GuardianRequestResult.NotFound; } float resetGuardianRadius = TargetedResetFeature.GetResetGuardianRadius(guardian); if (ChallengeHubWorldState.IsServer) { ResourceSiteRecord resourceSiteRecord = FindNearest(((Component)guardian).transform.position, resetGuardianRadius); if (resourceSiteRecord == null) { return GuardianRequestResult.NotFound; } QueueSiteForGuardian(resourceSiteRecord, val2.m_uid); TargetedResetFeature.MarkResourceResetGuardianPending(guardian, resourceSiteRecord.key, DateTime.UtcNow); Plugin plugin = _plugin; if (plugin != null) { ((MonoBehaviour)plugin).StartCoroutine(ProcessSite(resourceSiteRecord, forced: true)); } return GuardianRequestResult.Queued; } if (ZRoutedRpc.instance == null || !_rpcsRegistered) { return GuardianRequestResult.AwaitingServer; } ZPackage val3 = new ZPackage(); val3.Write(val2.m_uid); val3.Write(((Component)guardian).transform.position); val3.Write(resetGuardianRadius); val3.Write(player.GetPlayerID()); ZRoutedRpc.instance.InvokeRoutedRPC(ServerPeerId(), "ChallengeHub_ResourceReset_Request_v280", new object[1] { val3 }); TargetedResetFeature.MarkResourceResetGuardianPending(guardian, "resource:server_request", DateTime.UtcNow); return GuardianRequestResult.AwaitingServer; } internal static bool RequestImmediateReset(Vector3 position, float radius, Player player, out string message) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) message = string.Empty; if (!Enabled) { message = "Ressourcen-Lifecycle ist deaktiviert."; return false; } if (!ChallengeHubWorldState.IsServer) { message = "Ressourcen-Reset wird automatisch vom Server ausgefuehrt."; return false; } ResourceSiteRecord resourceSiteRecord = FindNearest(position, radius); if (resourceSiteRecord == null) { message = "Kein registriertes Ressourcenvorkommen in Reichweite."; return false; } resourceSiteRecord.state = "depleted"; resourceSiteRecord.depletedUtc = (string.IsNullOrWhiteSpace(resourceSiteRecord.depletedUtc) ? DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture) : resourceSiteRecord.depletedUtc); resourceSiteRecord.nextResetUtc = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture); Persist(resourceSiteRecord, "resource_manual_queue"); Plugin plugin = _plugin; if (plugin != null) { ((MonoBehaviour)plugin).StartCoroutine(ProcessSite(resourceSiteRecord, forced: true)); } message = "Ressourcen-Reset fuer " + resourceSiteRecord.resourceType + " vorgemerkt."; return true; } internal static IEnumerable SnapshotSites() { EnsureLoaded(); return Sites.Values.Where((ResourceSiteRecord site) => site != null).ToArray(); } private static IEnumerator RegisterRpcsWhenReady() { while (ZRoutedRpc.instance == null) { yield return (object)new WaitForSeconds(1f); } if (!_rpcsRegistered) { ZRoutedRpc.instance.Register("ChallengeHub_ResourceReset_Request_v280", (Action)Rpc_GuardianRequest); ZRoutedRpc.instance.Register("ChallengeHub_ResourceReset_Ack_v280", (Action)Rpc_GuardianAck); _rpcsRegistered = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Ressourcen-Reset-RPCs 2.8.0 registriert."); } } } private static void Rpc_GuardianRequest(long sender, ZPackage package) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) if (!ChallengeHubWorldState.IsServer || package == null) { return; } ZDOID val; Vector3 val2; float num; long playerId; try { val = package.ReadZDOID(); val2 = package.ReadVector3(); num = package.ReadSingle(); playerId = package.ReadLong(); } catch { SendGuardianAck(sender, ok: false, "Ungueltige Ressourcen-Reset-Anfrage."); return; } if (!DungeonTalentRewardFeature.ValidateSenderOwnsPlayer(sender, playerId)) { SendGuardianAck(sender, ok: false, "Spieler-/Peer-Bindung der Reset-Anfrage ist ungueltig."); return; } ZDOMan instance = ZDOMan.instance; ZDO val3 = ((instance != null) ? instance.GetZDO(val) : null); if (val3 == null || !val3.IsValid()) { SendGuardianAck(sender, ok: false, "Reset-Waechter wurde serverseitig nicht gefunden."); return; } Vector3 position = val3.GetPosition(); if (Vector3.Distance(position, val2) > 4f) { SendGuardianAck(sender, ok: false, "Position des Reset-Waechters stimmt nicht mit dem Serverstand ueberein."); return; } Player val4 = FindPlayer(playerId); float num2 = Mathf.Max(16f, Mathf.Clamp(num, 5f, 60f) + 8f); if ((Object)(object)val4 == (Object)null || Vector3.Distance(((Component)val4).transform.position, position) > num2) { SendGuardianAck(sender, ok: false, "Du bist nicht nah genug am Reset-Waechter."); return; } ResourceSiteRecord resourceSiteRecord = FindNearest(position, Mathf.Clamp(num, 5f, 60f)); if (resourceSiteRecord == null) { SendGuardianAck(sender, ok: false, "Kein registriertes Ressourcenvorkommen in Reichweite."); return; } QueueSiteForGuardian(resourceSiteRecord, val); TargetedResetFeature.MarkResourceResetGuardianZdoPending(val3, resourceSiteRecord.key, DateTime.UtcNow); Plugin plugin = _plugin; if (plugin != null) { ((MonoBehaviour)plugin).StartCoroutine(ProcessSite(resourceSiteRecord, forced: true)); } SendGuardianAck(sender, ok: true, "Ressourcen-Reset fuer " + resourceSiteRecord.resourceType + " serverseitig vorgemerkt."); } private static void Rpc_GuardianAck(long sender, ZPackage package) { if (package != null && sender == ServerPeerId()) { bool flag = false; string text = string.Empty; try { flag = package.ReadBool(); text = package.ReadString(); } catch { } if ((Object)(object)Player.m_localPlayer != (Object)null) { ((Character)Player.m_localPlayer).Message((MessageType)2, text, 0, (Sprite)null); } if (!flag) { TargetedResetFeature.ReleaseResourceResetGuardianPending("resource:server_request", text); } } } private static void SendGuardianAck(long peer, bool ok, string message) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown if (ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(ok); val.Write(message ?? string.Empty); ZRoutedRpc.instance.InvokeRoutedRPC(peer, "ChallengeHub_ResourceReset_Ack_v280", new object[1] { val }); } } private static void QueueSiteForGuardian(ResourceSiteRecord site, ZDOID guardianId) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) if (site != null) { site.state = "depleted"; if (string.IsNullOrWhiteSpace(site.depletedUtc)) { site.depletedUtc = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture); } site.nextResetUtc = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture); PendingGuardians[site.key] = guardianId; Persist(site, "resource_guardian_queue"); } } private static IEnumerator ResourceLifecycleLoop() { while (true) { if (!ChallengeHubServerGateFeature.GameplayAllowed) { yield return (object)new WaitForSeconds(5f); continue; } ResourceResetWebConfig remote = Remote; float num = ((remote != null && remote.scanSeconds > 0f) ? Remote.scanSeconds : (_scanSeconds?.Value ?? 120f)); yield return (object)new WaitForSeconds(Mathf.Clamp(num, 20f, 1800f)); if (!Enabled || !ChallengeHubWorldState.IsServer || _processing) { continue; } _processing = true; EnsureLoaded(); ResourceResetWebConfig remote2 = Remote; int max = ((remote2 != null && remote2.maxSitesPerPass > 0) ? Remote.maxSitesPerPass : (_maxSitesPerPass?.Value ?? 8)); int count = 0; ResourceSiteRecord[] array = Sites.Values.ToArray(); foreach (ResourceSiteRecord site in array) { yield return ProcessSite(site, forced: false); int num2 = count + 1; count = num2; if (num2 >= Mathf.Clamp(max, 1, 50)) { count = 0; yield return null; } } _processing = false; } } private static IEnumerator ProcessSite(ResourceSiteRecord site, bool forced) { if (!ChallengeHubServerGateFeature.GameplayAllowed || site == null || site.members == null || site.members.Length == 0) { yield break; } Vector2i zone = ZoneSystem.GetZone(site.Center); List worldSnapshot = ValheimPrivateAccess.SnapshotAllZdos(); int num = CountLiveMembers(site, worldSnapshot); DateTime now = DateTime.UtcNow; ResourceResetWebConfig remote = Remote; float num2 = Mathf.Clamp((remote != null && remote.restoreAtDepletionPercent > 0f) ? Remote.restoreAtDepletionPercent : (_restoreAtDepletionPercent?.Value ?? 50f), 1f, 100f); float num3 = ((site.members.Length == 0) ? 0f : (100f * (float)(site.members.Length - num) / (float)site.members.Length)); if (num3 + 0.001f < num2) { string text = ((num >= site.members.Length) ? "active" : "partial"); if (!string.Equals(site.state, text, StringComparison.Ordinal)) { site.state = text; site.depletedUtc = string.Empty; site.nextResetUtc = string.Empty; Persist(site, (text == "active") ? "resource_active_again" : "resource_partially_depleted"); if (text == "partial") { SendEvent("resource_partial", site, "site_partially_depleted", 0); } } if (forced && PendingGuardians.TryGetValue(site.key, out var value)) { PendingGuardians.Remove(site.key); TargetedResetFeature.ReleaseResourceResetGuardian(value, site.key, (num >= site.members.Length) ? "Das Ressourcenvorkommen ist noch nicht erschoepft. Es wurde nichts dupliziert oder geloescht." : ("Das Ressourcenvorkommen hat den konfigurierten Abbaugrad von " + num2.ToString("0", CultureInfo.InvariantCulture) + "% noch nicht erreicht.")); } yield break; } if (site.state != "depleted" && site.state != "blocked") { site.state = "depleted"; site.depletedUtc = now.ToString("O", CultureInfo.InvariantCulture); site.nextResetUtc = now.AddHours(CooldownHours(site.resourceType)).ToString("O", CultureInfo.InvariantCulture); Persist(site, "resource_depleted"); SendEvent("resource_depleted", site, "depletion_threshold_reached:" + num3.ToString("0.##", CultureInfo.InvariantCulture), 0); if (!forced) { yield break; } } DateTime dateTime = ParseUtc(site.nextResetUtc); if (!forced && dateTime != DateTime.MinValue && now < dateTime) { yield break; } ResourceResetWebConfig remote2 = Remote; float radius = ((remote2 != null && remote2.playerSafetyRadius > 0f) ? Remote.playerSafetyRadius : (_playerSafetyRadius?.Value ?? 50f)); if (AnyPlayerNear(site.Center, radius)) { yield break; } bool manuallyLoaded = (Object)(object)ZoneSystem.instance != (Object)null && !ZoneSystem.instance.IsZoneLoaded(zone); ValheimPrivateAccess.TryPokeLocalZone(ZoneSystem.instance, zone, out var _); for (int wait = 0; wait < 12; wait++) { yield return null; } worldSnapshot = ValheimPrivateAccess.SnapshotAllZdos(); int restored = 0; int blocked = 0; int missingPrefabs = 0; HashSet protectedPlayers = ServerAuthoritativeZdoDestroyer.CollectProtectedPlayerZdos(); ResourceMemberRecord[] members = site.members; foreach (ResourceMemberRecord member in members) { if (member == null || MemberAvailable(site, member, worldSnapshot)) { continue; } ResourceResetWebConfig remote3 = Remote; float radius2 = ((remote3 != null && remote3.playerBuildSafetyRadius > 0f) ? Remote.playerBuildSafetyRadius : (_playerBuildSafetyRadius?.Value ?? 6f)); if (PlayerBuildAt(member.Position, radius2)) { blocked++; continue; } ZDO val = FindMemberZdo(member, worldSnapshot); if (val != null && string.Equals(site.resourceType, "tar", StringComparison.Ordinal)) { ServerAuthoritativeZdoDestroyer.Destroy(val, protectedPlayers, "tar_resource_replace"); ServerAuthoritativeZdoDestroyer.FlushDestroyed("tar_resource_replace"); worldSnapshot.Remove(val); yield return null; } ValheimPrivateAccess.TryGetPrefab(ZNetScene.instance, member.prefabHash, out var prefab); if ((Object)(object)prefab == (Object)null) { missingPrefabs++; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Ressourcen-Prefab fehlt beim Reset: " + member.prefabName + " / " + member.prefabHash)); } continue; } try { Object.Instantiate(prefab, member.Position, member.Rotation).transform.localScale = member.Scale; restored++; } catch (Exception ex) { missingPrefabs++; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Ressourcenobjekt konnte nicht regeneriert werden: " + member.prefabName + " :: " + ex.Message)); } } if ((restored + blocked + missingPrefabs) % 8 == 0) { yield return null; } } for (int wait = 0; wait < 8; wait++) { yield return null; } List snapshot = ValheimPrivateAccess.SnapshotAllZdos(); int num4 = CountLiveMembers(site, snapshot); if (num4 >= site.members.Length) { site.state = "active"; site.generation = Math.Max(0, site.generation) + 1; site.lastResetUtc = now.ToString("O", CultureInfo.InvariantCulture); site.depletedUtc = string.Empty; site.nextResetUtc = string.Empty; Persist(site, "resource_restored"); SendEvent("resource_reset", site, forced ? "guardian_or_admin" : "cooldown_elapsed", restored); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Ressourcenstandort regeneriert: " + site.key + "; Typ=" + site.resourceType + "; Objekte=" + restored + "; Generation=" + site.generation)); } if (PendingGuardians.TryGetValue(site.key, out var value2)) { PendingGuardians.Remove(site.key); TargetedResetFeature.CompleteResourceResetGuardian(value2, site.key); } if (manuallyLoaded) { QueueZoneRelease(zone, site.Center, site.key); } yield break; } site.state = "blocked"; site.nextResetUtc = now.AddHours(1.0).ToString("O", CultureInfo.InvariantCulture); Persist(site, "resource_restore_blocked"); SendEvent("resource_reset_blocked", site, "verified=" + num4 + "/" + site.members.Length + ";builds=" + blocked + ";prefabs=" + missingPrefabs, restored); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)("Ressourcenstandort nicht vollstaendig regeneriert: " + site.key + "; Verifiziert=" + num4 + "/" + site.members.Length + "; Bauten=" + blocked + "; FehlendePrefabs=" + missingPrefabs)); } if (PendingGuardians.TryGetValue(site.key, out var value3)) { PendingGuardians.Remove(site.key); TargetedResetFeature.ReleaseResourceResetGuardian(value3, site.key, "Ressourcenobjekte konnten wegen Spielerbauten, fehlenden Prefabs oder unvollstaendiger Verifikation nicht sicher wiederhergestellt werden."); } if (manuallyLoaded) { QueueZoneRelease(zone, site.Center, site.key); } } private static Player FindPlayer(long playerId) { try { return ((IEnumerable)Player.GetAllPlayers()).FirstOrDefault((Func)((Player player) => (Object)(object)player != (Object)null && TalentStore.SafeGetPlayerId(player) == playerId)); } catch { return null; } } private static void QueueZoneRelease(Vector2i zone, Vector3 center, string siteKey) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_plugin == (Object)null) && PendingZoneReleases.Add(zone)) { ((MonoBehaviour)_plugin).StartCoroutine(ReleaseZoneWhenUnused(zone, center, siteKey)); } } private unsafe static IEnumerator ReleaseZoneWhenUnused(Vector2i zone, Vector3 center, string siteKey) { //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) float clearSeconds = 0f; while ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.IsZoneLoaded(zone)) { if (AnyPlayerNear(center, 128f)) { clearSeconds = 0f; } else { float num; clearSeconds = (num = clearSeconds + 2f); if (num >= 10f) { break; } } yield return (object)new WaitForSeconds(2f); } if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.IsZoneLoaded(zone)) { try { ZNetScene instance = ZNetScene.instance; foreach (ZDO item in ValheimPrivateAccess.SnapshotZoneZdos(zone)) { if (!((Object)(object)instance == (Object)null) && ValheimPrivateAccess.TryGetSceneInstance(instance, item, out var view)) { GameObject val = (((Object)(object)view != (Object)null) ? ((Component)view).gameObject : null); if (view != null) { view.ResetZDO(); } ValheimPrivateAccess.RemoveSceneInstance(instance, item); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } } if (ValheimPrivateAccess.TryRemoveZoneRoot(ZoneSystem.instance, zone, out var root) && (Object)(object)root != (Object)null) { Object.Destroy((Object)(object)root); } ManualLogSource log = Plugin.Log; if (log != null) { Vector2i val2 = zone; log.LogInfo((object)("Manuell geladene Ressourcen-Zone freigegeben: " + siteKey + "; Zone=" + ((object)(*(Vector2i*)(&val2))/*cast due to .constrained prefix*/).ToString())); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Ressourcen-Zone konnte nicht vollstaendig freigegeben werden: " + siteKey + " -> " + ex.Message)); } } } PendingZoneReleases.Remove(zone); } private static int CountLiveMembers(ResourceSiteRecord site, IEnumerable snapshot) { int num = 0; ResourceMemberRecord[] members = site.members; foreach (ResourceMemberRecord resourceMemberRecord in members) { if (resourceMemberRecord != null && MemberAvailable(site, resourceMemberRecord, snapshot)) { num++; } } return num; } private static bool MemberAvailable(ResourceSiteRecord site, ResourceMemberRecord member, IEnumerable snapshot) { ZDO val = FindMemberZdo(member, snapshot); if (val == null) { return false; } if (!string.Equals(site?.resourceType, "tar", StringComparison.Ordinal)) { return true; } float num = Math.Max(0f, member.initialLiquidQuantity); int num2 = Math.Max(0, member.initialLiquidByteSum); if (ValheimPrivateAccess.TryGetSceneInstance(ZNetScene.instance, val, out var view) && (Object)(object)view != (Object)null) { float num3 = ReadLiquidQuantity(((Component)view).gameObject); if (num > 0.001f && num3 >= 0f) { return num3 > num * 0.08f; } } try { byte[] data = null; if (val.GetByteArray(ZDOVars.s_liquidData, ref data) && num2 > 0) { return (float)ByteSum(data) > (float)num2 * 0.08f; } } catch { } return true; } private static ZDO FindMemberZdo(ResourceMemberRecord member, IEnumerable snapshot) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (member == null || snapshot == null) { return null; } try { foreach (ZDO item in snapshot) { if (item != null && item.IsValid() && item.GetPrefab() == member.prefabHash && Vector3.Distance(item.GetPosition(), member.Position) <= 1.75f) { return item; } } } catch { } return null; } private static bool MemberExists(ResourceMemberRecord member) { return MemberExists(member, ValheimPrivateAccess.SnapshotAllZdos()); } private static bool MemberExists(ResourceMemberRecord member, IEnumerable snapshot) { return FindMemberZdo(member, snapshot) != null; } private static float ReadLiquidQuantity(GameObject obj) { if ((Object)(object)obj == (Object)null) { return -1f; } try { Component val = ((IEnumerable)obj.GetComponentsInChildren(true)).FirstOrDefault((Func)((Component component) => (Object)(object)component != (Object)null && ((object)component).GetType().Name.IndexOf("LiquidVolume", StringComparison.OrdinalIgnoreCase) >= 0)); if ((Object)(object)val == (Object)null) { return -1f; } Type type = ((object)val).GetType(); string[] array = new string[4] { "GetTotalVolume", "GetLiquidVolume", "GetVolume", "GetAmount" }; foreach (string text in array) { MethodInfo methodInfo = AccessTools.Method(type, text, Type.EmptyTypes, (Type[])null); if (!(methodInfo == null)) { object obj2 = methodInfo.Invoke(val, null); if (obj2 is float val2) { return Math.Max(0f, val2); } if (obj2 is double num2) { return Math.Max(0f, (float)num2); } if (obj2 is int val3) { return Math.Max(0, val3); } } } float num3 = -1f; FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { string text2 = fieldInfo.Name.ToLowerInvariant(); if (!text2.Contains("liquid") && !text2.Contains("height") && !text2.Contains("volume") && !text2.Contains("level")) { continue; } object value = fieldInfo.GetValue(val); if (!(value is IEnumerable enumerable) || value is string) { continue; } float num4 = 0f; int num5 = 0; foreach (object item in enumerable) { if (item != null) { try { num4 += Math.Max(0f, Convert.ToSingle(item, CultureInfo.InvariantCulture)); num5++; } catch { } } } if (num5 > 0) { num3 = Math.Max(num3, num4); } } return num3; } catch { return -1f; } } private static int ByteSum(byte[] data) { if (data == null || data.Length == 0) { return 0; } long num = 0L; foreach (byte b in data) { num += b; } return (int)Math.Min(2147483647L, num); } private static bool PlayerBuildAt(Vector3 position, float radius) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) try { Collider[] array = Physics.OverlapSphere(position, radius, -1, (QueryTriggerInteraction)2); foreach (Collider val in array) { Piece val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponentInParent() : null); if ((Object)(object)val2 == (Object)null) { continue; } try { if (val2.GetCreator() != 0L) { return true; } } catch { } } } catch { } return false; } private static bool AnyPlayerNear(Vector3 position, float radius) { //IL_0024: 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) try { foreach (Player allPlayer in Player.GetAllPlayers()) { if (!((Object)(object)allPlayer == (Object)null) && Utils.DistanceXZ(((Component)allPlayer).transform.position, position) <= radius) { return true; } } } catch { } return false; } private static bool LooksLikeWorldResource(GameObject obj) { if ((Object)(object)obj == (Object)null) { return false; } Component[] componentsInChildren = obj.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { string text = ((object)componentsInChildren[i])?.GetType().Name ?? string.Empty; switch (text) { default: if (text.IndexOf("Tar", StringComparison.OrdinalIgnoreCase) < 0) { continue; } break; case "MineRock": case "MineRock5": case "Pickable": case "Destructible": break; } return true; } return false; } private static string ResourceType(string prefabName) { string name = (prefabName ?? string.Empty).ToLowerInvariant(); if (!(from value in ((Remote != null && !string.IsNullOrWhiteSpace(Remote.resourcePrefabPatterns)) ? Remote.resourcePrefabPatterns : (_patterns?.Value ?? string.Empty)).Split(new char[1] { ',' }) select value.Trim().ToLowerInvariant()).Any((string value) => value.Length > 0 && name.Contains(value))) { return string.Empty; } if (name.Contains("copper")) { return "copper"; } if (name.Contains("silver")) { return "silver"; } if (name.Contains("tar")) { return "tar"; } if (name.Contains("tin")) { return "tin"; } if (name.Contains("obsidian")) { return "obsidian"; } if (name.Contains("flametal")) { return "flametal"; } if (name.Contains("mudpile") || name.Contains("iron_scrap")) { return "iron"; } if (name.Contains("giant_brain") || name.Contains("softtissue")) { return "softtissue"; } return "resource"; } private static float CooldownHours(string type) { ResourceResetWebConfig remote = Remote; return type switch { "copper" => Mathf.Max(1f, (remote != null && remote.copperCooldownHours > 0f) ? remote.copperCooldownHours : (_copperCooldown?.Value ?? 24f)), "silver" => Mathf.Max(1f, (remote != null && remote.silverCooldownHours > 0f) ? remote.silverCooldownHours : (_silverCooldown?.Value ?? 36f)), "tar" => Mathf.Max(1f, (remote != null && remote.tarCooldownHours > 0f) ? remote.tarCooldownHours : (_tarCooldown?.Value ?? 12f)), "tin" => Mathf.Max(1f, (remote != null && remote.tinCooldownHours > 0f) ? remote.tinCooldownHours : (_tinCooldown?.Value ?? 12f)), "obsidian" => Mathf.Max(1f, (remote != null && remote.obsidianCooldownHours > 0f) ? remote.obsidianCooldownHours : (_obsidianCooldown?.Value ?? 24f)), "flametal" => Mathf.Max(1f, (remote != null && remote.flametalCooldownHours > 0f) ? remote.flametalCooldownHours : (_flametalCooldown?.Value ?? 72f)), "iron" => Mathf.Max(1f, (remote != null && remote.ironCooldownHours > 0f) ? remote.ironCooldownHours : (_ironCooldown?.Value ?? 24f)), "softtissue" => Mathf.Max(1f, (remote != null && remote.softTissueCooldownHours > 0f) ? remote.softTissueCooldownHours : (_softTissueCooldown?.Value ?? 48f)), _ => Mathf.Max(1f, (remote != null && remote.defaultCooldownHours > 0f) ? remote.defaultCooldownHours : (_defaultCooldown?.Value ?? 24f)), }; } private static void RecalculateCenter(ResourceSiteRecord site) { ResourceMemberRecord[] array = site.members ?? new ResourceMemberRecord[0]; if (array.Length != 0) { site.centerX = array.Average((ResourceMemberRecord member) => member.x); site.centerY = array.Average((ResourceMemberRecord member) => member.y); site.centerZ = array.Average((ResourceMemberRecord member) => member.z); } } private static void EnsureLoaded() { if (_loaded) { return; } _loaded = true; Sites.Clear(); SiteZdos.Clear(); foreach (ZDO item in ChallengeHubWorldState.Snapshot("resource_site")) { try { ResourceSiteRecord resourceSiteRecord = JsonUtility.FromJson(ChallengeHubWorldState.ReadPayload(item)); if (resourceSiteRecord != null && !string.IsNullOrWhiteSpace(resourceSiteRecord.key)) { Sites[resourceSiteRecord.key] = resourceSiteRecord; SiteZdos[resourceSiteRecord.key] = item; } } catch { } } } private static void Persist(ResourceSiteRecord site, string reason) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) if (ChallengeHubWorldState.IsServer && site != null && !string.IsNullOrWhiteSpace(site.key)) { if (!SiteZdos.TryGetValue(site.key, out var value) || value == null || !value.IsValid()) { value = ChallengeHubWorldState.Resolve("resource_site", site.key, new Vector3(site.centerX, -24000f, site.centerZ), create: true); SiteZdos[site.key] = value; } if (value != null) { ChallengeHubWorldState.WritePayload(value, JsonUtility.ToJson((object)site), reason); } } } private static void SendEvent(string eventType, ResourceSiteRecord site, string reason, int restored) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_plugin == (Object)null || site == null) { return; } Dictionary obj = new Dictionary { { "resourceSiteKey", site.key }, { "resourceType", site.resourceType }, { "item", site.resourceType }, { "position", ChallengeHubWorldState.Vector(site.Center) }, { "resourceState", site.state }, { "generation", site.generation }, { "nextResetAt", site.nextResetUtc ?? string.Empty }, { "lastResetAt", site.lastResetUtc ?? string.Empty }, { "depletedAt", site.depletedUtc ?? string.Empty }, { "registeredAt", site.registeredUtc ?? string.Empty } }; ResourceMemberRecord[] members = site.members; obj.Add("memberCount", (members != null) ? members.Length : 0); obj.Add("restoredObjects", restored); obj.Add("reason", reason ?? string.Empty); Dictionary extra = obj; Player val = (from candidate in Player.GetAllPlayers() where (Object)(object)candidate != (Object)null && Utils.DistanceXZ(((Component)candidate).transform.position, site.Center) <= 35f orderby Utils.DistanceXZ(((Component)candidate).transform.position, site.Center) select candidate).FirstOrDefault() ?? Player.m_localPlayer; if (!(eventType == "resource_site_registered") || !((Object)(object)val == (Object)null)) { if ((Object)(object)val != (Object)null) { _plugin.SendEvent(eventType, val, extra); } else { _plugin.SendServerEvent(eventType, extra); } } } private static long ServerPeerId() { try { MethodInfo methodInfo = AccessTools.Method(((object)ZRoutedRpc.instance)?.GetType(), "GetServerPeerID", (Type[])null, (Type[])null) ?? AccessTools.Method(((object)ZRoutedRpc.instance)?.GetType(), "GetServerPeerId", (Type[])null, (Type[])null); if (methodInfo != null) { return Convert.ToInt64(methodInfo.Invoke(ZRoutedRpc.instance, null)); } } catch { } return 0L; } private static DateTime ParseUtc(string raw) { if (!DateTime.TryParse(raw, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out var result)) { return DateTime.MinValue; } return result; } } [HarmonyPatch(typeof(ZNetView), "Awake")] internal static class ResourceRegistrationPatch { [HarmonyPostfix] private static void Postfix(ZNetView __instance) { if ((Object)(object)__instance != (Object)null) { ResourceResetFeature.RegisterObject(((Component)__instance).gameObject); } } } internal static class ServerAuthoritativeZdoDestroyer { internal enum DestroyResult { Destroyed, AlreadyGone, ProtectedPlayer, AuthorityFailed, Failed } private static readonly string[] SpawnedZdoKeys = new string[6] { "spawn_id", "spawned", "spawned_id", "spawned_zdo", "spawnedZDO", "SpawnedZDO" }; internal static HashSet CollectProtectedPlayerZdos() { //IL_00b3: 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) HashSet hashSet = new HashSet(); try { foreach (Player allPlayer in Player.GetAllPlayers()) { if (!((Object)(object)allPlayer == (Object)null)) { ZNetView component = ((Component)allPlayer).GetComponent(); ZDO val = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); if (val != null && val.IsValid()) { hashSet.Add(val.m_uid); } } } } catch { } try { if ((Object)(object)ZNet.instance != (Object)null) { foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (TryReadPeerCharacterId(peer, out var result) && !((ZDOID)(ref result)).IsNone()) { hashSet.Add(result); } } } } catch { } return hashSet; } internal static bool IsProtectedPlayerZdo(ZDO zdo, HashSet protectedIds) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || !zdo.IsValid()) { return false; } if (protectedIds != null && protectedIds.Contains(zdo.m_uid)) { return true; } GameObject val = null; try { val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(zdo.GetPrefab()) : null); } catch { } if ((Object)(object)val != (Object)null) { return (Object)(object)val.GetComponent() != (Object)null; } return false; } internal static DestroyResult Destroy(ZDO zdo, HashSet protectedIds, string context) { HashSet visited = new HashSet(); return DestroyRecursive(zdo, protectedIds ?? CollectProtectedPlayerZdos(), context, visited, 0); } private static DestroyResult DestroyRecursive(ZDO zdo, HashSet protectedIds, string context, HashSet visited, int depth) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: 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_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || !zdo.IsValid()) { return DestroyResult.AlreadyGone; } if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null) { return DestroyResult.Failed; } if (!visited.Add(zdo.m_uid)) { return DestroyResult.AlreadyGone; } if (IsProtectedPlayerZdo(zdo, protectedIds)) { return DestroyResult.ProtectedPlayer; } if (!ValheimNetworkCompatibility.TryTakeServerOwnership(zdo, context ?? "zdo_destroy")) { return DestroyResult.AuthorityFailed; } if (depth < 4) { foreach (ZDOID item in ReadSpawnedZdoIds(zdo)) { ZDOID current = item; if (((ZDOID)(ref current)).IsNone() || protectedIds.Contains(current)) { continue; } ZDO val = null; try { val = ZDOMan.instance.GetZDO(current); } catch { } if (val != null && val.IsValid()) { DestroyResult destroyResult = DestroyRecursive(val, protectedIds, (context ?? "zdo_destroy") + "/spawned", visited, depth + 1); switch (destroyResult) { case DestroyResult.ProtectedPlayer: return DestroyResult.ProtectedPlayer; case DestroyResult.AuthorityFailed: case DestroyResult.Failed: return destroyResult; } } } } ZDOID uid = zdo.m_uid; try { GameObject val2 = FindLoadedInstance(zdo); if ((Object)(object)val2 != (Object)null) { if ((Object)(object)val2.GetComponent() != (Object)null || (Object)(object)val2.GetComponentInParent() != (Object)null) { return DestroyResult.ProtectedPlayer; } if ((Object)(object)ZNetScene.instance != (Object)null) { ZNetScene.instance.Destroy(val2); } } ZDO val3 = null; try { val3 = ZDOMan.instance.GetZDO(uid); } catch { val3 = zdo; } if (val3 != null && val3.IsValid()) { if (!ValheimNetworkCompatibility.TryTakeServerOwnership(val3, context ?? "zdo_destroy")) { return DestroyResult.AuthorityFailed; } ZDOMan.instance.DestroyZDO(val3); } ZDO val4 = null; try { val4 = ZDOMan.instance.GetZDO(uid); } catch { } if (val4 != null && val4.IsValid()) { InvokeHandleDestroyedZdo(uid, val4); } ZDO val5 = null; try { val5 = ZDOMan.instance.GetZDO(uid); } catch { } return (val5 != null && val5.IsValid()) ? DestroyResult.Failed : DestroyResult.Destroyed; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Serverautoritärer ZDO-Loeschpfad fehlgeschlagen: " + (context ?? "unknown") + " -> " + ex.Message)); } return DestroyResult.Failed; } } internal static void FlushDestroyed(string context) { if (ZDOMan.instance == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } try { AccessTools.GetDeclaredMethods(((object)ZDOMan.instance).GetType()).FirstOrDefault((MethodInfo candidate) => (string.Equals(candidate.Name, "SendDestroyed", StringComparison.Ordinal) || string.Equals(candidate.Name, "FlushDestroyed", StringComparison.Ordinal)) && candidate.GetParameters().Length == 0)?.Invoke(ZDOMan.instance, null); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Destroy-Liste konnte nicht sofort gesendet werden: " + (context ?? "unknown") + " -> " + ex.Message)); } } } private static GameObject FindLoadedInstance(ZDO zdo) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNetScene.instance == (Object)null || zdo == null || !zdo.IsValid()) { return null; } ZDOID uid = zdo.m_uid; try { foreach (MethodInfo item in from candidate in AccessTools.GetDeclaredMethods(((object)ZNetScene.instance).GetType()) where string.Equals(candidate.Name, "FindInstance", StringComparison.Ordinal) && candidate.GetParameters().Length == 1 select candidate) { Type parameterType = item.GetParameters()[0].ParameterType; object obj; if (parameterType == typeof(ZDOID)) { obj = uid; } else { if (!(parameterType == typeof(ZDO))) { continue; } obj = zdo; } GameObject val = AsGameObject(item.Invoke(ZNetScene.instance, new object[1] { obj })); if ((Object)(object)val != (Object)null) { return val; } } } catch { } try { foreach (FieldInfo item2 in from candidate in ((object)ZNetScene.instance).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where candidate.Name.IndexOf("instance", StringComparison.OrdinalIgnoreCase) >= 0 select candidate) { if (item2.GetValue(ZNetScene.instance) is IDictionary dictionary && dictionary.Contains(uid)) { GameObject val2 = AsGameObject(dictionary[uid]); if ((Object)(object)val2 != (Object)null) { return val2; } } } foreach (PropertyInfo item3 in from candidate in ((object)ZNetScene.instance).GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where candidate.GetIndexParameters().Length == 0 && candidate.Name.IndexOf("instance", StringComparison.OrdinalIgnoreCase) >= 0 select candidate) { if (item3.GetValue(ZNetScene.instance, null) is IDictionary dictionary2 && dictionary2.Contains(uid)) { GameObject val3 = AsGameObject(dictionary2[uid]); if ((Object)(object)val3 != (Object)null) { return val3; } } } } catch { } return null; } private static GameObject AsGameObject(object value) { ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if (val != null) { return ((Component)val).gameObject; } GameObject val2 = (GameObject)((value is GameObject) ? value : null); if (val2 != null) { return val2; } Component val3 = (Component)((value is Component) ? value : null); if (val3 != null) { return val3.gameObject; } return null; } private static IEnumerable ReadSpawnedZdoIds(ZDO zdo) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) HashSet hashSet = new HashSet(); if (zdo == null) { return hashSet; } try { ZDOID connectionZDOID = zdo.GetConnectionZDOID((ConnectionType)3); if (!((ZDOID)(ref connectionZDOID)).IsNone()) { hashSet.Add(connectionZDOID); } } catch { } string[] spawnedZdoKeys = SpawnedZdoKeys; foreach (string text in spawnedZdoKeys) { try { foreach (MethodInfo item2 in from candidate in ((object)zdo).GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where string.Equals(candidate.Name, "GetZDOID", StringComparison.Ordinal) select candidate) { ParameterInfo[] parameters = item2.GetParameters(); if (parameters.Length != 0 && !(parameters[0].ParameterType != typeof(string))) { object[] array = new object[parameters.Length]; array[0] = text; for (int num = 1; num < parameters.Length; num++) { array[num] = (parameters[num].HasDefaultValue ? parameters[num].DefaultValue : (parameters[num].ParameterType.IsValueType ? Activator.CreateInstance(parameters[num].ParameterType) : null)); } if (item2.Invoke(zdo, array) is ZDOID item && !((ZDOID)(ref item)).IsNone()) { hashSet.Add(item); } } } } catch { } } return hashSet; } private static bool TryReadPeerCharacterId(ZNetPeer peer, out ZDOID result) { //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_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) result = ZDOID.None; if (peer == null) { return false; } string[] array = new string[4] { "m_characterID", "m_characterId", "CharacterID", "CharacterId" }; foreach (string name in array) { try { FieldInfo field = ((object)peer).GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.FieldType == typeof(ZDOID)) { result = (ZDOID)field.GetValue(peer); return !((ZDOID)(ref result)).IsNone(); } PropertyInfo property = ((object)peer).GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.PropertyType == typeof(ZDOID)) { result = (ZDOID)property.GetValue(peer, null); return !((ZDOID)(ref result)).IsNone(); } } catch { } } return false; } private static void InvokeHandleDestroyedZdo(ZDOID uid, ZDO zdo) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) try { foreach (MethodInfo item in from candidate in AccessTools.GetDeclaredMethods(((object)ZDOMan.instance).GetType()) where string.Equals(candidate.Name, "HandleDestroyedZDO", StringComparison.Ordinal) select candidate) { ParameterInfo[] parameters = item.GetParameters(); if (parameters.Length == 1) { if (parameters[0].ParameterType == typeof(ZDOID)) { item.Invoke(ZDOMan.instance, new object[1] { uid }); break; } if (parameters[0].ParameterType == typeof(ZDO)) { item.Invoke(ZDOMan.instance, new object[1] { zdo }); break; } } } } catch { } } } internal static class ServerSyncBridge { private static object _configSync; private static Type _configSyncType; private static ManualLogSource _logger; internal static bool Available => _configSync != null; internal static void Initialize(ManualLogSource logger) { _logger = logger; try { Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly item) => string.Equals(item.GetName().Name, "ServerSync", StringComparison.OrdinalIgnoreCase)); if (assembly == null) { ManualLogSource logger2 = _logger; if (logger2 != null) { logger2.LogInfo((object)"ServerSync nicht geladen. Mod läuft ohne Config-Sync. Installiere ServerSync, wenn Serverwerte zu Clients synchronisiert werden sollen."); } return; } _configSyncType = assembly.GetType("ServerSync.ConfigSync"); if (_configSyncType == null) { ManualLogSource logger3 = _logger; if (logger3 != null) { logger3.LogWarning((object)"ServerSync gefunden, aber ConfigSync-Typ nicht gefunden."); } return; } _configSync = Activator.CreateInstance(_configSyncType, "de.challengehub.valheim.bluteid"); SetProperty("DisplayName", "ChallengeHub Valheim - Der Blut-Eid: Vorzeichen des Nordens"); SetProperty("CurrentVersion", "2.12.50"); SetProperty("MinimumRequiredVersion", "2.12.50"); SetProperty("ModRequired", true); ManualLogSource logger4 = _logger; if (logger4 != null) { logger4.LogInfo((object)"ServerSync aktiv: ChallengeHub-Konfiguration wird vom Server zu Clients synchronisiert."); } } catch (Exception ex) { _configSync = null; _configSyncType = null; ManualLogSource logger5 = _logger; if (logger5 != null) { logger5.LogWarning((object)("ServerSync konnte nicht initialisiert werden: " + ex.Message)); } } } internal static void AddSynced(ConfigEntry entry, bool synchronized) { if (_configSync == null || _configSyncType == null || entry == null) { return; } try { object obj = null; foreach (MethodInfo item in from item in _configSyncType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where item.Name == "AddConfigEntry" select item) { try { MethodInfo methodInfo = (item.IsGenericMethodDefinition ? item.MakeGenericMethod(typeof(T)) : item); if (methodInfo.GetParameters().Length != 1) { continue; } obj = methodInfo.Invoke(_configSync, new object[1] { entry }); break; } catch { } } if (obj != null) { PropertyInfo property = obj.GetType().GetProperty("SynchronizedConfig", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite) { property.SetValue(obj, synchronized, null); } } } catch (Exception ex) { ManualLogSource logger = _logger; if (logger != null) { logger.LogWarning((object)("ServerSync-Eintrag " + ((ConfigEntryBase)entry).Definition.Section + "." + ((ConfigEntryBase)entry).Definition.Key + " konnte nicht registriert werden: " + ex.Message)); } } } private static void SetProperty(string name, object value) { PropertyInfo propertyInfo = _configSyncType?.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (propertyInfo != null && propertyInfo.CanWrite) { propertyInfo.SetValue(_configSync, value, null); } } } internal enum SkillBranch { Core, Comfort, Storage, Building, Orientation, Recovery, Travel } internal sealed class SkillNodeDefinition { internal string Id; internal SkillBranch Branch; internal string Title; internal string Description; internal string Hotkey; internal int Cost; internal string[] Prerequisites; internal bool Enabled; internal SkillNodeDefinition(string id, SkillBranch branch, string title, string description, string hotkey, params string[] prerequisites) { Id = id; Branch = branch; Title = title; Description = description; Hotkey = hotkey ?? string.Empty; Cost = 1; Prerequisites = prerequisites ?? Array.Empty(); Enabled = true; } } internal static class SkillIds { internal const string Pioneer = "core.pioneer"; internal const string OrderKeeper = "comfort.order_keeper"; internal const string StackMaster = "comfort.stack_master"; internal const string CollectorSight = "comfort.collector_sight"; internal const string ToolCare = "comfort.tool_care"; internal const string BuilderMemory = "comfort.builder_memory"; internal const string GrowthSight = "comfort.growth_sight"; internal const string AreaHarvest = "comfort.area_harvest"; internal const string DoorGuardian = "comfort.door_guardian"; internal const string QuickStore = "storage.quick_store"; internal const string StackNearby = "storage.stack_nearby"; internal const string ProtectedSlots = "storage.protected_slots"; internal const string Favorites = "storage.favorites"; internal const string PickupFilter = "storage.pickup_filter"; internal const string WeightPreview = "storage.weight_preview"; internal const string WorkshopStorage = "storage.workshop_storage"; internal const string FuelHelper = "storage.fuel_helper"; internal const string FuelHelperStorage = "storage.fuel_helper_storage"; internal const string MaterialOverview = "building.material_overview"; internal const string SmartCopy = "building.smart_copy"; internal const string FineRotation = "building.fine_rotation"; internal const string SnapSwitch = "building.snap_switch"; internal const string RefundPreview = "building.refund_preview"; internal const string GridPlanting = "building.grid_planting"; internal const string SignWorkshop = "building.sign_workshop"; internal const string Compass = "orientation.compass"; internal const string DungeonClock = "orientation.dungeon_clock"; internal const string ExitDirection = "orientation.exit_direction"; internal const string PersonalBuoys = "orientation.personal_buoys"; internal const string LootOverview = "orientation.loot_overview"; internal const string PrivateSigns = "orientation.private_signs"; internal const string DungeonMemory = "orientation.dungeon_memory"; internal const string SharedSigns = "orientation.shared_signs"; internal const string ParticipantStatus = "orientation.participant_status"; internal const string CompletionNotice = "orientation.completion_notice"; internal const string SwimOverview = "orientation.swim_overview"; internal const string GraveCompass = "recovery.grave_compass"; internal const string GraveContents = "recovery.grave_contents"; internal const string SafeRecovery = "recovery.safe_recovery"; internal const string RecoveryReserve = "recovery.recovery_reserve"; internal const string PortalTarget = "travel.portal_target"; internal const string TravelCheck = "travel.travel_check"; internal const string PortalNameWarning = "travel.portal_name_warning"; internal const string PortalHistory = "travel.portal_history"; internal const string PortalOverview = "travel.portal_overview"; } internal static class SkillTreeDefinitions { private static readonly List Nodes = new List { new SkillNodeDefinition("core.pioneer", SkillBranch.Core, "Pionier", "Schaltet die sechs unabhängigen QoL-Zweige frei.", "K"), new SkillNodeDefinition("comfort.order_keeper", SkillBranch.Comfort, "Ordnungshüter", "Sortiert das Spielerinventar nach Kategorie, Qualität und Name. Die Schnellleiste bleibt unangetastet.", "Alt+S", "core.pioneer"), new SkillNodeDefinition("comfort.stack_master", SkillBranch.Comfort, "Stapelmeister", "Führt gleiche Gegenstände im Spielerinventar zu vollständigen Stapeln zusammen.", "Alt+C", "comfort.order_keeper"), new SkillNodeDefinition("comfort.collector_sight", SkillBranch.Comfort, "Sammlerblick", "Zeigt beim Anvisieren Menge, Stapelgröße und Gewicht nach dem Aufheben.", "Hover", "comfort.stack_master"), new SkillNodeDefinition("comfort.tool_care", SkillBranch.Comfort, "Werkzeugpflege", "Repariert alle an der aktuellen Station reparierbaren Gegenstände in einem Durchgang.", "Alt+R", "core.pioneer"), new SkillNodeDefinition("comfort.builder_memory", SkillBranch.Comfort, "Baumeister-Gedächtnis", "Merkt sich das zuletzt platzierte Bauteil, den Drehschritt und die tatsächliche Drehung charaktergebunden.", "automatisch", "core.pioneer"), new SkillNodeDefinition("comfort.growth_sight", SkillBranch.Comfort, "Wachstumsblick", "Zeigt bei Pflanzen und nachwachsenden Pickables Fortschritt, Farbe und verbleibende Zeit im Hovertext.", "Hover", "comfort.collector_sight"), new SkillNodeDefinition("comfort.area_harvest", SkillBranch.Comfort, "Flächenernte", "Erntet mit Alt+Benutzen gleiche, erntereife Pickables im begrenzten Radius. Unreife oder geschützte Ziele werden ausgelassen.", "Alt+E am Pickable", "comfort.growth_sight"), new SkillNodeDefinition("comfort.door_guardian", SkillBranch.Comfort, "Türwächter", "Schließt nur vom Spieler geöffnete Türen nach einer Verzögerung, wenn niemand im Türbereich steht. Dungeonbetrieb ist separat schaltbar.", "Alt+D Option", "comfort.tool_care"), new SkillNodeDefinition("storage.quick_store", SkillBranch.Storage, "Intelligentes Einlagern", "Verteilt nicht geschützte Gegenstände zuerst in Kisten mit demselben Gegenstand und danach in passende Materialgruppen. Leere Zielkisten sind optional.", "Alt+Q", "core.pioneer"), new SkillNodeDefinition("storage.stack_nearby", SkillBranch.Storage, "Lager-Routing", "Nutzt alle berechtigten Kisten im Umkreis, respektiert Besitz, Wards, aktive Benutzer und die gespeicherte Lagergruppen-Option.", "Alt+Q / Alt+U", "storage.quick_store"), new SkillNodeDefinition("storage.protected_slots", SkillBranch.Storage, "Geschützte Plätze", "Schützt gezielt markierte Schnellleistenplätze vor ChallengeHub-Sortier- und Einlagerungsaktionen.", "Alt+L", "storage.quick_store"), new SkillNodeDefinition("storage.favorites", SkillBranch.Storage, "Favoriten", "Markiert den ausgerüsteten Gegenstand als Favorit; ChallengeHub-Aktionen lassen ihn unangetastet.", "Alt+F", "storage.protected_slots"), new SkillNodeDefinition("storage.pickup_filter", SkillBranch.Storage, "Aufnahmefilter", "Schaltet den anvisierten Bodengegenstand für automatisches Aufheben ein oder aus.", "Alt+P", "storage.quick_store"), new SkillNodeDefinition("storage.weight_preview", SkillBranch.Storage, "Gewichtsvorschau", "Warnt im Gegenstands-Hovertext, wenn das Aufheben überladen würde.", "Hover", "storage.pickup_filter"), new SkillNodeDefinition("storage.workshop_storage", SkillBranch.Storage, "Werkstattlager", "Zeigt nahe Lagerbestände an und stellt sie sicheren serverautoritären Komfortaktionen bereit. Normales Crafting verbraucht weiterhin regulär Ressourcen.", "Alt+M", "storage.stack_nearby"), new SkillNodeDefinition("storage.fuel_helper", SkillBranch.Storage, "Brennstoffhelfer", "Füllt Feuerstellen und Produktionsgeräte bis zum gewählten Zielwert aus dem eigenen Inventar. Der echte Brennstoff wird verbraucht.", "Alt+H Option / Interaktion", "storage.workshop_storage"), new SkillNodeDefinition("storage.fuel_helper_storage", SkillBranch.Storage, "Brennstofflager", "Erweitert den Brennstoffhelfer auf berechtigte nahe Kisten mit exklusiver Container-Sperre und bestätigter ZDO-Speicherung.", "automatisch", "storage.fuel_helper"), new SkillNodeDefinition("building.material_overview", SkillBranch.Building, "Materialübersicht", "Zeigt beim Bauen für jedes Material automatisch Inventarbestand, Bedarf und fehlende Menge farbig an.", "Bau-HUD / Alt+M", "core.pioneer"), new SkillNodeDefinition("building.smart_copy", SkillBranch.Building, "Intelligentes Nachbauen", "Wählt das anvisierte bekannte Bauteil bestmöglich im aktuellen Hammer-Bautisch aus.", "Alt+E", "building.material_overview"), new SkillNodeDefinition("building.fine_rotation", SkillBranch.Building, "Feine Drei-Achsen-Drehung", "Dreht um X, Y oder Z in Welt- oder Lokalachsen mit 1°, 5°, 15°, 22,5°, 45° und 90°. Achse und Rotation können zurückgesetzt werden.", "Alt+V / Alt+Z / Alt+Shift+Rad", "building.smart_copy"), new SkillNodeDefinition("building.snap_switch", SkillBranch.Building, "Snap-Wechsel", "Schaltet gezielt durch die nativen Snap-Punkte des aktuellen Bauteils.", "Alt+X", "building.smart_copy"), new SkillNodeDefinition("building.refund_preview", SkillBranch.Building, "Baumaterial zurück", "Zeigt im Bau-HUD die beim Abreißen des anvisierten Bauteils zurückgegebenen Materialien.", "HUD", "building.material_overview"), new SkillNodeDefinition("building.grid_planting", SkillBranch.Building, "Rasterpflanzung", "Pflanzt mit gehaltener Alt-Taste ein 2×2-, 3×3- oder 5×5-Raster. Nur gültige Positionen verbrauchen Samen, Ausdauer und Haltbarkeit.", "Alt+J Option / Alt beim Pflanzen", "building.material_overview"), new SkillNodeDefinition("building.sign_workshop", SkillBranch.Building, "Schildwerkstatt", "Öffnet einen sicheren Editor mit Vorschau, Farbe, Schriftgröße, Fett/Kursiv, Ausrichtung und drei charaktergebundenen Vorlagen.", "Alt+I am Schild", "building.smart_copy"), new SkillNodeDefinition("orientation.compass", SkillBranch.Orientation, "Kompass", "Zeigt eine dezente Himmelsrichtung im HUD.", "HUD", "core.pioneer"), new SkillNodeDefinition("orientation.dungeon_clock", SkillBranch.Orientation, "Dungeon-Uhr", "Zeigt den nächsten automatischen Reset beim Betreten und Verlassen.", "automatisch", "orientation.compass"), new SkillNodeDefinition("orientation.exit_direction", SkillBranch.Orientation, "Ausgangsrichtung", "Zeigt Richtung und Entfernung zum Dungeon-Ausgang.", "HUD", "orientation.compass"), new SkillNodeDefinition("orientation.personal_buoys", SkillBranch.Orientation, "Persönliche Bojen", "Setzt weltbezogene, ausschließlich lokal sichtbare Bojen und Wegpunkte.", "Alt+B / Alt+N", "orientation.compass"), new SkillNodeDefinition("orientation.loot_overview", SkillBranch.Orientation, "Beuteübersicht", "Zeigt beim Ausgang gefüllte Behälter und ungepflückte Objekte.", "automatisch", "orientation.dungeon_clock"), new SkillNodeDefinition("orientation.private_signs", SkillBranch.Orientation, "Private Wegzeichen", "Setzt private Dungeon-Wegzeichen, die Reset und Logout überleben.", "Alt+W", "orientation.personal_buoys"), new SkillNodeDefinition("orientation.dungeon_memory", SkillBranch.Orientation, "Grabkammer-Gedächtnis", "Ergänzt den Hovertext um geleert/aktiv und die verbleibende Resetzeit.", "Hover", "orientation.loot_overview"), new SkillNodeDefinition("orientation.shared_signs", SkillBranch.Orientation, "Pfadfinder-Netz", "Teilt Wegzeichen nur mit Spielern, die denselben Knoten besitzen.", "Alt+G", "orientation.private_signs"), new SkillNodeDefinition("orientation.participant_status", SkillBranch.Orientation, "Teilnehmerstatus", "Zeigt die eigene Registrierung und die Teilnehmerzahl des Dungeons.", "automatisch", "orientation.shared_signs"), new SkillNodeDefinition("orientation.completion_notice", SkillBranch.Orientation, "Abschlussbestätigung", "Zeigt Leerung, Belohnung und Empfängerzahl eindeutig an.", "automatisch", "orientation.participant_status"), new SkillNodeDefinition("orientation.swim_overview", SkillBranch.Orientation, "Schwimmübersicht", "Zeigt Ausdauer, geschätzte Restzeit, Schwimmeffizienz und eine Warnung bei kritischer Ausdauer. Es gibt keinen Geschwindigkeitsbonus.", "HUD beim Schwimmen", "orientation.compass"), new SkillNodeDefinition("recovery.grave_compass", SkillBranch.Recovery, "Grabstein-Kompass", "Zeigt Richtung und Entfernung zum nächsten eigenen geladenen Grabstein.", "HUD", "core.pioneer"), new SkillNodeDefinition("recovery.grave_contents", SkillBranch.Recovery, "Grabstein-Inhalt", "Zeigt Stapelanzahl, Gesamtgewicht und ruhende Erkundungspunkte im Hovertext.", "Hover", "recovery.grave_compass"), new SkillNodeDefinition("recovery.safe_recovery", SkillBranch.Recovery, "Sichere Bergung", "Warnt vor der Interaktion bei fehlenden Plätzen oder zu hohem Gesamtgewicht.", "automatisch", "recovery.grave_contents"), new SkillNodeDefinition("recovery.recovery_reserve", SkillBranch.Recovery, "Bergungsreserve", "Behandelt Schnellleiste, Ausrüstung und Favoriten bei ChallengeHub-Aktionen als reserviert.", "automatisch", "recovery.safe_recovery"), new SkillNodeDefinition("travel.portal_target", SkillBranch.Travel, "Zielanzeige", "Ergänzt Portalname, Verbindungsstatus und Zielposition, sofern geladen.", "Hover", "core.pioneer"), new SkillNodeDefinition("travel.travel_check", SkillBranch.Travel, "Reiseprüfung", "Listet vor der Reise nicht teleportierbare Gegenstände auf.", "Hover / Alt+T", "travel.portal_target"), new SkillNodeDefinition("travel.portal_name_warning", SkillBranch.Travel, "Namenswarnung", "Warnt beim Umbenennen, dass die Verbindung vorübergehend getrennt wird.", "automatisch", "travel.travel_check"), new SkillNodeDefinition("travel.portal_history", SkillBranch.Travel, "Portal-Verlauf", "Speichert die zuletzt benutzten Portalnamen und Positionen charakter- und weltbezogen.", "automatisch", "travel.portal_target"), new SkillNodeDefinition("travel.portal_overview", SkillBranch.Travel, "Portalübersicht", "Öffnet eine Liste bekannter und aktuell geladener Portale mit Status und Entfernung.", "Alt+O", "travel.portal_history") }; private static readonly Dictionary ById = Nodes.ToDictionary((SkillNodeDefinition node) => node.Id, (SkillNodeDefinition node) => node, StringComparer.Ordinal); internal static IReadOnlyList All => Nodes; internal static SkillNodeDefinition Get(string id) { if (string.IsNullOrWhiteSpace(id)) { return null; } ById.TryGetValue(id, out var value); return value; } internal static IReadOnlyList ForBranch(SkillBranch branch) { return Nodes.Where((SkillNodeDefinition node) => node.Branch == branch).ToArray(); } internal static bool PrerequisitesMet(TalentData data, SkillNodeDefinition node) { if (data == null || node == null) { return false; } return node.Prerequisites.All(data.HasUnlockedSkill); } internal static string BranchTitle(SkillBranch branch) { return branch switch { SkillBranch.Comfort => "Grundkomfort", SkillBranch.Storage => "Lager & Inventar", SkillBranch.Building => "Crafting & Bauen", SkillBranch.Orientation => "Orientierung & Dungeons", SkillBranch.Recovery => "Tod & Wiederbeschaffung", SkillBranch.Travel => "Portale & Reisen", _ => "Pionier", }; } } [Serializable] internal sealed class PersonalOrientationMarkerData { public string Id = string.Empty; public string WorldToken = string.Empty; public string Name = string.Empty; public int Kind; public float X; public float Y; public float Z; internal Vector3 Position => new Vector3(X, Y, Z); internal void Normalize(int fallbackIndex) { if (string.IsNullOrWhiteSpace(Id)) { Id = Guid.NewGuid().ToString("N"); } WorldToken = (WorldToken ?? string.Empty).Trim(); Name = OrientationText.SanitizeLabel(Name, "Markierung " + fallbackIndex); Kind = Mathf.Clamp(Kind, 1, 2); if (!OrientationText.IsFinite(X)) { X = 0f; } if (!OrientationText.IsFinite(Y)) { Y = 0f; } if (!OrientationText.IsFinite(Z)) { Z = 0f; } } } [Serializable] internal sealed class TalentData { internal const int CurrentVersion = 4; public int Version = 4; public int AvailablePoints; public int LevelWeight; public int LevelStamina; public int LevelInventory; public int LevelOrientation; public List UnlockedSkillIds = new List(); public List DisabledSkillIds = new List(); public List ProtectedInventorySlots = new List(); public List FavoriteItemKeys = new List(); public List PickupFilterItemKeys = new List(); public List PortalHistory = new List(); public string LastBuildPiece = string.Empty; public float LastBuildRotation; public int LastBuildRotationIndex; public bool AllowEmptyStorageTargets; public int FarmingGridSize = 3; public int FuelTargetPercent = 50; public bool AutoCloseDoorsInDungeons; public int FineRotationAxis; public bool FineRotationLocalFrame; public float FineRotationStep = 15f; public float FineRotationX; public float FineRotationY; public float FineRotationZ; public string SignColor = "#FFFFFF"; public int SignFontSize = 24; public bool SignBold; public bool SignItalic; public int SignAlignment; public List SignStylePresets = new List(); public List ProcessedRewardIds = new List(); public List PersonalOrientationMarkers = new List(); internal void Normalize() { Version = 4; AvailablePoints = Math.Max(0, AvailablePoints); LevelWeight = Mathf.Clamp(LevelWeight, 0, 10); LevelStamina = Mathf.Clamp(LevelStamina, 0, 10); LevelInventory = Math.Max(0, LevelInventory); LevelOrientation = Mathf.Clamp(LevelOrientation, 0, 4); if (UnlockedSkillIds == null) { UnlockedSkillIds = new List(); } if (DisabledSkillIds == null) { DisabledSkillIds = new List(); } if (ProtectedInventorySlots == null) { ProtectedInventorySlots = new List(); } if (FavoriteItemKeys == null) { FavoriteItemKeys = new List(); } if (PickupFilterItemKeys == null) { PickupFilterItemKeys = new List(); } if (PortalHistory == null) { PortalHistory = new List(); } UnlockedSkillIds = UnlockedSkillIds.Where((string id) => SkillTreeDefinitions.Get(id) != null).Distinct(StringComparer.Ordinal).ToList(); DisabledSkillIds = DisabledSkillIds.Where((string id) => SkillTreeDefinitions.Get(id) != null && UnlockedSkillIds.Contains(id)).Distinct(StringComparer.Ordinal).ToList(); if (LevelOrientation > 0) { AddSkillForMigration("core.pioneer"); } if (LevelOrientation >= 1) { AddSkillForMigration("orientation.compass"); AddSkillForMigration("orientation.dungeon_clock"); AddSkillForMigration("orientation.exit_direction"); } if (LevelOrientation >= 2) { AddSkillForMigration("orientation.personal_buoys"); } if (LevelOrientation >= 3) { AddSkillForMigration("orientation.private_signs"); } if (LevelOrientation >= 4) { AddSkillForMigration("orientation.shared_signs"); } ProtectedInventorySlots = (from slot in ProtectedInventorySlots.Where((int slot) => slot >= 0 && slot < 64).Distinct() orderby slot select slot).ToList(); FavoriteItemKeys = NormalizeStringKeys(FavoriteItemKeys, 64); PickupFilterItemKeys = NormalizeStringKeys(PickupFilterItemKeys, 64); List list = new List(); foreach (PortalHistoryEntry item in PortalHistory) { if (item != null) { item.Normalize(); if (!string.IsNullOrWhiteSpace(item.WorldToken)) { list.Add(item); } } } PortalHistory = list.TakeLastCompat(24).ToList(); LastBuildPiece = OrientationText.SanitizeLabel(LastBuildPiece, string.Empty); if (!OrientationText.IsFinite(LastBuildRotation)) { LastBuildRotation = 0f; } LastBuildRotationIndex = Mathf.Clamp(LastBuildRotationIndex, -4096, 4096); FarmingGridSize = ((FarmingGridSize <= 2) ? 2 : ((FarmingGridSize >= 5) ? 5 : 3)); FuelTargetPercent = Mathf.Clamp(FuelTargetPercent, 25, 100); FineRotationAxis = Mathf.Clamp(FineRotationAxis, 0, 2); if (!OrientationText.IsFinite(FineRotationStep)) { FineRotationStep = 15f; } float[] source = new float[6] { 1f, 5f, 15f, 22.5f, 45f, 90f }; FineRotationStep = source.OrderBy((float step) => Mathf.Abs(step - FineRotationStep)).First(); if (!OrientationText.IsFinite(FineRotationX)) { FineRotationX = 0f; } if (!OrientationText.IsFinite(FineRotationY)) { FineRotationY = 0f; } if (!OrientationText.IsFinite(FineRotationZ)) { FineRotationZ = 0f; } FineRotationX = Mathf.Repeat(FineRotationX, 360f); FineRotationY = Mathf.Repeat(FineRotationY, 360f); FineRotationZ = Mathf.Repeat(FineRotationZ, 360f); SignColor = NormalizeHexColor(SignColor); SignFontSize = Mathf.Clamp(SignFontSize, 12, 48); SignAlignment = Mathf.Clamp(SignAlignment, 0, 2); if (SignStylePresets == null) { SignStylePresets = new List(); } SignStylePresets = SignStylePresets.Where((SignStylePresetData preset) => preset != null).Take(3).ToList(); foreach (SignStylePresetData signStylePreset in SignStylePresets) { signStylePreset.Normalize(); } SyncLegacyOrientationLevel(); if (ProcessedRewardIds == null) { ProcessedRewardIds = new List(); } ProcessedRewardIds = ProcessedRewardIds.Where((string item) => !string.IsNullOrWhiteSpace(item)).Distinct(StringComparer.Ordinal).TakeLastCompat(128) .ToList(); if (PersonalOrientationMarkers == null) { PersonalOrientationMarkers = new List(); } List list2 = new List(); HashSet hashSet = new HashSet(StringComparer.Ordinal); int num = 1; foreach (PersonalOrientationMarkerData personalOrientationMarker in PersonalOrientationMarkers) { if (personalOrientationMarker == null) { continue; } personalOrientationMarker.Normalize(num++); if (!string.IsNullOrWhiteSpace(personalOrientationMarker.WorldToken) && hashSet.Add(personalOrientationMarker.Id)) { list2.Add(personalOrientationMarker); if (list2.Count >= 64) { break; } } } PersonalOrientationMarkers = list2; } internal bool HasUnlockedSkill(string skillId) { if (!string.IsNullOrWhiteSpace(skillId) && UnlockedSkillIds != null) { return UnlockedSkillIds.Contains(skillId); } return false; } internal bool IsSkillEnabled(string skillId) { if (HasUnlockedSkill(skillId)) { if (DisabledSkillIds != null) { return !DisabledSkillIds.Contains(skillId); } return true; } return false; } internal bool IsSkillOperational(string skillId) { return IsSkillOperational(skillId, new HashSet(StringComparer.Ordinal)); } private bool IsSkillOperational(string skillId, HashSet visiting) { if (!IsSkillEnabled(skillId) || !visiting.Add(skillId)) { return false; } SkillNodeDefinition skillNodeDefinition = SkillTreeDefinitions.Get(skillId); if (skillNodeDefinition == null || !skillNodeDefinition.Enabled) { return false; } string[] prerequisites = skillNodeDefinition.Prerequisites; foreach (string skillId2 in prerequisites) { if (!IsSkillOperational(skillId2, visiting)) { visiting.Remove(skillId); return false; } } visiting.Remove(skillId); return true; } internal bool HasSkill(string skillId) { if (ChallengeHubServerGateFeature.GameplayAllowed) { return IsSkillOperational(skillId); } return false; } internal bool ToggleSkillEnabled(string skillId, out bool enabled) { enabled = false; if (!HasUnlockedSkill(skillId)) { return false; } if (DisabledSkillIds.Contains(skillId)) { DisabledSkillIds.Remove(skillId); enabled = true; } else { DisabledSkillIds.Add(skillId); } return true; } internal void AddSkillForMigration(string skillId) { if (SkillTreeDefinitions.Get(skillId) != null && !HasUnlockedSkill(skillId)) { UnlockedSkillIds.Add(skillId); } } internal void SyncLegacyOrientationLevel() { int levelOrientation = 0; if (HasUnlockedSkill("orientation.compass")) { levelOrientation = 1; } if (HasUnlockedSkill("orientation.personal_buoys")) { levelOrientation = 2; } if (HasUnlockedSkill("orientation.private_signs")) { levelOrientation = 3; } if (HasUnlockedSkill("orientation.shared_signs")) { levelOrientation = 4; } LevelOrientation = levelOrientation; } private static string NormalizeHexColor(string value) { string text = (value ?? string.Empty).Trim().ToUpperInvariant(); if (!text.StartsWith("#", StringComparison.Ordinal)) { text = "#" + text; } if (text.Length != 7 || text.Skip(1).Any((char ch) => !Uri.IsHexDigit(ch))) { return "#FFFFFF"; } return text; } private static List NormalizeStringKeys(IEnumerable source, int max) { return (from value in source ?? Array.Empty() select (value ?? string.Empty).Trim() into value where value.Length > 0 select value).Distinct(StringComparer.Ordinal).TakeLastCompat(max).ToList(); } } [Serializable] internal sealed class SignStylePresetData { public string Name = "Vorlage"; public string Color = "#FFFFFF"; public int FontSize = 24; public bool Bold; public bool Italic; public int Alignment; internal void Normalize() { Name = OrientationText.SanitizeLabel(Name, "Vorlage"); string text = (Color ?? string.Empty).Trim().ToUpperInvariant(); if (!text.StartsWith("#", StringComparison.Ordinal)) { text = "#" + text; } Color = ((text.Length == 7 && text.Skip(1).All(Uri.IsHexDigit)) ? text : "#FFFFFF"); FontSize = Mathf.Clamp(FontSize, 12, 48); Alignment = Mathf.Clamp(Alignment, 0, 2); } } [Serializable] internal sealed class PortalHistoryEntry { public string WorldToken = string.Empty; public string Name = string.Empty; public float X; public float Y; public float Z; public long SeenUtcTicks; internal Vector3 Position => new Vector3(X, Y, Z); internal void Normalize() { WorldToken = (WorldToken ?? string.Empty).Trim(); Name = OrientationText.SanitizeLabel(Name, "Portal"); if (!OrientationText.IsFinite(X)) { X = 0f; } if (!OrientationText.IsFinite(Y)) { Y = 0f; } if (!OrientationText.IsFinite(Z)) { Z = 0f; } if (SeenUtcTicks >= 0) { long seenUtcTicks = SeenUtcTicks; DateTime maxValue = DateTime.MaxValue; if (seenUtcTicks <= maxValue.Ticks) { return; } } SeenUtcTicks = 0L; } } internal static class OrientationText { internal static bool IsFinite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } internal static string SanitizeLabel(string value, string fallback) { string text = (value ?? string.Empty).Trim(); if (text.Length == 0) { text = fallback ?? "Markierung"; } text = text.Replace("\r", " ").Replace("\n", " ").Replace("\t", " "); while (text.Contains(" ")) { text = text.Replace(" ", " "); } if (text.Length > 48) { return text.Substring(0, 48); } return text; } } internal static class EnumerableCompatibility { internal static IEnumerable TakeLastCompat(this IEnumerable source, int count) { if (source == null || count <= 0) { return Array.Empty(); } Queue queue = new Queue(count); foreach (T item in source) { if (queue.Count == count) { queue.Dequeue(); } queue.Enqueue(item); } return queue.ToArray(); } } internal static class TalentStore { internal const string CustomDataKey = "ChallengeHub_Talents"; private static Player _cachedPlayer; private static long _cachedPlayerId; private static TalentData _cachedData; internal static TalentData GetLocalData() { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer != (Object)null)) { return null; } return Load(localPlayer, forceReload: false); } internal static TalentData Load(Player player, bool forceReload) { if ((Object)(object)player == (Object)null) { return null; } long num = SafeGetPlayerId(player); if (!forceReload && _cachedData != null && (Object)(object)_cachedPlayer == (Object)(object)player && _cachedPlayerId == num) { return _cachedData; } TalentData talentData = null; string text = ReadCustomData(player, "ChallengeHub_Talents"); if (!string.IsNullOrWhiteSpace(text)) { try { talentData = JsonUtility.FromJson(text); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Talentdaten konnten nicht gelesen werden; sichere Standarddaten werden verwendet: " + ex.Message)); } } } if (talentData == null) { talentData = new TalentData(); } talentData.Normalize(); _cachedPlayer = player; _cachedPlayerId = num; _cachedData = talentData; return talentData; } internal static void Forget(Player player) { if (!((Object)(object)player != (Object)null) || !((Object)(object)_cachedPlayer != (Object)(object)player)) { _cachedPlayer = null; _cachedPlayerId = 0L; _cachedData = null; } } internal static bool GrantTalentPoint(string rewardId, int amount) { if (!ChallengeHubServerGateFeature.GameplayAllowed) { return false; } if (amount <= 0) { return false; } Player localPlayer = Player.m_localPlayer; TalentData talentData = Load(localPlayer, forceReload: false); if ((Object)(object)localPlayer == (Object)null || talentData == null) { return false; } string text = (rewardId ?? string.Empty).Trim(); if (text.Length > 0 && talentData.ProcessedRewardIds.Contains(text)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Talentpunkt-RPC bereits verarbeitet; Duplikat ignoriert: " + text)); } return false; } int availablePoints = talentData.AvailablePoints; List processedRewardIds = new List(talentData.ProcessedRewardIds); checked { try { talentData.AvailablePoints += amount; if (text.Length > 0) { talentData.ProcessedRewardIds.Add(text); talentData.ProcessedRewardIds = talentData.ProcessedRewardIds.TakeLastCompat(128).ToList(); } if (Save(localPlayer, talentData, flushProfile: true)) { return true; } } catch (OverflowException ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Talentpunktestand würde den gültigen Zahlenbereich überschreiten: " + ex.Message)); } } talentData.AvailablePoints = availablePoints; talentData.ProcessedRewardIds = processedRewardIds; return false; } } internal static bool TryUnlockSkill(string skillId) { if (!ChallengeHubServerGateFeature.GameplayAllowed) { return false; } Player localPlayer = Player.m_localPlayer; TalentData talentData = Load(localPlayer, forceReload: false); SkillNodeDefinition skillNodeDefinition = SkillTreeDefinitions.Get(skillId); if ((Object)(object)localPlayer == (Object)null || talentData == null || skillNodeDefinition == null || !skillNodeDefinition.Enabled || talentData.HasUnlockedSkill(skillId)) { return false; } if (talentData.AvailablePoints < skillNodeDefinition.Cost || !SkillTreeDefinitions.PrerequisitesMet(talentData, skillNodeDefinition)) { return false; } int availablePoints = talentData.AvailablePoints; List unlockedSkillIds = new List(talentData.UnlockedSkillIds); talentData.AvailablePoints -= skillNodeDefinition.Cost; talentData.UnlockedSkillIds.Add(skillNodeDefinition.Id); talentData.Normalize(); if (Save(localPlayer, talentData, flushProfile: true)) { OrientationMarkerFeature.OnTalentDataChanged(); QoLSkillRuntimeFeature.OnTalentDataChanged(); AdvancedQoLFeature.OnTalentDataChanged(); return true; } talentData.AvailablePoints = availablePoints; talentData.UnlockedSkillIds = unlockedSkillIds; talentData.Normalize(); return false; } internal static bool TryUpgradeTalent(string talentName) { if (!ChallengeHubServerGateFeature.GameplayAllowed) { return false; } Player localPlayer = Player.m_localPlayer; TalentData talentData = Load(localPlayer, forceReload: false); if ((Object)(object)localPlayer == (Object)null || talentData == null || talentData.AvailablePoints <= 0) { return false; } string text = (talentName ?? string.Empty).Trim().ToLowerInvariant(); switch (text) { case "orientation": case "orientierung": case "pioneer": case "pionier": { string text2 = ((!talentData.HasUnlockedSkill("core.pioneer")) ? "core.pioneer" : ((!talentData.HasUnlockedSkill("orientation.compass")) ? "orientation.compass" : ((!talentData.HasUnlockedSkill("orientation.personal_buoys")) ? "orientation.personal_buoys" : ((!talentData.HasUnlockedSkill("orientation.private_signs")) ? "orientation.private_signs" : ((!talentData.HasUnlockedSkill("orientation.shared_signs")) ? "orientation.shared_signs" : string.Empty))))); if (text2.Length > 0) { return TryUnlockSkill(text2); } return false; } default: { int availablePoints = talentData.AvailablePoints; int levelWeight = talentData.LevelWeight; int levelStamina = talentData.LevelStamina; switch (text) { case "weight": case "carry": case "gewicht": if (talentData.LevelWeight >= 10) { return false; } talentData.LevelWeight++; break; case "stamina": case "ausdauer": if (talentData.LevelStamina >= 10) { return false; } talentData.LevelStamina++; break; default: return TryUnlockSkill(talentName); } talentData.AvailablePoints--; if (Save(localPlayer, talentData, flushProfile: true)) { return true; } talentData.AvailablePoints = availablePoints; talentData.LevelWeight = levelWeight; talentData.LevelStamina = levelStamina; return false; } } } internal static bool HasSkill(string skillId) { return GetLocalData()?.HasSkill(skillId) ?? false; } internal static bool HasUnlockedSkill(string skillId) { return GetLocalData()?.HasUnlockedSkill(skillId) ?? false; } internal static bool ToggleSkillEnabled(string skillId, out bool enabled) { if (!ChallengeHubServerGateFeature.GameplayAllowed) { enabled = false; return false; } enabled = false; Player localPlayer = Player.m_localPlayer; TalentData talentData = Load(localPlayer, forceReload: false); if ((Object)(object)localPlayer == (Object)null || talentData == null) { return false; } List disabledSkillIds = new List(talentData.DisabledSkillIds ?? new List()); if (!talentData.ToggleSkillEnabled(skillId, out enabled)) { return false; } talentData.Normalize(); if (!Save(localPlayer, talentData, flushProfile: true)) { talentData.DisabledSkillIds = disabledSkillIds; talentData.Normalize(); enabled = talentData.IsSkillEnabled(skillId); return false; } OrientationMarkerFeature.OnTalentDataChanged(); QoLSkillRuntimeFeature.OnTalentDataChanged(); AdvancedQoLFeature.OnTalentDataChanged(); return true; } internal static bool SaveLocalSettings() { if (!ChallengeHubServerGateFeature.GameplayAllowed) { return false; } Player localPlayer = Player.m_localPlayer; TalentData talentData = Load(localPlayer, forceReload: false); if ((Object)(object)localPlayer != (Object)null && talentData != null) { return Save(localPlayer, talentData, flushProfile: true); } return false; } internal static bool ToggleFavoriteItem(string key, out bool enabled) { enabled = false; Player localPlayer = Player.m_localPlayer; TalentData talentData = Load(localPlayer, forceReload: false); key = (key ?? string.Empty).Trim(); if ((Object)(object)localPlayer == (Object)null || talentData == null || key.Length == 0 || !talentData.HasSkill("storage.favorites")) { return false; } if (talentData.FavoriteItemKeys.Contains(key)) { talentData.FavoriteItemKeys.Remove(key); } else { talentData.FavoriteItemKeys.Add(key); enabled = true; } return Save(localPlayer, talentData, flushProfile: true); } internal static bool TogglePickupFilter(string key, out bool blocked) { blocked = false; Player localPlayer = Player.m_localPlayer; TalentData talentData = Load(localPlayer, forceReload: false); key = (key ?? string.Empty).Trim(); if ((Object)(object)localPlayer == (Object)null || talentData == null || key.Length == 0 || !talentData.HasSkill("storage.pickup_filter")) { return false; } if (talentData.PickupFilterItemKeys.Contains(key)) { talentData.PickupFilterItemKeys.Remove(key); } else { talentData.PickupFilterItemKeys.Add(key); blocked = true; } return Save(localPlayer, talentData, flushProfile: true); } internal static bool ToggleProtectedSlot(int slotIndex, out bool protectedNow) { protectedNow = false; Player localPlayer = Player.m_localPlayer; TalentData talentData = Load(localPlayer, forceReload: false); if ((Object)(object)localPlayer == (Object)null || talentData == null || slotIndex < 0 || !talentData.HasSkill("storage.protected_slots")) { return false; } if (talentData.ProtectedInventorySlots.Contains(slotIndex)) { talentData.ProtectedInventorySlots.Remove(slotIndex); } else { talentData.ProtectedInventorySlots.Add(slotIndex); protectedNow = true; } return Save(localPlayer, talentData, flushProfile: true); } internal static void RememberPortal(string name, Vector3 position) { //IL_0086: 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_009e: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; TalentData talentData = Load(localPlayer, forceReload: false); if (!((Object)(object)localPlayer == (Object)null) && talentData != null && talentData.HasSkill("travel.portal_history")) { string world = CartographyMapModeFeature.CurrentWorldStorageToken(); string safeName = OrientationText.SanitizeLabel(name, "Portal"); talentData.PortalHistory.RemoveAll((PortalHistoryEntry entry) => entry != null && entry.WorldToken == world && string.Equals(entry.Name, safeName, StringComparison.OrdinalIgnoreCase)); talentData.PortalHistory.Add(new PortalHistoryEntry { WorldToken = world, Name = safeName, X = position.x, Y = position.y, Z = position.z, SeenUtcTicks = DateTime.UtcNow.Ticks }); talentData.PortalHistory = talentData.PortalHistory.TakeLastCompat(24).ToList(); Save(localPlayer, talentData, flushProfile: true); } } internal static IReadOnlyList GetPersonalMarkersForCurrentWorld() { TalentData localData = GetLocalData(); if (localData == null) { return Array.Empty(); } string worldToken = CartographyMapModeFeature.CurrentWorldStorageToken(); return localData.PersonalOrientationMarkers.Where((PersonalOrientationMarkerData marker) => marker != null && string.Equals(marker.WorldToken, worldToken, StringComparison.Ordinal)).ToArray(); } internal static bool AddPersonalOrientationMarker(Vector3 position, string name, int kind, out PersonalOrientationMarkerData created) { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) created = null; Player localPlayer = Player.m_localPlayer; TalentData talentData = Load(localPlayer, forceReload: false); if ((Object)(object)localPlayer == (Object)null || talentData == null || talentData.LevelOrientation < 2) { return false; } string worldToken = CartographyMapModeFeature.CurrentWorldStorageToken(); int num = talentData.PersonalOrientationMarkers.Count((PersonalOrientationMarkerData marker) => marker != null && string.Equals(marker.WorldToken, worldToken, StringComparison.Ordinal)); if (num >= 32 || talentData.PersonalOrientationMarkers.Count >= 64) { return false; } PersonalOrientationMarkerData personalOrientationMarkerData = new PersonalOrientationMarkerData { Id = Guid.NewGuid().ToString("N"), WorldToken = worldToken, Name = OrientationText.SanitizeLabel(name, "Boje " + (num + 1)), Kind = Mathf.Clamp(kind, 1, 2), X = position.x, Y = position.y, Z = position.z }; personalOrientationMarkerData.Normalize(num + 1); talentData.PersonalOrientationMarkers.Add(personalOrientationMarkerData); if (!Save(localPlayer, talentData, flushProfile: true)) { talentData.PersonalOrientationMarkers.Remove(personalOrientationMarkerData); return false; } created = personalOrientationMarkerData; return true; } internal static bool RemoveNearestPersonalOrientationMarker(Vector3 position, float maxDistance, out string removedName) { //IL_0064: 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) removedName = string.Empty; Player localPlayer = Player.m_localPlayer; TalentData talentData = Load(localPlayer, forceReload: false); if ((Object)(object)localPlayer == (Object)null || talentData == null) { return false; } string b = CartographyMapModeFeature.CurrentWorldStorageToken(); PersonalOrientationMarkerData personalOrientationMarkerData = null; float num = Mathf.Max(0.1f, maxDistance); foreach (PersonalOrientationMarkerData personalOrientationMarker in talentData.PersonalOrientationMarkers) { if (personalOrientationMarker != null && string.Equals(personalOrientationMarker.WorldToken, b, StringComparison.Ordinal)) { float num2 = Vector3.Distance(position, personalOrientationMarker.Position); if (num2 < num) { num = num2; personalOrientationMarkerData = personalOrientationMarker; } } } if (personalOrientationMarkerData == null) { return false; } int index = talentData.PersonalOrientationMarkers.IndexOf(personalOrientationMarkerData); removedName = personalOrientationMarkerData.Name; talentData.PersonalOrientationMarkers.RemoveAt(index); if (Save(localPlayer, talentData, flushProfile: true)) { return true; } talentData.PersonalOrientationMarkers.Insert(index, personalOrientationMarkerData); removedName = string.Empty; return false; } internal static bool Save(Player player, TalentData data, bool flushProfile) { if ((Object)(object)player == (Object)null || data == null) { return false; } try { data.Normalize(); string value = JsonUtility.ToJson((object)data); if (!WriteCustomData(player, "ChallengeHub_Talents", value)) { return false; } _cachedPlayer = player; _cachedPlayerId = SafeGetPlayerId(player); _cachedData = data; if (flushProfile) { TryFlushPlayerProfile(); } return true; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("Talentdaten konnten nicht gespeichert werden: " + ex)); } return false; } } internal static long SafeGetPlayerId(Player player) { try { return ((Object)(object)player != (Object)null) ? player.GetPlayerID() : 0; } catch { return 0L; } } private static string ReadCustomData(Player player, string key) { if ((Object)(object)player == (Object)null || string.IsNullOrWhiteSpace(key)) { return string.Empty; } try { MethodInfo method = ((object)player).GetType().GetMethod("GetCustomData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null); if (method != null) { return Convert.ToString(method.Invoke(player, new object[1] { key })) ?? string.Empty; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Player.GetCustomData fehlgeschlagen: " + ex.Message)); } } try { if (((object)player).GetType().GetField("m_customData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(player) is IDictionary dictionary && dictionary.TryGetValue(key, out var value)) { return value ?? string.Empty; } } catch (Exception ex2) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogDebug((object)("Player.m_customData konnte nicht gelesen werden: " + ex2.Message)); } } return string.Empty; } private static bool WriteCustomData(Player player, string key, string value) { if ((Object)(object)player == (Object)null || string.IsNullOrWhiteSpace(key)) { return false; } try { MethodInfo method = ((object)player).GetType().GetMethod("SetCustomData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2] { typeof(string), typeof(string) }, null); if (method != null) { method.Invoke(player, new object[2] { key, value ?? string.Empty }); return true; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Player.SetCustomData fehlgeschlagen: " + ex.Message)); } } try { if (((object)player).GetType().GetField("m_customData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(player) is IDictionary dictionary) { dictionary[key] = value ?? string.Empty; return true; } } catch (Exception ex2) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogDebug((object)("Player.m_customData konnte nicht geschrieben werden: " + ex2.Message)); } } ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)"Keine kompatible Player-CustomData-API gefunden."); } return false; } private static void TryFlushPlayerProfile() { try { Game instance = Game.instance; if (!((Object)(object)instance == (Object)null) && instance.GetPlayerProfile() != null) { AccessTools.Method(((object)instance).GetType(), "SavePlayerProfile", new Type[1] { typeof(bool) }, (Type[])null)?.Invoke(instance, new object[1] { false }); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Sofortiger Talent-Profil-Save fehlgeschlagen: " + ex.Message)); } } } } [HarmonyPatch(typeof(Player), "OnSpawned")] internal static class TalentPlayerSpawnedPatch { [HarmonyPostfix] private static void Postfix(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { TalentStore.Load(__instance, forceReload: true); OrientationMarkerFeature.OnLocalPlayerSpawned(__instance); QoLSkillRuntimeFeature.OnTalentDataChanged(); } } } [HarmonyPatch(typeof(Player), "OnDestroy")] internal static class TalentPlayerDestroyedPatch { [HarmonyPrefix] private static void Prefix(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { TalentMenuBehaviour.Close(); OrientationMarkerFeature.OnLocalPlayerDestroyed(__instance); TalentStore.Forget(__instance); } } } [HarmonyPatch(typeof(Player), "GetMaxCarryWeight")] internal static class TalentCarryWeightPatch { [HarmonyPostfix] private static void Postfix(Player __instance, ref float __result) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { TalentData localData = TalentStore.GetLocalData(); if (localData != null && localData.LevelWeight > 0) { __result += (float)localData.LevelWeight * 50f; } } } } [HarmonyPatch] internal static class TalentStaminaRegenPatch { private static MethodBase TargetMethod() { return typeof(SEMan).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo method) { if (!string.Equals(method.Name, "ModifyStaminaRegen", StringComparison.Ordinal)) { return false; } ParameterInfo[] parameters = method.GetParameters(); return parameters.Length == 1 && parameters[0].ParameterType == typeof(float).MakeByRefType(); }); } [HarmonyPostfix] private static void Postfix(SEMan __instance, ref float __0) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } try { if (__instance != ((Character)localPlayer).GetSEMan()) { return; } } catch { return; } TalentData localData = TalentStore.GetLocalData(); if (localData != null && localData.LevelStamina > 0) { __0 *= 1f + (float)localData.LevelStamina * 0.1f; } } } internal static class TalentInventoryPlaceholder { internal static void ApplyInventoryTalent(Player player, int inventoryLevel) { } } internal sealed class TalentMenuBehaviour : MonoBehaviour { private static TalentMenuBehaviour _instance; private static readonly int WindowId = "ChallengeHub_QoL_SkillTree_v4".GetHashCode(); private static Texture2D _lineTexture; private static Font _norseFont; private static Font _averiaFont; private static Texture2D _bgTexture; private static Texture2D _buttonTexture; private static Texture2D _buttonHoverTexture; private static GUIStyle _windowStyle; private static GUIStyle _buttonStyle; private static GUIStyle _labelStyle; private static GUIStyle _titleStyle; private bool _visible; private Rect _windowRect = new Rect(35f, 35f, 1120f, 720f); private SkillBranch _selectedBranch = SkillBranch.Orientation; private Vector2 _branchScroll; private string _lastTooltip = string.Empty; internal static bool IsVisible { get { if ((Object)(object)_instance != (Object)null) { return _instance._visible; } return false; } } internal static void Attach(GameObject host) { if (!((Object)(object)_instance != (Object)null) && !((Object)(object)host == (Object)null)) { _instance = host.GetComponent() ?? host.AddComponent(); } } internal static void Close() { if ((Object)(object)_instance != (Object)null) { _instance.SetVisible(value: false); } } private void InitStyles() { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Expected O, but got Unknown //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Expected O, but got Unknown //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Expected O, but got Unknown //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Expected O, but got Unknown //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Expected O, but got Unknown //IL_02cd: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_bgTexture != (Object)null)) { _norseFont = ((IEnumerable)Resources.FindObjectsOfTypeAll()).FirstOrDefault((Func)((Font x) => ((Object)x).name == "Norsebold")) ?? GUI.skin.font; _averiaFont = ((IEnumerable)Resources.FindObjectsOfTypeAll()).FirstOrDefault((Func)((Font x) => ((Object)x).name == "AveriaSansLibre-Bold" || ((Object)x).name == "AveriaSerifLibre-Bold")) ?? GUI.skin.font; _bgTexture = new Texture2D(1, 1); _bgTexture.SetPixel(0, 0, new Color(0.08f, 0.08f, 0.08f, 0.98f)); _bgTexture.Apply(); _buttonTexture = new Texture2D(1, 1); _buttonTexture.SetPixel(0, 0, new Color(0.42f, 0.42f, 0.42f, 1f)); _buttonTexture.Apply(); _buttonHoverTexture = new Texture2D(1, 1); _buttonHoverTexture.SetPixel(0, 0, new Color(0.58f, 0.58f, 0.58f, 1f)); _buttonHoverTexture.Apply(); _windowStyle = new GUIStyle(GUI.skin.window) { font = _norseFont, fontSize = 24 }; _windowStyle.normal.background = _bgTexture; _windowStyle.normal.textColor = new Color(1f, 0.65f, 0f, 1f); _buttonStyle = new GUIStyle(GUI.skin.button) { font = _averiaFont, fontSize = 14 }; _buttonStyle.normal.background = _buttonTexture; _buttonStyle.hover.background = _buttonHoverTexture; _buttonStyle.active.background = _buttonHoverTexture; _buttonStyle.normal.textColor = Color.white; _buttonStyle.hover.textColor = Color.white; _buttonStyle.active.textColor = Color.white; _labelStyle = new GUIStyle(GUI.skin.label) { font = _averiaFont, fontSize = 15 }; _labelStyle.normal.textColor = new Color(0.9f, 0.9f, 0.9f, 1f); _titleStyle = new GUIStyle(GUI.skin.label) { font = _norseFont, fontSize = 22 }; _titleStyle.normal.textColor = new Color(1f, 0.65f, 0f, 1f); } } private void Update() { if (Input.GetKeyDown((KeyCode)107) && !Input.GetKey((KeyCode)306) && !Input.GetKey((KeyCode)305) && (Object)(object)Player.m_localPlayer != (Object)null) { SetVisible(!_visible); } if (_visible && (Input.GetKeyDown((KeyCode)27) || (Object)(object)Player.m_localPlayer == (Object)null)) { SetVisible(value: false); } } private void OnGUI() { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_009b: 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) if (!_visible) { return; } InitStyles(); GUI.skin.window = _windowStyle; GUI.skin.button = _buttonStyle; GUI.skin.label = _labelStyle; try { ((Rect)(ref _windowRect)).width = Mathf.Min(1120f, (float)Screen.width - 30f); ((Rect)(ref _windowRect)).height = Mathf.Min(720f, (float)Screen.height - 30f); _windowRect = GUI.Window(WindowId, _windowRect, new WindowFunction(DrawWindow), "ChallengeHub QoL-Skilltree"); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Skilltree konnte nicht gezeichnet werden: " + ex.Message)); } SetVisible(value: false); } } private void DrawWindow(int windowId) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) TalentData localData = TalentStore.GetLocalData(); if (localData == null) { GUILayout.Label("Kein lokaler Spieler geladen.", Array.Empty()); if (GUILayout.Button("Schließen", Array.Empty())) { SetVisible(value: false); } return; } GUI.Label(new Rect(18f, 32f, 300f, 25f), "Verfügbare Talentpunkte: " + localData.AvailablePoints, _titleStyle); GUI.Label(new Rect(330f, 32f, 450f, 25f), "Kauf kostet 1 Punkt. Gekaufte QoL-Knoten können ohne Rückerstattung ein-/ausgeschaltet werden."); if (GUI.Button(new Rect(((Rect)(ref _windowRect)).width - 48f, 25f, 30f, 26f), "X")) { SetVisible(value: false); return; } float num = ((Rect)(ref _windowRect)).height - 112f; Rect val = default(Rect); ((Rect)(ref val))..ctor(18f, 62f, 405f, num); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(435f, 62f, ((Rect)(ref _windowRect)).width - 453f, num); GUI.DrawTexture(val, (Texture)(object)_bgTexture); GUI.DrawTexture(val2, (Texture)(object)_bgTexture); DrawBranchOverview(val, localData); DrawSelectedBranch(val2, localData); string text = (string.IsNullOrWhiteSpace(GUI.tooltip) ? _lastTooltip : GUI.tooltip); if (!string.IsNullOrWhiteSpace(text)) { _lastTooltip = text; GUI.DrawTexture(new Rect(18f, ((Rect)(ref _windowRect)).height - 42f, ((Rect)(ref _windowRect)).width - 36f, 30f), (Texture)(object)_bgTexture); GUI.Label(new Rect(25f, ((Rect)(ref _windowRect)).height - 38f, ((Rect)(ref _windowRect)).width - 50f, 30f), text); } GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width, 24f)); } private void DrawBranchOverview(Rect box, TalentData data) { //IL_0031: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_03f0: Unknown result type (might be due to invalid IL or missing references) //IL_02d1: Unknown result type (might be due to invalid IL or missing references) //IL_0316: Unknown result type (might be due to invalid IL or missing references) //IL_02f6: Unknown result type (might be due to invalid IL or missing references) //IL_0320: Unknown result type (might be due to invalid IL or missing references) //IL_0368: Unknown result type (might be due to invalid IL or missing references) //IL_036f: Expected O, but got Unknown //IL_0371: Unknown result type (might be due to invalid IL or missing references) //IL_0392: Unknown result type (might be due to invalid IL or missing references) //IL_0399: Unknown result type (might be due to invalid IL or missing references) //IL_0388: Unknown result type (might be due to invalid IL or missing references) //IL_038d: Unknown result type (might be due to invalid IL or missing references) GUI.Label(new Rect(((Rect)(ref box)).x + 14f, ((Rect)(ref box)).y + 10f, 260f, 28f), "Sechs QoL-Zweige", _titleStyle); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(((Rect)(ref box)).x + ((Rect)(ref box)).width * 0.5f, ((Rect)(ref box)).y + ((Rect)(ref box)).height * 0.5f); Rect rect = default(Rect); ((Rect)(ref rect))..ctor(val.x - 62f, val.y - 34f, 124f, 68f); Dictionary dictionary = new Dictionary { [SkillBranch.Building] = new Rect(val.x - 72f, ((Rect)(ref box)).y + 54f, 144f, 58f), [SkillBranch.Comfort] = new Rect(((Rect)(ref box)).x + 18f, val.y - 122f, 150f, 64f), [SkillBranch.Orientation] = new Rect(((Rect)(ref box)).x + ((Rect)(ref box)).width - 168f, val.y - 122f, 150f, 64f), [SkillBranch.Storage] = new Rect(((Rect)(ref box)).x + 18f, val.y + 62f, 150f, 64f), [SkillBranch.Travel] = new Rect(((Rect)(ref box)).x + ((Rect)(ref box)).width - 168f, val.y + 62f, 150f, 64f), [SkillBranch.Recovery] = new Rect(val.x - 82f, ((Rect)(ref box)).y + ((Rect)(ref box)).height - 104f, 164f, 64f) }; foreach (KeyValuePair item in dictionary) { Vector2 start = val; Rect value = item.Value; DrawLine(start, ((Rect)(ref value)).center, BranchHasAnySkill(data, item.Key)); } SkillNodeDefinition node = SkillTreeDefinitions.Get("core.pioneer"); DrawSkillButton(rect, node, data, root: true, showHotkey: false); foreach (KeyValuePair item2 in dictionary) { SkillBranch key = item2.Key; int num = SkillTreeDefinitions.ForBranch(key).Count((SkillNodeDefinition skillNodeDefinition) => data.HasUnlockedSkill(skillNodeDefinition.Id)); int count = SkillTreeDefinitions.ForBranch(key).Count; bool num2 = key == _selectedBranch; Color backgroundColor = GUI.backgroundColor; Color contentColor = GUI.contentColor; if (num2) { GUI.backgroundColor = new Color(1f, 0.72f, 0.18f, 1f); } else if (num > 0) { GUI.backgroundColor = new Color(0.85f, 0.5f, 0.08f, 1f); } else { GUI.backgroundColor = new Color(0.55f, 0.55f, 0.58f, 1f); } GUI.contentColor = Color.white; GUIContent val2 = new GUIContent(SkillTreeDefinitions.BranchTitle(key) + "\n" + num + "/" + count, "Zweig öffnen."); if (GUI.Button(item2.Value, val2)) { _selectedBranch = key; _branchScroll = Vector2.zero; } GUI.backgroundColor = backgroundColor; GUI.contentColor = contentColor; } GUI.Label(new Rect(((Rect)(ref box)).x + 12f, ((Rect)(ref box)).y + ((Rect)(ref box)).height - 28f, ((Rect)(ref box)).width - 24f, 22f), "Alt-Hotkeys funktionieren nur bei freigeschaltetem Knoten."); } private void DrawSelectedBranch(Rect box, TalentData data) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) GUI.Label(new Rect(((Rect)(ref box)).x + 14f, ((Rect)(ref box)).y + 10f, ((Rect)(ref box)).width - 28f, 28f), SkillTreeDefinitions.BranchTitle(_selectedBranch), _titleStyle); IReadOnlyList readOnlyList = SkillTreeDefinitions.ForBranch(_selectedBranch); float num = Math.Max(((Rect)(ref box)).height - 50f, 90f + (float)readOnlyList.Count * 94f); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref box)).x + 10f, ((Rect)(ref box)).y + 40f, ((Rect)(ref box)).width - 20f, ((Rect)(ref box)).height - 52f); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(0f, 0f, ((Rect)(ref val)).width - 18f, num); _branchScroll = GUI.BeginScrollView(val, _branchScroll, val2); Dictionary dictionary = BuildNodePositions(readOnlyList, ((Rect)(ref val2)).width); foreach (SkillNodeDefinition item in readOnlyList) { string[] prerequisites = item.Prerequisites; foreach (string text in prerequisites) { Rect value3; if (dictionary.TryGetValue(text, out var value) && dictionary.TryGetValue(item.Id, out var value2)) { DrawLine(((Rect)(ref value)).center, ((Rect)(ref value2)).center, data.IsSkillOperational(item.Id)); } else if (text == "core.pioneer" && dictionary.TryGetValue(item.Id, out value3)) { DrawLine(new Vector2(((Rect)(ref val2)).width * 0.5f, 18f), ((Rect)(ref value3)).center, data.IsSkillOperational(item.Id)); } } } foreach (SkillNodeDefinition item2 in readOnlyList) { if (dictionary.TryGetValue(item2.Id, out var value4)) { DrawSkillButton(value4, item2, data, root: false, showHotkey: true); } } GUI.EndScrollView(); int num2 = readOnlyList.Count((SkillNodeDefinition n) => data.HasUnlockedSkill(n.Id)); GUI.Label(new Rect(((Rect)(ref box)).x + ((Rect)(ref box)).width - 180f, ((Rect)(ref box)).y + 15f, 165f, 25f), num2 + "/" + readOnlyList.Count + " freigeschaltet"); } private static Dictionary BuildNodePositions(IReadOnlyList nodes, float width) { //IL_019e: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = new Dictionary(StringComparer.Ordinal); Dictionary depths = new Dictionary(StringComparer.Ordinal); foreach (SkillNodeDefinition node in nodes) { depths[node.Id] = CalculateDepth(node, nodes, depths); } foreach (KeyValuePair> item in from p in (from n in nodes group n by depths[n.Id]).ToDictionary((IGrouping g) => g.Key, (IGrouping g) => g.ToList()) orderby p.Key select p) { int count = item.Value.Count; float num = Mathf.Clamp((width - 30f) / (float)Math.Max(1, count) - 12f, 150f, 220f); float num2 = (float)count * num + (float)Math.Max(0, count - 1) * 16f; float num3 = Mathf.Max(8f, (width - num2) * 0.5f); float num4 = 48f + (float)item.Key * 94f; for (int num5 = 0; num5 < count; num5++) { dictionary[item.Value[num5].Id] = new Rect(num3 + (float)num5 * (num + 16f), num4, num, 70f); } } return dictionary; } private static int CalculateDepth(SkillNodeDefinition node, IReadOnlyList branchNodes, Dictionary cache) { if (cache.TryGetValue(node.Id, out var value)) { return value; } int num = 0; string[] prerequisites = node.Prerequisites; foreach (string prereq in prerequisites) { SkillNodeDefinition skillNodeDefinition = branchNodes.FirstOrDefault((SkillNodeDefinition c) => c.Id == prereq); if (skillNodeDefinition != null) { num = Math.Max(num, CalculateDepth(skillNodeDefinition, branchNodes, cache) + 1); } } cache[node.Id] = num; return num; } private static void DrawSkillButton(Rect rect, SkillNodeDefinition node, TalentData data, bool root, bool showHotkey) { //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Expected O, but got Unknown //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) if (node == null || data == null) { return; } bool flag = data.HasUnlockedSkill(node.Id); bool flag2 = data.IsSkillEnabled(node.Id); bool flag3 = data.IsSkillOperational(node.Id); bool flag4 = SkillTreeDefinitions.PrerequisitesMet(data, node); bool flag5 = node.Enabled && !flag && flag4 && data.AvailablePoints >= node.Cost; string text = ((!flag) ? (flag4 ? (node.Cost + " Punkt") : "GESPERRT") : ((!flag2) ? "GEKAUFT · INAKTIV" : (flag3 ? "GEKAUFT · AKTIV" : "GEKAUFT · VORAUSSETZUNG INAKTIV"))); string text2 = ((showHotkey && !string.IsNullOrWhiteSpace(node.Hotkey)) ? ("\n" + node.Hotkey) : string.Empty); Color backgroundColor = GUI.backgroundColor; Color contentColor = GUI.contentColor; if (flag3) { GUI.backgroundColor = new Color(0.9f, 0.52f, 0.06f, 1f); } else if (flag && !flag2) { GUI.backgroundColor = new Color(0.48f, 0.48f, 0.58f, 1f); } else if (flag) { GUI.backgroundColor = new Color(0.68f, 0.46f, 0.14f, 1f); } else if (flag5) { GUI.backgroundColor = new Color(0.72f, 0.62f, 0.36f, 1f); } else { GUI.backgroundColor = new Color(0.42f, 0.42f, 0.46f, 1f); } GUI.contentColor = Color.white; string text3 = (flag ? ("\nKlicken: Wirkung " + (flag2 ? "ausschalten" : "einschalten") + " (keine Punkterstattung).") : string.Empty); GUIContent val = new GUIContent(node.Title + "\n" + text + text2, node.Description + text3); if (GUI.Button(rect, val)) { bool enabled; if (flag5) { if (TalentStore.TryUnlockSkill(node.Id)) { MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, node.Title + " freigeschaltet.", 0, (Sprite)null, false); } } } else if (flag && TalentStore.ToggleSkillEnabled(node.Id, out enabled)) { MessageHud instance2 = MessageHud.instance; if (instance2 != null) { instance2.ShowMessage((MessageType)2, node.Title + (enabled ? " aktiviert." : " deaktiviert. Talentpunkte bleiben ausgegeben."), 0, (Sprite)null, false); } } } GUI.backgroundColor = backgroundColor; GUI.contentColor = contentColor; } private static bool BranchHasAnySkill(TalentData data, SkillBranch branch) { if (data != null) { return SkillTreeDefinitions.ForBranch(branch).Any((SkillNodeDefinition node) => data.HasUnlockedSkill(node.Id)); } return false; } private static void DrawLine(Vector2 start, Vector2 end, bool active) { //IL_0034: 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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_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) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_0020: 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_007e: 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_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00af: 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_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_lineTexture == (Object)null) { _lineTexture = new Texture2D(1, 1); _lineTexture.SetPixel(0, 0, Color.white); _lineTexture.Apply(); } Vector2 val = end - start; float magnitude = ((Vector2)(ref val)).magnitude; float num = Mathf.Atan2(val.y, val.x) * 57.29578f; Matrix4x4 matrix = GUI.matrix; Color color = GUI.color; GUI.color = (active ? new Color(1f, 0.65f, 0f, 0.8f) : new Color(0.3f, 0.3f, 0.3f, 0.8f)); GUIUtility.RotateAroundPivot(num, start); GUI.DrawTexture(new Rect(start.x, start.y - 1.5f, magnitude, 3f), (Texture)(object)_lineTexture); GUI.matrix = matrix; GUI.color = color; } private void SetVisible(bool value) { if (_visible != value) { _visible = value; if (_visible) { ChallengeHubCursorController.Acquire("skilltree-k"); } else { ChallengeHubCursorController.Release("skilltree-k"); } } } private void OnDestroy() { if (_visible) { SetVisible(value: false); } if ((Object)(object)_instance == (Object)(object)this) { _instance = null; } } } internal static class TargetedResetFeature { private sealed class PendingRadiusResetRequest { internal string RequestId; internal Piece Guardian; internal Player Player; internal Vector3 Center; internal float Radius; } private const float ResetGuardianRadiusMeters = 30f; private const string ResetConsumedKey = "ChallengeHub.Reset.Consumed"; private const string ResetConsumedUtcKey = "ChallengeHub.Reset.ConsumedUtc"; private const string ResetConsumedReasonKey = "ChallengeHub.Reset.ConsumedReason"; private const string ResetPendingLifecycleKey = "ChallengeHub.Reset.PendingDungeonLifecycle"; private const string ResetPendingDungeonIdKey = "ChallengeHub.Reset.PendingDungeonId"; private const string ResetPendingRequestedAtKey = "ChallengeHub.Reset.PendingRequestedAtUtc"; private const string ResetLifecycleCompletedKey = "ChallengeHub.Reset.DungeonLifecycleCompleted"; private static readonly Color ConsumedGuardianColor = new Color(0.38f, 0.38f, 0.38f, 1f); private static Plugin plugin; private static bool initialized; private static float lastDungeonHudCheck; private static bool wasInsideDungeon; private static string lastInteriorDungeonKey = string.Empty; private static readonly Dictionary PendingConfirmUntil = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Type RoomComponentType = ResolveTypeSilently("Room"); private static readonly Type LocationProxyComponentType = ResolveTypeSilently("LocationProxy"); private static readonly Type TeleportWorldComponentType = ResolveTypeSilently("TeleportWorld"); private static readonly Type PickableComponentType = ResolveTypeSilently("Pickable"); private static readonly Type DestructibleComponentType = ResolveTypeSilently("Destructible"); private static bool resetGuardianInProgress; private const string RadiusResetRequestRpc = "ChallengeHub_TargetedReset_Request_v2231"; private const string RadiusResetAckRpc = "ChallengeHub_TargetedReset_Ack_v2231"; private static bool radiusResetRpcRegistered; private static PendingRadiusResetRequest pendingRadiusResetRequest; private static readonly HashSet ActiveServerRadiusResetRequests = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly HashSet ActiveServerRadiusResetGuardians = new HashSet(StringComparer.OrdinalIgnoreCase); internal static ConfigEntry EnableTargetedDungeonReset; internal static ConfigEntry AllowDungeonResetInsideProtectedAreas; internal static ConfigEntry AutoResetDungeonsWhenLoaded; internal static ConfigEntry DungeonResetRadius; internal static ConfigEntry DungeonResetCooldownMinutes; internal static ConfigEntry DungeonResetCooldownMinMinutes; internal static ConfigEntry DungeonResetCooldownMaxMinutes; internal static ConfigEntry DungeonTier1Keywords; internal static ConfigEntry DungeonTier2Keywords; internal static ConfigEntry DungeonTier3Keywords; internal static ConfigEntry DungeonEntranceKeywords; internal static ConfigEntry ResettablePrefabKeywords; internal static ConfigEntry PreservePrefabKeywords; internal static ConfigEntry ResetCreatures; internal static ConfigEntry ResetPickables; internal static ConfigEntry ResetDroppedItems; internal static ConfigEntry ResetDestructibles; internal static ConfigEntry ResetPlayerBuildPieces; internal static ConfigEntry EnableResetGuardian; internal static ConfigEntry DefaultResetGuardianRadius; internal static ConfigEntry ResetGuardianRadiusTextKeys; internal static ConfigEntry ResetGuardianCooldownMinutes; internal static ConfigEntry ResetGuardianConfirmSeconds; internal static ConfigEntry ResetGuardianIgnoresProtectedArea; internal static ConfigEntry RequireResetGuardianOwner; internal static ConfigEntry ReportTargetedResetEvents; internal static ConfigEntry ResetScanSeconds; internal static ConfigEntry ResetBatchSize; internal static ConfigEntry ResetVerificationPasses; internal static void Initialize(Plugin owner) { if (!initialized) { plugin = owner; ConfigFile config = ((BaseUnityPlugin)owner).Config; EnableTargetedDungeonReset = config.Bind("TargetedReset", "EnableTargetedDungeonReset", true, "Erlaubt gezielte Krypten-/Hoehlen-Resets, auch wenn der Eingang in einer geschuetzten Zone liegt."); AllowDungeonResetInsideProtectedAreas = config.Bind("TargetedReset", "AllowDungeonResetInsideProtectedAreas", true, "Krypten/Hoehlen duerfen trotz Waechter-/Base-Schutz resettet werden."); AutoResetDungeonsWhenLoaded = config.Bind("TargetedReset", "AutoResetDungeonsWhenLoaded", false, "Aus Sicherheitsgruenden deaktiviert. Geladene Dungeons werden niemals waehrend des Streamings automatisch zerstoert."); DungeonResetRadius = config.Bind("TargetedReset", "DungeonResetRadius", 45f, "Radius um Krypten-/Hoehleneingaenge fuer den gezielten Reset."); DungeonResetCooldownMinutes = config.Bind("TargetedReset", "DungeonResetCooldownMinutes", 120f, "Fallback-Cooldown pro Krypte/Hoehle, falls die Min/Max-Spanne nicht gesetzt ist."); DungeonResetCooldownMinMinutes = config.Bind("TargetedReset", "DungeonResetCooldownMinMinutes", 90f, "Untergrenze der zufaelligen Reset-Zeit pro Krypte/Hoehle."); DungeonResetCooldownMaxMinutes = config.Bind("TargetedReset", "DungeonResetCooldownMaxMinutes", 240f, "Obergrenze der zufaelligen Reset-Zeit pro Krypte/Hoehle."); DungeonTier1Keywords = config.Bind("TargetedReset", "DungeonTier1Keywords", "burial,burialchamber,trollcave,cave,hoehle,höhle", "Stufe-1 Dungeon-/Hoehlen-Schluesselwoerter. Schlechtere Stufe: eher spaeter im Zufallsfenster."); DungeonTier2Keywords = config.Bind("TargetedReset", "DungeonTier2Keywords", "crypt,sunkencrypt,frostcave", "Stufe-2 Dungeon-/Hoehlen-Schluesselwoerter. Mittlere Stufe: gleichmaessiges Zufallsfenster."); DungeonTier3Keywords = config.Bind("TargetedReset", "DungeonTier3Keywords", "infestedmine,mine,queen,ashland,charred", "Stufe-3 Dungeon-/Hoehlen-Schluesselwoerter. Bessere Stufe: eher frueher im Zufallsfenster."); DungeonEntranceKeywords = config.Bind("TargetedReset", "DungeonEntranceKeywords", "crypt,sunkencrypt,burial,burialchamber,trollcave,frostcave,cave,hoehle,höhle,infestedmine,mine,dungeon", "Prefab-/Namens-Schluesselwoerter fuer Dungeon-/Hoehleneingaenge."); ResettablePrefabKeywords = config.Bind("TargetedReset", "ResettablePrefabKeywords", "spawner,pickable,loot,treasure,enemy,itemdrop", "Nur dynamische Inhalte zuruecksetzen. Dungeon-, Raum-, Hoehlen- und Eingangsprefabs werden immer geschuetzt."); PreservePrefabKeywords = config.Bind("TargetedReset", "PreservePrefabKeywords", "guardian,waechter,wächter,ward,bed,portal,chest,piece_chest,workbench,forge,fire_pit,hearth,sign,ship,cart,karve,raft,stonecutter,artisan,partnership,trade", "Prefab-Schluesselwoerter, die beim Reset nie entfernt werden."); ResetCreatures = config.Bind("TargetedReset", "ResetCreatures", true, "Gegner/Kreaturen im Reset-Radius entfernen."); ResetPickables = config.Bind("TargetedReset", "ResetPickables", true, "Pickables/Beeren/Pilze/Beuteobjekte im Reset-Radius entfernen."); ResetDroppedItems = config.Bind("TargetedReset", "ResetDroppedItems", true, "Gedroppte Items im Reset-Radius entfernen."); ResetDestructibles = config.Bind("TargetedReset", "ResetDestructibles", true, "Zerstoerbare Dungeon-/Hoehlenobjekte im Reset-Radius entfernen."); ResetPlayerBuildPieces = config.Bind("TargetedReset", "ResetPlayerBuildPieces", false, "Spieler-Bauteile entfernen. Standard aus Sicherheitsgruenden false."); EnableResetGuardian = config.Bind("GuardianStone.Reset", "EnableResetGuardian", true, "Lila Reset-Waechter aktiviert gezielten Radius-Reset."); DefaultResetGuardianRadius = config.Bind("GuardianStone.Reset", "DefaultRadius", 30f, "Reset-Radius des lila Waechters. Werte ueber 30 m werden aus Stabilitaetsgruenden auf 30 m begrenzt."); ResetGuardianRadiusTextKeys = config.Bind("GuardianStone.Reset", "RadiusTextZdoKeys", "ChallengeHub.Reset.Radius,text,Text,piece_text,sign_text", "Kompatibilitaetsfelder fuer alte Radius-Texte. Der wirksame Reset-Radius ist auf maximal 30 m begrenzt."); ResetGuardianCooldownMinutes = config.Bind("GuardianStone.Reset", "CooldownMinutes", 360f, "Cooldown je Reset-Waechter."); ResetGuardianConfirmSeconds = config.Bind("GuardianStone.Reset", "ConfirmSeconds", 10f, "Zeitfenster fuer zweite STRG+R-Bestaetigung."); ResetGuardianIgnoresProtectedArea = config.Bind("GuardianStone.Reset", "IgnoresProtectedArea", true, "Reset-Waechter darf trotz geschuetztem Bereich resetten."); RequireResetGuardianOwner = config.Bind("GuardianStone.Reset", "RequireOwner", true, "Nur Besitzer des Reset-Waechters darf ihn ausloesen."); ReportTargetedResetEvents = config.Bind("GuardianStone.Reset", "ReportEvents", true, "targeted_reset/reset_guardian Events an die App senden."); ResetScanSeconds = config.Bind("TargetedReset", "ScanSeconds", 10f, "Scan-Intervall fuer Dungeon-Hover/Cooldown und Auto-Reset."); ResetBatchSize = config.Bind("TargetedReset", "ResetBatchSize", 24, "ZDO-Loeschungen pro Frame vor FlushDestroyed. Reduziert Lastspitzen."); ResetVerificationPasses = config.Bind("TargetedReset", "ResetVerificationPasses", 2, "Kontrolllaeufe nach dem ersten Radius-Reset. Der Waechter wird nur bei leerem Restbestand verbraucht."); initialized = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"ChallengeHub Targeted Reset 2.8.0 initialisiert: nur Dungeon- und Ressourcen-Lifecycle; kein Overworld-Radius-Cleanup."); } } } internal static void Patch(Harmony harmony) { if (harmony != null) { PatchPostfix(harmony, typeof(Player), "Update", "PlayerUpdatePostfix"); PatchPostfix(harmony, AccessTools.TypeByName("HoverText"), "GetHoverText", "HoverTextPostfix"); PatchPostfix(harmony, AccessTools.TypeByName("TeleportWorld"), "GetHoverText", "HoverTextPostfix"); } } private static void PatchPostfix(Harmony harmony, Type targetType, string methodName, string postfixName) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown try { if (!(targetType == null)) { MethodInfo methodInfo = AccessTools.Method(targetType, methodName, (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(TargetedResetFeature), postfixName, (Type[])null, (Type[])null); if (methodInfo != null && methodInfo2 != null) { harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("TargetedReset Patch uebersprungen: " + targetType?.Name + "." + methodName + " -> " + ex.Message)); } } } private static void PlayerUpdatePostfix(Player __instance) { try { if (!initialized || (Object)(object)__instance == (Object)null || (Object)(object)Player.m_localPlayer != (Object)(object)__instance || !((Character)__instance).IsOwner()) { return; } if (Time.time - lastDungeonHudCheck >= 0.75f) { lastDungeonHudCheck = Time.time; UpdateDungeonEntryHud(__instance); } if (IsCtrlPressed() && Input.GetKeyDown((KeyCode)114)) { GameObject hoverObject = GetHoverObject(__instance); Piece piece = (((Object)(object)hoverObject != (Object)null) ? hoverObject.GetComponentInParent() : null); if (IsResetGuardian(piece)) { HandleResetGuardianUse(__instance, piece); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("TargetedReset PlayerUpdate Fehler: " + ex.Message)); } } } private static void HoverTextPostfix(object __instance, ref string __result) { try { if (!initialized || __instance == null) { return; } Component val = (Component)((__instance is Component) ? __instance : null); if ((Object)(object)val == (Object)null) { return; } GameObject gameObject = val.gameObject; if (!LooksLikeDungeonEntrance(gameObject)) { return; } string text = BuildDungeonResetLine(DungeonKey(gameObject)); if (!string.IsNullOrWhiteSpace(text)) { if (string.IsNullOrWhiteSpace(__result)) { __result = text; } else if (__result.IndexOf("Krypten-/Hoehlen-Reset", StringComparison.OrdinalIgnoreCase) < 0 && __result.IndexOf("Krypten-/Höhlen-Reset", StringComparison.OrdinalIgnoreCase) < 0) { __result = __result + "\n" + text; } } } catch { } } internal static float GetResetGuardianRadius(Piece piece) { float num = ((DefaultResetGuardianRadius != null && DefaultResetGuardianRadius.Value > 1f) ? DefaultResetGuardianRadius.Value : 30f); num = Mathf.Clamp(num, 5f, 30f); try { if ((Object)(object)piece == (Object)null) { return num; } foreach (string item in SplitCsvRaw((ResetGuardianRadiusTextKeys != null) ? ResetGuardianRadiusTextKeys.Value : "")) { if (TryParseRadius(ReadZdoString((Component)(object)piece, item), out var radius)) { return Mathf.Clamp(radius, 5f, 30f); } } } catch { } return num; } internal static void AppendResetGuardianHover(Piece piece, StringBuilder builder) { if (builder != null && !((Object)(object)piece == (Object)null)) { RefreshResetGuardianState(piece); if (IsResetGuardianConsumed(piece)) { builder.Append("\nStatus: verbraucht und dauerhaft deaktiviert"); builder.Append("\nFunktion: keine"); builder.Append("\nSchutz: keiner"); builder.Append("\nDer graue Stein kann vom Besitzer mit dem Hammer abgebaut werden."); return; } float resetGuardianRadius = GetResetGuardianRadius(piece); builder.Append("\nEinmaliger Reset-Ausloeser"); builder.Append("\nSchutz: keiner (kein Prevent-/Ward-Bereich)"); builder.Append("\nAm Dungeon-Eingang: echter Lifecycle-Reset"); builder.Append("\nAblauf: leer warten -> entladen -> sofort neu erzeugen -> Eingang freigeben"); builder.Append("\nOhne Dungeon: nur registrierte endliche Ressource (Kupfer, Silber, Teer usw.)"); builder.Append("\nSuchradius: ").Append(Math.Round(resetGuardianRadius, 0)).Append("m (maximal 30m)"); builder.Append("\nDie Oberwelt-Zone, Terrain, Wege und Spielerbauten werden niemals pauschal geloescht"); builder.Append("\nNach erfolgreicher Ausloesung wird dieser Stein grau und funktionslos"); builder.Append("\nSTRG+R: Reset vorbereiten / bestaetigen"); } } private static void HandleResetGuardianUse(Player player, Piece piece) { //IL_0159: Unknown result type (might be due to invalid IL or missing references) if (!ChallengeHubServerGateFeature.GameplayAllowed) { if (player != null) { ((Character)player).Message((MessageType)2, "ChallengeHub-Server und Welt sind noch nicht freigegeben.", 0, (Sprite)null); } } else { if ((Object)(object)player == (Object)null || (Object)(object)piece == (Object)null) { return; } RefreshResetGuardianState(piece); if (IsResetGuardianConsumed(piece)) { ((Character)player).Message((MessageType)2, "Dieser Reset-Waechter wurde bereits verwendet und ist dauerhaft deaktiviert.", 0, (Sprite)null); return; } if (IsResetGuardianPending(piece)) { ((Character)player).Message((MessageType)2, "Dungeon-Reset ist vorgemerkt. Der Waechter bleibt lila, bis der Innenraum wirklich entladen wurde.", 0, (Sprite)null); return; } if (EnableResetGuardian != null && !EnableResetGuardian.Value) { ((Character)player).Message((MessageType)1, "Reset-Waechter ist deaktiviert.", 0, (Sprite)null); return; } if (RequireResetGuardianOwner != null && RequireResetGuardianOwner.Value && !IsGuardianOwner(piece, player)) { ((Character)player).Message((MessageType)2, "Nur der Besitzer darf diesen Reset-Waechter ausloesen.", 0, (Sprite)null); return; } DateTime utcNow = DateTime.UtcNow; DateTime dateTime = ReadDate((Component)(object)piece, "ChallengeHub.Reset.LastResetUtc"); DateTime dateTime2 = ((dateTime == DateTime.MinValue) ? DateTime.MinValue : dateTime.AddMinutes(EffectiveResetGuardianCooldownMinutes())); if (dateTime2 != DateTime.MinValue && utcNow < dateTime2) { ((Character)player).Message((MessageType)2, "Reset-Waechter Cooldown: " + HumanTime(dateTime2 - utcNow), 0, (Sprite)null); return; } string key = ObjectKey(((Component)piece).gameObject); if (!PendingConfirmUntil.TryGetValue(key, out var value) || Time.time > value) { PendingConfirmUntil[key] = Time.time + EffectiveConfirmSeconds(); ((Character)player).Message((MessageType)2, DungeonResetLifecycleFeature.IsDungeonResetEntranceNearby(((Component)piece).transform.position) ? "Dungeon-Lifecycle-Reset vorbereitet. Nochmal STRG+R zum Bestaetigen." : ("Ressourcen-Lifecycle in " + Math.Round(GetResetGuardianRadius(piece), 0) + "m vorbereitet. Nochmal STRG+R zum Bestaetigen."), 0, (Sprite)null); return; } PendingConfirmUntil.Remove(key); if (resetGuardianInProgress) { ((Character)player).Message((MessageType)2, "Ein Reset-Waechter arbeitet bereits. Bitte kurz warten.", 0, (Sprite)null); return; } switch (DungeonResetLifecycleFeature.TryQueueFromGuardian(player, piece)) { case DungeonResetLifecycleFeature.GuardianQueueResult.Queued: ((Character)player).Message((MessageType)2, "Dungeon-Reset serverseitig vorgemerkt. Der Waechter bleibt lila und wird erst nach erfolgreichem Entladen grau deaktiviert.", 0, (Sprite)null); return; case DungeonResetLifecycleFeature.GuardianQueueResult.AwaitingServer: ((Character)player).Message((MessageType)2, "Warte auf Server-Bestaetigung. Ohne Bestaetigung bleibt der Waechter aktiv.", 0, (Sprite)null); return; case DungeonResetLifecycleFeature.GuardianQueueResult.EntranceUnresolved: return; } switch (ResourceResetFeature.RequestFromGuardian(piece, player)) { case ResourceResetFeature.GuardianRequestResult.Queued: ((Character)player).Message((MessageType)2, "Ressourcen-Reset serverseitig vorgemerkt. Der Waechter wird erst nach erfolgreicher Wiederherstellung grau.", 0, (Sprite)null); break; case ResourceResetFeature.GuardianRequestResult.AwaitingServer: ((Character)player).Message((MessageType)2, "Warte auf Server-Bestaetigung fuer den Ressourcen-Lifecycle.", 0, (Sprite)null); break; default: ((Character)player).Message((MessageType)2, "Kein Dungeon und kein registriertes Ressourcenvorkommen gefunden. Die Oberwelt wird nicht per Radius-Reset veraendert.", 0, (Sprite)null); break; } } } private static IEnumerator RegisterRadiusResetRpcsWhenReady() { while (ZRoutedRpc.instance == null) { yield return (object)new WaitForSeconds(1f); } if (radiusResetRpcRegistered) { yield break; } try { ZRoutedRpc.instance.Register("ChallengeHub_TargetedReset_Request_v2231", (Action)RPC_RadiusResetRequest); ZRoutedRpc.instance.Register("ChallengeHub_TargetedReset_Ack_v2231", (Action)RPC_RadiusResetAck); radiusResetRpcRegistered = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Targeted-Reset RPCs 2.2.31 registriert: serverautoritärer ZDO-Radius-Reset aktiv."); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Targeted-Reset RPC-Registrierung fehlgeschlagen: " + ex.Message)); } } } private static bool BeginServerRadiusReset(Piece guardian, Player player, float requestedRadius) { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: 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: Expected O, but got Unknown //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)guardian == (Object)null || (Object)(object)player == (Object)null || resetGuardianInProgress) { return false; } if ((Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null || ZRoutedRpc.instance == null || !radiusResetRpcRegistered) { return false; } ZNetView component = ((Component)guardian).GetComponent(); ZDO val = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); if (val == null || !val.IsValid()) { return false; } float num = Mathf.Clamp(requestedRadius, 5f, 30f); string text = Guid.NewGuid().ToString("N"); pendingRadiusResetRequest = new PendingRadiusResetRequest { RequestId = text, Guardian = guardian, Player = player, Center = ((Component)guardian).transform.position, Radius = num }; resetGuardianInProgress = true; ZPackage val2 = new ZPackage(); val2.Write(text); val2.Write(val.m_uid); val2.Write(((Component)guardian).transform.position); val2.Write(num); val2.Write(player.GetPlayerID()); if (ZNet.instance.IsServer()) { RPC_RadiusResetRequest(ValheimNetworkCompatibility.ResolveLocalPeerId(), val2); } else { long num2 = ResolveServerPeerId(); if (num2 == 0L) { pendingRadiusResetRequest = null; resetGuardianInProgress = false; return false; } ZRoutedRpc.instance.InvokeRoutedRPC(num2, "ChallengeHub_TargetedReset_Request_v2231", new object[1] { val2 }); } ((Character)player).Message((MessageType)2, "Serverseitiger " + Math.Round(num, 0).ToString(CultureInfo.InvariantCulture) + "-m-Reset angefordert. Der Waechter bleibt aktiv, bis der Server den Abschluss bestaetigt.", 0, (Sprite)null); ((MonoBehaviour)plugin).StartCoroutine(RadiusResetRequestTimeout(text)); return true; } private static IEnumerator RadiusResetRequestTimeout(string requestId) { yield return (object)new WaitForSeconds(60f); if (pendingRadiusResetRequest != null && string.Equals(pendingRadiusResetRequest.RequestId, requestId, StringComparison.OrdinalIgnoreCase)) { Player player = pendingRadiusResetRequest.Player; pendingRadiusResetRequest = null; resetGuardianInProgress = false; if ((Object)(object)player != (Object)null) { ((Character)player).Message((MessageType)2, "Keine Abschlussbestaetigung vom Server. Der Reset-Waechter bleibt aktiv.", 0, (Sprite)null); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Targeted-Reset Server-Antwort Timeout: " + requestId)); } } } private unsafe static void RPC_RadiusResetRequest(long sender, ZPackage payload) { //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_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_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) if (!ChallengeHubServerGateFeature.GameplayAllowed || payload == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)plugin == (Object)null) { return; } string text = string.Empty; ZDOID val = ZDOID.None; Vector3 zero = Vector3.zero; float num = 30f; long num2 = 0L; try { text = payload.ReadString(); val = payload.ReadZDOID(); zero = payload.ReadVector3(); num = Mathf.Clamp(payload.ReadSingle(), 5f, 30f); num2 = payload.ReadLong(); } catch (Exception ex) { SendRadiusResetAck(sender, text, success: false, 0, 0, 0, "Ungueltige Reset-Anfrage: " + ex.Message); return; } if (string.IsNullOrWhiteSpace(text) || ((ZDOID)(ref val)).IsNone()) { SendRadiusResetAck(sender, text, success: false, 0, 0, 0, "Reset-Anfrage ohne gueltige ID oder Waechter-ZDO."); } else { if (!ActiveServerRadiusResetRequests.Add(text)) { return; } ZDO zDO = ZDOMan.instance.GetZDO(val); if (!ValidateServerResetGuardian(zDO, zero, num2, out var failure)) { ActiveServerRadiusResetRequests.Remove(text); SendRadiusResetAck(sender, text, success: false, 0, 0, 0, failure); return; } string text2 = ((object)(*(ZDOID*)(&val))/*cast due to .constrained prefix*/).ToString(); if (!ActiveServerRadiusResetGuardians.Add(text2)) { ActiveServerRadiusResetRequests.Remove(text); SendRadiusResetAck(sender, text, success: false, 0, 0, 0, "Dieser Reset-Waechter wird bereits serverseitig verarbeitet."); } else { ((MonoBehaviour)plugin).StartCoroutine(ProcessServerRadiusReset(sender, text, zDO, zero, num, text2)); } } } private static bool ValidateServerResetGuardian(ZDO guardianZdo, Vector3 center, long requesterPlayerId, out string failure) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) failure = string.Empty; if (guardianZdo == null || !guardianZdo.IsValid()) { failure = "Der Server findet den Reset-Waechter nicht mehr."; return false; } if (Utils.DistanceXZ(guardianZdo.GetPosition(), center) > 3f) { failure = "Die Reset-Position stimmt nicht mit dem Waechter-ZDO ueberein."; return false; } if (IsTrue(guardianZdo.GetString("ChallengeHub.Reset.Consumed", string.Empty))) { failure = "Dieser Reset-Waechter wurde bereits verbraucht."; return false; } GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(guardianZdo.GetPrefab()) : null); string a = Plugin.NormalizeKey(guardianZdo.GetString("ChallengeHub.GuardianType", string.Empty)); string text = Plugin.NormalizeKey(((Object)(object)val != (Object)null) ? ((Object)val).name : string.Empty); if (!string.Equals(a, "reset", StringComparison.OrdinalIgnoreCase) && !text.Contains("guardian_reset") && !text.Contains("reset_guardian")) { failure = "Das angeforderte ZDO ist kein lila Reset-Waechter."; return false; } string text2 = guardianZdo.GetString("ChallengeHub.Guardian.OwnerId", string.Empty); if (RequireResetGuardianOwner != null && RequireResetGuardianOwner.Value && !string.IsNullOrWhiteSpace(text2) && !string.Equals(text2, requesterPlayerId.ToString(CultureInfo.InvariantCulture), StringComparison.OrdinalIgnoreCase)) { failure = "Nur der Besitzer darf diesen Reset-Waechter ausloesen."; return false; } return true; } private static IEnumerator ProcessServerRadiusReset(long requesterPeerId, string requestId, ZDO guardianZdo, Vector3 center, float radius, string guardianKey) { //IL_001c: 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) int removed = 0; int preserved = 0; int skipped = 0; string failure = string.Empty; List candidates = new List(); HashSet removedIds = new HashSet(); HashSet protectedPlayerZdos = ServerAuthoritativeZdoDestroyer.CollectProtectedPlayerZdos(); int batchSize = Mathf.Clamp((ResetBatchSize != null) ? ResetBatchSize.Value : 24, 1, 128); int verificationPasses = Mathf.Clamp((ResetVerificationPasses != null) ? ResetVerificationPasses.Value : 2, 1, 5); try { if (!TryCollectServerRadiusResetCandidates(center, radius, guardianZdo, candidates, out preserved, out skipped, out failure)) { SendRadiusResetAck(requesterPeerId, requestId, success: false, 0, preserved, skipped, failure); yield break; } if (candidates.Count == 0) { SendRadiusResetAck(requesterPeerId, requestId, success: false, 0, preserved, skipped, "Keine dynamischen Reset-Ziele innerhalb von " + Math.Round(radius, 0).ToString(CultureInfo.InvariantCulture) + "m gefunden."); yield break; } if (!ValheimNetworkCompatibility.TryTakeServerOwnership(guardianZdo, "radius_reset_guardian_preflight")) { SendRadiusResetAck(requesterPeerId, requestId, success: false, 0, preserved, skipped, "Der Server konnte die Authority fuer den Reset-Waechter nicht uebernehmen."); yield break; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Server-ZDO-Radius-Reset startet: Anfrage=" + requestId + "; Radius=" + radius.ToString("0", CultureInfo.InvariantCulture) + "m; Kandidaten=" + candidates.Count + "; Batch=" + batchSize + "; Kontrolllaeufe=" + verificationPasses + "; Spieler-ZDO-Sperren=" + protectedPlayerZdos.Count + "; Quelle=ZDOMan.FindSectorObjects")); } bool verifiedEmpty = false; for (int pass = 0; pass <= verificationPasses; pass++) { if (pass > 0) { candidates.Clear(); if (!TryCollectServerRadiusResetCandidates(center, radius, guardianZdo, candidates, out var preserved2, out var skipped2, out var failure2)) { failure = "Verifikationslauf " + pass + " fehlgeschlagen: " + failure2; break; } preserved += preserved2; skipped += skipped2; if (candidates.Count == 0) { verifiedEmpty = true; break; } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Server-ZDO-Radius-Reset Kontrolllauf " + pass + ": verblieben=" + candidates.Count + ".")); } } int processedInBatch = 0; ZDO[] array = candidates.ToArray(); foreach (ZDO val in array) { if (val == null || !val.IsValid() || Utils.DistanceXZ(val.GetPosition(), center) > radius) { skipped++; continue; } ZDOID uid = val.m_uid; switch (ServerAuthoritativeZdoDestroyer.Destroy(val, protectedPlayerZdos, "targeted_radius_reset_pass_" + pass)) { case ServerAuthoritativeZdoDestroyer.DestroyResult.Destroyed: case ServerAuthoritativeZdoDestroyer.DestroyResult.AlreadyGone: if (removedIds.Add(uid)) { removed++; } break; case ServerAuthoritativeZdoDestroyer.DestroyResult.ProtectedPlayer: preserved++; break; default: skipped++; break; } processedInBatch++; if (processedInBatch >= batchSize) { ServerAuthoritativeZdoDestroyer.FlushDestroyed("targeted_radius_reset_batch"); processedInBatch = 0; yield return null; } } ServerAuthoritativeZdoDestroyer.FlushDestroyed("targeted_radius_reset_pass_" + pass); yield return null; } if (!verifiedEmpty) { candidates.Clear(); if (TryCollectServerRadiusResetCandidates(center, radius, guardianZdo, candidates, out var preserved3, out var skipped3, out var failure3)) { preserved += preserved3; skipped += skipped3; verifiedEmpty = candidates.Count == 0; if (!verifiedEmpty) { failure = candidates.Count + " dynamische ZDOs blieben nach den Kontrolllaeufen uebrig."; } } else { failure = "Abschlusskontrolle fehlgeschlagen: " + failure3; } } if (removed <= 0) { SendRadiusResetAck(requesterPeerId, requestId, success: false, 0, preserved, skipped, "Der Server konnte kein dynamisches Objekt sicher entfernen."); yield break; } if (!verifiedEmpty) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("Server-ZDO-Radius-Reset unvollstaendig: Anfrage=" + requestId + "; " + failure)); } SendRadiusResetAck(requesterPeerId, requestId, success: false, removed, preserved, skipped, "Reset nicht verifiziert: " + failure + " Der Waechter bleibt aktiv."); yield break; } MarkGuardianZdoConsumedOnServer(guardianZdo, "radius_cleanup_completed_verified_server_zdo"); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)("Server-ZDO-Radius-Reset abgeschlossen und verifiziert: Anfrage=" + requestId + "; Entfernt=" + removed + "; Bewahrt=" + preserved + "; Uebersprungen=" + skipped)); } SendRadiusResetAck(requesterPeerId, requestId, success: true, removed, preserved, skipped, "Sicherer serverseitiger Radius-Reset mit Kontrolllauf abgeschlossen."); } finally { candidates.Clear(); ActiveServerRadiusResetRequests.Remove(requestId); ActiveServerRadiusResetGuardians.Remove(guardianKey); } } private static bool TryCollectServerRadiusResetCandidates(Vector3 center, float radius, ZDO guardianZdo, List candidates, out int preserved, out int skipped, out string failure) { //IL_0041: 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) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) preserved = 0; skipped = 0; failure = string.Empty; if (ZDOMan.instance == null || (Object)(object)ZoneSystem.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { failure = "ZDOMan, ZoneSystem oder ZNetScene ist auf dem Server noch nicht bereit."; return false; } List list = new List(); try { Vector2i zone = ZoneSystem.GetZone(center); MethodInfo method = typeof(ZDOMan).GetMethod("FindSectorObjects", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[5] { typeof(Vector2i), typeof(int), typeof(int), typeof(List), typeof(List) }, null); if (method != null) { method.Invoke(ZDOMan.instance, new object[5] { zone, 1, 0, list, null }); } else { MethodInfo method2 = typeof(ZDOMan).GetMethod("FindSectorObjects", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[3] { typeof(Vector2i), typeof(int), typeof(List) }, null); if (method2 == null) { failure = "Die Valheim-Version besitzt keine kompatible FindSectorObjects-Signatur."; return false; } method2.Invoke(ZDOMan.instance, new object[3] { zone, 1, list }); } } catch (Exception ex) { failure = "Serverseitige ZDO-Sektorsuche fehlgeschlagen: " + ex.Message; return false; } HashSet hashSet = new HashSet(); HashSet protectedIds = ServerAuthoritativeZdoDestroyer.CollectProtectedPlayerZdos(); foreach (ZDO item in list) { if (item == null || !item.IsValid() || !hashSet.Add(item.m_uid)) { skipped++; } else if (guardianZdo != null && ((ZDOID)(ref item.m_uid)).Equals(guardianZdo.m_uid)) { preserved++; } else { if (Utils.DistanceXZ(item.GetPosition(), center) > radius) { continue; } if (ServerAuthoritativeZdoDestroyer.IsProtectedPlayerZdo(item, protectedIds)) { preserved++; continue; } GameObject prefab = ZNetScene.instance.GetPrefab(item.GetPrefab()); if ((Object)(object)prefab == (Object)null) { skipped++; } else if ((Object)(object)prefab.GetComponent() != (Object)null || IsAnyChallengeHubGuardian(prefab) || ShouldPreserve(prefab)) { preserved++; } else if (ShouldReset(prefab)) { candidates.Add(item); } } } return true; } private static void MarkGuardianZdoConsumedOnServer(ZDO guardianZdo, string reason) { if (guardianZdo == null || !guardianZdo.IsValid() || ZDOMan.instance == null) { return; } if (!ValheimNetworkCompatibility.TryTakeServerOwnership(guardianZdo, "consume_reset_guardian")) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)"Reset-Waechter konnte serverseitig nicht als verbraucht markiert werden: fehlende ZDO-Authority."); } return; } guardianZdo.Set("ChallengeHub.Reset.Consumed", "true"); guardianZdo.Set("ChallengeHub.Reset.ConsumedUtc", DungeonResetLifecycleFeature.NetworkUtcNow().ToString("O", CultureInfo.InvariantCulture)); guardianZdo.Set("ChallengeHub.Reset.ConsumedReason", reason ?? string.Empty); guardianZdo.Set("ChallengeHub.Reset.PendingDungeonLifecycle", "false"); guardianZdo.Set("ChallengeHub.Reset.PendingDungeonId", string.Empty); guardianZdo.Set("ChallengeHub.Reset.PendingRequestedAtUtc", string.Empty); guardianZdo.Set("ChallengeHub.Reset.DungeonLifecycleCompleted", "true"); guardianZdo.Set("ChallengeHub.Reset.LastResetUtc", DungeonResetLifecycleFeature.NetworkUtcNow().ToString("O", CultureInfo.InvariantCulture)); guardianZdo.Set("ChallengeHub.Reset.NextResetUtc", string.Empty); } private static void SendRadiusResetAck(long targetPeerId, string requestId, bool success, int removed, int preserved, int skipped, string message) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(requestId ?? string.Empty); val.Write(success); val.Write(removed); val.Write(preserved); val.Write(skipped); val.Write(message ?? string.Empty); if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && pendingRadiusResetRequest != null && string.Equals(pendingRadiusResetRequest.RequestId, requestId, StringComparison.OrdinalIgnoreCase)) { RPC_RadiusResetAck(targetPeerId, val); } else if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(targetPeerId, "ChallengeHub_TargetedReset_Ack_v2231", new object[1] { val }); } } private static void RPC_RadiusResetAck(long sender, ZPackage payload) { //IL_0153: Unknown result type (might be due to invalid IL or missing references) if (payload == null) { return; } string b; bool flag; int removed; int preserved; int num; string text; try { b = payload.ReadString(); flag = payload.ReadBool(); removed = payload.ReadInt(); preserved = payload.ReadInt(); num = payload.ReadInt(); text = payload.ReadString(); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Targeted-Reset Ack konnte nicht gelesen werden: " + ex.Message)); } return; } if (TargetedResetFeature.pendingRadiusResetRequest == null || !string.Equals(TargetedResetFeature.pendingRadiusResetRequest.RequestId, b, StringComparison.OrdinalIgnoreCase)) { return; } PendingRadiusResetRequest pendingRadiusResetRequest = TargetedResetFeature.pendingRadiusResetRequest; TargetedResetFeature.pendingRadiusResetRequest = null; resetGuardianInProgress = false; if (flag && (Object)(object)pendingRadiusResetRequest.Guardian != (Object)null) { ApplyConsumedResetGuardianVisual(((Component)pendingRadiusResetRequest.Guardian).gameObject); GuardianZoneRadiusVisual.SetVisible(((Component)pendingRadiusResetRequest.Guardian).gameObject, visible: false); } if ((Object)(object)pendingRadiusResetRequest.Player != (Object)null) { string text2 = (flag ? (text + " " + removed + " entfernt, " + preserved + " bewahrt, " + num + " uebersprungen.") : (text + " Der Waechter bleibt aktiv.")); ((Character)pendingRadiusResetRequest.Player).Message((MessageType)2, text2, 0, (Sprite)null); if (flag) { SendResetEvent("reset_guardian_triggered", pendingRadiusResetRequest.Player, pendingRadiusResetRequest.Center, pendingRadiusResetRequest.Radius, removed, preserved, "reset_guardian_server_zdo"); } } } private static long ResolveServerPeerId() { try { if ((Object)(object)ZNet.instance != (Object)null) { MethodInfo method = typeof(ZNet).GetMethod("GetServerPeerID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method != null) { object obj = method.Invoke(ZNet.instance, null); long num = ((obj != null) ? Convert.ToInt64(obj, CultureInfo.InvariantCulture) : 0); if (num != 0L) { return num; } } } } catch { } try { if (ZRoutedRpc.instance != null) { MethodInfo method2 = typeof(ZRoutedRpc).GetMethod("GetServerPeerID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method2 != null) { object obj3 = method2.Invoke(ZRoutedRpc.instance, null); long num2 = ((obj3 != null) ? Convert.ToInt64(obj3, CultureInfo.InvariantCulture) : 0); if (num2 != 0L) { return num2; } } } } catch { } return 0L; } private static void UpdateDungeonEntryHud(Player player) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return; } bool flag = false; try { flag = ((Character)player).InInterior(); } catch { } if (!flag) { wasInsideDungeon = false; lastInteriorDungeonKey = string.Empty; return; } DungeonGenerator val = FindNearestDungeonGenerator(((Component)player).transform.position, 250f); if ((Object)(object)val == (Object)null) { return; } string b = DungeonKey(((Component)val).gameObject); if (wasInsideDungeon && string.Equals(lastInteriorDungeonKey, b, StringComparison.OrdinalIgnoreCase)) { return; } wasInsideDungeon = true; lastInteriorDungeonKey = b; int num = DetermineDungeonTier(((Component)val).gameObject); string text = new string('*', Mathf.Clamp(num, 1, 3)); string text2 = DungeonResetLifecycleFeature.BuildDungeonStatus(val); string text3 = "Lila Reset-Waechter am Eingang: Reset sofort vormerken"; try { if ((Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)2, "Grabkammer " + text + "\n" + text2 + "\n" + text3, 0, (Sprite)null, false); } } catch { } } private static DungeonGenerator FindNearestDungeonGenerator(Vector3 playerPosition, float maxDistance) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) try { DungeonGenerator[] array = Object.FindObjectsByType((FindObjectsSortMode)0); DungeonGenerator result = null; float num = maxDistance; DungeonGenerator[] array2 = array; foreach (DungeonGenerator val in array2) { if (!((Object)(object)val == (Object)null)) { float num2 = Vector3.Distance(((Component)val).transform.position, playerPosition); if (!(num2 >= num)) { result = val; num = num2; } } } return result; } catch { return null; } } private static bool ShouldPreserve(GameObject go) { if ((Object)(object)go == (Object)null) { return true; } if (IsDungeonStructure(go)) { return true; } if (ContainsAny(NameChain(go), (PreservePrefabKeywords != null) ? PreservePrefabKeywords.Value : "")) { return true; } if ((Object)(object)go.GetComponentInParent() != (Object)null && (ResetPlayerBuildPieces == null || !ResetPlayerBuildPieces.Value)) { return true; } return false; } private static bool IsDungeonStructure(GameObject go) { if ((Object)(object)go == (Object)null) { return true; } if (ContainsAny((((Object)go).name ?? string.Empty).ToLowerInvariant(), "dungeon,room,crypt,cave,hoehle,höhle,burial,trollcave,frostcave,sunkencrypt,infestedmine,locationproxy")) { return true; } try { if ((Object)(object)go.GetComponent() != (Object)null) { return true; } if (HasComponent(go, LocationProxyComponentType)) { return true; } if (HasComponent(go, TeleportWorldComponentType)) { return true; } if (HasComponent(go, RoomComponentType)) { return true; } } catch { } return false; } private static bool ShouldReset(GameObject go) { if ((Object)(object)go == (Object)null) { return false; } if (ContainsAny(NameChain(go), (ResettablePrefabKeywords != null) ? ResettablePrefabKeywords.Value : "")) { return true; } if (ResetCreatures == null || ResetCreatures.Value) { Character componentInParent = go.GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null && !componentInParent.IsPlayer()) { return true; } } if ((ResetDroppedItems == null || ResetDroppedItems.Value) && (Object)(object)go.GetComponentInParent() != (Object)null) { return true; } if ((ResetPickables == null || ResetPickables.Value) && HasComponentInParent(go, PickableComponentType)) { return true; } if ((ResetDestructibles == null || ResetDestructibles.Value) && HasComponentInParent(go, DestructibleComponentType)) { return true; } return false; } private static bool LooksLikeDungeonEntrance(GameObject go) { if ((Object)(object)go == (Object)null) { return false; } return ContainsAny(NameChain(go), (DungeonEntranceKeywords != null) ? DungeonEntranceKeywords.Value : ""); } private static string BuildDungeonResetLine(string key) { string text = "Stufe " + DungeonTierFromKey(key).ToString(CultureInfo.InvariantCulture); return "Krypten-/Hoehlen-Reset: " + text + " · sicherer Lifecycle aktiv · lila Waechter am Eingang"; } private static string ResetGuardianReadyText(Piece piece) { if (IsResetGuardianConsumed(piece)) { return "verbraucht"; } DateTime dateTime = ParseDate(ReadZdoString((Component)(object)piece, "ChallengeHub.Reset.NextResetUtc")); if (dateTime == DateTime.MinValue || dateTime <= DateTime.UtcNow) { return "bereit"; } return "in " + HumanTime(dateTime - DateTime.UtcNow); } private static void SendResetEvent(string eventType, Player player, Vector3 center, float radius, int removed, int preserved, string reason) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) try { if ((ReportTargetedResetEvents == null || ReportTargetedResetEvents.Value) && !((Object)(object)plugin == (Object)null) && !((Object)(object)player == (Object)null)) { plugin.SendEvent(eventType, player, new Dictionary { { "position", Plugin.SerializeVector(center) }, { "radius", Math.Round(radius, 1) }, { "removedObjects", removed }, { "preservedObjects", preserved }, { "resetReason", reason }, { "biome", Plugin.CurrentBiome(center) } }); } } catch { } } internal static void RefreshResetGuardianState(Piece piece) { if ((Object)(object)piece == (Object)null || !IsResetGuardian(piece)) { return; } StripResetGuardianProtection(((Component)piece).gameObject); string a = ReadZdoString((Component)(object)piece, "ChallengeHub.Reset.ConsumedReason"); bool flag = IsTrue(ReadZdoString((Component)(object)piece, "ChallengeHub.Reset.DungeonLifecycleCompleted")); if (IsResetGuardianConsumed(piece) && string.Equals(a, "dungeon_lifecycle_queued", StringComparison.OrdinalIgnoreCase) && !flag) { WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.Consumed", "false"); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.ConsumedUtc", string.Empty); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.ConsumedReason", string.Empty); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.LastResetUtc", string.Empty); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Fehlgeschlagenen 2.2.6-Dungeon-Waechter wieder aktiviert: " + ObjectKey(((Component)piece).gameObject))); } } if (IsResetGuardianPending(piece)) { string text = ReadZdoString((Component)(object)piece, "ChallengeHub.Reset.PendingDungeonId"); DateTime guardianRequestedAtUtc = ReadDate((Component)(object)piece, "ChallengeHub.Reset.PendingRequestedAtUtc"); switch (DungeonResetLifecycleFeature.GetPendingGuardianLifecycleState(text, guardianRequestedAtUtc)) { case DungeonResetLifecycleFeature.PendingGuardianLifecycleState.Completed: { WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.PendingDungeonLifecycle", "false"); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.PendingDungeonId", string.Empty); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.PendingRequestedAtUtc", string.Empty); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.DungeonLifecycleCompleted", "true"); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.LastResetUtc", DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)); MarkResetGuardianConsumed(piece, "dungeon_lifecycle_completed_recovered"); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Abgeschlossenen Dungeon-Waechterstatus wiederhergestellt: " + ObjectKey(((Component)piece).gameObject) + " => " + text)); } break; } case DungeonResetLifecycleFeature.PendingGuardianLifecycleState.Invalid: { WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.PendingDungeonLifecycle", "false"); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.PendingDungeonId", string.Empty); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.PendingRequestedAtUtc", string.Empty); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.DungeonLifecycleCompleted", "false"); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Festhaengenden Dungeon-Waechter wieder aktiviert: " + ObjectKey(((Component)piece).gameObject) + " => " + text)); } break; } } } if (!IsResetGuardianConsumed(piece) && !IsResetGuardianPending(piece) && ReadDate((Component)(object)piece, "ChallengeHub.Reset.LastResetUtc") != DateTime.MinValue) { MarkResetGuardianConsumed(piece, "legacy_reset_migration"); } if (IsResetGuardianConsumed(piece)) { ApplyConsumedResetGuardianVisual(((Component)piece).gameObject); GuardianZoneRadiusVisual.SetVisible(((Component)piece).gameObject, visible: false); } else { GuardianZoneRadiusVisual.SetVisible(((Component)piece).gameObject, visible: true); } } internal static void MarkResetGuardianPending(Piece piece, string dungeonId, DateTime requestedAtUtc) { if (!((Object)(object)piece == (Object)null) && !string.IsNullOrWhiteSpace(dungeonId)) { if (requestedAtUtc == DateTime.MinValue) { requestedAtUtc = DungeonResetLifecycleFeature.NetworkUtcNow(); } WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.PendingDungeonLifecycle", "true"); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.PendingDungeonId", dungeonId); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.PendingRequestedAtUtc", requestedAtUtc.ToString("O", CultureInfo.InvariantCulture)); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.DungeonLifecycleCompleted", "false"); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Reset-Waechter wartet auf erfolgreichen Dungeon-Lifecycle: " + ObjectKey(((Component)piece).gameObject) + " => " + dungeonId + "; Anfrage=" + requestedAtUtc.ToString("O", CultureInfo.InvariantCulture))); } } } internal static void MarkResourceResetGuardianPending(Piece piece, string resourceSiteKey, DateTime requestedAtUtc) { MarkResetGuardianPending(piece, "resource:" + (resourceSiteKey ?? string.Empty), requestedAtUtc); } internal static void MarkResourceResetGuardianZdoPending(ZDO guardianZdo, string resourceSiteKey, DateTime requestedAtUtc) { if (guardianZdo == null || !guardianZdo.IsValid()) { return; } try { ValheimNetworkCompatibility.TryTakeServerOwnership(guardianZdo, "resource_guardian_pending"); guardianZdo.Set("ChallengeHub.Reset.PendingDungeonLifecycle", "true"); guardianZdo.Set("ChallengeHub.Reset.PendingDungeonId", "resource:" + (resourceSiteKey ?? string.Empty)); guardianZdo.Set("ChallengeHub.Reset.PendingRequestedAtUtc", requestedAtUtc.ToString("O", CultureInfo.InvariantCulture)); guardianZdo.Set("ChallengeHub.Reset.DungeonLifecycleCompleted", "false"); } catch { } } internal static void CompleteResourceResetGuardian(ZDOID guardianId, string resourceSiteKey) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) ZDO val = ((ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(guardianId) : null); if (val == null || !val.IsValid()) { return; } try { ValheimNetworkCompatibility.TryTakeServerOwnership(val, "resource_guardian_complete"); val.Set("ChallengeHub.Reset.DungeonLifecycleCompleted", "true"); val.Set("ChallengeHub.Reset.LastResetUtc", DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)); MarkGuardianZdoConsumedOnServer(val, "resource_lifecycle_completed:" + (resourceSiteKey ?? string.Empty)); } catch { } } internal static void ReleaseResourceResetGuardian(ZDOID guardianId, string resourceSiteKey, string failure) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) ZDO val = ((ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(guardianId) : null); if (val == null || !val.IsValid()) { return; } try { ValheimNetworkCompatibility.TryTakeServerOwnership(val, "resource_guardian_release"); val.Set("ChallengeHub.Reset.PendingDungeonLifecycle", "false"); val.Set("ChallengeHub.Reset.PendingDungeonId", string.Empty); val.Set("ChallengeHub.Reset.PendingRequestedAtUtc", string.Empty); val.Set("ChallengeHub.Reset.DungeonLifecycleCompleted", "false"); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Ressourcen-Reset-Waechter bleibt aktiv: " + resourceSiteKey + " :: " + failure)); } } catch { } } internal static void ReleaseResourceResetGuardianPending(string pendingKey, string failure) { Piece[] array = (((Object)(object)Plugin.Instance != (Object)null) ? Plugin.Instance.RuntimeGuardianPiecesSnapshot() : Object.FindObjectsByType((FindObjectsSortMode)0)); foreach (Piece val in array) { if (!((Object)(object)val == (Object)null) && IsResetGuardian(val) && ReadZdoString((Component)(object)val, "ChallengeHub.Reset.PendingDungeonId").StartsWith("resource:", StringComparison.OrdinalIgnoreCase)) { WriteZdoString((Component)(object)val, "ChallengeHub.Reset.PendingDungeonLifecycle", "false"); WriteZdoString((Component)(object)val, "ChallengeHub.Reset.PendingDungeonId", string.Empty); WriteZdoString((Component)(object)val, "ChallengeHub.Reset.PendingRequestedAtUtc", string.Empty); GuardianZoneRadiusVisual.SetVisible(((Component)val).gameObject, visible: true); } } if (!string.IsNullOrWhiteSpace(failure)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)failure); } } } internal static void CompletePendingResetGuardians(string dungeonId, Vector3 dungeonPosition) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //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_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(dungeonId)) { return; } Piece[] array = (((Object)(object)Plugin.Instance != (Object)null) ? Plugin.Instance.RuntimeGuardianPiecesSnapshot() : Object.FindObjectsByType((FindObjectsSortMode)0)); bool flag = false; Piece[] array2 = array; Scene scene; foreach (Piece val in array2) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null)) { scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).IsValid() && IsResetGuardian(val) && string.Equals(ReadZdoString((Component)(object)val, "ChallengeHub.Reset.PendingDungeonId"), dungeonId, StringComparison.OrdinalIgnoreCase)) { CompleteGuardian(val, "dungeon_lifecycle_completed"); flag = true; } } } if (flag) { return; } Piece val2 = null; float num = 90f; Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(dungeonPosition.x, dungeonPosition.z); array2 = array; foreach (Piece val4 in array2) { if ((Object)(object)val4 == (Object)null || (Object)(object)((Component)val4).gameObject == (Object)null) { continue; } scene = ((Component)val4).gameObject.scene; if (((Scene)(ref scene)).IsValid() && IsResetGuardian(val4) && !IsResetGuardianConsumed(val4)) { Vector3 position = ((Component)val4).transform.position; float num2 = Vector2.Distance(val3, new Vector2(position.x, position.z)); if (!(num2 >= num)) { val2 = val4; num = num2; } } } if ((Object)(object)val2 != (Object)null) { CompleteGuardian(val2, "dungeon_lifecycle_completed_nearest_recovery"); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Reset-Waechter ueber Eingangsnachbarschaft abgeschlossen: " + ObjectKey(((Component)val2).gameObject) + " => " + dungeonId + "; Distanz=" + Math.Round(num, 1).ToString(CultureInfo.InvariantCulture) + "m")); } } } private static void CompleteGuardian(Piece piece, string reason) { if (!((Object)(object)piece == (Object)null)) { WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.PendingDungeonLifecycle", "false"); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.PendingDungeonId", string.Empty); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.PendingRequestedAtUtc", string.Empty); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.DungeonLifecycleCompleted", "true"); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.LastResetUtc", DungeonResetLifecycleFeature.NetworkUtcNow().ToString("O", CultureInfo.InvariantCulture)); MarkResetGuardianConsumed(piece, reason); } } internal static void ReleasePendingResetGuardians(string dungeonId, string failure) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(dungeonId)) { return; } Piece[] array = (((Object)(object)Plugin.Instance != (Object)null) ? Plugin.Instance.RuntimeGuardianPiecesSnapshot() : Object.FindObjectsByType((FindObjectsSortMode)0)); foreach (Piece val in array) { if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null) { continue; } Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).IsValid() && IsResetGuardian(val) && string.Equals(ReadZdoString((Component)(object)val, "ChallengeHub.Reset.PendingDungeonId"), dungeonId, StringComparison.OrdinalIgnoreCase)) { WriteZdoString((Component)(object)val, "ChallengeHub.Reset.PendingDungeonLifecycle", "false"); WriteZdoString((Component)(object)val, "ChallengeHub.Reset.PendingDungeonId", string.Empty); WriteZdoString((Component)(object)val, "ChallengeHub.Reset.PendingRequestedAtUtc", string.Empty); WriteZdoString((Component)(object)val, "ChallengeHub.Reset.DungeonLifecycleCompleted", "false"); GuardianZoneRadiusVisual.SetVisible(((Component)val).gameObject, visible: true); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Reset-Waechter bleibt aktiv, weil Dungeon-Entladung fehlschlug: " + ObjectKey(((Component)val).gameObject) + " -> " + failure)); } } } } private static bool IsResetGuardianPending(Piece piece) { if ((Object)(object)piece != (Object)null) { return IsTrue(ReadZdoString((Component)(object)piece, "ChallengeHub.Reset.PendingDungeonLifecycle")); } return false; } private static bool IsTrue(string raw) { if (!string.Equals(raw, "true", StringComparison.OrdinalIgnoreCase) && !string.Equals(raw, "1", StringComparison.OrdinalIgnoreCase)) { return string.Equals(raw, "yes", StringComparison.OrdinalIgnoreCase); } return true; } internal static bool IsResetGuardianConsumed(Piece piece) { if ((Object)(object)piece == (Object)null) { return false; } string a = ReadZdoString((Component)(object)piece, "ChallengeHub.Reset.Consumed"); if (!string.Equals(a, "true", StringComparison.OrdinalIgnoreCase) && !string.Equals(a, "1", StringComparison.OrdinalIgnoreCase)) { return string.Equals(a, "yes", StringComparison.OrdinalIgnoreCase); } return true; } private static void MarkResetGuardianConsumed(Piece piece, string reason) { if (!((Object)(object)piece == (Object)null)) { WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.Consumed", "true"); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.ConsumedUtc", DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.ConsumedReason", reason ?? string.Empty); WriteZdoString((Component)(object)piece, "ChallengeHub.Reset.NextResetUtc", string.Empty); ApplyConsumedResetGuardianVisual(((Component)piece).gameObject); GuardianZoneRadiusVisual.SetVisible(((Component)piece).gameObject, visible: false); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Reset-Waechter verbraucht und deaktiviert: " + ObjectKey(((Component)piece).gameObject) + " (" + (reason ?? "reset") + ")")); } } } internal static void StripResetGuardianProtection(GameObject root) { if ((Object)(object)root == (Object)null || (Object)(object)root.GetComponent() != (Object)null) { return; } try { Component[] componentsInChildren = root.GetComponentsInChildren(true); foreach (Component val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { Type type = ((object)val).GetType(); if (!(type == null) && string.Equals(type.Name ?? string.Empty, "PrivateArea", StringComparison.OrdinalIgnoreCase)) { Object.Destroy((Object)(object)val); } } } if ((Object)(object)root.GetComponent() == (Object)null) { root.AddComponent(); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Reset-Waechter: Schutzkomponente konnte nicht entfernt werden: " + ex.Message)); } } } private static void ApplyConsumedResetGuardianVisual(GameObject root) { //IL_0061: 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) if ((Object)(object)root == (Object)null || (Object)(object)root.GetComponent() != (Object)null) { return; } try { Renderer[] componentsInChildren = root.GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } Material[] materials = val.materials; foreach (Material val2 in materials) { if (!((Object)(object)val2 == (Object)null)) { if (val2.HasProperty("_Color")) { val2.color = ConsumedGuardianColor; } if (val2.HasProperty("_EmissionColor")) { val2.DisableKeyword("_EMISSION"); val2.SetColor("_EmissionColor", Color.black); } } } val.materials = materials; } Light[] componentsInChildren2 = root.GetComponentsInChildren(true); foreach (Light val3 in componentsInChildren2) { if ((Object)(object)val3 != (Object)null) { ((Behaviour)val3).enabled = false; } } if ((Object)(object)root.GetComponent() == (Object)null) { root.AddComponent(); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Reset-Waechter: graue Verbrauchsanzeige fehlgeschlagen: " + ex.Message)); } } } private static bool IsAnyChallengeHubGuardian(GameObject go) { if ((Object)(object)go == (Object)null) { return false; } try { if ((Object)(object)go.GetComponentInParent() != (Object)null) { return true; } Piece componentInParent = go.GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { if (!string.IsNullOrWhiteSpace(ReadZdoString((Component)(object)componentInParent, "ChallengeHub.GuardianType"))) { return true; } string text = Plugin.NormalizeKey(((Object)componentInParent).name); if (text.Contains("challengehub_guardian") || text.Contains("guardian_reset") || text.Contains("reset_guardian")) { return true; } } } catch { } return false; } private static bool IsResetGuardian(Piece piece) { if ((Object)(object)piece == (Object)null) { return false; } string text = Plugin.NormalizeKey(((Object)piece).name); if (text.Contains("guardian_reset") || text.Contains("reset_guardian")) { return true; } return string.Equals(Plugin.NormalizeKey(ReadZdoString((Component)(object)piece, "ChallengeHub.GuardianType")), "reset", StringComparison.OrdinalIgnoreCase); } private static bool IsGuardianOwner(Piece piece, Player player) { string text = ReadZdoString((Component)(object)piece, "ChallengeHub.Guardian.OwnerId"); if (string.IsNullOrWhiteSpace(text)) { return true; } if ((Object)(object)player != (Object)null) { return string.Equals(text, player.GetPlayerID().ToString(), StringComparison.OrdinalIgnoreCase); } return false; } private static GameObject GetHoverObject(Player player) { try { MethodInfo methodInfo = AccessTools.Method(((object)player).GetType(), "GetHoverObject", (Type[])null, (Type[])null); object obj = ((methodInfo != null) ? methodInfo.Invoke(player, null) : null); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if (val != null) { return val; } Component val2 = (Component)((obj is Component) ? obj : null); if (val2 != null) { return val2.gameObject; } string[] array = new string[3] { "m_hovering", "m_hoveringObject", "m_hoverObject" }; foreach (string name in array) { FieldInfo field = ((object)player).GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); obj = ((field != null) ? field.GetValue(player) : null); GameObject val3 = (GameObject)((obj is GameObject) ? obj : null); if (val3 != null) { return val3; } Component val4 = (Component)((obj is Component) ? obj : null); if (val4 != null) { return val4.gameObject; } } } catch { } return null; } private static bool IsCtrlPressed() { if (!Input.GetKey((KeyCode)306)) { return Input.GetKey((KeyCode)305); } return true; } private static bool TryParseRadius(string raw, out float radius) { radius = 0f; if (string.IsNullOrWhiteSpace(raw)) { return false; } Match match = Regex.Match(raw, "(?i)(radius|r|durchmesser|diameter)\\s*[:=]?\\s*([0-9]+(?:[\\.,][0-9]+)?)"); if (!match.Success) { match = Regex.Match(raw, "([0-9]+(?:[\\.,][0-9]+)?)"); } if (!match.Success) { return false; } return float.TryParse(match.Groups[match.Groups.Count - 1].Value.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out radius); } private static DateTime ReadDate(Component component, string key) { return ParseDate(ReadZdoString(component, key)); } private static DateTime ParseDate(string raw) { if (!string.IsNullOrWhiteSpace(raw) && DateTime.TryParse(raw, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out var result)) { return result.ToUniversalTime(); } return DateTime.MinValue; } private static string HumanTime(TimeSpan span) { if (span < TimeSpan.Zero) { span = TimeSpan.Zero; } if (span.TotalMinutes < 1.0) { return Math.Max(1.0, Math.Ceiling(span.TotalSeconds)).ToString("0", CultureInfo.InvariantCulture) + "s"; } int num = Math.Max(1, (int)Math.Ceiling(span.TotalMinutes)); int num2 = num / 1440; int num3 = num % 1440 / 60; int num4 = num % 60; StringBuilder stringBuilder = new StringBuilder(); if (num2 > 0) { stringBuilder.Append(num2).Append("d"); } if (num3 > 0) { stringBuilder.Append(num3).Append("h"); } if (num4 > 0 || stringBuilder.Length == 0) { stringBuilder.Append(num4).Append("m"); } return stringBuilder.ToString(); } private static int DetermineDungeonTier(GameObject go) { string haystack = NameChain(go); if (ContainsAny(haystack, (DungeonTier3Keywords != null) ? DungeonTier3Keywords.Value : "")) { return 3; } if (ContainsAny(haystack, (DungeonTier2Keywords != null) ? DungeonTier2Keywords.Value : "")) { return 2; } ContainsAny(haystack, (DungeonTier1Keywords != null) ? DungeonTier1Keywords.Value : ""); return 1; } private static int DungeonTierFromKey(string key) { string haystack = Plugin.NormalizeKey(key ?? string.Empty); if (ContainsAny(haystack, (DungeonTier3Keywords != null) ? DungeonTier3Keywords.Value : "")) { return 3; } if (ContainsAny(haystack, (DungeonTier2Keywords != null) ? DungeonTier2Keywords.Value : "")) { return 2; } return 1; } private static float EffectiveDungeonResetRadius() { if (DungeonResetRadius == null || !(DungeonResetRadius.Value > 1f)) { return 45f; } return DungeonResetRadius.Value; } private static float EffectiveDungeonCooldownMinutes() { if (DungeonResetCooldownMinutes == null || !(DungeonResetCooldownMinutes.Value > 0f)) { return 120f; } return DungeonResetCooldownMinutes.Value; } private static float EffectiveResetGuardianCooldownMinutes() { if (ResetGuardianCooldownMinutes == null || !(ResetGuardianCooldownMinutes.Value > 0f)) { return 360f; } return ResetGuardianCooldownMinutes.Value; } private static float EffectiveConfirmSeconds() { if (ResetGuardianConfirmSeconds == null || !(ResetGuardianConfirmSeconds.Value > 1f)) { return 10f; } return ResetGuardianConfirmSeconds.Value; } private static float EffectiveScanSeconds() { if (ResetScanSeconds == null || !(ResetScanSeconds.Value > 1f)) { return 10f; } return ResetScanSeconds.Value; } private static bool HasComponent(GameObject go, Type componentType) { if ((Object)(object)go == (Object)null || componentType == null) { return false; } try { return (Object)(object)go.GetComponent(componentType) != (Object)null; } catch { return false; } } private static bool HasComponentInParent(GameObject go, Type componentType) { if ((Object)(object)go == (Object)null || componentType == null) { return false; } try { return (Object)(object)go.GetComponentInParent(componentType) != (Object)null; } catch { return false; } } private static Type ResolveTypeSilently(string typeName) { if (string.IsNullOrWhiteSpace(typeName)) { return null; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { Type type = assembly.GetType(typeName, throwOnError: false, ignoreCase: false); if (type != null) { return type; } } catch { } } return null; } private static bool ContainsAny(string haystack, string csv) { if (string.IsNullOrWhiteSpace(haystack)) { return false; } foreach (string item in SplitCsvRaw(csv)) { if (!string.IsNullOrWhiteSpace(item) && haystack.IndexOf(Plugin.NormalizeKey(item), StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } private static IEnumerable SplitCsvRaw(string input) { return from part in (input ?? string.Empty).Split(new char[1] { ',' }) select part.Trim() into part where part.Length > 0 select part; } private static string NameChain(GameObject go) { StringBuilder stringBuilder = new StringBuilder(); Transform val = (((Object)(object)go != (Object)null) ? go.transform : null); int num = 0; while ((Object)(object)val != (Object)null && num++ < 5) { stringBuilder.Append(' ').Append(Plugin.NormalizeKey(((Object)val).name)); val = val.parent; } return stringBuilder.ToString(); } private static string ObjectKey(GameObject go) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)go == (Object)null) { return string.Empty; } return Plugin.NormalizeKey(((Object)go).name) + "@" + Mathf.RoundToInt(go.transform.position.x) + ":" + Mathf.RoundToInt(go.transform.position.z); } private static string DungeonKey(GameObject go) { return ObjectKey(go); } private static string ReadZdoString(Component component, string key) { try { ZNetView val = (((Object)(object)component != (Object)null) ? component.GetComponent() : null); object obj = (((Object)(object)val != (Object)null) ? val.GetZDO() : null); if (obj == null) { return string.Empty; } MethodInfo methodInfo = obj.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "GetString" && m.GetParameters().Length >= 1 && m.GetParameters()[0].ParameterType == typeof(string)); if (methodInfo == null) { return string.Empty; } object obj2 = ((methodInfo.GetParameters().Length >= 2) ? methodInfo.Invoke(obj, new object[2] { key, string.Empty }) : methodInfo.Invoke(obj, new object[1] { key })); return (obj2 != null) ? Convert.ToString(obj2) : string.Empty; } catch { return string.Empty; } } private static void WriteZdoString(Component component, string key, string value) { try { ZNetView val = (((Object)(object)component != (Object)null) ? component.GetComponent() : null); object obj = (((Object)(object)val != (Object)null) ? val.GetZDO() : null); obj?.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Set" && m.GetParameters().Length == 2 && m.GetParameters()[0].ParameterType == typeof(string) && m.GetParameters()[1].ParameterType == typeof(string))?.Invoke(obj, new object[2] { key, value ?? string.Empty }); } catch { } } } internal static class TombstoneMapFeature { private const string PendingPointsKey = "ChallengeHub.Tombstone.PendingExploration.v1"; private const string OwnerIdKey = "ChallengeHub.Tombstone.MapOwnerId"; private const string ForeignReadersKey = "ChallengeHub.Tombstone.ForeignMapReaders.v1"; private const float AttachRadius = 30f; private const int PendingFileVersion = 2; private static ConfigEntry EnableFeature; private static byte[] _pendingPoints; private static int _pendingPointCount; private static long _pendingOwnerId; private static Vector3 _pendingDeathPosition; private static string _pendingWorldToken = string.Empty; private static bool _initialized; private static readonly HashSet KnownOwnTombstones = new HashSet(); private static string PendingFilePath => Path.Combine(Paths.ConfigPath, "ChallengeHubValheim.pending-tombstone-points.bin"); internal static void Initialize(Plugin owner) { if (!_initialized && !((Object)(object)owner == (Object)null)) { EnableFeature = ((BaseUnityPlugin)owner).Config.Bind("TombstoneMap", "EnableTombstoneMapKnowledge", true, "Beim Tod noch offene Kartografie-Punkte in den eigenen Grabstein verschieben. Dauerhafte Karte bleibt bis zum Kartentisch unveraendert."); LoadPendingFromDisk(); _initialized = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"ChallengeHub Grabstein-Punktpuffer 2.2.31 initialisiert; nur eigene Grabsteine, kein direkter Karten-Commit."); } } } internal static void Patch(Harmony harmony) { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Expected O, but got Unknown //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Expected O, but got Unknown //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Expected O, but got Unknown //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Expected O, but got Unknown if (harmony == null) { return; } MethodInfo methodInfo = AccessTools.Method(typeof(TombstoneMapFeature), "PlayerOnDeathPrefix", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(TombstoneMapFeature), "TombstoneSpawnPostfix", (Type[])null, (Type[])null); MethodInfo methodInfo3 = AccessTools.Method(typeof(TombstoneMapFeature), "TombstoneInteractPrefix", (Type[])null, (Type[])null); MethodInfo methodInfo4 = AccessTools.Method(typeof(TombstoneMapFeature), "TombstoneHoverPostfix", (Type[])null, (Type[])null); int num = 0; foreach (MethodInfo item in from method in typeof(Player).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where string.Equals(method.Name, "OnDeath", StringComparison.Ordinal) select method) { harmony.Patch((MethodBase)item, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; } int num2 = 0; string[] spawnNames = new string[4] { "Start", "Awake", "Setup", "SetOwner" }; foreach (MethodInfo item2 in from method in typeof(TombStone).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where spawnNames.Contains(method.Name, StringComparer.Ordinal) select method) { harmony.Patch((MethodBase)item2, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num2++; } int num3 = 0; foreach (MethodInfo item3 in from method in typeof(TombStone).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where string.Equals(method.Name, "Interact", StringComparison.Ordinal) select method) { harmony.Patch((MethodBase)item3, new HarmonyMethod(methodInfo3), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num3++; } int num4 = 0; foreach (MethodInfo item4 in from method in typeof(TombStone).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where string.Equals(method.Name, "GetHoverText", StringComparison.Ordinal) && method.ReturnType == typeof(string) select method) { harmony.Patch((MethodBase)item4, (HarmonyMethod)null, new HarmonyMethod(methodInfo4), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num4++; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Grabstein-Punktpuffer Patches: Tod=" + num + ", Spawn=" + num2 + ", Interaktion=" + num3 + ", Hover=" + num4 + ".")); } } private static void PlayerOnDeathPrefix(Player __instance) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) try { if (!IsEnabled() || (Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } int count; byte[] array = CartographyMapModeFeature.TakePendingForTombstone(__instance, out count); if (array == null || array.Length == 0 || count <= 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Tod: keine offenen Erkundungspunkte fuer den Grabstein vorhanden."); } } else { _pendingPoints = array; _pendingPointCount = count; _pendingOwnerId = __instance.GetPlayerID(); _pendingDeathPosition = ((Component)__instance).transform.position; _pendingWorldToken = CartographyMapModeFeature.CurrentWorldStorageToken(); SavePendingToDisk(); ShowMessage("Du bist gestorben.\n" + count + " offene Erkundungspunkte ruhen in deinem Grabstein."); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Grabstein-Punktpuffer konnte beim Tod nicht gesichert werden: " + ex.Message)); } } } private static void TombstoneSpawnPostfix(TombStone __instance, object[] __args) { TryAttachPendingPoints(__instance, __args); RegisterKnownOwnTombstone(__instance, __args); } private static bool TombstoneInteractPrefix(TombStone __instance, object[] __args) { try { if (!IsEnabled() || (Object)(object)__instance == (Object)null) { return true; } TryAttachPendingPoints(__instance, __args); Player val = FindInteractingPlayer(__args); if ((Object)(object)val == (Object)null) { return true; } if (!IsOwnedByPlayer(__instance, val)) { if ((Object)(object)val == (Object)(object)Player.m_localPlayer) { TryRecoverForeignMapKnowledge(__instance, val); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Fremder Grabsteinzugriff blockiert: Spieler=" + SafePlayerName(val) + "; Grabstein=" + SafeOwnerName(__instance) + ".")); } return false; } if ((Object)(object)val != (Object)(object)Player.m_localPlayer) { return true; } RegisterKnownOwnTombstone(__instance, __args); WarnBeforeRecoveryIfNeeded(__instance, val); ZNetView component = ((Component)__instance).GetComponent(); object obj = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); if (obj == null) { return true; } byte[] array = ReadZdoBytes(obj, "ChallengeHub.Tombstone.PendingExploration.v1"); if (array == null || array.Length == 0) { return true; } long num = ReadZdoLong(obj, "ChallengeHub.Tombstone.MapOwnerId"); if (num == 0L || num != val.GetPlayerID()) { ShowMessage("Nur der Besitzer kann diese Erkundungspunkte bergen."); return false; } if (!component.IsOwner()) { try { component.ClaimOwnership(); } catch { } } if (!component.IsOwner()) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"Eigener Grabstein konnte nicht fuer den atomaren Punkt-Import uebernommen werden."); } return true; } int num2 = CartographyMapModeFeature.CountSerializedPoints(array); if (num2 <= 0) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"Eigener Grabstein enthaelt einen ungueltigen Punkt-Puffer."); } return true; } if (!WriteZdoBytes(obj, "ChallengeHub.Tombstone.PendingExploration.v1", Array.Empty()) || !IsZdoPayloadEmpty(obj, "ChallengeHub.Tombstone.PendingExploration.v1")) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)"Eigener Grabstein konnte vor dem Punkt-Import nicht atomar geleert werden."); } return true; } int num3 = CartographyMapModeFeature.ImportPendingFromTombstone(array); WriteZdoLong(obj, "ChallengeHub.Tombstone.MapOwnerId", 0L); ShowMessage(num2 + " Erkundungspunkte geborgen.\nDauerhafte Karte bleibt bis zum Kartentisch unveraendert."); ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)("Eigener Grabstein gelesen: importiert=" + num2 + "; neu_im_RAM=" + num3 + "; dauerhafte Karte unveraendert.")); } } catch (Exception ex) { ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogWarning((object)("Grabstein-Punktpuffer Interaktion fehlgeschlagen: " + ex.Message)); } } return true; } private static void TryRecoverForeignMapKnowledge(TombStone tombstone, Player player) { //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) ZNetView val = (((Object)(object)tombstone != (Object)null) ? ((Component)tombstone).GetComponent() : null); object obj = (((Object)(object)val != (Object)null) ? val.GetZDO() : null); byte[] array = ((obj != null) ? ReadZdoBytes(obj, "ChallengeHub.Tombstone.PendingExploration.v1") : null); long num = ((obj != null) ? ReadZdoLong(obj, "ChallengeHub.Tombstone.MapOwnerId") : 0); if (array == null || array.Length == 0 || num == 0L) { ShowMessage("Fremder Grabstein gesperrt.\nKeine Kartendaten zum Bergen vorhanden."); return; } if (!val.IsOwner()) { try { val.ClaimOwnership(); } catch { } } if (!val.IsOwner()) { ShowMessage("Kartendaten konnten noch nicht sicher geborgen werden."); return; } string text = "|" + player.GetPlayerID() + "|"; string text2 = ReadZdoString(obj, "ChallengeHub.Tombstone.ForeignMapReaders.v1"); if (text2.IndexOf(text, StringComparison.Ordinal) >= 0) { ShowMessage("Du hast diese Kartendaten bereits gelesen.\nGegenstaende bleiben gesperrt."); return; } string value = text2 + text; if (!InvokeZdoSet(obj, "ChallengeHub.Tombstone.ForeignMapReaders.v1", value, typeof(string)) || ReadZdoString(obj, "ChallengeHub.Tombstone.ForeignMapReaders.v1").IndexOf(text, StringComparison.Ordinal) < 0) { ShowMessage("Kartendaten konnten noch nicht sicher geborgen werden."); return; } int num2 = CartographyMapModeFeature.CountSerializedPoints(array); int num3 = CartographyMapModeFeature.ImportPendingFromTombstone(array); int num4 = Math.Max(0, Plugin.CartographyPartnerShareBasePointsValue()); Vector3 position = ((Component)tombstone).transform.position; string value2 = num + ":" + Mathf.RoundToInt(position.x) + ":" + Mathf.RoundToInt(position.y) + ":" + Mathf.RoundToInt(position.z); if (Plugin.ShouldReportCartographyPartnerShareEvents()) { Plugin.Instance?.SendEvent("tombstone_map_recovered", player, new Dictionary { { "tombstoneId", value2 }, { "sourceOwnerId", num.ToString() }, { "mapPointCount", num2 }, { "points", num4 }, { "label", "Kartendaten aus fremdem Grabstein geborgen" } }); } ShowMessage(num2 + " Kartendaten geborgen. +" + num4 + " Punkte.\nGegenstaende bleiben gesperrt."); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Fremde Grabsteinkarte geborgen: Punkte=" + num2 + "; neu_im_RAM=" + num3 + "; Wertung=" + num4 + "; Original fuer Besitzer erhalten.")); } } private static void TombstoneHoverPostfix(TombStone __instance, ref string __result) { TalentData localData = TalentStore.GetLocalData(); Player localPlayer = Player.m_localPlayer; if (localData != null && !((Object)(object)localPlayer == (Object)null) && !((Object)(object)__instance == (Object)null) && IsOwnedByLocalPlayer(__instance)) { RegisterKnownOwnTombstone(__instance, null); if (localData.HasSkill("recovery.grave_contents")) { Container val = ((Component)__instance).GetComponent() ?? ((Component)__instance).GetComponentInChildren(); Inventory val2 = (((Object)(object)val != (Object)null) ? val.GetInventory() : null); int num = ((val2 != null) ? val2.NrOfItems() : 0); float num2 = ((val2 != null) ? val2.GetTotalWeight() : 0f); int num3 = ReadPendingPointCount(__instance); __result = __result + "\n" + num + " Gegenstandsstapel | Gewicht " + num2.ToString("0.0") + " | Erkundungspunkte " + num3 + ""; } } } private static void WarnBeforeRecoveryIfNeeded(TombStone tombstone, Player player) { TalentData localData = TalentStore.GetLocalData(); if (localData == null || (Object)(object)player == (Object)null || (Object)(object)tombstone == (Object)null || !localData.HasSkill("recovery.safe_recovery") || !IsOwnedByLocalPlayer(tombstone)) { return; } Container val = ((Component)tombstone).GetComponent() ?? ((Component)tombstone).GetComponentInChildren(); Inventory val2 = (((Object)(object)val != (Object)null) ? val.GetInventory() : null); Inventory inventory = ((Humanoid)player).GetInventory(); if (val2 != null && inventory != null) { float num = inventory.GetTotalWeight() + val2.GetTotalWeight(); int num2 = val2.NrOfItems(); int num3 = Math.Max(0, InventoryReflection.Width(inventory) * InventoryReflection.Height(inventory) - inventory.NrOfItems()); if (num > player.GetMaxCarryWeight() || num2 > num3) { ShowMessage("Sichere Bergung: Inventar oder Tragkraft reicht möglicherweise nicht aus.\nFreie Plätze: " + num3 + " | Grabsteinstapel: " + num2 + " | Gewicht danach: " + num.ToString("0.0")); } } } internal static bool TryGetNearestOwnTombstone(Vector3 origin, out Vector3 position, out float distance) { //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_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; distance = float.MaxValue; KnownOwnTombstones.RemoveWhere((TombStone item) => (Object)(object)item == (Object)null); foreach (TombStone knownOwnTombstone in KnownOwnTombstones) { if (!((Object)(object)knownOwnTombstone == (Object)null) && IsOwnedByLocalPlayer(knownOwnTombstone)) { float num = Vector3.Distance(origin, ((Component)knownOwnTombstone).transform.position); if (!(num >= distance)) { distance = num; position = ((Component)knownOwnTombstone).transform.position; } } } return distance < float.MaxValue; } private static void RegisterKnownOwnTombstone(TombStone tombstone, object[] args) { if (!((Object)(object)tombstone == (Object)null) && (IsOwnedByLocalPlayer(tombstone) || MatchesLocalOwnerArguments(args))) { KnownOwnTombstones.Add(tombstone); } } private static bool MatchesLocalOwnerArguments(object[] args) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || args == null) { return false; } long num = TalentStore.SafeGetPlayerId(localPlayer); string text = string.Empty; try { text = localPlayer.GetPlayerName(); } catch { } foreach (object obj2 in args) { if (obj2 is long num2 && num2 == num) { return true; } if (obj2 is string a && !string.IsNullOrWhiteSpace(text) && string.Equals(a, text, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private static bool IsOwnedByLocalPlayer(TombStone tombstone) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { return IsOwnedByPlayer(tombstone, localPlayer); } return false; } private static bool IsOwnedByPlayer(TombStone tombstone, Player player) { if ((Object)(object)tombstone == (Object)null || (Object)(object)player == (Object)null) { return false; } long num = TalentStore.SafeGetPlayerId(player); string text = string.Empty; try { text = player.GetPlayerName(); } catch { } try { MethodInfo methodInfo = AccessTools.Method(((object)tombstone).GetType(), "GetOwner", (Type[])null, (Type[])null); if (methodInfo != null) { long num2 = Convert.ToInt64(methodInfo.Invoke(tombstone, Array.Empty())); if (num2 != 0L) { return num2 == num; } } } catch { } ZNetView component = ((Component)tombstone).GetComponent(); object obj3 = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); if (obj3 == null) { return false; } long num3 = ReadZdoLong(obj3, "ChallengeHub.Tombstone.MapOwnerId"); if (num3 != 0L) { return num3 == num; } try { string ownerName = tombstone.GetOwnerName(); if (!string.IsNullOrWhiteSpace(ownerName) && !string.IsNullOrWhiteSpace(text)) { return string.Equals(ownerName, text, StringComparison.OrdinalIgnoreCase); } } catch { } return false; } private static string SafePlayerName(Player player) { try { return ((Object)(object)player != (Object)null) ? (player.GetPlayerName() ?? "unbekannt") : "unbekannt"; } catch { return "unbekannt"; } } private static string SafeOwnerName(TombStone tombstone) { try { return ((Object)(object)tombstone != (Object)null) ? (tombstone.GetOwnerName() ?? "unbekannt") : "unbekannt"; } catch { return "unbekannt"; } } private static int ReadPendingPointCount(TombStone tombstone) { ZNetView val = (((Object)(object)tombstone != (Object)null) ? ((Component)tombstone).GetComponent() : null); byte[] array = ReadZdoBytes(((Object)(object)val != (Object)null) ? val.GetZDO() : null, "ChallengeHub.Tombstone.PendingExploration.v1"); if (array == null || array.Length == 0) { return 0; } return Math.Max(0, CartographyMapModeFeature.CountSerializedPoints(array)); } private static void TryAttachPendingPoints(TombStone tombstone, object[] lifecycleArgs) { //IL_006e: 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) try { if (!IsEnabled() || (Object)(object)tombstone == (Object)null) { return; } if ((_pendingPoints == null || _pendingPoints.Length == 0) && File.Exists(PendingFilePath)) { LoadPendingFromDisk(); } if (_pendingPoints == null || _pendingPoints.Length == 0 || _pendingPointCount <= 0 || !string.Equals(_pendingWorldToken, CartographyMapModeFeature.CurrentWorldStorageToken(), StringComparison.Ordinal) || Vector3.Distance(((Component)tombstone).transform.position, _pendingDeathPosition) > 30f || !MatchesPendingOwner(tombstone, lifecycleArgs, _pendingOwnerId)) { return; } ZNetView component = ((Component)tombstone).GetComponent(); object obj = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); if ((Object)(object)component == (Object)null || obj == null || !component.IsValid()) { return; } byte[] array = ReadZdoBytes(obj, "ChallengeHub.Tombstone.PendingExploration.v1"); if (array != null && array.Length != 0) { if (ReadZdoLong(obj, "ChallengeHub.Tombstone.MapOwnerId") == _pendingOwnerId && array.SequenceEqual(_pendingPoints)) { ClearPending(); } return; } if (!component.IsOwner()) { try { component.ClaimOwnership(); } catch { } } if (!component.IsOwner()) { return; } if (!WriteZdoLong(obj, "ChallengeHub.Tombstone.MapOwnerId", _pendingOwnerId) || ReadZdoLong(obj, "ChallengeHub.Tombstone.MapOwnerId") != _pendingOwnerId) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)"Grabstein-Besitzer konnte noch nicht persistent geschrieben werden."); } return; } if (!WriteZdoBytes(obj, "ChallengeHub.Tombstone.PendingExploration.v1", _pendingPoints)) { WriteZdoLong(obj, "ChallengeHub.Tombstone.MapOwnerId", 0L); return; } byte[] array2 = ReadZdoBytes(obj, "ChallengeHub.Tombstone.PendingExploration.v1"); if (array2 == null || !array2.SequenceEqual(_pendingPoints)) { WriteZdoLong(obj, "ChallengeHub.Tombstone.MapOwnerId", 0L); return; } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Tod: " + _pendingPointCount + " gepufferte Erkundungspunkte im eigenen Grabstein gespeichert.")); } ClearPending(); } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogDebug((object)("Grabstein-Punktpuffer konnte noch nicht angehaengt werden: " + ex.Message)); } } } private static bool MatchesPendingOwner(TombStone tombstone, object[] lifecycleArgs, long expectedOwnerId) { if (expectedOwnerId == 0L) { return false; } string text = string.Empty; try { text = (((Object)(object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetPlayerName() : string.Empty); } catch { } if (lifecycleArgs != null) { bool flag = false; bool flag2 = false; foreach (object obj2 in lifecycleArgs) { if (obj2 is long num && num != 0L) { flag = true; if (num == expectedOwnerId) { return true; } } else if (obj2 is string text2 && !string.IsNullOrWhiteSpace(text2) && !string.IsNullOrWhiteSpace(text)) { flag2 = true; if (string.Equals(text2, text, StringComparison.OrdinalIgnoreCase)) { return true; } } } if (flag || flag2) { return false; } } ZNetView val = (((Object)(object)tombstone != (Object)null) ? ((Component)tombstone).GetComponent() : null); object obj3 = (((Object)(object)val != (Object)null) ? val.GetZDO() : null); if (obj3 != null) { string[] array = new string[6] { "playerID", "playerId", "PlayerID", "ownerID", "ownerId", "OwnerID" }; foreach (string key in array) { long num2 = ReadZdoLong(obj3, key); if (num2 != 0L) { return num2 == expectedOwnerId; } } if (!string.IsNullOrWhiteSpace(text)) { array = new string[4] { "owner", "ownerName", "playerName", "OwnerName" }; foreach (string key2 in array) { string text3 = ReadZdoString(obj3, key2); if (!string.IsNullOrWhiteSpace(text3)) { return string.Equals(text3, text, StringComparison.OrdinalIgnoreCase); } } } } return false; } private static Player FindInteractingPlayer(object[] args) { if (args != null) { foreach (object obj in args) { Player val = (Player)((obj is Player) ? obj : null); if (val != null) { return val; } Humanoid val2 = (Humanoid)((obj is Humanoid) ? obj : null); if (val2 != null) { Player val3 = (Player)(object)((val2 is Player) ? val2 : null); if (val3 != null) { return val3; } } } } return Player.m_localPlayer; } private static object[] BuildDefaultArguments(ParameterInfo[] parameters) { object[] array = new object[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { if (parameters[i].HasDefaultValue) { array[i] = parameters[i].DefaultValue; } else if (parameters[i].ParameterType.IsValueType) { array[i] = Activator.CreateInstance(parameters[i].ParameterType); } else { array[i] = null; } } return array; } private static byte[] ReadZdoBytes(object zdo, string key) { if (zdo == null) { return null; } foreach (MethodInfo item in from method in zdo.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where (string.Equals(method.Name, "GetByteArray", StringComparison.Ordinal) || string.Equals(method.Name, "GetBytes", StringComparison.Ordinal)) && method.ReturnType == typeof(byte[]) select method) { ParameterInfo[] parameters = item.GetParameters(); if (parameters.Length != 0 && !(parameters[0].ParameterType != typeof(string))) { try { object[] array = BuildDefaultArguments(parameters); array[0] = key; return item.Invoke(zdo, array) as byte[]; } catch { } } } return null; } private static bool WriteZdoBytes(object zdo, string key, byte[] value) { return InvokeZdoSet(zdo, key, value ?? Array.Empty(), typeof(byte[])); } private static bool IsZdoPayloadEmpty(object zdo, string key) { byte[] array = ReadZdoBytes(zdo, key); if (array != null) { return array.Length == 0; } return true; } private static long ReadZdoLong(object zdo, string key) { if (zdo == null) { return 0L; } foreach (MethodInfo item in from method in zdo.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where string.Equals(method.Name, "GetLong", StringComparison.Ordinal) && method.ReturnType == typeof(long) select method) { ParameterInfo[] parameters = item.GetParameters(); if (parameters.Length != 0 && !(parameters[0].ParameterType != typeof(string))) { try { object[] array = BuildDefaultArguments(parameters); array[0] = key; return Convert.ToInt64(item.Invoke(zdo, array)); } catch { } } } return 0L; } private static string ReadZdoString(object zdo, string key) { if (zdo == null) { return string.Empty; } foreach (MethodInfo item in from candidate in zdo.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where string.Equals(candidate.Name, "GetString", StringComparison.Ordinal) && candidate.ReturnType == typeof(string) select candidate) { ParameterInfo[] parameters = item.GetParameters(); if (parameters.Length != 0 && !(parameters[0].ParameterType != typeof(string))) { try { object[] array = BuildDefaultArguments(parameters); array[0] = key; return Convert.ToString(item.Invoke(zdo, array)) ?? string.Empty; } catch { } } } return string.Empty; } private static bool WriteZdoLong(object zdo, string key, long value) { return InvokeZdoSet(zdo, key, value, typeof(long)); } private static bool InvokeZdoSet(object zdo, string key, object value, Type valueType) { if (zdo == null) { return false; } MethodInfo methodInfo = zdo.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo candidate) => string.Equals(candidate.Name, "Set", StringComparison.Ordinal) && candidate.GetParameters().Length == 2 && candidate.GetParameters()[0].ParameterType == typeof(string) && candidate.GetParameters()[1].ParameterType == valueType); if (methodInfo == null) { return false; } try { methodInfo.Invoke(zdo, new object[2] { key, value }); return true; } catch { return false; } } private static void SavePendingToDisk() { string text = PendingFilePath + ".tmp"; try { Directory.CreateDirectory(Path.GetDirectoryName(PendingFilePath)); using (BinaryWriter binaryWriter = new BinaryWriter(File.Open(text, FileMode.Create, FileAccess.Write, FileShare.None))) { binaryWriter.Write(2); binaryWriter.Write(_pendingWorldToken ?? string.Empty); binaryWriter.Write(_pendingOwnerId); binaryWriter.Write(_pendingDeathPosition.x); binaryWriter.Write(_pendingDeathPosition.y); binaryWriter.Write(_pendingDeathPosition.z); binaryWriter.Write(_pendingPointCount); byte[] pendingPoints = _pendingPoints; binaryWriter.Write((pendingPoints != null) ? pendingPoints.Length : 0); if (_pendingPoints != null) { binaryWriter.Write(_pendingPoints); } binaryWriter.Flush(); } if (File.Exists(PendingFilePath)) { File.Delete(PendingFilePath); } File.Move(text, PendingFilePath); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Grabstein-Punktpuffer konnte nicht lokal gesichert werden: " + ex.Message)); } } finally { try { if (File.Exists(text)) { File.Delete(text); } } catch { } } } private static void LoadPendingFromDisk() { //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) try { if (!File.Exists(PendingFilePath)) { return; } using BinaryReader binaryReader = new BinaryReader(File.Open(PendingFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)); int num = binaryReader.ReadInt32(); if (num != 2) { throw new InvalidDataException("Unbekannte Dateiversion " + num + "."); } _pendingWorldToken = binaryReader.ReadString(); _pendingOwnerId = binaryReader.ReadInt64(); _pendingDeathPosition = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()); _pendingPointCount = binaryReader.ReadInt32(); int num2 = binaryReader.ReadInt32(); if (num2 <= 0 || num2 > 131072) { throw new InvalidDataException("Ungueltige Payload-Laenge " + num2 + "."); } _pendingPoints = binaryReader.ReadBytes(num2); if (_pendingPoints.Length != num2 || _pendingPointCount <= 0 || CartographyMapModeFeature.CountSerializedPoints(_pendingPoints) != _pendingPointCount) { throw new InvalidDataException("Punktanzahl oder Payload ist unvollstaendig."); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Lokale Grabstein-Punktsicherung konnte nicht geladen werden und wurde verworfen: " + ex.Message)); } ClearPending(); } } private static void ClearPending() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) _pendingPoints = null; _pendingPointCount = 0; _pendingOwnerId = 0L; _pendingDeathPosition = Vector3.zero; _pendingWorldToken = string.Empty; try { if (File.Exists(PendingFilePath)) { File.Delete(PendingFilePath); } } catch { } } private static bool IsEnabled() { if (_initialized) { if (EnableFeature != null) { return EnableFeature.Value; } return true; } return false; } private static void ShowMessage(string message) { try { if ((Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)2, message, 0, (Sprite)null, false); return; } Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, message, 0, (Sprite)null); } } catch { } } } internal static class TradeChestUiRules { private const string PartnershipIdKey = "ChallengeHub.Trade.PartnershipId"; private const string OwnerIdKey = "ChallengeHub.Trade.OwnerId"; private const string MirrorKey = "ChallengeHub.Trade.Mirror"; internal const int GiveRows = 2; internal const int TakeStartRow = 2; internal const int TotalRows = 4; internal static bool IsBound(Container container) { return !string.IsNullOrWhiteSpace(Read(container, "ChallengeHub.Trade.PartnershipId")); } internal static bool IsOwner(Container container, Player player) { if (!IsBound(container) || (Object)(object)player == (Object)null) { return true; } return Read(container, "ChallengeHub.Trade.OwnerId") == player.GetPlayerID().ToString(); } internal static bool IsMirror(ItemData item) { if (item != null && item.m_customData != null && item.m_customData.TryGetValue("ChallengeHub.Trade.Mirror", out var value)) { return value == "1"; } return false; } internal static Container CurrentContainer(InventoryGui gui) { object? obj = AccessTools.Field(typeof(InventoryGui), "m_currentContainer")?.GetValue(gui); return (Container)((obj is Container) ? obj : null); } internal static void EnsureLayout(Container container) { if (!IsBound(container)) { return; } try { Inventory inventory = container.GetInventory(); FieldInfo fieldInfo = AccessTools.Field(((object)inventory).GetType(), "m_height"); if (fieldInfo != null && Convert.ToInt32(fieldInfo.GetValue(inventory)) != 4) { fieldInfo.SetValue(inventory, 4); AccessTools.Method(((object)inventory).GetType(), "Changed", (Type[])null, (Type[])null)?.Invoke(inventory, null); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Handelskisten-Layoutgroesse fehlgeschlagen: " + ex.Message)); } } } private static string Read(Container container, string key) { try { ZNetView val = (((Object)(object)container != (Object)null) ? ((Component)container).GetComponent() : null); return ((Object)(object)val != (Object)null && val.GetZDO() != null) ? val.GetZDO().GetString(key, string.Empty) : string.Empty; } catch { return string.Empty; } } } [HarmonyPatch(typeof(Container), "Interact")] internal static class TradeChestOwnerOpenPatch { private static bool Prefix(Container __instance, Humanoid character, ref bool __result) { Player val = (Player)(object)((character is Player) ? character : null); if (!TradeChestUiRules.IsBound(__instance) || TradeChestUiRules.IsOwner(__instance, val)) { return true; } __result = false; try { if (val != null) { ((Character)val).Message((MessageType)2, "Diese Handelskiste gehoert deinem Partner. Benutze deine eigene Handelskiste.", 0, (Sprite)null); } } catch { } return false; } } [HarmonyPatch(typeof(InventoryGui), "OnTakeAll")] internal static class TradeChestTakeAllPatch { private static bool Prefix(InventoryGui __instance) { if (!TradeChestUiRules.IsBound(TradeChestUiRules.CurrentContainer(__instance))) { return true; } try { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, "Bei Handelskisten ist 'Alles nehmen' gesperrt. Entnimm Partnerware einzeln aus der unteren Reihe.", 0, (Sprite)null); } } catch { } return false; } } [HarmonyPatch(typeof(InventoryGui), "Show")] internal static class TradeChestTitlePatch { private static void Postfix(InventoryGui __instance, Container container) { if (!TradeChestUiRules.IsBound(container)) { TradeChestLayoutVisual.Reset(__instance); return; } try { TradeChestUiRules.EnsureLayout(container); object obj = AccessTools.Field(typeof(InventoryGui), "m_containerName")?.GetValue(__instance); PropertyInfo propertyInfo = obj?.GetType().GetProperty("text"); if (propertyInfo != null && propertyInfo.CanWrite) { propertyInfo.SetValue(obj, "Handelskiste | GEBEN ↓ → NEHMEN", null); } TradeChestLayoutVisual.Ensure(__instance); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, "Handelskiste aktiv: zwei gruene Reihen geben, zwei blaue Reihen nehmen.", 0, (Sprite)null); } PartnershipGameplayFeature.SynchronizeOpenedTradeChest(container); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Handelskisten-Titel fehlgeschlagen: " + ex.Message)); } } } } [HarmonyPatch(typeof(InventoryGui), "Hide")] internal static class TradeChestCloseSyncPatch { private static void Prefix(InventoryGui __instance) { Container val = TradeChestUiRules.CurrentContainer(__instance); if (TradeChestUiRules.IsBound(val)) { PartnershipGameplayFeature.PublishClosedTradeChest(val); } } } [HarmonyPatch(typeof(InventoryGui), "OnSelectedItem")] internal static class TradeChestRowAccessPatch { private static bool Prefix(InventoryGui __instance, InventoryGrid grid, ItemData item, Vector2i pos) { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) Container val = TradeChestUiRules.CurrentContainer(__instance); if (!TradeChestUiRules.IsBound(val)) { return true; } object? obj = AccessTools.Field(typeof(InventoryGui), "m_containerGrid")?.GetValue(__instance); InventoryGrid val2 = (InventoryGrid)((obj is InventoryGrid) ? obj : null); if ((Object)(object)grid != (Object)(object)val2) { return true; } Inventory inventory = val.GetInventory(); object? obj2 = AccessTools.Field(typeof(InventoryGui), "m_dragInventory")?.GetValue(__instance); Inventory val3 = (Inventory)((obj2 is Inventory) ? obj2 : null); object? obj3 = AccessTools.Field(typeof(InventoryGui), "m_dragItem")?.GetValue(__instance); ItemData val4 = (ItemData)((obj3 is ItemData) ? obj3 : null); if (val4 == null) { return true; } bool flag = val3 == inventory; if (!flag && pos.y >= 2) { Message("In den blauen Nehmen-Bereich kann nichts hineingelegt werden."); return false; } if (flag && TradeChestUiRules.IsMirror(val4) && pos.y < 2) { Message("Partnerware kann nur entnommen, nicht in die Angebotsreihe verschoben werden."); return false; } if (flag && !TradeChestUiRules.IsMirror(val4) && pos.y >= 2) { Message("Eigene Angebote bleiben in den beiden gruene Geben-Reihen."); return false; } return true; } private static void Message(string text) { try { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null); } } catch { } } } [HarmonyPatch] internal static class TradeChestRowColorPatch { private static IEnumerable TargetMethods() { return from method in AccessTools.GetDeclaredMethods(typeof(InventoryGrid)) where method.Name == "UpdateInventory" select method; } private static void Postfix(InventoryGrid __instance) { //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) try { InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance == (Object)null) { return; } object? obj = AccessTools.Field(typeof(InventoryGui), "m_containerGrid")?.GetValue(instance); InventoryGrid val = (InventoryGrid)((obj is InventoryGrid) ? obj : null); Container container = TradeChestUiRules.CurrentContainer(instance); if ((Object)(object)__instance != (Object)(object)val) { return; } if (!TradeChestUiRules.IsBound(container)) { TradeChestLayoutVisual.Reset(instance); } else { if (!(AccessTools.Field(typeof(InventoryGrid), "m_elements")?.GetValue(__instance) is IEnumerable enumerable)) { return; } { foreach (object item in enumerable) { Vector2i val2 = (Vector2i)AccessTools.Field(item.GetType(), "m_pos").GetValue(item); object? value = AccessTools.Field(item.GetType(), "m_go").GetValue(item); GameObject val3 = (GameObject)((value is GameObject) ? value : null); Image val4 = (((Object)(object)val3 != (Object)null) ? val3.GetComponent() : null); if ((Object)(object)val4 == (Object)null && (Object)(object)val3 != (Object)null) { val4 = ((IEnumerable)val3.GetComponentsInChildren(true)).FirstOrDefault((Func)((Image image) => (Object)(object)image != (Object)null && !((Object)image).name.ToLowerInvariant().Contains("icon"))); } if ((Object)(object)val4 == (Object)null) { continue; } TradeChestLayoutVisual.RememberSlot(val3, val4); ((Graphic)val4).color = ((val2.y < 2) ? new Color(0.2f, 0.62f, 0.36f, 0.82f) : new Color(0.25f, 0.48f, 0.82f, 0.82f)); if (val2.y >= 2 && (Object)(object)val3 != (Object)null) { Transform transform = val3.transform; RectTransform val5 = (RectTransform)(object)((transform is RectTransform) ? transform : null); if ((Object)(object)val5 != (Object)null && !((Object)val3).name.EndsWith("_CHTradeShift", StringComparison.Ordinal)) { val5.anchoredPosition += new Vector2(0f, -18f); ((Object)val3).name = ((Object)val3).name + "_CHTradeShift"; } } } return; } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Handelskisten-Reihenfarbe fehlgeschlagen: " + ex.Message)); } } } } internal static class TradeChestLayoutVisual { private static readonly Dictionary OriginalColors = new Dictionary(); private static readonly Dictionary OriginalPositions = new Dictionary(); internal static void RememberSlot(GameObject go, Image image) { //IL_002e: 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) if (!((Object)(object)go == (Object)null) && !((Object)(object)image == (Object)null)) { int instanceID = ((Object)go).GetInstanceID(); if (!OriginalColors.ContainsKey(instanceID)) { OriginalColors[instanceID] = ((Graphic)image).color; } Transform transform = go.transform; RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null); if ((Object)(object)val != (Object)null && !OriginalPositions.ContainsKey(instanceID)) { OriginalPositions[instanceID] = val.anchoredPosition; } } } internal static void Reset(InventoryGui gui) { //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) try { object? obj = AccessTools.Field(typeof(InventoryGui), "m_containerGrid")?.GetValue(gui); InventoryGrid val = (InventoryGrid)((obj is InventoryGrid) ? obj : null); IEnumerable enumerable = (((Object)(object)val != (Object)null) ? (AccessTools.Field(typeof(InventoryGrid), "m_elements")?.GetValue(val) as IEnumerable) : null); if (enumerable != null) { foreach (object item in enumerable) { object? obj2 = AccessTools.Field(item.GetType(), "m_go")?.GetValue(item); GameObject val2 = (GameObject)((obj2 is GameObject) ? obj2 : null); if (!((Object)(object)val2 == (Object)null)) { int instanceID = ((Object)val2).GetInstanceID(); Image val3 = val2.GetComponent() ?? ((IEnumerable)val2.GetComponentsInChildren(true)).FirstOrDefault((Func)((Image image) => (Object)(object)image != (Object)null && !((Object)image).name.ToLowerInvariant().Contains("icon"))); if ((Object)(object)val3 != (Object)null && OriginalColors.TryGetValue(instanceID, out var value)) { ((Graphic)val3).color = value; } Transform transform = val2.transform; RectTransform val4 = (RectTransform)(object)((transform is RectTransform) ? transform : null); if ((Object)(object)val4 != (Object)null && OriginalPositions.TryGetValue(instanceID, out var value2)) { val4.anchoredPosition = value2; } if (((Object)val2).name.EndsWith("_CHTradeShift", StringComparison.Ordinal)) { ((Object)val2).name = ((Object)val2).name.Substring(0, ((Object)val2).name.Length - "_CHTradeShift".Length); } } } } Transform val5 = (((Object)(object)val != (Object)null && (Object)(object)((Component)val).transform.parent != (Object)null) ? ((Component)val).transform.parent.Find("ChallengeHubTradeDivider") : null); if ((Object)(object)val5 != (Object)null) { ((Component)val5).gameObject.SetActive(false); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Handelskisten-Layoutreset fehlgeschlagen: " + ex.Message)); } } } internal static void Ensure(InventoryGui gui) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: 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_00c9: 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_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) try { object? obj = AccessTools.Field(typeof(InventoryGui), "m_containerGrid")?.GetValue(gui); InventoryGrid val = (InventoryGrid)((obj is InventoryGrid) ? obj : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).transform.parent == (Object)null)) { Transform parent = ((Component)val).transform.parent; Transform val2 = parent.Find("ChallengeHubTradeDivider"); if ((Object)(object)val2 != (Object)null) { ((Component)val2).gameObject.SetActive(true); return; } GameObject val3 = new GameObject("ChallengeHubTradeDivider", new Type[2] { typeof(RectTransform), typeof(Image) }); val3.transform.SetParent(parent, false); RectTransform component = val3.GetComponent(); Vector2 anchorMin = (component.anchorMax = new Vector2(0.5f, 0.5f)); component.anchorMin = anchorMin; component.sizeDelta = new Vector2(330f, 16f); component.anchoredPosition = new Vector2(0f, -10f); ((Graphic)val3.GetComponent()).color = new Color(0.08f, 0.1f, 0.14f, 0.92f); AddText(val3.transform, "GEBEN", new Vector2(-112f, 0f), new Color(0.55f, 1f, 0.65f, 1f)); AddText(val3.transform, "↓ TRANSFER ↓", Vector2.zero, Color.white); AddText(val3.transform, "NEHMEN", new Vector2(112f, 0f), new Color(0.55f, 0.75f, 1f, 1f)); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Handelskisten-Trenner fehlgeschlagen: " + ex.Message)); } } } private static void AddText(Transform parent, string value, Vector2 position, Color color) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(value, new Type[2] { typeof(RectTransform), typeof(Text) }); val.transform.SetParent(parent, false); RectTransform component = val.GetComponent(); component.sizeDelta = new Vector2(125f, 20f); component.anchoredPosition = position; Text component2 = val.GetComponent(); component2.text = value; component2.alignment = (TextAnchor)4; ((Graphic)component2).color = color; component2.fontSize = 12; component2.font = Resources.GetBuiltinResource("Arial.ttf"); ((Graphic)component2).raycastTarget = false; } } internal static class ValheimNetworkCompatibility { private static readonly string[] IdMethodNames = new string[4] { "GetUID", "GetUid", "GetMyID", "GetMyId" }; private static readonly string[] IdPropertyNames = new string[6] { "UID", "Uid", "MyID", "MyId", "SessionID", "SessionId" }; private static readonly string[] IdFieldNames = new string[6] { "m_sessionID", "m_sessionId", "m_myid", "m_myID", "m_uid", "m_id" }; private static bool warnedMissingLocalId; private static object cachedZNetInstance; private static long cachedLocalPeerId; internal static long ResolveLocalPeerId() { object instance = ZNet.instance; if (cachedLocalPeerId != 0L && cachedZNetInstance == instance) { return cachedLocalPeerId; } cachedZNetInstance = instance; cachedLocalPeerId = 0L; if (TryResolveFromInstance(ZNet.instance, out var result)) { return Cache(result); } if (TryResolveFromInstance(ZRoutedRpc.instance, out result)) { return Cache(result); } if (TryResolveFromInstance(ZDOMan.instance, out result)) { return Cache(result); } if (!warnedMissingLocalId) { warnedMissingLocalId = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)"Lokale Valheim Netzwerk-/Session-ID konnte nicht bestimmt werden. Server-ZDO-Operation wird sicher abgebrochen."); } } return 0L; } private static long Cache(long value) { cachedLocalPeerId = value; warnedMissingLocalId = false; return value; } internal static bool TryTakeServerOwnership(ZDO zdo, string context) { if (zdo == null || !zdo.IsValid() || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return false; } try { if (zdo.IsOwner()) { return true; } long num = ResolveLocalPeerId(); if (num == 0L) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("Server-ZDO-Authority ohne lokale Session-ID abgebrochen: " + (context ?? "unknown"))); } return false; } zdo.SetOwner(num); bool num2 = zdo.IsOwner(); if (!num2) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Server-ZDO-Authority konnte nicht uebernommen werden: " + (context ?? "unknown"))); } } return num2; } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("Server-ZDO-Authority Exception: " + (context ?? "unknown") + " -> " + ex.Message)); } return false; } } private static bool TryResolveFromInstance(object instance, out long result) { result = 0L; if (instance == null) { return false; } Type type = instance.GetType(); string[] idMethodNames = IdMethodNames; foreach (string name in idMethodNames) { try { MethodInfo method = type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method != null && TryConvertPositiveId(method.Invoke(instance, null), out result)) { return true; } } catch { } } idMethodNames = IdPropertyNames; foreach (string name2 in idMethodNames) { try { PropertyInfo property = type.GetProperty(name2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.GetIndexParameters().Length == 0 && TryConvertPositiveId(property.GetValue(instance, null), out result)) { return true; } } catch { } } idMethodNames = IdFieldNames; foreach (string name3 in idMethodNames) { try { FieldInfo field = type.GetField(name3, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && TryConvertPositiveId(field.GetValue(instance), out result)) { return true; } } catch { } } return false; } private static bool TryConvertPositiveId(object value, out long result) { result = 0L; if (value == null) { return false; } try { result = Convert.ToInt64(value, CultureInfo.InvariantCulture); return result != 0; } catch { result = 0L; return false; } } } internal static class ValheimPrivateAccess { internal static bool TryPokeLocalZone(ZoneSystem zoneSystem, Vector2i zone, out string error) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) error = string.Empty; if ((Object)(object)zoneSystem == (Object)null) { error = "ZoneSystem ist nicht verfügbar."; return false; } try { MethodInfo methodInfo = AccessTools.Method(((object)zoneSystem).GetType(), "PokeLocalZone", new Type[1] { typeof(Vector2i) }, (Type[])null) ?? AccessTools.Method(((object)zoneSystem).GetType(), "PokeZone", new Type[1] { typeof(Vector2i) }, (Type[])null); if (methodInfo == null) { error = "Weder PokeLocalZone noch PokeZone wurde gefunden."; return false; } methodInfo.Invoke(zoneSystem, new object[1] { zone }); return true; } catch (Exception ex) { error = Unwrap(ex).Message; return false; } } internal static bool TryGetZoneRoot(ZoneSystem zoneSystem, Vector2i zone, out GameObject root) { //IL_0024: 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) root = null; if ((Object)(object)zoneSystem == (Object)null) { return false; } try { if (!(ReadField(zoneSystem, "m_zones") is IDictionary dictionary) || !dictionary.Contains(zone)) { return false; } object instance = dictionary[zone]; object obj = ReadField(instance, "m_root"); root = (GameObject)((obj is GameObject) ? obj : null); return (Object)(object)root != (Object)null; } catch { return false; } } internal static bool TryRemoveZoneRoot(ZoneSystem zoneSystem, Vector2i zone, out GameObject root) { //IL_0024: 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) //IL_0055: Unknown result type (might be due to invalid IL or missing references) root = null; if ((Object)(object)zoneSystem == (Object)null) { return false; } try { if (!(ReadField(zoneSystem, "m_zones") is IDictionary dictionary) || !dictionary.Contains(zone)) { return false; } object instance = dictionary[zone]; object obj = ReadField(instance, "m_root"); root = (GameObject)((obj is GameObject) ? obj : null); dictionary.Remove(zone); return true; } catch { return false; } } internal static bool TryPlaceLocations(ZoneSystem zoneSystem, Vector2i zone, GameObject zoneRoot, out List temporaryObjects, out string error) { //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) temporaryObjects = new List(); error = string.Empty; if ((Object)(object)zoneSystem == (Object)null || (Object)(object)zoneRoot == (Object)null) { error = "ZoneSystem oder Zonenwurzel fehlt."; return false; } try { Heightmap componentInChildren = zoneRoot.GetComponentInChildren(); if ((Object)(object)componentInChildren == (Object)null) { error = "Heightmap der geladenen Zone fehlt."; return false; } MethodInfo methodInfo = (from candidate in ((object)zoneSystem).GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where string.Equals(candidate.Name, "PlaceLocations", StringComparison.Ordinal) orderby Math.Abs(candidate.GetParameters().Length - 7) select candidate).FirstOrDefault((MethodInfo candidate) => candidate.GetParameters().Length == 7); if (methodInfo == null) { error = "ZoneSystem.PlaceLocations mit sieben Parametern wurde nicht gefunden."; return false; } ParameterInfo[] parameters = methodInfo.GetParameters(); object obj = ReadField(zoneSystem, "m_tempClearAreas") ?? CreateCollection(parameters[4].ParameterType); object obj2 = ReadField(zoneSystem, "m_tempSpawnedObjects") ?? CreateCollection(parameters[6].ParameterType); ClearCollection(obj); ClearCollection(obj2); object obj3 = Enum.Parse(parameters[5].ParameterType, "Ghost", ignoreCase: true); object[] parameters2 = new object[7] { zone, ZoneSystem.GetZonePos(zone), zoneRoot.transform, componentInChildren, obj, obj3, obj2 }; methodInfo.Invoke(zoneSystem, parameters2); temporaryObjects.AddRange(from item in Enumerate(obj2).OfType() where (Object)(object)item != (Object)null select item); ClearCollection(obj2); ClearCollection(obj); return true; } catch (Exception ex) { error = Unwrap(ex).Message; return false; } } internal static List SnapshotAllZdos() { List list = new List(); ZDOMan instance = ZDOMan.instance; if (instance == null) { return list; } try { AddDictionaryValues(ReadField(instance, "m_objectsByID"), list); if (list.Count == 0) { MethodInfo methodInfo = ((object)instance).GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo candidate) => (candidate.Name == "GetAllZDOs" || candidate.Name == "GetAllZdos") && candidate.GetParameters().Length == 0); if (methodInfo != null) { list.AddRange(Enumerate(methodInfo.Invoke(instance, null)).OfType()); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("ZDO-Gesamtsnapshot fehlgeschlagen: " + Unwrap(ex).Message)); } } return list.Where(IsValid).Distinct().ToList(); } internal static List SnapshotZoneZdos(Vector2i zone) { //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) return SnapshotAllZdos().Where(delegate(ZDO zdo) { //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_000b: 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) try { Vector2i zone2 = ZoneSystem.GetZone(zdo.GetPosition()); return ((Vector2i)(ref zone2)).Equals(zone); } catch { return false; } }).ToList(); } internal static List SnapshotLocationInstances(ZoneSystem zoneSystem) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) List list = new List(); if ((Object)(object)zoneSystem == (Object)null) { return list; } try { object obj = ReadField(zoneSystem, "m_locationInstances"); if (obj is IDictionary dictionary) { foreach (DictionaryEntry item3 in dictionary) { if (item3.Value is LocationInstance item) { list.Add(item); } } return list; } PropertyInfo propertyInfo = ((obj != null) ? AccessTools.Property(obj.GetType(), "Values") : null); foreach (object item4 in Enumerate((propertyInfo != null) ? propertyInfo.GetValue(obj, null) : null)) { if (item4 is LocationInstance item2) { list.Add(item2); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("LocationInstance-Snapshot fehlgeschlagen: " + Unwrap(ex).Message)); } } return list; } internal static bool TryGetSceneInstance(ZNetScene scene, ZDO zdo, out ZNetView view) { view = null; if ((Object)(object)scene == (Object)null || zdo == null) { return false; } try { if (!(ReadField(scene, "m_instances") is IDictionary dictionary) || !dictionary.Contains(zdo)) { return false; } object? obj = dictionary[zdo]; view = (ZNetView)((obj is ZNetView) ? obj : null); return (Object)(object)view != (Object)null; } catch { return false; } } internal static void RemoveSceneInstance(ZNetScene scene, ZDO zdo) { if ((Object)(object)scene == (Object)null || zdo == null) { return; } try { if (ReadField(scene, "m_instances") is IDictionary dictionary && dictionary.Contains(zdo)) { dictionary.Remove(zdo); } } catch { } } internal static bool TryGetLocationMetadata(LocationInstance location, out string prefabName, out float exteriorRadius) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) prefabName = string.Empty; exteriorRadius = 80f; try { object obj = ReadField(location, "m_location"); if (obj == null) { return false; } object obj2 = ReadField(obj, "m_prefab"); if (obj2 == null) { return false; } prefabName = ReadStringMember(obj2, "Name", "m_name", "name"); object obj3 = ReadField(obj, "m_exteriorRadius"); if (obj3 != null) { exteriorRadius = Mathf.Clamp(Convert.ToSingle(obj3), 12f, 220f); } return !string.IsNullOrWhiteSpace(prefabName); } catch { prefabName = string.Empty; exteriorRadius = 80f; return false; } } internal static bool TryGetPrefab(ZNetScene scene, int prefabHash, out GameObject prefab) { prefab = null; if ((Object)(object)scene == (Object)null || prefabHash == 0) { return false; } try { MethodInfo methodInfo = AccessTools.Method(((object)scene).GetType(), "GetPrefab", new Type[1] { typeof(int) }, (Type[])null); if (methodInfo != null) { object? obj = methodInfo.Invoke(scene, new object[1] { prefabHash }); prefab = (GameObject)((obj is GameObject) ? obj : null); if ((Object)(object)prefab != (Object)null) { return true; } } if (ReadField(scene, "m_namedPrefabs") is IDictionary dictionary && dictionary.Contains(prefabHash)) { object? obj2 = dictionary[prefabHash]; prefab = (GameObject)((obj2 is GameObject) ? obj2 : null); return (Object)(object)prefab != (Object)null; } } catch { } prefab = null; return false; } private static object ReadField(object instance, string name) { if (instance == null || string.IsNullOrWhiteSpace(name)) { return null; } return AccessTools.Field(instance.GetType(), name)?.GetValue(instance); } private static string ReadStringMember(object instance, params string[] names) { if (instance == null) { return string.Empty; } Type type = instance.GetType(); foreach (string text in names) { try { if (AccessTools.Property(type, text)?.GetValue(instance, null) is string text2 && !string.IsNullOrWhiteSpace(text2)) { return text2; } if (AccessTools.Field(type, text)?.GetValue(instance) is string text3 && !string.IsNullOrWhiteSpace(text3)) { return text3; } } catch { } } return string.Empty; } private static object CreateCollection(Type type) { if (type == null) { return null; } try { return Activator.CreateInstance(type); } catch { return null; } } private static void ClearCollection(object collection) { if (collection == null) { return; } try { if (collection is IList list) { list.Clear(); } else { AccessTools.Method(collection.GetType(), "Clear", Type.EmptyTypes, (Type[])null)?.Invoke(collection, null); } } catch { } } private static IEnumerable Enumerate(object value) { return (value as IEnumerable) ?? Array.Empty(); } private static void AddDictionaryValues(object dictionaryObject, List result) { if (dictionaryObject == null || result == null) { return; } if (dictionaryObject is IDictionary dictionary) { { foreach (DictionaryEntry item in dictionary) { object? value = item.Value; ZDO val = (ZDO)((value is ZDO) ? value : null); if (val != null) { result.Add(val); } } return; } } try { object value2 = AccessTools.Property(dictionaryObject.GetType(), "Values")?.GetValue(dictionaryObject, null); result.AddRange(Enumerate(value2).OfType()); } catch { } } private static bool IsValid(ZDO zdo) { try { return zdo != null && zdo.IsValid(); } catch { return false; } } private static Exception Unwrap(Exception ex) { if (ex is TargetInvocationException { InnerException: not null } ex2) { return ex2.InnerException; } return ex; } } [Serializable] public sealed class WebConfigEnvelope { public bool ok; public WebConfig config; public PlaystyleWebConfig[] playstyles = new PlaystyleWebConfig[0]; public BossWebConfig[] bosses = new BossWebConfig[0]; public BiomeWebConfig[] biomes = new BiomeWebConfig[0]; } [Serializable] public sealed class GoalCatalogEnvelope { public bool ok; public PlaystyleWebConfig[] playstyles = new PlaystyleWebConfig[0]; public BossWebConfig[] bosses = new BossWebConfig[0]; public BiomeWebConfig[] biomes = new BiomeWebConfig[0]; } [Serializable] public sealed class WebConfig { public bool enabled; public string challengeShortCode = "VALNODEATH"; public string serverId = "valheim-main-server"; public string worldName = "ChallengeHub-Valheim"; public string defaultDifficulty = "medium"; public bool seedHidden = true; public bool noMap = true; public bool disableBossVegvisir = true; public bool damageScalingEnabled; public DifficultyConfig[] categoryOptions = new DifficultyConfig[0]; public IntervalConfig intervals = new IntervalConfig(); public SkillTrackingConfig skillTracking = new SkillTrackingConfig(); public AdmissionConfig admission = new AdmissionConfig(); public ServerGateConfig serverGate = new ServerGateConfig(); public PortalConfig portals = new PortalConfig(); public ZoneResetConfig zoneReset = new ZoneResetConfig(); public QoLWebConfig qol = new QoLWebConfig(); public SettlementWebConfig settlements = new SettlementWebConfig(); public RenaturationWebConfig renaturation = new RenaturationWebConfig(); public ResourceResetWebConfig resourceReset = new ResourceResetWebConfig(); public ZoneRestorationWebConfig zoneRestoration = new ZoneRestorationWebConfig(); public GuardianStoneConfig guardianStones = new GuardianStoneConfig(); public EvidenceConfig evidenceTracking = new EvidenceConfig(); public PlaystyleWebConfig[] playstyles = new PlaystyleWebConfig[0]; public BossWebConfig[] bosses = new BossWebConfig[0]; public BiomeWebConfig[] biomes = new BiomeWebConfig[0]; public ScoringWebConfig scoring = new ScoringWebConfig(); public static WebConfig Default() { WebConfig webConfig = new WebConfig(); webConfig.categoryOptions = new DifficultyConfig[3] { new DifficultyConfig { key = "easy", label = "Einfach", pointsMultiplier = 0.75f, enemyDamageMultiplier = 0.75f, playerDamageMultiplier = 1.25f }, new DifficultyConfig { key = "medium", label = "Mittel", pointsMultiplier = 1f, enemyDamageMultiplier = 1f, playerDamageMultiplier = 1f }, new DifficultyConfig { key = "hard", label = "Schwer", pointsMultiplier = 1.35f, enemyDamageMultiplier = 1.25f, playerDamageMultiplier = 0.85f } }; return webConfig; } public DifficultyConfig Difficulty(string key) { if (categoryOptions != null) { DifficultyConfig[] array = categoryOptions; foreach (DifficultyConfig difficultyConfig in array) { if (difficultyConfig != null && difficultyConfig.key == key) { return difficultyConfig; } } } if (key == "easy") { return new DifficultyConfig { key = "easy", enemyDamageMultiplier = 0.75f, playerDamageMultiplier = 1.25f, pointsMultiplier = 0.75f }; } if (key == "hard") { return new DifficultyConfig { key = "hard", enemyDamageMultiplier = 1.25f, playerDamageMultiplier = 0.85f, pointsMultiplier = 1.35f }; } return new DifficultyConfig { key = "medium", enemyDamageMultiplier = 1f, playerDamageMultiplier = 1f, pointsMultiplier = 1f }; } } [Serializable] public sealed class BossWebConfig { public string key; public string label; public string biome; public int basePoints; } [Serializable] public sealed class BiomeWebConfig { public string key; public string label; public int trophyPoints; public int requiredTrophies; } [Serializable] public sealed class ScoringWebConfig { public float bonusBaseMultiplier = 2f; } [Serializable] public sealed class PlaystyleWebConfig { public string key; public string label; public GoalWebConfig[] goals = new GoalWebConfig[0]; } [Serializable] public sealed class GoalWebConfig { public string key; public string label; public string description; public string detection; public string rewardMode; public string scopeKind; public int detectorVersion; public int basePoints; public float progressTarget; public string progressUnit; public string progressMode; public float progressScopeTarget; } [Serializable] public sealed class DifficultyConfig { public string key; public string label; public float pointsMultiplier = 1f; public float enemyDamageMultiplier = 1f; public float playerDamageMultiplier = 1f; public int deathPenalty = 8; public int bossDeathStepPenalty = 6; public int trophyDeathStepPenalty = 3; public int portalPenaltyPerExtra = 10; } [Serializable] public sealed class IntervalConfig { public float heartbeatSeconds = 20f; public float skillScanSeconds = 20f; public float portalScanSeconds = 45f; public float zoneResetScanSeconds = 180f; } [Serializable] public sealed class SkillTrackingConfig { public bool enabled; public bool requireZeroStart = true; public int stepLevels = 5; public int pointsPerStep = 25; public int maxStepsPerArea = 6; } [Serializable] public sealed class AdmissionConfig { public bool enabled = true; public VectorConfig waitingPosition = new VectorConfig { x = 0f, y = 0f, z = 0f }; public VectorConfig startPosition = new VectorConfig { x = 0f, y = 0f, z = 0f }; public float waitingRadius; public float pollSeconds = 8f; public float checkpointSeconds = 30f; public bool failClosed; public bool allowUnapprovedWorldAccess = true; public bool challengeScoringRequiresActive = true; public bool freezeFirstEntrySnapshot = true; public bool restoreForeignWorldState = true; } [Serializable] public sealed class ServerGateConfig { public bool enabled = true; public bool failClosed = true; public int protocolVersion = 1; public float verificationTimeoutSeconds = 10f; public float disconnectDelaySeconds = 3f; public float serverHeartbeatSeconds = 30f; public float leaseMinutes = 10f; public bool boundCharacterHardLock = true; public int wrongWorldWarningLimit = 2; public int wrongWorldRequireAdminAfter = 3; } [Serializable] public sealed class VectorConfig { public float x; public float y; public float z; } [Serializable] public sealed class PortalConfig { public int allowedActivePerBiome = 1; public bool penaltyEnabled = true; public bool countOnlyActive = true; } [Serializable] public sealed class GuardianStoneConfig { public bool enabled = true; public bool customPiecesEnabled = true; public string prefabNames = "guard_stone,piece_guardstone,piece_ward,ward,piece_challengehub_guardian_farmer,piece_challengehub_guardian_explorer,piece_challengehub_guardian_combat,piece_challengehub_guardian_builder,piece_challengehub_guardian_neutral"; public float defaultRadius = 80f; public string defaultType = "farmer"; public string typeByPrefab = "piece_challengehub_guardian_farmer:farmer,piece_challengehub_guardian_explorer:explorer,piece_challengehub_guardian_combat:combat,piece_challengehub_guardian_builder:builder,piece_challengehub_guardian_neutral:neutral"; public string farmerRecipe = "Stone:20,Wood:10,Resin:5,CarrotSeeds:1"; public string explorerRecipe = "Stone:15,Wood:10,Resin:5,Amber:1,Feathers:1"; public string combatRecipe = "Stone:25,Wood:10,Resin:5,BoneFragments:2,TrophyEikthyr:1"; public string builderRecipe = "Stone:20,Wood:15,FineWood:5,Resin:2,BronzeNails:5"; public string neutralRecipe = "Stone:10,Wood:10,Resin:2"; public bool farmerPreventReset = true; public bool explorerPreventReset; public bool combatPreventReset = true; public bool builderPreventReset = true; public bool neutralPreventReset = true; } [Serializable] public sealed class EvidenceConfig { public bool enabled; public bool buildEvents; public bool craftEvents; public bool pickupEvents = true; public bool dropSpawnEvents = true; public bool creatureKillEvents = true; public bool gatherEvents; public bool explorationEvents; public bool mapViolationPenalty; } [Serializable] public sealed class QoLWebConfig { public bool enabled = true; public float containerRadius = 12f; public int storageMaxContainers = 20; public float harvestRadius = 7f; public int harvestMaxTargets = 24; public int maxPlantGridSize = 5; public float doorCloseDelaySeconds = 5f; public float doorSafetyRadius = 1.8f; public float fuelContainerRadius = 12f; public int signMaxFontSize = 48; public float swimCriticalStaminaPercent = 0.25f; } [Serializable] public sealed class SettlementWebConfig { public bool enabled = true; public float protectionRadius = 80f; public float minimumSpacing = 350f; public float searchMinRadius = 450f; public float searchMaxRadius = 5000f; public int candidateAttempts = 180; public float maximumHeightVariance = 8f; public bool teleportOnFirstActivation = true; public bool setCustomSpawnPoint = true; public bool ownerOnlyBuilding = true; public float activityRadius = 32f; public float activeGraceHours = 72f; } [Serializable] public sealed class RenaturationWebConfig { public bool enabled = true; public bool requireWorkbenchForPaths = true; public float workbenchRadius = 20f; public float scanSeconds = 60f; public int maxObjectsPerPass = 64; public float pathGraceHours = 24f; public float pathStageHours = 12f; public float buildingGraceHours = 168f; public float buildingStageHours = 72f; public bool finalBuildingRemovalEnabled = true; public float finalBuildingRemovalHours = 720f; public string protectedPrefabKeywords = "container,chest,portal,ward,guard,bed,workbench,forge,stonecutter,artisan,sign,ship,cart,raft,karve,challengehub"; } [Serializable] public sealed class ResourceResetWebConfig { public bool enabled = true; public float scanSeconds = 120f; public int maxSitesPerPass = 8; public float playerSafetyRadius = 50f; public float playerBuildSafetyRadius = 6f; public float restoreAtDepletionPercent = 50f; public float defaultCooldownHours = 24f; public float copperCooldownHours = 24f; public float silverCooldownHours = 36f; public float tarCooldownHours = 12f; public float tinCooldownHours = 12f; public float obsidianCooldownHours = 24f; public float flametalCooldownHours = 72f; public float ironCooldownHours = 24f; public float softTissueCooldownHours = 48f; public string resourcePrefabPatterns = "rock4_copper,rock4_copper_frac,copper,rock3_silver,silver,tar,tarliquid,tarpit,tin,obsidian,flametal,mudpile,iron_scrap,giant_brain,softtissue"; } [Serializable] public sealed class ZoneRestorationWebConfig { public bool enabled = true; public float scanSeconds = 2f; public float meadowsDays = 7f; public float blackForestDays = 7f; public float swampDays = 10f; public float mountainDays = 10f; public float plainsDays = 12f; public float mistlandsDays = 14f; public float ashlandsDays = 14f; public float deepNorthDays = 14f; public bool restoreNaturalLocations = true; public float naturalLocationScanSeconds = 60f; public string excludedNaturalLocationKeywords = "start,spawn,altar,boss,offer,trader,haldor,hildir,bogwitch,witch,queen,fader,crypt,burial,cave,mine,dungeon,sunken"; } [Serializable] public sealed class ZoneResetConfig { public bool enabled; public int intervalMinutes = 180; public float zoneRadiusMeters = 64f; public int minInactiveMinutes = 120; public bool bossRadiusResetEnabled = true; public int bossResetIntervalMinutes = 360; public float bossResetRadiusMeters = 180f; public string[] protectedByAny = new string[0]; public string[] resetObjects = new string[0]; } internal sealed class WorldEventConsoleFeature : MonoBehaviour { [CompilerGenerated] private static class <>O { public static Action <0>__RpcRequest; public static Action <1>__RpcSync; public static Action <2>__RpcResponse; public static ConsoleEvent <3>__Command; } private const string RequestRpc = "ChallengeHub_WorldEventAdmin_Request_v2"; private const string SyncRpc = "ChallengeHub_WorldEventAdmin_Sync_v2"; private const string ResponseRpc = "ChallengeHub_WorldEventAdmin_Response_v2"; private static bool _commandRegistered; private static ZRoutedRpc _rpc; private float _nextRegister; internal static void Initialize(Plugin plugin) { if ((Object)(object)plugin != (Object)null && (Object)(object)((Component)plugin).gameObject.GetComponent() == (Object)null) { ((Component)plugin).gameObject.AddComponent(); } } private void Update() { if (!(Time.unscaledTime < _nextRegister)) { _nextRegister = Time.unscaledTime + 2f; RegisterRpc(); RegisterCommand(); } } private static void RegisterRpc() { if (ZRoutedRpc.instance != null && _rpc != ZRoutedRpc.instance) { _rpc = ZRoutedRpc.instance; _rpc.Register("ChallengeHub_WorldEventAdmin_Request_v2", (Action)RpcRequest); _rpc.Register("ChallengeHub_WorldEventAdmin_Sync_v2", (Action)RpcSync); _rpc.Register("ChallengeHub_WorldEventAdmin_Response_v2", (Action)RpcResponse); } } private static void RegisterCommand() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown if (!_commandRegistered) { _commandRegistered = true; object obj = <>O.<3>__Command; if (obj == null) { ConsoleEvent val = Command; <>O.<3>__Command = val; obj = (object)val; } new ConsoleCommand("ch_worldevent", "Admin: help | list | status | timers | effects [key] | players | start [Min.] | test [Min.] | stop ", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } } private static void Command(ConsoleEventArgs args) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown string[] array = args.Args ?? new string[0]; string text = ((array.Length > 1) ? array[1].ToLowerInvariant() : "help"); ZPackage val = new ZPackage(); val.Write(text); val.Write((array.Length > 2) ? array[2] : "all"); float result = ((text == "test") ? 5f : 1440f); if (array.Length > 3) { float.TryParse(array[3], NumberStyles.Float, CultureInfo.InvariantCulture, out result); } val.Write(Mathf.Clamp(result, 1f, 10080f)); long num = (((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) ? 0 : ServerPeerId()); if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { RpcRequest(0L, val); } else { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(num, "ChallengeHub_WorldEventAdmin_Request_v2", new object[1] { val }); } } args.Context.AddString("ChallengeHub Welt-Ereignis-Anfrage an Server gesendet."); } private static void RpcRequest(long sender, ZPackage package) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || package == null) { return; } string text = package.ReadString(); string key = package.ReadString(); float minutes = package.ReadSingle(); List list = WorldEventGameplayFeature.ScheduleSnapshot().ToList(); switch (text) { case "help": Reply(sender, "ch_worldevent list | status | timers | effects [key] | players | start [Minuten] | test [Minuten] | stop . 'test' simuliert Effekte, sendet aber keine Punkte/API-Abschlüsse."); return; case "list": Reply(sender, "Verfügbare Ereignisse: fimbulwinter, aegirs_zorn, skadis_atem, fenrirs_blutmond, joermungandrs_erwachen, thors_zorn, freyrs_duerre, odins_raben, hels_schleier, baldurs_licht"); return; case "status": case "timers": Reply(sender, DescribeSchedule(list, text == "timers")); return; case "effects": Reply(sender, DescribeEffects(key)); return; case "players": Reply(sender, DescribePlayers()); return; } if (!IsAdmin(sender)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Nicht autorisierter verändernder Welt-Ereignis-Befehl verworfen: " + sender + " / " + text)); } Reply(sender, "Keine Berechtigung: start, test und stop sind ausschließlich für Server-Admins verfügbar."); return; } switch (text) { case "start": case "test": { WorldEventRuntimeDefinition worldEventRuntimeDefinition = WorldEventGameplayFeature.BuiltIn(key, minutes); if (worldEventRuntimeDefinition == null) { Reply(sender, "Unbekanntes Welt-Ereignis: " + key); return; } worldEventRuntimeDefinition.Simulation = text == "test"; list.RemoveAll((WorldEventRuntimeDefinition entry) => string.Equals(entry.Key, key, StringComparison.OrdinalIgnoreCase)); list.Add(worldEventRuntimeDefinition); break; } case "stop": list.RemoveAll((WorldEventRuntimeDefinition entry) => key == "all" || string.Equals(entry.Key, key, StringComparison.OrdinalIgnoreCase)); break; default: Reply(sender, "Unbekannte Aktion. Nutze: ch_worldevent help"); return; } Broadcast(list); Reply(sender, "Welt-Ereignis-Befehl ausgeführt: " + text + " " + key + ((text == "test") ? " (SIMULATION: keine Punkte/API-Abschlüsse)" : "")); } private static string DescribeSchedule(List schedule, bool timers) { DateTime now = DateTime.UtcNow; if (schedule.Count == 0) { return "Welt-Ereignisse: keine geplant oder aktiv."; } return string.Join("\n", schedule.OrderBy((WorldEventRuntimeDefinition e) => e.StartUtc).Select(delegate(WorldEventRuntimeDefinition e) { string text = ((now < e.StartUtc) ? "GEPLANT" : ((now < e.EndUtc) ? "AKTIV" : "ABGELAUFEN")); TimeSpan value = ((now < e.StartUtc) ? e.StartUtc : e.EndUtc) - now; string text2 = "[" + text + "] " + e.Key + (e.Simulation ? " [SIMULATION]" : ""); return (!timers) ? (text2 + " | " + e.StartUtc.ToString("u") + " bis " + e.EndUtc.ToString("u")) : (text2 + " | " + ((now < e.StartUtc) ? "Start in " : "Restzeit ") + FormatDuration(value)); }).ToArray()); } private static string DescribeEffects(string key) { IEnumerable source; if (!(key == "all")) { source = from e in WorldEventGameplayFeature.ScheduleSnapshot() where string.Equals(e.Key, key, StringComparison.OrdinalIgnoreCase) select e; } else { IEnumerable enumerable = WorldEventGameplayFeature.ScheduleSnapshot(); source = enumerable; } List list = source.ToList(); if (list.Count == 0 && key != "all") { WorldEventRuntimeDefinition worldEventRuntimeDefinition = WorldEventGameplayFeature.BuiltIn(key, 1f); if (worldEventRuntimeDefinition != null) { list.Add(worldEventRuntimeDefinition); } } if (list.Count != 0) { return string.Join("\n", list.Select((WorldEventRuntimeDefinition e) => e.Key + ": " + WorldEventGameplayFeature.DescribeEffects(e)).ToArray()); } return "Keine Effekte für '" + key + "' gefunden."; } private static string DescribePlayers() { List allPlayers = Player.GetAllPlayers(); if (allPlayers == null || allPlayers.Count == 0) { return "Welt-Ereignis-Spieler: niemand online."; } return "Online während Welt-Ereignis (" + allPlayers.Count + "): " + string.Join(", ", (from p in allPlayers where (Object)(object)p != (Object)null select p.GetPlayerName() + " @ " + ((object)((WorldGenerator.instance != null) ? WorldGenerator.instance.GetBiome(((Component)p).transform.position) : Heightmap.FindBiome(((Component)p).transform.position))/*cast due to .constrained prefix*/).ToString()).ToArray()); } private static string FormatDuration(TimeSpan value) { if (value < TimeSpan.Zero) { value = TimeSpan.Zero; } if (value.Days <= 0) { if (value.Hours <= 0) { return value.Minutes + "m " + value.Seconds + "s"; } return value.Hours + "h " + value.Minutes + "m"; } return value.Days + "d " + value.Hours + "h"; } private static void Broadcast(List schedule) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown WorldEventGameplayFeature.ApplySchedule(schedule); ZPackage val = new ZPackage(); val.Write(schedule.Count); foreach (WorldEventRuntimeDefinition item in schedule) { val.Write(item.Key ?? ""); val.Write(item.Id ?? ""); val.Write(item.StartUtc.Ticks); val.Write(item.EndUtc.Ticks); val.Write(item.Simulation); } ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "ChallengeHub_WorldEventAdmin_Sync_v2", new object[1] { val }); } private static void RpcSync(long sender, ZPackage package) { if (package == null || (Object)(object)ZNet.instance == (Object)null || (!ZNet.instance.IsServer() && sender != ServerPeerId())) { return; } int num = package.ReadInt(); List list = new List(); for (int i = 0; i < num; i++) { string key = package.ReadString(); string id = package.ReadString(); long ticks = package.ReadLong(); long ticks2 = package.ReadLong(); bool simulation = package.ReadBool(); WorldEventRuntimeDefinition worldEventRuntimeDefinition = WorldEventGameplayFeature.BuiltIn(key, 1f); if (worldEventRuntimeDefinition != null) { worldEventRuntimeDefinition.Id = id; worldEventRuntimeDefinition.StartUtc = new DateTime(ticks, DateTimeKind.Utc); worldEventRuntimeDefinition.EndUtc = new DateTime(ticks2, DateTimeKind.Utc); worldEventRuntimeDefinition.Simulation = simulation; list.Add(worldEventRuntimeDefinition); } } WorldEventGameplayFeature.ApplySchedule(list); } private static long ServerPeerId() { try { MethodInfo methodInfo = AccessTools.Method(((object)ZRoutedRpc.instance)?.GetType(), "GetServerPeerID", (Type[])null, (Type[])null) ?? AccessTools.Method(((object)ZRoutedRpc.instance)?.GetType(), "GetServerPeerId", (Type[])null, (Type[])null); return (methodInfo == null) ? 0 : Convert.ToInt64(methodInfo.Invoke(ZRoutedRpc.instance, null)); } catch { return 0L; } } internal static bool IsAdmin(long sender) { if (sender == 0L) { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } try { object obj = AccessTools.Method(((object)ZNet.instance).GetType(), "GetPeer", new Type[1] { typeof(long) }, (Type[])null)?.Invoke(ZNet.instance, new object[1] { sender }); object obj2 = AccessTools.Field(obj?.GetType(), "m_socket")?.GetValue(obj); string text = Convert.ToString(AccessTools.Method(obj2?.GetType(), "GetHostName", (Type[])null, (Type[])null)?.Invoke(obj2, null)); return !string.IsNullOrEmpty(text) && ZNet.instance.IsAdmin(text); } catch { return false; } } private static void Reply(long sender, string value) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown if (sender == 0L) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)value); } Print(value); } else { ZPackage val = new ZPackage(); val.Write(value ?? ""); ZRoutedRpc.instance.InvokeRoutedRPC(sender, "ChallengeHub_WorldEventAdmin_Response_v2", new object[1] { val }); } } private static void RpcResponse(long sender, ZPackage package) { if (package != null && !((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer()) { Print(package.ReadString()); } } private static void Print(string value) { try { object obj = AccessTools.Field(typeof(Terminal), "m_terminalInstance")?.GetValue(null); AccessTools.Method(obj?.GetType(), "AddString", new Type[1] { typeof(string) }, (Type[])null)?.Invoke(obj, new object[1] { value }); } catch { } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)value); } } } internal sealed class WorldEventRuntimeDefinition { internal string Id; internal string Key; internal string Label; internal DateTime StartUtc; internal DateTime EndUtc; internal bool Simulation; internal WorldEventRuntimeEffects Effects; } internal sealed class WorldEventRuntimeEffects { internal string ForcedEnvironment; internal bool NightOnly; internal bool ColdEverywhere; internal bool WetEverywhere; internal bool SleepBlocked; internal float CropGrowthMultiplier = 1f; internal float PickableRespawnMultiplier = 1f; internal float EnemyHealthMultiplier = 1f; internal float EnemySpawnMultiplier = 1f; internal float EnemyDamageMultiplier = 1f; internal float EnemyDropMultiplier = 1f; internal float SeaCreatureHealthMultiplier = 1f; internal float OceanSpawnMultiplier = 1f; internal float ShipSpeedMultiplier = 1f; internal float MovementMultiplier = 1f; internal float FireDamageMultiplier = 1f; internal float StaminaRegenMultiplier = 1f; internal float SkillGainMultiplier = 1f; internal bool LightningStrikes; internal float LightningIntervalSeconds = 90f; internal float LightningDamage = 40f; internal float LightningSafeRadius = 12f; internal bool HiddenLocation; internal float ClueIntervalMinutes = 20f; internal float DiscoveryRadius = 20f; internal float DiscoveryPoints = 80f; internal bool Regional; internal float UndeadSpawnMultiplier = 1f; internal float UndeadHealthMultiplier = 1f; internal float CoastalFloodVisual; internal float VisibilityMultiplier = 1f; internal float CraftDurabilityBonus; } internal sealed class WorldEventGameplayFeature : MonoBehaviour { private static WorldEventGameplayFeature _instance; private static readonly object Sync = new object(); private static List _schedule = new List(); private static WorldEventRuntimeDefinition[] _active = new WorldEventRuntimeDefinition[0]; private float _nextRefresh; private string _forcedEnvironment = string.Empty; private string _lastAnnouncement = string.Empty; private DateTime _nextLightningUtc = DateTime.MinValue; private DateTime _nextClueUtc = DateTime.MinValue; private readonly HashSet _discoveredLocations = new HashSet(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _pickableRespawnOriginals = new Dictionary(); private float? _originalWaterLevel; private float _nextObjectScan; private bool _fogCaptured; private bool _originalFog; private float _originalFogDensity; private readonly Dictionary> _corruptionNodes = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _corruptionReported = new Dictionary(StringComparer.OrdinalIgnoreCase); private GameObject _odinMarker; internal static bool SleepBlockedNow => _active.Any((WorldEventRuntimeDefinition entry) => IsEffectiveNow(entry) && entry.Effects != null && entry.Effects.SleepBlocked); internal static bool AnyActive => _active.Length != 0; internal static void Initialize(Plugin plugin) { if (!((Object)(object)_instance != (Object)null) && !((Object)(object)plugin == (Object)null)) { _instance = ((Component)plugin).gameObject.AddComponent(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Nordische Welt-Ereignissteuerung initialisiert; Effekte bleiben ohne aktiven Web-Termin neutral."); } } } internal static void ApplySchedule(IEnumerable schedule) { lock (Sync) { _schedule = (schedule ?? Enumerable.Empty()).Where((WorldEventRuntimeDefinition entry) => entry != null && entry.EndUtc > entry.StartUtc).ToList(); } if ((Object)(object)_instance != (Object)null) { _instance._nextRefresh = 0f; } } internal static WorldEventRuntimeDefinition[] ScheduleSnapshot() { lock (Sync) { return _schedule.ToArray(); } } internal static WorldEventRuntimeDefinition[] ActiveSnapshot() { return _active.ToArray(); } internal static string DescribeEffects(WorldEventRuntimeDefinition entry) { if (entry == null || entry.Effects == null) { return "keine Effekte"; } WorldEventRuntimeEffects effects = entry.Effects; List list = new List(); if (!string.IsNullOrWhiteSpace(effects.ForcedEnvironment)) { list.Add("Wetter=" + effects.ForcedEnvironment); } if (effects.NightOnly) { list.Add("nur nachts"); } if (effects.ColdEverywhere) { list.Add("Kälte"); } if (effects.WetEverywhere) { list.Add("Nässe"); } if (effects.SleepBlocked) { list.Add("Schlaf gesperrt"); } AddModifier(list, "Pflanzen", effects.CropGrowthMultiplier); AddModifier(list, "Respawn", effects.PickableRespawnMultiplier); AddModifier(list, "Gegnerleben", effects.EnemyHealthMultiplier); AddModifier(list, "Gegnerzahl", effects.EnemySpawnMultiplier); AddModifier(list, "Gegnerschaden", effects.EnemyDamageMultiplier); AddModifier(list, "Drops", effects.EnemyDropMultiplier); AddModifier(list, "Seeleben", effects.SeaCreatureHealthMultiplier); AddModifier(list, "Ozeanspawns", effects.OceanSpawnMultiplier); AddModifier(list, "Schiff", effects.ShipSpeedMultiplier); AddModifier(list, "Bewegung", effects.MovementMultiplier); AddModifier(list, "Feuer", effects.FireDamageMultiplier); AddModifier(list, "Ausdauer", effects.StaminaRegenMultiplier); AddModifier(list, "Skills", effects.SkillGainMultiplier); if (effects.LightningStrikes) { list.Add("Blitze alle " + effects.LightningIntervalSeconds.ToString("0") + "s"); } if (effects.HiddenLocation) { list.Add("verborgener Ort (" + effects.DiscoveryRadius.ToString("0") + "m)"); } if (effects.Regional) { list.Add("regionale Verderbnis"); } if (effects.CoastalFloodVisual > 0f) { list.Add("Wasser +" + effects.CoastalFloodVisual.ToString("0.0") + "m"); } if (effects.VisibilityMultiplier < 0.999f) { list.Add("Sicht x" + effects.VisibilityMultiplier.ToString("0.00")); } if (effects.CraftDurabilityBonus > 0f) { list.Add("Haltbarkeit +" + (effects.CraftDurabilityBonus * 100f).ToString("0") + "%"); } if (list.Count != 0) { return string.Join(", ", list.ToArray()); } return "keine Effekte"; } private static void AddModifier(List values, string label, float value) { if (Math.Abs(value - 1f) > 0.001f) { values.Add(label + " x" + value.ToString("0.00")); } } internal static WorldEventRuntimeDefinition BuiltIn(string key, float minutes) { WorldEventRuntimeEffects worldEventRuntimeEffects = new WorldEventRuntimeEffects(); string text = key; switch ((key ?? string.Empty).ToLowerInvariant()) { case "fimbulwinter": text = "Fimbulwinter"; worldEventRuntimeEffects.ForcedEnvironment = "Snow"; worldEventRuntimeEffects.ColdEverywhere = true; worldEventRuntimeEffects.CropGrowthMultiplier = 0.5f; break; case "aegirs_zorn": text = "Ægirs Zorn"; worldEventRuntimeEffects.ForcedEnvironment = "ThunderStorm"; worldEventRuntimeEffects.CoastalFloodVisual = 1.5f; worldEventRuntimeEffects.ShipSpeedMultiplier = 1.2f; worldEventRuntimeEffects.WetEverywhere = true; break; case "skadis_atem": text = "Skadis Atem"; worldEventRuntimeEffects.ForcedEnvironment = "Misty"; worldEventRuntimeEffects.VisibilityMultiplier = 0.45f; worldEventRuntimeEffects.MovementMultiplier = 0.9f; break; case "fenrirs_blutmond": text = "Fenrirs Blutmond"; worldEventRuntimeEffects.NightOnly = true; worldEventRuntimeEffects.ForcedEnvironment = "Darklands_dark"; worldEventRuntimeEffects.EnemySpawnMultiplier = 1.35f; worldEventRuntimeEffects.EnemyHealthMultiplier = 1.35f; worldEventRuntimeEffects.EnemyDamageMultiplier = 1.25f; worldEventRuntimeEffects.EnemyDropMultiplier = 1.5f; worldEventRuntimeEffects.SleepBlocked = true; break; case "joermungandrs_erwachen": text = "Jörmungandrs Erwachen"; worldEventRuntimeEffects.ForcedEnvironment = "ThunderStorm"; worldEventRuntimeEffects.OceanSpawnMultiplier = 2f; worldEventRuntimeEffects.SeaCreatureHealthMultiplier = 1.25f; worldEventRuntimeEffects.ShipSpeedMultiplier = 1.15f; break; case "thors_zorn": text = "Thors Zorn"; worldEventRuntimeEffects.ForcedEnvironment = "ThunderStorm"; worldEventRuntimeEffects.LightningStrikes = true; worldEventRuntimeEffects.LightningIntervalSeconds = 90f; worldEventRuntimeEffects.LightningDamage = 40f; worldEventRuntimeEffects.LightningSafeRadius = 12f; break; case "freyrs_duerre": text = "Freyrs Dürre"; worldEventRuntimeEffects.ForcedEnvironment = "Clear"; worldEventRuntimeEffects.CropGrowthMultiplier = 0.5f; worldEventRuntimeEffects.PickableRespawnMultiplier = 1.5f; worldEventRuntimeEffects.FireDamageMultiplier = 1.25f; break; case "odins_raben": text = "Odins Raben"; worldEventRuntimeEffects.HiddenLocation = true; worldEventRuntimeEffects.ClueIntervalMinutes = 20f; worldEventRuntimeEffects.DiscoveryRadius = 20f; worldEventRuntimeEffects.DiscoveryPoints = 80f; break; case "hels_schleier": text = "Hels Schleier"; worldEventRuntimeEffects.Regional = true; worldEventRuntimeEffects.UndeadSpawnMultiplier = 2f; worldEventRuntimeEffects.UndeadHealthMultiplier = 1.25f; break; case "baldurs_licht": text = "Baldurs Licht"; worldEventRuntimeEffects.ForcedEnvironment = "Clear"; worldEventRuntimeEffects.StaminaRegenMultiplier = 1.25f; worldEventRuntimeEffects.SkillGainMultiplier = 1.2f; worldEventRuntimeEffects.CraftDurabilityBonus = 0.1f; worldEventRuntimeEffects.NightOnly = true; break; default: return null; } DateTime utcNow = DateTime.UtcNow; return new WorldEventRuntimeDefinition { Id = key + "-console-" + utcNow.Ticks, Key = key, Label = text, StartUtc = utcNow, EndUtc = utcNow.AddMinutes(Mathf.Clamp(minutes, 1f, 10080f)), Effects = worldEventRuntimeEffects }; } internal static bool Has(string key) { return _active.Any((WorldEventRuntimeDefinition entry) => string.Equals(entry.Key, key, StringComparison.OrdinalIgnoreCase)); } internal static float Modifier(Func selector) { float num = 1f; WorldEventRuntimeDefinition[] active = _active; foreach (WorldEventRuntimeDefinition worldEventRuntimeDefinition in active) { if (IsEffectiveNow(worldEventRuntimeDefinition)) { num *= Mathf.Max(0.05f, selector(worldEventRuntimeDefinition.Effects ?? new WorldEventRuntimeEffects())); } } return num; } internal static bool IsEffective(string key) { return _active.Any((WorldEventRuntimeDefinition entry) => IsEffectiveNow(entry) && string.Equals(entry.Key, key, StringComparison.OrdinalIgnoreCase)); } private static bool IsEffectiveNow(WorldEventRuntimeDefinition entry) { if (entry == null || entry.Effects == null) { return false; } if (!entry.Effects.NightOnly) { return true; } try { return (Object)(object)EnvMan.instance != (Object)null && EnvMan.IsNight(); } catch { return false; } } private void Update() { if (Time.unscaledTime < _nextRefresh) { return; } _nextRefresh = Time.unscaledTime + 2f; DateTime now = DateTime.UtcNow; lock (Sync) { _active = (from entry in _schedule where entry.StartUtc <= now && now < entry.EndUtc orderby entry.StartUtc select entry).ToArray(); } ApplyEnvironment(); ApplyTemporaryWorldValues(); ApplyVisibility(); ApplyPlayerState(); UpdateLightning(now); UpdateHiddenLocation(now); UpdateCorruptionNodes(); } private void ApplyVisibility() { float num = Modifier((WorldEventRuntimeEffects effect) => effect.VisibilityMultiplier); if (num < 0.999f) { if (!_fogCaptured) { _fogCaptured = true; _originalFog = RenderSettings.fog; _originalFogDensity = RenderSettings.fogDensity; } RenderSettings.fog = true; RenderSettings.fogDensity = Mathf.Max(_originalFogDensity, Mathf.Lerp(0.018f, 0.075f, 1f - Mathf.Clamp01(num))); } else if (_fogCaptured) { RenderSettings.fog = _originalFog; RenderSettings.fogDensity = _originalFogDensity; _fogCaptured = false; } } private void ApplyTemporaryWorldValues() { float num = (from entry in _active.Where(IsEffectiveNow) select entry.Effects?.CoastalFloodVisual ?? 0f).DefaultIfEmpty(0f).Max(); try { FieldInfo fieldInfo = (((Object)(object)ZoneSystem.instance == (Object)null) ? null : AccessTools.Field(((object)ZoneSystem.instance).GetType(), "m_waterLevel")); if (fieldInfo != null) { float value = Convert.ToSingle(fieldInfo.GetValue(ZoneSystem.instance)); if (num > 0f) { if (!_originalWaterLevel.HasValue) { _originalWaterLevel = value; } fieldInfo.SetValue(ZoneSystem.instance, _originalWaterLevel.Value + num); } else if (_originalWaterLevel.HasValue) { fieldInfo.SetValue(ZoneSystem.instance, _originalWaterLevel.Value); _originalWaterLevel = null; } } } catch { } if (Time.unscaledTime < _nextObjectScan) { return; } _nextObjectScan = Time.unscaledTime + 10f; float num2 = Modifier((WorldEventRuntimeEffects effect) => effect.PickableRespawnMultiplier); Pickable[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Pickable val in array) { if (!((Object)(object)val == (Object)null) && !(val.m_respawnTimeMinutes <= 0f)) { if (!_pickableRespawnOriginals.TryGetValue(val, out var value2)) { value2 = val.m_respawnTimeMinutes; _pickableRespawnOriginals[val] = value2; } val.m_respawnTimeMinutes = value2 * num2; } } if (!(Math.Abs(num2 - 1f) < 0.001f)) { return; } KeyValuePair[] array2 = _pickableRespawnOriginals.ToArray(); for (int num3 = 0; num3 < array2.Length; num3++) { KeyValuePair keyValuePair = array2[num3]; if ((Object)(object)keyValuePair.Key != (Object)null) { keyValuePair.Key.m_respawnTimeMinutes = keyValuePair.Value; } } _pickableRespawnOriginals.Clear(); } private void UpdateLightning(DateTime now) { //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Expected O, but got Unknown //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) WorldEventRuntimeDefinition worldEventRuntimeDefinition = _active.FirstOrDefault((WorldEventRuntimeDefinition entry) => IsEffectiveNow(entry) && (entry.Effects?.LightningStrikes ?? false)); if (worldEventRuntimeDefinition == null) { _nextLightningUtc = DateTime.MinValue; return; } if (_nextLightningUtc == DateTime.MinValue) { _nextLightningUtc = now.AddSeconds(Mathf.Max(20f, worldEventRuntimeDefinition.Effects.LightningIntervalSeconds)); } if (now < _nextLightningUtc) { return; } _nextLightningUtc = now.AddSeconds(Mathf.Max(20f, worldEventRuntimeDefinition.Effects.LightningIntervalSeconds)); if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } List allPlayers = Player.GetAllPlayers(); if (allPlayers == null || allPlayers.Count == 0) { return; } Player val = allPlayers[Random.Range(0, allPlayers.Count)]; if ((Object)(object)val == (Object)null) { return; } Vector2 insideUnitCircle = Random.insideUnitCircle; Vector2 val2 = ((Vector2)(ref insideUnitCircle)).normalized * Random.Range(Mathf.Max(14f, worldEventRuntimeDefinition.Effects.LightningSafeRadius), 35f); Vector3 point = ((Component)val).transform.position + new Vector3(val2.x, 20f, val2.y); RaycastHit val3 = default(RaycastHit); if (Physics.Raycast(point, Vector3.down, ref val3, 80f, -1, (QueryTriggerInteraction)1)) { point = ((RaycastHit)(ref val3)).point; } if (Object.FindObjectsByType((FindObjectsSortMode)0).Any((PrivateArea area) => (Object)(object)area != (Object)null && Vector3.Distance(((Component)area).transform.position, point) < 20f)) { return; } try { ZNetScene instance = ZNetScene.instance; GameObject val4 = ((instance != null) ? instance.GetPrefab("fx_Lightning") : null); if ((Object)(object)val4 != (Object)null) { Object.Instantiate(val4, point, Quaternion.identity); } foreach (Character allCharacter in Character.GetAllCharacters()) { if (!((Object)(object)allCharacter == (Object)null) && !allCharacter.IsPlayer() && !(Vector3.Distance(((Component)allCharacter).transform.position, point) > 5f)) { HitData val5 = new HitData(); val5.m_point = point; val5.m_damage.m_lightning = Mathf.Max(1f, worldEventRuntimeDefinition.Effects.LightningDamage); allCharacter.Damage(val5); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Thors Blitz konnte nicht vollständig ausgeführt werden: " + ex.Message)); } } } private void UpdateHiddenLocation(DateTime now) { //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_03b6: Unknown result type (might be due to invalid IL or missing references) //IL_03be: Unknown result type (might be due to invalid IL or missing references) //IL_03c3: Unknown result type (might be due to invalid IL or missing references) //IL_03c8: Unknown result type (might be due to invalid IL or missing references) //IL_03cc: Unknown result type (might be due to invalid IL or missing references) //IL_03d1: Unknown result type (might be due to invalid IL or missing references) //IL_03d3: Unknown result type (might be due to invalid IL or missing references) //IL_03df: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0409: Unknown result type (might be due to invalid IL or missing references) //IL_03ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_0338: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Unknown result type (might be due to invalid IL or missing references) //IL_0366: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } WorldEventRuntimeDefinition worldEventRuntimeDefinition = _active.FirstOrDefault((WorldEventRuntimeDefinition entry) => IsEffectiveNow(entry) && (entry.Effects?.HiddenLocation ?? false)); if (worldEventRuntimeDefinition == null || _discoveredLocations.Contains(worldEventRuntimeDefinition.Id)) { return; } int stableHashCode = StringExtensionMethods.GetStableHashCode(worldEventRuntimeDefinition.Id ?? worldEventRuntimeDefinition.Key ?? "odins_raben"); float num = (float)Mathf.Abs(stableHashCode % 360) * ((float)Math.PI / 180f); float num2 = 1400f + (float)Mathf.Abs(stableHashCode / 360 % 1400); Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(Mathf.Cos(num) * num2, ((Component)localPlayer).transform.position.y, Mathf.Sin(num) * num2); float num3 = Vector2.Distance(new Vector2(((Component)localPlayer).transform.position.x, ((Component)localPlayer).transform.position.z), new Vector2(val.x, val.z)); if (num3 < 220f && (Object)(object)_odinMarker == (Object)null) { RaycastHit val2 = default(RaycastHit); if (Physics.Raycast(val + Vector3.up * 80f, Vector3.down, ref val2, 180f, -1, (QueryTriggerInteraction)1)) { val.y = ((RaycastHit)(ref val2)).point.y; } _odinMarker = GameObject.CreatePrimitive((PrimitiveType)2); ((Object)_odinMarker).name = "ChallengeHub_OdinsRaben_Fundort"; _odinMarker.transform.position = val + Vector3.up * 1.5f; _odinMarker.transform.localScale = new Vector3(1.3f, 3f, 1.3f); Renderer component = _odinMarker.GetComponent(); if ((Object)(object)component != (Object)null) { component.material.color = new Color(0.2f, 0.85f, 1f); component.material.EnableKeyword("_EMISSION"); component.material.SetColor("_EmissionColor", new Color(0.1f, 0.6f, 1f) * 2f); } Collider component2 = _odinMarker.GetComponent(); if ((Object)(object)component2 != (Object)null) { Object.Destroy((Object)(object)component2); } } if (num3 <= Mathf.Max(8f, worldEventRuntimeDefinition.Effects.DiscoveryRadius)) { _discoveredLocations.Add(worldEventRuntimeDefinition.Id); if ((Object)(object)_odinMarker != (Object)null) { Object.Destroy((Object)(object)_odinMarker); _odinMarker = null; } ((Character)localPlayer).Message((MessageType)2, "Odins Raben: Der verborgene Fundort wurde entdeckt", 0, (Sprite)null); if (!worldEventRuntimeDefinition.Simulation) { Plugin.Instance?.SendEvent("world_event_hidden_location_found", localPlayer, new Dictionary { { "eventId", worldEventRuntimeDefinition.Id }, { "scope", worldEventRuntimeDefinition.Id }, { "value", 1 }, { "points", worldEventRuntimeDefinition.Effects.DiscoveryPoints }, { "position", new Dictionary { { "x", val.x }, { "y", val.y }, { "z", val.z } } } }); } } else if (!(now < _nextClueUtc)) { _nextClueUtc = now.AddMinutes(Mathf.Max(2f, worldEventRuntimeDefinition.Effects.ClueIntervalMinutes)); Vector3 val3 = val - ((Component)localPlayer).transform.position; Vector3 normalized = ((Vector3)(ref val3)).normalized; string text = ((!(Mathf.Abs(normalized.x) > Mathf.Abs(normalized.z))) ? ((normalized.z > 0f) ? "Norden" : "Süden") : ((normalized.x > 0f) ? "Osten" : "Westen")); ((Character)localPlayer).Message((MessageType)2, "Odins Raben weisen nach " + text + " (noch etwa " + Mathf.RoundToInt(num3 / 100f) * 100 + " m)", 0, (Sprite)null); } } private void UpdateCorruptionNodes() { //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)ZNetScene.instance == (Object)null) { return; } WorldEventRuntimeDefinition worldEventRuntimeDefinition = _active.FirstOrDefault((WorldEventRuntimeDefinition entry) => IsEffectiveNow(entry) && string.Equals(entry.Key, "hels_schleier", StringComparison.OrdinalIgnoreCase)); if (worldEventRuntimeDefinition == null) { foreach (List value3 in _corruptionNodes.Values) { foreach (GameObject item in value3) { try { if ((Object)(object)item != (Object)null) { ZNetScene.instance.Destroy(item); } } catch { } } } _corruptionNodes.Clear(); _corruptionReported.Clear(); return; } if (!_corruptionNodes.TryGetValue(worldEventRuntimeDefinition.Id, out var value)) { value = new List(); _corruptionNodes[worldEventRuntimeDefinition.Id] = value; _corruptionReported[worldEventRuntimeDefinition.Id] = 0; Player val = Player.GetAllPlayers()?.FirstOrDefault(); GameObject val2 = ZNetScene.instance.GetPrefab("Spawner_DraugrPile") ?? ZNetScene.instance.GetPrefab("Spawner_Blob"); if ((Object)(object)val != (Object)null && (Object)(object)val2 != (Object)null) { RaycastHit val4 = default(RaycastHit); for (int num = 0; num < 5; num++) { float num2 = (float)num * (float)Math.PI * 2f / 5f; Vector3 val3 = ((Component)val).transform.position + new Vector3(Mathf.Cos(num2), 0f, Mathf.Sin(num2)) * Random.Range(90f, 150f); if (Physics.Raycast(val3 + Vector3.up * 60f, Vector3.down, ref val4, 140f, -1, (QueryTriggerInteraction)1)) { val3 = ((RaycastHit)(ref val4)).point; } value.Add(Object.Instantiate(val2, val3, Quaternion.identity)); } } } int num3 = value.Count((GameObject node) => (Object)(object)node == (Object)null); _corruptionReported.TryGetValue(worldEventRuntimeDefinition.Id, out var value2); if (num3 > value2) { _corruptionReported[worldEventRuntimeDefinition.Id] = num3; if (!worldEventRuntimeDefinition.Simulation) { Plugin.Instance?.SendServerEvent("world_event_corruption_cleansed", new Dictionary { { "eventId", worldEventRuntimeDefinition.Id }, { "scope", worldEventRuntimeDefinition.Id }, { "value", num3 - value2 }, { "target", 5 } }); } } if (num3 >= 5 && value2 < 5 && !worldEventRuntimeDefinition.Simulation) { Plugin.Instance?.SendServerEvent("world_event_completed", new Dictionary { { "eventId", worldEventRuntimeDefinition.Id }, { "scope", worldEventRuntimeDefinition.Id }, { "value", 1 }, { "method", "five_corruption_nodes_destroyed" } }); } } private void ApplyEnvironment() { WorldEventRuntimeDefinition worldEventRuntimeDefinition = _active.LastOrDefault((WorldEventRuntimeDefinition entry) => IsEffectiveNow(entry) && !string.IsNullOrWhiteSpace(entry.Effects?.ForcedEnvironment)); string text = worldEventRuntimeDefinition?.Effects?.ForcedEnvironment ?? string.Empty; if (string.Equals(text, _forcedEnvironment, StringComparison.Ordinal)) { return; } try { if ((Object)(object)EnvMan.instance != (Object)null) { EnvMan.instance.SetForceEnvironment(text); } _forcedEnvironment = text; if (worldEventRuntimeDefinition != null && _lastAnnouncement != worldEventRuntimeDefinition.Id) { _lastAnnouncement = worldEventRuntimeDefinition.Id; Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, worldEventRuntimeDefinition.Label + " hat begonnen", 0, (Sprite)null); } } if (worldEventRuntimeDefinition == null) { _lastAnnouncement = string.Empty; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Welt-Ereignis-Umgebung konnte nicht gesetzt werden: " + ex.Message)); } } } private void ApplyPlayerState() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead()) { return; } bool flag = _active.Any((WorldEventRuntimeDefinition entry) => IsEffectiveNow(entry) && (entry.Effects?.ColdEverywhere ?? false)); bool flag2 = _active.Any((WorldEventRuntimeDefinition entry) => IsEffectiveNow(entry) && (entry.Effects?.WetEverywhere ?? false)); try { if (flag) { SEMan sEMan = ((Character)localPlayer).GetSEMan(); if (sEMan != null) { sEMan.AddStatusEffect(StringExtensionMethods.GetStableHashCode("Cold"), true, 0, 0f); } } if (flag2) { SEMan sEMan2 = ((Character)localPlayer).GetSEMan(); if (sEMan2 != null) { sEMan2.AddStatusEffect(StringExtensionMethods.GetStableHashCode("Wet"), true, 0, 0f); } } } catch { } } private void OnDestroy() { try { if ((Object)(object)EnvMan.instance != (Object)null && !string.IsNullOrEmpty(_forcedEnvironment)) { EnvMan.instance.SetForceEnvironment(string.Empty); } } catch { } try { if (_originalWaterLevel.HasValue && (Object)(object)ZoneSystem.instance != (Object)null) { AccessTools.Field(((object)ZoneSystem.instance).GetType(), "m_waterLevel")?.SetValue(ZoneSystem.instance, _originalWaterLevel.Value); } } catch { } KeyValuePair[] array = _pickableRespawnOriginals.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; try { if ((Object)(object)keyValuePair.Key != (Object)null) { keyValuePair.Key.m_respawnTimeMinutes = keyValuePair.Value; } } catch { } } if (_fogCaptured) { RenderSettings.fog = _originalFog; RenderSettings.fogDensity = _originalFogDensity; _fogCaptured = false; } if ((Object)(object)_odinMarker != (Object)null) { Object.Destroy((Object)(object)_odinMarker); _odinMarker = null; } _active = new WorldEventRuntimeDefinition[0]; if ((Object)(object)_instance == (Object)(object)this) { _instance = null; } } } [HarmonyPatch] internal static class WorldEventMovementPatch { internal static MethodBase TargetMethod() { return AccessTools.Method(typeof(Character), "GetJogSpeedFactor", Type.EmptyTypes, (Type[])null); } private static void Postfix(Character __instance, ref float __result) { if ((Object)(object)__instance != (Object)null && __instance.IsPlayer()) { __result *= WorldEventGameplayFeature.Modifier((WorldEventRuntimeEffects effect) => effect.MovementMultiplier); } } } [HarmonyPatch] internal static class WorldEventShipForcePatch { internal static MethodBase TargetMethod() { return AccessTools.Method(typeof(Ship), "GetSailForce", (Type[])null, (Type[])null); } private static void Postfix(ref Vector3 __result) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) __result *= WorldEventGameplayFeature.Modifier((WorldEventRuntimeEffects effect) => effect.ShipSpeedMultiplier); } } [HarmonyPatch] internal static class WorldEventEnemyHealthPatch { private static readonly HashSet Scaled = new HashSet(); private static bool SpawningBonus; internal static MethodBase TargetMethod() { return AccessTools.Method(typeof(Character), "Awake", (Type[])null, (Type[])null); } private static void Postfix(Character __instance) { if ((Object)(object)__instance == (Object)null || __instance.IsPlayer() || !WorldEventGameplayFeature.AnyActive || !Scaled.Add(((Object)__instance).GetInstanceID())) { return; } float num = WorldEventGameplayFeature.Modifier(delegate(WorldEventRuntimeEffects effect) { string text = (((Object)__instance).name ?? string.Empty).ToLowerInvariant(); bool flag = text.Contains("serpent") || text.Contains("bonemaw"); bool num3 = text.Contains("skeleton") || text.Contains("draugr") || text.Contains("wraith") || text.Contains("ghost") || text.Contains("charred"); float num4 = (flag ? (effect.EnemyHealthMultiplier * effect.SeaCreatureHealthMultiplier) : effect.EnemyHealthMultiplier); if (num3) { num4 *= effect.UndeadHealthMultiplier; } return num4; }); if (Math.Abs(num - 1f) < 0.001f) { return; } try { float num2 = __instance.GetMaxHealth() * num; MethodInfo methodInfo = AccessTools.Method(typeof(Character), "SetMaxHealth", (Type[])null, (Type[])null); if (methodInfo == null) { return; } ParameterInfo[] parameters = methodInfo.GetParameters(); methodInfo.Invoke(__instance, (parameters.Length <= 1) ? new object[1] { num2 } : new object[2] { num2, true }); __instance.SetHealth(num2); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Welt-Ereignis Gegnerleben konnte nicht gesetzt werden: " + ex.Message)); } } TrySpawnBonus(__instance); } private static void TrySpawnBonus(Character character) { //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) if (SpawningBonus || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)character == (Object)null) { return; } string text = (((Object)character).name ?? string.Empty).ToLowerInvariant(); if (text.Contains("boss") || text.Contains("eikthyr") || text.Contains("gd_king") || text.Contains("bonemass") || text.Contains("dragonqueen") || text.Contains("goblinking") || text.Contains("queen") || text.Contains("fader")) { return; } bool num = text.Contains("serpent") || text.Contains("bonemaw"); bool flag = text.Contains("skeleton") || text.Contains("draugr") || text.Contains("wraith") || text.Contains("ghost") || text.Contains("charred"); float num2 = Mathf.Max(0f, WorldEventGameplayFeature.Modifier((WorldEventRuntimeEffects effect) => effect.EnemySpawnMultiplier) - 1f); if (num) { num2 = Mathf.Max(num2, WorldEventGameplayFeature.Modifier((WorldEventRuntimeEffects effect) => effect.OceanSpawnMultiplier) - 1f); } if (flag) { num2 = Mathf.Max(num2, WorldEventGameplayFeature.Modifier((WorldEventRuntimeEffects effect) => effect.UndeadSpawnMultiplier) - 1f); } if (num2 <= 0f || Random.value > Mathf.Clamp01(num2)) { return; } try { SpawningBonus = true; Object.Instantiate(((Component)character).gameObject, ((Component)character).transform.position + Random.insideUnitSphere * 2f, ((Component)character).transform.rotation); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Welt-Ereignis Zusatzspawn fehlgeschlagen: " + ex.Message)); } } finally { SpawningBonus = false; } } } [HarmonyPatch] internal static class WorldEventDropPatch { private sealed class Saved { internal object Entry; internal FieldInfo Min; internal FieldInfo Max; internal int MinValue; internal int MaxValue; } internal static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("CharacterDrop"); if (!(type == null)) { return AccessTools.Method(type, "DropItems", (Type[])null, (Type[])null); } return null; } private static void Prefix(object __instance, ref List __state) { float num = WorldEventGameplayFeature.Modifier((WorldEventRuntimeEffects effect) => effect.EnemyDropMultiplier); if (__instance == null || num <= 1.001f) { return; } try { if (!(AccessTools.Field(__instance.GetType(), "m_drops")?.GetValue(__instance) is IEnumerable enumerable)) { return; } __state = new List(); foreach (object item in enumerable) { FieldInfo fieldInfo = AccessTools.Field(item.GetType(), "m_amountMin"); FieldInfo fieldInfo2 = AccessTools.Field(item.GetType(), "m_amountMax"); if (!(fieldInfo == null) && !(fieldInfo2 == null)) { int num2 = Convert.ToInt32(fieldInfo.GetValue(item)); int num3 = Convert.ToInt32(fieldInfo2.GetValue(item)); __state.Add(new Saved { Entry = item, Min = fieldInfo, Max = fieldInfo2, MinValue = num2, MaxValue = num3 }); fieldInfo.SetValue(item, Math.Max(num2, Mathf.CeilToInt((float)num2 * num))); fieldInfo2.SetValue(item, Math.Max(num3, Mathf.CeilToInt((float)num3 * num))); } } } catch { Restore(__state); } } private static Exception Finalizer(Exception __exception, List __state) { Restore(__state); return __exception; } private static void Restore(List state) { if (state == null) { return; } foreach (Saved item in state) { try { item.Min.SetValue(item.Entry, item.MinValue); item.Max.SetValue(item.Entry, item.MaxValue); } catch { } } } } [HarmonyPatch] internal static class WorldEventCraftDurabilityPatch { private const string Key = "ChallengeHub.BaldurDurability"; internal static MethodBase TargetMethod() { return AccessTools.Method(typeof(InventoryGui), "DoCrafting", (Type[])null, (Type[])null); } private static void Prefix(ref HashSet __state) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { Inventory inventory = ((Humanoid)localPlayer).GetInventory(); __state = new HashSet(((inventory != null) ? inventory.GetAllItems() : null) ?? new List()); } } private static void Postfix(HashSet __state) { float num = WorldEventGameplayFeature.Modifier((WorldEventRuntimeEffects effect) => 1f + effect.CraftDurabilityBonus) - 1f; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || num <= 0f) { return; } Inventory inventory = ((Humanoid)localPlayer).GetInventory(); foreach (ItemData item in ((inventory != null) ? inventory.GetAllItems() : null) ?? new List()) { if (__state == null || !__state.Contains(item)) { item.m_customData["ChallengeHub.BaldurDurability"] = num.ToString(CultureInfo.InvariantCulture); } } } internal static float Bonus(ItemData item) { if (item?.m_customData == null) { return 0f; } if (!item.m_customData.TryGetValue("ChallengeHub.BaldurDurability", out var value) || !float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return 0f; } return Mathf.Max(0f, result); } } [HarmonyPatch] internal static class WorldEventDurabilityValuePatch { internal static MethodBase TargetMethod() { return AccessTools.Method(typeof(ItemData), "GetMaxDurability", Type.EmptyTypes, (Type[])null); } private static void Postfix(ItemData __instance, ref float __result) { __result *= 1f + WorldEventCraftDurabilityPatch.Bonus(__instance); } } [HarmonyPatch(typeof(Bed), "Interact")] internal static class WorldEventSleepBlockPatch { private static bool Prefix(Humanoid human) { if (!WorldEventGameplayFeature.SleepBlockedNow) { return true; } Player val = (Player)(object)((human is Player) ? human : null); if ((Object)(object)val != (Object)null && (Object)(object)val == (Object)(object)Player.m_localPlayer) { ((Character)val).Message((MessageType)2, "Fenrirs Blutmond verhindert den Schlaf", 0, (Sprite)null); } return false; } } [HarmonyPatch(typeof(Player), "UseStamina")] internal static class WorldEventStaminaPatch { private static void Prefix(ref float v) { v /= WorldEventGameplayFeature.Modifier((WorldEventRuntimeEffects effect) => effect.StaminaRegenMultiplier); } } [HarmonyPatch(typeof(Skills), "RaiseSkill")] internal static class WorldEventSkillPatch { private static void Prefix(ref float factor) { factor *= WorldEventGameplayFeature.Modifier((WorldEventRuntimeEffects effect) => effect.SkillGainMultiplier); } } [HarmonyPatch(typeof(Plant), "GetGrowTime")] internal static class WorldEventPlantGrowthPatch { private static void Postfix(ref float __result) { __result /= WorldEventGameplayFeature.Modifier((WorldEventRuntimeEffects effect) => effect.CropGrowthMultiplier); } } [HarmonyPatch(typeof(Character), "Damage")] internal static class WorldEventDamagePatch { private static void Prefix(Character __instance, HitData hit) { if ((Object)(object)__instance == (Object)null || hit == null) { return; } Character val = null; try { val = hit.GetAttacker(); } catch { } float num = 1f; if (__instance.IsPlayer() && (Object)(object)val != (Object)null && !val.IsPlayer()) { num *= WorldEventGameplayFeature.Modifier((WorldEventRuntimeEffects effect) => effect.EnemyDamageMultiplier); } if (!__instance.IsPlayer() && (Object)(object)val != (Object)null && val.IsPlayer()) { float num2 = WorldEventGameplayFeature.Modifier((WorldEventRuntimeEffects effect) => effect.FireDamageMultiplier); if (Math.Abs(num2 - 1f) > 0.001f) { hit.m_damage.m_fire *= num2; } } if (Math.Abs(num - 1f) > 0.001f) { ((DamageTypes)(ref hit.m_damage)).Modify(num); } } } internal static class WorldIntroductionFeature { private static readonly string[] Titles = new string[14] { "Willkommen bei Die Siegreichen Valheimer", "Warum dieser Server anders ist", "Mehr als einmal durchspielen", "Viele Wege führen zum Sieg", "Deine Challenge-Zentrale: F6", "Teilen, nachweisen und helfen: F10", "Wächtersteine, Siedlungen und Städte", "Partnerschaften und gemeinsamer Fortschritt", "Chronik und lebendige Welt", "Die Ruhmeshalle der Trophäen", "Erkundung und der Tiefe Norden", "Freischaltbare Komfortfunktionen", "Fair, automatisch und nachvollziehbar", "Eine Welt mit Zukunft" }; private static readonly string[] Texts = new string[14] { "Dieser Community-Server wurde für ein langfristiges Valheim-Abenteuer mit Challenges entwickelt – für Anfänger ebenso wie für erfahrene Wikinger. Du kannst allein losziehen, dich zeitweise mit anderen zusammenschließen oder dauerhaft im Team spielen. Es gibt nicht nur einen richtigen Weg: Dein Spielstil entscheidet, wie du zum Serverfortschritt beiträgst.", "Valheim funktioniert hervorragend allein und in kleinen festen Gruppen. Schwieriger wird es, wenn viele Spieler unabhängig beginnen, zu unterschiedlichen Zeiten weiterspielen und trotzdem Dörfer, Städte und eine gemeinsame Geschichte entstehen sollen. Genau dafür ist Die Siegreichen Valheimer ausgelegt: persönlicher Fortschritt und gemeinsamer Serverfortschritt können nebeneinander bestehen.", "Hier geht es nicht darum, Valheim einmal möglichst schnell durchzuspielen und die Welt danach zu verlassen. Challenges, Spezialisierungen, Partnerschaften, Siedlungen, Erkundung und wechselnde Ziele schaffen Spannung vor, während und nach dem klassischen Spielfortschritt. Auch nach einem Boss bleibt etwas zu entdecken, aufzubauen, zu handeln und für andere vorzubereiten.", "Punkte entstehen durch Bosse, Biome, Erkundung, Versorgung, Aufbau, Handwerk, Fähigkeiten und Teamaktionen. Kämpfer, Entdecker, Bauern, Baumeister und Sammler sind gleichwertige Wege. Die meisten Ziele werden automatisch erkannt und erklären, was bereits erfüllt ist und was noch fehlt.", "Drücke F6 für Übersicht, Einführung, Bosse, Biome, Ziele, Fähigkeiten, Partnerschaften und Diagnose. Mit Q und E wechselst du die Bereiche. Dort siehst du erreichte Ziele, Fortschrittsstände, zuletzt erhaltene Punkte und die Verbindung zu ChallengeHub.", "Drücke F10 für die Ingame-Kamera. Du kannst einen Screenshot als Community-Post, Nachweis oder Bugmeldung senden. Beschreibe kurz, was zu sehen ist. So bleiben besondere Momente erhalten und Fehler können mit einem echten Spielnachweis untersucht werden.", "Wächtersteine kennzeichnen geschützte und spezialisierte Bereiche. Sie schaffen eine Grundlage, damit einzelne Höfe, gemeinsame Siedlungen und später größere Städte in einer dauerhaft bewohnten Welt nebeneinander funktionieren. Baurechte, gebundene Kisten und Besitzerzuordnung werden nachvollziehbar verwaltet, damit niemand fremden Fortschritt blockiert oder zugerechnet bekommt.", "Partnerschaften verbinden Spieler für Handel, Erkundung und weitere gemeinsame Aktionen. Handelskisten ermöglichen nachvollziehbare Transfers; Erkundungspartner können Wissen und Fundorte teilen. Zustimmung und Rechte bleiben gegenseitig und transparent.", "Persönliche Chroniken, gemeinsame Meilensteine, wechselnde Wochenaufträge, Welt-Ereignisse, Rollen, Bauprojekte, Handel, Gerüchte, Titel, Rivalitäten, F10-Fotoaufträge und Community-Abstimmungen machen aus dem Server eine fortlaufende Kampagne. F6 zeigt bei jedem System den aktuellen Wert, deinen bisherigen Bestwert, das Ziel und die eindeutig vergebenen Punkte.", "Eine neutrale Wächterzone kann als Ruhmeshalle dienen. Hänge dort echte Kreaturen-Trophäen auf Item-Ständer. Jede bekannte Trophäenart gibt beim ersten Aufhängen 10 Punkte; doppelte Arten geben keine weiteren Punkte. Sobald alle für die aktuellen Biome definierten Trophäenarten ausgestellt wurden, vergibt ChallengeHub zusätzlich 500 Punkte.", "Die Welt soll entdeckt und nicht aus einer fertigen Lösung abgelesen werden. Eigene Funde, Wege, Boss-Hinweise, Händler, Biome und Trophäen werden schrittweise sichtbar. Mit Valheim 1.0 und dem Tiefen Norden liegt ein besonderer, noch unbekannter Abschnitt vor uns. Kartografie und Orientierung belohnen echte Erkundung und den Nutzen für andere Spieler.", "Die zusätzlichen Komfortfunktionen sind kein sofort vollständig aktives Cheat-Paket. Viele QoL-Fähigkeiten werden im Fähigkeitenbaum erst durch Spielen freigeschaltet. Du entscheidest selbst, welche Verbesserungen du verwenden möchtest, und kannst freigeschaltete Funktionen jederzeit wieder deaktivieren. So bleibt Valheim für unterschiedliche Vorlieben und Erfahrungsstufen spielbar.", "ChallengeHub speichert bestätigte Ereignisse, Ziele und Punkte dauerhaft. Belohnungen erscheinen sofort im Spiel und später in F6 sowie der Web-App. Bei ausstehender Übertragung gehen Ereignisse nicht verloren. Spiele natürlich – die Systeme werten echte Aktionen. Viel Erfolg auf deinem Weg zum siegreichen Valheimer!", "Die Siegreichen Valheimer ist als Community-Welt gedacht, die sich weiterentwickeln darf. Nach der Jagd auf das Böse und den Herausforderungen des Tiefen Nordens muss die Geschichte nicht enden. Wer weiß: Vielleicht warten auf die siegreichen Valheimer danach noch weitere Abenteuer, neue Challenges und die eine oder andere Season." }; internal static int PageCount => Titles.Length; internal static void Initialize(Plugin plugin) { if ((Object)(object)plugin != (Object)null && !Application.isBatchMode && (Object)(object)((Component)plugin).GetComponent() == (Object)null) { ((Component)plugin).gameObject.AddComponent(); } } internal static string Title(int page) { return Titles[Mathf.Clamp(page, 0, Titles.Length - 1)]; } internal static string Text(int page) { return Texts[Mathf.Clamp(page, 0, Texts.Length - 1)]; } internal static void DrawF6(Rect box, ref Vector2 scroll) { //IL_002c: 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_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) GUI.Label(new Rect(((Rect)(ref box)).x + 22f, ((Rect)(ref box)).y + 18f, ((Rect)(ref box)).width - 44f, 34f), "Einführung und Serverkonzept"); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref box)).x + 14f, ((Rect)(ref box)).y + 58f, ((Rect)(ref box)).width - 28f, ((Rect)(ref box)).height - 72f); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(0f, 0f, ((Rect)(ref val)).width - 18f, (float)Titles.Length * 122f); scroll = GUI.BeginScrollView(val, scroll, val2); float num = 4f; for (int i = 0; i < Titles.Length; i++) { GUI.Label(new Rect(16f, num, ((Rect)(ref val2)).width - 32f, 26f), i + 1 + ". " + Titles[i]); GUI.Label(new Rect(28f, num + 30f, ((Rect)(ref val2)).width - 56f, 82f), Texts[i]); num += 122f; } GUI.EndScrollView(); } } internal sealed class WorldIntroductionBehaviour : MonoBehaviour { private bool _visible; private int _page; private float _playerReadyAt = -1f; private string _key; private Rect _window = new Rect(0f, 0f, 850f, 520f); private void Update() { if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer()) { _playerReadyAt = -1f; return; } if (_playerReadyAt < 0f) { _playerReadyAt = Time.realtimeSinceStartup; } if (_visible && Input.GetKeyDown((KeyCode)27)) { Close(remember: false); } if (!_visible && Time.realtimeSinceStartup - _playerReadyAt >= 5f) { _key = BuildKey(); if (!PlayerPrefs.HasKey(_key)) { Open(); } } } private string BuildKey() { long num = 0L; long num2 = 0L; try { num = Player.m_localPlayer.GetPlayerID(); } catch { } try { num2 = ZNet.instance.GetWorldUID(); } catch { } return "ChallengeHub.Introduction.v1." + num2 + "." + num; } private void Open() { _page = 0; _visible = true; ChallengeHubCursorController.Acquire("world-introduction"); } private void Close(bool remember) { if (remember && !string.IsNullOrWhiteSpace(_key)) { PlayerPrefs.SetInt(_key, 1); PlayerPrefs.Save(); } _visible = false; ChallengeHubCursorController.Release("world-introduction"); } private void OnGUI() { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) if (_visible) { ChallengeHubWindowTheme.Apply(); ((Rect)(ref _window)).width = Mathf.Min(850f, (float)Screen.width - 30f); ((Rect)(ref _window)).height = Mathf.Min(520f, (float)Screen.height - 30f); ((Rect)(ref _window)).x = ((float)Screen.width - ((Rect)(ref _window)).width) * 0.5f; ((Rect)(ref _window)).y = ((float)Screen.height - ((Rect)(ref _window)).height) * 0.5f; _window = GUI.Window("ChallengeHub_WorldIntroduction".GetHashCode(), _window, new WindowFunction(DrawWindow), "Die Siegreichen Valheimer – Einführung"); } } private void DrawWindow(int id) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) ChallengeHubWindowTheme.DrawPanel(new Rect(18f, 54f, ((Rect)(ref _window)).width - 36f, ((Rect)(ref _window)).height - 126f)); GUI.Label(new Rect(42f, 76f, ((Rect)(ref _window)).width - 84f, 44f), _page + 1 + " / " + WorldIntroductionFeature.PageCount + " " + WorldIntroductionFeature.Title(_page), ChallengeHubWindowTheme.Title); GUI.Label(new Rect(48f, 132f, ((Rect)(ref _window)).width - 96f, ((Rect)(ref _window)).height - 230f), WorldIntroductionFeature.Text(_page), ChallengeHubWindowTheme.Label); if (_page > 0 && GUI.Button(new Rect(28f, ((Rect)(ref _window)).height - 58f, 150f, 38f), "Zurück")) { _page--; } if (GUI.Button(new Rect(((Rect)(ref _window)).width * 0.5f - 95f, ((Rect)(ref _window)).height - 58f, 190f, 38f), "Später über F6")) { Close(remember: true); } if (_page < WorldIntroductionFeature.PageCount - 1) { if (GUI.Button(new Rect(((Rect)(ref _window)).width - 178f, ((Rect)(ref _window)).height - 58f, 150f, 38f), "Weiter")) { _page++; } } else if (GUI.Button(new Rect(((Rect)(ref _window)).width - 208f, ((Rect)(ref _window)).height - 58f, 180f, 38f), "Abenteuer beginnen")) { Close(remember: true); } } } internal static class WorldRenaturationFeature { private sealed class RenaturationVisual { private static Material _mossMaterial; private readonly GameObject _root; private readonly bool _path; private int _stage; internal RenaturationVisual(GameObject owner, bool path) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_003f: Unknown result type (might be due to invalid IL or missing references) _path = path; _root = new GameObject("ChallengeHub_RenaturationVisual"); _root.transform.SetParent(owner.transform, false); _root.transform.localPosition = Vector3.zero; } internal void SetStage(int stage) { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Expected O, but got Unknown stage = Mathf.Clamp(stage, 0, _path ? 4 : 5); if (_stage == stage) { return; } _stage = stage; while (_root.transform.childCount > 0) { Object.Destroy((Object)(object)((Component)_root.transform.GetChild(0)).gameObject); } for (int i = 0; i < stage; i++) { GameObject obj = GameObject.CreatePrimitive((PrimitiveType)5); ((Object)obj).name = "MossPatch"; obj.transform.SetParent(_root.transform, false); float num = (float)i * 137.5f * ((float)Math.PI / 180f); obj.transform.localPosition = new Vector3(Mathf.Cos(num) * (0.25f + (float)i * 0.08f), 0.04f + (float)i * 0.01f, Mathf.Sin(num) * (0.25f + (float)i * 0.08f)); obj.transform.localRotation = Quaternion.Euler(90f, (float)i * 71f, 0f); obj.transform.localScale = Vector3.one * (0.25f + (float)i * 0.05f); Collider component = obj.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } Renderer component2 = obj.GetComponent(); if (!((Object)(object)component2 != (Object)null)) { continue; } if ((Object)(object)_mossMaterial == (Object)null) { Shader val = Shader.Find("Unlit/Color") ?? Shader.Find("Standard"); if ((Object)(object)val != (Object)null) { _mossMaterial = new Material(val) { color = new Color(0.22f, 0.48f, 0.16f, 0.72f) }; } } if ((Object)(object)_mossMaterial != (Object)null) { component2.sharedMaterial = _mossMaterial; } component2.shadowCastingMode = (ShadowCastingMode)0; } } internal void Destroy() { if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); } } } private const string KindKey = "ChallengeHub.Renaturation.Kind"; private const string LastSupportedKey = "ChallengeHub.Renaturation.LastSupportedUtc"; private const string StageKey = "ChallengeHub.Renaturation.Stage"; private const string SchemaKey = "ChallengeHub.Renaturation.Schema"; private const string Schema = "2.8.0"; private static Plugin _plugin; private static ConfigEntry _enabled; private static ConfigEntry _requireWorkbenchPaths; private static ConfigEntry _workbenchRadius; private static ConfigEntry _scanSeconds; private static ConfigEntry _maxPerPass; private static ConfigEntry _pathGraceHours; private static ConfigEntry _pathStageHours; private static ConfigEntry _buildingGraceHours; private static ConfigEntry _buildingStageHours; private static ConfigEntry _finalRemoval; private static ConfigEntry _finalRemovalHours; private static ConfigEntry _protectedKeywords; private static readonly Dictionary Paths = new Dictionary(); private static readonly Dictionary Buildings = new Dictionary(); private static readonly Dictionary Workbenches = new Dictionary(); private static readonly Dictionary Visuals = new Dictionary(); private static readonly List Stale = new List(); private static RenaturationWebConfig Remote => _plugin?.RemoteConfig?.renaturation; internal static bool Enabled { get { RenaturationWebConfig remote = Remote; if (remote == null || remote.enabled) { if (_enabled != null) { return _enabled.Value; } return true; } return false; } } internal static float WorkbenchRadius { get { RenaturationWebConfig remote = Remote; if (remote == null || !(remote.workbenchRadius > 0f)) { return Mathf.Clamp(_workbenchRadius?.Value ?? 20f, 5f, 50f); } return Mathf.Clamp(Remote.workbenchRadius, 5f, 50f); } } internal static void Initialize(Plugin plugin) { if (!((Object)(object)plugin == (Object)null)) { _plugin = plugin; _enabled = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Renaturation", "Enabled", true, "Aktiviert Werkbank-gebundene Wege und schrittweisen Siedlungsverfall."); _requireWorkbenchPaths = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Renaturation", "RequireWorkbenchForPaths", true, "Wege/Pflaster koennen nur mit Werkbank in Reichweite angelegt werden."); _workbenchRadius = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Renaturation", "WorkbenchRadius", 20f, "Werkbankreichweite fuer Wege und Pflege."); _scanSeconds = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Renaturation", "ScanSeconds", 60f, "Intervall des ereignisbasierten Caches; es findet kein Welt-Vollscan statt."); _maxPerPass = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Renaturation", "MaxObjectsPerPass", 64, "Maximale geladene Pfade/Bauteile pro Frame-Batch."); _pathGraceHours = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Renaturation", "PathGraceHours", 24f, "Schonzeit eines unversorgten Weges."); _pathStageHours = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Renaturation", "PathStageHours", 12f, "Stunden pro Verwilderungsstufe eines Weges."); _buildingGraceHours = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Renaturation", "BuildingGraceHours", 168f, "Schonzeit einer inaktiven Siedlung vor optischer Verwitterung."); _buildingStageHours = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Renaturation", "BuildingStageHours", 72f, "Stunden pro optischer Gebaeudeverfallsstufe."); _finalRemoval = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Renaturation", "FinalBuildingRemovalEnabled", true, "Erlaubt nach sehr langer Inaktivitaet die sichere Entfernung ungeschuetzter Bauteile."); _finalRemovalHours = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Renaturation", "FinalBuildingRemovalHours", 720f, "Gesamtstunden ohne Pflege bis ungeschuetzte Bauteile entfernt werden duerfen."); _protectedKeywords = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.Renaturation", "ProtectedPrefabKeywords", "container,chest,portal,ward,guard,bed,workbench,forge,stonecutter,artisan,sign,ship,cart,raft,karve,challengehub", "Diese Objekte werden niemals automatisch entfernt."); ((MonoBehaviour)plugin).StartCoroutine(LifecycleLoop()); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"WorldRenaturation 2.8.0 aktiv: keine Overworld-Zonenresets, nur gezielte Pfad-/Siedlungspflege."); } } } internal static void RegisterPiece(Piece piece) { if (!((Object)(object)piece == (Object)null) && !((Object)(object)((Component)piece).gameObject == (Object)null)) { int instanceID = ((Object)((Component)piece).gameObject).GetInstanceID(); if (IsTerrainChangePiece(piece) && IsPlayerPiece(piece)) { Paths[instanceID] = piece; Buildings.Remove(instanceID); } else if (IsPlayerPiece(piece)) { Buildings[instanceID] = piece; Paths.Remove(instanceID); } } } internal static void UnregisterPiece(Piece piece) { if (!((Object)(object)piece == (Object)null) && !((Object)(object)((Component)piece).gameObject == (Object)null)) { int instanceID = ((Object)((Component)piece).gameObject).GetInstanceID(); Paths.Remove(instanceID); Buildings.Remove(instanceID); if (Visuals.TryGetValue(instanceID, out var value)) { value.Destroy(); Visuals.Remove(instanceID); } } } internal static void RegisterStation(CraftingStation station) { if (!((Object)(object)station == (Object)null) && !((Object)(object)((Component)station).gameObject == (Object)null) && LooksLikeWorkbench(((Component)station).gameObject)) { Workbenches[((Object)((Component)station).gameObject).GetInstanceID()] = station; } } internal static void UnregisterStation(CraftingStation station) { if (!((Object)(object)station == (Object)null) && !((Object)(object)((Component)station).gameObject == (Object)null)) { Workbenches.Remove(((Object)((Component)station).gameObject).GetInstanceID()); } } internal static bool HasWorkbench(Vector3 position, float radius = 0f) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) float num = ((radius > 0f) ? radius : WorkbenchRadius); float num2 = num * num; bool result = false; Stale.Clear(); foreach (KeyValuePair workbench in Workbenches) { CraftingStation value = workbench.Value; if ((Object)(object)value == (Object)null || (Object)(object)((Component)value).gameObject == (Object)null || !((Component)value).gameObject.activeInHierarchy) { Stale.Add(workbench.Key); continue; } Vector3 val = ((Component)value).transform.position - position; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude <= num2) { result = true; } } foreach (int item in Stale) { Workbenches.Remove(item); } return result; } internal static bool IsPathPiece(Piece piece) { if ((Object)(object)piece == (Object)null) { return false; } TerrainModifier val = ((Component)piece).GetComponent() ?? ((Component)piece).GetComponentInChildren(true); if ((Object)(object)val == (Object)null) { return false; } try { FieldInfo fieldInfo = AccessTools.Field(((object)val).GetType(), "m_paintCleared"); if (fieldInfo != null) { object value = fieldInfo.GetValue(val); if (value is bool && !(bool)value) { return false; } } string text = Convert.ToString(AccessTools.Field(((object)val).GetType(), "m_paintType")?.GetValue(val), CultureInfo.InvariantCulture) ?? string.Empty; string text2 = Utils.GetPrefabName(((Component)piece).gameObject).ToLowerInvariant(); return text.IndexOf("Dirt", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Paved", StringComparison.OrdinalIgnoreCase) >= 0 || text2.Contains("path") || text2.Contains("road") || text2.Contains("paved"); } catch { return false; } } internal static bool IsTerrainChangePiece(Piece piece) { if ((Object)(object)piece == (Object)null) { return false; } if (!((Object)(object)((Component)piece).GetComponent() != (Object)null)) { return (Object)(object)((Component)piece).GetComponentInChildren(true) != (Object)null; } return true; } internal static bool CanPlacePath(Player player, Piece piece, Vector3 position) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (!Enabled || (Object)(object)player == (Object)null || (Object)(object)piece == (Object)null || !RequiresWorkbenchForTerrainAction(piece)) { return true; } if (!WorkbenchRequiredForPaths() || HasWorkbench(position)) { return true; } ((Character)player).Message((MessageType)2, "Bodenarbeiten und Wege benoetigen eine Werkbank in der Naehe.", 0, (Sprite)null); return false; } private static bool RequiresWorkbenchForTerrainAction(Piece piece) { if ((Object)(object)piece == (Object)null) { return false; } TerrainModifier val = ((Component)piece).GetComponent() ?? ((Component)piece).GetComponentInChildren(true); if ((Object)(object)val == (Object)null) { return false; } if (IsPathPiece(piece)) { return true; } try { FieldInfo fieldInfo = AccessTools.Field(((object)val).GetType(), "m_level"); bool flag = default(bool); int num; if (fieldInfo != null) { object value = fieldInfo.GetValue(val); if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } catch { return false; } } private static bool WorkbenchRequiredForPaths() { RenaturationWebConfig remote = Remote; if (remote == null) { if (_requireWorkbenchPaths != null) { return _requireWorkbenchPaths.Value; } return true; } return remote.requireWorkbenchForPaths; } internal static void QueueServerPathRequirement(Piece piece) { if (!((Object)(object)_plugin == (Object)null) && !((Object)(object)piece == (Object)null)) { ((MonoBehaviour)_plugin).StartCoroutine(EnforceServerPathRequirementDelayed(piece)); } } private static IEnumerator EnforceServerPathRequirementDelayed(Piece piece) { if (ChallengeHubServerGateFeature.GameplayAllowed) { yield return null; yield return null; EnforceServerPathRequirement(piece); } } internal static void EnforceServerPathRequirement(Piece piece) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (!ChallengeHubWorldState.IsServer || !Enabled || (Object)(object)piece == (Object)null || !IsPathPiece(piece) || !IsPlayerPiece(piece) || !WorkbenchRequiredForPaths() || HasWorkbench(((Component)piece).transform.position)) { return; } try { ZNetView val = ((Component)piece).GetComponent() ?? ((Component)piece).GetComponentInParent(); ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if (val2 != null) { ServerAuthoritativeZdoDestroyer.Destroy(val2, ServerAuthoritativeZdoDestroyer.CollectProtectedPlayerZdos(), "path_without_workbench"); ServerAuthoritativeZdoDestroyer.FlushDestroyed("path_without_workbench"); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Weg ohne Werkbank serverseitig entfernt: " + Utils.GetPrefabName(((Component)piece).gameObject))); } } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Serverpruefung fuer Werkbank-Weg fehlgeschlagen: " + ex.Message)); } } } private static IEnumerator LifecycleLoop() { while (true) { if (!ChallengeHubServerGateFeature.GameplayAllowed) { yield return (object)new WaitForSeconds(5f); continue; } RenaturationWebConfig remote = Remote; float num = ((remote != null && remote.scanSeconds > 0f) ? Remote.scanSeconds : (_scanSeconds?.Value ?? 60f)); yield return (object)new WaitForSeconds(Mathf.Clamp(num, 10f, 600f)); if (!Enabled) { continue; } RenaturationWebConfig remote2 = Remote; int budget = ((remote2 != null && remote2.maxObjectsPerPass > 0) ? Remote.maxObjectsPerPass : (_maxPerPass?.Value ?? 64)); budget = Mathf.Clamp(budget, 8, 500); int processed = 0; Piece[] array = Paths.Values.ToArray(); foreach (Piece val in array) { if (!((Object)(object)val == (Object)null)) { ProcessPath(val); int num2 = processed + 1; processed = num2; if (num2 >= budget) { processed = 0; yield return null; } } } array = Buildings.Values.ToArray(); foreach (Piece val2 in array) { if (!((Object)(object)val2 == (Object)null)) { ProcessBuilding(val2); int num2 = processed + 1; processed = num2; if (num2 >= budget) { processed = 0; yield return null; } } } } } private static void ProcessPath(Piece piece) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)piece == (Object)null || (Object)(object)((Component)piece).gameObject == (Object)null) { return; } ZNetView val = ((Component)piece).GetComponent() ?? ((Component)piece).GetComponentInParent(); ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if (val2 != null) { bool flag = HasWorkbench(((Component)piece).transform.position) || ZoneActivityRestorationFeature.IsOccupied(((Component)piece).transform.position); bool flag2 = IsPathPiece(piece); string kind = (flag2 ? "path" : "terrain"); int num = ComputeStage(val2, kind, flag, EffectivePathGraceHours(), EffectivePathStageHours(), 4); ApplyVisual(piece, num, path: true); if (ChallengeHubWorldState.IsServer && num >= 4 && !flag) { RemoveExactPiece(piece, val2, flag2 ? "path_returned_to_origin" : "terrain_returned_to_origin"); } } } private static void ProcessBuilding(Piece piece) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)piece == (Object)null || (Object)(object)((Component)piece).gameObject == (Object)null || !IsPlayerPiece(piece)) { return; } long num = 0L; try { num = piece.GetCreator(); } catch { } if (num == 0L) { return; } ZNetView val = ((Component)piece).GetComponent() ?? ((Component)piece).GetComponentInParent(); ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if (val2 == null) { return; } bool flag = HasWorkbench(((Component)piece).transform.position) || ZoneActivityRestorationFeature.IsOccupied(((Component)piece).transform.position); int num2 = ComputeStage(val2, "building", flag, EffectiveBuildingGraceHours(), EffectiveBuildingStageHours(), 5); ApplyVisual(piece, num2, path: false); if (!(!ChallengeHubWorldState.IsServer || num2 < 5 || !EffectiveFinalRemoval() || flag)) { DateTime dateTime = ParseUtc(val2.GetString("ChallengeHub.Renaturation.LastSupportedUtc", string.Empty)); if (!(((dateTime == DateTime.MinValue) ? 0.0 : (DateTime.UtcNow - dateTime).TotalHours) < (double)EffectiveFinalRemovalHours()) && !IsProtectedBuilding(piece) && !PlayerNear(((Component)piece).transform.position, 25f)) { RemoveExactPiece(piece, val2, "abandoned_settlement_returned_to_nature"); } } } private static int ComputeStage(ZDO zdo, string kind, bool supported, float graceHours, float stageHours, int maxStage) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) DateTime utcNow = DateTime.UtcNow; DateTime dateTime = ParseUtc(zdo.GetString("ChallengeHub.Renaturation.LastSupportedUtc", string.Empty)); int num = zdo.GetInt("ChallengeHub.Renaturation.Stage", 0); DateTime dateTime2 = ZoneActivityRestorationFeature.LastExitedAt(zdo.GetPosition()); if (supported) { if (ChallengeHubWorldState.IsServer && (dateTime == DateTime.MinValue || (utcNow - dateTime).TotalSeconds >= 20.0 || num != 0)) { Take(zdo, "renaturation_supported"); zdo.Set("ChallengeHub.Renaturation.Kind", kind); zdo.Set("ChallengeHub.Renaturation.Schema", "2.8.0"); zdo.Set("ChallengeHub.Renaturation.LastSupportedUtc", utcNow.ToString("O", CultureInfo.InvariantCulture)); if (num != 0) { zdo.Set("ChallengeHub.Renaturation.Stage", 0); } } return 0; } if (dateTime == DateTime.MinValue) { dateTime = ((dateTime2 != DateTime.MinValue) ? dateTime2 : utcNow); if (ChallengeHubWorldState.IsServer) { Take(zdo, "renaturation_first_seen"); zdo.Set("ChallengeHub.Renaturation.Kind", kind); zdo.Set("ChallengeHub.Renaturation.Schema", "2.8.0"); zdo.Set("ChallengeHub.Renaturation.LastSupportedUtc", dateTime.ToString("O", CultureInfo.InvariantCulture)); } return 0; } if (dateTime2 > dateTime) { dateTime = dateTime2; if (ChallengeHubWorldState.IsServer) { Take(zdo, "renaturation_zone_exit"); zdo.Set("ChallengeHub.Renaturation.LastSupportedUtc", dateTime.ToString("O", CultureInfo.InvariantCulture)); if (num != 0) { zdo.Set("ChallengeHub.Renaturation.Stage", 0); } } num = 0; } double totalHours = (utcNow - dateTime).TotalHours; int num2 = ((!(totalHours <= (double)graceHours)) ? Mathf.Clamp(1 + Mathf.FloorToInt((float)((totalHours - (double)graceHours) / (double)Math.Max(0.25f, stageHours))), 1, maxStage) : 0); if (ChallengeHubWorldState.IsServer && num2 != num) { Take(zdo, "renaturation_stage"); zdo.Set("ChallengeHub.Renaturation.Stage", num2); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Renaturierungsstufe " + num2 + " fuer " + kind + ": " + ((object)zdo.GetPosition()/*cast due to .constrained prefix*/).ToString())); } MeadowsSettlementRecord meadowsSettlementRecord = MeadowsSettlementFeature.FindAt(zdo.GetPosition()); _plugin?.SendServerEvent("renaturation_stage", new Dictionary { { "targetKind", kind }, { "stage", num2 }, { "position", ChallengeHubWorldState.Vector(zdo.GetPosition()) }, { "lastSupportedAt", zdo.GetString("ChallengeHub.Renaturation.LastSupportedUtc", string.Empty) }, { "settlementKey", meadowsSettlementRecord?.key ?? string.Empty }, { "ownerPlayerId", meadowsSettlementRecord?.playerId ?? string.Empty }, { "ownerPlayerName", meadowsSettlementRecord?.playerName ?? string.Empty } }); } return num2; } private static void ApplyVisual(Piece piece, int stage, bool path) { //IL_009c: 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_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) int instanceID = ((Object)((Component)piece).gameObject).GetInstanceID(); if (stage <= 0) { if (Visuals.TryGetValue(instanceID, out var value)) { value.Destroy(); Visuals.Remove(instanceID); } ClearTint(((Component)piece).gameObject); return; } if (!Visuals.TryGetValue(instanceID, out var value2)) { value2 = new RenaturationVisual(((Component)piece).gameObject, path); Visuals[instanceID] = value2; } value2.SetStage(stage); Color color = (path ? Color.Lerp(Color.white, new Color(0.55f, 0.72f, 0.48f, 1f), (float)stage / 4f) : Color.Lerp(Color.white, new Color(0.48f, 0.62f, 0.42f, 1f), (float)stage / 5f)); Tint(((Component)piece).gameObject, color); } private static void Tint(GameObject obj, Color color) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0036: 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)obj == (Object)null) { return; } MaterialPropertyBlock val = new MaterialPropertyBlock(); Renderer[] componentsInChildren = obj.GetComponentsInChildren(true); foreach (Renderer val2 in componentsInChildren) { if (!((Object)(object)val2 == (Object)null)) { val2.GetPropertyBlock(val); val.SetColor("_Color", color); val.SetColor("_BaseColor", color); val2.SetPropertyBlock(val); } } } private static void ClearTint(GameObject obj) { if ((Object)(object)obj == (Object)null) { return; } Renderer[] componentsInChildren = obj.GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val != (Object)null) { val.SetPropertyBlock((MaterialPropertyBlock)null); } } } private static void RemoveExactPiece(Piece piece, ZDO zdo, string reason) { //IL_001b: 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_0069: Unknown result type (might be due to invalid IL or missing references) try { HashSet protectedIds = ServerAuthoritativeZdoDestroyer.CollectProtectedPlayerZdos(); ServerAuthoritativeZdoDestroyer.Destroy(zdo, protectedIds, reason); ServerAuthoritativeZdoDestroyer.FlushDestroyed(reason); Heightmap obj = Heightmap.FindHeightmap(((Component)piece).transform.position); if (obj != null) { obj.Poke(true); } MeadowsSettlementRecord meadowsSettlementRecord = MeadowsSettlementFeature.FindAt(((Component)piece).transform.position); Dictionary extra = new Dictionary { { "targetPrefab", Utils.GetPrefabName(((Component)piece).gameObject) }, { "position", ChallengeHubWorldState.Vector(((Component)piece).transform.position) }, { "reason", reason }, { "settlementKey", meadowsSettlementRecord?.key ?? string.Empty }, { "ownerPlayerId", meadowsSettlementRecord?.playerId ?? string.Empty }, { "ownerPlayerName", meadowsSettlementRecord?.playerName ?? string.Empty } }; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { _plugin?.SendEvent("renaturation_completed", localPlayer, extra); } else { _plugin?.SendServerEvent("renaturation_completed", extra); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Renaturierung konnte Objekt nicht entfernen: " + ex.Message)); } } } private static bool IsProtectedBuilding(Piece piece) { if ((Object)(object)piece == (Object)null) { return true; } string text = Utils.GetPrefabName(((Component)piece).gameObject).ToLowerInvariant(); string[] array = ((Remote != null && !string.IsNullOrWhiteSpace(Remote.protectedPrefabKeywords)) ? Remote.protectedPrefabKeywords : (_protectedKeywords?.Value ?? string.Empty)).Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim().ToLowerInvariant(); if (text2.Length > 0 && text.Contains(text2)) { return true; } } Container val = ((Component)piece).GetComponent() ?? ((Component)piece).GetComponentInChildren(true); if (((val != null) ? val.GetInventory() : null) != null && val.GetInventory().NrOfItemsIncludingStacks() > 0) { return true; } if ((Object)(object)((Component)piece).GetComponentInChildren(true) != (Object)null || (Object)(object)((Component)piece).GetComponentInChildren(true) != (Object)null || (Object)(object)((Component)piece).GetComponentInChildren(true) != (Object)null || (Object)(object)((Component)piece).GetComponentInChildren(true) != (Object)null || (Object)(object)((Component)piece).GetComponentInChildren(true) != (Object)null) { return true; } return false; } private static bool IsPlayerPiece(Piece piece) { if ((Object)(object)piece == (Object)null) { return false; } try { return piece.GetCreator() != 0; } catch { return false; } } private static bool LooksLikeWorkbench(GameObject obj) { string text = Utils.GetPrefabName(obj).ToLowerInvariant(); if (!text.Contains("workbench")) { return text.Contains("piece_workbench"); } return true; } private static bool PlayerNear(Vector3 position, float radius) { //IL_0024: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) try { foreach (Player allPlayer in Player.GetAllPlayers()) { if (!((Object)(object)allPlayer == (Object)null)) { Vector3 val = ((Component)allPlayer).transform.position - position; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude <= radius * radius) { return true; } } } } catch { } return false; } private static void Take(ZDO zdo, string reason) { try { ValheimNetworkCompatibility.TryTakeServerOwnership(zdo, reason); } catch { } } private static DateTime ParseUtc(string raw) { if (!DateTime.TryParse(raw, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out var result)) { return DateTime.MinValue; } return result; } private static float EffectivePathGraceHours() { RenaturationWebConfig remote = Remote; return Mathf.Max(0f, (remote != null && remote.pathGraceHours > 0f) ? Remote.pathGraceHours : (_pathGraceHours?.Value ?? 24f)); } private static float EffectivePathStageHours() { RenaturationWebConfig remote = Remote; return Mathf.Max(0.25f, (remote != null && remote.pathStageHours > 0f) ? Remote.pathStageHours : (_pathStageHours?.Value ?? 12f)); } private static float EffectiveBuildingGraceHours() { RenaturationWebConfig remote = Remote; return Mathf.Max(0f, (remote != null && remote.buildingGraceHours > 0f) ? Remote.buildingGraceHours : (_buildingGraceHours?.Value ?? 168f)); } private static float EffectiveBuildingStageHours() { RenaturationWebConfig remote = Remote; return Mathf.Max(1f, (remote != null && remote.buildingStageHours > 0f) ? Remote.buildingStageHours : (_buildingStageHours?.Value ?? 72f)); } private static bool EffectiveFinalRemoval() { RenaturationWebConfig remote = Remote; if (remote == null) { if (_finalRemoval != null) { return _finalRemoval.Value; } return false; } return remote.finalBuildingRemovalEnabled; } private static float EffectiveFinalRemovalHours() { RenaturationWebConfig remote = Remote; return Mathf.Max(168f, (remote != null && remote.finalBuildingRemovalHours > 0f) ? Remote.finalBuildingRemovalHours : (_finalRemovalHours?.Value ?? 720f)); } } [HarmonyPatch] internal static class RenaturationPieceLifecyclePatch { private static IEnumerable TargetMethods() { foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(Piece))) { if (declaredMethod.Name == "Awake" || declaredMethod.Name == "SetCreator" || declaredMethod.Name == "OnPlaced" || declaredMethod.Name == "OnDestroy") { yield return declaredMethod; } } } [HarmonyPostfix] private static void Postfix(Piece __instance, MethodBase __originalMethod) { if ((Object)(object)__instance == (Object)null) { return; } if (__originalMethod?.Name == "OnDestroy") { WorldRenaturationFeature.UnregisterPiece(__instance); return; } WorldRenaturationFeature.RegisterPiece(__instance); if (__originalMethod?.Name == "SetCreator") { WorldRenaturationFeature.QueueServerPathRequirement(__instance); } } } [HarmonyPatch] internal static class RenaturationWorkbenchLifecyclePatch { private static IEnumerable TargetMethods() { foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(CraftingStation))) { if (declaredMethod.Name == "Awake" || declaredMethod.Name == "OnDestroy") { yield return declaredMethod; } } } [HarmonyPostfix] private static void Postfix(CraftingStation __instance, MethodBase __originalMethod) { if (!((Object)(object)__instance == (Object)null)) { if (__originalMethod?.Name == "OnDestroy") { WorldRenaturationFeature.UnregisterStation(__instance); } else { WorldRenaturationFeature.RegisterStation(__instance); } } } } [HarmonyPatch(typeof(Player), "PlacePiece", new Type[] { typeof(Piece), typeof(Vector3), typeof(Quaternion), typeof(bool) })] internal static class RenaturationPathRequirementPatch { [HarmonyPrefix] private static bool Prefix(Player __instance) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } Piece selectedPiece = PlayerBuildReflection.GetSelectedPiece(__instance); GameObject placementGhost = PlayerBuildReflection.GetPlacementGhost(__instance); if ((Object)(object)selectedPiece == (Object)null || (Object)(object)placementGhost == (Object)null) { return true; } if (WorldRenaturationFeature.CanPlacePath(__instance, selectedPiece, placementGhost.transform.position)) { return true; } return false; } } [HarmonyPatch(typeof(Container), "Interact")] internal static class SettlementContainerActivityPatch { [HarmonyPostfix] private static void Postfix(Container __instance, Humanoid character, bool __result) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (__result && (Object)(object)character == (Object)(object)Player.m_localPlayer && (Object)(object)__instance != (Object)null) { MeadowsSettlementFeature.TouchLocal(((Component)__instance).transform.position, "container_used"); } } } [HarmonyPatch(typeof(Door), "Interact")] internal static class SettlementDoorActivityPatch { [HarmonyPostfix] private static void Postfix(Door __instance, Humanoid character, bool __result) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (__result && (Object)(object)character == (Object)(object)Player.m_localPlayer && (Object)(object)__instance != (Object)null) { MeadowsSettlementFeature.TouchLocal(((Component)__instance).transform.position, "door_used"); } } } [Serializable] internal sealed class ZoneActivityRecord { public long worldUid; public int zoneX; public int zoneY; public string biome = string.Empty; public bool wasVisited; public string lastExitedUtc = string.Empty; public string lastResetUtc = string.Empty; } [Serializable] internal sealed class ZoneActivityStore { public ZoneActivityRecord[] zones = new ZoneActivityRecord[0]; } internal static class ZoneActivityRestorationFeature { private static Plugin _plugin; private static ConfigEntry _enabled; private static ConfigEntry _scanSeconds; private static ConfigEntry _meadowsDays; private static ConfigEntry _blackForestDays; private static ConfigEntry _swampDays; private static ConfigEntry _mountainDays; private static ConfigEntry _plainsDays; private static ConfigEntry _mistlandsDays; private static ConfigEntry _ashlandsDays; private static ConfigEntry _deepNorthDays; private static readonly Dictionary Records = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary PlayerZones = new Dictionary(); private static bool _loaded; private static bool _dirty; private static string StorePath => Path.Combine(Paths.ConfigPath, "ChallengeHubValheim", "zone-activity.json"); private static ZoneRestorationWebConfig Remote => _plugin?.RemoteConfig?.zoneRestoration; internal static bool Enabled { get { ZoneRestorationWebConfig remote = Remote; if (remote == null || remote.enabled) { if (_enabled != null) { return _enabled.Value; } return true; } return false; } } internal static void Initialize(Plugin plugin) { _plugin = plugin; _enabled = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.ZoneActivity", "Enabled", true, "Persistente Inaktivitaetszeit pro tatsaechlich betretener Zone."); _scanSeconds = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.ZoneActivity", "ScanSeconds", 2f, "Intervall fuer Spieler-Zonenwechsel."); _meadowsDays = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.ZoneActivity", "MeadowsDays", 7f, "Inaktivitaet in Tagen fuer Wiesen."); _blackForestDays = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.ZoneActivity", "BlackForestDays", 7f, "Inaktivitaet in Tagen fuer Schwarzwald."); _swampDays = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.ZoneActivity", "SwampDays", 10f, "Inaktivitaet in Tagen fuer Sumpf."); _mountainDays = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.ZoneActivity", "MountainDays", 10f, "Inaktivitaet in Tagen fuer Gebirge."); _plainsDays = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.ZoneActivity", "PlainsDays", 12f, "Inaktivitaet in Tagen fuer Ebenen."); _mistlandsDays = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.ZoneActivity", "MistlandsDays", 14f, "Inaktivitaet in Tagen fuer Nebellande."); _ashlandsDays = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.ZoneActivity", "AshlandsDays", 14f, "Inaktivitaet in Tagen fuer Aschlande."); _deepNorthDays = ((BaseUnityPlugin)plugin).Config.Bind("WorldLifecycle.ZoneActivity", "DeepNorthDays", 14f, "Inaktivitaet in Tagen fuer Hohen Norden."); ((MonoBehaviour)plugin).StartCoroutine(TrackingLoop()); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Zonenaktivitaet aktiv: Nach jedem Verlassen durch den letzten Spieler startet die vollstaendige Biom-Frist neu; waehrend Spieler- oder Werkbankschutz wird nicht restauriert."); } } internal static bool IsOccupied(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) return IsOccupied(ZoneSystem.GetZone(position)); } internal static bool IsOccupied(Vector2i zone) { //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) return PlayerZones.Values.Any((Vector2i current) => current == zone); } internal static DateTime LastExitedAt(Vector3 position) { //IL_0005: 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_000b: 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) EnsureLoaded(); Vector2i zone = ZoneSystem.GetZone(position); if (!Records.TryGetValue(Key(ChallengeHubWorldState.WorldUid, zone), out var value)) { return DateTime.MinValue; } return ParseUtc(value.lastExitedUtc); } internal static bool IsResetDue(Vector2i zone, out ZoneActivityRecord record) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) EnsureLoaded(); if (!Records.TryGetValue(Key(ChallengeHubWorldState.WorldUid, zone), out record) || !record.wasVisited || IsOccupied(zone)) { return false; } DateTime dateTime = ParseUtc(record.lastExitedUtc); if (dateTime == DateTime.MinValue) { return false; } DateTime dateTime2 = ParseUtc(record.lastResetUtc); if (dateTime2 != DateTime.MinValue && dateTime2 >= dateTime) { return false; } return DateTime.UtcNow >= dateTime.AddDays(DaysFor(record.biome)); } internal static IEnumerable DueZones() { EnsureLoaded(); ZoneActivityRecord[] array = Records.Values.ToArray(); Vector2i val = default(Vector2i); foreach (ZoneActivityRecord zoneActivityRecord in array) { if (zoneActivityRecord != null && zoneActivityRecord.worldUid == ChallengeHubWorldState.WorldUid) { ((Vector2i)(ref val))..ctor(zoneActivityRecord.zoneX, zoneActivityRecord.zoneY); if (IsResetDue(val, out var _)) { yield return val; } } } } internal static void MarkReset(Vector2i zone) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) EnsureLoaded(); if (Records.TryGetValue(Key(ChallengeHubWorldState.WorldUid, zone), out var value)) { value.lastResetUtc = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture); _dirty = true; Save(); } } private static IEnumerator TrackingLoop() { while (true) { ZoneRestorationWebConfig remote = Remote; yield return (object)new WaitForSeconds(Mathf.Clamp((remote != null && remote.scanSeconds > 0f) ? Remote.scanSeconds : (_scanSeconds?.Value ?? 2f), 1f, 30f)); if (Enabled && ChallengeHubWorldState.IsServer && ChallengeHubServerGateFeature.GameplayAllowed) { EnsureLoaded(); TrackPlayers(); if (_dirty) { Save(); } } } } private static void TrackPlayers() { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_008f: 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) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) HashSet seen = new HashSet(); foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer == (Object)null) { continue; } int instanceID = ((Object)allPlayer).GetInstanceID(); seen.Add(instanceID); Vector2i zone = ZoneSystem.GetZone(((Component)allPlayer).transform.position); if (!PlayerZones.TryGetValue(instanceID, out var value) || !(value == zone)) { if (PlayerZones.TryGetValue(instanceID, out value)) { Leave(value, instanceID); } PlayerZones[instanceID] = zone; Enter(zone, ((Component)allPlayer).transform.position); } } int[] array = PlayerZones.Keys.Where((int id) => !seen.Contains(id)).ToArray(); foreach (int num2 in array) { Vector2i zone2 = PlayerZones[num2]; PlayerZones.Remove(num2); Leave(zone2, num2); } } private static void Enter(Vector2i zone, Vector3 position) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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_0065: Unknown result type (might be due to invalid IL or missing references) string key = Key(ChallengeHubWorldState.WorldUid, zone); if (!Records.TryGetValue(key, out var value)) { value = new ZoneActivityRecord { worldUid = ChallengeHubWorldState.WorldUid, zoneX = zone.x, zoneY = zone.y }; Records[key] = value; } value.wasVisited = true; if (string.IsNullOrWhiteSpace(value.biome)) { value.biome = BiomeAt(position); } _dirty = true; } private static void Leave(Vector2i zone, int leavingPlayer) { //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) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (!PlayerZones.Any((KeyValuePair pair) => pair.Key != leavingPlayer && pair.Value == zone)) { string key = Key(ChallengeHubWorldState.WorldUid, zone); if (Records.TryGetValue(key, out var value)) { value.lastExitedUtc = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture); _dirty = true; } } } private static string BiomeAt(Vector3 position) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_0008: 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) try { return ((object)((WorldGenerator.instance != null) ? WorldGenerator.instance.GetBiome(position) : Heightmap.FindBiome(position))/*cast due to .constrained prefix*/).ToString().ToLowerInvariant(); } catch { return "unknown"; } } private static double DaysFor(string biome) { string text = (biome ?? string.Empty).ToLowerInvariant(); if (text.Contains("blackforest")) { ZoneRestorationWebConfig remote = Remote; return (remote != null && remote.blackForestDays > 0f) ? Remote.blackForestDays : (_blackForestDays?.Value ?? 7f); } if (text.Contains("swamp")) { ZoneRestorationWebConfig remote2 = Remote; return (remote2 != null && remote2.swampDays > 0f) ? Remote.swampDays : (_swampDays?.Value ?? 10f); } if (text.Contains("mountain")) { ZoneRestorationWebConfig remote3 = Remote; return (remote3 != null && remote3.mountainDays > 0f) ? Remote.mountainDays : (_mountainDays?.Value ?? 10f); } if (text.Contains("plains")) { ZoneRestorationWebConfig remote4 = Remote; return (remote4 != null && remote4.plainsDays > 0f) ? Remote.plainsDays : (_plainsDays?.Value ?? 12f); } if (text.Contains("mistlands")) { ZoneRestorationWebConfig remote5 = Remote; return (remote5 != null && remote5.mistlandsDays > 0f) ? Remote.mistlandsDays : (_mistlandsDays?.Value ?? 14f); } if (text.Contains("ashlands")) { ZoneRestorationWebConfig remote6 = Remote; return (remote6 != null && remote6.ashlandsDays > 0f) ? Remote.ashlandsDays : (_ashlandsDays?.Value ?? 14f); } if (text.Contains("deepnorth")) { ZoneRestorationWebConfig remote7 = Remote; return (remote7 != null && remote7.deepNorthDays > 0f) ? Remote.deepNorthDays : (_deepNorthDays?.Value ?? 14f); } ZoneRestorationWebConfig remote8 = Remote; return (remote8 != null && remote8.meadowsDays > 0f) ? Remote.meadowsDays : (_meadowsDays?.Value ?? 7f); } private static string Key(long worldUid, Vector2i zone) { return worldUid.ToString(CultureInfo.InvariantCulture) + ":" + zone.x + ":" + zone.y; } private static void EnsureLoaded() { //IL_0067: Unknown result type (might be due to invalid IL or missing references) if (_loaded) { return; } _loaded = true; try { if (!File.Exists(StorePath)) { return; } ZoneActivityRecord[] array = JsonUtility.FromJson(File.ReadAllText(StorePath))?.zones ?? new ZoneActivityRecord[0]; foreach (ZoneActivityRecord zoneActivityRecord in array) { if (zoneActivityRecord != null) { Records[Key(zoneActivityRecord.worldUid, new Vector2i(zoneActivityRecord.zoneX, zoneActivityRecord.zoneY))] = zoneActivityRecord; } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Zonenaktivitaet konnte nicht geladen werden: " + ex.Message)); } } } private static void Save() { try { Directory.CreateDirectory(Path.GetDirectoryName(StorePath)); string text = StorePath + ".tmp"; File.WriteAllText(text, JsonUtility.ToJson((object)new ZoneActivityStore { zones = Records.Values.ToArray() }, true)); if (File.Exists(StorePath)) { File.Delete(StorePath); } File.Move(text, StorePath); _dirty = false; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Zonenaktivitaet konnte nicht gespeichert werden: " + ex.Message)); } } } private static DateTime ParseUtc(string raw) { if (!DateTime.TryParse(raw, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out var result)) { return DateTime.MinValue; } return result; } } }