Decompiled source of GenerallyLitServerMod v1.1.0

plugins/GenerallyLitServerMod.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("GenerallyLitServerMod")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0")]
[assembly: AssemblyProduct("GenerallyLitServerMod")]
[assembly: AssemblyTitle("GenerallyLitServerMod")]
[assembly: AssemblyVersion("1.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace GenerallyLit
{
	internal static class ConfigSync
	{
		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		private static class RegisterOnRpcReady
		{
			private static void Postfix(ZRoutedRpc __instance)
			{
				Register(__instance);
			}
		}

		[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
		private static class PeerJoined
		{
			private static void Postfix()
			{
				BroadcastIfServer();
			}
		}

		[HarmonyPatch(typeof(ZNet), "Shutdown")]
		private static class Disconnected
		{
			private static void Postfix()
			{
				if (Plugin.ShuttingDown)
				{
					return;
				}
				try
				{
					RevertToLocal();
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Could not restore local settings on disconnect: " + ex.Message));
				}
			}
		}

		private const string RPC_NAME = "GenerallyLit_Config";

		internal static bool UsingServerValues { get; private set; }

		private static bool IsServer
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					return ZNet.instance.IsServer();
				}
				return false;
			}
		}

		private static ZPackage Pack(Values 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_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(1);
			val.Write(v.DaySeconds);
			val.Write(v.NightSeconds);
			val.Write(v.StackMultiplier);
			val.Write(v.WeightMultiplier);
			val.Write(v.SendRate);
			return val;
		}

		private static Values Unpack(ZPackage pkg)
		{
			int num = pkg.ReadInt();
			if (num != 1)
			{
				Plugin.Log.LogWarning((object)($"Server sent Generally Lit Server Mod wire version {num}, expected 1. " + "Versions differ - keeping local values."));
				return null;
			}
			return new Values
			{
				DaySeconds = pkg.ReadInt(),
				NightSeconds = pkg.ReadInt(),
				StackMultiplier = pkg.ReadSingle(),
				WeightMultiplier = pkg.ReadSingle(),
				SendRate = ((pkg.GetPos() < pkg.Size()) ? pkg.ReadInt() : Plugin.CfgSendRate.Value)
			};
		}

		internal static void Register(ZRoutedRpc rpc)
		{
			if (rpc != null)
			{
				rpc.Register<ZPackage>("GenerallyLit_Config", (Action<long, ZPackage>)OnReceived);
				Plugin.Log.LogInfo((object)"Registered RPC GenerallyLit_Config.");
			}
		}

		internal static void BroadcastIfServer()
		{
			if (IsServer && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "GenerallyLit_Config", new object[1] { Pack(Values.Local()) });
				Plugin.Log.LogInfo((object)$"Sent settings to all peers: {Values.Local()}");
			}
		}

		private static void OnReceived(long sender, ZPackage pkg)
		{
			if (IsServer)
			{
				return;
			}
			Values values = Unpack(pkg);
			if (values != null)
			{
				Values values2 = Values.Local();
				UsingServerValues = true;
				values.Apply("from server");
				if (!values.SameAs(values2))
				{
					Plugin.Log.LogWarning((object)"This server sets Generally Lit Server Mod differently from your own config. The server's values are the ones in force; yours come back when you leave.");
					Plugin.Log.LogWarning((object)$"  server: {values}");
					Plugin.Log.LogWarning((object)$"  yours : {values2}");
				}
			}
		}

		private static void RevertToLocal()
		{
			if (UsingServerValues)
			{
				UsingServerValues = false;
				Values.Local().Apply("left server, back to local config");
			}
		}
	}
	internal static class DayNight
	{
		[HarmonyPatch(typeof(EnvMan), "RescaleDayFraction")]
		private static class RescalePatch
		{
			private static bool Prefix(float fraction, ref float __result)
			{
				if (!_usable)
				{
					return true;
				}
				if (fraction >= _dawn && fraction <= _dusk)
				{
					__result = 0.25f + (fraction - _dawn) / (_dusk - _dawn) * 0.5f;
				}
				else if (fraction < _dawn)
				{
					__result = fraction / _dawn * 0.25f;
				}
				else
				{
					__result = 0.75f + (fraction - _dusk) / (1f - _dusk) * 0.25f;
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(EnvMan), "Awake")]
		private static class AwakePatch
		{
			private static void Postfix()
			{
				ApplyCycleLength();
				LogCurrentTimeOfDay();
			}
		}

		private static float _dawn;

		private static float _dusk;

		private static bool _usable;

		private static void RecomputeBoundaries(Values v)
		{
			float num = v.DaySeconds;
			float num2 = v.NightSeconds;
			float num3 = num + num2;
			if (num < 1f || num2 < 1f || num3 <= 0f)
			{
				_usable = false;
				return;
			}
			_dawn = num2 / (2f * num3);
			_dusk = _dawn + num / num3;
			_usable = _dawn > 0.0001f && _dusk < 0.9999f && _dusk > _dawn;
		}

		internal static void ApplyCycleLength()
		{
			Values active = Values.Active;
			if (active == null)
			{
				return;
			}
			RecomputeBoundaries(active);
			EnvMan instance = EnvMan.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				long num = active.DaySeconds + active.NightSeconds;
				if (instance.m_dayLengthSec != num)
				{
					instance.m_dayLengthSec = num;
					Plugin.Log.LogInfo((object)($"Cycle length set to {num}s ({active.DaySeconds}s day + {active.NightSeconds}s night). " + $"Dawn at raw {_dawn:F4}, dusk at raw {_dusk:F4} (vanilla is 0.1500 / 0.8500)."));
				}
			}
		}

		internal static void LogCurrentTimeOfDay()
		{
			EnvMan instance = EnvMan.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				float dayFraction = instance.GetDayFraction();
				string arg = ((dayFraction >= 0.25f && dayFraction <= 0.75f) ? "DAYLIGHT" : "NIGHT");
				Plugin.Log.LogInfo((object)$"Time of day is now {dayFraction:F3} ({arg}). 0.50 is noon, 0.00 is midnight.");
			}
		}
	}
	internal static class ItemTuning
	{
		private struct Original
		{
			public int MaxStack;

			public float Weight;
		}

		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static class AwakePatch
		{
			private static void Postfix()
			{
				Apply();
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
		private static class CopyPatch
		{
			private static void Postfix()
			{
				Apply();
			}
		}

		private static readonly Dictionary<SharedData, Original> Originals = new Dictionary<SharedData, Original>();

		internal static void Apply()
		{
			ObjectDB instance = ObjectDB.instance;
			Values active = Values.Active;
			if ((Object)(object)instance == (Object)null || instance.m_items == null || active == null)
			{
				return;
			}
			int num = 0;
			foreach (GameObject item in instance.m_items)
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				SharedData val = item.GetComponent<ItemDrop>()?.m_itemData?.m_shared;
				if (val != null)
				{
					if (!Originals.TryGetValue(val, out var value))
					{
						value = new Original
						{
							MaxStack = val.m_maxStackSize,
							Weight = val.m_weight
						};
						Originals[val] = value;
					}
					if (value.MaxStack > 1)
					{
						val.m_maxStackSize = Mathf.Max(1, Mathf.RoundToInt((float)value.MaxStack * active.StackMultiplier));
					}
					val.m_weight = value.Weight * active.WeightMultiplier;
					num++;
				}
			}
			if (num > 0)
			{
				Plugin.Log.LogInfo((object)$"Applied stack x{active.StackMultiplier} and weight x{active.WeightMultiplier} to {num} items.");
			}
		}
	}
	internal static class StackGuard
	{
		private static string StatePath => Path.Combine(Paths.ConfigPath, "generallylit-laststack.txt");

		internal static void WarnIfLowered(float current)
		{
			float num = Read();
			Write(Mathf.Max(num, current));
			if (!(num <= 0f) && !(current >= num))
			{
				Plugin.Log.LogWarning((object)"=======================================================================");
				Plugin.Log.LogWarning((object)$" Stack size multiplier LOWERED: x{num} -> x{current}");
				Plugin.Log.LogWarning((object)"");
				Plugin.Log.LogWarning((object)$" Every stack holding more than {current / num:P0} of its old maximum");
				Plugin.Log.LogWarning((object)" will be CUT DOWN to the new limit the next time the world loads,");
				Plugin.Log.LogWarning((object)" and the excess is gone for good.");
				Plugin.Log.LogWarning((object)"");
				Plugin.Log.LogWarning((object)" This does NOT happen now. It happens on the next world load, which");
				Plugin.Log.LogWarning((object)" on a dedicated server is the next restart - often within minutes of");
				Plugin.Log.LogWarning((object)" the last player leaving.");
				Plugin.Log.LogWarning((object)"");
				Plugin.Log.LogWarning((object)" Back up the world, or spread over-sized stacks out first.");
				Plugin.Log.LogWarning((object)"=======================================================================");
			}
		}

		private static float Read()
		{
			try
			{
				if (!File.Exists(StatePath))
				{
					return 0f;
				}
				float result;
				return float.TryParse(File.ReadAllText(StatePath).Trim(), out result) ? result : 0f;
			}
			catch
			{
				return 0f;
			}
		}

		private static void Write(float v)
		{
			try
			{
				File.WriteAllText(StatePath, v.ToString("R"));
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch(typeof(ZDOMan), "SendZDOToPeers2")]
	internal static class NetworkRate
	{
		private static readonly FieldRef<ZDOMan, float> SendTimer = AccessTools.FieldRefAccess<ZDOMan, float>("m_sendTimer");

		private static readonly FieldInfo PeersField = AccessTools.Field(typeof(ZDOMan), "m_peers");

		private static readonly MethodInfo SendZDOs = AccessTools.Method(typeof(ZDOMan), "SendZDOs", (Type[])null, (Type[])null);

		private static readonly object[] Args = new object[2];

		private static bool _failed;

		private static bool Prefix(ZDOMan __instance, float dt)
		{
			if (_failed)
			{
				return true;
			}
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || instance.IsServer())
			{
				return true;
			}
			int num = Values.Active?.SendRate ?? Plugin.CfgSendRate.Value;
			if (num <= 0)
			{
				return true;
			}
			try
			{
				ref float reference = ref SendTimer.Invoke(__instance);
				reference += dt;
				float num2 = 1f / (float)num;
				if (reference < num2)
				{
					return false;
				}
				reference -= num2;
				if (reference > num2)
				{
					reference = 0f;
				}
				IList list = (IList)PeersField.GetValue(__instance);
				for (int i = 0; i < list.Count; i++)
				{
					Args[0] = list[i];
					Args[1] = false;
					SendZDOs.Invoke(__instance, Args);
				}
				return false;
			}
			catch (Exception ex)
			{
				_failed = true;
				Plugin.Log.LogWarning((object)("Send rate control disabled, using vanilla sending: " + ex.GetBaseException().Message));
				return true;
			}
		}
	}
	[BepInPlugin("swatsqad.generallylitservermod", "Generally Lit Server Mod", "1.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string GUID = "swatsqad.generallylitservermod";

		public const string NAME = "Generally Lit Server Mod";

		public const string VERSION = "1.1.0";

		internal static ManualLogSource Log;

		internal static Plugin Instance;

		internal static ConfigEntry<int> CfgDaySeconds;

		internal static ConfigEntry<int> CfgNightSeconds;

		internal static ConfigEntry<float> CfgStackMultiplier;

		internal static ConfigEntry<float> CfgWeightMultiplier;

		internal static ConfigEntry<int> CfgSendRate;

		private Harmony _harmony;

		internal static bool ShuttingDown;

		private const float ConfigPollSeconds = 2f;

		private DateTime _configStamp;

		private float _nextConfigPoll;

		private void Awake()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Expected O, but got Unknown
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Expected O, but got Unknown
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Expected O, but got Unknown
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			CfgDaySeconds = ((BaseUnityPlugin)this).Config.Bind<int>("Day and night", "Day length in seconds", 840, new ConfigDescription("How long daylight lasts, in real seconds. Vanilla is 840 (14 minutes). Set together with night length: the two add up to one full day, so the day counter and everything that runs off it stay sensible.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 86400), Array.Empty<object>()));
			CfgNightSeconds = ((BaseUnityPlugin)this).Config.Bind<int>("Day and night", "Night length in seconds", 360, new ConfigDescription("How long night lasts, in real seconds. Vanilla is 360 (6 minutes). Unlike the game's own day length this is set on its own, so you can have a long day and a short night.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 86400), Array.Empty<object>()));
			CfgStackMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Items", "Stack size multiplier", 1f, new ConfigDescription("Multiplies the maximum stack size of things that already stack. Items that do not stack in vanilla are never made stackable.\nWARNING: LOWERING this on a world that already has bigger stacks destroys everything above the new limit the next time the world loads. Raising it is always safe.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
			CfgWeightMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Items", "Item weight multiplier", 1f, new ConfigDescription("Multiplies the weight of every item. 0.8 makes everything 20% lighter.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 10f), Array.Empty<object>()));
			CfgSendRate = ((BaseUnityPlugin)this).Config.Bind<int>("Network", "Updates sent per second", 30, new ConfigDescription("How many times a second a player's game sends what it controls - its character, a ship it steers, creatures near it - to the server. Vanilla aims for 20 and in practice sends about 14, because each send waits for the next frame. Higher looks smoother to everyone else, especially ships, and costs a little upload. Set on the server, it is sent to every player. 0 = leave vanilla alone. The server's own sending is not affected.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 60), Array.Empty<object>()));
			CfgDaySeconds.SettingChanged += OnSettingChanged;
			CfgNightSeconds.SettingChanged += OnSettingChanged;
			CfgStackMultiplier.SettingChanged += OnSettingChanged;
			CfgWeightMultiplier.SettingChanged += OnSettingChanged;
			CfgSendRate.SettingChanged += OnSettingChanged;
			StackGuard.WarnIfLowered(CfgStackMultiplier.Value);
			_configStamp = ConfigStamp();
			_harmony = new Harmony("swatsqad.generallylitservermod");
			_harmony.PatchAll();
			Values.Local().Apply("startup");
			Log.LogInfo((object)string.Format("{0} {1} loaded. {2}", "Generally Lit Server Mod", "1.1.0", Values.Local()));
		}

		private void OnDestroy()
		{
			ShuttingDown = true;
		}

		private DateTime ConfigStamp()
		{
			try
			{
				return File.GetLastWriteTimeUtc(((BaseUnityPlugin)this).Config.ConfigFilePath);
			}
			catch
			{
				return DateTime.MinValue;
			}
		}

		private void Update()
		{
			if (Time.unscaledTime < _nextConfigPoll)
			{
				return;
			}
			_nextConfigPoll = Time.unscaledTime + 2f;
			DateTime dateTime = ConfigStamp();
			if (dateTime == _configStamp)
			{
				return;
			}
			_configStamp = dateTime;
			try
			{
				((BaseUnityPlugin)this).Config.Reload();
				Log.LogInfo((object)"Config file change detected by timestamp, reloaded.");
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Could not reload config: " + ex.Message));
			}
		}

		private void OnSettingChanged(object sender, EventArgs e)
		{
			if (ConfigSync.UsingServerValues)
			{
				Log.LogInfo((object)"Local setting changed, but this server's values are in force. Your own values come back when you leave.");
				return;
			}
			StackGuard.WarnIfLowered(CfgStackMultiplier.Value);
			Values.Local().Apply("local config changed");
			ConfigSync.BroadcastIfServer();
		}
	}
	internal class Values
	{
		public int DaySeconds;

		public int NightSeconds;

		public float StackMultiplier;

		public float WeightMultiplier;

		public int SendRate;

		public static Values Active { get; private set; }

		public static Values Local()
		{
			return new Values
			{
				DaySeconds = Plugin.CfgDaySeconds.Value,
				NightSeconds = Plugin.CfgNightSeconds.Value,
				StackMultiplier = Plugin.CfgStackMultiplier.Value,
				WeightMultiplier = Plugin.CfgWeightMultiplier.Value,
				SendRate = Plugin.CfgSendRate.Value
			};
		}

		public bool SameAs(Values o)
		{
			if (o != null && DaySeconds == o.DaySeconds && NightSeconds == o.NightSeconds && Mathf.Approximately(StackMultiplier, o.StackMultiplier) && Mathf.Approximately(WeightMultiplier, o.WeightMultiplier))
			{
				return SendRate == o.SendRate;
			}
			return false;
		}

		public void Apply(string reason)
		{
			bool num = !SameAs(Active);
			Active = this;
			ItemTuning.Apply();
			DayNight.ApplyCycleLength();
			if (num)
			{
				Plugin.Log.LogInfo((object)$"Applied ({reason}): {this}");
				DayNight.LogCurrentTimeOfDay();
			}
		}

		public override string ToString()
		{
			return $"day={DaySeconds}s night={NightSeconds}s stacks=x{StackMultiplier} weight=x{WeightMultiplier} sends={SendRate}/s";
		}
	}
}