avatar/Assets/VRCFury/Scripts/Editor/VF/VRCFuryVRCPatch.cs

147 lines
5.9 KiB
C#

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<GameObject>)runField.GetValue(null);
runField.SetValue(null, Fix + run);
runField = sdkBuilder.GetField("RunExportAndTestAvatarBlueprint",
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
run = (Action<GameObject>)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<string>(list);
foreach (var type in GetVRCFuryComponentTypes()) {
updated.Add(type.FullName);
}
return updated.ToArray();
}
public static List<Type> GetVRCFuryComponentTypes() {
var list = new List<Type> {
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<VRCAvatarDescriptor>()) {
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<VRCAvatarDescriptor>();
var normalizedNames = new HashSet<string>();
var fullNames = new HashSet<string>();
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;
}
}
}