Decompiled source of SpawnRestrictions v0.2.0

BepInEx/plugins/SpawnRestrictions.dll

Decompiled 3 weeks ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SpawnRestrictions.Configuration;
using SpawnRestrictions.Patches;
using SpawnRestrictions.Services;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SpawnRestrictions
{
	[BepInPlugin("spawnrestrictions.valheim", "Spawn Restrictions", "0.2.0")]
	public sealed class SpawnRestrictionsPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "spawnrestrictions.valheim";

		public const string PluginName = "Spawn Restrictions";

		public const string PluginVersion = "0.2.0";

		private Harmony _harmony;

		private ServerRuleSync _sync;

		private float _nextUpdate;

		private void Awake()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			BossRestrictionState state = new BossRestrictionState();
			_sync = new ServerRuleSync(new BossRestrictionSettings(((BaseUnityPlugin)this).Config), state);
			BossPatches.Initialize(state, _sync);
			_harmony = new Harmony("spawnrestrictions.valheim");
			_harmony.CreateClassProcessor(typeof(BossPatches)).Patch();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Spawn Restrictions loaded. Install this version on the host/server and every client.");
		}

		private void Update()
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.HaveStopped && !(Time.unscaledTime < _nextUpdate))
			{
				_nextUpdate = Time.unscaledTime + 0.5f;
				_sync.Broadcast();
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			BossPatches.Clear();
		}
	}
}
namespace SpawnRestrictions.Services
{
	internal static class BossMessages
	{
		public static void Show(Humanoid player, BossRestrictionState state)
		{
			if (!((Object)(object)player == (Object)null))
			{
				string text = (state.IsReady ? $"Bosses require {state.RequiredPlayers} players online ({state.OnlinePlayers} online)." : "Waiting for the server's boss restrictions.");
				((Character)player).Message((MessageType)2, text, 0, (Sprite)null, false);
			}
		}
	}
	internal static class BossProgressionService
	{
		public static bool IsDefeated(GameObject bossPrefab)
		{
			if ((Object)(object)bossPrefab == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null)
			{
				return false;
			}
			Character component = bossPrefab.GetComponent<Character>();
			if ((Object)(object)component != (Object)null && !string.IsNullOrEmpty(component.m_defeatSetGlobalKey))
			{
				return ZoneSystem.instance.GetGlobalKey(component.m_defeatSetGlobalKey);
			}
			return false;
		}
	}
	internal sealed class BossRestrictionState
	{
		public bool IsReady { get; private set; }

		public int RequiredPlayers { get; private set; }

		public int OnlinePlayers { get; private set; }

		public bool AllowDefeatedBosses { get; private set; }

		public bool AllowsSummoning(bool defeatedInWorld)
		{
			if (IsReady)
			{
				if (RequiredPlayers != 0 && OnlinePlayers < RequiredPlayers)
				{
					return AllowDefeatedBosses && defeatedInWorld;
				}
				return true;
			}
			return false;
		}

		public void Set(int requiredPlayers, int onlinePlayers, bool allowDefeatedBosses)
		{
			if (requiredPlayers >= 0 && onlinePlayers >= 0)
			{
				RequiredPlayers = requiredPlayers;
				OnlinePlayers = onlinePlayers;
				AllowDefeatedBosses = allowDefeatedBosses;
				IsReady = true;
			}
		}

		public void Reset()
		{
			IsReady = false;
			RequiredPlayers = 0;
			OnlinePlayers = 0;
			AllowDefeatedBosses = false;
		}
	}
	internal sealed class ServerRuleSync
	{
		private const string StateRpc = "SpawnRestrictions.State.v2";

		private readonly BossRestrictionSettings _settings;

		private readonly BossRestrictionState _state;

		public ServerRuleSync(BossRestrictionSettings settings, BossRestrictionState state)
		{
			_settings = settings;
			_state = state;
		}

		public void RegisterPeer(ZNetPeer peer)
		{
			peer.m_rpc.Register<int, int, bool>("SpawnRestrictions.State.v2", (Action<ZRpc, int, int, bool>)ReceiveState);
		}

		public void RefreshHost()
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance != (Object)null && instance.IsServer())
			{
				_state.Set(_settings.RequiredOnlinePlayers.Value, instance.GetNrOfPlayers(), _settings.AllowDefeatedBosses.Value);
			}
		}

		public void Broadcast()
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || !instance.IsServer())
			{
				return;
			}
			RefreshHost();
			foreach (ZNetPeer peer in instance.GetPeers())
			{
				if (peer.IsReady())
				{
					peer.m_rpc.Invoke("SpawnRestrictions.State.v2", new object[3] { _state.RequiredPlayers, _state.OnlinePlayers, _state.AllowDefeatedBosses });
				}
			}
		}

		private void ReceiveState(ZRpc rpc, int requiredPlayers, int onlinePlayers, bool allowDefeatedBosses)
		{
			ZNet instance = ZNet.instance;
			if (!((Object)(object)instance == (Object)null) && !instance.IsServer() && rpc == instance.GetServerRPC())
			{
				_state.Set(requiredPlayers, onlinePlayers, allowDefeatedBosses);
			}
		}
	}
}
namespace SpawnRestrictions.Patches
{
	[HarmonyPatch]
	internal static class BossPatches
	{
		private static BossRestrictionState _state;

		private static ServerRuleSync _sync;

		public static void Initialize(BossRestrictionState state, ServerRuleSync sync)
		{
			_state = state;
			_sync = sync;
		}

		public static void Clear()
		{
			_state = null;
			_sync = null;
		}

		[HarmonyPatch(typeof(ZNet), "Awake")]
		[HarmonyPrefix]
		private static void BeginSession()
		{
			_state.Reset();
		}

		[HarmonyPatch(typeof(ZNet), "OnDestroy")]
		[HarmonyPostfix]
		private static void EndSession()
		{
			_state.Reset();
		}

		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		[HarmonyPrefix]
		private static void RegisterPeer(ZNetPeer peer)
		{
			_sync.RegisterPeer(peer);
		}

		[HarmonyPatch(typeof(OfferingBowl), "UseItem")]
		[HarmonyPrefix]
		private static bool UseItem(OfferingBowl __instance, Humanoid user, ItemData item, ref bool __result)
		{
			if (__instance.m_useItemStands || (Object)(object)__instance.m_bossPrefab == (Object)null || (Object)(object)__instance.m_bossItem == (Object)null || item == null || item.m_shared.m_name != __instance.m_bossItem.m_itemData.m_shared.m_name || AllowsBosses(__instance))
			{
				return true;
			}
			BossMessages.Show(user, _state);
			__result = true;
			return false;
		}

		[HarmonyPatch(typeof(OfferingBowl), "Interact")]
		[HarmonyPrefix]
		private static bool Interact(OfferingBowl __instance, Humanoid user, bool hold, ref bool __result)
		{
			if (hold || !__instance.m_useItemStands || (Object)(object)__instance.m_bossPrefab == (Object)null || AllowsBosses(__instance))
			{
				return true;
			}
			BossMessages.Show(user, _state);
			__result = false;
			return false;
		}

		[HarmonyPatch(typeof(OfferingBowl), "RPC_SpawnBoss")]
		[HarmonyPrefix]
		private static bool SpawnBoss(OfferingBowl __instance)
		{
			if (!((Object)(object)__instance.m_bossPrefab == (Object)null))
			{
				return AllowsBosses(__instance);
			}
			return true;
		}

		private static bool AllowsBosses(OfferingBowl bowl)
		{
			_sync.RefreshHost();
			bool defeatedInWorld = _state.AllowDefeatedBosses && BossProgressionService.IsDefeated(bowl.m_bossPrefab);
			return _state.AllowsSummoning(defeatedInWorld);
		}
	}
}
namespace SpawnRestrictions.Configuration
{
	internal sealed class BossRestrictionSettings
	{
		public ConfigEntry<int> RequiredOnlinePlayers { get; }

		public ConfigEntry<bool> AllowDefeatedBosses { get; }

		public BossRestrictionSettings(ConfigFile config)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			RequiredOnlinePlayers = config.Bind<int>("Boss restrictions", "Required online players", 2, new ConfigDescription("Minimum online players to summon bosses at altars. Only the host/server value is used. 0 disables the restriction. Existing bosses are unaffected.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, int.MaxValue), Array.Empty<object>()));
			AllowDefeatedBosses = config.Bind<bool>("Boss restrictions", "Allow defeated bosses", true, "Allow bosses already defeated in the current world to be summoned regardless of player count. Only the host/server value is used. False applies the player requirement to every boss.");
		}
	}
}