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 DeepNorthSnowfall v1.0.8
BepInEx/plugins/DeepNorthSnowfall.dll
Decompiled 3 weeks agousing System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace DeepNorthSnowRestore; [BepInPlugin("com.teamgrimgoren.valheim.deepnorthsnowfall", "Deep North Snowfall", "1.0.8")] public sealed class DeepNorthSnowRestorePlugin : BaseUnityPlugin { [HarmonyPatch(typeof(EnvMan), "UpdateTimeSkip")] private static class EnvManUpdateTimeSkipPatch { private static void Prefix(EnvMan __instance, out bool __state) { __state = (Object)(object)__instance != (Object)null && __instance.IsTimeSkipping(); } private static void Postfix(EnvMan __instance, bool __state) { if (__state && !((Object)(object)__instance == (Object)null) && !__instance.IsTimeSkipping()) { ZNet instance = ZNet.instance; if (!((Object)(object)instance == (Object)null) && instance.IsServer()) { RestoreAfterCompletedTimeSkip(); } } } } [HarmonyPatch(typeof(TerrainComp), "Awake")] private static class TerrainCompAwakePatch { private static void Postfix(TerrainComp __instance) { SnowRestorer.RestoreLoadedTerrainCompIfPending(__instance, "pending sleep restore"); } } public const string ModGuid = "com.teamgrimgoren.valheim.deepnorthsnowfall"; public const string ModName = "Deep North Snowfall"; public const string ModVersion = "1.0.8"; private const string RpcRestoreEpoch = "com.teamgrimgoren.valheim.deepnorthsnowfall.RestoreEpoch"; private const string RpcRequestState = "com.teamgrimgoren.valheim.deepnorthsnowfall.RequestState"; private static ManualLogSource _log; private static ConfigEntry<bool> _restoreAfterSleep; private static ConfigEntry<float> _postEventScanSeconds; private static ConfigEntry<float> _postEventScanIntervalSeconds; private static ConfigEntry<float> _minimumSnowDelta; private static ConfigEntry<bool> _claimTerrainOwnership; private static ConfigEntry<bool> _verboseZoneLogs; private static Harmony _harmony; private static int _scheduledScanEpoch; private static float _scheduledScanUntil; private static float _nextScheduledScan; private static bool _rpcRegistered; private static bool _requestedServerState; private static readonly MethodInfo ZRoutedRpcGetServerPeerIDMethod = typeof(ZRoutedRpc).GetMethod("GetServerPeerID", BindingFlags.Instance | BindingFlags.NonPublic); private void Awake() { //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Expected O, but got Unknown _log = ((BaseUnityPlugin)this).Logger; _restoreAfterSleep = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RestoreAfterSleep", true, "Restore Deep North ground snow once after a successful sleep/night skip."); _postEventScanSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "PostEventScanSeconds", 30f, "After sleep or snowrestore, keep checking loaded terrain for this many seconds to catch zones that become ready shortly after the event."); _postEventScanIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "PostEventScanIntervalSeconds", 2f, "Seconds between post-event loaded terrain checks."); _minimumSnowDelta = ((BaseUnityPlugin)this).Config.Bind<float>("Safety", "MinimumSnowDelta", 0.001f, "Minimum missing snow value required before a paint sample is changed."); _claimTerrainOwnership = ((BaseUnityPlugin)this).Config.Bind<bool>("Safety", "ClaimTerrainOwnership", true, "On the server, claim ownership of loaded TerrainComp ZNetViews before saving if needed."); _verboseZoneLogs = ((BaseUnityPlugin)this).Config.Bind<bool>("Logging", "VerboseZoneLogs", true, "Log each touched zone and changed snow sample count."); SnowRestoreState.Load(_log); SnowRestorer.Initialize(_log, _minimumSnowDelta, _claimTerrainOwnership, _verboseZoneLogs); RegisterConsoleCommand(); _harmony = new Harmony("com.teamgrimgoren.valheim.deepnorthsnowfall"); _harmony.PatchAll(); _log.LogInfo((object)"Deep North Snowfall 1.0.8 loaded. Command: snowrestore."); } private void OnDestroy() { if (_harmony != null) { _harmony.UnpatchSelf(); } } private void Update() { EnsureRpcRegistered(); RequestServerStateIfNeeded(); RunScheduledPostEventScan(); } private static void RegisterConsoleCommand() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) new ConsoleCommand("snowrestore", "Queue a Deep North ground snow refresh and process currently loaded Deep North terrain.", new ConsoleEvent(RunSnowRestoreCommand), false, true, true, false, false, false, (ConsoleOptionsFetcher)null, false, true, true); } private static void RunSnowRestoreCommand(ConsoleEventArgs args) { int num = SnowRestoreState.BeginNewEpoch(); RestoreReport restoreReport = SnowRestorer.RestoreLoadedDeepNorthTerrainComps("manual command", markCurrentEpoch: true, "Started pending Deep North restore epoch " + num + "; loaded Deep North zones were processed now, and unloaded Deep North zones will restore when loaded."); BroadcastRestoreEpoch(num, "manual command"); SchedulePostEventScans(); string text = restoreReport.ToConsoleString(); if (args != null && (Object)(object)args.Context != (Object)null) { args.Context.AddString(text); } } private static void RestoreAfterCompletedTimeSkip() { if (_restoreAfterSleep.Value) { int num = SnowRestoreState.BeginNewEpoch(); _log.LogInfo((object)("snowrestore sleep/night skip: started pending Deep North restore epoch " + num + ".")); SnowRestorer.RestoreLoadedDeepNorthTerrainComps("sleep/night skip", markCurrentEpoch: true, "Started pending Deep North restore epoch " + num + "; loaded Deep North zones were processed now, and unloaded Deep North zones will restore when loaded."); BroadcastRestoreEpoch(num, "sleep/night skip"); SchedulePostEventScans(); } } private static void EnsureRpcRegistered() { if (!_rpcRegistered && ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register<int>("com.teamgrimgoren.valheim.deepnorthsnowfall.RestoreEpoch", (Action<long, int>)RPC_RestoreEpoch); ZRoutedRpc.instance.Register("com.teamgrimgoren.valheim.deepnorthsnowfall.RequestState", (Action<long>)RPC_RequestState); _rpcRegistered = true; _log.LogInfo((object)"snowrestore: registered routed RPC synchronization."); } } private static void RequestServerStateIfNeeded() { if (_requestedServerState || !_rpcRegistered) { return; } ZNet instance = ZNet.instance; ZRoutedRpc instance2 = ZRoutedRpc.instance; if (!((Object)(object)instance == (Object)null) && instance2 != null && !instance.IsServer()) { long serverPeerID = GetServerPeerID(instance2); if (serverPeerID != 0) { instance2.InvokeRoutedRPC(serverPeerID, "com.teamgrimgoren.valheim.deepnorthsnowfall.RequestState", new object[0]); _requestedServerState = true; _log.LogInfo((object)"snowrestore: requested pending restore state from server."); } } } private static void BroadcastRestoreEpoch(int epoch, string reason) { EnsureRpcRegistered(); ZNet instance = ZNet.instance; ZRoutedRpc instance2 = ZRoutedRpc.instance; if (epoch > 0 && !((Object)(object)instance == (Object)null) && instance2 != null && instance.IsServer()) { instance2.InvokeRoutedRPC(0L, "com.teamgrimgoren.valheim.deepnorthsnowfall.RestoreEpoch", new object[1] { epoch }); _log.LogInfo((object)("snowrestore: broadcast restore epoch " + epoch + " to modded clients (" + reason + ").")); } } private static void RPC_RequestState(long sender) { ZNet instance = ZNet.instance; ZRoutedRpc instance2 = ZRoutedRpc.instance; if (!((Object)(object)instance == (Object)null) && instance2 != null && instance.IsServer() && SnowRestoreState.HasPendingEpoch()) { instance2.InvokeRoutedRPC(sender, "com.teamgrimgoren.valheim.deepnorthsnowfall.RestoreEpoch", new object[1] { SnowRestoreState.CurrentEpoch }); _log.LogInfo((object)("snowrestore: sent restore epoch " + SnowRestoreState.CurrentEpoch + " to peer " + sender + ".")); } } private static void RPC_RestoreEpoch(long sender, int epoch) { ZNet instance = ZNet.instance; ZRoutedRpc instance2 = ZRoutedRpc.instance; if (!((Object)(object)instance == (Object)null) && instance2 != null && epoch > 0 && !instance.IsServer()) { long serverPeerID = GetServerPeerID(instance2); if (serverPeerID != 0 && sender != serverPeerID) { _log.LogWarning((object)("snowrestore: ignored restore epoch " + epoch + " from non-server peer " + sender + ".")); } else { SnowRestoreState.SetEpoch(epoch); _log.LogInfo((object)("snowrestore: received restore epoch " + epoch + " from server.")); SnowRestorer.RestoreLoadedDeepNorthTerrainComps("server restore epoch " + epoch, markCurrentEpoch: true, "Server requested Deep North snow restore; loaded Deep North zones were processed now, and unloaded Deep North zones will restore when loaded."); SchedulePostEventScans(); } } } private static long GetServerPeerID(ZRoutedRpc rpc) { if (rpc == null || ZRoutedRpcGetServerPeerIDMethod == null) { return 0L; } object obj = ZRoutedRpcGetServerPeerIDMethod.Invoke(rpc, null); if (obj is long) { return (long)obj; } return 0L; } private static void SchedulePostEventScans() { float num = Mathf.Max(0f, _postEventScanSeconds.Value); if (!(num <= 0f) && SnowRestoreState.HasPendingEpoch()) { float num2 = Mathf.Max(0.5f, _postEventScanIntervalSeconds.Value); _scheduledScanEpoch = SnowRestoreState.CurrentEpoch; _scheduledScanUntil = Time.realtimeSinceStartup + num; _nextScheduledScan = Time.realtimeSinceStartup + num2; _log.LogInfo((object)("snowrestore: scheduled post-event Deep North loaded-zone scans for " + num + " seconds.")); } } private static void RunScheduledPostEventScan() { if (_scheduledScanEpoch <= 0 || SnowRestoreState.CurrentEpoch != _scheduledScanEpoch) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup > _scheduledScanUntil) { _scheduledScanEpoch = 0; } else if (!(realtimeSinceStartup < _nextScheduledScan)) { ZNet instance = ZNet.instance; if (!((Object)(object)instance == (Object)null)) { float num = Mathf.Max(0.5f, _postEventScanIntervalSeconds.Value); _nextScheduledScan = realtimeSinceStartup + num; SnowRestorer.RestoreLoadedDeepNorthTerrainComps("post-event retry", markCurrentEpoch: true, "Retry scan for loaded Deep North terrain after a pending restore was started.", logAlways: false); } } } } internal static class SnowRestoreState { private static readonly Dictionary<string, int> RestoredZones = new Dictionary<string, int>(); private static ManualLogSource _log; private static string _statePath; public static int CurrentEpoch { get; private set; } public static void Load(ManualLogSource log) { _log = log; _statePath = Path.Combine(Paths.ConfigPath, "com.teamgrimgoren.valheim.deepnorthsnowfall.state"); CurrentEpoch = 0; RestoredZones.Clear(); if (!File.Exists(_statePath)) { return; } try { string[] array = File.ReadAllLines(_statePath); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0 || text.StartsWith("#")) { continue; } if (text.StartsWith("epoch=", StringComparison.OrdinalIgnoreCase)) { if (int.TryParse(text.Substring("epoch=".Length), out var result)) { CurrentEpoch = Math.Max(0, result); } } else if (text.StartsWith("zone=", StringComparison.OrdinalIgnoreCase)) { ParseZoneLine(text); } } _log.LogInfo((object)("Loaded snowrestore state: epoch=" + CurrentEpoch + ", restoredZones=" + RestoredZones.Count + ".")); } catch (Exception ex) { _log.LogWarning((object)("Could not load snowrestore state file: " + ex.Message)); CurrentEpoch = 0; RestoredZones.Clear(); } } public static int BeginNewEpoch() { CurrentEpoch = ((CurrentEpoch == int.MaxValue) ? 1 : (CurrentEpoch + 1)); RestoredZones.Clear(); Save(); return CurrentEpoch; } public static void SetEpoch(int epoch) { if (epoch > 0 && CurrentEpoch != epoch) { CurrentEpoch = epoch; RestoredZones.Clear(); Save(); } } public static bool HasPendingEpoch() { return CurrentEpoch > 0; } public static bool IsRestoredThisEpoch(string zoneKey) { if (CurrentEpoch > 0 && RestoredZones.TryGetValue(zoneKey, out var value)) { return value >= CurrentEpoch; } return false; } public static void MarkRestoredThisEpoch(string zoneKey) { if (CurrentEpoch > 0 && !string.IsNullOrEmpty(zoneKey)) { RestoredZones[zoneKey] = CurrentEpoch; Save(); } } private static void ParseZoneLine(string line) { string text = line.Substring("zone=".Length); string[] array = text.Split(new char[1] { ';' }); if (array.Length == 0) { return; } int result = 0; for (int i = 1; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.StartsWith("epoch=", StringComparison.OrdinalIgnoreCase)) { int.TryParse(text2.Substring("epoch=".Length), out result); } } string text3 = array[0].Trim(); if (text3.Length > 0 && result > 0) { RestoredZones[text3] = result; } } private static void Save() { if (string.IsNullOrEmpty(_statePath)) { return; } try { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# Deep North Snowfall state"); stringBuilder.AppendLine("epoch=" + CurrentEpoch); foreach (KeyValuePair<string, int> restoredZone in RestoredZones) { stringBuilder.Append("zone="); stringBuilder.Append(restoredZone.Key); stringBuilder.Append(";epoch="); stringBuilder.Append(restoredZone.Value); stringBuilder.AppendLine(); } File.WriteAllText(_statePath, stringBuilder.ToString()); } catch (Exception ex) { if (_log != null) { _log.LogWarning((object)("Could not save snowrestore state file: " + ex.Message)); } } } } internal static class SnowRestorer { private const BindingFlags PrivateInstance = BindingFlags.Instance | BindingFlags.NonPublic; private const BindingFlags PrivateStatic = BindingFlags.Static | BindingFlags.NonPublic; private static readonly FieldInfo TerrainCompInstancesField = typeof(TerrainComp).GetField("s_instances", BindingFlags.Static | BindingFlags.NonPublic); private static readonly FieldInfo TerrainCompHeightmapField = typeof(TerrainComp).GetField("m_hmap", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo TerrainCompNetViewField = typeof(TerrainComp).GetField("m_nview", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo TerrainCompPitchField = typeof(TerrainComp).GetField("m_pitch", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo TerrainCompModifiedPaintField = typeof(TerrainComp).GetField("m_modifiedPaint", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo TerrainCompPaintMaskField = typeof(TerrainComp).GetField("m_paintMask", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly MethodInfo TerrainCompSaveMethod = typeof(TerrainComp).GetMethod("Save", BindingFlags.Instance | BindingFlags.NonPublic); private static ManualLogSource _log; private static ConfigEntry<float> _minimumSnowDelta; private static ConfigEntry<bool> _claimTerrainOwnership; private static ConfigEntry<bool> _verboseZoneLogs; public static void Initialize(ManualLogSource log, ConfigEntry<float> minimumSnowDelta, ConfigEntry<bool> claimTerrainOwnership, ConfigEntry<bool> verboseZoneLogs) { _log = log; _minimumSnowDelta = minimumSnowDelta; _claimTerrainOwnership = claimTerrainOwnership; _verboseZoneLogs = verboseZoneLogs; } public static RestoreReport RestoreLoadedDeepNorthTerrainComps(string reason, bool markCurrentEpoch, string note) { return RestoreLoadedDeepNorthTerrainComps(reason, markCurrentEpoch, note, logAlways: true); } public static RestoreReport RestoreLoadedDeepNorthTerrainComps(string reason, bool markCurrentEpoch, string note, bool logAlways) { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) RestoreReport restoreReport = new RestoreReport(reason, "loadedDeepNorth"); restoreReport.DeepNorthPlayerCount = -1; restoreReport.Note = note; if (!ReflectionReady(restoreReport)) { return restoreReport; } ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null) { restoreReport.Refuse("Refusing to run: network is not ready."); LogRefusal(restoreReport); return restoreReport; } WorldGenerator instance2 = WorldGenerator.instance; if (instance2 == null) { restoreReport.Refuse("Refusing to run: WorldGenerator is not ready."); LogRefusal(restoreReport); return restoreReport; } List<TerrainComp> loadedTerrainComps = GetLoadedTerrainComps(); restoreReport.LoadedTerrainComps = loadedTerrainComps.Count; HashSet<string> hashSet = new HashSet<string>(); for (int i = 0; i < loadedTerrainComps.Count; i++) { TerrainComp val = loadedTerrainComps[i]; if ((Object)(object)val == (Object)null) { continue; } object? value = TerrainCompHeightmapField.GetValue(val); Heightmap val2 = (Heightmap)((value is Heightmap) ? value : null); if (!((Object)(object)val2 == (Object)null) && !val2.IsDistantLod) { Vector2s zone = ZoneSystem.GetZone(((Component)val2).transform.position); if ((!markCurrentEpoch || !SnowRestoreState.IsRestoredThisEpoch(ZoneKey(zone))) && RestoreTerrainComp(val, val2, zone, instance2, restoreReport, markCurrentEpoch)) { hashSet.Add(ZoneKey(zone)); } } } restoreReport.InspectedZoneCount = hashSet.Count; if (logAlways || restoreReport.ZoneResults.Count > 0 || restoreReport.ChangedSnowValues > 0) { LogReport(restoreReport); } return restoreReport; } public static void RestoreLoadedTerrainCompIfPending(TerrainComp terrainComp, string reason) { //IL_0069: 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_0073: 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_00bb: Unknown result type (might be due to invalid IL or missing references) if (!SnowRestoreState.HasPendingEpoch() || (Object)(object)terrainComp == (Object)null) { return; } RestoreReport report = new RestoreReport(reason, "pendingLoadedZone"); if (!ReflectionReady(report)) { return; } ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null) { return; } WorldGenerator instance2 = WorldGenerator.instance; if (instance2 == null) { return; } object? value = TerrainCompHeightmapField.GetValue(terrainComp); Heightmap val = (Heightmap)((value is Heightmap) ? value : null); if ((Object)(object)val == (Object)null || val.IsDistantLod) { return; } Vector2s zone = ZoneSystem.GetZone(((Component)val).transform.position); string zoneKey = ZoneKey(zone); if (!SnowRestoreState.IsRestoredThisEpoch(zoneKey)) { RestoreReport restoreReport = new RestoreReport(reason + " epoch " + SnowRestoreState.CurrentEpoch, "pendingLoadedZone"); restoreReport.LoadedTerrainComps = 1; restoreReport.InspectedZoneCount = 1; if (RestoreTerrainComp(terrainComp, val, zone, instance2, restoreReport, markCurrentEpoch: true) && restoreReport.ChangedSnowValues > 0) { LogReport(restoreReport); } } } private static bool ReflectionReady(RestoreReport report) { if (TerrainCompInstancesField == null || TerrainCompHeightmapField == null || TerrainCompNetViewField == null || TerrainCompPitchField == null || TerrainCompModifiedPaintField == null || TerrainCompPaintMaskField == null || TerrainCompSaveMethod == null) { report.Refuse("Refusing to run: current Valheim TerrainComp internals do not match the inspected layout."); LogRefusal(report); return false; } return true; } private static List<TerrainComp> GetLoadedTerrainComps() { if (!(TerrainCompInstancesField.GetValue(null) is List<TerrainComp> collection)) { return new List<TerrainComp>(); } return new List<TerrainComp>(collection); } private static bool RestoreTerrainComp(TerrainComp terrainComp, Heightmap heightmap, Vector2s zone, WorldGenerator worldGenerator, RestoreReport report, bool markCurrentEpoch) { //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0101: 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_010a: Invalid comparison between Unknown and I4 //IL_01bd: 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_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) object? value = TerrainCompNetViewField.GetValue(terrainComp); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if ((Object)(object)val == (Object)null || !val.IsValid()) { report.SkippedInvalidNetView++; return false; } if (!val.IsOwner()) { if (_claimTerrainOwnership.Value) { val.ClaimOwnership(); } if (!val.IsOwner()) { report.SkippedNotOwner++; return false; } } int num = (int)TerrainCompPitchField.GetValue(terrainComp); bool[] array = TerrainCompModifiedPaintField.GetValue(terrainComp) as bool[]; Color[] array2 = TerrainCompPaintMaskField.GetValue(terrainComp) as Color[]; if (num <= 0 || array == null || array2 == null || array.Length != array2.Length) { report.SkippedBadPaintData++; return false; } Texture2D paintMask = heightmap.GetPaintMask(); int num2 = 0; int num3 = 0; float num4 = Mathf.Max(0f, _minimumSnowDelta.Value); Color val3 = default(Color); for (int i = 0; i < array2.Length; i++) { int num5 = i % num; int num6 = i / num; Vector3 val2 = heightmap.VertexMaskToWorld(num5, num6); if ((int)worldGenerator.GetBiome(val2) != 64) { continue; } num3++; worldGenerator.GetBiomeHeight((Biome)64, val2.x, val2.z, ref val3, false, true); float num7 = Mathf.Clamp01(val3.g); if (num7 <= 0f) { continue; } Color val4 = array2[i]; float num8 = Mathf.Clamp01(val4.g); if (!(num8 + num4 >= num7)) { val4.g = num7; array2[i] = val4; array[i] = true; if ((Object)(object)paintMask != (Object)null) { paintMask.SetPixel(num5, num6, val4); } num2++; } } if (num3 == 0) { return false; } string zoneKey = ZoneKey(zone); report.AddTouchedZone(zoneKey, num2); if (num2 <= 0) { if (markCurrentEpoch) { SnowRestoreState.MarkRestoredThisEpoch(zoneKey); } return true; } if ((Object)(object)paintMask != (Object)null) { paintMask.Apply(); } TerrainCompSaveMethod.Invoke(terrainComp, new object[1] { false }); heightmap.Poke(1, true); report.ChangedSnowValues += num2; if (markCurrentEpoch) { SnowRestoreState.MarkRestoredThisEpoch(zoneKey); } return true; } private static void LogRefusal(RestoreReport report) { if (_log != null) { _log.LogWarning((object)report.ToLogString()); } } private static void LogReport(RestoreReport report) { if (_log == null) { return; } _log.LogInfo((object)report.ToLogString()); if (_verboseZoneLogs.Value) { for (int i = 0; i < report.ZoneResults.Count; i++) { ZoneRestoreResult zoneRestoreResult = report.ZoneResults[i]; _log.LogInfo((object)("snowrestore zone " + zoneRestoreResult.Zone + ": changed " + zoneRestoreResult.ChangedSnowValues + " snow values")); } } } private static string ZoneKey(Vector2s zone) { return zone.x + "," + zone.y; } } internal sealed class RestoreReport { private readonly Dictionary<string, ZoneRestoreResult> _zonesByKey = new Dictionary<string, ZoneRestoreResult>(); public string Reason { get; private set; } public string Scope { get; private set; } public bool Refused { get; private set; } public string RefusalReason { get; private set; } public string Note { get; set; } public int DeepNorthPlayerCount { get; set; } public int LoadedTerrainComps { get; set; } public int InspectedZoneCount { get; set; } public int ChangedSnowValues { get; set; } public int SkippedNotOwner { get; set; } public int SkippedInvalidNetView { get; set; } public int SkippedBadPaintData { get; set; } public List<ZoneRestoreResult> ZoneResults { get; private set; } public RestoreReport(string reason, string scope) { Reason = reason; Scope = scope; ZoneResults = new List<ZoneRestoreResult>(); } public void Refuse(string reason) { Refused = true; RefusalReason = reason; } public void AddTouchedZone(string zoneKey, int changedSnowValues) { if (_zonesByKey.TryGetValue(zoneKey, out var value)) { value.ChangedSnowValues += changedSnowValues; return; } ZoneRestoreResult zoneRestoreResult = new ZoneRestoreResult(zoneKey, changedSnowValues); _zonesByKey.Add(zoneKey, zoneRestoreResult); ZoneResults.Add(zoneRestoreResult); } public string ToConsoleString() { return ToSummaryString(); } public string ToLogString() { return ToSummaryString(); } private string ToSummaryString() { if (Refused) { return "snowrestore refused (" + Reason + "): " + RefusalReason; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("snowrestore "); stringBuilder.Append(Reason); stringBuilder.Append(": playersInDeepNorth="); if (DeepNorthPlayerCount < 0) { stringBuilder.Append("notChecked"); } else { stringBuilder.Append(DeepNorthPlayerCount); } stringBuilder.Append(", scope="); stringBuilder.Append(Scope); stringBuilder.Append(", loadedTerrainComps="); stringBuilder.Append(LoadedTerrainComps); stringBuilder.Append(", inspectedZones="); stringBuilder.Append(InspectedZoneCount); stringBuilder.Append(", touchedZones="); stringBuilder.Append(ZoneResults.Count); stringBuilder.Append(", changedSnowValues="); stringBuilder.Append(ChangedSnowValues); if (!string.IsNullOrEmpty(Note)) { stringBuilder.Append(", note="); stringBuilder.Append(Note); } if (SkippedNotOwner > 0 || SkippedInvalidNetView > 0 || SkippedBadPaintData > 0) { stringBuilder.Append(", skippedNotOwner="); stringBuilder.Append(SkippedNotOwner); stringBuilder.Append(", skippedInvalidNetView="); stringBuilder.Append(SkippedInvalidNetView); stringBuilder.Append(", skippedBadPaintData="); stringBuilder.Append(SkippedBadPaintData); } return stringBuilder.ToString(); } } internal sealed class ZoneRestoreResult { public string Zone { get; private set; } public int ChangedSnowValues { get; set; } public ZoneRestoreResult(string zone, int changedSnowValues) { Zone = zone; ChangedSnowValues = changedSnowValues; } }