Decompiled source of ScatteredVikings v1.0.1

IronLabs.ScatteredVikings.dll

Decompiled 2 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using IronLabs.SharedLib;
using Splatform;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("IronLabs.ScatteredVikings")]
[assembly: AssemblyDescription("Gives every Viking a personal Meadows spawn.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("IronLabs")]
[assembly: AssemblyProduct("IronLabs.ScatteredVikings")]
[assembly: AssemblyCopyright("Copyright © 2026 End3rbyte")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("3EF3F0B9-0D06-4746-AAE4-B5FAFD1F0F95")]
[assembly: AssemblyFileVersion("1.0.1")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.1.27463")]
namespace IronLabs.ScatteredVikings
{
	[HarmonyPatch(typeof(Game), "FindSpawnPoint")]
	internal static class GameFindSpawnPointPatch
	{
		private static bool Prefix(Game __instance, ref bool __result, ref Vector3 point, ref bool usedLogoutPoint, bool ___m_respawnAfterDeath)
		{
			//IL_001b: 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)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			PlayerProfile playerProfile = __instance.GetPlayerProfile();
			if (ShouldUseVanillaSpawn(playerProfile, ___m_respawnAfterDeath))
			{
				return true;
			}
			point = SpawnPointSelector.Get(playerProfile);
			usedLogoutPoint = false;
			ZNet.instance.SetReferencePosition(point);
			__result = TryPrepareSpawnPoint(ref point);
			return false;
		}

		private static bool ShouldUseVanillaSpawn(PlayerProfile profile, bool respawnAfterDeath)
		{
			return profile.HaveCustomSpawnPoint() || (!respawnAfterDeath && profile.HaveLogoutPoint());
		}

		private static bool TryPrepareSpawnPoint(ref Vector3 point)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			if (!ZNetScene.instance.IsAreaReady(point))
			{
				return false;
			}
			float num = default(float);
			if (!ZoneSystem.instance.GetGroundHeight(point, ref num))
			{
				ScatteredVikingsPlugin.Log.LogWarning($"No ground was found at scattered spawn {point}; waiting for terrain.");
				return false;
			}
			point.y = num + 0.25f;
			return true;
		}
	}
	[BepInPlugin("IronLabs.ScatteredVikings", "IronLabs.ScatteredVikings", "1.0.1")]
	public sealed class ScatteredVikingsPlugin : IronLabsPlugin
	{
		private const string PluginGuid = "IronLabs.ScatteredVikings";

		private const string PluginName = "IronLabs.ScatteredVikings";

		private const string PluginVersion = "1.0.1";

		internal static ModLog Log { get; private set; }

		private void Awake()
		{
			Log = InitializePlugin("IronLabs.ScatteredVikings");
			Log.LogInfo("IronLabs.ScatteredVikings 1.0.1 is loaded.");
		}

		private void OnDestroy()
		{
			SpawnPointSelector.Clear();
			ShutdownPlugin();
			Log = null;
		}
	}
	internal static class SpawnPointSelector
	{
		private const float MaximumRadius = 1000f;

		private const float MinimumHeightAboveWater = 2f;

		private const int MaximumAttempts = 10000;

		internal static Vector3 Get(PlayerProfile profile)
		{
			//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_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: 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_005f: 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_0020: 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)
			long worldUID = ZNet.instance.GetWorldUID();
			if (SpawnPointStore.TryGet(profile.GetPlayerID(), worldUID, out var point))
			{
				return point;
			}
			point = FindSpawnPoint(profile);
			SpawnPointStore.Set(profile.GetPlayerID(), worldUID, point);
			ScatteredVikingsPlugin.Log.LogDebug($"Selected scattered spawn {point} for player {profile.GetPlayerID()}.");
			return point;
		}

		internal static void Clear()
		{
			SpawnPointStore.ClearCache();
		}

		private static Vector3 FindSpawnPoint(PlayerProfile profile)
		{
			//IL_0013: 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_0019: 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_008c: 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_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: 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)
			Random random = new Random(CreateSeed(profile));
			for (int i = 0; i < 10000; i++)
			{
				Vector3 val = CreateCandidate(random);
				if (IsValid(val))
				{
					val.y = WorldGenerator.instance.GetHeight(val.x, val.z);
					return val;
				}
			}
			ScatteredVikingsPlugin.Log.LogWarning("No random Meadows spawn was found; using the Meadows world center.");
			return new Vector3(0f, WorldGenerator.instance.GetHeight(0f, 0f), 0f);
		}

		private static int CreateSeed(PlayerProfile profile)
		{
			long worldUID = ZNet.instance.GetWorldUID();
			long playerID = profile.GetPlayerID();
			int num = (int)worldUID ^ (int)(worldUID >> 32);
			return (num * 397) ^ (int)playerID ^ (int)(playerID >> 32);
		}

		private static Vector3 CreateCandidate(Random random)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			double num = random.NextDouble() * Math.PI * 2.0;
			float num2 = 1000f * Mathf.Sqrt((float)random.NextDouble());
			return new Vector3(num2 * (float)Math.Cos(num), 0f, num2 * (float)Math.Sin(num));
		}

		private static bool IsValid(Vector3 candidate)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Invalid comparison between Unknown and I4
			//IL_002b: 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)
			WorldGenerator instance = WorldGenerator.instance;
			if ((int)instance.GetBiome(candidate) != 1 || (int)instance.GetBiomeArea(candidate) != 2)
			{
				return false;
			}
			float height = instance.GetHeight(candidate.x, candidate.z);
			return height >= ZoneSystem.instance.m_waterLevel + 2f;
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	internal static class PlayerOnSpawnedPatch
	{
		private static void Postfix(Player __instance)
		{
			//IL_0085: 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_00b2: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)Minimap.instance == (Object)null) && !((Object)(object)Game.instance == (Object)null) && !((Object)(object)ZNet.instance == (Object)null))
			{
				PlayerProfile playerProfile = Game.instance.GetPlayerProfile();
				long playerID = playerProfile.GetPlayerID();
				long worldUID = ZNet.instance.GetWorldUID();
				if (SpawnPointStore.NeedsPin(playerID, worldUID) && SpawnPointStore.TryGet(playerID, worldUID, out var point))
				{
					Minimap.instance.AddPin(point, (PinType)3, "START", true, false, 0L, default(PlatformUserID));
					SpawnPointStore.MarkPinCreated(playerID, worldUID);
					ScatteredVikingsPlugin.Log.LogDebug($"Added removable start pin at {point}.");
				}
			}
		}
	}
	internal static class SpawnPointStore
	{
		private struct SpawnRecord
		{
			internal readonly Vector3 Point;

			internal readonly bool PinCreated;

			internal SpawnRecord(Vector3 point, bool pinCreated)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0003: Unknown result type (might be due to invalid IL or missing references)
				Point = point;
				PinCreated = pinCreated;
			}
		}

		private const string Header = "IronLabs.ScatteredVikings:1";

		private static readonly Dictionary<long, Dictionary<long, SpawnRecord>> Cache = new Dictionary<long, Dictionary<long, SpawnRecord>>();

		internal static bool TryGet(long playerId, long worldId, out Vector3 point)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<long, SpawnRecord> playerSpawns = GetPlayerSpawns(playerId);
			SpawnRecord value;
			bool flag = playerSpawns.TryGetValue(worldId, out value);
			point = (flag ? value.Point : Vector3.zero);
			return flag;
		}

		internal static void Set(long playerId, long worldId, Vector3 point)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<long, SpawnRecord> playerSpawns = GetPlayerSpawns(playerId);
			playerSpawns[worldId] = new SpawnRecord(point, pinCreated: false);
			Save(playerId, playerSpawns);
		}

		internal static bool NeedsPin(long playerId, long worldId)
		{
			Dictionary<long, SpawnRecord> playerSpawns = GetPlayerSpawns(playerId);
			SpawnRecord value;
			return playerSpawns.TryGetValue(worldId, out value) && !value.PinCreated;
		}

		internal static void MarkPinCreated(long playerId, long worldId)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<long, SpawnRecord> playerSpawns = GetPlayerSpawns(playerId);
			if (playerSpawns.TryGetValue(worldId, out var value))
			{
				playerSpawns[worldId] = new SpawnRecord(value.Point, pinCreated: true);
				Save(playerId, playerSpawns);
			}
		}

		internal static void ClearCache()
		{
			Cache.Clear();
		}

		private static Dictionary<long, SpawnRecord> GetPlayerSpawns(long playerId)
		{
			if (Cache.TryGetValue(playerId, out var value))
			{
				return value;
			}
			value = Load(playerId);
			Cache.Add(playerId, value);
			return value;
		}

		private static Dictionary<long, SpawnRecord> Load(long playerId)
		{
			Dictionary<long, SpawnRecord> dictionary = new Dictionary<long, SpawnRecord>();
			string path = GetPath(playerId);
			if (!File.Exists(path))
			{
				return dictionary;
			}
			try
			{
				ParseLines(File.ReadAllLines(path), dictionary);
				ScatteredVikingsPlugin.Log.LogDebug($"Loaded {dictionary.Count} scattered spawn(s) for player {playerId}.");
			}
			catch (Exception ex)
			{
				ScatteredVikingsPlugin.Log.LogWarning("Failed to load scattered spawns from " + path + ": " + ex.Message);
			}
			return dictionary;
		}

		private static void ParseLines(string[] lines, Dictionary<long, SpawnRecord> spawns)
		{
			if (lines.Length == 0 || lines[0] != "IronLabs.ScatteredVikings:1")
			{
				throw new InvalidDataException("Unsupported scattered spawn file format.");
			}
			for (int i = 1; i < lines.Length; i++)
			{
				if (TryParse(lines[i], out var worldId, out var record))
				{
					spawns[worldId] = record;
				}
				else
				{
					ScatteredVikingsPlugin.Log.LogWarning("Ignored malformed scattered spawn entry: " + lines[i]);
				}
			}
		}

		private static bool TryParse(string line, out long worldId, out SpawnRecord record)
		{
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			string[] array = line.Split(new char[1] { '|' });
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			worldId = 0L;
			float result = 0f;
			float result2 = 0f;
			float result3 = 0f;
			bool result4 = false;
			bool result5 = array.Length == 5 && long.TryParse(array[0], NumberStyles.Integer, invariantCulture, out worldId) && float.TryParse(array[1], NumberStyles.Float, invariantCulture, out result) && float.TryParse(array[2], NumberStyles.Float, invariantCulture, out result2) && float.TryParse(array[3], NumberStyles.Float, invariantCulture, out result3) && bool.TryParse(array[4], out result4);
			record = new SpawnRecord(new Vector3(result, result2, result3), result4);
			return result5;
		}

		private static void Save(long playerId, Dictionary<long, SpawnRecord> spawns)
		{
			string path = GetPath(playerId);
			string text = path + ".new";
			try
			{
				Directory.CreateDirectory(Path.GetDirectoryName(path));
				File.WriteAllLines(text, CreateLines(spawns));
				ReplaceFile(text, path);
				ScatteredVikingsPlugin.Log.LogDebug($"Saved {spawns.Count} scattered spawn(s) for player {playerId}.");
			}
			catch (Exception ex)
			{
				ScatteredVikingsPlugin.Log.LogWarning("Failed to save scattered spawns to " + path + ": " + ex.Message);
				DeleteTemporaryFile(text);
			}
		}

		private static string[] CreateLines(Dictionary<long, SpawnRecord> spawns)
		{
			//IL_004c: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string> { "IronLabs.ScatteredVikings:1" };
			foreach (KeyValuePair<long, SpawnRecord> spawn in spawns)
			{
				SpawnRecord value = spawn.Value;
				list.Add(string.Join("|", spawn.Key, Format(value.Point.x), Format(value.Point.y), Format(value.Point.z), value.PinCreated));
			}
			return list.ToArray();
		}

		private static string Format(float value)
		{
			return value.ToString("R", CultureInfo.InvariantCulture);
		}

		private static void ReplaceFile(string temporaryPath, string path)
		{
			if (File.Exists(path))
			{
				File.Replace(temporaryPath, path, null);
			}
			else
			{
				File.Move(temporaryPath, path);
			}
		}

		private static void DeleteTemporaryFile(string path)
		{
			if (File.Exists(path))
			{
				File.Delete(path);
			}
		}

		private static string GetPath(long playerId)
		{
			return Path.Combine(Paths.ConfigPath, "IronLabs.ScatteredVikings", $"{playerId}.spawns");
		}
	}
}
namespace IronLabs.SharedLib
{
	public abstract class IronLabsPlugin : BaseUnityPlugin
	{
		private Harmony _harmony;

		private bool _patchesApplied;

		protected ModLog InitializePlugin(string pluginGuid)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			ModLog modLog = new ModLog(((BaseUnityPlugin)this).Logger);
			Version version = ((object)this).GetType().Assembly.GetName().Version;
			modLog.LogInfo($"AssemblyVersion: {version}.");
			_harmony = new Harmony(pluginGuid);
			PatchOwnNamespace(modLog);
			return modLog;
		}

		protected void PatchOwnNamespace(ModLog log)
		{
			if (_patchesApplied)
			{
				log.LogDebug("Harmony patches are already active; skipping registration.");
				return;
			}
			string text = ((object)this).GetType().Namespace;
			Type[] types = Assembly.GetExecutingAssembly().GetTypes();
			foreach (Type type in types)
			{
				if (type.Namespace == text)
				{
					_harmony.CreateClassProcessor(type).Patch();
				}
			}
			_patchesApplied = true;
			log.LogDebug("Harmony patches were applied for the plugin namespace.");
		}

		protected void ShutdownPlugin()
		{
			if (_patchesApplied)
			{
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
				_patchesApplied = false;
			}
		}
	}
	public sealed class ModLog
	{
		private readonly ManualLogSource _logger;

		public ModLog(ManualLogSource logger)
		{
			_logger = logger;
		}

		public void LogFatal(object message)
		{
			Write((LogLevel)1, message);
		}

		public void LogError(object message)
		{
			Write((LogLevel)2, message);
		}

		public void LogWarning(object message)
		{
			Write((LogLevel)4, message);
		}

		public void LogMessage(object message)
		{
			Write((LogLevel)8, message);
		}

		public void LogInfo(object message)
		{
			Write((LogLevel)16, message);
		}

		public void LogDebug(object message)
		{
			Write((LogLevel)32, message);
		}

		public void Log(LogLevel level, object message)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			Write(level, message);
		}

		private void Write(LogLevel level, object message)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			string arg = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
			_logger.Log(level, (object)$"[{arg}] {message}");
		}
	}
}