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 ScatteredVikings v1.0.0
IronLabs.ScatteredVikings.dll
Decompiled a day agousing 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 Splatform; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [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.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] 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_0015: 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_0028: 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) { if (!profile.HaveCustomSpawnPoint()) { if (!respawnAfterDeath) { return profile.HaveLogoutPoint(); } return false; } return true; } private static bool TryPrepareSpawnPoint(ref Vector3 point) { //IL_0006: 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_002e: 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)) { ModLog.LogWarning($"No ground was found at scattered spawn {point}; waiting for terrain."); return false; } point.y = num + 0.25f; return true; } } internal static class ModLog { private static ManualLogSource _logger; private static string _pluginName; internal static void Initialize(ManualLogSource logger, string pluginName) { _logger = logger; _pluginName = pluginName; } internal static void Clear() { _logger = null; _pluginName = null; } internal static void LogDebug(object message) { Write((LogLevel)32, message); } internal static void LogInfo(object message) { Write((LogLevel)16, message); } internal static void LogWarning(object message) { Write((LogLevel)4, message); } private static void Write(LogLevel level, object message) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) ManualLogSource logger = _logger; if (logger != null) { logger.Log(level, message); } } } [BepInPlugin("IronLabs.ScatteredVikings", "IronLabs.ScatteredVikings", "1.0.0")] public sealed class ScatteredVikingsPlugin : BaseUnityPlugin { private const string PluginGuid = "IronLabs.ScatteredVikings"; private const string PluginName = "IronLabs.ScatteredVikings"; private const string PluginVersion = "1.0.0"; private readonly Harmony _harmony = new Harmony("IronLabs.ScatteredVikings"); private static bool _patchesApplied; private void Awake() { ModLog.Initialize(((BaseUnityPlugin)this).Logger, "IronLabs.ScatteredVikings"); PatchOwnNamespace(); ModLog.LogInfo("IronLabs.ScatteredVikings 1.0.0 is loaded."); } private void PatchOwnNamespace() { if (_patchesApplied) { ModLog.LogDebug("Harmony patches are already active; skipping registration."); return; } string text = typeof(ScatteredVikingsPlugin).Namespace; Type[] types = Assembly.GetExecutingAssembly().GetTypes(); foreach (Type type in types) { if (type.Namespace == text) { _harmony.CreateClassProcessor(type).Patch(); } } _patchesApplied = true; ModLog.LogDebug("Harmony patches were applied for the plugin namespace."); } private void OnDestroy() { SpawnPointSelector.Clear(); if (_patchesApplied) { _harmony.UnpatchSelf(); _patchesApplied = false; } ModLog.Clear(); } } 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_001e: 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_002b: 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_0051: 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) 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); ModLog.LogDebug($"Selected scattered spawn {point} for player {profile.GetPlayerID()}."); return point; } internal static void Clear() { SpawnPointStore.ClearCache(); } private static Vector3 FindSpawnPoint(PlayerProfile profile) { //IL_0011: 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_0017: 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) //IL_0026: 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_003c: 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; } } ModLog.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(); return (((int)worldUID ^ (int)(worldUID >> 32)) * 397) ^ (int)playerID ^ (int)(playerID >> 32); } private static Vector3 CreateCandidate(Random random) { //IL_0045: 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_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) //IL_000e: Invalid comparison between Unknown and I4 //IL_0011: 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_0018: Invalid comparison between Unknown and I4 //IL_001d: 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) WorldGenerator instance = WorldGenerator.instance; if ((int)instance.GetBiome(candidate) != 1 || (int)instance.GetBiomeArea(candidate) != 2) { return false; } return instance.GetHeight(candidate.x, candidate.z) >= ZoneSystem.instance.m_waterLevel + 2f; } } [HarmonyPatch(typeof(Player), "OnSpawned")] internal static class PlayerOnSpawnedPatch { private static void Postfix(Player __instance) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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) 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)) { long playerID = Game.instance.GetPlayerProfile().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); ModLog.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_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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_001b: 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_0020: Unknown result type (might be due to invalid IL or missing references) SpawnRecord value; bool flag = GetPlayerSpawns(playerId).TryGetValue(worldId, out value); point = (flag ? value.Point : Vector3.zero); return flag; } internal static void Set(long playerId, long worldId, Vector3 point) { //IL_0009: 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) { if (GetPlayerSpawns(playerId).TryGetValue(worldId, out var value)) { return !value.PinCreated; } return false; } internal static void MarkPinCreated(long playerId, long worldId) { //IL_0016: 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); ModLog.LogDebug($"Loaded {dictionary.Count} scattered spawn(s) for player {playerId}."); } catch (Exception ex) { ModLog.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 { ModLog.LogWarning("Ignored malformed scattered spawn entry: " + lines[i]); } } } private static bool TryParse(string line, out long worldId, out SpawnRecord record) { //IL_008d: 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); ModLog.LogDebug($"Saved {spawns.Count} scattered spawn(s) for player {playerId}."); } catch (Exception ex) { ModLog.LogWarning("Failed to save scattered spawns to " + path + ": " + ex.Message); DeleteTemporaryFile(text); } } private static string[] CreateLines(Dictionary<long, SpawnRecord> spawns) { //IL_0048: 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_006e: 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"); } }