using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using UnityEditor; using UnityEngine; using VRC.SDK3.Avatars.Components; using VRC.SDKBase.Editor.BuildPipeline; using VF.Builder; using VF.Inspector; using VF.Model; using VRC.Core; using VRC.SDK3.Dynamics.Contact.Components; using Object = UnityEngine.Object; namespace VF { [InitializeOnLoad] public class Startup { static Startup() { Exception preprocessPatchEx = null; try { var validation = ReflectionUtils.GetTypeFromAnyAssembly("VRC.SDKBase.Validation.AvatarValidation"); var whitelistField = validation.GetField("ComponentTypeWhiteListCommon", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); var whitelist = whitelistField.GetValue(null); whitelistField.SetValue(null, UpdateComponentList((string[])whitelist)); } catch (Exception e) { preprocessPatchEx = e; } try { var validation = ReflectionUtils.GetTypeFromAnyAssembly("VRC.SDK3.Validation.AvatarValidation"); var whitelistField = validation.GetField("ComponentTypeWhiteListCommon", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); var whitelist = whitelistField.GetValue(null); whitelistField.SetValue(null, UpdateComponentList((string[])whitelist)); } catch (Exception) { if (preprocessPatchEx != null) { Debug.LogError(new Exception("VRCFury preprocess patch failed", preprocessPatchEx)); } } try { var sdkBuilder = ReflectionUtils.GetTypeFromAnyAssembly("VRC.SDKBase.Editor.VRC_SdkBuilder"); var runField = sdkBuilder.GetField("RunExportAndUploadAvatarBlueprint", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); void Fix(GameObject obj) => VRCFPrefabFixer.Fix(new[] { obj }); var run = (Action)runField.GetValue(null); runField.SetValue(null, Fix + run); runField = sdkBuilder.GetField("RunExportAndTestAvatarBlueprint", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); run = (Action)runField.GetValue(null); runField.SetValue(null, Fix + run); } catch (Exception e) { Debug.LogError(new Exception("VRCFury prefab fix patch failed", e)); } } private static string[] UpdateComponentList(string[] list) { var updated = new List(list); foreach (var type in GetVRCFuryComponentTypes()) { updated.Add(type.FullName); } return updated.ToArray(); } public static List GetVRCFuryComponentTypes() { var list = new List { typeof(VRCFury), typeof(VRCFuryTest), typeof(OGBOrifice), typeof(OGBPenetrator), typeof(VRCFGlobalCollider), }; var d4k3 = ReflectionUtils.GetTypeFromAnyAssembly("d4rkAvatarOptimizer"); if (d4k3 != null) list.Add(d4k3); return list; } } public class VRCFuryVRCPatch : IVRCSDKPreprocessAvatarCallback { public int callbackOrder => 0; public bool OnPreprocessAvatar(GameObject vrcCloneObject) { // When vrchat is uploading our avatar, we are actually operating on a clone of the avatar object. // Let's get a reference to the original avatar, so we can apply our changes to it as well. if (!vrcCloneObject.name.EndsWith("(Clone)")) { Debug.LogError("Seems that we're not operating on a vrc avatar clone? Bailing. Please report this to VRCFury."); return false; } // Clean up junk from the original avatar, in case it still has junk from way back when we used to // dirty the original GameObject original = null; { foreach (var desc in Object.FindObjectsOfType()) { if (desc.gameObject.name + "(Clone)" == vrcCloneObject.name && desc.gameObject.activeInHierarchy) { original = desc.gameObject; break; } } } var builder = new VRCFuryBuilder(); var vrcFurySuccess = builder.SafeRun(vrcCloneObject, original); if (!vrcFurySuccess) return false; // Try to detect conflicting parameter names that break OSC try { var avatar = vrcCloneObject.GetComponent(); var normalizedNames = new HashSet(); var fullNames = new HashSet(); foreach (var c in VRCAvatarUtils.GetAllControllers(avatar).Select(c => c.Item1).Where(c => c != null)) { foreach (var param in c.parameters) { if (!fullNames.Contains(param.name)) { var normalized = param.name.Replace(' ', '_'); if (normalizedNames.Contains(normalized)) { EditorUtility.DisplayDialog("VRCFury Error", "Your avatar controllers contain multiple parameters with the same normalized name: " + normalized + " This will cause OSC and various other vrchat functions to fail. Please fix it.", "Ok"); return false; } fullNames.Add(param.name); normalizedNames.Add(normalized); } } } } catch (Exception e) { Debug.LogException(e); } // Make absolutely positively certain that we've removed every non-standard component from the avatar // before it gets uploaded VRCFuryBuilder.StripAllVrcfComponents(vrcCloneObject); return true; } } }