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

175 lines
6.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
using VF.Builder;
using VF.Inspector;
using VF.Menu;
using VF.Model;
using VRC.SDK3.Avatars.Components;
using VRC.SDKBase.Editor.BuildPipeline;
using Object = UnityEngine.Object;
namespace VF {
[InitializeOnLoad]
public class PlayModeTrigger : IVRCSDKPostprocessAvatarCallback {
private static double lastRescan = 0;
private static string AboutToUploadKey = "vrcf_vrcAboutToUpload";
public int callbackOrder => int.MaxValue;
public void OnPostprocessAvatar() {
EditorPrefs.SetFloat(AboutToUploadKey, Now());
}
private static float Now() {
return (float)EditorApplication.timeSinceStartup;
}
private static bool activeNow = false;
static PlayModeTrigger() {
SceneManager.sceneLoaded += OnSceneLoaded;
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
EditorApplication.update += () => {
var now = Now();
if (now > lastRescan + 0.5) {
lastRescan = now;
Rescan();
}
};
}
private static void OnPlayModeStateChanged(PlayModeStateChange state) {
var aboutToUploadTime = EditorPrefs.GetFloat(AboutToUploadKey, 0);
var now = Now();
activeNow = false;
var problyUploading = aboutToUploadTime <= now && aboutToUploadTime > Now() - 10;
if (state == PlayModeStateChange.ExitingEditMode) {
if (!problyUploading && PlayModeMenuItem.Get()) {
var rootObjects = GetRootObjects().ToArray();
VRCFPrefabFixer.Fix(rootObjects);
}
} else if (state == PlayModeStateChange.EnteredPlayMode) {
if (problyUploading) {
EditorPrefs.DeleteKey(AboutToUploadKey);
return;
}
EditorPrefs.DeleteKey(AboutToUploadKey);
activeNow = true;
Rescan();
}
}
private static void OnSceneLoaded(Scene scene, LoadSceneMode mode) {
Rescan();
}
// This should absolutely always be false in play mode, but we check just in case
private static bool ContainsAnyPrefabs(GameObject obj) {
foreach (var t in obj.GetComponentsInChildren<Transform>(true)) {
if (PrefabUtility.IsPartOfAnyPrefab(t.gameObject)) {
return true;
}
}
return false;
}
private static void Rescan() {
if (!Application.isPlaying) return;
if (!PlayModeMenuItem.Get()) return;
if (!activeNow) return;
var builder = new VRCFuryBuilder();
var oneChanged = false;
foreach (var root in GetRootObjects()) {
foreach (var avatar in root.GetComponentsInChildren<VRCAvatarDescriptor>(true)) {
if (!avatar.gameObject.activeInHierarchy) continue;
if (ContainsAnyPrefabs(avatar.gameObject)) continue;
if (IsAv3EmulatorClone(avatar.gameObject)) {
// these are av3emulator temp objects. Building on them doesn't work.
continue;
}
if (avatar.gameObject.GetComponentsInChildren<VRCFuryTest>(true).Length > 0) {
continue;
}
if (!VRCFuryBuilder.ShouldRun(avatar.gameObject)) continue;
builder.SafeRun(avatar.gameObject);
VRCFuryBuilder.StripAllVrcfComponents(avatar.gameObject);
oneChanged = true;
}
foreach (var o in root.GetComponentsInChildren<OGBOrifice>(true)) {
if (ContainsAnyPrefabs(o.gameObject)) continue;
OGBOrificeEditor.Bake(o, onlySenders: true);
Object.DestroyImmediate(o);
}
foreach (var o in root.GetComponentsInChildren<OGBPenetrator>(true)) {
if (ContainsAnyPrefabs(o.gameObject)) continue;
OGBPenetratorEditor.Bake(o, onlySenders: true);
Object.DestroyImmediate(o);
}
}
if (oneChanged) {
RestartAv3Emulator();
RestartGestureManager();
}
}
private static IEnumerable<GameObject> GetRootObjects() {
return Enumerable.Range(0, SceneManager.sceneCount)
.Select(SceneManager.GetSceneAt)
.Where(scene => scene.isLoaded)
.SelectMany(scene => scene.GetRootGameObjects());
}
private static bool IsAv3EmulatorClone(GameObject obj) {
return obj.name.Contains("(ShadowClone)") || obj.name.Contains("(MirrorReflection)");
}
private static void RestartAv3Emulator() {
try {
var av3EmulatorType = ReflectionUtils.GetTypeFromAnyAssembly("LyumaAv3Emulator");
if (av3EmulatorType == null) return;
var restartField = av3EmulatorType.GetField("RestartEmulator");
if (restartField == null) return;
var emulators = Object.FindObjectsOfType(av3EmulatorType);
foreach (var emulator in emulators) {
restartField.SetValue(emulator, true);
}
var av3RuntimeType = ReflectionUtils.GetTypeFromAnyAssembly("LyumaAv3Runtime");
foreach (var runtime in Object.FindObjectsOfType(av3RuntimeType)) {
Object.Destroy(runtime);
}
foreach (var root in GetRootObjects()) {
if (IsAv3EmulatorClone(root)) {
Object.DestroyImmediate(root);
}
}
} catch (Exception e) {
Debug.LogException(e);
}
}
private static void RestartGestureManager() {
try {
var gmType = ReflectionUtils.GetTypeFromAnyAssembly("BlackStartX.GestureManager.GestureManager");
if (gmType == null) return;
foreach (var gm in Object.FindObjectsOfType(gmType).OfType<Component>()) {
if (gm.gameObject.activeSelf) {
gm.gameObject.SetActive(false);
gm.gameObject.SetActive(true);
}
if (Selection.activeGameObject == gm.gameObject) {
Selection.activeGameObject = null;
EditorApplication.delayCall += () => Selection.activeGameObject = gm.gameObject;
}
}
} catch (Exception e) {
Debug.LogException(e);
}
}
}
}