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 QuanChunkLoader v1.1.3
QuanChunkLoader.dll
Decompiled 19 hours 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.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using QuanChunkLoader.Core; using Splatform; using UnityEngine; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("QuanChunkLoader")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.3.0")] [assembly: AssemblyInformationalVersion("1.1.3+362f1cb68cc8dc9d417073e2f4a1b8006410d2f9")] [assembly: AssemblyProduct("QuanChunkLoader")] [assembly: AssemblyTitle("QuanChunkLoader")] [assembly: AssemblyVersion("1.1.3.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace QuanChunkLoader { internal static class Log { internal static ManualLogSource Source; private static string _fileDay; private static StreamWriter _file; private static string Folder => Path.Combine(Paths.BepInExRootPath, "QuanChunkLoaderLogs"); internal static bool DebugOn => Settings.Detail.Value >= LogDetail.Debug; internal static bool TraceOn => Settings.Detail.Value >= LogDetail.Trace; internal static void Info(string message) { Write((LogLevel)16, message); } internal static void Warning(string message) { Write((LogLevel)4, message); } internal static void Error(string message) { Write((LogLevel)2, message); } internal static void Debug(string message) { if (DebugOn) { Write((LogLevel)16, "[debug] " + message); } } internal static void Trace(string message) { if (TraceOn) { Write((LogLevel)16, "[trace] " + message); } } private static void Write(LogLevel level, string message) { //IL_0023: 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) DateTime now = DateTime.Now; string text = now.ToString("HH:mm:ss zzz") + " " + message; Source.Log(level, (object)text); if (Settings.DailyFile != null && Settings.DailyFile.Value) { WriteFile(now, level, text); } } private static void WriteFile(DateTime now, LogLevel level, string line) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Invalid comparison between Unknown and I4 //IL_0088: 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_00a8: Invalid comparison between Unknown and I4 try { string text = now.ToString("yyyy-MM-dd"); if (_file == null || text != _fileDay) { _file?.Dispose(); Directory.CreateDirectory(Folder); _file = new StreamWriter(Path.Combine(Folder, "QuanChunkLoader-" + text + ".log"), append: true); _fileDay = text; PruneOldFiles(now); } _file.WriteLine(now.ToString("yyyy-MM-dd ") + (((int)level == 16) ? "" : $"[{level}] ") + line); if ((int)level != 16) { _file.Flush(); } } catch (Exception ex) { Settings.DailyFile.Value = false; Source.LogWarning((object)("Couldn't write the daily log file, so it's switched off: " + ex.Message)); } } internal static void Flush() { try { _file?.Flush(); } catch (Exception) { } } private static void PruneOldFiles(DateTime now) { DateTime dateTime = now.Date.AddDays(-Settings.DaysToKeep.Value); string[] files = Directory.GetFiles(Folder, "QuanChunkLoader-*.log"); foreach (string path in files) { if (DateTime.TryParse(Path.GetFileNameWithoutExtension(path).Substring("QuanChunkLoader-".Length), out var result) && result < dateTime) { File.Delete(path); } } } } internal static class BuildPermission { private static readonly List<Piece> Pieces = new List<Piece>(); private static readonly Action<Player> UpdateAvailablePieces = AccessTools.MethodDelegate<Action<Player>>(AccessTools.Method(typeof(Player), "UpdateAvailablePiecesList", (Type[])null, (Type[])null), (object)null, true); internal static void Track(Piece piece) { if ((Object)(object)piece != (Object)null) { Pieces.Add(piece); Apply(); } } internal static void Hook() { Settings.AdminOnlyBuilding.SettingChanged += delegate { Apply(); }; SynchronizationManager.OnAdminStatusChanged += Apply; SynchronizationManager.OnConfigurationSynchronized += delegate { Apply(); }; } internal static void Apply() { bool flag = !Settings.AdminOnlyBuilding.Value || SynchronizationManager.Instance.PlayerIsAdmin; bool flag2 = false; foreach (Piece piece in Pieces) { if ((Object)(object)piece != (Object)null && piece.m_enabled != flag) { piece.m_enabled = flag; flag2 = true; } } if (flag2 && (Object)(object)Player.m_localPlayer != (Object)null) { UpdateAvailablePieces(Player.m_localPlayer); } } } internal sealed class ChunkObserver : MonoBehaviour, Hoverable, Interactable { private ZNetView _nview; private Piece _piece; private GameObject _effect; private Material _columnMaterial; private Color _onGlow; private bool _shownOn; private static readonly int EmissionColor = Shader.PropertyToID("_EmissionColor"); private static readonly List<ChunkObserver> Loaded = new List<ChunkObserver>(); private static readonly Color OffGlow = new Color(6f, 0f, 0f, 1f); private bool IsOn { get { if ((Object)(object)_nview != (Object)null && _nview.IsValid()) { return _nview.GetZDO().GetBool("QCL_on", false); } return false; } } private string BuilderName { get { string text = _nview.GetZDO().GetString("QCL_builder", ""); if (!string.IsNullOrEmpty(text)) { return text; } return "unknown"; } } private static bool LocalIsAdmin => SynchronizationManager.Instance.PlayerIsAdmin; internal static bool AnyOnCovering(Vector2s zone) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) foreach (ChunkObserver item in Loaded) { if (!((Object)(object)item == (Object)null) && item.IsOn) { Vector2s zone2 = ZoneSystem.GetZone(((Component)item).transform.position); int num = ObserverPiece.RadiusOf(item._nview.GetZDO().GetPrefab()); if (Mathf.Abs(zone.x - zone2.x) <= num && Mathf.Abs(zone.y - zone2.y) <= num) { return true; } } } return false; } private void Awake() { _nview = ((Component)this).GetComponent<ZNetView>(); _piece = ((Component)this).GetComponent<Piece>(); Transform val = ((Component)this).transform.Find("WayEffect"); _effect = (((Object)(object)val != (Object)null) ? ((Component)val).gameObject : null); TakeOwnMaterial(); if ((Object)(object)_nview == (Object)null || _nview.GetZDO() == null) { ShowState(on: false); return; } _nview.Register<string, bool>("QCL_SetBool", (Action<long, string, bool>)RPC_SetBool); _nview.Register<string, long>("QCL_SetLong", (Action<long, string, long>)RPC_SetLong); Loaded.Add(this); _shownOn = IsOn; ShowState(_shownOn); } private void TakeOwnMaterial() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) Material columnMaterial = ObserverPiece.ColumnMaterial; if ((Object)(object)columnMaterial == (Object)null || !columnMaterial.HasProperty(EmissionColor)) { return; } _columnMaterial = new Material(columnMaterial); _onGlow = columnMaterial.GetColor(EmissionColor); Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val.sharedMaterial == (Object)(object)columnMaterial) { val.sharedMaterial = _columnMaterial; } } } private void Start() { //IL_009d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_nview == (Object)null) && _nview.IsValid() && _nview.IsOwner() && !((Object)(object)Player.m_localPlayer == (Object)null)) { ZDO zDO = _nview.GetZDO(); if (zDO.GetString("QCL_builder", "") == "" && _piece.GetCreator() == Player.m_localPlayer.GetPlayerID()) { zDO.Set("QCL_builder", Player.m_localPlayer.GetPlayerName()); ZRoutedRpc.instance.InvokeRoutedRPC("QCL_Placed", new object[1] { zDO.m_uid }); } } } private void Update() { if (ZInput.GetButtonDown("AltPlace") && !Typing() && LookedAtByLocalPlayer()) { ShowZones(); } bool isOn = IsOn; if (isOn != _shownOn) { _shownOn = isOn; ShowState(isOn); } } private static bool Typing() { if ((!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Console.IsVisible() && !TextInput.IsVisible() && !Menu.IsVisible() && !InventoryGui.IsVisible() && !Minimap.IsOpen()) { if ((Object)(object)Hud.instance != (Object)null) { return Hud.instance.m_buildUi.SearchFieldFocused; } return false; } return true; } private void ShowState(bool on) { //IL_003e: 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) if ((Object)(object)_effect != (Object)null) { _effect.SetActive(on); } if ((Object)(object)_columnMaterial != (Object)null) { _columnMaterial.SetColor(EmissionColor, on ? _onGlow : OffGlow); } } private bool LookedAtByLocalPlayer() { Player localPlayer = Player.m_localPlayer; GameObject val = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetHoverObject() : null); if ((Object)(object)val != (Object)null) { return (Object)(object)val.GetComponentInParent<ChunkObserver>() == (Object)(object)this; } return false; } private void ShowZones() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_nview == (Object)null) && _nview.IsValid()) { ZoneGrid.Show(ZoneSystem.GetZone(((Component)this).transform.position), ObserverPiece.RadiusOf(_nview.GetZDO().GetPrefab())); } } private void OnDestroy() { Loaded.Remove(this); if ((Object)(object)_columnMaterial != (Object)null) { Object.Destroy((Object)(object)_columnMaterial); } } private void RPC_SetBool(long sender, string key, bool value) { if (FromServerToOwner(sender)) { switch (key) { case "QCL_on": case "QCL_admin": case "QCL_blocked": _nview.GetZDO().Set(key, value); break; } } } private void RPC_SetLong(long sender, string key, long value) { if (FromServerToOwner(sender) && key == "QCL_refreshed") { _nview.GetZDO().Set(key, value); } } private bool FromServerToOwner(long sender) { if (!_nview.IsOwner()) { return false; } if (ZNet.instance.IsServer()) { return true; } ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer != null) { return sender == serverPeer.m_uid; } return false; } private bool MaySwitch(out string refusal) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) refusal = null; if (Settings.BuilderOrAdminSwitches.Value) { if (_piece.IsCreator() || LocalIsAdmin) { return true; } refusal = "Only " + BuilderName + " or an admin can switch this observer."; return false; } if (PrivateArea.CheckAccess(((Component)this).transform.position, 0f, true, false)) { return true; } refusal = "A ward here stops you switching this observer."; return false; } public string GetHoverName() { if (!((Object)(object)_piece != (Object)null)) { return ""; } return Localization.instance.Localize(_piece.m_name); } public float GetHoverOffset() { return 0f; } public string GetHoverText() { if ((Object)(object)_nview == (Object)null || !_nview.IsValid()) { return ""; } ZDO zDO = _nview.GetZDO(); bool isOn = IsOn; string text = ObserverPiece.SizeLabel(zDO.GetPrefab()); string text2 = GetHoverName() + (isOn ? ("\n<color=#7fffd4>On</color>: keeping " + text + " zones loaded") : ("\n<color=#a0a0a0>Off</color> (loads " + text + " zones)")) + "\nBuilt by " + BuilderName; if (zDO.GetBool("QCL_blocked", false)) { text2 += "\n<color=#ff8080>Built while only admins could build these: it stays off</color>"; } if (LocalIsAdmin && zDO.GetBool("QCL_admin", false)) { text2 += "\n<color=orange>Admin-placed</color>"; } if (MaySwitch(out var _)) { text2 += Localization.instance.Localize("\n[<color=yellow><b>$KEY_Use</b></color>] " + (isOn ? "Switch off" : "Switch on")); } return text2 + Localization.instance.Localize("\n[<color=yellow><b>$KEY_AltPlace</b></color>] Show its zones for 2m"); } public bool Interact(Humanoid user, bool hold, bool alt) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) if (hold || (Object)(object)_nview == (Object)null || !_nview.IsValid()) { return false; } if (alt) { ShowZones(); return true; } if (!MaySwitch(out var refusal)) { ((Character)user).Message((MessageType)2, refusal, 0, (Sprite)null, false); return true; } ZRoutedRpc.instance.InvokeRoutedRPC("QCL_Switch", new object[2] { _nview.GetZDO().m_uid, !IsOn }); return true; } public bool UseItem(Humanoid user, ItemData item) { return false; } } internal static class ClusterModel { private const float ShaftBottom = 0.295f; private const float ShaftTop = 2.205f; private const float ColumnOut = 0.08f; private const float ColumnSideways = 0f; private static readonly Vector3[] Discs = (Vector3[])(object)new Vector3[4] { new Vector3(0.56f, 0f, 0.15f), new Vector3(0.44f, 0.15f, 0.31f), new Vector3(0.44f, 2.19f, 2.37f), new Vector3(0.37f, 2.37f, 2.47f) }; private static readonly Rect Stone = new Rect(0.04f, 0.72f, 0.16f, 0.16f); private const int Segments = 32; internal static void Build(GameObject prefab) { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00da: 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_010d: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0154: 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_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) Transform val = prefab.transform.Find("new/SM_cloumn"); Transform val2 = prefab.transform.Find("new"); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { throw new InvalidOperationException("the column or model child is missing"); } Mesh sharedMesh = ((Component)val).GetComponent<MeshFilter>().sharedMesh; Material sharedMaterial = ((Component)val).GetComponent<Renderer>().sharedMaterial; Mesh sharedMesh2 = ShaftOnly(sharedMesh); List<Transform> list = new List<Transform> { val }; for (int i = 1; i < 3; i++) { list.Add(Object.Instantiate<GameObject>(((Component)val).gameObject, val2, false).transform); } for (int j = 0; j < 3; j++) { Transform obj = list[j]; ((Object)obj).name = $"column{j}"; float num = (float)j * 120f; Quaternion val3 = Quaternion.Euler(0f, num, 0f); obj.localPosition = val3 * new Vector3(0f, 0f, 0.08f); obj.localRotation = Quaternion.Euler(0f, num - 90f, 0f); ((Component)obj).GetComponent<MeshFilter>().sharedMesh = sharedMesh2; MeshCollider component = ((Component)obj).GetComponent<MeshCollider>(); if ((Object)(object)component != (Object)null) { component.sharedMesh = sharedMesh2; } } GameObject val4 = new GameObject("plinths") { layer = ((Component)val).gameObject.layer }; val4.transform.SetParent(val2, false); Mesh sharedMesh3 = DiscStack(Discs, "QuanChunkObserver_plinths"); val4.AddComponent<MeshFilter>().sharedMesh = sharedMesh3; ((Renderer)val4.AddComponent<MeshRenderer>()).sharedMaterial = sharedMaterial; val4.AddComponent<MeshCollider>().sharedMesh = sharedMesh3; LODGroup component2 = prefab.GetComponent<LODGroup>(); if ((Object)(object)component2 != (Object)null) { Object.DestroyImmediate((Object)(object)component2); } } private static Mesh ShaftOnly(Mesh source) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0092: 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_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown Vector3[] vertices = source.vertices; List<int> list = new List<int>(); int[] triangles = source.triangles; for (int i = 0; i < triangles.Length; i += 3) { bool flag = true; for (int j = 0; j < 3; j++) { float y = vertices[triangles[i + j]].y; if (y < 0.295f || y > 2.205f) { flag = false; break; } } if (flag) { list.Add(triangles[i]); list.Add(triangles[i + 1]); list.Add(triangles[i + 2]); } } Mesh val = new Mesh { name = "QuanChunkObserver_shaft", vertices = vertices, normals = source.normals, tangents = source.tangents, uv = source.uv }; val.SetTriangles(list, 0); val.RecalculateBounds(); return val; } internal static Mesh DiscStack(Vector3[] discs, string name) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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_005c: 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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown List<Vector3> list = new List<Vector3>(); List<Vector3> list2 = new List<Vector3>(); List<Vector2> list3 = new List<Vector2>(); List<int> list4 = new List<int>(); foreach (Vector3 val in discs) { Disc(val.x, val.y, val.z, list, list2, list3, list4); } Mesh val2 = new Mesh { name = name }; val2.SetVertices(list); val2.SetNormals(list2); val2.SetUVs(0, list3); val2.SetTriangles(list4, 0); val2.RecalculateTangents(); val2.RecalculateBounds(); return val2; } private static Vector2 StoneUv(float u, float v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) Rect stone = Stone; float x = ((Rect)(ref stone)).x; stone = Stone; float num = x + ((Rect)(ref stone)).width * u; stone = Stone; float y = ((Rect)(ref stone)).y; stone = Stone; return new Vector2(num, y + ((Rect)(ref stone)).height * v); } private static void Disc(float radius, float bottom, float top, List<Vector3> v, List<Vector3> n, List<Vector2> uv, List<int> tri) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0109: 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_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) int num = 8; Vector3 val = default(Vector3); Vector3 val2 = default(Vector3); for (int i = 0; i < 32; i++) { float num2 = (float)Math.PI * 2f * (float)i / 32f; float num3 = (float)Math.PI * 2f * (float)(i + 1) / 32f; ((Vector3)(ref val))..ctor(Mathf.Sin(num2), 0f, Mathf.Cos(num2)); ((Vector3)(ref val2))..ctor(Mathf.Sin(num3), 0f, Mathf.Cos(num3)); float u = (float)(i % num) / (float)num; float u2 = (float)(i % num + 1) / (float)num; int count = v.Count; v.Add(val * radius + Vector3.up * bottom); n.Add(val); uv.Add(StoneUv(u, 0f)); v.Add(val * radius + Vector3.up * top); n.Add(val); uv.Add(StoneUv(u, 1f)); v.Add(val2 * radius + Vector3.up * top); n.Add(val2); uv.Add(StoneUv(u2, 1f)); v.Add(val2 * radius + Vector3.up * bottom); n.Add(val2); uv.Add(StoneUv(u2, 0f)); tri.Add(count); tri.Add(count + 2); tri.Add(count + 1); tri.Add(count); tri.Add(count + 3); tri.Add(count + 2); } Cap(radius, top, Vector3.up, v, n, uv, tri); Cap(radius, bottom, Vector3.down, v, n, uv, tri); } private static void Cap(float radius, float y, Vector3 normal, List<Vector3> v, List<Vector3> n, List<Vector2> uv, List<int> tri) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) int count = v.Count; v.Add(new Vector3(0f, y, 0f)); n.Add(normal); uv.Add(StoneUv(0.5f, 0.5f)); for (int i = 0; i <= 32; i++) { float num = (float)Math.PI * 2f * (float)i / 32f; float num2 = Mathf.Sin(num); float num3 = Mathf.Cos(num); v.Add(new Vector3(num2 * radius, y, num3 * radius)); n.Add(normal); uv.Add(StoneUv(0.5f + num2 / 2f, 0.5f + num3 / 2f)); } for (int j = 0; j < 32; j++) { int item = count + 1 + j; int item2 = count + 2 + j; if (normal.y > 0f) { tri.Add(count); tri.Add(item); tri.Add(item2); } else { tri.Add(count); tri.Add(item2); tri.Add(item); } } } } internal static class ObserverPiece { internal const string SingleName = "Quan_ChunkObserver"; internal const string ClusterName = "Quan_ChunkObserverCluster"; internal static readonly int SingleHash = StringExtensionMethods.GetStableHashCode("Quan_ChunkObserver"); internal static readonly int ClusterHash = StringExtensionMethods.GetStableHashCode("Quan_ChunkObserverCluster"); internal const int SingleCost = 10; internal const int ClusterCost = 30; private const string BundleResource = "chunkloader"; private const string ModelPrefab = "ChunkLoader_stone"; private const string EffectsFrom = "stone_wall_1x1"; private const float ModelScale = 0.5f; internal const string ModelChild = "new"; internal const string ColumnChild = "new/SM_cloumn"; internal const string EffectChild = "WayEffect"; private const string Dedication = "\n\n<i>Dedicated to Frogger, who first brought chunk loading to Valheim. This is his original chunk loader model, used as a tribute.</i>"; private const string ClusterDedication = "\n\n<i>Dedicated to Frogger, who first brought chunk loading to Valheim. Our own adaptation of his original chunk loader model, in its style, as a tribute.</i>"; private const string SingleDescription = "Keeps its own zone loaded while switched on, so your base keeps working while you're elsewhere. Nearby players still own everything as usual.\n\n<i>Dedicated to Frogger, who first brought chunk loading to Valheim. This is his original chunk loader model, used as a tribute.</i>"; private const string ClusterDescription = "Keeps the 3x3 zones around it loaded while switched on, so a large base keeps working while you're elsewhere. Nearby players still own everything as usual.\n\n<i>Dedicated to Frogger, who first brought chunk loading to Valheim. Our own adaptation of his original chunk loader model, in its style, as a tribute.</i>"; internal static Material ColumnMaterial { get; private set; } internal static Sprite IconActive { get; private set; } internal static Sprite IconDisabled { get; private set; } internal static bool IsObserver(int prefabHash) { if (prefabHash != SingleHash) { return prefabHash == ClusterHash; } return true; } internal static int RadiusOf(int prefabHash) { return (prefabHash == ClusterHash) ? 1 : 0; } internal static int ZonesOf(int prefabHash) { int num = 2 * RadiusOf(prefabHash) + 1; return num * num; } internal static string SizeLabel(int prefabHash) { if (prefabHash != ClusterHash) { return "1x1"; } return "3x3"; } public static void Register() { AssetBundle val = AssetUtils.LoadAssetBundleFromResources("chunkloader", Assembly.GetExecutingAssembly()); if ((Object)(object)val == (Object)null) { Log.Error("The observer model's asset bundle 'chunkloader' wasn't found; Chunk Observers not added."); return; } GameObject val2 = val.LoadAsset<GameObject>("ChunkLoader_stone"); IconActive = val.LoadAsset<Sprite>("icon_active"); IconDisabled = val.LoadAsset<Sprite>("icon_disabled"); if ((Object)(object)val2 == (Object)null) { Log.Error("'ChunkLoader_stone' isn't in the asset bundle; Chunk Observers not added."); return; } GameObject prefab = PrefabManager.Instance.CreateClonedPrefab("Quan_ChunkObserver", val2); Setup(prefab); Add(prefab, "Quan's Chunk Observer", "Keeps its own zone loaded while switched on, so your base keeps working while you're elsewhere. Nearby players still own everything as usual.\n\n<i>Dedicated to Frogger, who first brought chunk loading to Valheim. This is his original chunk loader model, used as a tribute.</i>", 10, IconActive); GameObject prefab2 = PrefabManager.Instance.CreateClonedPrefab("Quan_ChunkObserverCluster", val2); Setup(prefab2); try { ClusterModel.Build(prefab2); } catch (Exception arg) { Log.Error($"Couldn't build the Chunk Observer Cluster's model; it isn't added: {arg}"); return; } Add(prefab2, "Quan's Chunk Observer Cluster", "Keeps the 3x3 zones around it loaded while switched on, so a large base keeps working while you're elsewhere. Nearby players still own everything as usual.\n\n<i>Dedicated to Frogger, who first brought chunk loading to Valheim. Our own adaptation of his original chunk loader model, in its style, as a tribute.</i>", 30, RenderIcon(prefab2) ?? IconActive); } private static void Add(GameObject prefab, string name, string description, int stone, Sprite icon) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown PieceConfig val = new PieceConfig(); val.Name = name; val.Description = description; val.PieceTable = PieceTables.Hammer; val.Category = "QuanPieces"; val.Requirements = (RequirementConfig[])(object)new RequirementConfig[1] { new RequirementConfig("Stone", stone, 0, true) }; PieceConfig val2 = val; if ((Object)(object)icon != (Object)null) { val2.Icon = icon; } PieceManager.Instance.AddPiece(new CustomPiece(prefab, false, val2)); BuildPermission.Track(prefab.GetComponent<Piece>()); } internal static Sprite RenderIcon(GameObject prefab) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0011: Unknown result type (might be due to invalid IL or missing references) if ((int)SystemInfo.graphicsDeviceType == 4) { return null; } try { return RenderManager.Instance.Render(prefab, RenderManager.IsometricRotation); } catch (Exception ex) { Log.Warning("Couldn't render an icon for " + ((Object)prefab).name + "; using the single column's: " + ex.Message); return null; } } private static void Setup(GameObject prefab) { //IL_004a: 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_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_0145: Unknown result type (might be due to invalid IL or missing references) prefab.GetComponent<ZNetView>().m_persistent = true; string[] array = new string[2] { "new", "WayEffect" }; foreach (string text in array) { Transform val = prefab.transform.Find(text); if ((Object)(object)val != (Object)null) { val.localScale *= 0.5f; } else { Log.Warning("Observer model: child '" + text + "' not found."); } } ParticleSystem[] componentsInChildren = prefab.GetComponentsInChildren<ParticleSystem>(true); for (int i = 0; i < componentsInChildren.Length; i++) { MainModule main = componentsInChildren[i].main; ((MainModule)(ref main)).scalingMode = (ParticleSystemScalingMode)0; } Light componentInChildren = prefab.GetComponentInChildren<Light>(true); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.range *= 0.5f; } FixShaders(prefab); Transform val2 = prefab.transform.Find("new/SM_cloumn"); if ((Object)(object)val2 != (Object)null && (Object)(object)ColumnMaterial == (Object)null) { ColumnMaterial = ((Component)val2).GetComponent<Renderer>().sharedMaterial; } Piece component = prefab.GetComponent<Piece>(); ((StaticTarget)component).m_primaryTarget = false; ((StaticTarget)component).m_randomTarget = false; component.m_targetNonPlayerBuilt = false; WearNTear component2 = prefab.GetComponent<WearNTear>(); component2.m_noRoofWear = false; component2.m_noSupportWear = false; component2.m_materialType = (MaterialType)1; component2.m_burnable = false; component2.m_triggerPrivateArea = false; CopyEffects(component, component2); prefab.AddComponent<ChunkObserver>(); } internal static void CopyEffects(Piece piece, WearNTear wear) { GameObject prefab = PrefabManager.Instance.GetPrefab("stone_wall_1x1"); if ((Object)(object)prefab == (Object)null) { Log.Warning("'stone_wall_1x1' not found; " + ((Object)piece).name + " has no place or destroy effects."); return; } piece.m_placeEffect = prefab.GetComponent<Piece>().m_placeEffect; WearNTear component = prefab.GetComponent<WearNTear>(); wear.m_hitEffect = component.m_hitEffect; wear.m_destroyedEffect = component.m_destroyedEffect; } private static void FixShaders(GameObject prefab) { Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Material[] sharedMaterials = componentsInChildren[i].sharedMaterials; foreach (Material val in sharedMaterials) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val.shader == (Object)null)) { string name = ((Object)val.shader).name; Shader val2 = Shader.Find(name); if ((Object)(object)val2 != (Object)null) { val.shader = val2; } Log.Debug("Observer model: material '" + ((Object)val).name + "' uses shader '" + name + "'" + (((Object)(object)val2 != (Object)null) ? " (game copy)." : " (bundle copy; no game copy found).")); } } } } } internal static class PhantomPiece { internal const string PrefabName = "Quan_PhantomSpawner"; internal static readonly int PrefabHash = StringExtensionMethods.GetStableHashCode("Quan_PhantomSpawner"); internal const int Cost = 10; private const string WardPrefab = "guard_stone"; private const float ModelScale = 0.25f; private static readonly Color Glow = new Color(1f, 0.15f, 0.1f); private static readonly Color StoneTint = new Color(0.86f, 0.87f, 0.88f); private static readonly int MainTex = Shader.PropertyToID("_MainTex"); private static readonly int EmissionColor = Shader.PropertyToID("_EmissionColor"); private static readonly int EmissionMap = Shader.PropertyToID("_EmissionMap"); private const string Description = "Makes nearby creature spawners act as if a player were standing here, so a surtling farm keeps producing while you're away. Only works inside zones a switched-on Chunk Observer keeps loaded."; private static readonly Vector3[] PlinthDiscs = (Vector3[])(object)new Vector3[2] { new Vector3(0.2f, 0f, 0.07f), new Vector3(0.16f, 0.07f, 0.11f) }; private const float PlinthHeight = 0.11f; public static void Register() { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Expected O, but got Unknown //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Expected O, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Expected O, but got Unknown GameObject prefab = PrefabManager.Instance.GetPrefab("guard_stone"); if ((Object)(object)prefab == (Object)null) { Log.Error("The vanilla ward 'guard_stone' wasn't found; Phantom Spawner not added."); return; } GameObject val = PrefabManager.Instance.CreateClonedPrefab("Quan_PhantomSpawner", prefab); Strip(val); Shrink(val); Redden(val); AddPlinth(val); val.GetComponent<ZNetView>().m_persistent = true; Piece component = val.GetComponent<Piece>(); ((StaticTarget)component).m_primaryTarget = false; ((StaticTarget)component).m_randomTarget = false; component.m_targetNonPlayerBuilt = false; component.m_craftingStation = null; component.m_comfort = 0; component.m_isUpgrade = false; component.m_spaceRequirement = 0f; WearNTear component2 = val.GetComponent<WearNTear>(); if ((Object)(object)component2 != (Object)null) { component2.m_noRoofWear = false; component2.m_noSupportWear = false; component2.m_materialType = (MaterialType)1; component2.m_burnable = false; component2.m_triggerPrivateArea = false; ObserverPiece.CopyEffects(component, component2); } val.AddComponent<PhantomSpawner>(); PieceConfig val2 = new PieceConfig(); val2.Name = "Quan's Phantom Spawner"; val2.Description = "Makes nearby creature spawners act as if a player were standing here, so a surtling farm keeps producing while you're away. Only works inside zones a switched-on Chunk Observer keeps loaded."; val2.PieceTable = PieceTables.Hammer; val2.Category = "QuanPieces"; val2.CraftingStation = ""; val2.Requirements = (RequirementConfig[])(object)new RequirementConfig[1] { new RequirementConfig("Stone", 10, 0, true) }; PieceConfig val3 = val2; Sprite val4 = ObserverPiece.RenderIcon(val); if ((Object)(object)val4 != (Object)null) { val3.Icon = val4; } PieceManager.Instance.AddPiece(new CustomPiece(val, false, val3)); BuildPermission.Track(component); } private static void Strip(GameObject prefab) { StringBuilder stringBuilder = new StringBuilder(); ParticleSystem[] componentsInChildren = prefab.GetComponentsInChildren<ParticleSystem>(true); foreach (ParticleSystem val in componentsInChildren) { if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).gameObject != (Object)(object)prefab) { stringBuilder.Append("particles ").Append(((Object)val).name).Append("; "); Object.DestroyImmediate((Object)(object)((Component)val).gameObject); } } HashSet<Type> hashSet = new HashSet<Type> { typeof(ZNetView), typeof(Piece), typeof(WearNTear) }; Component[] componentsInChildren2 = prefab.GetComponentsInChildren<Component>(true); foreach (Component val2 in componentsInChildren2) { if ((Object)(object)val2 == (Object)null) { continue; } if ((!(val2 is MonoBehaviour) || hashSet.Contains(((object)val2).GetType())) && !(val2 is Light) && !(val2 is AudioSource)) { Collider val3 = (Collider)(object)((val2 is Collider) ? val2 : null); if (val3 == null || !val3.isTrigger) { continue; } } stringBuilder.Append(((object)val2).GetType().Name).Append(" on ").Append(((Object)val2).name) .Append("; "); Object.DestroyImmediate((Object)(object)val2); } Log.Debug($"Phantom Spawner: removed from the ward clone: {stringBuilder}"); if ((Object)(object)prefab.GetComponentInChildren<PrivateArea>(true) != (Object)null || (Object)(object)prefab.GetComponentInChildren<EffectArea>(true) != (Object)null) { throw new InvalidOperationException("the ward clone still has a PrivateArea or EffectArea"); } } private static void AddPlinth(GameObject prefab) { //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_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: 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_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) Material columnMaterial = ObserverPiece.ColumnMaterial; if ((Object)(object)columnMaterial == (Object)null) { Log.Warning("Phantom Spawner: the observers' stone material isn't loaded, so it has no plinth."); return; } foreach (Transform item in prefab.transform) { item.localPosition += Vector3.up * 0.11f; } BoxCollider[] components = prefab.GetComponents<BoxCollider>(); foreach (BoxCollider obj in components) { obj.center += Vector3.up * 0.11f; } CapsuleCollider[] components2 = prefab.GetComponents<CapsuleCollider>(); foreach (CapsuleCollider obj2 in components2) { obj2.center += Vector3.up * 0.11f; } SphereCollider[] components3 = prefab.GetComponents<SphereCollider>(); foreach (SphereCollider obj3 in components3) { obj3.center += Vector3.up * 0.11f; } GameObject val2 = new GameObject("plinth") { layer = prefab.layer }; val2.transform.SetParent(prefab.transform, false); Mesh sharedMesh = ClusterModel.DiscStack(PlinthDiscs, "QuanPhantomSpawner_plinth"); val2.AddComponent<MeshFilter>().sharedMesh = sharedMesh; ((Renderer)val2.AddComponent<MeshRenderer>()).sharedMaterial = columnMaterial; val2.AddComponent<MeshCollider>().sharedMesh = sharedMesh; } private static void Shrink(GameObject prefab) { //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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: 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_00be: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) foreach (Transform item in prefab.transform) { item.localPosition *= 0.25f; item.localScale *= 0.25f; } BoxCollider[] components = prefab.GetComponents<BoxCollider>(); foreach (BoxCollider obj in components) { obj.center *= 0.25f; obj.size *= 0.25f; } CapsuleCollider[] components2 = prefab.GetComponents<CapsuleCollider>(); foreach (CapsuleCollider obj2 in components2) { obj2.center *= 0.25f; obj2.radius *= 0.25f; obj2.height *= 0.25f; } SphereCollider[] components3 = prefab.GetComponents<SphereCollider>(); foreach (SphereCollider obj3 in components3) { obj3.center *= 0.25f; obj3.radius *= 0.25f; } } private static void Redden(GameObject prefab) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) Dictionary<Texture, Texture> dictionary = new Dictionary<Texture, Texture>(); Dictionary<Material, Material> dictionary2 = new Dictionary<Material, Material>(); Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true); foreach (Renderer val in componentsInChildren) { Material[] sharedMaterials = val.sharedMaterials; for (int j = 0; j < sharedMaterials.Length; j++) { Material val2 = sharedMaterials[j]; if ((Object)(object)val2 == (Object)null) { continue; } if (!dictionary2.TryGetValue(val2, out var value)) { value = new Material(val2) { name = ((Object)val2).name + "_phantom" }; Texture val3 = (value.HasProperty(MainTex) ? value.GetTexture(MainTex) : null); if ((Object)(object)val3 != (Object)null) { if (!dictionary.TryGetValue(val3, out var value2)) { value2 = (dictionary[val3] = StoneCopy(val3)); } if ((Object)(object)value2 != (Object)null) { value.SetTexture(MainTex, value2); } } Texture val5 = (value.HasProperty(EmissionMap) ? value.GetTexture(EmissionMap) : null); if ((Object)(object)val5 != (Object)null && value.HasProperty(EmissionColor)) { value.EnableKeyword("_EMISSION"); value.SetColor(EmissionColor, Glow * 2f); } else { value.DisableKeyword("_EMISSION"); } string[] obj = new string[6] { "Phantom Spawner: material '", ((Object)val2).name, "' (", null, null, null }; Shader shader = val2.shader; obj[3] = ((shader != null) ? ((Object)shader).name : null); obj[4] = ") in stone"; obj[5] = (((Object)(object)val5 != (Object)null) ? (", red glow on its emission map '" + ((Object)val5).name + "'.") : ", no emission map."); Log.Debug(string.Concat(obj)); dictionary2[val2] = value; } sharedMaterials[j] = value; } val.sharedMaterials = sharedMaterials; } } private static Texture StoneCopy(Texture source) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) if ((int)SystemInfo.graphicsDeviceType == 4) { return null; } RenderTexture active = RenderTexture.active; RenderTexture val = null; try { val = RenderTexture.GetTemporary(source.width, source.height, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)2); Graphics.Blit(source, val); RenderTexture.active = val; Texture2D val2 = new Texture2D(source.width, source.height, (TextureFormat)4, true) { name = ((Object)source).name + "_stone", wrapMode = source.wrapMode, filterMode = source.filterMode, anisoLevel = source.anisoLevel }; val2.ReadPixels(new Rect(0f, 0f, (float)source.width, (float)source.height), 0, 0); Color32[] pixels = val2.GetPixels32(); for (int i = 0; i < pixels.Length; i++) { Color32 val3 = pixels[i]; float num = (0.3f * (float)(int)val3.r + 0.59f * (float)(int)val3.g + 0.11f * (float)(int)val3.b) / 255f; float num2 = Mathf.Clamp01(0.08f + num * 0.85f); pixels[i] = new Color32((byte)(num2 * StoneTint.r * 255f), (byte)(num2 * StoneTint.g * 255f), (byte)(num2 * StoneTint.b * 255f), val3.a); } val2.SetPixels32(pixels); val2.Apply(true, true); return (Texture)(object)val2; } catch (Exception ex) { Log.Warning("Phantom Spawner: couldn't make a stone copy of '" + ((Object)source).name + "', so it stays wood: " + ex.Message); return null; } finally { RenderTexture.active = active; if ((Object)(object)val != (Object)null) { RenderTexture.ReleaseTemporary(val); } } } } internal sealed class PhantomSpawner : MonoBehaviour, Hoverable { private ZNetView _nview; private Piece _piece; private static readonly List<PhantomSpawner> Loaded = new List<PhantomSpawner>(); internal static bool AnyActiveWithin(Vector3 point, float range) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) foreach (PhantomSpawner item in Loaded) { if ((Object)(object)item != (Object)null && (Object)(object)item._nview != (Object)null && item._nview.IsValid() && item._nview.GetZDO().GetBool("QCL_active", false) && Vector3.Distance(((Component)item).transform.position, point) < range) { return true; } } return false; } private void OnDestroy() { Loaded.Remove(this); } private void Awake() { _nview = ((Component)this).GetComponent<ZNetView>(); _piece = ((Component)this).GetComponent<Piece>(); if (!((Object)(object)_nview == (Object)null) && _nview.GetZDO() != null) { _nview.Register<string, bool>("QCL_SetBool", (Action<long, string, bool>)RPC_SetBool); Loaded.Add(this); } } private void Start() { //IL_009d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_nview == (Object)null) && _nview.IsValid() && _nview.IsOwner() && !((Object)(object)Player.m_localPlayer == (Object)null)) { ZDO zDO = _nview.GetZDO(); if (zDO.GetString("QCL_builder", "") == "" && _piece.GetCreator() == Player.m_localPlayer.GetPlayerID()) { zDO.Set("QCL_builder", Player.m_localPlayer.GetPlayerName()); ZRoutedRpc.instance.InvokeRoutedRPC("QCL_Placed", new object[1] { zDO.m_uid }); } } } private void RPC_SetBool(long sender, string key, bool value) { if (_nview.IsOwner() && (!(key != "QCL_active") || !(key != "QCL_blocked") || !(key != "QCL_admin"))) { ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (ZNet.instance.IsServer() || (serverPeer != null && sender == serverPeer.m_uid)) { _nview.GetZDO().Set(key, value); } } } public string GetHoverName() { if (!((Object)(object)_piece != (Object)null)) { return ""; } return Localization.instance.Localize(_piece.m_name); } public float GetHoverOffset() { return 0f; } public string GetHoverText() { if ((Object)(object)_nview == (Object)null || !_nview.IsValid()) { return ""; } ZDO zDO = _nview.GetZDO(); string text = (zDO.GetBool("QCL_blocked", false) ? "<color=#ff8080>Dormant</color>: built while only admins could build these" : ((!zDO.GetBool("QCL_active", false)) ? "<color=#a0a0a0>Dormant</color>: no active chunk observer" : "<color=#ff9070>Active</color>: nearby spawners act as if a player were here")); string text2 = zDO.GetString("QCL_builder", ""); return GetHoverName() + "\n" + text + (string.IsNullOrEmpty(text2) ? "" : ("\nBuilt by " + text2)); } } internal sealed class ZoneGrid : MonoBehaviour { internal const float ShowSeconds = 120f; private const int NearbyReach = 4; private const string RequestRpc = "QCL_ZonesNear"; private const string ReplyRpc = "QCL_ZonesNearReply"; private const float ZoneSize = 64f; private const float WaterLevel = 30f; private const float SampleStep = 2f; private const float LineWidth = 0.12f; private const float PostWidth = 0.15f; private const float PostHeight = 20f; private static readonly Color White = new Color(1f, 1f, 1f, 0.85f); private static readonly Color Yellow = new Color(1f, 0.85f, 0.1f, 0.85f); private static ZoneGrid _instance; private readonly List<LineRenderer> _pool = new List<LineRenderer>(); private Transform _root; private Material _material; private float _hideAt; private Vector2s _centre; private int _radius; internal static void Hook(GameObject host) { _instance = host.AddComponent<ZoneGrid>(); } internal static void Show(Vector2s centre, int radius) { //IL_0013: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_instance == (Object)null)) { _instance._centre = centre; _instance._radius = radius; _instance.Draw(null); _instance._hideAt = Time.time + 120f; if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC("QCL_ZonesNear", new object[2] { (int)centre.x, (int)centre.y }); } } } internal static void RegisterRpcs() { ZRoutedRpc.instance.Register<int, int>("QCL_ZonesNear", (Action<long, int, int>)RPC_Request); ZRoutedRpc.instance.Register<ZPackage>("QCL_ZonesNearReply", (Action<long, ZPackage>)RPC_Reply); } private static void RPC_Request(long sender, int x, int z) { //IL_0022: 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_0028: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) if (!ZNet.instance.IsServer()) { return; } List<Vector2s> list = new List<Vector2s>(); foreach (Vector2s item in Observers.Forced) { if (Mathf.Abs(item.x - x) <= 4 && Mathf.Abs(item.y - z) <= 4) { list.Add(item); } } ZPackage val = new ZPackage(); val.Write(x); val.Write(z); val.Write(list.Count); foreach (Vector2s item2 in list) { val.Write((int)item2.x); val.Write((int)item2.y); } ZRoutedRpc.instance.InvokeRoutedRPC(sender, "QCL_ZonesNearReply", new object[1] { val }); } private static void RPC_Reply(long sender, ZPackage package) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) if (!ZNet.instance.IsServer()) { ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer == null || sender != serverPeer.m_uid) { return; } } int num = package.ReadInt(); int num2 = package.ReadInt(); int num3 = package.ReadInt(); HashSet<Vector2s> hashSet = new HashSet<Vector2s>(); for (int i = 0; i < num3; i++) { hashSet.Add(new Vector2s((short)package.ReadInt(), (short)package.ReadInt())); } ZoneGrid instance = _instance; if (!((Object)(object)instance == (Object)null) && ((Component)instance._root).gameObject.activeSelf && instance._centre.x == num && instance._centre.y == num2) { instance.Draw(hashSet); } } private void Awake() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) _root = new GameObject("QuanChunkLoader_ZoneGrid").transform; Object.DontDestroyOnLoad((Object)(object)((Component)_root).gameObject); ((Component)_root).gameObject.SetActive(false); } private void OnDestroy() { if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)((Component)_root).gameObject); } } private void Update() { if (((Component)_root).gameObject.activeSelf && (Time.time >= _hideAt || (Object)(object)Player.m_localPlayer == (Object)null)) { ((Component)_root).gameObject.SetActive(false); } } private static float CornerCoord(int i) { return (float)i * 64f - 32f; } private void Draw(HashSet<Vector2s> loadedNearby) { //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) int used = 0; int iMin = _centre.x - _radius; int iMax = _centre.x + _radius + 1; int jMin = _centre.y - _radius; int jMax = _centre.y + _radius + 1; for (int i = iMin; i <= iMax; i++) { GroundLine(CornerCoord(i), CornerCoord(jMin), CornerCoord(i), CornerCoord(jMax), White, ref used); } for (int j = jMin; j <= jMax; j++) { GroundLine(CornerCoord(iMin), CornerCoord(j), CornerCoord(iMax), CornerCoord(j), White, ref used); } for (int k = iMin; k <= iMax; k++) { for (int l = jMin; l <= jMax; l++) { Post(CornerCoord(k), CornerCoord(l), White, ref used); } } HashSet<long> edges; HashSet<long> posts; if (loadedNearby != null) { edges = new HashSet<long>(); posts = new HashSet<long>(); foreach (Vector2s item in loadedNearby) { int x = item.x; int y = item.y; if (x < iMin || x >= iMax || y < jMin || y >= jMax) { TryEdge(x, y, x + 1, y); TryEdge(x, y + 1, x + 1, y + 1); TryEdge(x, y, x, y + 1); TryEdge(x + 1, y, x + 1, y + 1); TryPost(x, y); TryPost(x + 1, y); TryPost(x, y + 1); TryPost(x + 1, y + 1); } } } for (int m = used; m < _pool.Count; m++) { ((Component)_pool[m]).gameObject.SetActive(false); } ((Component)_root).gameObject.SetActive(true); bool InBlock(int num, int num2) { if (num >= iMin && num <= iMax && num2 >= jMin) { return num2 <= jMax; } return false; } void TryEdge(int ai, int aj, int bi, int bj) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (!InBlock(ai, aj) || !InBlock(bi, bj)) { long num = ((ai == bi) ? 1 : 0); if (edges.Add(Key(ai, aj) * 2 + num)) { GroundLine(CornerCoord(ai), CornerCoord(aj), CornerCoord(bi), CornerCoord(bj), Yellow, ref used); } } } void TryPost(int ci, int cj) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (!InBlock(ci, cj) && posts.Add(Key(ci, cj))) { Post(CornerCoord(ci), CornerCoord(cj), Yellow, ref used); } } } private static long Key(int i, int j) { return ((long)i << 32) ^ (uint)j; } private static float Ground(Vector3 at) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) float num = default(float); return Mathf.Max(Heightmap.GetHeight(at, ref num) ? num : 30f, 30f); } private void GroundLine(float ax, float az, float bx, float bz, Color colour, ref int used) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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) Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(ax, 0f, az); Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(bx, 0f, bz); int num = Mathf.Max(2, Mathf.CeilToInt(Vector3.Distance(val, val2) / 2f) + 1); LineRenderer val3 = Next(ref used, 0.12f, colour); val3.positionCount = num; for (int i = 0; i < num; i++) { Vector3 val4 = Vector3.Lerp(val, val2, (float)i / (float)(num - 1)); val4.y = Ground(val4) + 0.3f; val3.SetPosition(i, val4); } } private void Post(float x, float z, Color colour, ref int used) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) float num = Ground(new Vector3(x, 0f, z)); LineRenderer obj = Next(ref used, 0.15f, colour); obj.positionCount = 2; obj.SetPosition(0, new Vector3(x, num, z)); obj.SetPosition(1, new Vector3(x, num + 20f, z)); } private LineRenderer Next(ref int used, float width, Color colour) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0092: 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_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) if (used == _pool.Count) { GameObject val = new GameObject("line"); val.transform.SetParent(_root, false); LineRenderer val2 = val.AddComponent<LineRenderer>(); val2.useWorldSpace = true; ((Renderer)val2).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)val2).receiveShadows = false; ((Renderer)val2).material = Material(); _pool.Add(val2); } LineRenderer obj = _pool[used++]; ((Component)obj).gameObject.SetActive(true); float startWidth = (obj.endWidth = width); obj.startWidth = startWidth; Color startColor = (obj.endColor = colour); obj.startColor = startColor; return obj; } private Material Material() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown if ((Object)(object)_material != (Object)null) { return _material; } Shader val = Shader.Find("Sprites/Default") ?? Shader.Find("Hidden/Internal-Colored") ?? Shader.Find("UI/Default"); _material = new Material(val); return _material; } } [BepInPlugin("quandru.QuanChunkLoader", "QuanChunkLoader", "1.1.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInIncompatibility("com.Frogger.ChunkLoader")] [BepInIncompatibility("com.zeecamelsnake.ChunkLoaderFix")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class QuanChunkLoaderPlugin : BaseUnityPlugin { public const string PluginGuid = "quandru.QuanChunkLoader"; public const string PluginName = "QuanChunkLoader"; public const string PluginVersion = "1.1.3"; private const string FroggerGuid = "com.Frogger.ChunkLoader"; private const string ZeeCamelSnakeGuid = "com.zeecamelsnake.ChunkLoaderFix"; private const string SteadyFrameGuid = "ru.ivest.steadyframe"; internal const string HammerCategory = "QuanPieces"; internal static QuanChunkLoaderPlugin Instance { get; private set; } private void Awake() { Instance = this; Log.Source = ((BaseUnityPlugin)this).Logger; Settings.Bind(((BaseUnityPlugin)this).Config); string[] array = new string[2] { "com.Frogger.ChunkLoader", "com.zeecamelsnake.ChunkLoaderFix" }; foreach (string text in array) { if (Chainloader.PluginInfos.ContainsKey(text)) { Log.Error("An old chunk loader (" + text + ") is installed. Quan's Chunk Loader stays inactive: remove the old one from the server and every client (see the README, \"Migrating\")."); return; } } if (!ApplyPatches()) { Log.Error("Not every patch applied (see above), so chunk loading is off. Check for a game update."); return; } PrefabManager.OnVanillaPrefabsAvailable += AddPieces; Settings.ExpireAfterHours.SettingChanged += delegate { Observers.ExpiryChanged(); }; BuildPermission.Hook(); ObserverMap.Hook(); ZoneGrid.Hook(((Component)this).gameObject); ((Component)this).gameObject.AddComponent<ServerLoader>(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"QuanChunkLoader 1.1.3 loaded."); } private static bool ApplyPatches() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown bool result = true; Harmony val = new Harmony("quandru.QuanChunkLoader"); Type[] types = typeof(QuanChunkLoaderPlugin).Assembly.GetTypes(); foreach (Type type in types) { if (type.IsDefined(typeof(HarmonyPatch), inherit: false)) { try { val.CreateClassProcessor(type).Patch(); } catch (Exception arg) { Log.Error($"Couldn't apply {type.Name}: {arg}"); result = false; } } } return result; } private void AddPieces() { PrefabManager.OnVanillaPrefabsAvailable -= AddPieces; try { ObserverPiece.Register(); } catch (Exception arg) { Log.Error($"Couldn't add the Chunk Observers: {arg}"); } try { PhantomPiece.Register(); } catch (Exception arg2) { Log.Error($"Couldn't add the Phantom Spawner: {arg2}"); } } private void Update() { ObserverMap.ClientUpdate(); } } internal enum LogDetail { Info, Debug, Trace } internal static class Settings { internal static ConfigEntry<bool> AdminOnlyBuilding; internal static ConfigEntry<bool> BuilderOrAdminSwitches; internal static ConfigEntry<int> ZonesPerPlayer; internal static ConfigEntry<int> ZonesInTotal; internal static ConfigEntry<bool> AdminsIgnoreLimits; internal static ConfigEntry<int> ExpireAfterHours; internal static ConfigEntry<bool> SimulateTames; internal static ConfigEntry<int> EmptyServerMinutes; internal static ConfigEntry<string> PhantomCreatureSpawners; internal static ConfigEntry<bool> PhantomSpawnAreas; internal static ConfigEntry<bool> DespawnProtection; internal static ConfigEntry<bool> ShowOnMap; internal static ConfigEntry<LogDetail> Detail; internal static ConfigEntry<bool> DailyFile; internal static ConfigEntry<int> DaysToKeep; private static ConfigDescription Enforced(string text, AcceptableValueBase values = null) { //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_0021: Expected O, but got Unknown //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown return new ConfigDescription(text + " Synced from the server; admins only.", values, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } }); } internal static void Bind(ConfigFile config) { //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Expected O, but got Unknown AdminOnlyBuilding = config.Bind<bool>("0 - General", "Only admins can build observers and phantoms", false, Enforced("On: the Chunk Observers and the Phantom Spawner are hidden from other players' hammers, and any a non-admin still manages to build stay switched off. Pieces already built are untouched. Useful for trying the mod out on a live server.")); BuilderOrAdminSwitches = config.Bind<bool>("0 - General", "Only the builder or an admin can switch an observer", true, Enforced("On: only the player who built an observer, or an admin, can switch it on or off. Off: anyone with access through the wards around it can.")); ZonesPerPlayer = config.Bind<int>("1 - Limits", "Zones per player", 18, Enforced("How many zones one player's switched-on observers may load. A Chunk Observer loads 1 zone, a Chunk Observer Cluster 9. Checked when an observer is switched on; observers already on stay on if you lower it.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000))); ZonesInTotal = config.Bind<int>("1 - Limits", "Zones in total", 108, Enforced("How many zones all switched-on observers on the server may load together.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10000))); AdminsIgnoreLimits = config.Bind<bool>("1 - Limits", "Admins ignore limits", true, Enforced("On: observers an admin built don't count towards either limit, and admins can switch any observer on regardless of the limits.")); ExpireAfterHours = config.Bind<int>("1 - Limits", "Expire after hours without a refresh", 0, Enforced("Switches an observer off after this many real hours since it was last switched on. It's never deleted; switching it on again restarts the count. 0 means never. Turning this on later starts every observer's count from that moment.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 8760))); SimulateTames = config.Bind<bool>("2 - Tamed Creatures", "Simulate tames in loaded zones", false, Enforced("Off: tamed creatures in a loaded base pause while nobody is near, as they would without a chunk loader, and load normally when a player arrives. On: the server runs them, so pens keep breeding with nobody there. Risks: unattended pens can grow past their limits, and tame mods' feeding and breeding code runs on the server. See the README.")); EmptyServerMinutes = config.Bind<int>("3 - Empty server", "Empty-server window in minutes", 60, Enforced("After the last player logs off, the world clock keeps running for up to this many real minutes while at least one observer is on, so loaded bases finish their work. 0 turns it off; at most 240 (4 hours).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 240))); PhantomCreatureSpawners = config.Bind<string>("4 - Phantom spawner", "Creature spawners", "Spawner_imp_respawn", Enforced("Comma-separated prefab names of the creature spawners a Phantom Spawner keeps running. Spawner_imp_respawn is the surtling spawner. Every spawner name in your game, including other mods', is listed in quandru.QuanChunkLoader.spawners.txt next to this file (written when the game starts). Anything added here runs its monsters on the server while nobody is near, and they can attack the base.")); PhantomSpawnAreas = config.Bind<bool>("4 - Phantom spawner", "Spawner areas", false, Enforced("Also keep spawner areas running, anywhere in a loaded zone: the kind of spawner that keeps a group of creatures topped up around itself, such as greydwarf nests and bone piles, including other mods' ones. They can't be chosen one by one; this turns them all on or off. Their monsters run on the server while nobody is near and can attack the base.")); DespawnProtection = config.Bind<bool>("5 - Despawn protection", "Despawn protection", true, Enforced("In loaded zones, items on the ground near a production station or an active Phantom Spawner don't vanish after an hour, so offline output waits for you.")); ShowOnMap = config.Bind<bool>("6 - Map (personal)", "Show observers on the map", false, new ConfigDescription("Admins only: shows every chunk observer in the world on your map, lit when it's on. Personal.", (AcceptableValueBase)null, Array.Empty<object>())); Detail = config.Bind<LogDetail>("7 - Logging", "Log detail", LogDetail.Info, Enforced("How much the server logs. Info: switches, loaded-zone changes, a summary every 5 minutes and warnings; a handful of lines a minute at most. Debug: adds the diagnostic checks: a tame watch (each tame that moves 10 m or more, jumps, or changes who runs it), owner changes grouped, players coming and going, ships' buoyancy, plants overdue to regrow, station capacities at start and a heartbeat with each summary. Trace: adds a line per object loaded or taken, for chasing a bug; thousands of lines when players come and go, so don't leave it on.")); DailyFile = config.Bind<bool>("7 - Logging", "Write a daily log file", false, Enforced("Also write this mod's log lines to BepInEx/QuanChunkLoaderLogs, one file per day.")); DaysToKeep = config.Bind<int>("7 - Logging", "Days of daily files to keep", 14, Enforced("Older daily files are deleted.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 365))); } } internal static class PluginInfo { public const string Version = "1.1.3"; } } namespace QuanChunkLoader.Patches { [HarmonyPatch(typeof(WearNTear), "RPC_Damage")] internal static class ObserverTakesNoDamage { private static bool Prefix(WearNTear __instance) { if ((Object)(object)((Component)__instance).GetComponent<ChunkObserver>() == (Object)null) { return (Object)(object)((Component)__instance).GetComponent<PhantomSpawner>() == (Object)null; } return false; } } [HarmonyPatch(typeof(Game), "Start")] internal static class RegisterRpcs { private static void Postfix() { Observers.RegisterRpcs(); } } internal static class PhantomContext { [ThreadStatic] internal static bool Allowed; [ThreadStatic] internal static bool Triggered; [ThreadStatic] internal static float SpawnAreaRange; private static readonly char[] Comma = new char[1] { ',' }; private static string _listText; private static HashSet<string> _list = new HashSet<string>(); internal static bool Enter(bool kindAllowed) { Allowed = kindAllowed && ServerLoader.Running && Phantoms.AnyActive; Triggered = false; return Allowed; } internal static void Exit() { Allowed = false; Triggered = false; SpawnAreaRange = 0f; } internal static bool SpawnAreaActiveAt(Vector3 point) { //IL_0013: 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_0020: Unknown result type (might be due to invalid IL or missing references) if (Allowed && SpawnAreaRange > 0f && Observers.IsForced(ZoneSystem.GetZone(point))) { return Phantoms.AnyWithin(point, SpawnAreaRange); } return false; } internal static bool ServerSkips(Vector3 position, float range) { //IL_0013: 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_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (!ServerLoader.Running || Observers.Forced.Count == 0 || !Observers.IsForced(ZoneSystem.GetZone(position))) { return false; } if (Allowed && Phantoms.AnyWithin(position, range)) { return false; } if (!ZNet.instance.IsDedicated()) { return !ZNetScene.InActiveArea(position, ZNet.instance.GetReferencePosition()); } return true; } internal static bool CreatureSpawnerAllowed(CreatureSpawner spawner) { string text = Settings.PhantomCreatureSpawners.Value ?? ""; if (text != _listText) { _listText = text; _list = new HashSet<string>(); string[] array = text.Split(Comma, StringSplitOptions.RemoveEmptyEntries); foreach (string text2 in array) { _list.Add(text2.Trim()); } } return _list.Contains(Utils.GetPrefabName(((Component)spawner).gameObject)); } } [HarmonyPatch(typeof(CreatureSpawner), "UpdateSpawner")] internal static class PhantomCreatureSpawnerContext { private static bool Prefix(CreatureSpawner __instance) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) PhantomContext.Enter(PhantomContext.CreatureSpawnerAllowed(__instance)); return !PhantomContext.ServerSkips(((Component)__instance).transform.position, __instance.m_triggerDistance); } private static Exception Finalizer(Exception __exception) { PhantomContext.Exit(); return __exception; } } [HarmonyPatch(typeof(SpawnArea), "UpdateSpawn")] internal static class PhantomSpawnAreaContext { private static bool Prefix(SpawnArea __instance) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) if (PhantomContext.Enter(Settings.PhantomSpawnAreas.Value)) { PhantomContext.SpawnAreaRange = __instance.m_triggerDistance; } return !PhantomContext.ServerSkips(((Component)__instance).transform.position, __instance.m_triggerDistance); } private static Exception Finalizer(Exception __exception) { PhantomContext.Exit(); return __exception; } } [HarmonyPatch] internal static class PhantomCountsAsPlayer { private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(Player), "IsPlayerInRange", new Type[2] { typeof(Vector3), typeof(float) }, (Type[])null); yield return AccessTools.Method(typeof(Player), "IsPlayerInRange", new Type[3] { typeof(Vector3), typeof(float), typeof(float) }, (Type[])null); } private static void Postfix(Vector3 point, float range, ref bool __result) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (!__result && PhantomContext.Allowed && Phantoms.AnyWithin(point, range)) { __result = true; PhantomContext.Triggered = true; } } } [HarmonyPatch(typeof(Character), "Awake")] internal static class MarkPhantomSpawned { private static void Postfix(Character __instance) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (PhantomContext.Triggered) { ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); ZDO val = ((component != null) ? component.GetZDO() : null); if (val != null) { val.Set("QCL_phantom", true); Log.Debug($"Phantom-run spawner made {Utils.GetPrefabName(((Component)__instance).gameObject)} {val.m_uid}."); } } } } [HarmonyPatch(typeof(ZNetScene), "RemoveObjects")] internal static class KeepForcedInstances { private static void Prefix(ZNetScene __instance) { if (!ServerLoader.Running || Observers.Forced.Count == 0) { return; } byte tempRemoveEarmark = (byte)(Time.frameCount & 0xFF); foreach (ZDO key in GameAccess.Instances.Invoke(__instance).Keys) { if (Keeps(key)) { key.TempRemoveEarmark = tempRemoveEarmark; } } } internal static bool Keeps(ZDO zdo) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (ServerLoader.Running && Observers.IsForced(zdo.GetSector())) { if (!ObjectKinds.ServerLoads(zdo)) { if (!zdo.Persistent) { return zdo.IsOwner(); } return false; } return true; } return false; } } [HarmonyPatch] internal static class SteadyFrameZoneUnload { private static List<ZDO> _removed; private static bool _announced; private static bool Prepare() { Type type = AccessTools.TypeByName("SteadyFrame.SceneStreaming.ZoneUnload"); if (type == null) { return false; } _removed = AccessTools.Field(type, "Removed")?.GetValue(null) as List<ZDO>; MethodInfo methodInfo = AccessTools.Method(type, "Collect", (Type[])null, (Type[])null); if (_removed == null || methodInfo == null) { Log.Warning("SteadyFrame is installed but its zone unloading has changed shape; if its \"Unload by zone\" is on, turn it off on the server or loaded zones won't stay loaded."); return false; } if (!_announced) { _announced = true; Log.Info("SteadyFrame found: its \"Unload by zone\" is kept away from loaded zones."); } return true; } private static MethodBase TargetMethod() { return AccessTools.Method(AccessTools.TypeByName("SteadyFrame.SceneStreaming.ZoneUnload"), "Collect", (Type[])null, (Type[])null); } private static void Postfix() { if (_removed != null && _removed.Count > 0 && ServerLoader.Running && Observers.Forced.Count > 0) { _removed.RemoveAll(KeepForcedInstances.Keeps); } } } [HarmonyPatch(typeof(ZNetScene), "CreateObjectsSorted")] internal static class UnknownPrefabGuardNear { private static void Prefix(List<ZDO> currentNearObjects) { UnknownPrefabGuard.Strip(currentNearObjects); } } [HarmonyPatch(typeof(ZNetScene), "CreateDistantObjects")] internal static class UnknownPrefabGuardDistant { private static void Prefix(List<ZDO> objects) { UnknownPrefabGuard.Strip(objects); } } internal static class UnknownPrefabGuard { private static readonly Predicate<ZDO> Unknown = (ZDO zdo) => !zdo.Created && Observers.IsForced(zdo.GetSector()) && ObjectKinds.Of(zdo) == Kind.UnknownPrefab; internal static void Strip(List<ZDO> list) { if (ServerLoader.Running && Observers.Forced.Count > 0) { list.RemoveAll(Unknown); } } } [HarmonyPatch] internal static class EdgeRuleContext { [ThreadStatic] internal static int Depth; private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(WearNTear), "UpdateWear", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(StaticPhysics), "SUpdate", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(SpawnArea), "UpdateSpawn", (Type[])null, (Type[])null); } private static void Prefix() { Depth++; } private static Exception Finalizer(Exception __exception) { Depth--; return __exception; } } [HarmonyPatch(typeof(ZNetScene), "PointInsideActiveArea")] internal static class EdgeRule { private static void Postfix(Vector3 point, ref bool __result) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (!((EdgeRuleContext.Depth == 0) | __result) && ServerLoader.Running && Observers.Forced.Count != 0 && (Observers.IsInterior(ZoneSystem.GetZone(point)) || PhantomContext.SpawnAreaActiveAt(point))) { __result = true; } } } [HarmonyPatch(typeof(ItemDrop), "TimedDestruction")] internal static class DespawnProtection { private const float NearStation = 8f; private const float NearPhantom = 40f; private static int _stationMask; private static readonly Collider[] Hits = (Collider[])(object)new Collider[64]; private static bool Prefix(ItemDrop __instance) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return !Protected(((Component)__instance).transform.position); } private static bool Protected(Vector3 pos) { //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_0014: 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_001c: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) if (!Settings.DespawnProtection.Value) { return false; } Vector2s zone = ZoneSystem.GetZone(pos); if (ServerLoader.Running) { if (!Observers.IsForced(zone)) { return false; } foreach (Vector3 station in ServerLoader.Stations) { if (Vector3.Distance(station, pos) < 8f) { return true; } } return Phantoms.AnyWithin(pos, 40f); } if (!ChunkObserver.AnyOnCovering(zone)) { return false; } if (!PhantomSpawner.AnyActiveWithin(pos, 40f)) { return StationNear(pos); } return true; } private static bool StationNear(Vector3 pos) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (_stationMask == 0) { _stationMask = LayerMask.GetMask(new string[4] { "piece", "piece_nonsolid", "Default", "static_solid" }); } int num = Physics.OverlapSphereNonAlloc(pos, 8f, Hits, _stationMask); for (int i = 0; i < num; i++) { Collider val = Hits[i]; if (!((Object)(object)val == (Object)null) && ((Object)(object)((Component)val).GetComponentInParent<Smelter>() != (Object)null || (Object)(object)((Component)val).GetComponentInParent<Fermenter>() != (Object)null || (Object)(object)((Component)val).GetComponentInParent<Beehive>() != (Object)null || (Object)(object)((Component)val).GetComponentInParent<CookingStation>() != (Object)null || (Object)(object)((Component)val).GetComponentInParent<SapCollector>() != (Object)null)) { return true; } } return false; } } internal static class BoatContext { [ThreadStatic] internal static bool Active; [ThreadStatic] internal static int Samples; [ThreadStatic] internal static float Centre; internal const float NoWater = -10000f; } [HarmonyPatch(typeof(Ship), "CustomFixedUpdate")] internal static class BoatContextScope { private static void Prefix(Ship __instance) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) BoatContext.Active = ServerLoader.Running && Observers.Forced.Count > 0 && Observers.IsForced(ZoneSystem.GetZone(((Component)__instance).transform.position)); BoatContext.Samples = 0; BoatContext.Centre = -10000f; } private static Exception Finalizer(Exception __exception) { BoatContext.Active = false; return __exception; } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class BoatIgnoresUnloadedWater { private static void Postfix(Vector3 p, ref float __result) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) if (BoatContext.Active) { if (BoatContext.Samples++ == 0) { BoatContext.Centre = __result; } else if (__result <= -9999f && BoatContext.Centre > -9999f && !ZoneSystem.instance.IsZoneLoaded(ZoneSystem.GetZone(p))) { __result = BoatContext.Centre; } } } } [HarmonyPatch(typeof(Pickable), "RPC_Pick")] internal static class PickOnServerOwned { private static bool Prefix(Pickable __instance, long sender, int bonus) { //IL_0060: 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) if ((Object)(object)Player.m_localPlayer != (Object)null || sender == ZDOMan.GetSessionID()) { return true; } ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner() || __instance.GetPicked()) { return true; } ZDO zDO = component.GetZDO(); zDO.SetOwner(sender); Log.Debug($"Pick on server-owned {Utils.GetPrefabName(((Component)__instance).gameObject)} {zDO.m_uid}: handed to {Observers.PlayerName(sender)}."); ((MonoBehaviour)QuanChunkLoaderPlugin.Instance).StartCoroutine(ResendPick(zDO.m_uid, sender, bonus)); return false; } private static IEnumerator ResendPick(ZDOID id, long picker, int bonus) { //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) yield return (object)new WaitForSeconds(0.5f); ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(id) : null); if (val != null && val.GetOwner() == picker) { ZRoutedRpc.instance.InvokeRoutedRPC(picker, id, "RPC_Pick", new object[1] { bonus }); } } } [HarmonyPatch(typeof(Ship), "UpdateSailSize")] internal static class SailOnServer { private static void Prefix(ref bool haveControllingPlayer) { if ((Object)(object)Player.m_localPlayer == (Object)null) { haveControllingPlayer = false; } } } } namespace QuanChunkLoader.Core { internal static class DebugWatch { private sealed class Change { internal string Category; internal string Name; internal Vector3 Position; } private sealed class ShipState { internal long Owner; internal float Height; internal bool GameSeesWater; internal bool InLoadedZone; internal bool Floats; internal bool FloatsWithFix; } private sealed class Probe { internal bool Floats; internal bool FloatsWithFix; internal string Summary; } private sealed class PlantState { internal string Name; internal Vector3 Position; internal long Owner; internal bool Overdue; internal double OverdueMinutes; } private const float PlayerInterval = 2f; private const float ShipInterval = 5f; private const float PlantInterval = 30f; private const int MaxErrorsPerCheck = 5; private const int MaxDetailLines = 15; private static readonly Dictionary<string, int> Errors = new Dictionary<string, int>(); private static bool _started; private static float _playerTimer; private static float _shipTimer; private static float _plantTimer; private static readonly FieldInfo WaterMaskField = AccessTools.Field(typeof(Floating), "s_waterVolumeMask"); private static readonly FieldInfo ItemDropsField = AccessTools.Field(typeof(ItemDrop), "s_instances"); private static readonly Dictionary<long, string> Peers = new Dictionary<long, string>(); private static readonly Dictionary<string, List<Change>> OwnerChanges = new Dictionary<string, List<Change>>(); private static readonly Dictionary<ZDOID, ShipState> Ships = new Dictionary<ZDOID, ShipState>(); private static int? _lastWaterMask; private const float ShipMoveThreshold = 0.25f; private const float BoatNoWater = -10000f; private static readonly Collider[] Overlap = (Collider[])(object)new Collider[32]; private const double OverdueGraceMinutes = 2.0; private static readonly Dictionary<ZDOID, PlantState> Picked = new Dictionary<ZDOID, PlantState>(); private static readonly Dictionary<int, float> RespawnMinutesByPrefab = new Dictionary<int, float>(); private static readonly List<ZDO> Scratch = new List<ZDO>(); private static int _regrew; private static int _regrewOverdue; private static readonly Dictionary<string, int> RegrewBy = new Dictionary<string, int>(); private static string WorldTime => $"world time {ZNet.instance.GetTimeSeconds():0} s, {ZNet.instance.GetNrOfPlayers()} player(s)"; private static int? WaterMask => WaterMaskField?.GetValue(null) as int?; private static int ExpectedWaterMask => LayerMask.GetMask(new string[1] { "WaterVolume" }); internal static void Reset() { Errors.Clear(); _started = false; _playerTimer = (_shipTimer = 0f); _plantTimer = 30f; Peers.Clear(); Ships.Clear(); Picked.Clear(); OwnerChanges.Clear(); _lastWaterMask = null; _regrew = (_regrewOverdue = 0); RegrewBy.Clear(); } internal static void Update(float dt) { if (!Log.DebugOn) { if (_started) { Reset(); } return; } if (!_started) { _started = true; Guard("startup report", StartupReport); } if ((_playerTimer += dt) >= 2f) { _playerTimer = 0f; Guard("players", PollPlayers); } if ((_shipTimer += dt) >= 5f) { _shipTimer = 0f; Guard("ships and water", PollShips); } if ((_plantTimer += dt) >= 30f) { _plantTimer = 0f; Guard("plants", PollPlants); } } private static void Guard(string check, Action action) { Errors.TryGetValue(check, out var value); if (value >= 5) { return; } try { action(); } catch (Exception ex) { value = (Errors[check] = value + 1); string text = ((value >= 5) ? " Switched off until the next restart." : ""); Log.Warning($"Debug check \"{check}\" failed ({value}/{5}).{text} {ex}"); } } private static string Where(Vector3 p) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_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_004a: 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) return $"({p.x:0}, {p.y:0.0}, {p.z:0}) zone {ZoneSystem.GetZone(p).x},{ZoneSystem.GetZone(p).y}"; } private static bool InLoadedZone(Vector3 p) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) return Observers.IsForced(ZoneSystem.GetZone(p)); } private static void StartupReport() { Log.Debug($"Debug checks on: Valheim {Version.GetVersionString(false)}, {Chainloader.PluginInfos.Count} BepInEx plugins."); foreach (PluginInfo item in Chainloader.PluginInfos.Values.OrderBy((PluginInfo p) => p.Metadata.Name)) { Log.Debug($" Plugin: {item.Metadata.Name} {item.Metadata.Version} ({item.Metadata.GUID})"); } Log.Debug(string.Format("Water lookup mask at start: {0} (set up correctly it's {1}, ", WaterMask?.ToString() ?? "unreadable", ExpectedWaterMask) + "or 0 until the game's first lookup)."); foreach (GameObject item2 in ZNetScene.instance.m_prefabs.Where((GameObject p) => (Object)(object)p != (Object)null)) { Smelter component = item2.GetComponent<Smelter>(); if (!((Object)(object)component == (Object)null)) { string arg = string.Join(", ", from c in component.m_conversion where (Object)(object)c?.m_from != (Object)null && (Object)(object)c.m_to != (Object)null select ((Object)c.m_from).name + "->" + ((Object)c.m_to).name); string text = ((component.m_maxFuel > 0) ? string.Format("max fuel {0} ({1} x{2} per product)", component.m_maxFuel, ((Object)(object)component.m_fuelItem != (Object)null) ? ((Object)component.m_fuelItem).name : "?", component.m_fuelPerProduct) : "no fuel"); Log.Debug($" Station {((Object)item2).name}: max input {component.m_maxOre}, {text}, {component.m_secPerProduct} s per product, " + $"stacks output {component.m_spawnStack}, needs roof {component.m_requiresRoof}; [{arg}]"); } } Log.Debug("Spawners: see the reference list next to the config (quandru.QuanChunkLoader.spawners.txt)."); } private static void PollPlayers() { //IL_009d: Unkn