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 RandomYggdrasil v1.2.0
RandomYggdrasil.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using MushroomMods; using MushroomSync; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("RandomYggdrasil")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("RandomYggdrasil")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("f50fb28c-a3c2-4aed-8241-53d463a14ab0")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace RandomYggdrasil { [BepInPlugin("gonfreecss.RandomYggdrasilMod", "RandomYggdrasil", "1.2.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInProcess("valheim.exe")] [BepInProcess("valheim_server.exe")] public class RandomYggdrasilMod : BaseUnityPlugin { [HarmonyPatch(typeof(ZNetScene))] private class RandomizeYggdrasil_Patch { [HarmonyPatch("Awake")] [HarmonyPostfix] private static void RandomizeYggdrasil() { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) Debug.Log((object)"Starting location randomizer for Yggdrassil"); string worldIdentifier = GetWorldIdentifier(); int num = -1; if (worldIdentifier != null) { num = GetOrCreateRotation(worldIdentifier); } GameObject val = FindYggdrasilBranch(); if ((Object)(object)val == (Object)null) { if (worldIdentifier != null && num >= 0) { Debug.Log((object)$"RandomYggdrasil: Stored rotation {num} degrees for world '{worldIdentifier}' (no scene object on this instance)"); } } else if (num >= 0) { val.transform.rotation = Quaternion.Euler(0f, (float)num, 0f); } else if (worldIdentifier == null) { num = new Random().Next(360); val.transform.rotation = Quaternion.Euler(0f, (float)num, 0f); Debug.Log((object)$"RandomYggdrasil: No world info available, using one-time random rotation of {num} degrees"); } else { Debug.Log((object)"RandomYggdrasil: Waiting for server config sync before applying rotation"); } } } internal const string PluginId = "gonfreecss.RandomYggdrasilMod"; internal const string PluginName = "RandomYggdrasil"; internal const string PluginVersion = "1.2.0"; private const string RotationSection = "WorldRotations"; private const string ConfigFileName = "RandomYggdrasil.cfg"; private const int RotationNotGenerated = -1; private const string BranchObjectName = "YggdrasilBranch"; private const string VanillaBranchPath = "_GameMain/_Environment/YggdrasilBranch"; private readonly Harmony harmony = new Harmony("gonfreecss.RandomYggdrasilMod"); private static ConfigFile modConfig; private static ConfigFile alternateConfig; private static bool loggedMissingBranch; private static readonly Dictionary<string, ConfigEntry<int>> rotationEntries = new Dictionary<string, ConfigEntry<int>>(); private void Awake() { InitializeConfigFiles(); modConfig.Bind<bool>("General", "LockConfiguration", true, "If on, the server owns world rotations and connected clients use the server's values."); RotationSync.Initialize(((BaseUnityPlugin)this).Logger); int num = PatchIsolation.PatchAllIsolated(harmony, typeof(RandomYggdrasilMod).Assembly, ((BaseUnityPlugin)this).Logger); if (num > 0) { ((BaseUnityPlugin)this).Logger.LogWarning((object)$"RandomYggdrasil: {num} patch class(es) skipped - see the errors above."); } Debug.Log((object)("RandomYggdrasil: Loaded (" + (IsDedicatedServer() ? "dedicated server" : "client") + "), config at '" + modConfig.ConfigFilePath + "'")); } private static bool IsDedicatedServer() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 if ((int)SystemInfo.graphicsDeviceType == 4) { return true; } return Process.GetCurrentProcess().ProcessName.IndexOf("valheim_server", StringComparison.OrdinalIgnoreCase) >= 0; } private static string GetClientConfigPath() { return Path.Combine(Paths.ConfigPath, "RandomYggdrasil.cfg"); } private static string GetServerConfigPath() { if (Directory.Exists("/config")) { return Path.Combine("/config", "bepinex", "RandomYggdrasil.cfg"); } return Path.Combine(Path.GetDirectoryName(Paths.BepInExRootPath), "config", "bepinex", "RandomYggdrasil.cfg"); } private static string GetPrimaryConfigPath() { if (!IsDedicatedServer()) { return GetClientConfigPath(); } return GetServerConfigPath(); } private static string GetAlternateConfigPath() { if (!IsDedicatedServer()) { return GetServerConfigPath(); } return GetClientConfigPath(); } private static void EnsureConfigDirectoryExists(string configPath) { string directoryName = Path.GetDirectoryName(configPath); if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } } private static void InitializeConfigFiles() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown string primaryConfigPath = GetPrimaryConfigPath(); string alternateConfigPath = GetAlternateConfigPath(); string text = (File.Exists(primaryConfigPath) ? primaryConfigPath : ((!File.Exists(alternateConfigPath)) ? primaryConfigPath : alternateConfigPath)); EnsureConfigDirectoryExists(text); modConfig = new ConfigFile(text, true); alternateConfig = null; if (File.Exists(alternateConfigPath) && !string.Equals(text, alternateConfigPath, StringComparison.OrdinalIgnoreCase)) { alternateConfig = new ConfigFile(alternateConfigPath, true); } } private static bool TryGetRotationFromConfig(ConfigFile config, string worldIdentifier, out int degrees) { ConfigEntry<int> val = config.Bind<int>("WorldRotations", worldIdentifier, -1, "Y-axis rotation (in degrees, 0-359) of Yggdrasil for this world. Generated once per world on the server; delete this entry to re-roll."); if (val.Value >= 0 && val.Value < 360) { degrees = val.Value; return true; } degrees = -1; return false; } private static bool TryImportRotationFromAlternateConfig(string worldIdentifier, ConfigEntry<int> entry) { if (alternateConfig == null) { return false; } if (!TryGetRotationFromConfig(alternateConfig, worldIdentifier, out var degrees)) { return false; } entry.Value = degrees; modConfig.Save(); Debug.Log((object)$"RandomYggdrasil: Imported rotation of {degrees} degrees for world '{worldIdentifier}' from alternate config"); return true; } private static string GetWorldIdentifier() { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || instance.GetWorldName() == null) { return null; } string text = $"{instance.GetWorldName()}_{instance.GetWorldUID()}"; string text2 = "=\n\t\\\"'[]"; for (int i = 0; i < text2.Length; i++) { text = text.Replace(text2[i].ToString(), ""); } return text; } private static ConfigEntry<int> GetRotationEntry(string worldIdentifier) { if (!rotationEntries.TryGetValue(worldIdentifier, out var value)) { value = modConfig.Bind<int>("WorldRotations", worldIdentifier, -1, "Y-axis rotation (in degrees, 0-359) of Yggdrasil for this world. Generated once per world on the server; delete this entry to re-roll."); rotationEntries[worldIdentifier] = value; } return value; } private static int GetOrCreateRotation(string worldIdentifier) { if (!RotationSync.IsServerAuthority() && RotationSync.TryGetSyncedRotation(worldIdentifier, out var degrees)) { Debug.Log((object)$"RandomYggdrasil: Using server rotation of {degrees} degrees for world '{worldIdentifier}'"); return degrees; } ConfigEntry<int> rotationEntry = GetRotationEntry(worldIdentifier); if (rotationEntry.Value >= 0 && rotationEntry.Value < 360) { Debug.Log((object)$"RandomYggdrasil: Using rotation of {rotationEntry.Value} degrees for world '{worldIdentifier}'"); return rotationEntry.Value; } if (TryImportRotationFromAlternateConfig(worldIdentifier, rotationEntry)) { RotationSync.Broadcast(); return rotationEntry.Value; } if (RotationSync.IsServerAuthority()) { rotationEntry.Value = new Random().Next(360); modConfig.Save(); RotationSync.Broadcast(); Debug.Log((object)$"RandomYggdrasil: Generated new rotation of {rotationEntry.Value} degrees for world '{worldIdentifier}'"); return rotationEntry.Value; } if (!RotationSync.HasReceivedSync) { return -1; } Debug.LogWarning((object)("RandomYggdrasil: No rotation received from server for world '" + worldIdentifier + "', leaving Yggdrasil at default orientation")); return -1; } internal static void EnsureCurrentWorldRotation() { if (RotationSync.IsServerAuthority()) { string worldIdentifier = GetWorldIdentifier(); if (worldIdentifier != null) { GetOrCreateRotation(worldIdentifier); } } } internal static Dictionary<string, int> GetRotationSnapshot() { Dictionary<string, int> dictionary = new Dictionary<string, int>(); foreach (KeyValuePair<string, ConfigEntry<int>> rotationEntry in rotationEntries) { int value = rotationEntry.Value.Value; if (value >= 0 && value < 360) { dictionary[rotationEntry.Key] = value; } } return dictionary; } private static GameObject FindYggdrasilBranch() { GameObject obj = GameObject.Find("YggdrasilBranch"); if ((Object)(object)obj == (Object)null) { LogMissingBranchOnce(); } return obj; } private static void LogMissingBranchOnce() { //IL_000e: 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_0068: 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) if (!loggedMissingBranch) { loggedMissingBranch = true; Scene activeScene = SceneManager.GetActiveScene(); string text = (IsDedicatedServer() ? "dedicated server" : "client"); Debug.LogWarning((object)("RandomYggdrasil: no active GameObject named 'YggdrasilBranch' in scene '" + ((Scene)(ref activeScene)).name + "' (" + text + "). Valheim 1.0.7 keeps it at '_GameMain/_Environment/YggdrasilBranch'. Yggdrasil stays at its default orientation until the name below is corrected in the source.")); Debug.LogWarning((object)("RandomYggdrasil: scene roots: " + string.Join(", ", GetRootObjectNames(activeScene)))); List<string> list = FindRenameCandidates(activeScene); Debug.LogWarning((object)((list.Count == 0) ? "RandomYggdrasil: no object in this scene has 'ygg' in its name, so the branch was removed rather than renamed." : ("RandomYggdrasil: scene objects with 'ygg' in the name - a rename shows up here: " + string.Join(", ", list)))); } } private static string[] GetRootObjectNames(Scene scene) { GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects(); string[] array = new string[rootGameObjects.Length]; for (int i = 0; i < rootGameObjects.Length; i++) { array[i] = ((Object)rootGameObjects[i]).name; } return array; } private static List<string> FindRenameCandidates(Scene scene) { //IL_001f: 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_0035: Unknown result type (might be due to invalid IL or missing references) List<string> list = new List<string>(); GameObject[] array = Resources.FindObjectsOfTypeAll<GameObject>(); foreach (GameObject val in array) { if (list.Count >= 20) { break; } Scene scene2 = val.scene; if (((Scene)(ref scene2)).IsValid() && !(val.scene != scene) && ((Object)val).name.IndexOf("ygg", StringComparison.OrdinalIgnoreCase) >= 0) { list.Add(GetHierarchyPath(val) + (val.activeInHierarchy ? "" : " (inactive)")); } } return list; } private static string GetHierarchyPath(GameObject gameObject) { string text = ((Object)gameObject).name; Transform parent = gameObject.transform.parent; while ((Object)(object)parent != (Object)null) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } internal static void TryApplyStoredRotation() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) string worldIdentifier = GetWorldIdentifier(); if (worldIdentifier == null) { return; } int orCreateRotation = GetOrCreateRotation(worldIdentifier); if (orCreateRotation >= 0) { GameObject val = FindYggdrasilBranch(); if (!((Object)(object)val == (Object)null)) { val.transform.rotation = Quaternion.Euler(0f, (float)orCreateRotation, 0f); Debug.Log((object)$"RandomYggdrasil: Applied rotation of {orCreateRotation} degrees after config sync"); } } } } internal static class RotationSync { private const int DegreesInCircle = 360; private static readonly Dictionary<string, int> ClientSyncedRotations = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private static SyncChannel _channel; internal static bool HasReceivedSync { get { if (_channel != null) { return _channel.ClientSyncActive; } return false; } } internal static bool IsServerAuthority() { return SyncChannel.IsServerAuthority(); } internal static bool TryGetSyncedRotation(string worldIdentifier, out int degrees) { if (HasReceivedSync && ClientSyncedRotations.TryGetValue(worldIdentifier, out degrees)) { return IsValid(degrees); } degrees = -1; return false; } internal static void Initialize(ManualLogSource log) { _channel = SyncChannel.Create("RandomYggdrasil.Rotations", "1.2.0", log); _channel.WritePayload = WritePayload; _channel.ReadPayload = ReadPayload; _channel.Cleared = OnCleared; _channel.Start(); } internal static void Broadcast() { if (_channel != null) { _channel.Broadcast(); } } private static bool IsValid(int degrees) { if (degrees >= 0) { return degrees < 360; } return false; } private static void WritePayload(ZPackage payload) { RandomYggdrasilMod.EnsureCurrentWorldRotation(); Dictionary<string, int> rotationSnapshot = RandomYggdrasilMod.GetRotationSnapshot(); payload.Write(rotationSnapshot.Count); foreach (KeyValuePair<string, int> item in rotationSnapshot) { payload.Write(item.Key); payload.Write(item.Value); } } private static void ReadPayload(ZPackage payload) { int num = SyncChannel.ReadCount(payload); ClientSyncedRotations.Clear(); for (int i = 0; i < num; i++) { string text = payload.ReadString(); int num2 = payload.ReadInt(); if (IsValid(num2) && !string.IsNullOrEmpty(text)) { ClientSyncedRotations[text] = num2; } } Debug.Log((object)("RandomYggdrasil: received " + ClientSyncedRotations.Count + " world rotation(s) from server")); RandomYggdrasilMod.TryApplyStoredRotation(); } private static void OnCleared(string reason) { ClientSyncedRotations.Clear(); Debug.Log((object)("RandomYggdrasil: server rotations dropped (" + reason + ")")); } } } namespace MushroomMods { internal static class PatchIsolation { internal static int PatchAllIsolated(Harmony harmony, Assembly assembly, ManualLogSource log) { int num = 0; Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(assembly); foreach (Type type in typesFromAssembly) { try { harmony.CreateClassProcessor(type).Patch(); } catch (Exception ex) { num++; log.LogError((object)("Harmony patch class " + type.FullName + " was skipped and the rest of " + harmony.Id + " is still active. " + ex)); } } return num; } } }