332 lines
12 KiB
C#
332 lines
12 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using UnityEditor.SceneManagement;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
|
|
#pragma warning disable IDE0005
|
|
using Serilog = Meryel.Serilog;
|
|
#pragma warning restore IDE0005
|
|
|
|
|
|
#nullable enable
|
|
|
|
|
|
namespace Meryel.UnityCodeAssist.Editor
|
|
{
|
|
|
|
//[InitializeOnLoad]
|
|
public static class Monitor
|
|
{
|
|
private readonly static string tagManagerFilePath;
|
|
private static System.DateTime previousTagManagerLastWrite;
|
|
|
|
private static bool isAppFocused;
|
|
private static bool isAppFocusedOnTagManager;
|
|
|
|
private static int dirtyCounter;
|
|
private static readonly Dictionary<GameObject, int> dirtyDict;
|
|
|
|
static Monitor()
|
|
{
|
|
tagManagerFilePath = CommonTools.GetTagManagerFilePath();
|
|
try
|
|
{
|
|
previousTagManagerLastWrite = System.IO.File.GetLastWriteTime(tagManagerFilePath);
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
Serilog.Log.Debug(ex, "Exception at {Location}", nameof(System.IO.File.GetLastWriteTime));
|
|
}
|
|
dirtyDict = new Dictionary<GameObject, int>();
|
|
dirtyCounter = 0;
|
|
|
|
EditorApplication.hierarchyChanged += OnHierarchyChanged;
|
|
EditorApplication.update += OnUpdate;
|
|
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
|
Undo.postprocessModifications += MyPostprocessModificationsCallback;
|
|
//Undo.undoRedoPerformed += MyUndoCallback;
|
|
Selection.selectionChanged += OnSelectionChanged;
|
|
//EditorSceneManager.sceneOpened += EditorSceneManager_sceneOpened;
|
|
EditorSceneManager.activeSceneChangedInEditMode += EditorSceneManager_activeSceneChangedInEditMode;
|
|
EditorBuildSettings.sceneListChanged += EditorBuildSettings_sceneListChanged;
|
|
|
|
Application.logMessageReceived += Application_logMessageReceived;
|
|
//System.Threading.Tasks.TaskScheduler.UnobservedTaskException +=
|
|
}
|
|
|
|
private static void EditorBuildSettings_sceneListChanged()
|
|
{
|
|
OnSceneListChanged();
|
|
}
|
|
|
|
private static void OnPlayModeStateChanged(PlayModeStateChange playModeStateChange)
|
|
{
|
|
if (playModeStateChange != PlayModeStateChange.EnteredEditMode)
|
|
return;
|
|
|
|
var delayedRequestUpdate = MQTTnetInitializer.Publisher?.DelayedRequestUpdate;
|
|
if (delayedRequestUpdate == null)
|
|
return;
|
|
|
|
var processor = MQTTnetInitializer.Publisher as Synchronizer.Model.IProcessor;
|
|
processor?.Process(delayedRequestUpdate);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Empty method for invoking static class ctor
|
|
/// </summary>
|
|
public static void Bump() { }
|
|
|
|
private static void EditorSceneManager_activeSceneChangedInEditMode(Scene arg0, Scene arg1)
|
|
{
|
|
//Debug.Log("EditorSceneManager_activeSceneChangedInEditMode");
|
|
OnHierarchyChanged();
|
|
}
|
|
|
|
private static void EditorSceneManager_sceneOpened(Scene scene, OpenSceneMode mode)
|
|
{
|
|
Serilog.Log.Debug("Monitor {Event} scene:{Scene} mode:{Mode}", nameof(EditorSceneManager_sceneOpened), scene.name, mode);
|
|
//Debug.Log("EditorSceneManager_sceneOpened");
|
|
OnHierarchyChanged();
|
|
}
|
|
|
|
static void OnUpdate()
|
|
{
|
|
string? currentEditorFocus = null;
|
|
if (Selection.activeObject)
|
|
currentEditorFocus = Selection.activeObject.GetType().ToString();
|
|
|
|
//**-- use this instead? https://learn.microsoft.com/en-us/dotnet/api/system.io.filesystemwatcher?view=net-8.0
|
|
// there is also this approach, but need to check OnUpdate anyways for focus checking, https://github.com/AlkimeeGames/TagLayerTypeGenerator/blob/main/Editor/TypeGenerator.cs#L36
|
|
var currentTagManagerLastWrite = previousTagManagerLastWrite;
|
|
try
|
|
{
|
|
currentTagManagerLastWrite = System.IO.File.GetLastWriteTime(tagManagerFilePath);
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
Serilog.Log.Debug(ex, "Exception at {Location}", nameof(System.IO.File.GetLastWriteTime));
|
|
}
|
|
if (currentTagManagerLastWrite != previousTagManagerLastWrite)
|
|
{
|
|
previousTagManagerLastWrite = currentTagManagerLastWrite;
|
|
OnTagsOrLayersModified();
|
|
}
|
|
else if (currentEditorFocus == "UnityEditor.TagManager")
|
|
{
|
|
// since unity does not commit changes to the file immediately, checking if user is displaying and focusing on tag manager (tags & layers) inspector
|
|
isAppFocusedOnTagManager = true;
|
|
}
|
|
|
|
|
|
if (isAppFocused != UnityEditorInternal.InternalEditorUtility.isApplicationActive)
|
|
{
|
|
isAppFocused = UnityEditorInternal.InternalEditorUtility.isApplicationActive;
|
|
OnOnUnityEditorFocusChanged(isAppFocused);
|
|
//Serilog.Log.Debug("On focus {State}", isAppFocused);
|
|
}
|
|
}
|
|
|
|
static void OnTagsOrLayersModified()
|
|
{
|
|
Serilog.Log.Debug("Monitor {Event}", nameof(OnTagsOrLayersModified));
|
|
|
|
Assister.SendTagsAndLayers();
|
|
}
|
|
|
|
static void OnHierarchyChanged()
|
|
{
|
|
Serilog.Log.Debug("Monitor {Event}", nameof(OnHierarchyChanged));
|
|
|
|
// For requesting active doc's GO
|
|
MQTTnetInitializer.Publisher?.SendHandshake();
|
|
|
|
if (ScriptFinder.GetActiveGameObject(out var activeGO))
|
|
MQTTnetInitializer.Publisher?.SendGameObject(activeGO);
|
|
//Assister.SendTagsAndLayers(); Don't send tags & layers here
|
|
}
|
|
|
|
static void OnSceneListChanged()
|
|
{
|
|
// link below for scenes which are not on build list, but we can skip this for now, because of performance and needlessness (user probably wont work with it if its not on build list)
|
|
// https://gist.github.com/xfleckx/2527f0420fbcc428a8b86be191d8ad96
|
|
|
|
var scenes = EditorBuildSettings.scenes;
|
|
var count = scenes.Length;
|
|
|
|
string[] buildIndicies = new string[count];
|
|
string[] names = new string[count];
|
|
string[] paths = new string[count];
|
|
string[] namesAndPaths = new string[count * 2];
|
|
string[] pathsAndNames = new string[count * 2];
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var scene = scenes[i];
|
|
var path = scene.path;
|
|
var name = System.IO.Path.GetFileNameWithoutExtension(path);
|
|
|
|
buildIndicies[i] = i.ToString();
|
|
names[i] = name;
|
|
paths[i] = path;
|
|
namesAndPaths[i] = name;
|
|
namesAndPaths[i + count] = path;
|
|
pathsAndNames[i] = path;
|
|
pathsAndNames[i + count] = name;
|
|
}
|
|
|
|
MQTTnetInitializer.Publisher?.SendSceneList(names, paths, buildIndicies, namesAndPaths, pathsAndNames);
|
|
}
|
|
|
|
static UndoPropertyModification[] MyPostprocessModificationsCallback(UndoPropertyModification[] modifications)
|
|
{
|
|
Serilog.Log.Debug("Monitor {Event}", nameof(MyPostprocessModificationsCallback));
|
|
|
|
foreach (var modification in modifications)
|
|
{
|
|
var target = modification.currentValue?.target;
|
|
SetDirty(target);
|
|
}
|
|
|
|
// here, you can perform processing of the recorded modifications before returning them
|
|
return modifications;
|
|
}
|
|
|
|
//static void MyUndoCallback()
|
|
//{
|
|
// Serilog.Log.Debug("Monitor {Event}", nameof(MyUndoCallback));
|
|
// // code for the action to take on Undo
|
|
//}
|
|
|
|
static void OnOnUnityEditorFocusChanged(bool isFocused)
|
|
{
|
|
if (!isFocused)
|
|
{
|
|
if (isAppFocusedOnTagManager)
|
|
{
|
|
isAppFocusedOnTagManager = false;
|
|
OnTagsOrLayersModified();
|
|
}
|
|
|
|
OnSelectionChanged();
|
|
FlushAllDirty();
|
|
/*
|
|
Serilog.Log.Debug("exporting {Count} objects", selectedObjects.Count);
|
|
|
|
//**--if too many
|
|
foreach (var obj in selectedObjects)
|
|
{
|
|
if (obj is GameObject go)
|
|
MQTTnetInitializer.Publisher.SendGameObject(go);
|
|
}
|
|
|
|
selectedObjects.Clear();
|
|
*/
|
|
}
|
|
}
|
|
|
|
static void OnSelectionChanged()
|
|
{
|
|
|
|
//**--check order, last selected should be sent last as well
|
|
//**--limit here, what if too many?
|
|
//selectedObjects.UnionWith(Selection.objects);
|
|
foreach(var so in Selection.objects)
|
|
{
|
|
SetDirty(so);
|
|
}
|
|
}
|
|
|
|
public static void SetDirty(Object? obj)
|
|
{
|
|
if (obj == null)
|
|
return;
|
|
else if (obj is GameObject go && go)
|
|
SetDirty(go);
|
|
else if (obj is Component component && component)
|
|
//SetDirty(component.gameObject);
|
|
{
|
|
var componentGo = component.gameObject;
|
|
if (componentGo)
|
|
SetDirty(componentGo);
|
|
}
|
|
//else
|
|
//;//**--scriptable obj
|
|
}
|
|
|
|
public static void SetDirty(GameObject go)
|
|
{
|
|
dirtyCounter++;
|
|
dirtyDict[go] = dirtyCounter;
|
|
}
|
|
|
|
static void FlushAllDirty()
|
|
{
|
|
// Sending order is important, must send them in the same order as they are added to/modified in the collection
|
|
// Using dict instead of hashset because of that. Dict value is used as add/modify order
|
|
|
|
var sortedDict = from entry in dirtyDict orderby entry.Value descending select entry;
|
|
|
|
foreach (var entry in sortedDict)
|
|
{
|
|
var go = entry.Key;
|
|
MQTTnetInitializer.Publisher?.SendGameObject(go);
|
|
}
|
|
|
|
dirtyDict.Clear();
|
|
dirtyCounter = 0;
|
|
}
|
|
|
|
|
|
private static void Application_logMessageReceived(string condition, string stackTrace, LogType type)
|
|
{
|
|
if (type != LogType.Exception && type != LogType.Error && type != LogType.Warning)
|
|
return;
|
|
|
|
if (!stackTrace.Contains("Meryel.UnityCodeAssist.Editor"))
|
|
return;
|
|
|
|
var typeStr = type.ToString();
|
|
|
|
MQTTnetInitializer.Publisher?.SendErrorReport(condition, stackTrace, typeStr);
|
|
}
|
|
|
|
|
|
public static void LazyLoad(string category)
|
|
{
|
|
if (category == "PlayerPrefs")
|
|
{
|
|
Preferences.PreferenceMonitor.InstanceOfPlayerPrefs.Bump();
|
|
}
|
|
else if (category == "EditorPrefs")
|
|
{
|
|
Preferences.PreferenceMonitor.InstanceOfEditorPrefs.Bump();
|
|
}
|
|
else if (category == "InputManager")
|
|
{
|
|
Input.InputManagerMonitor.Instance.Bump();
|
|
}
|
|
else if (category == "AnimationHuman")
|
|
{
|
|
MQTTnetInitializer.Publisher?.SendComponentHumanTrait(HumanTrait.BoneName, HumanTrait.MuscleName);
|
|
}
|
|
else if (category == "Scene")
|
|
{
|
|
OnSceneListChanged();
|
|
}
|
|
else if (category == "ShaderGlobalKeywords")
|
|
{
|
|
MQTTnetInitializer.Publisher?.SendShaderGlobalKeywords();
|
|
}
|
|
else
|
|
{
|
|
Serilog.Log.Error("Invalid LazyLoad category {Category}", category);
|
|
}
|
|
}
|
|
}
|
|
|
|
} |