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.1
IronLabs.ScatteredVikings.dll
Decompiled 2 months 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 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}"); } } }