Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ValheimVillages v0.3.6
plugins/ValheimVillages/ValheimVillages.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Newtonsoft.Json; using Splatform; using UnityEngine; using UnityEngine.AI; using UnityEngine.Events; using UnityEngine.UI; using ValheimVillages.Abilities; using ValheimVillages.Attributes; using ValheimVillages.Behaviors; using ValheimVillages.Behaviors.Combat; using ValheimVillages.Behaviors.Crafting; using ValheimVillages.Behaviors.Farming; using ValheimVillages.Behaviors.Feasts; using ValheimVillages.Behaviors.Forestry; using ValheimVillages.Behaviors.Patrol; using ValheimVillages.Behaviors.Relax; using ValheimVillages.Behaviors.Repair; using ValheimVillages.Behaviors.Tidy; using ValheimVillages.Behaviors.Wander; using ValheimVillages.Behaviors.Work; using ValheimVillages.Dev; using ValheimVillages.Diagnostics; using ValheimVillages.Enums; using ValheimVillages.Interfaces; using ValheimVillages.Items; using ValheimVillages.Items.Fragments; using ValheimVillages.Items.Icons; using ValheimVillages.Items.VirtualRecipes; using ValheimVillages.Items.WorkOrders; using ValheimVillages.Networking; using ValheimVillages.Patches; using ValheimVillages.Scheduling; using ValheimVillages.Scheduling.Producers; using ValheimVillages.Schemas; using ValheimVillages.Settings; using ValheimVillages.Tags; using ValheimVillages.TaskQueue; using ValheimVillages.TaskQueue.ActivityLog; using ValheimVillages.TaskQueue.Handlers; using ValheimVillages.Testing; using ValheimVillages.UI.Alerts; using ValheimVillages.UI.ContextMenus; using ValheimVillages.UI.Core; using ValheimVillages.UI.Interaction; using ValheimVillages.UI.Panels; using ValheimVillages.UI.Tabs; using ValheimVillages.Villager; using ValheimVillages.Villager.AI; using ValheimVillages.Villager.AI.Memory; using ValheimVillages.Villager.AI.Navigation; using ValheimVillages.Villager.AI.Pathfinding; using ValheimVillages.Villager.AI.Work; using ValheimVillages.Villager.Records; using ValheimVillages.Villager.Registry; using ValheimVillages.Villager.Station; using ValheimVillages.Villages; using ValheimVillages.Villages.Entity; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Valheim Villages")] [assembly: AssemblyDescription("A village-building and NPC management mod for Valheim")] [assembly: AssemblyCompany("Myrcutio")] [assembly: AssemblyProduct("ValheimVillages")] [assembly: AssemblyCopyright("Copyright © Myrcutio 2026")] [assembly: AssemblyFileVersion("0.3.6")] [assembly: InternalsVisibleTo("ValheimVillages.Tests")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("0.3.6.0")] namespace ValheimVillages { internal static class DebugLog { private sealed class ThrottleState { public bool EverEmitted; public TimeSpan LastEmitted; public int Suppressed; } private static int _captureCounter; private static DebugCaptureBehaviour _captureBehaviour; private static int _cycleNumber; private static readonly Dictionary<string, ThrottleState> _throttle = new Dictionary<string, ThrottleState>(); private static readonly TimeSpan DefaultThrottleWindow = TimeSpan.FromSeconds(10.0); private static readonly Stopwatch _sessionClock = Stopwatch.StartNew(); private static string SidecarDir { get { string path; try { path = Paths.ConfigPath; } catch (Exception ex) { SwallowedFailure.Note("DebugLog.Sidecar.cs", ex); path = "."; } return Path.Combine(path, "vv_dumps"); } } public static void Capture(string trigger) { Capture(CaptureRequest.Default(trigger)); } public static void Capture(CaptureRequest request) { try { Plugin instance = Plugin.Instance; if (!((Object)(object)instance == (Object)null)) { if ((Object)(object)_captureBehaviour == (Object)null) { _captureBehaviour = ((Component)instance).gameObject.GetComponent<DebugCaptureBehaviour>() ?? ((Component)instance).gameObject.AddComponent<DebugCaptureBehaviour>(); } _captureBehaviour.Enqueue(request); } } catch (Exception ex) { SwallowedFailure.Note("DebugLog.Capture.cs", ex); } } internal static int NextCaptureCounter() { return Interlocked.Increment(ref _captureCounter); } public static string Vid(Guid id) { string text = id.ToString("N"); if (text.Length < 8) { return text; } return text.Substring(0, 8); } public static string Vid(string id) { if (string.IsNullOrEmpty(id)) { return "unknown"; } if (Guid.TryParse(id, out var result)) { return Vid(result); } string text = id.Replace("-", ""); if (text.Length < 8) { return text; } return text.Substring(0, 8); } public static string Tid(string taskName, int instanceId) { return (taskName ?? "task") + "#" + instanceId; } public static void BeginCycle(bool isHotReload) { int num = Interlocked.Increment(ref _cycleNumber); string arg = (isHotReload ? "true" : "false"); string arg2 = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"); Plugin.Log.LogInfo((object)$"===== VV CYCLE n={num} hot={arg} t={arg2} ====="); } public static void Event(string component, string eventName, params (string key, object val)[] kv) { StringBuilder stringBuilder = new StringBuilder(64 + ((kv != null) ? kv.Length : 0) * 16); stringBuilder.Append('[').Append(component).Append("] ") .Append(eventName); stringBuilder.Append(' ').Append(T()); if (kv != null) { for (int i = 0; i < kv.Length; i++) { stringBuilder.Append(' ').Append(kv[i].key).Append('='); AppendValue(stringBuilder, kv[i].val); } } Plugin.Log.LogInfo((object)stringBuilder.ToString()); } private static void AppendValue(StringBuilder sb, object v) { if (v == null) { sb.Append("null"); return; } string text = ((v is float num) ? num.ToString("0.###", CultureInfo.InvariantCulture) : ((v is double num2) ? num2.ToString("0.###", CultureInfo.InvariantCulture) : ((!(v is bool)) ? (v.ToString() ?? "") : (((bool)v) ? "true" : "false")))); bool flag = false; foreach (char c in text) { if (c == ' ' || c == '=' || c == '"') { flag = true; break; } } if (flag) { sb.Append('"').Append(text.Replace("\"", "\\\"")).Append('"'); } else { sb.Append(text); } } public static void List(string component, string name, IEnumerable<object> items) { string[] array = items?.Select((object i) => i?.ToString() ?? "null").ToArray() ?? new string[0]; string text = ShortSha(string.Join(",", array)); string text2 = WriteSidecar(name, text, array); Event(component, name, ("count", array.Length), ("sha", text), ("path", text2 ?? "(sidecar off)")); } private static string WriteSidecar(string name, string sha, string[] arr) { if (!DevSettings.WriteListSidecars) { return null; } string sidecarDir = SidecarDir; string text = Path.Combine(sidecarDir, name + "_" + sha + ".json"); try { Directory.CreateDirectory(sidecarDir); if (File.Exists(text)) { return text; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append('['); for (int i = 0; i < arr.Length; i++) { if (i > 0) { stringBuilder.Append(','); } stringBuilder.Append('"').Append(JsonEscape(arr[i])).Append('"'); } stringBuilder.Append(']'); File.WriteAllText(text, stringBuilder.ToString()); return text; } catch (Exception ex) { SwallowedFailure.Note("DebugLog.Sidecar.WriteSidecar", ex); return null; } } private static string ShortSha(string s) { using SHA1 sHA = SHA1.Create(); byte[] array = sHA.ComputeHash(Encoding.UTF8.GetBytes(s ?? "")); StringBuilder stringBuilder = new StringBuilder(8); for (int i = 0; i < 4; i++) { stringBuilder.AppendFormat("{0:x2}", array[i]); } return stringBuilder.ToString(); } private static string JsonEscape(string s) { return (s ?? "").Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n") .Replace("\r", "\\r") .Replace("\t", "\\t"); } public static void Throttled(string key, string component, string eventName, params (string key, object val)[] kv) { ThrottledWindow(key, DefaultThrottleWindow, component, eventName, kv); } public static void ThrottledWindow(string key, TimeSpan window, string component, string eventName, params (string key, object val)[] kv) { if (string.IsNullOrEmpty(key)) { Event(component, eventName, kv); return; } ThrottleState value; lock (_throttle) { if (!_throttle.TryGetValue(key, out value)) { value = new ThrottleState(); _throttle[key] = value; } } lock (value) { TimeSpan timeSpan = Elapsed(); bool num = !value.EverEmitted; bool flag = timeSpan - value.LastEmitted >= window; if (num || flag) { if (value.Suppressed > 0) { (string, object)[] array = new(string, object)[kv.Length + 2]; Array.Copy(kv, array, kv.Length); array[kv.Length] = ("suppressed", value.Suppressed); array[kv.Length + 1] = ("window_s", window.TotalSeconds); Event(component, eventName, array); } else { Event(component, eventName, kv); } value.LastEmitted = timeSpan; value.EverEmitted = true; value.Suppressed = 0; } else { value.Suppressed++; } } } public static TimeSpan Elapsed() { return _sessionClock.Elapsed; } public static string T() { return "t=+" + _sessionClock.Elapsed.TotalSeconds.ToString("0.00", CultureInfo.InvariantCulture) + "s"; } } internal readonly struct CaptureRequest { public readonly string Trigger; public readonly string OutputSubdir; public readonly string OutputBaseName; public readonly Vector3? AnchorOverride; public readonly float AnchorClearance; public readonly bool IncludeDiagnostics; public CaptureRequest(string trigger, string outputSubdir, string outputBaseName, Vector3? anchorOverride, float anchorClearance, bool includeDiagnostics) { Trigger = trigger ?? "unknown"; OutputSubdir = outputSubdir ?? ""; OutputBaseName = (string.IsNullOrEmpty(outputBaseName) ? "last_capture" : outputBaseName); AnchorOverride = anchorOverride; AnchorClearance = anchorClearance; IncludeDiagnostics = includeDiagnostics; } public static CaptureRequest Default(string trigger) { return new CaptureRequest(trigger, "", "last_capture", null, 45f, includeDiagnostics: true); } public static CaptureRequest ForIncident(string trigger, string incidentSubdir, string baseName, Vector3 anchor, float clearance) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) return new CaptureRequest(trigger, incidentSubdir, baseName, anchor, clearance, includeDiagnostics: false); } } internal class DebugCaptureBehaviour : MonoBehaviour { private readonly ConcurrentQueue<CaptureRequest> _pending = new ConcurrentQueue<CaptureRequest>(); private bool _processing; private static readonly HashSet<string> OrchestratedTriggers = new HashSet<string> { "repartition", "manual" }; private void Update() { if (!_processing && !_pending.IsEmpty) { ((MonoBehaviour)this).StartCoroutine(ProcessQueue()); } } public void Enqueue(CaptureRequest req) { _pending.Enqueue(req); } private IEnumerator ProcessQueue() { _processing = true; try { CaptureRequest result; while (_pending.TryDequeue(out result)) { int counter = DebugLog.NextCaptureCounter(); yield return CaptureRoutine(result, counter); yield return null; } } finally { _processing = false; } } private IEnumerator CaptureRoutine(CaptureRequest req, int counter) { yield return (object)new WaitForSecondsRealtime(0.6f); Camera cam = Camera.main; if ((Object)(object)cam == (Object)null) { yield break; } OrchestrationSession session = null; if (OrchestratedTriggers.Contains(req.Trigger) || req.AnchorOverride.HasValue) { session = OrchestrationSession.TryBegin(req); session?.LogStarted(req.Trigger, session.AnchorPos); } try { if (session != null) { yield return null; } session?.ApplyCameraPose(); Vector3 pos = ((Component)cam).transform.position; Vector3 euler = ((Component)cam).transform.eulerAngles; float worldTime = -1f; try { if ((Object)(object)EnvMan.instance != (Object)null) { worldTime = EnvMan.instance.GetDayFraction(); } } catch (Exception ex) { SwallowedFailure.Note("DebugLog.Capture.cs", ex); } yield return (object)new WaitForEndOfFrame(); WritePngAndSidecar(req, counter, pos, euler, worldTime); } finally { session?.Restore(); } } private static void WritePngAndSidecar(CaptureRequest req, int counter, Vector3 pos, Vector3 euler, float worldTime) { string text; try { string path; try { path = Paths.ConfigPath; } catch (Exception ex) { SwallowedFailure.Note("DebugLog.Capture.cs", ex); path = "."; } text = Path.Combine(path, "vv_dumps"); if (!string.IsNullOrEmpty(req.OutputSubdir)) { text = Path.Combine(text, req.OutputSubdir); } Directory.CreateDirectory(text); } catch (Exception ex2) { SwallowedFailure.Note("DebugLog.Capture.cs", ex2); return; } string path2 = Path.Combine(text, req.OutputBaseName + ".png"); string path3 = Path.Combine(text, req.OutputBaseName + ".json"); Texture2D val = null; try { val = ScreenCapture.CaptureScreenshotAsTexture(); byte[] bytes = ImageConversion.EncodeToPNG(val); File.WriteAllBytes(path2, bytes); } catch (Exception ex3) { SwallowedFailure.Note("DebugLog.Capture.cs", ex3); } finally { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } try { StringBuilder stringBuilder = new StringBuilder(2048); stringBuilder.Append('{'); stringBuilder.Append("\"counter\":").Append(counter.ToString(CultureInfo.InvariantCulture)).Append(','); stringBuilder.Append("\"trigger\":\"").Append(JsonEscape(req.Trigger)).Append("\","); stringBuilder.Append("\"timestampUtc\":\"").Append(DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture)).Append("\","); stringBuilder.Append("\"worldTimeOfDay\":").Append(worldTime.ToString("R", CultureInfo.InvariantCulture)).Append(','); stringBuilder.Append("\"cameraPos\":[").Append(pos.x.ToString("R", CultureInfo.InvariantCulture)).Append(',') .Append(pos.y.ToString("R", CultureInfo.InvariantCulture)) .Append(',') .Append(pos.z.ToString("R", CultureInfo.InvariantCulture)) .Append("],"); stringBuilder.Append("\"cameraYaw\":").Append(euler.y.ToString("R", CultureInfo.InvariantCulture)).Append(','); stringBuilder.Append("\"cameraPitch\":").Append(euler.x.ToString("R", CultureInfo.InvariantCulture)); if (req.IncludeDiagnostics) { AppendDiagnostics(stringBuilder); } stringBuilder.Append('}'); File.WriteAllText(path3, stringBuilder.ToString()); } catch (Exception ex4) { SwallowedFailure.Note("DebugLog.Capture.cs", ex4); } } private static void AppendDiagnostics(StringBuilder sb) { sb.Append(",\"diagnostics\":{"); sb.Append("\"lastRelaxation\":{\"available\":false},"); sb.Append("\"villages\":["); bool flag = true; try { foreach (RegionGraph item in VillageRegistry.AllGraphs()) { if (!flag) { sb.Append(','); } flag = false; sb.Append('{'); sb.Append("\"key\":\"").Append(JsonEscape(item.RegisteredVillageKey ?? "")).Append("\","); sb.Append("\"regionCount\":").Append(item.RegionCount).Append(','); sb.Append("\"linkCount\":").Append(item.LinkCount).Append(','); sb.Append("\"bfsCells\":").Append(item.Diagnostics.LookupGridCellCount).Append(','); sb.Append("\"boundaryCells\":").Append(item.Diagnostics.BoundaryCellCount).Append(','); sb.Append("\"bfsBounds\":"); AppendBounds(sb, item.Diagnostics.TryGetLookupGridBounds(out var minX, out var maxX, out var minZ, out var maxZ), minX, maxX, minZ, maxZ); sb.Append(",\"boundaryBounds\":"); AppendBounds(sb, item.Diagnostics.TryGetBoundaryCellsBounds(out var minX2, out var maxX2, out var minZ2, out var maxZ2), minX2, maxX2, minZ2, maxZ2); sb.Append('}'); } } catch (Exception ex) { SwallowedFailure.Note("DebugLog.Capture.cs", ex); } sb.Append(']'); sb.Append('}'); } private static void AppendBounds(StringBuilder sb, bool ok, float minX, float maxX, float minZ, float maxZ) { if (!ok) { sb.Append("null"); } else { sb.Append("{\"x\":[").Append(minX.ToString("R", CultureInfo.InvariantCulture)).Append(',') .Append(maxX.ToString("R", CultureInfo.InvariantCulture)) .Append("],\"z\":[") .Append(minZ.ToString("R", CultureInfo.InvariantCulture)) .Append(',') .Append(maxZ.ToString("R", CultureInfo.InvariantCulture)) .Append("]}"); } } private static string JsonFloat(float v) { if (float.IsNaN(v) || float.IsInfinity(v)) { return "null"; } return v.ToString("R", CultureInfo.InvariantCulture); } private static string JsonEscape(string s) { return (s ?? "").Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n") .Replace("\r", "\\r") .Replace("\t", "\\t"); } } internal class OrchestrationSession { private readonly Player m_player; private readonly Vector3 m_savedPos; private readonly Quaternion m_savedRot; private readonly Hud m_hud; private readonly bool m_savedHudVisible; private readonly Vector3 m_savedCamPos; private readonly Quaternion m_savedCamRot; private readonly GameCamera m_gameCamera; private readonly bool m_savedGameCameraEnabled; private readonly Vector3 m_anchorPos; private readonly Quaternion m_anchorRot; public Vector3 AnchorPos => m_anchorPos; private OrchestrationSession(Player player, Vector3 savedPos, Quaternion savedRot, Hud hud, bool savedHudVisible, Vector3 savedCamPos, Quaternion savedCamRot, GameCamera gameCamera, bool savedGameCameraEnabled, Vector3 anchorPos, Quaternion anchorRot) { //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_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_002c: 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_0034: 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_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_0056: Unknown result type (might be due to invalid IL or missing references) m_player = player; m_savedPos = savedPos; m_savedRot = savedRot; m_hud = hud; m_savedHudVisible = savedHudVisible; m_savedCamPos = savedCamPos; m_savedCamRot = savedCamRot; m_gameCamera = gameCamera; m_savedGameCameraEnabled = savedGameCameraEnabled; m_anchorPos = anchorPos; m_anchorRot = anchorRot; } public void ApplyCameraPose() { //IL_0018: 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) Camera main = Camera.main; if ((Object)(object)main == (Object)null) { return; } try { ((Component)main).transform.position = m_anchorPos; ((Component)main).transform.rotation = m_anchorRot; } catch (Exception ex) { SwallowedFailure.Note("DebugLog.Capture.cs", ex); } } public static OrchestrationSession TryBegin(CaptureRequest req) { //IL_0054: 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_00fe: 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_0103: 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) //IL_0230: 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_009e: 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_011d: 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_0122: 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_0160: 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_0189: 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_0278: Unknown result type (might be due to invalid IL or missing references) //IL_0279: 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_0280: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[Capture] Orchestration skipped: no local player. Falling back to passive capture."); } return null; } CaptureAnchor.Result result = ((!req.AnchorOverride.HasValue) ? CaptureAnchor.Resolve(((Component)localPlayer).transform.position) : CaptureAnchor.ResolveAt(req.AnchorOverride.Value, req.AnchorClearance)); if (!result.HasAnchor) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[Capture] Orchestration skipped: " + result.Reason + ". Falling back to passive capture (no silent coordinate fabrication).")); } return null; } Vector3 position = ((Component)localPlayer).transform.position; Quaternion rotation = ((Component)localPlayer).transform.rotation; Hud instance = Hud.instance; bool flag = true; try { if ((Object)(object)instance != (Object)null) { flag = ((Component)instance).gameObject.activeSelf; } } catch (Exception ex) { SwallowedFailure.Note("DebugLog.Capture.cs", ex); } Camera main = Camera.main; Vector3 savedCamPos = (((Object)(object)main != (Object)null) ? ((Component)main).transform.position : Vector3.zero); Quaternion savedCamRot = (((Object)(object)main != (Object)null) ? ((Component)main).transform.rotation : Quaternion.identity); GameCamera instance2 = GameCamera.instance; bool flag2 = (Object)(object)instance2 != (Object)null && ((Behaviour)instance2).enabled; Quaternion val = Quaternion.Euler(result.Pitch, result.Yaw, 0f); try { ((Component)localPlayer).transform.position = result.Pos; ((Component)localPlayer).transform.rotation = val; if ((Object)(object)main != (Object)null) { ((Component)main).transform.position = result.Pos; ((Component)main).transform.rotation = val; } if ((Object)(object)instance2 != (Object)null) { ((Behaviour)instance2).enabled = false; } if ((Object)(object)instance != (Object)null) { ((Component)instance).gameObject.SetActive(false); } } catch (Exception ex2) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("[Capture] Orchestration apply failed: " + ex2.GetType().Name + ": " + ex2.Message + ". Restoring state and falling back to passive capture.")); } try { ((Component)localPlayer).transform.position = position; ((Component)localPlayer).transform.rotation = rotation; } catch { } try { if ((Object)(object)instance2 != (Object)null) { ((Behaviour)instance2).enabled = flag2; } } catch { } try { if ((Object)(object)instance != (Object)null) { ((Component)instance).gameObject.SetActive(flag); } } catch { } return null; } return new OrchestrationSession(localPlayer, position, rotation, instance, flag, savedCamPos, savedCamRot, instance2, flag2, result.Pos, val); } public void LogStarted(string trigger, Vector3 anchorPos) { //IL_001b: 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_0037: Unknown result type (might be due to invalid IL or missing references) ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[Capture] Orchestrated trigger='{trigger}' anchor=({anchorPos.x:F1},{anchorPos.y:F1},{anchorPos.z:F1}) yaw=0 pitch=90"); } } public void Restore() { //IL_001a: 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) try { if ((Object)(object)m_player != (Object)null) { ((Component)m_player).transform.position = m_savedPos; ((Component)m_player).transform.rotation = m_savedRot; } } catch (Exception ex) { SwallowedFailure.Note("DebugLog.Capture.cs", ex); } try { if ((Object)(object)m_gameCamera != (Object)null) { ((Behaviour)m_gameCamera).enabled = m_savedGameCameraEnabled; } } catch (Exception ex2) { SwallowedFailure.Note("DebugLog.Capture.cs", ex2); } try { if ((Object)(object)m_hud != (Object)null) { ((Component)m_hud).gameObject.SetActive(m_savedHudVisible); } } catch (Exception ex3) { SwallowedFailure.Note("DebugLog.Capture.cs", ex3); } } } public static class HotReloadHelper { private const string ModPrefix = "VV_"; private const string ModPrefabPrefix = "vv_"; private const string VillagerStationPrefix = "$vv_"; private static readonly Assembly CurrentAssembly = typeof(HotReloadHelper).Assembly; public static void FullCleanup() { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[HotReload] Starting full cleanup..."); } ResetAllStaticState(); int num = DestroyStaleComponents(); int num2 = DestroyOrphanedModObjects(); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[HotReload] Cleanup complete: " + $"{num} stale component(s), " + $"{num2} orphaned GameObject(s) destroyed.")); } } public static string PurgeStaleObjects() { string arg = ReportModInstances(); int num = DestroyStaleComponents(); return $"{arg}\n[HotReload] Purged {num} stale component(s)."; } public static string ReportModInstances() { Dictionary<string, int> dictionary = new Dictionary<string, int>(); Dictionary<string, int> dictionary2 = new Dictionary<string, int>(); MonoBehaviour[] array = Object.FindObjectsByType<MonoBehaviour>((FindObjectsInactive)1, (FindObjectsSortMode)0); foreach (MonoBehaviour val in array) { if (!((Object)(object)val == (Object)null)) { Type type = ((object)val).GetType(); if (type.FullName != null && type.FullName.StartsWith("ValheimVillages.")) { Dictionary<string, int> obj = ((type.Assembly == CurrentAssembly) ? dictionary : dictionary2); obj.TryGetValue(type.Name, out var value); obj[type.Name] = value + 1; } } } int num = 0; List<string> list = new List<string>(); foreach (KeyValuePair<string, int> item in dictionary2) { num += item.Value; list.Add($" STALE {item.Key} x{item.Value}"); } string text = $"[HotReload] Mod MonoBehaviours: {dictionary.Count} current type(s), " + $"{num} stale instance(s)"; if (list.Count <= 0) { return text; } return text + "\n" + string.Join("\n", list); } private static void ResetAllStaticState() { AttributeScanner.InvokeAllCleanup(CurrentAssembly); ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)"[HotReload] All static registries cleared."); } } private static int DestroyStaleComponents() { int num = 0; MonoBehaviour[] array = Object.FindObjectsByType<MonoBehaviour>((FindObjectsInactive)1, (FindObjectsSortMode)0); foreach (MonoBehaviour val in array) { if ((Object)(object)val == (Object)null) { continue; } Type type = ((object)val).GetType(); string fullName = type.FullName; if (fullName != null && fullName.StartsWith("ValheimVillages.") && !(type.Assembly == CurrentAssembly)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[HotReload] Destroying stale component: " + fullName + " on \"" + ((Object)((Component)val).gameObject).name + "\"")); } ((Behaviour)val).enabled = false; Object.Destroy((Object)(object)val); num++; } } return num; } private static int DestroyOrphanedModObjects() { int num = 0; Transform[] array = Object.FindObjectsByType<Transform>((FindObjectsInactive)1, (FindObjectsSortMode)0); List<GameObject> list = new List<GameObject>(); Transform[] array2 = array; foreach (Transform val in array2) { if (!((Object)(object)val == (Object)null)) { GameObject gameObject = ((Component)val).gameObject; string name = ((Object)gameObject).name; if ((AttributeScanner.GetModObjectNames(CurrentAssembly).Contains(name) || name.StartsWith("VV_") || name.StartsWith("vv_")) && !((Object)(object)gameObject.GetComponentInParent<ZNetView>(true) != (Object)null) && !HasCurrentAssemblyComponent(gameObject)) { list.Add(gameObject); } } } foreach (GameObject item in list) { if (!((Object)(object)item == (Object)null)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[HotReload] Destroying orphaned mod object: \"" + ((Object)item).name + "\"")); } item.SetActive(false); Object.Destroy((Object)(object)item); num++; } } return num; } private static bool HasCurrentAssemblyComponent(GameObject go) { MonoBehaviour[] components = go.GetComponents<MonoBehaviour>(); foreach (MonoBehaviour val in components) { if (!((Object)(object)val == (Object)null)) { Type type = ((object)val).GetType(); if (type.FullName != null && type.FullName.StartsWith("ValheimVillages.") && type.Assembly == CurrentAssembly) { return true; } } } return false; } public static void FixupExistingNPCs() { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) int num = 0; ZNetView[] array = Object.FindObjectsOfType<ZNetView>(); foreach (ZNetView val in array) { if ((Object)(object)val == (Object)null) { continue; } ZDO zDO = val.GetZDO(); if (zDO != null && zDO.GetPrefab() != RecordPrefabFactory.RecordPrefabHash && !NativeNpcStripper.IsPlayerOwned(((Component)val).gameObject) && (!string.IsNullOrEmpty(zDO.GetString("vv_record_id", "")) || !string.IsNullOrEmpty(zDO.GetString("vv_villager_type", "")))) { RemoveOrphanedCraftingStations(((Component)val).gameObject); VillagerRestoration.Restore(((Component)val).gameObject, zDO); num++; ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)$"[HotReload] Fixed up NPC at {((Component)val).transform.position}"); } } } if (num > 0) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[HotReload] Fixed up {num} existing NPC(s)"); } } } public static void FixupExistingRegistries() { int stableHashCode = StringExtensionMethods.GetStableHashCode("vv_village_registry"); int num = 0; ZNetView[] array = Object.FindObjectsOfType<ZNetView>(); foreach (ZNetView val in array) { if (!((Object)(object)val == (Object)null)) { ZDO zDO = val.GetZDO(); if (zDO != null && zDO.GetPrefab() == stableHashCode) { PieceFactory.ReapplyInteractionToInstance(((Component)val).gameObject); num++; } } } if (num > 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[HotReload] Re-applied interaction to {num} placed registry station(s)"); } } } private static void RemoveOrphanedCraftingStations(GameObject go) { CraftingStation[] components = go.GetComponents<CraftingStation>(); foreach (CraftingStation val in components) { if (!((Object)(object)val == (Object)null) && val.m_name != null && val.m_name.StartsWith("$vv_")) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[HotReload] Removing orphaned CraftingStation '" + val.m_name + "' on \"" + ((Object)go).name + "\"")); } ((Behaviour)val).enabled = false; Object.Destroy((Object)(object)val); } } } } internal static class PartitionProfile { private static bool s_active; private static readonly List<(string name, double ms)> s_stages = new List<(string, double)>(); public static long SamplePos; public static long CheckCapsule; public static long CheckSphere; public static long OverlapBox; public static long GetGroundHeight; public static long CalcPath; public static string LastStage { get; private set; } public static void Begin() { s_active = true; s_stages.Clear(); LastStage = "(start)"; SamplePos = 0L; CheckCapsule = 0L; CheckSphere = 0L; OverlapBox = 0L; GetGroundHeight = 0L; CalcPath = 0L; } public static long Mark() { return Stopwatch.GetTimestamp(); } public static void Since(string name, long mark) { if (s_active) { double item = (double)(Stopwatch.GetTimestamp() - mark) * 1000.0 / (double)Stopwatch.Frequency; s_stages.Add((name, item)); LastStage = name; } } public static void Emit(string villageKey) { if (!s_active) { return; } s_active = false; List<(string, object)> list = new List<(string, object)>(s_stages.Count + 7) { ("village", villageKey ?? "") }; foreach (var (text, num) in s_stages) { list.Add((text + "_ms", num.ToString("F2", CultureInfo.InvariantCulture))); } list.Add(("samplepos_calls", SamplePos)); list.Add(("checkcapsule_calls", CheckCapsule)); list.Add(("checksphere_calls", CheckSphere)); list.Add(("overlapbox_calls", OverlapBox)); list.Add(("getgroundheight_calls", GetGroundHeight)); list.Add(("calcpath_calls", CalcPath)); DebugLog.Event("Region", "PartitionProfile", list.ToArray()); } } public static class PathTelemetry { [Serializable] private class HnaGraphData { public int regionCount; public int linkCount; public float minX; public float minZ; public float maxX; public float maxZ; public string regionCenters; public string linksSummary; } private static readonly string LogPath = Path.Combine(Paths.ConfigPath, "vv_dumps", "path_telemetry.ndjson"); private static readonly object Lock = new object(); public static void LogRegionGraph(int regionCount, int linkCount, float minX, float minZ, float maxX, float maxZ, string regionCenters, string linksSummary) { HnaGraphData hnaGraphData = new HnaGraphData { regionCount = regionCount, linkCount = linkCount, minX = (float)Math.Round(minX, 2), minZ = (float)Math.Round(minZ, 2), maxX = (float)Math.Round(maxX, 2), maxZ = (float)Math.Round(maxZ, 2), regionCenters = (regionCenters ?? ""), linksSummary = (linksSummary ?? "") }; Write("hna_graph", JsonUtility.ToJson((object)hnaGraphData), "hna"); } private static void Write(string message, string dataJson, string runId) { if (!DevSettings.WritePathTelemetry) { return; } try { long num = (long)(Time.time * 1000f); string text = "pt_" + num; string contents = "{\"id\":\"" + text + "\",\"timestamp\":" + num + ",\"location\":\"PathTelemetry\",\"message\":\"" + Escape(message) + "\",\"data\":" + dataJson + ",\"runId\":\"" + Escape(runId) + "\",\"hypothesisId\":\"path_compare\"}\n"; lock (Lock) { File.AppendAllText(LogPath, contents); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[PathTelemetry] Write failed: " + ex.Message)); } } } private static string Escape(string s) { if (string.IsNullOrEmpty(s)) { return ""; } return s.Replace("\\", "\\\\").Replace("\"", "\\\""); } } public static class PhysicsHelper { public static T GetFirstInRadius<T>(Vector3 center, float radius) where T : Component { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Collider[] array = Physics.OverlapSphere(center, radius); foreach (Collider val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null)) { T componentInParent = ((Component)val).gameObject.GetComponentInParent<T>(); if ((Object)(object)componentInParent != (Object)null) { return componentInParent; } } } return default(T); } public static List<T> GetAllInRadius<T>(Vector3 center, float radius) where T : Component { //IL_0006: Unknown result type (might be due to invalid IL or missing references) List<T> list = new List<T>(); Collider[] array = Physics.OverlapSphere(center, radius); foreach (Collider val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null)) { T componentInParent = ((Component)val).gameObject.GetComponentInParent<T>(); if ((Object)(object)componentInParent != (Object)null) { list.Add(componentInParent); } } } return list; } public static bool SupportsClosestPoint(Collider col) { if ((Object)(object)col == (Object)null) { return false; } if (col is BoxCollider || col is SphereCollider || col is CapsuleCollider) { return true; } MeshCollider val = (MeshCollider)(object)((col is MeshCollider) ? col : null); if (val != null) { return val.convex; } return false; } public static Vector3 ClosestPointSafe(Collider col, Vector3 point) { //IL_0009: 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_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_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)col == (Object)null) { return point; } if (!SupportsClosestPoint(col)) { Bounds bounds = col.bounds; return ((Bounds)(ref bounds)).ClosestPoint(point); } return col.ClosestPoint(point); } public static float SqrDistanceTo(Collider col, Vector3 point) { //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_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) Vector3 val = ClosestPointSafe(col, point) - point; return ((Vector3)(ref val)).sqrMagnitude; } } [BepInPlugin("com.valheimvillages.mod", "Valheim Villages", "0.3.6")] public class Plugin : BaseUnityPlugin { public const string PluginGUID = "com.valheimvillages.mod"; public const string PluginName = "Valheim Villages"; public const string PluginVersion = "0.3.6"; private static bool _recipeRefreshEnqueued; private static bool _recordIndexEnqueued; private static bool _villageIndexEnqueued; private static float _lastBakeSweep; private const float StructureChangeMargin = 30f; private static readonly Dictionary<string, float> _villageBakeEnqueuedAt = new Dictionary<string, float>(); private static float _hotReloadAt; private Harmony _harmony; public static readonly DateTime AssemblyLoadedAt = DateTime.UtcNow; public static ManualLogSource Log { get; private set; } public static Plugin Instance { get; private set; } public static bool LastLoadWasHotReload { get; private set; } private void Awake() { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; bool num = (LastLoadWasHotReload = (Object)(object)ObjectDB.instance != (Object)null && ObjectDB.instance.m_items.Count > 0); DebugLog.BeginCycle(num); RegionGraphPersistence.LogAction = delegate(string msg) { Log.LogInfo((object)msg); }; Log.LogInfo((object)"Valheim Villages v0.3.6 loading..."); Harmony.UnpatchID("com.valheimvillages.mod"); _harmony = new Harmony("com.valheimvillages.mod"); _harmony.PatchAll(); LocalizationPatch.RegisterTokens(); if (num) { Log.LogInfo((object)"Hot reload detected — running full cleanup"); HotReloadHelper.FullCleanup(); } TaskHandlerRegistry.Clear(); AttributeScanner.ScanAndRegister(typeof(Plugin).Assembly); if (num) { Log.LogInfo((object)"Hot reload — re-registering items in ObjectDB"); ItemFactory.RegisterAll(ObjectDB.instance); VirtualRecipeLoader.RegisterAll(ObjectDB.instance); AttributeScanner.InvokeObjectDBRegistrations(typeof(Plugin).Assembly, ObjectDB.instance); } if (num && (Object)(object)ZNetScene.instance != (Object)null) { Log.LogInfo((object)"Hot reload — re-registering prefabs in ZNetScene"); AttributeScanner.EnqueueObjectDBDependentTasks(); AttributeScanner.EnqueueAgentDependentTasks(); RecordPrefabFactory.RegisterInZNetScene(ZNetScene.instance); VillagePrefabFactory.RegisterInZNetScene(ZNetScene.instance); Log.LogInfo((object)"Hot reload — fixing up existing NPC components"); HotReloadHelper.FixupExistingNPCs(); Log.LogInfo((object)"Hot reload — fixing up placed registry stations"); HotReloadHelper.FixupExistingRegistries(); _hotReloadAt = Time.realtimeSinceStartup; _villageBakeEnqueuedAt.Clear(); _lastBakeSweep = 0f; _recordIndexEnqueued = false; _villageIndexEnqueued = false; Log.LogInfo((object)"Hot reload — armed per-village navmesh bake sweep (will fire 5s after settle)"); } Log.LogInfo((object)"Valheim Villages loaded successfully!"); IncidentRecorder.ClearOnLoad(); FreezeTime.ApplyAutoFreeze(); if (num && ModTestRunner.AutoRunEnabled) { Log.LogInfo((object)"Scheduling integration tests (will defer until fixtures are ready)..."); GlobalTaskQueue.Enqueue(new VillagerTask { Name = "integration_tests", SourceId = "system", Priority = TaskPriority.Low, TimeoutSeconds = 120f, Attributes = new Dictionary<string, string>() }); } } private void Update() { //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Unknown result type (might be due to invalid IL or missing references) //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_02e2: Unknown result type (might be due to invalid IL or missing references) VillagerRecruitRpc.EnsureRegistered(); VillagerRecallRpc.EnsureRegistered(); WorkOrderConfigRpc.EnsureRegistered(); VillagerPauseRpc.EnsureRegistered(); VillageCleanupRpc.EnsureRegistered(); FragmentQuestRpc.EnsureRegistered(); RescueQuestTracker.TickArrival(Player.m_localPlayer); VillageAlertMonitor.Tick(); if (!_recipeRefreshEnqueued && (Object)(object)ObjectDB.instance != (Object)null && (Object)(object)ZNetScene.instance != (Object)null && Time.realtimeSinceStartup > 3f) { _recipeRefreshEnqueued = true; GlobalTaskQueue.Enqueue(new VillagerTask { Name = "recipe_discovery_refresh", SourceId = "system", Priority = TaskPriority.Low, TimeoutSeconds = 30f, Attributes = new Dictionary<string, string>() }); ManualLogSource log = Log; if (log != null) { log.LogDebug((object)"[Valheim Villages] Enqueued recipe_discovery_refresh (post–world load)"); } } if (!_recordIndexEnqueued && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && (Object)(object)ObjectDB.instance != (Object)null && (Object)(object)ZNetScene.instance != (Object)null && Time.realtimeSinceStartup > 3f) { _recordIndexEnqueued = true; GlobalTaskQueue.Enqueue(new VillagerTask { Name = "villager_record_index", SourceId = "system", Priority = TaskPriority.High, TimeoutSeconds = 30f, Attributes = new Dictionary<string, string>() }); ManualLogSource log2 = Log; if (log2 != null) { log2.LogDebug((object)"[Valheim Villages] Enqueued villager_record_index (post–world load)"); } } if (!_villageIndexEnqueued && (Object)(object)ObjectDB.instance != (Object)null && (Object)(object)ZNetScene.instance != (Object)null && Time.realtimeSinceStartup > 3f) { _villageIndexEnqueued = true; GlobalTaskQueue.Enqueue(new VillagerTask { Name = "village_index", SourceId = "system", Priority = TaskPriority.High, TimeoutSeconds = 30f, Attributes = new Dictionary<string, string>() }); ManualLogSource log3 = Log; if (log3 != null) { log3.LogDebug((object)"[Valheim Villages] Enqueued village_index (post–world load)"); } } if ((Object)(object)ObjectDB.instance != (Object)null && (Object)(object)ZNetScene.instance != (Object)null && (Object)(object)ZoneSystem.instance != (Object)null && Time.realtimeSinceStartup > _hotReloadAt + 5f && Time.realtimeSinceStartup - _lastBakeSweep > 2f) { _lastBakeSweep = Time.realtimeSinceStartup; bool flag = PieceChangePatch.IsDirty && Time.realtimeSinceStartup - PieceChangePatch.LastStructureChangeTime > 3f; bool flag2 = true; if (flag) { ManualLogSource log4 = Log; if (log4 != null) { log4.LogInfo((object)($"[Valheim Villages] {PieceChangePatch.PendingRegions.Count} settled structure " + "change(s); matching against village footprints")); } } foreach (Village item in VillageRegistry.EnumerateAll()) { string villageId = item.VillageId; if (string.IsNullOrEmpty(villageId)) { continue; } Vector3 anchor = item.Anchor; if (anchor == Vector3.zero || !ZoneSystem.instance.IsZoneLoaded(ZoneSystem.GetZone(anchor))) { continue; } float minX = 0f; float minZ = 0f; float maxX = 0f; float maxZ = 0f; bool flag3 = false; if (flag && item.TryGetFootprint(out minX, out minZ, out maxX, out maxZ)) { flag3 = PieceChangePatch.Affects(minX, minZ, maxX, maxZ, 30f); } if (flag3 || (!NavMeshBakeManager.HasVillage(villageId) && (!_villageBakeEnqueuedAt.TryGetValue(villageId, out var value) || !(Time.realtimeSinceStartup - value < 15f)))) { Dictionary<string, string> dictionary = new Dictionary<string, string> { { "village_id", villageId }, { "anchor_x", anchor.x.ToString("F2", CultureInfo.InvariantCulture) }, { "anchor_z", anchor.z.ToString("F2", CultureInfo.InvariantCulture) } }; if (flag3) { PieceChangePatch.DirtyRegion dirtyRegion = PieceChangePatch.UnionAffecting(minX, minZ, maxX, maxZ, 30f); float minX2 = dirtyRegion.MinX; dictionary["dirty_min_x"] = minX2.ToString("R", CultureInfo.InvariantCulture); minX2 = dirtyRegion.MinZ; dictionary["dirty_min_z"] = minX2.ToString("R", CultureInfo.InvariantCulture); minX2 = dirtyRegion.MaxX; dictionary["dirty_max_x"] = minX2.ToString("R", CultureInfo.InvariantCulture); minX2 = dirtyRegion.MaxZ; dictionary["dirty_max_z"] = minX2.ToString("R", CultureInfo.InvariantCulture); } _villageBakeEnqueuedAt[villageId] = Time.realtimeSinceStartup; bool flag4 = GlobalTaskQueue.Enqueue(new VillagerTask { Name = "hna_partition", SourceId = "system", Priority = TaskPriority.Low, TimeoutSeconds = 30f, Attributes = dictionary }); if (!flag4 && flag3) { flag2 = false; } ManualLogSource log5 = Log; if (log5 != null) { log5.LogInfo((object)("[Valheim Villages] Enqueued hna_partition for village " + villageId + " " + $"(baked={NavMeshBakeManager.HasVillage(villageId)}, " + $"structureDirty={flag3}, accepted={flag4})")); } } } if (flag && flag2) { PieceChangePatch.ClearPending(); } } GlobalTaskQueue.ProcessBatch(); PathDebugRenderer.AutoEnable(); PartitionRunner.EnsureHost(); PlayerAvoidanceObstacle.Tick(); foreach (VillagerAI value2 in VillagerAIManager.ActiveVillagers.Values) { if ((Object)(object)value2 != (Object)null) { ((BaseAI)value2).UpdateAI(Time.deltaTime); } } } } public static class VectorExtensions { public static float DistXZ(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 (float)Math.Sqrt(num * num + num2 * num2); } } } namespace ValheimVillages.Villages { [HarmonyPatch(typeof(BaseAI), "RandomMovement")] public static class EnemyAvoidancePatch { private const float AvoidanceRadius = 20f; private const int MaxRerollAttempts = 3; private static void Prefix(BaseAI __instance) { //IL_001a: 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_0057: 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_00a6: 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_00c5: 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_00cc: 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_00f5: 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_00ff: 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_0106: 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_010b: 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_0115: 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_00dc: Unknown result type (might be due to invalid IL or missing references) if (VillageAreaManager.AreaCount == 0) { return; } Character component = ((Component)__instance).GetComponent<Character>(); if ((Object)(object)component == (Object)null || (int)component.m_faction == 0 || component.IsTamed()) { return; } FieldInfo fieldInfo = AccessTools.Field(typeof(BaseAI), "m_randomMoveTarget"); if (fieldInfo == null) { return; } Vector3 val = (Vector3)fieldInfo.GetValue(__instance); if (!VillageAreaManager.IsNearAnyVillage(val, 20f)) { return; } Vector3 position = ((Component)__instance).transform.position; float randomMoveRange = __instance.m_randomMoveRange; for (int i = 0; i < 3; i++) { float num = Random.Range(0f, 360f) * ((float)Math.PI / 180f); float num2 = Random.Range(randomMoveRange * 0.3f, randomMoveRange); Vector3 val2 = position + new Vector3(Mathf.Cos(num) * num2, 0f, Mathf.Sin(num) * num2); if (!VillageAreaManager.IsNearAnyVillage(val2, 20f)) { fieldInfo.SetValue(__instance, val2); return; } } Vector3 val3 = position - val; Vector3 normalized = ((Vector3)(ref val3)).normalized; Vector3 val4 = position + normalized * randomMoveRange; fieldInfo.SetValue(__instance, val4); } } [HarmonyPatch(typeof(Piece), "SetCreator")] public static class StationBuildPatch { [HarmonyPostfix] public static void Postfix(Piece __instance) { if (!((Object)(object)__instance == (Object)null)) { VillageStationRegistry.RegisterStation(((Component)__instance).gameObject); ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); ZDO val = ((component != null) ? component.GetZDO() : null); if (val != null && val.GetPrefab() == StringExtensionMethods.GetStableHashCode("vv_village_registry")) { LinkRegistryToVillage(val); } } } private static void LinkRegistryToVillage(ZDO registryZdo) { //IL_0019: 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_0039: 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_003e: 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_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_0061: 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_009c: 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_00b8: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrEmpty(registryZdo.GetString("vv_village_id", ""))) { return; } Vector3 position = registryZdo.GetPosition(); Vector3 founderPos = (((Object)(object)Player.m_localPlayer != (Object)null) ? ((Component)Player.m_localPlayer).transform.position : position); Village village = VillageRegistry.GetVillageCovering(position) ?? VillageRegistry.GetOrCreateAt(position, founderPos); if (village != null && village.IsInvalid) { village = VillageRegistry.Create(position, founderPos); } if (village != null) { registryZdo.Set("vv_village_id", village.VillageId); registryZdo.Persistent = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[StationBuildPatch] Registry at ({position.x:F1},{position.y:F1},{position.z:F1}) -> village {village.VillageId}"); } } } } public class VillageArea { private readonly List<Vector2> m_polygon2D; private readonly List<Vector3> m_waypoints; public string VillageId { get; } public IReadOnlyList<Vector3> Waypoints => m_waypoints; public VillageArea(string villageId, List<Vector3> waypoints) { //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) //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_004d: Unknown result type (might be due to invalid IL or missing references) VillageId = villageId; m_waypoints = new List<Vector3>(waypoints); m_polygon2D = new List<Vector2>(waypoints.Count); foreach (Vector3 waypoint in waypoints) { m_polygon2D.Add(new Vector2(waypoint.x, waypoint.z)); } } public bool IsInsideArea(Vector3 position) { //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_001d: Unknown result type (might be due to invalid IL or missing references) if (m_polygon2D.Count < 3) { return false; } return IsPointInPolygon(new Vector2(position.x, position.z)); } public bool IsNearBoundary(Vector3 position, float radius) { //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_003b: 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_004f: Unknown result type (might be due to invalid IL or missing references) if (m_polygon2D.Count < 3) { return false; } Vector2 point = default(Vector2); ((Vector2)(ref point))..ctor(position.x, position.z); float num = radius * radius; for (int i = 0; i < m_polygon2D.Count; i++) { int index = (i + 1) % m_polygon2D.Count; if (PointToSegmentDistanceSq(point, m_polygon2D[i], m_polygon2D[index]) <= num) { return true; } } return false; } private bool IsPointInPolygon(Vector2 point) { //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_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_0035: 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_0044: 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_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_0062: 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_0070: 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_0080: 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) bool flag = false; int count = m_polygon2D.Count; int num = 0; int index = count - 1; while (num < count) { Vector2 val = m_polygon2D[num]; Vector2 val2 = m_polygon2D[index]; if (val.y > point.y != val2.y > point.y && point.x < (val2.x - val.x) * (point.y - val.y) / (val2.y - val.y) + val.x) { flag = !flag; } index = num++; } return flag; } private static float PointToSegmentDistanceSq(Vector2 point, Vector2 segA, Vector2 segB) { //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) //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_0008: 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_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_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_0037: 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_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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_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) Vector2 val = segB - segA; Vector2 val2 = point - segA; float sqrMagnitude = ((Vector2)(ref val)).sqrMagnitude; if (sqrMagnitude < 0.0001f) { return ((Vector2)(ref val2)).sqrMagnitude; } float num = Mathf.Clamp01(Vector2.Dot(val2, val) / sqrMagnitude); Vector2 val3 = segA + val * num; Vector2 val4 = point - val3; return ((Vector2)(ref val4)).sqrMagnitude; } } public static class VillageAreaManager { private static readonly Dictionary<string, VillageArea> s_areas = new Dictionary<string, VillageArea>(); public static int AreaCount => s_areas.Count; public static IEnumerable<VillageArea> AllAreas => s_areas.Values; public static void RegisterArea(VillageArea area) { if (area != null) { s_areas[area.VillageId] = area; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[VillageArea] Registered area for {area.VillageId} with {area.Waypoints.Count} waypoints"); } VillageStationRegistry.RefreshFor(area); VillagePoiRegistry.RefreshFor(area); } } public static void UnregisterArea(string villageId) { if (s_areas.Remove(villageId)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[VillageArea] Unregistered area for " + villageId)); } VillageStationRegistry.RemoveFor(villageId); VillagePoiRegistry.RemoveFor(villageId); } } public static void RefreshFromVillage(Village village) { if (village == null || !village.HasGraph) { return; } string villageId = village.VillageId; List<Vector3> list = PatrolRouteBuilder.Build(village.Graph.GetBoundaryCells()); if (list == null || list.Count < 3) { VillageArea villageArea = TryBuildWorkbenchRadiusArea(village); if (villageArea != null) { RegisterArea(villageArea); return; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[VillageArea] Skipped registration for village=" + villageId + ": insufficient boundary " + $"waypoints ({list?.Count ?? 0}) and no nearby workbench to size a fallback")); } } else { RegisterArea(new VillageArea(villageId, list)); } } private static VillageArea TryBuildWorkbenchRadiusArea(Village village) { //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_000c: 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 anchor = village.Anchor; CraftingStation val = CraftingStation.FindClosestStationInRange("$piece_workbench", anchor, 64f); if ((Object)(object)val == (Object)null) { return null; } float stationBuildRange = val.GetStationBuildRange(); if (stationBuildRange <= 0f) { return null; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[VillageArea] village=" + village.VillageId + ": no boundary found — using circular fallback " + $"radius={stationBuildRange:F1}m (nearest workbench) centered on the registry")); } return new VillageArea(village.VillageId, BuildCircleWaypoints(anchor, stationBuildRange, 16)); } private static List<Vector3> BuildCircleWaypoints(Vector3 center, float radius, int segments) { //IL_0018: 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_002d: 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) List<Vector3> list = new List<Vector3>(segments); for (int i = 0; i < segments; i++) { float num = (float)Math.PI * 2f * (float)i / (float)segments; list.Add(new Vector3(center.x + radius * Mathf.Cos(num), center.y, center.z + radius * Mathf.Sin(num))); } return list; } public static bool IsInsideAnyVillage(Vector3 position) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) foreach (VillageArea value in s_areas.Values) { if (value.IsInsideArea(position)) { return true; } } return false; } public static bool IsNearAnyVillage(Vector3 position, float radius) { //IL_001b: 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) foreach (VillageArea value in s_areas.Values) { if (value.IsInsideArea(position) || value.IsNearBoundary(position, radius)) { return true; } } return false; } public static bool TryGetAreaBounds(string villageId, out float minX, out float minZ, out float maxX, out float maxZ) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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_006b: 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_007d: 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_008a: Unknown result type (might be due to invalid IL or missing references) minX = (minZ = float.MaxValue); maxX = (maxZ = float.MinValue); if (string.IsNullOrEmpty(villageId) || !s_areas.TryGetValue(villageId, out var value)) { return false; } foreach (Vector3 waypoint in value.Waypoints) { if (waypoint.x < minX) { minX = waypoint.x; } if (waypoint.x > maxX) { maxX = waypoint.x; } if (waypoint.z < minZ) { minZ = waypoint.z; } if (waypoint.z > maxZ) { maxZ = waypoint.z; } } if (minX <= maxX) { return minZ <= maxZ; } return false; } [RegisterCleanup] public static void Clear() { s_areas.Clear(); VillageStationRegistry.Clear(); VillagePoiRegistry.Clear(); } } public static class VillageCleanupRpc { private const string RpcName = "VV_DeleteVillage"; private static object s_registeredInstance; public static void EnsureRegistered() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance == null || instance == s_registeredInstance) { return; } s_registeredInstance = instance; try { RoutedRpcRegistrar.ClearStale(instance, "VV_DeleteVillage"); instance.Register<string>("VV_DeleteVillage", (Action<long, string>)OnDelete); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[VillageCleanup] register skipped: " + ex.Message)); } } } public static void OnRegistryRemoved(WearNTear wnt) { ZNetView val = (((Object)(object)wnt != (Object)null) ? ((Component)wnt).GetComponent<ZNetView>() : null); ZDO val2 = (((Object)(object)val != (Object)null) ? val.GetZDO() : null); if (val2 != null && val2.GetPrefab() == StringExtensionMethods.GetStableHashCode("vv_village_registry")) { string text = val2.GetString("vv_village_id", ""); if (!string.IsNullOrEmpty(text) && VillageRegistry.CanDelete(text, out var _, 1)) { RequestDelete(text); } } } private static void RequestDelete(string villageId) { EnsureRegistered(); ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC("VV_DeleteVillage", new object[1] { villageId }); } } private static void OnDelete(long sender, string villageId) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || VillageRegistry.FindById(villageId) == null) { return; } if (!VillageRegistry.CanDelete(villageId, out var reason)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[VillageCleanup] not reaping {villageId}: {reason} (requestedBy={sender})"); } return; } VillageRegistry.Delete(villageId); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[VillageCleanup] reaped empty village " + villageId + " — no villagers, no registry " + $"(requestedBy={sender})")); } } } public static class VillagePoiRegistry { private static readonly Dictionary<string, List<KnownLocation>> s_poisByVillage = new Dictionary<string, List<KnownLocation>>(); public static void RefreshFor(VillageArea area) { //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_0058: 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_0062: 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_013e: 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) //IL_0158: 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_0197: 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_019b: 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_0074: 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_0086: 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_0098: 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_00ab: 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_01f2: 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_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0204: 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) if (area == null || area.Waypoints == null || area.Waypoints.Count < 3) { return; } float num = float.MaxValue; float num2 = float.MaxValue; float num3 = float.MinValue; float num4 = float.MinValue; float num5 = float.MaxValue; float num6 = float.MinValue; foreach (Vector3 waypoint in area.Waypoints) { if (waypoint.x < num) { num = waypoint.x; } if (waypoint.x > num3) { num3 = waypoint.x; } if (waypoint.z < num2) { num2 = waypoint.z; } if (waypoint.z > num4) { num4 = waypoint.z; } if (waypoint.y < num5) { num5 = waypoint.y; } if (waypoint.y > num6) { num6 = waypoint.y; } } Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor((num + num3) * 0.5f, (num5 + num6) * 0.5f, (num2 + num4) * 0.5f); Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor((num3 - num) * 0.5f + 1f, (num6 - num5) * 0.5f + 20f, (num4 - num2) * 0.5f + 1f); HashSet<long> outsideCells = RubberBandPrune.ComputeOutsideCellsForHull(new Bounds(val, new Vector3(val2.x * 2f, val2.y * 2f, val2.z * 2f)), area.VillageId + "|poi", null); List<KnownLocation> list = new List<KnownLocation>(); Collider[] array = Physics.OverlapBox(val, val2, Quaternion.identity); foreach (Collider val3 in array) { if ((Object)(object)val3 == (Object)null || (Object)(object)((Component)val3).gameObject == (Object)null) { continue; } LocationType? locationType = ClassifyPoi(((Component)val3).gameObject); if (locationType.HasValue) { Vector3 position = ((Component)val3).gameObject.transform.position; if (!RubberBandPrune.IsOutsideCell(position, outsideCells)) { bool hasShelter = VillagerBehaviorLogic.CheckShelter(position); float comfort = ComfortFor(locationType.Value, hasShelter); TryAddDeduped(list, position, locationType.Value, comfort, hasShelter); } } } s_poisByVillage[area.VillageId] = list; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[VillagePoiRegistry] {area.VillageId}: cached {list.Count} PoI(s) inside hull"); } } public static void RemoveFor(string villageKey) { s_poisByVillage.Remove(villageKey); } public static IReadOnlyList<KnownLocation> GetPois(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Village villageAt = VillageRegistry.GetVillageAt(position); if (villageAt == null) { return Array.Empty<KnownLocation>(); } if (!s_poisByVillage.TryGetValue(villageAt.VillageId, out var value)) { return Array.Empty<KnownLocation>(); } return value; } public static IEnumerable<KnownLocation> GetPois(Vector3 position, LocationType type) { //IL_0008: 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) foreach (KnownLocation poi in GetPois(position)) { if (poi.Type == type) { yield return poi; } } } private static LocationType? ClassifyPoi(GameObject obj) { if ((Object)(object)obj == (Object)null) { return null; } Piece val = obj.GetComponent<Piece>() ?? obj.GetComponentInParent<Piece>(); if ((Object)(object)val != (Object)null && ((Object)((Component)val).gameObject).name.IndexOf("bathtub", StringComparison.OrdinalIgnoreCase) >= 0) { return LocationType.HotTub; } if ((Object)(object)obj.GetComponent<Chair>() != (Object)null) { return LocationType.Chair; } if ((Object)(object)obj.GetComponent<Fireplace>() != (Object)null) { return LocationType.Fire; } string text = ((Object)obj).name.ToLowerInvariant(); if (text.Contains("table") || text.Contains("bench")) { return LocationType.Table; } if (text.Contains("cultivat") || text.Contains("sapling") || text.Contains("plant_")) { return LocationType.Farm; } return null; } private static float ComfortFor(LocationType type, bool hasShelter) { return type switch { LocationType.Fire => hasShelter ? 2f : 0.5f, LocationType.HotTub => 2f, _ => 1f, }; } private static void TryAddDeduped(List<KnownLocation> list, Vector3 pos, LocationType type, float comfort, bool hasShelter) { //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_0062: Unknown result type (might be due to invalid IL or missing references) float minDistanceForType = KnownLocation.GetMinDistanceForType(type); int num = 0; foreach (KnownLocation item in list) { if (item.Type == type) { num++; if (Vector3.Distance(item.Position, pos) < minDistanceForType) { return; } } } if (num < KnownLocation.GetMaxLocationsForType(type)) { list.Add(new KnownLocation { Position = pos, Type = type, ComfortValue = comfort, HasShelter = hasShelter }); } } public static void DumpPois(Village target = null) { //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_0034: 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_004f: 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_0167: 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) Player localPlayer = Player.m_localPlayer; StringBuilder stringBuilder = new StringBuilder(); Village village = target; if (village == null) { if ((Object)(object)localPlayer == (Object)null) { stringBuilder.AppendLine("[vv_village pois] No local player and no village id given."); } else { Vector3 position = ((Component)localPlayer).transform.position; village = VillageRegistry.GetVillageAt(position); if (village == null) { stringBuilder.AppendLine($"[vv_village pois] player at ({position.x:F1},{position.z:F1}) is not inside any registered village."); } } } if (village != null) { string villageId = village.VillageId; List<KnownLocation> value; List<KnownLocation> list = (s_poisByVillage.TryGetValue(villageId, out value) ? value : null); stringBuilder.AppendLine($"[vv_village pois] village {villageId}: {list?.Count ?? 0} PoI(s)"); if (list != null) { foreach (KnownLocation item in list) { stringBuilder.AppendLine($" [{item.Type}] @ ({item.Position.x:F1},{item.Position.y:F1},{item.Position.z:F1}) " + $"shelter={item.HasShelter} comfort={item.ComfortValue:F1}" + (((Object)(object)localPlayer != (Object)null) ? $" dist={Vector3.Distance(((Component)localPlayer).transform.position, item.Position):F1}m" : "")); } } } Console instance = Console.instance; if (instance != null) { instance.Print(stringBuilder.ToString()); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)stringBuilder.ToString()); } } [RegisterCleanup] public static void Clear() { s_poisByVillage.Clear(); } } public static class VillageStationRegistry { private readonly struct StationEntry { public readonly Vector3 Position; public readonly string PrefabName; public StationEntry(Vector3 position, string prefabName) { //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) Position = position; PrefabName = prefabName ?? ""; } } private static readonly Dictionary<string, List<Component>> s_stationsByVillage = new Dictionary<string, List<Component>>(); private const float StationScanPadXZ = 12f; private const float StationVillageReachXZ = 3f; private static readonly Dictionary<Type, bool> s_conversionTypeCache = new Dictionary<Type, bool>(); private const char StationFieldSep = '|'; private const char StationRecordSep = ';'; private const float StationResolveRadius = 2f; private const float ApproachSnapRadius = 3f; private const int MaxRejectionLines = 60; private static string SerializeStations(IEnumerable<StationEntry> entries) { //IL_003c: 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_008a: Unknown result type (might be due to invalid IL or missing references) if (entries == null) { return ""; } CultureInfo invariantCulture = CultureInfo.InvariantCulture; StringBuilder stringBuilder = new StringBuilder(); bool flag = true; foreach (StationEntry entry in entries) { if (!flag) { stringBuilder.Append(';'); } flag = false; float x = entry.Position.x; StringBuilder stringBuilder2 = stringBuilder.Append(x.ToString("R", invariantCulture)).Append('|'); x = entry.Position.y; StringBuilder stringBuilder3 = stringBuilder2.Append(x.ToString("R", invariantCulture)).Append('|'); x = entry.Position.z; stringBuilder3.Append(x.ToString("R", invariantCulture)).Append('|').Append(SanitizeStationName(entry.PrefabName)); } return stringBuilder.ToString(); } private static bool RestoreStations(string data, List<StationEntry> outEntries) { //IL_0092: Unknown result type (might be due to invalid IL or missing references) outEntries.Clear(); if (string.IsNullOrEmpty(data)) { return false; } CultureInfo invariantCulture = CultureInfo.InvariantCulture; string[] array = data.Split(new char[1] { ';' }); foreach (string text in array) { if (!string.IsNullOrEmpty(text)) { string[] array2 = text.Split(new char[1] { '|' }); if (array2.Length >= 4 && float.TryParse(array2[0], NumberStyles.Float, invariantCulture, out var result) && float.TryParse(array2[1], NumberStyles.Float, invariantCulture, out var result2) && float.TryParse(array2[2], NumberStyles.Float, invariantCulture, out var result3)) { outEntries.Add(new StationEntry(new Vector3(result, result2, result3), array2[3])); } } } return outEntries.Count > 0; } private static string SanitizeStationName(string name) { if (string.IsNullOrEmpty(name)) { return ""; } return name.Replace('|', '_').Replace(';', '_'); } private static string CleanPrefabName(GameObject go) { if ((Object)(object)go == (Object)null) { return ""; } string name = ((Object)go).name; if (string.IsNullOrEmpty(name)) { return ""; } int num = name.IndexOf("(Clone)", StringComparison.Ordinal); if (num < 0) { return name; } return name.Substring(0, num); } private static bool HasConversionField(Type t) { if (s_conversionTypeCache.TryGetValue(t, out var value)) { return value; } value = t.GetField("m_conversion", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) != null; s_conversionTypeCache[t] = value; return value; } public static bool TryClassifyStation(GameObject go, out Component station) { station = null; if ((Object)(object)go == (Object)null) { return false; } if ((Object)(object)go.GetComponentInParent<ValheimVillages.Villager.Villager>() != (Object)null) { return false; } if ((Object)(object)go.GetComponentInParent<VillagerStation>() != (Object)null) { return false; } CraftingStation componentInParent = go.GetComponentInParent<CraftingStation>(); if ((Object)(object)componentInParent != (Object)null) { station = (Component)(object)componentInParent; return true; } Piece componentInParent2 = go.GetComponentInParent<Piece>(); MonoBehaviour[] componentsInChildren = (((Object)(object)componentInParent2 != (Object)null) ? ((Component)componentInParent2).gameObject : go).GetComponentsInChildren<MonoBehaviour>(true); foreach (MonoBehaviour val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && HasConversionField(((object)val).GetType())) { station = (Component)(object)val; return true; } } return false; } private static bool BelongsToVillage(RegionGraph graph, Vector3 pos) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (graph == null) { return false; } if (!string.IsNullOrEmpty(graph.PointToRegionId(pos))) { return true; } Vector3 worldPos; string regionId; return graph.TryFindNearestLookupCell(pos, null, out worldPos, out regionId, 3f); } public static void RegisterStation(GameObject go) { //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_0034: 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_00f1: 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) if (!TryClassifyStation(go, out var station)) { return; } Vector3 position = station.transform.position; foreach (Village item in VillageRegistry.EnumerateWithGraph()) { if (!BelongsToVillage(item.Graph, position)) { continue; } string villageId = item.VillageId; if (string.IsNullOrEmpty(villageId)) { continue; } if (!s_stationsByVillage.TryGetValue(villageId, out var value)) { value = (s_stationsByVillage[villageId] = new List<Component>()); } if (!value.Contains(station)) { value.Add(station); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[VillageStationRegistry] +" + ((object)station).GetType().Name + " " + ((Object)station.gameObject).name + " " + $"@ ({position.x:F1},{position.y:F1},{position.z:F1}) → {villageId} (on build)")); } } break; } } public static void RefreshFor(VillageArea area) { //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_0058: 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_0062: 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_007c: 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_008e: 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_0167: 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_00a0: 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_00b4: 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_00bf: 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_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) //IL_032f: Unknown result type (might be due to invalid IL or missing references) //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_034d: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) if (area == null || area.Waypoints == null || area.Waypoints.Count < 3) { return; } float num = float.MaxValue; float num2 = float.MaxValue; float num3 = float.MinValue; float num4 = float.MinValue; float num5 = float.MaxValue; float num6 = float.MinValue; foreach (Vector3 waypoint in area.Waypoints) { if (waypoint.x < num) { num = waypoint.x; } if (waypoint.x > num3) { num3 = waypoint.x; } if (waypoint.z < num2) { num2 = waypoint.z; } if (waypoint.z > num4) { num4 = waypoint.z; } if (waypoint.y < num5) { num5 = waypoint.y; } if (waypoint.y > num6) { num6 = waypoint.y; } } Vector3 val = new Vector3((num + num3) * 0.5f, (num5 + num6) * 0.5f, (num2 + num4) * 0.5f); Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor((num3 - num) * 0.5f + 12f, (num6 - num5) * 0.5f + 20f, (num4 - num2) * 0.5f + 12f); RegionGraph regionGraph = VillageRegistry.FindById(area.VillageId)?.Graph; List<Component> list = new List<Component>(); HashSet<Component> hashSet = new HashSet<Component>(); int num7 = 0; int num8 = 0; Collider[] array = Physics.OverlapBox(val, val2, Quaternion.identity); foreach (Collider val3 in array) { if (!((Object)(object)val3 == (Object)null) && !((Object)(object)((Component)val3).gameObject == (Object)null) && TryClassifyStation(((Component)val3).gameObject, out var station) && hashSet.Add(station)) { num7++; if (regionGraph == null || BelongsToVillage(regionGraph, station.transform.position)) { list.Add(station); num8++; } } } s_stationsByVillage[area.VillageId] = list; Village village = VillageRegistry.FindById(area.VillageId); if (village != null) { List<StationEntry> list2 = new List<StationEntry>(list.Count); foreach (Component item in list) { if (!((Object)(object)item == (Object)null)) { list2.Add(new StationEntry(item.transform.position, CleanPrefabName(item.gameObject))); } } village.SetBlob("vv_village_stations", SerializeStations(list2)); } if (Plugin.Log == null) { return; } Plugin.Log.LogInfo((object)($"[VillageStationRegistry] {area.VillageId}: cached {list.Count} stations " + $"(candidates {num7} → kept {num8})")); foreach (Component item2 in list) { Vector3 position = item2.transform.position; Plugin.Log.LogInfo((object)$" [{((object)item2).GetType().Name}] {((Object)item2.gameObject).name} @ ({position.x:F1},{position.y:F1},{position.z:F1})"); } } private static void EnsureHydrated(string villageKey) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(villageKey) || (s_stationsByVillage.TryGetValue(villageKey, out var value) && value != null && value.Count > 0)) { return; } Village village = VillageRegistry.FindById(villageKey); if (village == null) { return; } List<StationEntry> list = new List<StationEntry>(); if (!RestoreStations(village.GetBlob("vv_village_stations"), list)) { return; } List<Component> list2 = new List<Component>(list.Count); HashSet<Component> hashSet = new HashSet<Component>(); foreach (StationEntry item in list) { Collider[] array = Physics.OverlapSphere(item.Position, 2f); foreach (Collider val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && TryClassifyStation(((Component)val).gameObject, out var station) && hashSet.Add(station)) { list2.Add(station); } } } s_stationsByVillage[villageKey] = list2; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)($"[VillageStationRegistry] {villageKey}: hydrated {list2.Count}/{list.Count} " + "station(s) from ZDO blob")); } } public static void RemoveFor(string villageKey) { s_stationsByVillage.Remove(villageKey); } public static bool HasVillageFor(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) string villageId; return TryGetVillage(position, out villageId); } public static int CountCookingOutputInFlight(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (!TryGetVillage(position, out var villageId)) { return 0; } EnsureHydrated(villageId); if (!s_stationsByVillage.TryGetValue(villageId, out var value)) { return 0; } int num = 0; foreach (Component item in value) { CookingStation val = (CookingStation)(object)((item is CookingStation) ? item : null); if (val != null) { num += StationFinder.CountOccupiedSlots(val); } } return num; } public static bool TryFindStation<T>(Vector3 position, Func<T, bool> filter, out Vector3 stationPos, out T component) where T : Component { //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_0012: 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_00f0: 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_01c1: 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) //IL_0159: 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_018d: 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) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) stationPos = Vector3.zero; component = default(T); if (!TryGetVillage(position, out var villageId)) { return false; } EnsureHydrated(villageId); if (!s_stationsByVillage.TryGetValue(villageId, out var value)) { return false; } T val = default(T); float num = float.MaxValue; foreach (Component item in value) { if ((Object)(object)item == (Object)null) { continue; } T val2 = (T)(object)((item is T) ? item : null); if (!((Object)(object)val2 == (Object)null) && (filter == null || filter(val2))) { Vector3 val3 = ((Component)val2).transform.position - position; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; val = val2; } } } if ((Object)(object)val == (Object)null) { return false; } if (!VillagerMovement.TryResolveApproach(((Component)val).transform.position, position, null, out var approach)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[VillageStationRegistry] " + villageId + ": no HNA-valid approach to " + ((Object)((Component)val).gameObject).name + " " + $"@ ({((Component)val).transform.position.x:F1},{((Component)val).transform.position.y:F1},{((Component)val).transform.position.z:F1})")); } stationPos = Vector3.zero; component = default(T); return false; } stationPos = approach; component = val; return true; } [DevCommand("Explain why a target is (un)reachable, gate by gate. Usage: vv_approach <x> <z> [y] [villagerName] (X,Z,[Y] order)", Name = "vv_approach")] public static void ApproachDiag(ConsoleEventArgs args) { //IL_00ab: 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_0101: 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_0161: 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_0221: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_0239: 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) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: 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_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_02e9: Unknown result type (might be due to in