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 VRMCompanions v0.2.0
BepInEx/plugins/VRMCompanions/VRMCompanions.dll
Decompiled 2 months agousing System; using System.Collections; 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.Logging; using HarmonyLib; using UnityEngine; using ValheimVRM; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("VRMCompanions")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("VRMCompanions")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("8aaf6e91-f870-4b1f-a224-b3b681dbbf1f")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] [BepInPlugin("com.vrm.companions", "VRM Companions", "1.1.1")] public class VRMCompanionsPlugin : BaseUnityPlugin { internal static ManualLogSource Log; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; Harmony.CreateAndPatchAll(typeof(CompanionPatch), (string)null); Harmony.CreateAndPatchAll(typeof(HideArmorPatch), (string)null); Log.LogInfo((object)"VRM Companions loaded"); } } [HarmonyPatch(typeof(VisEquipment), "UpdateEquipmentVisuals")] public class HideArmorPatch { private static void Postfix(VisEquipment __instance) { Humanoid component = ((Component)__instance).GetComponent<Humanoid>(); if (!((Object)(object)component == (Object)null) && ((Object)component).name.Contains("HC_Companion")) { CompanionPatch.ApplyArmorHide(component); } } } [HarmonyPatch(typeof(Humanoid), "Start")] public class CompanionPatch { public class VRMMarker : MonoBehaviour { } private static void Postfix(Humanoid __instance) { if (!((Object)(object)__instance == (Object)null) && ((Object)__instance).name.Contains("HC_Companion")) { LogSafe("✔ Companion detected: " + ((Object)__instance).name); ((MonoBehaviour)__instance).StartCoroutine(ReplaceVRM(__instance)); } } private static IEnumerator ReplaceVRM(Humanoid humanoid) { yield return (object)new WaitForSeconds(0.5f); VisEquipment vis = ((Component)humanoid).GetComponent<VisEquipment>(); if ((Object)(object)vis == (Object)null) { LogSafe("❌ VisEquipment missing"); yield break; } string companionName = null; float timeout = 3f; float timer = 0f; while (timer < timeout) { Character c2 = (Character)(object)humanoid; if (c2 != null && !string.IsNullOrWhiteSpace(c2.m_name) && c2.m_name != "Companion") { companionName = c2.m_name; LogSafe("✔ Name resolved from Character.m_name: " + companionName); break; } timer += 0.2f; yield return (object)new WaitForSeconds(0.2f); } if (string.IsNullOrWhiteSpace(companionName)) { ZNetView nview = ((Component)humanoid).GetComponent<ZNetView>(); if ((Object)(object)nview != (Object)null && nview.IsValid()) { string zdoName = nview.GetZDO().GetString("name", ""); if (!string.IsNullOrWhiteSpace(zdoName) && zdoName != "Companion") { companionName = zdoName; LogSafe("✔ Name from ZDO: " + companionName); } } } if (string.IsNullOrWhiteSpace(companionName) || companionName == "Companion") { LogSafe("❌ Failed to resolve unique companion name, using fallback"); companionName = "Companion"; } Character dbgChar = (Character)(object)humanoid; if (dbgChar != null) { LogSafe("DEBUG final m_name: " + dbgChar.m_name); } ZNetView dbgNview = ((Component)humanoid).GetComponent<ZNetView>(); if ((Object)(object)dbgNview != (Object)null && dbgNview.IsValid()) { LogSafe("DEBUG ZDO name: " + dbgNview.GetZDO().GetString("name", "NULL")); } char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char c in invalidFileNameChars) { companionName = companionName.Replace(c.ToString(), ""); } string folder = Path.Combine(Paths.GameRootPath, "ValheimVRM"); if (!Directory.Exists(folder)) { LogSafe("❌ VRM folder missing: " + folder); yield break; } string path = null; string[] files = Directory.GetFiles(folder, "*.vrm"); foreach (string file in files) { if (Path.GetFileNameWithoutExtension(file).Equals(companionName, StringComparison.OrdinalIgnoreCase)) { path = file; break; } } if (path == null) { LogSafe("❌ No VRM found for '" + companionName + "', trying fallback"); string fallback = Path.Combine(folder, "Companion.vrm"); if (!File.Exists(fallback)) { LogSafe("❌ Fallback VRM missing (Companion.vrm)"); yield break; } path = fallback; } LogSafe("✔ Using VRM: " + path); Dictionary<string, string> cfg = LoadSettingsFile(path); byte[] data = File.ReadAllBytes(path); bool done = false; yield return VRM.ImportVisualAsync(data, path, 1f, (Action<GameObject>)delegate(GameObject loaded) { //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)loaded == (Object)null) { LogSafe("❌ VRM load failed"); done = true; } else { VisEquipment component = ((Component)humanoid).GetComponent<VisEquipment>(); if ((Object)(object)component != (Object)null && (Object)(object)component.m_bodyModel != (Object)null) { ((Renderer)component.m_bodyModel).enabled = false; LogSafe("✔ Vanilla body hidden"); } float result = 1f; float result2 = 1f; float result3 = 1f; if (cfg.TryGetValue("ModelScale", out var value)) { float.TryParse(value, out result); } if (cfg.TryGetValue("SpringBoneStiffness", out var value2)) { float.TryParse(value2, out result2); } if (cfg.TryGetValue("SpringBoneGravityPower", out var value3)) { float.TryParse(value3, out result3); } loaded.transform.localScale = Vector3.one * result; Animator componentInChildren = ((Component)humanoid).GetComponentInChildren<Animator>(); VrmSettingsContainer settings = Settings.GetSettings(companionName); loaded.transform.SetParent(((Component)componentInChildren).transform.parent, false); loaded.transform.localPosition = Vector3.zero; loaded.transform.localRotation = Quaternion.identity; loaded.AddComponent<VRMMarker>(); LogSafe("✔ VRM attached to companion"); VRMAnimationSync val = loaded.GetComponent<VRMAnimationSync>(); if ((Object)(object)val == (Object)null) { val = loaded.AddComponent<VRMAnimationSync>(); } val.Setup(componentInChildren, settings, false); componentInChildren.cullingMode = (AnimatorCullingMode)0; componentInChildren.updateMode = (AnimatorUpdateMode)0; ForceSpringBoneUpdate(loaded, result2, result3); ApplyArmorHide(humanoid); done = true; } }); while (!done) { yield return null; } } public static void ApplyArmorHide(Humanoid humanoid) { VisEquipment component = ((Component)humanoid).GetComponent<VisEquipment>(); if ((Object)(object)component != (Object)null) { component.SetHelmetItem(""); component.SetShoulderItem("", 0); component.SetChestItem(""); component.SetLegItem(""); component.SetUtilityItem(""); component.SetHairItem(""); component.SetBeardItem(""); } Renderer[] componentsInChildren = ((Component)humanoid).GetComponentsInChildren<Renderer>(true); foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).GetComponentInParent<VRMMarker>() != (Object)null)) { string text = ((Object)((Component)val).gameObject).name.ToLower(); if (text.Contains("armor") || text.Contains("helmet") || text.Contains("helm") || text.Contains("chest") || text.Contains("shoulder") || text.Contains("cape") || text.Contains("leg")) { val.enabled = false; } } } } private static void ForceSpringBoneUpdate(GameObject vrmRoot, float springStiffness, float springGravity) { MonoBehaviour[] componentsInChildren = vrmRoot.GetComponentsInChildren<MonoBehaviour>(true); foreach (MonoBehaviour val in componentsInChildren) { Type type = ((object)val).GetType(); if (type.Name.Contains("VRMSpringBone")) { FieldInfo field = type.GetField("m_stiffnessForce"); FieldInfo field2 = type.GetField("m_gravityPower"); if (field != null) { float num = (float)field.GetValue(val); field.SetValue(val, num * Mathf.Clamp(springStiffness, 0.1f, 5f)); } if (field2 != null) { float num2 = (float)field2.GetValue(val); field2.SetValue(val, num2 * Mathf.Clamp(springGravity, 0.1f, 5f)); } } } } private static Dictionary<string, string> LoadSettingsFile(string vrmPath) { string text = Path.Combine(Path.GetDirectoryName(vrmPath), "settings_" + Path.GetFileNameWithoutExtension(vrmPath) + ".txt"); Dictionary<string, string> dictionary = new Dictionary<string, string>(); if (!File.Exists(text)) { return dictionary; } string[] array = File.ReadAllLines(text); foreach (string text2 in array) { if (!string.IsNullOrWhiteSpace(text2) && !text2.StartsWith("//") && text2.Contains("=")) { string[] array2 = text2.Split(new char[1] { '=' }); if (array2.Length == 2) { dictionary[array2[0].Trim()] = array2[1].Trim(); } } } LogSafe("✔ Loaded VRM settings: " + text); return dictionary; } private static void LogSafe(string msg) { if (VRMCompanionsPlugin.Log != null) { VRMCompanionsPlugin.Log.LogInfo((object)msg); } else { Debug.Log((object)msg); } } }