first commit

This commit is contained in:
Stefano Rossi 2025-07-12 18:59:18 +02:00
commit aa44d3ad70
Signed by: chadmin
GPG key ID: 9EFA2130646BC893
1585 changed files with 277994 additions and 0 deletions

View file

@ -0,0 +1,65 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
#nullable enable
namespace Meryel.UnityCodeAssist.Editor
{
public class AboutWindow : EditorWindow
{
GUIStyle? styleLabel;
public static void Display()
{
// Get existing open window or if none, make a new one:
var window = GetWindow<AboutWindow>();
window.Show();
Serilog.Log.Debug("Displaying about window");
NetMQInitializer.Publisher?.SendAnalyticsEvent("Gui", "AboutWindow_Display");
}
private void OnEnable()
{
//**--icon
//var icon = AssetDatabase.LoadAssetAtPath<Texture>("Assets/Sprites/Gear.png");
//titleContent = new GUIContent("Code Assist", icon);
titleContent = new GUIContent("Code Assist About");
}
private void OnGUI()
{
styleLabel ??= new GUIStyle(GUI.skin.label)
{
wordWrap = true,
alignment = TextAnchor.MiddleLeft,
};
EditorGUILayout.LabelField($"Version number: {Assister.Version}", styleLabel, GUILayout.ExpandWidth(true));
#if MERYEL_UCA_LITE_VERSION
EditorGUILayout.LabelField($"License type: Lite", styleLabel, GUILayout.ExpandWidth(true));
#else // MERYEL_UCA_LITE_VERSION
EditorGUILayout.LabelField($"License type: Full", styleLabel, GUILayout.ExpandWidth(true));
#endif // MERYEL_UCA_LITE_VERSION
if (GUILayout.Button("View changelog"))
{
Application.OpenURL("https://unitycodeassist.netlify.app/changelog");
}
if (GUILayout.Button("View third party notices"))
{
Application.OpenURL("https://unitycodeassist.netlify.app/thirdpartynotices");
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b2aee5311fd620c4393b0ba06cb90476
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,263 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
#nullable enable
namespace Meryel.UnityCodeAssist.Editor
{
public class Assister
{
public const string Version = "1.1.9";
#if MERYEL_UCA_LITE_VERSION
public const string Title = "Code Assist Lite";
#else
public const string Title = "Code Assist";
#endif
[MenuItem("Tools/" + Title + "/Status", false, 1)]
static void DisplayStatusWindow()
{
StatusWindow.Display();
}
[MenuItem("Tools/" + Title + "/Synchronize", false, 2)]
static void Sync()
{
EditorCoroutines.EditorCoroutineUtility.StartCoroutine(SyncAux(), NetMQInitializer.Publisher);
//NetMQInitializer.Publisher.SendConnect();
//Serilog.Log.Information("Code Assist is looking for more IDEs to connect to...");
NetMQInitializer.Publisher?.SendAnalyticsEvent("Gui", "Synchronize_MenuItem");
}
[MenuItem("Tools/" + Title + "/Report error", false, 51)]
static void DisplayFeedbackWindow()
{
FeedbackWindow.Display();
}
[MenuItem("Tools/" + Title + "/About", false, 52)]
static void DisplayAboutWindow()
{
AboutWindow.Display();
}
#if MERYEL_UCA_LITE_VERSION
[MenuItem("Tools/" + Title + "/Compare versions", false, 31)]
static void CompareVersions()
{
Application.OpenURL("http://unitycodeassist.netlify.app/compare");
NetMQInitializer.Publisher?.SendAnalyticsEvent("Gui", "CompareVersions_MenuItem");
}
[MenuItem("Tools/" + Title + "/Get full version", false, 32)]
static void GetFullVersion()
{
Application.OpenURL("http://u3d.as/2N2H");
NetMQInitializer.Publisher?.SendAnalyticsEvent("Gui", "FullVersion_MenuItem");
}
#endif // MERYEL_UCA_LITE_VERSION
static IEnumerator SyncAux()
{
var clientCount = NetMQInitializer.Publisher?.clients.Count ?? 0;
NetMQInitializer.Publisher?.SendConnect();
Serilog.Log.Information("Code Assist is looking for more IDEs to connect to...");
//yield return new WaitForSeconds(3);
yield return new EditorCoroutines.EditorWaitForSeconds(3);
var newClientCount = NetMQInitializer.Publisher?.clients.Count ?? 0;
var dif = newClientCount - clientCount;
if (dif <= 0)
Serilog.Log.Information("Code Assist couldn't find any new IDE to connect to.");
else
Serilog.Log.Information("Code Assist is connected to {Dif} new IDE(s).", dif);
}
#if MERYEL_DEBUG
[MenuItem("Code Assist/Binary2Text")]
static void Binary2Text()
{
var filePath = CommonTools.GetInputManagerFilePath();
var hash = Input.UnityInputManager.GetMD5Hash(filePath);
var convertedPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), $"UCA_IM_{hash}.txt");
var b = new Input.Binary2TextExec();
b.Exec(filePath, convertedPath, detailed: false, largeBinaryHashOnly: false, hexFloat: false);
}
[MenuItem("Code Assist/Bump InputManager")]
static void BumpInputManager()
{
Input.InputManagerMonitor.Instance.Bump();
}
[MenuItem("Code Assist/Layer Check")]
static void UpdateLayers()
{
var names = UnityEditorInternal.InternalEditorUtility.layers;
var indices = names.Select(l => LayerMask.NameToLayer(l).ToString()).ToArray();
NetMQInitializer.Publisher?.SendLayers(indices, names);
var sls = SortingLayer.layers;
var sortingNames = sls.Select(sl => sl.name).ToArray();
var sortingIds = sls.Select(sl => sl.id.ToString()).ToArray();
var sortingValues = sls.Select(sl => sl.value.ToString()).ToArray();
NetMQInitializer.Publisher?.SendSortingLayers(sortingNames, sortingIds, sortingValues);
/*
for (var i = 0; i < 32; i++)
{
var name = LayerMask.LayerToName(i);
if (!string.IsNullOrEmpty(name))
{
Debug.Log(i + ":" + name);
}
}
if (ScriptFinder.FindGameObjectOfType("Deneme", out var go))
NetMQInitializer.Publisher.SendGameObject(go);
*/
}
[MenuItem("Code Assist/Tag Check")]
static void UpdateTags()
{
Serilog.Log.Debug("Listing tags {Count}", UnityEditorInternal.InternalEditorUtility.tags.Length);
foreach (var tag in UnityEditorInternal.InternalEditorUtility.tags)
{
if (!string.IsNullOrEmpty(tag))
{
Serilog.Log.Debug("{Tag}", tag);
}
}
NetMQInitializer.Publisher?.SendTags(UnityEditorInternal.InternalEditorUtility.tags);
}
[MenuItem("Code Assist/GO Check")]
static void TestGO()
{
var go = GameObject.Find("Deneme");
//var go = MonoBehaviour.FindObjectOfType<Deneme>().gameObject;
NetMQInitializer.Publisher?.SendGameObject(go);
}
[MenuItem("Code Assist/Undo Records Test")]
static void UndoTest()
{
var undos = new List<string>();
var redos = new List<string>();
var type = typeof(Undo);
System.Reflection.MethodInfo dynMethod = type.GetMethod("GetRecords",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
dynMethod.Invoke(null, new object[] { undos, redos });
Serilog.Log.Debug("undos:{UndoCount},redos:{RedoCount}", undos.Count, redos.Count);
var last = undos.LastOrDefault();
if (last != null)
{
Serilog.Log.Debug("last:{Last}", last);
Serilog.Log.Debug("group:{UndoCurrentGroup},{UndoCurrentGroupName}",
Undo.GetCurrentGroup(), Undo.GetCurrentGroupName());
}
}
[MenuItem("Code Assist/Undo List Test")]
static void Undo2Test()
{
//List<string> undoList, out int undoCursor
var undoList = new List<string>();
int undoCursor = int.MaxValue;
var type = typeof(Undo);
System.Reflection.MethodInfo dynMethod = type.GetMethod("GetUndoList",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
dynMethod = type.GetMethod("GetUndoList",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static,
null,
new System.Type[] { typeof(List<string>), typeof(int).MakeByRefType() },
null);
dynMethod.Invoke(null, new object[] { undoList, undoCursor });
Serilog.Log.Debug("undo count: {Count}", undoList.Count);
}
[MenuItem("Code Assist/Reload Domain")]
static void ReloadDomain()
{
EditorUtility.RequestScriptReload();
}
/*
[MenuItem("Code Assist/TEST")]
static void TEST()
{
//if (ScriptFinder.FindGameObjectOfType("Deneme_OtherScene", out var go))
if (ScriptFinder.FindInstanceOfType("Deneme_SO", out var go, out var so))
{
NetMQInitializer.Publisher.SendScriptableObject(so);
}
ScriptFinder.DENEMEEEE();
}
*/
#endif // MERYEL_DEBUG
public static void SendTagsAndLayers()
{
Serilog.Log.Debug(nameof(SendTagsAndLayers));
var tags = UnityEditorInternal.InternalEditorUtility.tags;
NetMQInitializer.Publisher?.SendTags(tags);
var names = UnityEditorInternal.InternalEditorUtility.layers;
var indices = names.Select(l => LayerMask.NameToLayer(l).ToString()).ToArray();
NetMQInitializer.Publisher?.SendLayers(indices, names);
var sls = SortingLayer.layers;
var sortingNames = sls.Select(sl => sl.name).ToArray();
var sortingIds = sls.Select(sl => sl.id.ToString()).ToArray();
var sortingValues = sls.Select(sl => sl.value.ToString()).ToArray();
NetMQInitializer.Publisher?.SendSortingLayers(sortingNames, sortingIds, sortingValues);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 13b841023080a994d99f5a6aed7bcb29
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,16 @@
{
"name": "Meryel.CodeAssist.Editor",
"rootNamespace": "",
"references": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 7dbe264750f8c1a44ae4b49065b068ec
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 38ac97ab60822544f9cb266c1596645f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,157 @@
/*
* Derived from Unity package
* https://docs.unity3d.com/Packages/com.unity.editorcoroutines@0.0/api/Unity.EditorCoroutines.Editor.html
*/
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
//namespace Unity.EditorCoroutines.Editor
namespace Meryel.UnityCodeAssist.Editor.EditorCoroutines
{
/// <summary>
/// A handle to an EditorCoroutine, can be passed to <see cref="EditorCoroutineUtility">EditorCoroutineUtility</see> methods to control lifetime.
/// </summary>
public class EditorCoroutine
{
private struct YieldProcessor
{
enum DataType : byte
{
None = 0,
WaitForSeconds = 1,
EditorCoroutine = 2,
AsyncOP = 3,
}
struct ProcessorData
{
public DataType type;
public double targetTime;
public object current;
}
ProcessorData data;
public void Set(object yield)
{
if (yield == data.current)
return;
var type = yield.GetType();
var dataType = DataType.None;
double targetTime = -1;
if(type == typeof(EditorWaitForSeconds))
{
targetTime = EditorApplication.timeSinceStartup + (yield as EditorWaitForSeconds).WaitTime;
dataType = DataType.WaitForSeconds;
}
else if(type == typeof(EditorCoroutine))
{
dataType = DataType.EditorCoroutine;
}
else if(type == typeof(AsyncOperation) || type.IsSubclassOf(typeof(AsyncOperation)))
{
dataType = DataType.AsyncOP;
}
data = new ProcessorData { current = yield, targetTime = targetTime, type = dataType };
}
public bool MoveNext(IEnumerator enumerator)
{
var advance = data.type switch
{
DataType.WaitForSeconds => data.targetTime <= EditorApplication.timeSinceStartup,
DataType.EditorCoroutine => (data.current as EditorCoroutine).m_IsDone,
DataType.AsyncOP => (data.current as AsyncOperation).isDone,
_ => data.current == enumerator.Current,//a IEnumerator or a plain object was passed to the implementation
};
if (advance)
{
data = default;// (ProcessorData);
return enumerator.MoveNext();
}
return true;
}
}
internal WeakReference m_Owner;
IEnumerator m_Routine;
YieldProcessor m_Processor;
bool m_IsDone;
internal EditorCoroutine(IEnumerator routine)
{
m_Owner = null;
m_Routine = routine;
EditorApplication.update += MoveNext;
}
internal EditorCoroutine(IEnumerator routine, object owner)
{
m_Processor = new YieldProcessor();
m_Owner = new WeakReference(owner);
m_Routine = routine;
EditorApplication.update += MoveNext;
}
internal void MoveNext()
{
if (m_Owner != null && !m_Owner.IsAlive)
{
EditorApplication.update -= MoveNext;
return;
}
bool done = ProcessIEnumeratorRecursive(m_Routine);
m_IsDone = !done;
if (m_IsDone)
EditorApplication.update -= MoveNext;
}
static readonly Stack<IEnumerator> kIEnumeratorProcessingStack = new Stack<IEnumerator>(32);
private bool ProcessIEnumeratorRecursive(IEnumerator enumerator)
{
var root = enumerator;
while(enumerator.Current as IEnumerator != null)
{
kIEnumeratorProcessingStack.Push(enumerator);
enumerator = enumerator.Current as IEnumerator;
}
//process leaf
m_Processor.Set(enumerator.Current);
var result = m_Processor.MoveNext(enumerator);
while (kIEnumeratorProcessingStack.Count > 1)
{
if (!result)
{
result = kIEnumeratorProcessingStack.Pop().MoveNext();
}
else
kIEnumeratorProcessingStack.Clear();
}
if (kIEnumeratorProcessingStack.Count > 0 && !result && root == kIEnumeratorProcessingStack.Pop())
{
result = root.MoveNext();
}
return result;
}
internal void Stop()
{
m_Owner = null;
m_Routine = null;
EditorApplication.update -= MoveNext;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 476bd9faa78d34f40a8ecd78bcbeb5be
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,128 @@
/*
* Derived from Unity package
* https://docs.unity3d.com/Packages/com.unity.editorcoroutines@0.0/api/Unity.EditorCoroutines.Editor.html
*/
using System.Collections;
using UnityEngine;
//namespace Unity.EditorCoroutines.Editor
namespace Meryel.UnityCodeAssist.Editor.EditorCoroutines
{
public static class EditorCoroutineUtility
{
/// <summary>
/// Starts an <see cref ="EditorCoroutine">EditorCoroutine</see> with the specified owner object.
/// If the garbage collector collects the owner object, while the resulting coroutine is still executing, the coroutine will stop running.
/// <code>
/// using System.Collections;
/// using Unity.EditorCoroutines.Editor;
/// using UnityEditor;
///
/// public class ExampleWindow : EditorWindow
/// {
/// int m_Updates = 0;
/// void OnEnable()
/// {
/// EditorCoroutineUtility.StartCoroutine(CountEditorUpdates(), this);
/// }
///
/// IEnumerator CountEditorUpdates()
/// {
/// while (true)
/// {
/// ++m_Updates;
/// yield return null;
/// }
/// }
/// }
/// </code>
/// </summary>
/// <param name="routine"> IEnumerator to iterate over. </param>
/// <param name="owner">Object owning the coroutine. </param>
/// <remarks>
/// Only types that don't inherit from <see cref="UnityEngine.Object">UnityEngine.Object</see> will get collected the next time the GC runs instead of getting null-ed immediately.
/// </remarks>
/// <returns>A handle to an <see cref="EditorCoroutine">EditorCoroutine</see>.</returns>
public static EditorCoroutine StartCoroutine(IEnumerator routine, object owner)
{
return new EditorCoroutine(routine, owner);
}
/// <summary>
/// This method starts an <see cref="EditorCoroutine">EditorCoroutine</see> without an owning object. The <see cref="EditorCoroutine">EditorCoroutine</see> runs until it completes or is canceled using <see cref="StopCoroutine(EditorCoroutine)">StopCoroutine</see>.
/// <code>
/// using System.Collections;
/// using Unity.EditorCoroutines.Editor;
/// using UnityEditor;
/// using UnityEngine;
///
/// public class ExampleWindow : EditorWindow
/// {
/// void OnEnable()
/// {
/// EditorCoroutineUtility.StartCoroutineOwnerless(LogTimeSinceStartup());
/// }
///
/// IEnumerator LogTimeSinceStartup()
/// {
/// while (true)
/// {
/// Debug.LogFormat("Time since startup: {0} s", Time.realtimeSinceStartup);
/// yield return null;
/// }
/// }
/// }
/// </code>
/// </summary>
/// <param name="routine"> Generator function to execute. </param>
/// <returns>A handle to an <see cref="EditorCoroutine">EditorCoroutine.</see></returns>
public static EditorCoroutine StartCoroutineOwnerless(IEnumerator routine)
{
return new EditorCoroutine(routine);
}
/// <summary>
/// Immediately stop an <see cref="EditorCoroutine">EditorCoroutine</see>. This method is safe to call on an already completed <see cref="EditorCoroutine">EditorCoroutine</see>.
/// <code>
/// using System.Collections;
/// using Unity.EditorCoroutines.Editor;
/// using UnityEditor;
/// using UnityEngine;
///
/// public class ExampleWindow : EditorWindow
/// {
/// EditorCoroutine m_LoggerCoroutine;
/// void OnEnable()
/// {
/// m_LoggerCoroutine = EditorCoroutineUtility.StartCoroutineOwnerless(LogRunning());
/// }
///
/// void OnDisable()
/// {
/// EditorCoroutineUtility.StopCoroutine(m_LoggerCoroutine);
/// }
///
/// IEnumerator LogRunning()
/// {
/// while (true)
/// {
/// Debug.Log("Running");
/// yield return null;
/// }
/// }
/// }
/// </code>
/// </summary>
/// <param name="coroutine">A handle to an <see cref="EditorCoroutine">EditorCoroutine.</see></param>
public static void StopCoroutine(EditorCoroutine coroutine)
{
if (coroutine == null)
{
Debug.LogAssertion("EditorCoroutine handle is null.");
return;
}
coroutine.Stop();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7e1a1ea9a4320124cab499782f159158
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,49 @@
/*
* Derived from Unity package
* https://docs.unity3d.com/Packages/com.unity.editorcoroutines@0.0/api/Unity.EditorCoroutines.Editor.html
*/
//namespace Unity.EditorCoroutines.Editor
namespace Meryel.UnityCodeAssist.Editor.EditorCoroutines
{
/// <summary>
/// Suspends the <see cref="EditorCoroutine">EditorCoroutine</see> execution for the given amount of seconds, using unscaled time.
/// The coroutine execution continues after the specified time has elapsed.
/// <code>
/// using System.Collections;
/// using UnityEngine;
/// using Unity.EditorCoroutines.Editor;
/// using UnityEditor;
///
/// public class MyEditorWindow : EditorWindow
/// {
/// IEnumerator PrintEachSecond()
/// {
/// var waitForOneSecond = new EditorWaitForSeconds(1.0f);
///
/// while (true)
/// {
/// yield return waitForOneSecond;
/// Debug.Log("Printing each second");
/// }
/// }
/// }
/// </code>
/// </summary>
public class EditorWaitForSeconds
{
/// <summary>
/// The time to wait in seconds.
/// </summary>
public float WaitTime { get; }
/// <summary>
/// Creates a instruction object for yielding inside a generator function.
/// </summary>
/// <param name="time">The amount of time to wait in seconds.</param>
public EditorWaitForSeconds(float time)
{
WaitTime = time;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 068b6a385caf5344e90c84a86136fe06
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,109 @@
/*
* Derived from Unity package
* https://docs.unity3d.com/Packages/com.unity.editorcoroutines@0.0/api/Unity.EditorCoroutines.Editor.html
*/
using System.Collections;
using UnityEditor;
using UnityEngine;
//namespace Unity.EditorCoroutines.Editor
namespace Meryel.UnityCodeAssist.Editor.EditorCoroutines
{
public static class EditorWindowCoroutineExtension
{
/// <summary>
/// Start an <see cref="EditorCoroutine">EditorCoroutine</see>, owned by the calling <see cref="EditorWindow">EditorWindow</see> instance.
/// <code>
/// using System.Collections;
/// using Unity.EditorCoroutines.Editor;
/// using UnityEditor;
///
/// public class ExampleWindow : EditorWindow
/// {
/// void OnEnable()
/// {
/// this.StartCoroutine(CloseWindowDelayed());
/// }
///
/// IEnumerator CloseWindowDelayed() //close the window after 1000 frames have elapsed
/// {
/// int count = 1000;
/// while (count > 0)
/// {
/// yield return null;
/// }
/// Close();
/// }
/// }
/// </code>
/// </summary>
/// <param name="routine"></param>
/// <returns></returns>
public static EditorCoroutine StartCoroutine(this EditorWindow window, IEnumerator routine)
{
return new EditorCoroutine(routine, window);
}
/// <summary>
/// Immediately stop an <see cref="EditorCoroutine">EditorCoroutine</see> that was started by the calling <see cref="EditorWindow"/> instance. This method is safe to call on an already completed <see cref="EditorCoroutine">EditorCoroutine</see>.
/// <code>
/// using System.Collections;
/// using Unity.EditorCoroutines.Editor;
/// using UnityEditor;
/// using UnityEngine;
///
/// public class ExampleWindow : EditorWindow
/// {
/// EditorCoroutine coroutine;
/// void OnEnable()
/// {
/// coroutine = this.StartCoroutine(CloseWindowDelayed());
/// }
///
/// private void OnDisable()
/// {
/// this.StopCoroutine(coroutine);
/// }
///
/// IEnumerator CloseWindowDelayed()
/// {
/// while (true)
/// {
/// Debug.Log("Running");
/// yield return null;
/// }
/// }
/// }
/// </code>
/// </summary>
/// <param name="coroutine"></param>
public static void StopCoroutine(this EditorWindow window, EditorCoroutine coroutine)
{
if(coroutine == null)
{
Debug.LogAssertion("Provided EditorCoroutine handle is null.");
return;
}
if(coroutine.m_Owner == null)
{
Debug.LogError("The EditorCoroutine is ownerless. Please use EditorCoroutineEditor.StopCoroutine to terminate such coroutines.");
return;
}
if (!coroutine.m_Owner.IsAlive)
return; //The EditorCoroutine's owner was already terminated execution will cease next time it is processed
var owner = coroutine.m_Owner.Target as EditorWindow;
if (owner == null || owner != null && owner != window)
{
Debug.LogErrorFormat("The EditorCoroutine is owned by another object: {0}.", coroutine.m_Owner.Target);
return;
}
EditorCoroutineUtility.StopCoroutine(coroutine);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d73e3ad2af3d6c246ae57b50d3a44bc2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c592f75bdc6ba8d41b60edd273a61b30
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 9cbe467076181fd428865475c3ffc790
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,47 @@
{
"runtimeTarget": {
"name": ".NETStandard,Version=v2.0/",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETStandard,Version=v2.0": {},
".NETStandard,Version=v2.0/": {
"Meryel.UnityCodeAssist.SynchronizerModel/1.1.9": {
"dependencies": {
"NETStandard.Library": "2.0.3"
},
"runtime": {
"Meryel.UnityCodeAssist.SynchronizerModel.dll": {}
}
},
"Microsoft.NETCore.Platforms/1.1.0": {},
"NETStandard.Library/2.0.3": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0"
}
}
}
},
"libraries": {
"Meryel.UnityCodeAssist.SynchronizerModel/1.1.9": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Microsoft.NETCore.Platforms/1.1.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==",
"path": "microsoft.netcore.platforms/1.1.0",
"hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512"
},
"NETStandard.Library/2.0.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
"path": "netstandard.library/2.0.3",
"hashPath": "netstandard.library.2.0.3.nupkg.sha512"
}
}
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c8f87de6fa268834d8e371c27af9c6a3
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: e38103a6e2ae80446a9ca23ac48d9509
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,528 @@
{
"runtimeTarget": {
"name": ".NETStandard,Version=v2.0/",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETStandard,Version=v2.0": {},
".NETStandard,Version=v2.0/": {
"Meryel.UnityCodeAssist.YamlDotNet/1.0.0": {
"dependencies": {
"Microsoft.NETFramework.ReferenceAssemblies": "1.0.3",
"NETStandard.Library": "2.0.3",
"Nullable": "1.3.1",
"System.ComponentModel.TypeConverter": "4.3.0",
"System.Diagnostics.Debug": "4.3.0",
"System.Runtime.Serialization.Formatters": "4.3.0"
},
"runtime": {
"Meryel.UnityCodeAssist.YamlDotNet.dll": {}
}
},
"Microsoft.NETCore.Platforms/1.1.0": {},
"Microsoft.NETCore.Targets/1.1.0": {},
"Microsoft.NETFramework.ReferenceAssemblies/1.0.3": {
"dependencies": {
"Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3"
}
},
"Microsoft.NETFramework.ReferenceAssemblies.net461/1.0.3": {},
"NETStandard.Library/2.0.3": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0"
}
},
"Nullable/1.3.1": {},
"System.Collections/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0",
"Microsoft.NETCore.Targets": "1.1.0",
"System.Runtime": "4.3.0"
}
},
"System.Collections.NonGeneric/4.3.0": {
"dependencies": {
"System.Diagnostics.Debug": "4.3.0",
"System.Globalization": "4.3.0",
"System.Resources.ResourceManager": "4.3.0",
"System.Runtime": "4.3.0",
"System.Runtime.Extensions": "4.3.0",
"System.Threading": "4.3.0"
},
"runtime": {
"lib/netstandard1.3/System.Collections.NonGeneric.dll": {
"assemblyVersion": "4.0.2.0",
"fileVersion": "4.6.24705.1"
}
}
},
"System.Collections.Specialized/4.3.0": {
"dependencies": {
"System.Collections.NonGeneric": "4.3.0",
"System.Globalization": "4.3.0",
"System.Globalization.Extensions": "4.3.0",
"System.Resources.ResourceManager": "4.3.0",
"System.Runtime": "4.3.0",
"System.Runtime.Extensions": "4.3.0",
"System.Threading": "4.3.0"
},
"runtime": {
"lib/netstandard1.3/System.Collections.Specialized.dll": {
"assemblyVersion": "4.0.2.0",
"fileVersion": "4.6.24705.1"
}
}
},
"System.ComponentModel/4.3.0": {
"dependencies": {
"System.Runtime": "4.3.0"
},
"runtime": {
"lib/netstandard1.3/System.ComponentModel.dll": {
"assemblyVersion": "4.0.2.0",
"fileVersion": "4.6.24705.1"
}
}
},
"System.ComponentModel.Primitives/4.3.0": {
"dependencies": {
"System.ComponentModel": "4.3.0",
"System.Resources.ResourceManager": "4.3.0",
"System.Runtime": "4.3.0"
},
"runtime": {
"lib/netstandard1.0/System.ComponentModel.Primitives.dll": {
"assemblyVersion": "4.1.1.0",
"fileVersion": "4.6.24705.1"
}
}
},
"System.ComponentModel.TypeConverter/4.3.0": {
"dependencies": {
"System.Collections": "4.3.0",
"System.Collections.NonGeneric": "4.3.0",
"System.Collections.Specialized": "4.3.0",
"System.ComponentModel": "4.3.0",
"System.ComponentModel.Primitives": "4.3.0",
"System.Globalization": "4.3.0",
"System.Linq": "4.3.0",
"System.Reflection": "4.3.0",
"System.Reflection.Extensions": "4.3.0",
"System.Reflection.Primitives": "4.3.0",
"System.Reflection.TypeExtensions": "4.3.0",
"System.Resources.ResourceManager": "4.3.0",
"System.Runtime": "4.3.0",
"System.Runtime.Extensions": "4.3.0",
"System.Threading": "4.3.0"
},
"runtime": {
"lib/netstandard1.5/System.ComponentModel.TypeConverter.dll": {
"assemblyVersion": "4.1.1.0",
"fileVersion": "4.6.24705.1"
}
}
},
"System.Diagnostics.Debug/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0",
"Microsoft.NETCore.Targets": "1.1.0",
"System.Runtime": "4.3.0"
}
},
"System.Globalization/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0",
"Microsoft.NETCore.Targets": "1.1.0",
"System.Runtime": "4.3.0"
}
},
"System.Globalization.Extensions/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0",
"System.Globalization": "4.3.0",
"System.Resources.ResourceManager": "4.3.0",
"System.Runtime": "4.3.0",
"System.Runtime.Extensions": "4.3.0",
"System.Runtime.InteropServices": "4.3.0"
}
},
"System.IO/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0",
"Microsoft.NETCore.Targets": "1.1.0",
"System.Runtime": "4.3.0",
"System.Text.Encoding": "4.3.0",
"System.Threading.Tasks": "4.3.0"
}
},
"System.Linq/4.3.0": {
"dependencies": {
"System.Collections": "4.3.0",
"System.Diagnostics.Debug": "4.3.0",
"System.Resources.ResourceManager": "4.3.0",
"System.Runtime": "4.3.0",
"System.Runtime.Extensions": "4.3.0"
},
"runtime": {
"lib/netstandard1.6/System.Linq.dll": {
"assemblyVersion": "4.1.1.0",
"fileVersion": "4.6.24705.1"
}
}
},
"System.Reflection/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0",
"Microsoft.NETCore.Targets": "1.1.0",
"System.IO": "4.3.0",
"System.Reflection.Primitives": "4.3.0",
"System.Runtime": "4.3.0"
}
},
"System.Reflection.Extensions/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0",
"Microsoft.NETCore.Targets": "1.1.0",
"System.Reflection": "4.3.0",
"System.Runtime": "4.3.0"
}
},
"System.Reflection.Primitives/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0",
"Microsoft.NETCore.Targets": "1.1.0",
"System.Runtime": "4.3.0"
}
},
"System.Reflection.TypeExtensions/4.3.0": {
"dependencies": {
"System.Reflection": "4.3.0",
"System.Runtime": "4.3.0"
},
"runtime": {
"lib/netstandard1.5/System.Reflection.TypeExtensions.dll": {
"assemblyVersion": "4.1.1.0",
"fileVersion": "4.6.24705.1"
}
}
},
"System.Resources.ResourceManager/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0",
"Microsoft.NETCore.Targets": "1.1.0",
"System.Globalization": "4.3.0",
"System.Reflection": "4.3.0",
"System.Runtime": "4.3.0"
}
},
"System.Runtime/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0",
"Microsoft.NETCore.Targets": "1.1.0"
}
},
"System.Runtime.Extensions/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0",
"Microsoft.NETCore.Targets": "1.1.0",
"System.Runtime": "4.3.0"
}
},
"System.Runtime.Handles/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0",
"Microsoft.NETCore.Targets": "1.1.0",
"System.Runtime": "4.3.0"
}
},
"System.Runtime.InteropServices/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0",
"Microsoft.NETCore.Targets": "1.1.0",
"System.Reflection": "4.3.0",
"System.Reflection.Primitives": "4.3.0",
"System.Runtime": "4.3.0",
"System.Runtime.Handles": "4.3.0"
}
},
"System.Runtime.Serialization.Formatters/4.3.0": {
"dependencies": {
"System.Collections": "4.3.0",
"System.Reflection": "4.3.0",
"System.Resources.ResourceManager": "4.3.0",
"System.Runtime": "4.3.0",
"System.Runtime.Serialization.Primitives": "4.3.0"
},
"runtime": {
"lib/netstandard1.4/System.Runtime.Serialization.Formatters.dll": {
"assemblyVersion": "4.0.1.0",
"fileVersion": "4.6.24705.1"
}
}
},
"System.Runtime.Serialization.Primitives/4.3.0": {
"dependencies": {
"System.Resources.ResourceManager": "4.3.0",
"System.Runtime": "4.3.0"
},
"runtime": {
"lib/netstandard1.3/System.Runtime.Serialization.Primitives.dll": {
"assemblyVersion": "4.1.2.0",
"fileVersion": "4.6.24705.1"
}
}
},
"System.Text.Encoding/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0",
"Microsoft.NETCore.Targets": "1.1.0",
"System.Runtime": "4.3.0"
}
},
"System.Threading/4.3.0": {
"dependencies": {
"System.Runtime": "4.3.0",
"System.Threading.Tasks": "4.3.0"
},
"runtime": {
"lib/netstandard1.3/System.Threading.dll": {
"assemblyVersion": "4.0.12.0",
"fileVersion": "4.6.24705.1"
}
}
},
"System.Threading.Tasks/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0",
"Microsoft.NETCore.Targets": "1.1.0",
"System.Runtime": "4.3.0"
}
}
}
},
"libraries": {
"Meryel.UnityCodeAssist.YamlDotNet/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Microsoft.NETCore.Platforms/1.1.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==",
"path": "microsoft.netcore.platforms/1.1.0",
"hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512"
},
"Microsoft.NETCore.Targets/1.1.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==",
"path": "microsoft.netcore.targets/1.1.0",
"hashPath": "microsoft.netcore.targets.1.1.0.nupkg.sha512"
},
"Microsoft.NETFramework.ReferenceAssemblies/1.0.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
"path": "microsoft.netframework.referenceassemblies/1.0.3",
"hashPath": "microsoft.netframework.referenceassemblies.1.0.3.nupkg.sha512"
},
"Microsoft.NETFramework.ReferenceAssemblies.net461/1.0.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==",
"path": "microsoft.netframework.referenceassemblies.net461/1.0.3",
"hashPath": "microsoft.netframework.referenceassemblies.net461.1.0.3.nupkg.sha512"
},
"NETStandard.Library/2.0.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
"path": "netstandard.library/2.0.3",
"hashPath": "netstandard.library.2.0.3.nupkg.sha512"
},
"Nullable/1.3.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Mk4ZVDfAORTjvckQprCSehi1XgOAAlk5ez06Va/acRYEloN9t6d6zpzJRn5MEq7+RnagyFIq9r+kbWzLGd+6QA==",
"path": "nullable/1.3.1",
"hashPath": "nullable.1.3.1.nupkg.sha512"
},
"System.Collections/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==",
"path": "system.collections/4.3.0",
"hashPath": "system.collections.4.3.0.nupkg.sha512"
},
"System.Collections.NonGeneric/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==",
"path": "system.collections.nongeneric/4.3.0",
"hashPath": "system.collections.nongeneric.4.3.0.nupkg.sha512"
},
"System.Collections.Specialized/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Epx8PoVZR0iuOnJJDzp7pWvdfMMOAvpUo95pC4ScH2mJuXkKA2Y4aR3cG9qt2klHgSons1WFh4kcGW7cSXvrxg==",
"path": "system.collections.specialized/4.3.0",
"hashPath": "system.collections.specialized.4.3.0.nupkg.sha512"
},
"System.ComponentModel/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-VyGn1jGRZVfxnh8EdvDCi71v3bMXrsu8aYJOwoV7SNDLVhiEqwP86pPMyRGsDsxhXAm2b3o9OIqeETfN5qfezw==",
"path": "system.componentmodel/4.3.0",
"hashPath": "system.componentmodel.4.3.0.nupkg.sha512"
},
"System.ComponentModel.Primitives/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-j8GUkCpM8V4d4vhLIIoBLGey2Z5bCkMVNjEZseyAlm4n5arcsJOeI3zkUP+zvZgzsbLTYh4lYeP/ZD/gdIAPrw==",
"path": "system.componentmodel.primitives/4.3.0",
"hashPath": "system.componentmodel.primitives.4.3.0.nupkg.sha512"
},
"System.ComponentModel.TypeConverter/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-16pQ6P+EdhcXzPiEK4kbA953Fu0MNG2ovxTZU81/qsCd1zPRsKc3uif5NgvllCY598k6bI0KUyKW8fanlfaDQg==",
"path": "system.componentmodel.typeconverter/4.3.0",
"hashPath": "system.componentmodel.typeconverter.4.3.0.nupkg.sha512"
},
"System.Diagnostics.Debug/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==",
"path": "system.diagnostics.debug/4.3.0",
"hashPath": "system.diagnostics.debug.4.3.0.nupkg.sha512"
},
"System.Globalization/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==",
"path": "system.globalization/4.3.0",
"hashPath": "system.globalization.4.3.0.nupkg.sha512"
},
"System.Globalization.Extensions/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==",
"path": "system.globalization.extensions/4.3.0",
"hashPath": "system.globalization.extensions.4.3.0.nupkg.sha512"
},
"System.IO/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==",
"path": "system.io/4.3.0",
"hashPath": "system.io.4.3.0.nupkg.sha512"
},
"System.Linq/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==",
"path": "system.linq/4.3.0",
"hashPath": "system.linq.4.3.0.nupkg.sha512"
},
"System.Reflection/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==",
"path": "system.reflection/4.3.0",
"hashPath": "system.reflection.4.3.0.nupkg.sha512"
},
"System.Reflection.Extensions/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==",
"path": "system.reflection.extensions/4.3.0",
"hashPath": "system.reflection.extensions.4.3.0.nupkg.sha512"
},
"System.Reflection.Primitives/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==",
"path": "system.reflection.primitives/4.3.0",
"hashPath": "system.reflection.primitives.4.3.0.nupkg.sha512"
},
"System.Reflection.TypeExtensions/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==",
"path": "system.reflection.typeextensions/4.3.0",
"hashPath": "system.reflection.typeextensions.4.3.0.nupkg.sha512"
},
"System.Resources.ResourceManager/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==",
"path": "system.resources.resourcemanager/4.3.0",
"hashPath": "system.resources.resourcemanager.4.3.0.nupkg.sha512"
},
"System.Runtime/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==",
"path": "system.runtime/4.3.0",
"hashPath": "system.runtime.4.3.0.nupkg.sha512"
},
"System.Runtime.Extensions/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==",
"path": "system.runtime.extensions/4.3.0",
"hashPath": "system.runtime.extensions.4.3.0.nupkg.sha512"
},
"System.Runtime.Handles/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==",
"path": "system.runtime.handles/4.3.0",
"hashPath": "system.runtime.handles.4.3.0.nupkg.sha512"
},
"System.Runtime.InteropServices/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==",
"path": "system.runtime.interopservices/4.3.0",
"hashPath": "system.runtime.interopservices.4.3.0.nupkg.sha512"
},
"System.Runtime.Serialization.Formatters/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-IfJxJOaVssiaLVmQqRy7GEvkwtQoYvNdVp+cTpRJTadzllivaLaX0Y0yVTRiP5Yp5kIijuuXdB5hMujpwduYcg==",
"path": "system.runtime.serialization.formatters/4.3.0",
"hashPath": "system.runtime.serialization.formatters.4.3.0.nupkg.sha512"
},
"System.Runtime.Serialization.Primitives/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-w6fBfup0Yq7bTpY7J6iGR150eTXGiYUGF2E/4DPyv9i3nGpVPIEzbP4w4CcFnADZGQvDSVtzHGXa3Oa5/bj48Q==",
"path": "system.runtime.serialization.primitives/4.3.0",
"hashPath": "system.runtime.serialization.primitives.4.3.0.nupkg.sha512"
},
"System.Text.Encoding/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==",
"path": "system.text.encoding/4.3.0",
"hashPath": "system.text.encoding.4.3.0.nupkg.sha512"
},
"System.Threading/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==",
"path": "system.threading/4.3.0",
"hashPath": "system.threading.4.3.0.nupkg.sha512"
},
"System.Threading.Tasks/4.3.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==",
"path": "system.threading.tasks/4.3.0",
"hashPath": "system.threading.tasks.4.3.0.nupkg.sha512"
}
}
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 3ec866449fed6af40a047e12f31768a2
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: dd04e29e638cfd84e987c860dca8fd84
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 669f868bb308d5549bba700d849bd4bb
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 1329dcf5acec8d546bfaba8f80e94192
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: a236a5ab591f120428cf934460efd4dc
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: e64a643b67ae4924592c37a10a230396
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 0ef0d0874ba9b934d99ff6a291ad7bc1
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 8d84ac9193770fa43b325cf6b810e046
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 3ecc917cdff3bc1498219fd346515293
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 4076d5097b2019e419e215538689454c
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: b0f671a0a1784ab4c9ed3a96215a8e64
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,105 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
#nullable enable
namespace Meryel.UnityCodeAssist.Editor
{
public class FeedbackWindow : EditorWindow
{
GUIStyle? styleLabel;
public static void Display()
{
NetMQInitializer.Publisher?.SendRequestInternalLog();
// Get existing open window or if none, make a new one:
var window = GetWindow<FeedbackWindow>();
window.Show();
Serilog.Log.Debug("Displaying feedback window");
NetMQInitializer.Publisher?.SendAnalyticsEvent("Gui", "FeedbackWindow_Display");
}
private void OnEnable()
{
//**--icon
//var icon = AssetDatabase.LoadAssetAtPath<Texture>("Assets/Sprites/Gear.png");
//titleContent = new GUIContent("Code Assist", icon);
titleContent = new GUIContent("Code Assist Feedback");
}
private void OnGUI()
{
var errorCount = Logger.ELogger.GetErrorCountInInternalLog();
var warningCount = Logger.ELogger.GetWarningCountInInternalLog();
var logContent = Logger.ELogger.GetInternalLogContent();
if (!string.IsNullOrEmpty(Logger.ELogger.VsInternalLog))
logContent += Logger.ELogger.VsInternalLog;
styleLabel ??= new GUIStyle(GUI.skin.label)
{
wordWrap = true,
alignment = TextAnchor.MiddleCenter,
};
if (errorCount > 0)
EditorGUILayout.LabelField($"{errorCount} error(s) found in logs. Please submit a feedback (via e-mail, Discord or GitHub) with the logs if possible.", styleLabel, GUILayout.ExpandWidth(true));
else if (warningCount > 0)
EditorGUILayout.LabelField($"{warningCount} warnings(s) found in logs. Please submit a feedback (via e-mail, Discord or GitHub) with the logs if possible.", styleLabel, GUILayout.ExpandWidth(true));
else
EditorGUILayout.LabelField("No errors found in logs. Please submit a feedback (via e-mail, Discord or GitHub) describing what went wrong or unexpected.", styleLabel, GUILayout.ExpandWidth(true));
if (GUILayout.Button("Send e-mail"))
{
var uri = "mailto:merryyellow@outlook.com";
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(uri));
}
if (GUILayout.Button("Send Discord message"))
{
//var uri = "discord://invites/2CgKHDq";
var uri = "https://discord.gg/2CgKHDq";
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(uri));
}
if (GUILayout.Button("Submit GitHub issue"))
{
var uri = "https://github.com/merryyellow/Unity-Code-Assist/issues/new/choose";
Application.OpenURL(uri);
}
EditorGUILayout.Separator();
if (GUILayout.Button("View Unity full log"))
{
var filePath = Logger.ELogger.FilePath;
System.Diagnostics.Process.Start(filePath);
}
if (GUILayout.Button("View Visual Studio full log"))
{
var filePath = Logger.ELogger.VSFilePath;
System.Diagnostics.Process.Start(filePath);
}
if (GUILayout.Button("Copy recent logs to clipboard"))
{
GUIUtility.systemCopyBuffer = logContent;
}
EditorGUILayout.LabelField("Recent logs:", styleLabel, GUILayout.ExpandWidth(true));
EditorGUILayout.SelectableLabel(logContent, EditorStyles.textArea, GUILayout.ExpandHeight(true));
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 32df5c90db6b3ed4a84031cab040d4d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b0c5aeddac1e8884da485bf2a0c4d544
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,141 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using UnityEditor;
using UnityEngine;
#nullable enable
//namespace UTJ.UnityCommandLineTools
namespace Meryel.UnityCodeAssist.Editor.Input
{
// <summary>
// bin2textをUnityEditorから実行する為のClass
// programed by Katsumasa.Kimura
// </summary>
public class Binary2TextExec : EditorToolExec
{
public Binary2TextExec() : base("binary2text") { }
// <summary>
// bin2text filePath outPath options
// /summary>
public int Exec(string filePath, string outPath, string options)
{
var args = string.Format(@"""{0}"" ""{1}"" {2}", filePath, outPath, options);
return Exec(args);
}
public int Exec(string filePath, string outPath, bool detailed = false, bool largeBinaryHashOnly = false, bool hexFloat = false)
{
//var args = string.Format(@"""{0}"" ""{1}"" {2}", filePath, outPath, options);
var args = string.Format(@"""{0}"" ""{1}""", filePath, outPath);
if (detailed)
args += " -detailed";
if (largeBinaryHashOnly)
args += " -largebinaryhashonly";
if (hexFloat)
args += " -hexfloat";
return Exec(args);
}
}
// <summary>
// UnityEditorに含まれるコマンドラインツールを実行する為の基底Class
// programed by Katsumasa.Kimura
//</summary>
public class EditorToolExec
{
// <value>
// UnityEditorがインストールされているディレクトリへのパス
// </value>
protected string mEditorPath;
// <value>
// Toolsディレクトリへのパス
// </value>
protected string mToolsPath;
// <value>
// 実行ファイル名
// </value>
protected string mExecFname;
// <value>
// 実行ファイルへのフルパス
// </value>
protected string mExecFullPath;
// <value>
// 実行結果のOUTPUT
// </value>
private string? mOutput;
// <value>
// 実行結果のOUTPUT
// </value>
public string? Output
{
get { return mOutput; }
}
// <summary>
// コンストラクタ
// <param>
// mExecFname : 実行ファイル名
// </param>
// /summary>
public EditorToolExec(string mExecFname)
{
mEditorPath = Path.GetDirectoryName(EditorApplication.applicationPath);
mToolsPath = Path.Combine(mEditorPath, @"Data/Tools");
this.mExecFname = mExecFname;
//var files = Directory.GetFiles(mToolsPath, mExecFname, SearchOption.AllDirectories);
var files = Directory.GetFiles(mEditorPath, mExecFname + "*", SearchOption.AllDirectories);
if (files.Length == 0)
Serilog.Log.Error("{App} app couldn't be found at {Path}", mExecFname, mEditorPath);
mExecFullPath = files[0];
}
// <summary>
// コマンドラインツールを実行する
// <param>
// arg : コマンドラインツールに渡す引数
// </param>
// </summary>
public int Exec(string arg)
{
int exitCode = -1;
try
{
using var process = new Process();
process.StartInfo.FileName = mExecFullPath;
process.StartInfo.Arguments = arg;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
mOutput = process.StandardOutput.ReadToEnd();
process.WaitForExit();
exitCode = process.ExitCode;
process.Close();
}
catch (Exception e)
{
//UnityEngine.Debug.Log(e);
Serilog.Log.Error(e, "Exception while running process at {Scope}.{Location}", nameof(EditorToolExec), nameof(Exec));
}
return exitCode;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6db78598a7c20a141975035612031328
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,111 @@
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
#nullable enable
namespace Meryel.UnityCodeAssist.Editor.Input
{
public class InputManagerMonitor
{
private static readonly Lazy<InputManagerMonitor> _instance = new Lazy<InputManagerMonitor>(() => new InputManagerMonitor());
public static InputManagerMonitor Instance => _instance.Value;
//UnityInputManager inputManager;
readonly string inputManagerFilePath;
DateTime previousTagManagerLastWrite;
public InputManagerMonitor()
{
EditorApplication.update += Update;
inputManagerFilePath = CommonTools.GetInputManagerFilePath();
previousTagManagerLastWrite = System.IO.File.GetLastWriteTime(inputManagerFilePath);
}
void Update()
{
var currentInputManagerLastWrite = System.IO.File.GetLastWriteTime(inputManagerFilePath);
if (currentInputManagerLastWrite != previousTagManagerLastWrite)
{
previousTagManagerLastWrite = currentInputManagerLastWrite;
Bump();
}
}
public void Bump()
{
Serilog.Log.Debug("InputMonitor {Event}", nameof(Bump));
var inputManager = new UnityInputManager();
inputManager.ReadFromPath(inputManagerFilePath);
inputManager.SendData();
}
}
public static partial class Extensions
{
public static string GetInfo(this List<InputAxis> axes, string? name)
{
if (name == null || string.IsNullOrEmpty(name))
return string.Empty;
//axis.descriptiveName
var axesWithName = axes.Where(a => a.Name == name);
int threshold = 80;
var sb = new System.Text.StringBuilder();
foreach (var axis in axesWithName)
if (!string.IsNullOrEmpty(axis.descriptiveName))
sb.Append($"{axis.descriptiveName} ");
if (sb.Length > threshold)
return sb.ToString();
foreach (var axis in axesWithName)
if (!string.IsNullOrEmpty(axis.descriptiveNegativeName))
sb.Append($"{axis.descriptiveNegativeName} ");
if (sb.Length > threshold)
return sb.ToString();
foreach (var axis in axesWithName)
if (!string.IsNullOrEmpty(axis.positiveButton))
sb.Append($"[{axis.positiveButton}] ");
if (sb.Length > threshold)
return sb.ToString();
foreach (var axis in axesWithName)
if (!string.IsNullOrEmpty(axis.altPositiveButton))
sb.Append($"{{{axis.altPositiveButton}}} ");
if (sb.Length > threshold)
return sb.ToString();
foreach (var axis in axesWithName)
if (!string.IsNullOrEmpty(axis.negativeButton))
sb.Append($"-[{axis.negativeButton}] ");
if (sb.Length > threshold)
return sb.ToString();
foreach (var axis in axesWithName)
if (!string.IsNullOrEmpty(axis.altNegativeButton))
sb.Append($"-{{{axis.altNegativeButton}}} ");
return sb.ToString();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: abc0210fcf4b7cb46baab11d1db6fdb0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,210 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
#nullable enable
namespace Meryel.UnityCodeAssist.Editor.Input
{
public class Text2Yaml
{
public static string Convert(IEnumerable<string> textLines)
{
var sb = new StringBuilder();
var stack = new Stack<(string typeName, string identifier, int indentation)>();
sb.AppendLine(@"%YAML 1.1");
sb.AppendLine(@"%TAG !u! tag:unity3d.com,2011:");
sb.AppendLine(@"--- !u!13 &1");
sb.AppendLine(@"InputManager:");
var regexIndentation = new Regex("^\\s*");
var regexString = new Regex("^(\\s+)(\\w+)\\s+\"([a-zA-Z0-9_ ]*)\"\\s+\\(string\\)$");
var regexValue = new Regex("^(\\s+)(\\w+)\\s+(-?[0-9.]*)\\s+\\(((bool)|(int)|(float)|(unsigned int))\\)$");
var regexType = new Regex("^(\\s+)(\\w+)\\s+\\((\\w+)\\)$");
var regexVectorSize = new Regex("(\\s+)size\\s+(\\d)+\\s+\\(int\\)");
//var regexVectorData = new Regex("(\\s+)data \\(InputAxis\\)"); // remove InputAxis to make it more generic
string curTextLine;
var curTextLineNo = 3;
var textIndentation = 1;
var indentationPrefix = new string(' ', textIndentation * 2);
stack.Push(("InputManager", "InputManager", textIndentation));
foreach (var line in textLines.Skip(4))
{
curTextLine = line;
curTextLineNo++;
// Skip empty lines
if (line.Length == 0)
continue;
// Check if type undeclared, scope goes down, indentation decrements
{
var indentationMatch = regexIndentation.Match(line);
if (indentationMatch.Success)
{
var indentation = indentationMatch.Groups[0].Value.Length;
if (indentation > textIndentation)
Error($"indentation({indentation}) > textIndentation({textIndentation})");
while (indentation < textIndentation)
{
stack.Pop();
textIndentation--;
var typeIndentation = textIndentation;
if (stack.TryPeek(out var curType2))
typeIndentation = curType2.indentation;
else if (line.Length > 0)
Error("stack empty at type undeclaration");
indentationPrefix = new string(' ', typeIndentation * 2);
}
}
else
{
Error($"{nameof(regexIndentation)} failed");
}
}
// Skip size field of vectors
if (stack.TryPeek(out var curType1) && curType1.typeName == "vector")
{
var vectorSizeMatch = regexVectorSize.Match(line);
if (vectorSizeMatch.Success)
{
continue;
}
}
// Read string fields
{
var stringMatch = regexString.Match(line);
if (stringMatch.Success)
{
AddLine(stringMatch.Groups[2] + ": " + stringMatch.Groups[3]);
continue;
}
}
// Read bool/int/float/unsignedInt fields
{
var valueMatch = regexValue.Match(line);
if (valueMatch.Success)
{
AddLine(valueMatch.Groups[2] + ": " + valueMatch.Groups[3]);
continue;
}
}
// Check if new type declared, scope goes up, indentation increases
{
var typeMatch = regexType.Match(line);
if (typeMatch.Success)
{
var identifier = typeMatch.Groups[2].Value;
var typeName = typeMatch.Groups[3].Value;
var isVectorData = false;
if (stack.TryPeek(out var curType2) && curType2.typeName == "vector" && identifier == "data")
isVectorData = true;
var typeIndentation = textIndentation;
if (stack.TryPeek(out var curType3))
typeIndentation = curType3.indentation;
else if (line.Length > 0)
Error("stack empty at type declaration");
if (!isVectorData)
{
AddLine(typeMatch.Groups[2] + ":");
}
else
{
var customIndentation = typeIndentation - 1;
if (customIndentation < 0)
Error($"customIndentation({customIndentation}) < 0");
var customIndentationPrefix = new string(' ', customIndentation * 2);
AddLine("- serializedVersion: 3", customIndentationPrefix);
}
textIndentation++;
typeIndentation++;
if (isVectorData)
typeIndentation--;
stack.Push((typeName, identifier, typeIndentation));
indentationPrefix = new string(' ', typeIndentation * 2);
continue;
}
}
Error("line failed to match all cases");
}
return sb.ToString();
void AddLine(string line, string? customIndentationPrefix = null)
{
string suffix;
if (stack.TryPeek(out var top))
suffix = $" # {textIndentation}, {top.indentation}, {top.typeName} {top.identifier}";
else
suffix = $" # {textIndentation}, nil";
if (customIndentationPrefix != null)
sb.AppendLine(customIndentationPrefix + line + suffix);
else
sb.AppendLine(indentationPrefix + line + suffix);
}
void Error(string message)
{
var errorMessage = $"Text2Yaml error '{message}' at lineNo: {curTextLineNo}, line: '{curTextLine}' at {Environment.StackTrace}";
//throw new Exception(errorMessage);
Serilog.Log.Warning(errorMessage);
}
}
}
public static partial class Extensions
{
public static bool TryPeek<T>(this Stack<T> stack, /*[MaybeNullWhen(false)]*/ out T result)
{
if (stack.Count > 0)
{
result = stack.Peek();
return true;
}
else
{
result = default!;
return false;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d662105b3c16a894aa31647bdbd740ea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,405 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
#nullable enable
namespace Meryel.UnityCodeAssist.Editor.Input
{
internal class UnityInputManager
{
//string yamlPath;
TextReader? reader;
InputManager? inputManager;
public void ReadFromText(string text)
{
reader = new StringReader(text);
ReadAux(false, out _);
}
public void ReadFromPath(string yamlPath)
{
switch (UnityEditor.EditorSettings.serializationMode)
{
case UnityEditor.SerializationMode.ForceText:
{
reader = new StreamReader(yamlPath);
ReadAux(false, out _);
}
break;
case UnityEditor.SerializationMode.ForceBinary:
{
// this approach will work for InputManager since its file size is small and limited
// but in the future, we may need to switch to reading binary files for big files
// like this https://github.com/Unity-Technologies/UnityDataTools
// or this https://github.com/SeriousCache/UABE
var converted = GetOrCreateConvertedFile(yamlPath);
if (!File.Exists(converted))
{
Serilog.Log.Warning("Temp file {TempFile} couldn't found for converted yaml input file. Auto Input Manager will not work!", converted);
return;
}
var rawLines = File.ReadLines(converted);
var yamlText = Text2Yaml.Convert(rawLines);
reader = new StringReader(yamlText);
ReadAux(false, out _);
}
break;
case UnityEditor.SerializationMode.Mixed:
{
reader = new StreamReader(yamlPath);
ReadAux(true, out var hasSemanticError);
if (hasSemanticError)
{
var converted = GetOrCreateConvertedFile(yamlPath);
if (!File.Exists(converted))
{
Serilog.Log.Warning("Temp file {TempFile} couldn't found for converted yaml input file. Auto Input Manager will not work!", converted);
return;
}
var rawLines = File.ReadLines(converted);
var yamlText = Text2Yaml.Convert(rawLines);
reader = new StringReader(yamlText);
ReadAux(false, out _);
}
}
break;
}
}
void ReadAux(bool canHaveSemanticError, out bool hasSemanticError)
{
hasSemanticError = false;
if (reader == null)
{
Serilog.Log.Warning($"{nameof(UnityInputManager)}.{nameof(reader)} is null");
return;
}
//var reader = new StreamReader(yamlPath);
var deserializer = new YamlDotNet.Serialization.DeserializerBuilder()
.WithTagMapping("tag:unity3d.com,2011:13", typeof(Class13Mapper))
.IgnoreUnmatchedProperties()
.Build();
//serializer.Settings.RegisterTagMapping("tag:unity3d.com,2011:13", typeof(Class13));
//serializer.Settings.ComparerForKeySorting = null;
Class13Mapper? result;
try
{
result = deserializer.Deserialize<Class13Mapper>(reader);
}
catch (YamlDotNet.Core.SemanticErrorException semanticErrorException)
{
Serilog.Log.Debug(semanticErrorException, "Couldn't parse InputManager.asset yaml file");
if (!canHaveSemanticError)
Serilog.Log.Error(semanticErrorException, "Couldn't parse InputManager.asset yaml file unexpectedly");
hasSemanticError = true;
return;
}
finally
{
reader.Close();
}
var inputManagerMapper = result?.InputManager;
if (inputManagerMapper == null)
{
Serilog.Log.Warning($"{nameof(inputManagerMapper)} is null");
return;
}
inputManager = new InputManager(inputManagerMapper);
}
public void SendData()
{
if (inputManager == null)
return;
var axisNames = inputManager.Axes.Select(a => a.Name!).Where(n => !string.IsNullOrEmpty(n)).Distinct().ToArray();
var axisInfos = axisNames.Select(a => inputManager.Axes.GetInfo(a)).ToArray();
if (!CreateBindingsMap(out var buttonKeys, out var buttonAxis))
return;
string[] joystickNames;
try
{
joystickNames = UnityEngine.Input.GetJoystickNames();
}
catch (InvalidOperationException)
{
// Occurs if user have switched active Input handling to Input System package in Player Settings.
joystickNames = new string[0];
}
NetMQInitializer.Publisher?.SendInputManager(axisNames, axisInfos, buttonKeys, buttonAxis, joystickNames);
/*
NetMQInitializer.Publisher?.SendInputManager(
inputManager.Axes.Select(a => a.Name).Distinct().ToArray(),
inputManager.Axes.Select(a => a.positiveButton).ToArray(),
inputManager.Axes.Select(a => a.negativeButton).ToArray(),
inputManager.Axes.Select(a => a.altPositiveButton).ToArray(),
inputManager.Axes.Select(a => a.altNegativeButton).ToArray(),
UnityEngine.Input.GetJoystickNames()
);
*/
}
bool CreateBindingsMap([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out string[]? inputKeys, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out string[]? inputAxis)
{
if (inputManager == null)
{
inputKeys = null;
inputAxis = null;
return false;
}
var dict = new Dictionary<string, string?>();
foreach (var axis in inputManager.Axes)
{
if (axis.altNegativeButton != null && !string.IsNullOrEmpty(axis.altNegativeButton))
dict[axis.altNegativeButton] = axis.Name;
}
foreach (var axis in inputManager.Axes)
{
if (axis.negativeButton != null && !string.IsNullOrEmpty(axis.negativeButton))
dict[axis.negativeButton] = axis.Name;
}
foreach (var axis in inputManager.Axes)
{
if (axis.altPositiveButton != null && !string.IsNullOrEmpty(axis.altPositiveButton))
dict[axis.altPositiveButton] = axis.Name;
}
foreach (var axis in inputManager.Axes)
{
if (axis.positiveButton != null && !string.IsNullOrEmpty(axis.positiveButton))
dict[axis.positiveButton] = axis.Name;
}
var keys = new string[dict.Count];
var values = new string[dict.Count];
dict.Keys.CopyTo(keys, 0);
dict.Values.CopyTo(values, 0);
inputKeys = keys;
inputAxis = values;
return true;
}
static string GetOrCreateConvertedFile(string filePath)
{
var hash = GetMD5Hash(filePath);
var convertedPath = Path.Combine(Path.GetTempPath(), $"UCA_IM_{hash}.txt");
if (!File.Exists(convertedPath))
{
Serilog.Log.Debug("Converting binary to text format of {File} to {Target}", filePath, convertedPath);
var converter = new Binary2TextExec();
converter.Exec(filePath, convertedPath);
}
else
{
Serilog.Log.Debug("Converted file already exists at {Target}", convertedPath);
}
return convertedPath;
}
/// <summary>
/// Gets a hash of the file using MD5.
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
public static string GetMD5Hash(string filePath)
{
using var md5 = new MD5CryptoServiceProvider();
return GetHash(filePath, md5);
}
/// <summary>
/// Gets a hash of the file using MD5.
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
public static string GetMD5Hash(Stream s)
{
using var md5 = new MD5CryptoServiceProvider();
return GetHash(s, md5);
}
private static string GetHash(string filePath, HashAlgorithm hasher)
{
using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return GetHash(fs, hasher);
}
private static string GetHash(Stream s, HashAlgorithm hasher)
{
var hash = hasher.ComputeHash(s);
var hashStr = Convert.ToBase64String(hash);
//return hashStr.TrimEnd('=');
var hashStrAlphaNumeric = System.Text.RegularExpressions.Regex.Replace(hashStr, "[^A-Za-z0-9]", "");
return hashStrAlphaNumeric;
}
}
public enum AxisType
{
KeyOrMouseButton = 0,
MouseMovement = 1,
JoystickAxis = 2
};
#pragma warning disable IDE1006
public class InputAxisMapper
{
public int serializedVersion { get; set; }
public string? m_Name { get; set; }
public string? descriptiveName { get; set; }
public string? descriptiveNegativeName { get; set; }
public string? negativeButton { get; set; }
public string? positiveButton { get; set; }
public string? altNegativeButton { get; set; }
public string? altPositiveButton { get; set; }
//public float gravity { get; set; }
//public float dead { get; set; }
//public float sensitivity { get; set; }
public string? gravity { get; set; }
public string? dead { get; set; }
public string? sensitivity { get; set; }
//public bool snap { get; set; }
public int snap { get; set; }
//public bool invert { get; set; }
public int invert { get; set; }
//public AxisType type { get; set; }
public int type { get; set; }
public int axis { get; set; }
public int joyNum { get; set; }
}
public class InputAxis
{
readonly InputAxisMapper map;
public InputAxis(InputAxisMapper map)
{
this.map = map;
}
public int SerializedVersion
{
get { return map.serializedVersion; }
set { map.serializedVersion = value; }
}
public string? Name => map.m_Name;
public string? descriptiveName => map.descriptiveName;
public string? descriptiveNegativeName => map.descriptiveNegativeName;
public string? negativeButton => map.negativeButton;
public string? positiveButton => map.positiveButton;
public string? altNegativeButton => map.altNegativeButton;
public string? altPositiveButton => map.altPositiveButton;
public float gravity => float.Parse(map.gravity);//**--format
public float dead => float.Parse(map.dead);//**--format
public float sensitivity => float.Parse(map.sensitivity);//**--format
public bool snap => map.snap != 0;
public bool invert => map.invert != 0;
public AxisType type => (AxisType)map.type;
public int axis => map.axis;
public int joyNum => map.joyNum;
}
public class InputManagerMapper
{
public int m_ObjectHideFlags { get; set; }
public int serializedVersion { get; set; }
public int m_UsePhysicalKeys { get; set; }
public List<InputAxisMapper>? m_Axes { get; set; }
}
#pragma warning restore IDE1006
public class InputManager
{
readonly InputManagerMapper map;
readonly List<InputAxis> axes;
public InputManager(InputManagerMapper map)
{
this.map = map;
this.axes = new List<InputAxis>();
if (map.m_Axes == null)
{
Serilog.Log.Warning($"map.m_Axes is null");
return;
}
foreach (var a in map.m_Axes)
this.axes.Add(new InputAxis(a));
}
public int ObjectHideFlags
{
get { return map.m_ObjectHideFlags; }
set { map.m_ObjectHideFlags = value; }
}
public int SerializedVersion
{
get { return map.serializedVersion; }
set { map.serializedVersion = value; }
}
public bool UsePhysicalKeys
{
get { return map.m_UsePhysicalKeys != 0; }
set { map.m_UsePhysicalKeys = value ? 1 : 0; }
}
/*public List<InputAxisMapper> Axes
{
get { return map.m_Axes; }
set { map.m_Axes = value; }
}*/
public List<InputAxis> Axes => axes;
}
public class Class13Mapper
{
public InputManagerMapper? InputManager { get; set; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 94bc173b04900674ba9cf4b722f8f8d9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 33adb743cc8450b4c8a6105599c145be
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Text;
#if !NET_UNITY_4_8
namespace System.Diagnostics.CodeAnalysis
{
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
internal sealed class NotNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter will not be null.
/// </param>
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eebe08ae528d7724887b8d74bd2a5551
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using static System.IO.Path;
#nullable enable
namespace Meryel.UnityCodeAssist.Editor
{
public static class CommonTools
{
public static string GetTagManagerFilePath()
{
var projectPath = GetProjectPathRaw();
var tagManagerPath = Combine(projectPath, "ProjectSettings/TagManager.asset");
return tagManagerPath;
}
public static string GetInputManagerFilePath()
{
var projectPath = GetProjectPathRaw();
var inputManagerPath = Combine(projectPath, "ProjectSettings/InputManager.asset");
return inputManagerPath;
}
public static string GetProjectPath()
{
var rawPath = GetProjectPathRaw();
var osPath = new OSPath(rawPath);
var unixPath = osPath.Unix;
var trimmed = unixPath.TrimEnd('\\', '/');
return trimmed;
}
/// <summary>
/// Get the path to the project folder.
/// </summary>
/// <returns>The project folder path</returns>
static string GetProjectPathRaw()
{
// Application.dataPath returns the path including /Assets, which we need to strip off
var path = UnityEngine.Application.dataPath;
var directory = new DirectoryInfo(path);
var parent = directory.Parent;
if (parent != null)
return parent.FullName;
return path;
}
public static int GetHashOfPath(string path)
{
var osPath = new OSPath(path);
var unixPath = osPath.Unix;
var trimmed = unixPath.TrimEnd('\\', '/');
var hash = trimmed.GetHashCode();
if (hash < 0) // Get rid of the negative values, so there will be no '-' char in file names
{
hash++;
hash = Math.Abs(hash);
}
return hash;
}
}
// https://github.com/dmitrynogin/cdsf/blob/master/Cds.Folders/OSPath.cs
internal class OSPath
{
public static readonly OSPath Empty = "";
public static bool IsWindows => DirectorySeparatorChar == '\\';
public OSPath(string text)
{
Text = text.Trim();
}
public static implicit operator OSPath(string text) => new OSPath(text);
public static implicit operator string(OSPath path) => path.Normalized;
public override string ToString() => Normalized;
protected string Text { get; }
public string Normalized => IsWindows ? Windows : Unix;
public string Windows => Text.Replace('/', '\\');
//public string Unix => Simplified.Text.Replace('\\', '/');
public string Unix => Text.Replace('\\', '/');
public OSPath Relative => Simplified.Text.TrimStart('/', '\\');
public OSPath Absolute => IsAbsolute ? this : "/" + Relative;
public bool IsAbsolute => IsRooted || HasVolume;
public bool IsRooted => Text.Length >= 1 && (Text[0] == '/' || Text[0] == '\\');
public bool HasVolume => Text.Length >= 2 && Text[1] == ':';
public OSPath Simplified => HasVolume ? Text.Substring(2) : Text;
public OSPath Parent => GetDirectoryName(Text);
public bool Contains(OSPath path) =>
Normalized.StartsWith(path);
public static OSPath operator +(OSPath left, OSPath right) =>
new OSPath(Combine(left, right.Relative));
public static OSPath operator -(OSPath left, OSPath right) =>
left.Contains(right)
? new OSPath(left.Normalized.Substring(right.Normalized.Length)).Relative
: left;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cb6ae855b8059214095179e3a316467b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,31 @@
using System;
using System.Linq;
using Serilog;
using Serilog.Core;
using Serilog.Events;
using Serilog.Configuration;
#nullable enable
namespace Meryel.UnityCodeAssist.Editor.Logger
{
public class DomainHashEnricher : ILogEventEnricher
{
static readonly int domainHash;
static DomainHashEnricher()
{
var guid = UnityEditor.GUID.Generate();
domainHash = guid.GetHashCode();
}
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(
"DomainHash", domainHash));
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d1235c7b5b457d848833746026792ef7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,186 @@
using Serilog;
using Serilog.Core;
using UnityEngine;
using UnityEditor;
using System.Linq;
#nullable enable
namespace Meryel.UnityCodeAssist.Editor.Logger
{
[InitializeOnLoad]
public static class ELogger
{
public static event System.Action? OnVsInternalLogChanged;
// Change 'new LoggerConfiguration().MinimumLevel.Debug();' if you change these values
const Serilog.Events.LogEventLevel fileMinLevel = Serilog.Events.LogEventLevel.Debug;
const Serilog.Events.LogEventLevel outputWindowMinLevel = Serilog.Events.LogEventLevel.Information;
static LoggingLevelSwitch? fileLevelSwitch, outputWindowLevelSwitch;
//static bool IsInitialized { get; set; }
static ILogEventSink? _outputWindowSink;
static ILogEventSink? _memorySink;
public static string GetInternalLogContent() => _memorySink == null ? string.Empty : ((MemorySink)_memorySink).Export();
public static int GetErrorCountInInternalLog() => _memorySink == null ? 0 : ((MemorySink)_memorySink).ErrorCount;
public static int GetWarningCountInInternalLog() => _memorySink == null ? 0 : ((MemorySink)_memorySink).WarningCount;
public static string? FilePath { get; private set; }
public static string? VSFilePath { get; private set; }
//**-- make it work with multiple clients
static string? _vsInternalLog;
public static string? VsInternalLog
{
get => _vsInternalLog;
set
{
_vsInternalLog = value;
OnVsInternalLogChanged?.Invoke();
}
}
static ELogger()
{
var isFirst = false;
const string stateName = "isFirst";
if (!SessionState.GetBool(stateName, false))
{
isFirst = true;
SessionState.SetBool(stateName, true);
}
var projectPath = CommonTools.GetProjectPath();
var outputWindowSink = new System.Lazy<ILogEventSink>(() => new UnityOutputWindowSink(null));
Init(isFirst, projectPath, outputWindowSink);
if (isFirst)
LogHeader(Application.unityVersion, projectPath);
Serilog.Log.Debug("PATH: {Path}", projectPath);
}
static void LogHeader(string unityVersion, string solutionDir)
{
var os = System.Runtime.InteropServices.RuntimeInformation.OSDescription;
var assisterVersion = Assister.Version;
var syncModel = Synchronizer.Model.Utilities.Version;
var hash = CommonTools.GetHashOfPath(solutionDir);
Serilog.Log.Debug(
"Beginning logging {OS}, Unity {U}, Unity Code Assist {A}, Communication Protocol {SM}, Project: '{Dir}', Project Hash: {Hash}",
os, unityVersion, assisterVersion, syncModel, solutionDir, hash);
}
static string GetFilePath(string solutionDir)
{
var solutionHash = CommonTools.GetHashOfPath(solutionDir);
var tempDir = System.IO.Path.GetTempPath();
var fileName = $"UCA_U_LOG_{solutionHash}_.TXT"; // hour code will be appended to the end of file, so add a trailing '_'
var filePath = System.IO.Path.Combine(tempDir, fileName);
return filePath;
}
static string GetVSFilePath(string solutionDir)
{
var solutionHash = CommonTools.GetHashOfPath(solutionDir);
var tempDir = System.IO.Path.GetTempPath();
var fileName = $"UCA_VS_LOG_{solutionHash}_.TXT"; // hour code will be appended to the end of file, so add a trailing '_'
var filePath = System.IO.Path.Combine(tempDir, fileName);
return filePath;
}
public static void Init(bool isFirst, string solutionDir, System.Lazy<ILogEventSink> outputWindowSink)
{
FilePath = GetFilePath(solutionDir);
VSFilePath = GetVSFilePath(solutionDir);
fileLevelSwitch = new LoggingLevelSwitch(fileMinLevel);
outputWindowLevelSwitch = new LoggingLevelSwitch(outputWindowMinLevel);
var config = new LoggerConfiguration()
.MinimumLevel.Debug()
.Enrich.With(new DomainHashEnricher());
const string outputTemplate = "{Timestamp:HH:mm:ss.fff} [U] [{Level:u3}] [{DomainHash}] {Message:lj}{NewLine}{Exception}";
config = config.WriteTo.PersistentFile(FilePath
, outputTemplate: outputTemplate
, shared: true
, persistentFileRollingInterval: PersistentFileRollingInterval.Day
, preserveLogFilename: true
, levelSwitch: fileLevelSwitch
, rollOnEachProcessRun: isFirst
);
_outputWindowSink ??= outputWindowSink.Value;
if (_outputWindowSink != null)
config = config.WriteTo.Sink(_outputWindowSink, outputWindowMinLevel, outputWindowLevelSwitch);
_memorySink ??= new MemorySink(outputTemplate);
config = config.WriteTo.Sink(_memorySink, fileMinLevel, null);
config = config.Destructure.With(new MyDestructuringPolicy());
Serilog.Log.Logger = config.CreateLogger();
//switchableLogger.Set(config.CreateLogger(), disposePrev: true);
OnOptionsChanged();
//IsInitialized = true;
}
public static void OnOptionsChanged()
{
// Since we don't use LogEventLevel.Fatal, we can use it for disabling sinks
var isLoggingToFile = OptionsIsLoggingToFile;
var targetFileLevel = isLoggingToFile ? fileMinLevel : Serilog.Events.LogEventLevel.Fatal;
if (fileLevelSwitch != null)
fileLevelSwitch.MinimumLevel = targetFileLevel;
var isLoggingToOutputWindow = OptionsIsLoggingToOutputWindow;
var targetOutputWindowLevel = isLoggingToOutputWindow ? outputWindowMinLevel : Serilog.Events.LogEventLevel.Fatal;
if (outputWindowLevelSwitch != null)
outputWindowLevelSwitch.MinimumLevel = targetOutputWindowLevel;
}
//**-- UI for these two
static bool OptionsIsLoggingToFile => true;
static bool OptionsIsLoggingToOutputWindow => true;
}
public class MyDestructuringPolicy : IDestructuringPolicy
{
// serilog cannot destruct StringArrayContainer by default, so do it manually
public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out Serilog.Events.LogEventPropertyValue? result)
{
if (value is Synchronizer.Model.StringArrayContainer sac)
{
var items = sac.Container.Select(item => propertyValueFactory.CreatePropertyValue(item, true));
result = new Serilog.Events.SequenceValue(items);
return true;
}
result = null;
return false;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 054dc6105589bce4081e205ac04bf169
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,131 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Text;
using Serilog.Core;
using Serilog.Events;
using Serilog.Formatting;
#nullable enable
namespace Meryel.UnityCodeAssist.Editor.Logger
{
//**--
// remove this in unity???
// need to serialize/deserialize data to survive domain reload, which will effect performance
// right now data is lost during domain reloads, which makes its function kinda useless
// or maybe move it to a external process like com.unity.process-server
public class MemorySink : ILogEventSink
{
readonly ConcurrentQueue<LogEvent> logs;
readonly ConcurrentQueue<LogEvent[]> warningLogs;
readonly ConcurrentQueue<LogEvent[]> errorLogs;
const int logsLimit = 30;
const int warningLimit = 5;
const int errorLimit = 3;
readonly string outputTemplate;
public MemorySink(string outputTemplate)
{
this.outputTemplate = outputTemplate;
logs = new ConcurrentQueue<LogEvent>();
warningLogs = new ConcurrentQueue<LogEvent[]>();
errorLogs = new ConcurrentQueue<LogEvent[]>();
}
public void Emit(LogEvent logEvent)
{
if (logEvent == null)
return;
logs.Enqueue(logEvent);
if (logs.Count > logsLimit)
logs.TryDequeue(out _);
if (logEvent.Level == LogEventLevel.Warning)
{
var warningAndLeadingLogs = logs.ToArray();
warningLogs.Enqueue(warningAndLeadingLogs);
if (warningLogs.Count > warningLimit)
warningLogs.TryDequeue(out _);
}
if (logEvent.Level == LogEventLevel.Error)
{
var errorAndLeadingLogs = logs.ToArray();
errorLogs.Enqueue(errorAndLeadingLogs);
if (errorLogs.Count > errorLimit)
errorLogs.TryDequeue(out _);
}
}
public bool HasError => !errorLogs.IsEmpty;
public bool HasWarning => !warningLogs.IsEmpty;
public int ErrorCount => errorLogs.Count;
public int WarningCount => warningLogs.Count;
public string Export()
{
IFormatProvider? formatProvider = null;
var formatter = new Serilog.Formatting.Display.MessageTemplateTextFormatter(
outputTemplate, formatProvider);
var result = string.Empty;
using (var outputStream = new MemoryStream())
{
var encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
using var output = new StreamWriter(outputStream, encoding);
if (!errorLogs.IsEmpty)
{
var errorArray = errorLogs.ToArray();
foreach (var error in errorArray)
{
foreach (var logEvent in error)
{
formatter.Format(logEvent, output);
}
}
}
if (!warningLogs.IsEmpty)
{
var warningArray = warningLogs.ToArray();
foreach (var warning in warningArray)
{
foreach (var logEvent in warning)
{
formatter.Format(logEvent, output);
}
}
}
if (!logs.IsEmpty)
{
var logArray = logs.ToArray();
foreach (var logEvent in logArray)
{
formatter.Format(logEvent, output);
}
}
output.Flush();
outputStream.Seek(0, SeekOrigin.Begin);
using var streamReader = new StreamReader(outputStream, encoding);
result = streamReader.ReadToEnd();
}
return result;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0fe13269bf1182142a9b46409339fb5b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,47 @@
using System;
using System.Linq;
using Serilog;
using Serilog.Core;
using Serilog.Events;
using Serilog.Configuration;
#nullable enable
namespace Meryel.UnityCodeAssist.Editor.Logger
{
public class UnityOutputWindowSink : ILogEventSink
{
private readonly IFormatProvider? _formatProvider;
public UnityOutputWindowSink(IFormatProvider? formatProvider)
{
_formatProvider = formatProvider;
}
public void Emit(LogEvent logEvent)
{
var message = logEvent.RenderMessage(_formatProvider);
switch (logEvent.Level)
{
case LogEventLevel.Verbose:
case LogEventLevel.Debug:
case LogEventLevel.Information:
UnityEngine.Debug.Log(message);
break;
case LogEventLevel.Warning:
UnityEngine.Debug.LogWarning(message);
break;
case LogEventLevel.Error:
case LogEventLevel.Fatal:
UnityEngine.Debug.LogError(message);
break;
default:
break;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3995562507d0dd147b1533d40e3de339
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,35 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Concurrent;
using UnityEngine;
using UnityEditor;
#nullable enable
namespace Meryel.UnityCodeAssist.Editor
{
[InitializeOnLoad]
public static class MainThreadDispatcher
{
readonly static ConcurrentBag<System.Action> actions;
static MainThreadDispatcher()
{
actions = new ConcurrentBag<System.Action>();
EditorApplication.update += Update;
}
static void Update()
{
while (actions.TryTake(out var action))
{
action.Invoke();
}
}
public static void Add(System.Action action) => actions.Add(action);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a8e4c653b47e4164b916eb282781b9ac
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,237 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
#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();
previousTagManagerLastWrite = System.IO.File.GetLastWriteTime(tagManagerFilePath);
dirtyDict = new Dictionary<GameObject, int>();
dirtyCounter = 0;
EditorApplication.hierarchyChanged += OnHierarchyChanged;
EditorApplication.update += OnUpdate;
Undo.postprocessModifications += MyPostprocessModificationsCallback;
Undo.undoRedoPerformed += MyUndoCallback;
Selection.selectionChanged += OnSelectionChanged;
//EditorSceneManager.sceneOpened += EditorSceneManager_sceneOpened;
EditorSceneManager.activeSceneChangedInEditMode += EditorSceneManager_activeSceneChangedInEditMode;
Application.logMessageReceived += Application_logMessageReceived;
//System.Threading.Tasks.TaskScheduler.UnobservedTaskException +=
}
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();
var currentTagManagerLastWrite = System.IO.File.GetLastWriteTime(tagManagerFilePath);
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
NetMQInitializer.Publisher?.SendHandshake();
if (ScriptFinder.GetActiveGameObject(out var activeGO))
NetMQInitializer.Publisher?.SendGameObject(activeGO);
//Assister.SendTagsAndLayers(); Don't send tags & layers here
}
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)
NetMQInitializer.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);
//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;
NetMQInitializer.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();
NetMQInitializer.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
{
Serilog.Log.Error("Invalid LazyLoad category {Category}", category);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5a5c5865a2051964ebed462471427777
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,95 @@
using System;
using UnityEngine;
using UnityEditor;
#nullable enable
namespace Meryel.UnityCodeAssist.Editor
{
[InitializeOnLoad]
public static class NetMQInitializer
{
public static NetMQPublisher? Publisher;
static NetMQInitializer()
{
EditorApplication.quitting += EditorApplication_quitting;
AssemblyReloadEvents.beforeAssemblyReload += AssemblyReloadEvents_beforeAssemblyReload;
AssemblyReloadEvents.afterAssemblyReload += AssemblyReloadEvents_afterAssemblyReload;
RunOnceOnUpdate(Initialize);
}
public static void Initialize()
{
Serilog.Log.Debug("NetMQ initializing");
AsyncIO.ForceDotNet.Force();
Serilog.Log.Debug("NetMQ cleaning up (true)");
NetMQ.NetMQConfig.Cleanup(true);
Serilog.Log.Debug("NetMQ constructing");
Publisher = new NetMQPublisher();
RunOnShutdown(OnShutDown);
Serilog.Log.Debug("NetMQ initialized");
}
private static void OnShutDown()
{
Serilog.Log.Debug("NetMQ OnShutDown");
Clear();
}
private static void AssemblyReloadEvents_afterAssemblyReload()
{
Serilog.Log.Debug("NetMQ AssemblyReloadEvents_afterAssemblyReload");
}
//private static void AssemblyReloadEvents_beforeAssemblyReload() => Clear();
private static void AssemblyReloadEvents_beforeAssemblyReload()
{
Serilog.Log.Debug("NetMQ AssemblyReloadEvents_beforeAssemblyReload");
Clear();
}
private static void EditorApplication_quitting()
{
Serilog.Log.Debug("NetMQ EditorApplication_quitting");
Publisher?.SendDisconnect();
Clear();
}
static void Clear() => Publisher?.Clear();
private static void RunOnceOnUpdate(Action action)
{
void callback()
{
EditorApplication.update -= callback;
action();
}
EditorApplication.update += callback;
}
private static void RunOnShutdown(Action action)
{
// Mono on OSX has all kinds of quirks on AppDomain shutdown
//if (!VisualStudioEditor.IsWindows)
//return;
#if !UNITY_EDITOR_WIN
return;
#else
AppDomain.CurrentDomain.DomainUnload += (_, __) => action();
#endif
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ab712cada0ef881478108b33d3062ac3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,792 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using UnityEngine;
using UnityEditor;
using NetMQ;
using NetMQ.Sockets;
using System.Threading;
using Task = System.Threading.Tasks.Task;
//using CancellationToken = System.Threading;
using Application = UnityEngine.Application;
#nullable enable
//**--
// can also do this for better clear, sometimes it gets locked
// https://answers.unity.com/questions/704066/callback-before-unity-reloads-editor-assemblies.html#
namespace Meryel.UnityCodeAssist.Editor
{
public class NetMQPublisher : Synchronizer.Model.IProcessor
{
PublisherSocket? pubSocket;
readonly string pubConnString;
Task? pullTask;
CancellationTokenSource? pullTaskCancellationTokenSource;
readonly Synchronizer.Model.Manager syncMngr;
public List<Synchronizer.Model.Connect> clients;
Synchronizer.Model.Connect? _self;
Synchronizer.Model.Connect Self => _self!;
void InitializeSelf()
{
var projectPath = CommonTools.GetProjectPath();
_self = new Synchronizer.Model.Connect()
{
ModelVersion = Synchronizer.Model.Utilities.Version,
ProjectPath = projectPath,
ProjectName = getProjectName(),
ContactInfo = $"Unity {Application.unityVersion}",
AssemblyVersion = Assister.Version,
};
string getProjectName()
{
string[] s = projectPath.Split('/');
string projectName = s[s.Length - 2];
//Logg("project = " + projectName);
return projectName;
}
}
public static void LogContext()
{
Serilog.Log.Debug("LogginContext begin");
//var context = typeof(NetMQConfig).GetProperty("Context", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).GetValue(null);
var context = typeof(NetMQConfig).GetField("s_ctx", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).GetValue(null);
Serilog.Log.Debug("context: {Context}", context);
if (context == null)
return;
var starting = context.GetType().GetField("m_starting", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(context);
Serilog.Log.Debug("starting: {Starting}", starting);
var terminating = context.GetType().GetField("m_terminating", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(context);
Serilog.Log.Debug("terminating: {Terminating}", terminating);
var sockets = context.GetType().GetField("m_sockets", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(context);
//Logg("sockets:" + sockets);
//var socketList = sockets as System.Collections.IList;
if (sockets is System.Collections.IList socketList)
{
Serilog.Log.Debug("socketList: {SocketList} [{Count}]", socketList, socketList.Count);
foreach (var socketItem in socketList)
{
Serilog.Log.Debug("socketItem: {SocketItem}", socketItem);
}
}
var endPoints = context.GetType().GetField("m_endpoints", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(context);
//Logg("endPoints:" + endPoints);
//var endPointDict = endPoints as System.Collections.IDictionary;
if (endPoints is System.Collections.IDictionary endPointDict)
{
Serilog.Log.Debug("endPointDict: {EndPointDict} ,{Count}", endPointDict, endPointDict.Count);
foreach (var endPointDictKey in endPointDict.Keys)
{
Serilog.Log.Debug("endPointDictKey: {EndPointDictKey} => {EndPointDictValue}", endPointDictKey, endPointDict[endPointDictKey]);
}
}
Serilog.Log.Debug("LogginContext end");
}
bool isBind = false;
public NetMQPublisher()
{
// LogContext();
Serilog.Log.Debug("NetMQ server initializing, begin");
InitializeSelf();
clients = new List<Synchronizer.Model.Connect>();
syncMngr = new Synchronizer.Model.Manager(this);
var (pubSub, pushPull) = Synchronizer.Model.Utilities.GetConnectionString(Self!.ProjectPath);
pubConnString = pubSub;
//NetMQConfig.Linger = new TimeSpan(0);
//pub = new Publisher();
pubSocket = new PublisherSocket();
pubSocket.Options.SendHighWatermark = 1000;
Serilog.Log.Debug("NetMQ server initializing, Publisher socket binding... {PubConnString}", pubConnString);
//pubSocket.Bind("tcp://127.0.0.1:12349");
try
{
pubSocket.Bind(pubConnString);
isBind = true;
Serilog.Log.Debug("NetMQ server initializing, Publisher socket bound");
}
catch (AddressAlreadyInUseException ex)
{
Serilog.Log.Warning(ex, "NetMQ.AddressAlreadyInUseException");
LogContext();
Serilog.Log.Warning("NetMQ.AddressAlreadyInUseException disposing pubSocket");
pubSocket.Dispose();
pubSocket = null;
return;
}
catch (System.Net.Sockets.SocketException ex)
{
Serilog.Log.Warning(ex, "Socket exception");
LogContext();
Serilog.Log.Warning("Socket exception disposing pubSocket");
pubSocket.Dispose();
pubSocket = null;
return;
}
//pubSocket.SendReady += PubSocket_SendReady;
//SendConnect();
pullTaskCancellationTokenSource = new CancellationTokenSource();
//pullThread = new System.Threading.Thread(async () => await PullAsync(conn.pushPull, pullThreadCancellationTokenSource.Token));
//pullThread = new System.Threading.Thread(() => InitPull(conn.pushPull, pullTaskCancellationTokenSource.Token));
//pullThread.Start();
//Task.Run(() => InitPullAsync());
/*
pullTask = Task.Factory.StartNew(
() => InitPull(conn.pushPull, pullTaskCancellationTokenSource.Token), pullTaskCancellationTokenSource.Token,
System.Threading.Tasks.TaskCreationOptions.LongRunning, System.Threading.Tasks.TaskScheduler.Current);
*/
pullTask = Task.Factory.StartNew(
() => InitPull(pushPull, pullTaskCancellationTokenSource.Token),
System.Threading.Tasks.TaskCreationOptions.LongRunning);
//InitPull(conn.pushPull);
Serilog.Log.Debug("NetMQ server initializing, initialized");
// need to sleep here, clients will take some time to start subscribing
// https://github.com/zeromq/netmq/issues/482#issuecomment-182200323
Thread.Sleep(1000);
SendConnect();
}
private void InitPull(string connectionString, CancellationToken cancellationToken)
{
using (var runtime = new NetMQRuntime())
{
runtime.Run(//cancellationToken,
PullAsync(connectionString, cancellationToken)
);
Serilog.Log.Debug("Puller runtime ended");
}
Serilog.Log.Debug("Puller runtime disposed");
}
async Task PullAsync(string connectionString, CancellationToken cancellationToken)
{
Serilog.Log.Debug("Puller begin");
using (var pullSocket = new PullSocket(connectionString))
{
while (!cancellationToken.IsCancellationRequested)
{
string header, content;
try
{
var headerTuple = await pullSocket.ReceiveFrameStringAsync(cancellationToken);
var contentTuple = await pullSocket.ReceiveFrameStringAsync(cancellationToken);
header = headerTuple.Item1;
content = contentTuple.Item1;
}
catch (System.Threading.Tasks.TaskCanceledException)
{
// Cancellation (token) requested
break;
}
Serilog.Log.Debug("Pulled: {Header}, {Content}", header, content);
if (cancellationToken.IsCancellationRequested)
break;
//**--optimize here, pass only params
MainThreadDispatcher.Add(() => syncMngr.ProcessMessage(header, content));
//syncMngr.ProcessMessage(header.Item1, content.Item1);
}
Serilog.Log.Debug("Puller closing");
pullSocket.Unbind(connectionString);
pullSocket.Close();
Serilog.Log.Debug("Puller closed");
}
Serilog.Log.Debug("Puller disposed");
}
public void Clear()
{
// LogContext();
Serilog.Log.Debug("NetMQ clearing, begin 1, pullTaskCancellationTokenSource: {PullTaskCancellationTokenSource}", pullTaskCancellationTokenSource);
pullTaskCancellationTokenSource?.Cancel();
Serilog.Log.Verbose("NetMQ clearing, begin 2, pubSocket: {PubSocket}", pubSocket);
var pubSocketDebugStr = pubSocket?.ToString();
Serilog.Log.Debug("NetMQ clearing, begin 3, isBind: {IsBind}", isBind);
if (isBind)
{
pubSocket?.Unbind(pubConnString);
isBind = false;
}
Serilog.Log.Verbose("NetMQ clearing, begin 4");
pubSocket?.Close();
Serilog.Log.Verbose("NetMQ clearing, begin 5");
pubSocket?.Dispose();
Serilog.Log.Verbose("NetMQ clearing, begin 6");
pubSocket = null;
Serilog.Log.Debug("NetMQ clearing, publisher closed pubSocketDebugStr: {PubSocketDebugStr}", pubSocketDebugStr);
try
{
Serilog.Log.Debug("NetMQ clearing, Task 1 begin");
if (pullTask != null && !pullTask.Wait(1000))
Serilog.Log.Warning("NetMQ clearing, pull task termination failed");
Serilog.Log.Verbose("NetMQ clearing, Task 2 waited");
pullTask?.Dispose();
pullTask = null;
Serilog.Log.Debug("NetMQ clearing, Task 3 disposed");
}
catch (Exception ex)
{
//Console.WriteLine($"\n{nameof(OperationCanceledException)} thrown\n");
Serilog.Log.Error(ex, "NetMQ clearing, pull task");
}
finally
{
pullTaskCancellationTokenSource?.Dispose();
pullTaskCancellationTokenSource = null;
Serilog.Log.Debug("NetMQ clearing, task cancelled");
}
Serilog.Log.Debug("NetMQ clearing, cleaning up");
//pullSocket?.Close();
NetMQConfig.Cleanup(false); // Must be here to work more than once. Also argument false is important, otherwise might freeze Unity upon exit or domain reload
//pullThread?.Abort();
Serilog.Log.Debug("NetMQ clearing, cleared");
}
string SerializeObject<T>(T obj)
where T : class
{
// Odin cant serialize string arrays, https://github.com/TeamSirenix/odin-serializer/issues/26
//var buffer = OdinSerializer.SerializationUtility.SerializeValue<T>(obj, OdinSerializer.DataFormat.JSON);
//var str = System.Text.Encoding.UTF8.GetString(buffer, 0, buffer.Length);
// Newtonsoft works fine, but needs package reference
//var str = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
// not working
//var str = EditorJsonUtility.ToJson(obj);
// needs nuget
//System.Text.Json.JsonSerializer;
var str = TinyJson.JsonWriter.ToJson(obj);
return str;
}
void SendAux(Synchronizer.Model.IMessage message, bool logContent = true)
{
if (message == null)
return;
SendAux(message.GetType().Name, message, logContent);
}
void SendAux(string messageType, object content, bool logContent = true)
{
if (logContent)
Serilog.Log.Debug("Publishing {MessageType} {@Content}", messageType, content);
else
Serilog.Log.Debug("Publishing {MessageType}", messageType);
var publisher = pubSocket;
if (publisher != null)
publisher.SendMoreFrame(messageType).SendFrame(SerializeObject(content));
else
Serilog.Log.Error("Publisher socket is null");
}
public void SendConnect()
{
var connect = Self;
SendAux(connect);
}
public void SendDisconnect()
{
var disconnect = new Synchronizer.Model.Disconnect()
{
ModelVersion = Self.ModelVersion,
ProjectPath = Self.ProjectPath,
ProjectName = Self.ProjectName,
ContactInfo = Self.ContactInfo,
AssemblyVersion = Self.AssemblyVersion,
};
SendAux(disconnect);
}
public void SendConnectionInfo()
{
var connectionInfo = new Synchronizer.Model.ConnectionInfo()
{
ModelVersion = Self.ModelVersion,
ProjectPath = Self.ProjectPath,
ProjectName = Self.ProjectName,
ContactInfo = Self.ContactInfo,
AssemblyVersion = Self.AssemblyVersion,
};
SendAux(connectionInfo);
}
public void SendHandshake()
{
var handshake = new Synchronizer.Model.Handshake();
SendAux(handshake);
}
public void SendRequestInternalLog()
{
var requestInternalLog = new Synchronizer.Model.RequestInternalLog();
SendAux(requestInternalLog);
}
public void SendInternalLog()
{
var internalLog = new Synchronizer.Model.InternalLog()
{
LogContent = Logger.ELogger.GetInternalLogContent(),
};
SendAux(internalLog, logContent: false);
}
void SendStringArrayAux(string id, string[] array)
{
var stringArray = new Synchronizer.Model.StringArray()
{
Id = id,
Array = array,
};
SendAux(stringArray);
}
void SendStringArrayContainerAux(params (string id, string[] array)[] container)
{
var stringArrayContainer = new Synchronizer.Model.StringArrayContainer()
{
Container = new Synchronizer.Model.StringArray[container.Length],
};
for (int i = 0; i < container.Length; i++)
{
stringArrayContainer.Container[i] = new Synchronizer.Model.StringArray
{
Id = container[i].id,
Array = container[i].array
};
}
SendAux(stringArrayContainer);
}
public void SendTags(string[] tags) =>
SendStringArrayAux(Synchronizer.Model.Ids.Tags, tags);
/*
{
var tags = new Synchronizer.Model.Tags()
{
TagArray = tagArray,
};
var serialized = Newtonsoft.Json.JsonConvert.SerializeObject(tags);
pubSocket.SendMoreFrame(nameof(Synchronizer.Model.Tags)).SendFrame(serialized);
}*/
public void SendLayers(string[] layerIndices, string[] layerNames)
{
/*
var layers = new Synchronizer.Model.Layers()
{
LayerIndices = layerIndices,
LayerNames = layerNames,
};
var serialized = Newtonsoft.Json.JsonConvert.SerializeObject(layers);
pubSocket.SendMoreFrame(nameof(Synchronizer.Model.Layers)).SendFrame(serialized);
*/
//SendStringArrayAux(Synchronizer.Model.Ids.Layers, layerNames);
//SendStringArrayAux(Synchronizer.Model.Ids.LayerIndices, layerIndices);
SendStringArrayContainerAux(
(Synchronizer.Model.Ids.Layers, layerNames),
(Synchronizer.Model.Ids.LayerIndices, layerIndices)
);
}
public void SendSortingLayers(string[] sortingLayers, string[] sortingLayerIds, string[] sortingLayerValues)
{
//SendStringArrayAux(Synchronizer.Model.Ids.SortingLayers, sortingLayers);
//SendStringArrayAux(Synchronizer.Model.Ids.SortingLayerIds, sortingLayerIds);
//SendStringArrayAux(Synchronizer.Model.Ids.SortingLayerValues, sortingLayerValues);
SendStringArrayContainerAux(
(Synchronizer.Model.Ids.SortingLayers, sortingLayers),
(Synchronizer.Model.Ids.SortingLayerIds, sortingLayerIds),
(Synchronizer.Model.Ids.SortingLayerValues, sortingLayerValues)
);
}
public void SendPlayerPrefs(string[] playerPrefKeys, string[] playerPrefValues,
string[] playerPrefStringKeys, string[] playerPrefIntegerKeys, string[] playerPrefFloatKeys)
{
SendStringArrayContainerAux(
(Synchronizer.Model.Ids.PlayerPrefKeys, playerPrefKeys),
(Synchronizer.Model.Ids.PlayerPrefValues, playerPrefValues),
(Synchronizer.Model.Ids.PlayerPrefStringKeys, playerPrefStringKeys),
(Synchronizer.Model.Ids.PlayerPrefIntegerKeys, playerPrefIntegerKeys),
(Synchronizer.Model.Ids.PlayerPrefFloatKeys, playerPrefFloatKeys)
);
}
public void SendEditorPrefs(string[] editorPrefKeys, string[] editorPrefValues,
string[] editorPrefStringKeys, string[] editorPrefIntegerKeys, string[] editorPrefFloatKeys,
string[] editorPrefBooleanKeys)
{
SendStringArrayContainerAux(
(Synchronizer.Model.Ids.EditorPrefKeys, editorPrefKeys),
(Synchronizer.Model.Ids.EditorPrefValues, editorPrefValues),
(Synchronizer.Model.Ids.EditorPrefStringKeys, editorPrefStringKeys),
(Synchronizer.Model.Ids.EditorPrefIntegerKeys, editorPrefIntegerKeys),
(Synchronizer.Model.Ids.EditorPrefFloatKeys, editorPrefFloatKeys),
(Synchronizer.Model.Ids.EditorPrefBooleanKeys, editorPrefBooleanKeys)
);
}
public void SendInputManager(string[] axisNames, string[] axisInfos, string[] buttonKeys, string[] buttonAxis, string[] joystickNames)
{
SendStringArrayContainerAux(
(Synchronizer.Model.Ids.InputManagerAxes, axisNames),
(Synchronizer.Model.Ids.InputManagerAxisInfos, axisInfos),
(Synchronizer.Model.Ids.InputManagerButtonKeys, buttonKeys),
(Synchronizer.Model.Ids.InputManagerButtonAxis, buttonAxis),
(Synchronizer.Model.Ids.InputManagerJoystickNames, joystickNames)
);
}
public void SendScriptMissing(string component)
{
var scriptMissing = new Synchronizer.Model.ScriptMissing()
{
Component = component,
};
SendAux(scriptMissing);
}
public void SendGameObject(GameObject go)
{
if (!go)
return;
Serilog.Log.Debug("SendGO: {GoName}", go.name);
var dataOfSelf = go.ToSyncModel(priority:10000);
if (dataOfSelf != null)
SendAux(dataOfSelf);
var dataOfHierarchy = go.ToSyncModelOfHierarchy();
if (dataOfHierarchy != null)
{
foreach (var doh in dataOfHierarchy)
SendAux(doh);
}
var dataOfComponents = go.ToSyncModelOfComponents();
if (dataOfComponents != null)
{
foreach (var doc in dataOfComponents)
SendAux(doc);
}
}
public void SendScriptableObject(ScriptableObject so)
{
Serilog.Log.Debug("SendSO: {SoName}", so.name);
var dataOfSo = so.ToSyncModel();
if (dataOfSo != null)
SendAux(dataOfSo);
}
public void SendAnalyticsEvent(string type, string content)
{
var dataOfAe = new Synchronizer.Model.AnalyticsEvent()
{
EventType = type,
EventContent = content
};
SendAux(dataOfAe);
}
public void SendErrorReport(string errorMessage, string stack, string type)
{
var dataOfER = new Synchronizer.Model.ErrorReport()
{
ErrorMessage = errorMessage,
ErrorStack = stack,
ErrorType = type,
};
SendAux(dataOfER);
}
public void SendRequestVerboseType(string type, string docPath)
{
var dataOfRVT = new Synchronizer.Model.RequestVerboseType()
{
Type = type,
DocPath = docPath,
};
SendAux(dataOfRVT);
}
string Synchronizer.Model.IProcessor.Serialize<T>(T value)
{
//return System.Text.Json.JsonSerializer.Serialize<T>(value);
//return Newtonsoft.Json.JsonConvert.SerializeObject(value);
return SerializeObject(value);
}
T Synchronizer.Model.IProcessor.Deserialize<T>(string data)
{
//return System.Text.Json.JsonSerializer.Deserialize<T>(data)!;
//return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(data)!;
return TinyJson.JsonParser.FromJson<T>(data)!;
//byte[] buffer = System.Text.Encoding.UTF8.GetBytes(data);
//T val = OdinSerializer.SerializationUtility.DeserializeValue<T>(buffer, OdinSerializer.DataFormat.JSON);
//return val;
}
//**--make sure all Synchronizer.Model.IProcessor.Process methods are thread-safe
// a new client has connected
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.Connect connect)
{
if (connect.ModelVersion != Self.ModelVersion)
{
Serilog.Log.Error("Version mismatch with {ContactInfo}. Please update your asset and reinstall the Visual Studio extension. {ContactModel} != {SelfModel}", connect.ContactInfo, connect.ModelVersion, Self.ModelVersion);
return;
}
if (connect.ProjectPath != Self.ProjectPath)
{
Serilog.Log.Error("Project mismatch with {ProjectName}. '{ConnectPath}' != '{SelfPath}'", connect.ProjectName, connect.ProjectPath, Self.ProjectPath);
return;
}
if (!clients.Any(c => c.ContactInfo == connect.ContactInfo))
{
clients.Add(connect);
}
SendHandshake();
if (ScriptFinder.GetActiveGameObject(out var activeGO))
SendGameObject(activeGO);
Assister.SendTagsAndLayers();
}
// a new client is online and requesting connection
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.RequestConnect requestConnect)
{
SendConnect();
}
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.Disconnect disconnect)
{
var client = clients.FirstOrDefault(c => c.ContactInfo == disconnect.ContactInfo);
if (client == null)
return;
clients.Remove(client);
}
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.ConnectionInfo connectionInfo)
{
if (connectionInfo.ModelVersion != Self.ModelVersion)
{
Serilog.Log.Error("Version mismatch with {ContactInfo}. Please update your asset and reinstall the Visual Studio extension. {ContactModel} != {SelfModel}", connectionInfo.ContactInfo, connectionInfo.ModelVersion, Self.ModelVersion);
return;
}
if (connectionInfo.ProjectPath != Self.ProjectPath)
{
Serilog.Log.Error("Project mismatch with {ProjectName}. '{ConnectPath}' != '{SelfPath}'", connectionInfo.ProjectName, connectionInfo.ProjectPath, Self.ProjectPath);
return;
}
if (!clients.Any(c => c.ContactInfo == connectionInfo.ContactInfo))
{
SendConnect();
}
else
{
SendHandshake();
if (ScriptFinder.GetActiveGameObject(out var activeGO))
SendGameObject(activeGO);
Assister.SendTagsAndLayers();
}
}
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.RequestConnectionInfo requestConnectionInfo)
{
SendConnectionInfo();
}
/*
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.Layers layers)
{
}
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.Tags tags)
{
}
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.SortingLayers sortingLayers)
{
}*/
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.StringArray stringArray)
{
Serilog.Log.Warning("Unity/Server shouldn't call Synchronizer.Model.IProcessor.Process(Synchronizer.Model.StringArray)");
}
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.StringArrayContainer stringArrayContainer)
{
Serilog.Log.Warning("Unity/Server shouldn't call Synchronizer.Model.IProcessor.Process(Synchronizer.Model.StringArrayContainer)");
}
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.GameObject gameObject)
{
Serilog.Log.Warning("Unity/Server shouldn't call Synchronizer.Model.IProcessor.Process(Synchronizer.Model.GameObject)");
}
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.ComponentData component)
{
Serilog.Log.Warning("Unity/Server shouldn't call Synchronizer.Model.IProcessor.Process(Synchronizer.Model.ComponentData)");
}
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.RequestScript requestScript)
{
if (requestScript.DeclaredTypes == null || requestScript.DeclaredTypes.Length == 0)
return;
var documentPath = requestScript.DocumentPath;
foreach (var declaredType in requestScript.DeclaredTypes)
{
if (ScriptFinder.FindInstanceOfType(declaredType, documentPath, out var go, out var so))
{
if (go != null)
SendGameObject(go);
else if (so != null)
SendScriptableObject(so);
else
Serilog.Log.Warning("Invalid instance of type");
}
else
{
SendScriptMissing(declaredType);
}
}
}
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.ScriptMissing scriptMissing)
{
Serilog.Log.Warning("Unity/Server shouldn't call Synchronizer.Model.IProcessor.Process(Synchronizer.Model.ScriptMissing)");
}
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.Handshake handshake)
{
// Do nothing
}
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.RequestInternalLog requestInternalLog)
{
SendInternalLog();
}
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.InternalLog internalLog)
{
Logger.ELogger.VsInternalLog = internalLog.LogContent;
}
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.AnalyticsEvent analyticsEvent)
{
Serilog.Log.Warning("Unity/Server shouldn't call Synchronizer.Model.IProcessor.Process(Synchronizer.Model.AnalyticsEvent)");
}
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.ErrorReport errorReport)
{
Serilog.Log.Warning("Unity/Server shouldn't call Synchronizer.Model.IProcessor.Process(Synchronizer.Model.ErrorReport)");
}
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.RequestVerboseType requestVerboseType)
{
Serilog.Log.Warning("Unity/Server shouldn't call Synchronizer.Model.IProcessor.Process(Synchronizer.Model.RequestVerboseType)");
}
void Synchronizer.Model.IProcessor.Process(Synchronizer.Model.RequestLazyLoad requestLazyLoad)
{
Monitor.LazyLoad(requestLazyLoad.Category);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e27a6a4e55657c4469a3397032ed9546
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a27215493476a2e43b65b286517deb35
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,60 @@
using System.Collections.Generic;
using UnityEngine;
#nullable enable
//namespace BgTools.PlayerPrefsEditor
namespace Meryel.UnityCodeAssist.Editor.Preferences
{
[System.Serializable]
public class PreferenceEntryHolder : ScriptableObject
{
public List<PreferenceEntry>? userDefList;
public List<PreferenceEntry>? unityDefList;
private void OnEnable()
{
hideFlags = HideFlags.DontSave;
userDefList ??= new List<PreferenceEntry>();
unityDefList ??= new List<PreferenceEntry>();
}
public void ClearLists()
{
userDefList?.Clear();
unityDefList?.Clear();
}
}
[System.Serializable]
public class PreferenceEntry
{
public enum PrefTypes
{
String = 0,
Int = 1,
Float = 2
}
public PrefTypes m_typeSelection;
public string? m_key;
// Need diffrend ones for auto type selection of serilizedProerty
public string? m_strValue;
public int m_intValue;
public float m_floatValue;
public string? ValueAsString()
{
return m_typeSelection switch
{
PrefTypes.String => m_strValue,
PrefTypes.Int => m_intValue.ToString(),
PrefTypes.Float => m_floatValue.ToString(),
_ => string.Empty,
};
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 38f9da72011fd244b94cc233de93ac92
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,370 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using UnityEditor;
using UnityEngine;
using UnityEditorInternal;
#nullable enable
namespace Meryel.UnityCodeAssist.Editor.Preferences
{
public class PreferenceMonitor
{
private static readonly Lazy<PreferenceMonitor> _instanceOfPlayerPrefs = new Lazy<PreferenceMonitor>(() => new PreferenceMonitor(true));
private static readonly Lazy<PreferenceMonitor> _instanceOfEditorPrefs = new Lazy<PreferenceMonitor>(() => new PreferenceMonitor(false));
public static PreferenceMonitor InstanceOfPlayerPrefs => _instanceOfPlayerPrefs.Value;
public static PreferenceMonitor InstanceOfEditorPrefs => _instanceOfEditorPrefs.Value;
//const int Limit = 128;
const int Limit = 8192;
/// <summary>
/// PlayerPrefs or EditorPrefs
/// </summary>
readonly bool isPlayerPrefs;
#region ErrorValues
private readonly int ERROR_VALUE_INT = int.MinValue;
private readonly string ERROR_VALUE_STR = "<UCA_ERR_2407201713>";
#endregion //ErrorValues
#pragma warning disable CS0414
private static string pathToPrefs = String.Empty;
private static string platformPathPrefix = @"~";
#pragma warning restore CS0414
//private string[] userDef;
//private string[] unityDef;
//private bool showSystemGroup = false;
private SerializedObject? serializedObject;
private ReorderableList? userDefList;
private ReorderableList? unityDefList;
private PreferenceEntryHolder? prefEntryHolder;
private PreferanceStorageAccessor? entryAccessor;
private bool updateView = false;
//private bool monitoring = false;
//private bool showLoadingIndicatorOverlay = false;
#if UNITY_EDITOR_LINUX
private readonly char[] invalidFilenameChars = { '"', '\\', '*', '/', ':', '<', '>', '?', '|' };
#elif UNITY_EDITOR_OSX
private readonly char[] invalidFilenameChars = { '$', '%', '&', '\\', '/', ':', '<', '>', '|', '~' };
#endif
PreferenceMonitor(bool isPlayerPrefs)
{
this.isPlayerPrefs = isPlayerPrefs;
OnEnable();
EditorApplication.update += Update;
}
~PreferenceMonitor()
{
OnDisable();
}
public void Bump()
{
Serilog.Log.Debug("Bumping preference {IsPlayerPrefs}", isPlayerPrefs);
RetrieveAndSendKeysAndValues(false);
}
private void RetrieveAndSendKeysAndValues(bool reloadKeys)
{
string[]? keys = GetKeys(reloadKeys);
if (keys == null)
return;
string[] values = GetKeyValues(reloadKeys, keys, out var stringKeys, out var integerKeys, out var floatKeys, out var booleanKeys);
if (isPlayerPrefs)
NetMQInitializer.Publisher?.SendPlayerPrefs(keys, values, stringKeys, integerKeys, floatKeys);
else
NetMQInitializer.Publisher?.SendEditorPrefs(keys, values, stringKeys, integerKeys, floatKeys, booleanKeys);
}
private void OnEnable()
{
#if UNITY_EDITOR_WIN
if (isPlayerPrefs)
pathToPrefs = @"SOFTWARE\Unity\UnityEditor\" + PlayerSettings.companyName + @"\" + PlayerSettings.productName;
else
pathToPrefs = @"Software\Unity Technologies\Unity Editor 5.x";
platformPathPrefix = @"<CurrentUser>";
entryAccessor = new WindowsPrefStorage(pathToPrefs);
#elif UNITY_EDITOR_OSX
if (isPlayerPrefs)
pathToPrefs = @"Library/Preferences/com." + MakeValidFileName(PlayerSettings.companyName) + "." + MakeValidFileName(PlayerSettings.productName) + ".plist";
else
pathToPrefs = @"Library/Preferences/com.unity3d.UnityEditor5.x.plist";
platformPathPrefix = @"~";
entryAccessor = new MacPrefStorage(pathToPrefs);
//entryAccessor.StartLoadingDelegate = () => { showLoadingIndicatorOverlay = true; };
//entryAccessor.StopLoadingDelegate = () => { showLoadingIndicatorOverlay = false; };
#elif UNITY_EDITOR_LINUX
if (isPlayerPrefs)
pathToPrefs = @".config/unity3d/" + MakeValidFileName(PlayerSettings.companyName) + "/" + MakeValidFileName(PlayerSettings.productName) + "/prefs";
else
pathToPrefs = @".local/share/unity3d/prefs";
platformPathPrefix = @"~";
entryAccessor = new LinuxPrefStorage(pathToPrefs);
#else
Serilog.Log.Warning("Undefined Unity Editor platform");
pathToPrefs = String.Empty;
platformPathPrefix = @"~";
entryAccessor = null;
#endif
if (entryAccessor != null)
{
entryAccessor.PrefEntryChangedDelegate = () => { updateView = true; };
entryAccessor.StartMonitoring();
}
}
// Handel view updates for monitored changes
// Necessary to avoid main thread access issue
private void Update()
{
if (updateView)
{
updateView = false;
//PrepareData();
//Repaint();
Serilog.Log.Debug("Updating preference {IsPlayerPrefs}", isPlayerPrefs);
RetrieveAndSendKeysAndValues(true);
}
}
private void OnDisable()
{
entryAccessor?.StopMonitoring();
}
private void InitReorderedList()
{
if (prefEntryHolder == null)
{
var tmp = Resources.FindObjectsOfTypeAll<PreferenceEntryHolder>();
if (tmp.Length > 0)
{
prefEntryHolder = tmp[0];
}
else
{
prefEntryHolder = ScriptableObject.CreateInstance<PreferenceEntryHolder>();
}
}
serializedObject ??= new SerializedObject(prefEntryHolder);
userDefList = new ReorderableList(serializedObject, serializedObject.FindProperty("userDefList"), false, true, true, true);
unityDefList = new ReorderableList(serializedObject, serializedObject.FindProperty("unityDefList"), false, true, false, false);
}
private string[]? GetKeys(bool reloadKeys)
{
if (entryAccessor == null)
{
Serilog.Log.Warning($"{nameof(entryAccessor)} is null");
return null;
}
string[] keys = entryAccessor.GetKeys(reloadKeys);
if (keys.Length > Limit)
keys = keys.Where(k => !k.StartsWith("unity.") && !k.StartsWith("UnityGraphicsQuality")).Take(Limit).ToArray();
return keys;
}
string[]? _cachedKeyValues = null;
string[]? _cachedStringKeys = null;
string[]? _cachedIntegerKeys = null;
string[]? _cachedFloatKeys = null;
string[]? _cachedBooleanKeys = null;
private string[] GetKeyValues(bool reloadData, string[] keys,
out string[] stringKeys, out string[] integerKeys, out string[] floatKeys, out string[] booleanKeys)
{
if (!reloadData && _cachedKeyValues != null && _cachedKeyValues.Length == keys.Length)
{
stringKeys = _cachedStringKeys!;
integerKeys = _cachedIntegerKeys!;
floatKeys = _cachedFloatKeys!;
booleanKeys = _cachedBooleanKeys!;
return _cachedKeyValues;
}
string[] values = new string[keys.Length];
var stringKeyList = new List<string>();
var integerKeyList = new List<string>();
var floatKeyList = new List<string>();
var boolenKeyList = new List<string>();
for (int i = 0; i < keys.Length; i++)
{
var key = keys[i];
string stringValue;
if (isPlayerPrefs)
stringValue = PlayerPrefs.GetString(key, ERROR_VALUE_STR);
else
stringValue = EditorPrefs.GetString(key, ERROR_VALUE_STR);
if (stringValue != ERROR_VALUE_STR)
{
values[i] = stringValue;
stringKeyList.Add(key);
continue;
}
float floatValue;
if (isPlayerPrefs)
floatValue = PlayerPrefs.GetFloat(key, float.NaN);
else
floatValue = EditorPrefs.GetFloat(key, float.NaN);
if (!float.IsNaN(floatValue))
{
values[i] = floatValue.ToString();
floatKeyList.Add(key);
continue;
}
int intValue;
if (isPlayerPrefs)
intValue = PlayerPrefs.GetInt(key, ERROR_VALUE_INT);
else
intValue = EditorPrefs.GetInt(key, ERROR_VALUE_INT);
if (intValue != ERROR_VALUE_INT)
{
values[i] = intValue.ToString();
integerKeyList.Add(key);
continue;
}
bool boolValue = false;
if (!isPlayerPrefs)
{
bool boolValueTrue = EditorPrefs.GetBool(key, true);
bool boolValueFalse = EditorPrefs.GetBool(key, false);
boolValue = boolValueFalse;
if (boolValueTrue == boolValueFalse)
{
values[i] = boolValueTrue.ToString();
boolenKeyList.Add(key);
continue;
}
}
values[i] = string.Empty;
if (isPlayerPrefs)
{
// Keys with ? causing problems, just ignore them
if (key.Contains("?"))
Serilog.Log.Debug("Invalid {PreferenceType} KEY WITH '?', '{Key}' at {Location}, str:{StringValue}, int:{IntegerValue}, float:{FloatValue}, bool:{BooleanValue}",
(isPlayerPrefs ? "PlayerPrefs" : "EditorPrefs"), key, nameof(GetKeyValues),
stringValue, intValue, floatValue, boolValue);
else
// EditorPrefs gives error for some keys
Serilog.Log.Error("Invalid {PreferenceType} '{Key}' at {Location}, str:{StringValue}, int:{IntegerValue}, float:{FloatValue}, bool:{BooleanValue}",
(isPlayerPrefs ? "PlayerPrefs" : "EditorPrefs"), key, nameof(GetKeyValues),
stringValue, intValue, floatValue, boolValue);
}
}
stringKeys = stringKeyList.ToArray();
integerKeys = integerKeyList.ToArray();
floatKeys = floatKeyList.ToArray();
booleanKeys = boolenKeyList.ToArray();
_cachedKeyValues = values;
_cachedStringKeys = stringKeys;
_cachedIntegerKeys = integerKeys;
_cachedFloatKeys = floatKeys;
_cachedBooleanKeys = booleanKeys;
return values;
}
private void LoadKeys(out string[]? userDef, out string[]? unityDef, bool reloadKeys)
{
if(entryAccessor == null)
{
userDef = null;
unityDef = null;
return;
}
string[] keys = entryAccessor.GetKeys(reloadKeys);
//keys.ToList().ForEach( e => { Debug.Log(e); } );
// Seperate keys int unity defined and user defined
Dictionary<bool, List<string>> groups = keys
.GroupBy((key) => key.StartsWith("unity.") || key.StartsWith("UnityGraphicsQuality"))
.ToDictionary((g) => g.Key, (g) => g.ToList());
unityDef = (groups.ContainsKey(true)) ? groups[true].ToArray() : new string[0];
userDef = (groups.ContainsKey(false)) ? groups[false].ToArray() : new string[0];
}
#if (UNITY_EDITOR_LINUX || UNITY_EDITOR_OSX)
private string MakeValidFileName(string unsafeFileName)
{
string normalizedFileName = unsafeFileName.Trim().Normalize(NormalizationForm.FormD);
StringBuilder stringBuilder = new StringBuilder();
// We need to use a TextElementEmumerator in order to support UTF16 characters that may take up more than one char(case 1169358)
TextElementEnumerator charEnum = StringInfo.GetTextElementEnumerator(normalizedFileName);
while (charEnum.MoveNext())
{
string c = charEnum.GetTextElement();
if (c.Length == 1 && invalidFilenameChars.Contains(c[0]))
{
stringBuilder.Append('_');
continue;
}
UnicodeCategory unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c, 0);
if (unicodeCategory != UnicodeCategory.NonSpacingMark)
stringBuilder.Append(c);
}
return stringBuilder.ToString().Normalize(NormalizationForm.FormC);
}
#endif
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7885cbf1aab77214c8bbcf789440022d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,287 @@
using System;
using System.Linq;
#if UNITY_EDITOR_WIN
using Microsoft.Win32;
using System.Text;
#elif UNITY_EDITOR_OSX
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
#elif UNITY_EDITOR_LINUX
using System.IO;
using System.Xml;
using System.Xml.Linq;
#endif
#nullable enable
//namespace BgTools.PlayerPrefsEditor
namespace Meryel.UnityCodeAssist.Editor.Preferences
{
public abstract class PreferanceStorageAccessor
{
protected string prefPath;
protected string[] cachedData = new string[0];
protected abstract void FetchKeysFromSystem();
protected PreferanceStorageAccessor(string pathToPrefs)
{
prefPath = pathToPrefs;
}
public string[] GetKeys(bool reloadData = true)
{
if (reloadData || cachedData.Length == 0)
{
FetchKeysFromSystem();
}
return cachedData;
}
public Action? PrefEntryChangedDelegate;
protected bool ignoreNextChange = false;
public void IgnoreNextChange()
{
ignoreNextChange = true;
}
protected virtual void OnPrefEntryChanged()
{
if (ignoreNextChange)
{
ignoreNextChange = false;
return;
}
PrefEntryChangedDelegate?.Invoke();
}
public Action? StartLoadingDelegate;
public Action? StopLoadingDelegate;
public abstract void StartMonitoring();
public abstract void StopMonitoring();
public abstract bool IsMonitoring();
}
#if UNITY_EDITOR_WIN
public class WindowsPrefStorage : PreferanceStorageAccessor
{
readonly RegistryMonitor monitor;
public WindowsPrefStorage(string pathToPrefs) : base(pathToPrefs)
{
monitor = new RegistryMonitor(RegistryHive.CurrentUser, prefPath);
monitor.RegChanged += new EventHandler(OnRegChanged);
}
private void OnRegChanged(object sender, EventArgs e)
{
OnPrefEntryChanged();
}
protected override void FetchKeysFromSystem()
{
cachedData = new string[0];
using (RegistryKey rootKey = Registry.CurrentUser.OpenSubKey(prefPath))
{
if (rootKey != null)
{
cachedData = rootKey.GetValueNames();
rootKey.Close();
}
}
// Clean <key>_h3320113488 nameing
//cachedData = cachedData.Select((key) => { return key.Substring(0, key.LastIndexOf("_h", StringComparison.Ordinal)); }).ToArray();
for (int i = 0; i < cachedData.Length; i++)
{
var indexOfSuffix = cachedData[i].LastIndexOf("_h", StringComparison.Ordinal);
if (indexOfSuffix >= 0)
cachedData[i] = cachedData[i].Substring(0, indexOfSuffix);
}
EncodeAnsiInPlace();
}
public override void StartMonitoring()
{
monitor.Start();
}
public override void StopMonitoring()
{
monitor.Stop();
}
public override bool IsMonitoring()
{
return monitor.IsMonitoring;
}
private void EncodeAnsiInPlace()
{
Encoding utf8 = Encoding.UTF8;
Encoding ansi = Encoding.GetEncoding(1252);
for (int i = 0; i < cachedData.Length; i++)
{
cachedData[i] = utf8.GetString(ansi.GetBytes(cachedData[i]));
}
}
}
#elif UNITY_EDITOR_LINUX
public class LinuxPrefStorage : PreferanceStorageAccessor
{
readonly FileSystemWatcher fileWatcher;
public LinuxPrefStorage(string pathToPrefs) : base(Path.Combine(Environment.GetEnvironmentVariable("HOME"), pathToPrefs))
{
fileWatcher = new FileSystemWatcher
{
Path = Path.GetDirectoryName(prefPath),
NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite,
Filter = "prefs"
};
fileWatcher.Changed += OnWatchedFileChanged;
}
protected override void FetchKeysFromSystem()
{
cachedData = new string[0];
if (File.Exists(prefPath))
{
XmlReaderSettings settings = new XmlReaderSettings();
XmlReader reader = XmlReader.Create(prefPath, settings);
XDocument doc = XDocument.Load(reader);
cachedData = doc.Element("unity_prefs").Elements().Select((e) => e.Attribute("name").Value).ToArray();
}
}
public override void StartMonitoring()
{
fileWatcher.EnableRaisingEvents = true;
}
public override void StopMonitoring()
{
fileWatcher.EnableRaisingEvents = false;
}
public override bool IsMonitoring()
{
return fileWatcher.EnableRaisingEvents;
}
private void OnWatchedFileChanged(object source, FileSystemEventArgs e)
{
OnPrefEntryChanged();
}
}
#elif UNITY_EDITOR_OSX
public class MacPrefStorage : PreferanceStorageAccessor
{
private readonly FileSystemWatcher fileWatcher;
private readonly DirectoryInfo prefsDirInfo;
private readonly String prefsFileNameWithoutExtension;
public MacPrefStorage(string pathToPrefs) : base(Path.Combine(Environment.GetEnvironmentVariable("HOME"), pathToPrefs))
{
prefsDirInfo = new DirectoryInfo(Path.GetDirectoryName(prefPath));
prefsFileNameWithoutExtension = Path.GetFileNameWithoutExtension(prefPath);
fileWatcher = new FileSystemWatcher
{
Path = Path.GetDirectoryName(prefPath),
NotifyFilter = NotifyFilters.LastWrite,
Filter = Path.GetFileName(prefPath)
};
// MAC delete the old and create a new file instead of updating
fileWatcher.Created += OnWatchedFileChanged;
}
protected override void FetchKeysFromSystem()
{
// Workaround to avoid incomplete tmp phase from MAC OS
foreach (FileInfo info in prefsDirInfo.GetFiles())
{
// Check if tmp PlayerPrefs file exist
if (info.FullName.Contains(prefsFileNameWithoutExtension) && !info.FullName.EndsWith(".plist"))
{
StartLoadingDelegate?.Invoke();
return;
}
}
StopLoadingDelegate?.Invoke();
cachedData = new string[0];
if (File.Exists(prefPath))
{
string fixedPrefsPath = prefPath.Replace("\"", "\\\"").Replace("'", "\\'").Replace("`", "\\`");
var cmdStr = string.Format(@"-p '{0}'", fixedPrefsPath);
string stdOut = String.Empty;
string errOut = String.Empty;
var process = new System.Diagnostics.Process();
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "plutil";
process.StartInfo.Arguments = cmdStr;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.OutputDataReceived += new DataReceivedEventHandler((sender, evt) => { stdOut += evt.Data + "\n"; });
process.ErrorDataReceived += new DataReceivedEventHandler((sender, evt) => { errOut += evt.Data + "\n"; });
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
MatchCollection matches = Regex.Matches(stdOut, @"(?: "")(.*)(?:"" =>.*)");
cachedData = matches.Cast<Match>().Select((e) => e.Groups[1].Value).ToArray();
}
}
public override void StartMonitoring()
{
fileWatcher.EnableRaisingEvents = true;
}
public override void StopMonitoring()
{
fileWatcher.EnableRaisingEvents = false;
}
public override bool IsMonitoring()
{
return fileWatcher.EnableRaisingEvents;
}
private void OnWatchedFileChanged(object source, FileSystemEventArgs e)
{
OnPrefEntryChanged();
}
}
#endif
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5a10351133ed64a488d6be3043f5402a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,339 @@

/*
* Thanks to gr0ss for the inspiration.
*
* https://github.com/gr0ss/RegistryMonitor
*
* 11/08/2019
*/
using System;
using System.ComponentModel;
using System.IO;
using System.Threading;
using System.Runtime.InteropServices;
using Microsoft.Win32;
//namespace BgTools.PlayerPrefsEditor
namespace Meryel.UnityCodeAssist.Editor.Preferences
{
public class RegistryMonitor : IDisposable
{
#region P/Invoke
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern int RegOpenKeyEx(IntPtr hKey, string subKey, uint options, int samDesired, out IntPtr phkResult);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern int RegNotifyChangeKeyValue(IntPtr hKey, bool bWatchSubtree, RegChangeNotifyFilter dwNotifyFilter, IntPtr hEvent, bool fAsynchronous);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern int RegCloseKey(IntPtr hKey);
private const int KEY_QUERY_VALUE = 0x0001;
private const int KEY_NOTIFY = 0x0010;
private const int STANDARD_RIGHTS_READ = 0x00020000;
private static readonly IntPtr HKEY_CLASSES_ROOT = new IntPtr(unchecked((int)0x80000000));
private static readonly IntPtr HKEY_CURRENT_USER = new IntPtr(unchecked((int)0x80000001));
private static readonly IntPtr HKEY_LOCAL_MACHINE = new IntPtr(unchecked((int)0x80000002));
private static readonly IntPtr HKEY_USERS = new IntPtr(unchecked((int)0x80000003));
private static readonly IntPtr HKEY_PERFORMANCE_DATA = new IntPtr(unchecked((int)0x80000004));
private static readonly IntPtr HKEY_CURRENT_CONFIG = new IntPtr(unchecked((int)0x80000005));
private static readonly IntPtr HKEY_DYN_DATA = new IntPtr(unchecked((int)0x80000006));
#endregion
#region Event handling
/// <summary>
/// Occurs when the specified registry key has changed.
/// </summary>
public event EventHandler RegChanged;
/// <summary>
/// Raises the <see cref="RegChanged"/> event.
/// </summary>
/// <remarks>
/// <p>
/// <b>OnRegChanged</b> is called when the specified registry key has changed.
/// </p>
/// <note type="inheritinfo">
/// When overriding <see cref="OnRegChanged"/> in a derived class, be sure to call
/// the base class's <see cref="OnRegChanged"/> method.
/// </note>
/// </remarks>
protected virtual void OnRegChanged()
{
RegChanged?.Invoke(this, null);
}
/// <summary>
/// Occurs when the access to the registry fails.
/// </summary>
public event ErrorEventHandler Error;
/// <summary>
/// Raises the <see cref="Error"/> event.
/// </summary>
/// <param name="e">The <see cref="Exception"/> which occured while watching the registry.</param>
/// <remarks>
/// <p>
/// <b>OnError</b> is called when an exception occurs while watching the registry.
/// </p>
/// <note type="inheritinfo">
/// When overriding <see cref="OnError"/> in a derived class, be sure to call
/// the base class's <see cref="OnError"/> method.
/// </note>
/// </remarks>
protected virtual void OnError(Exception e)
{
Error?.Invoke(this, new ErrorEventArgs(e));
}
#endregion
#region Private member variables
private IntPtr _registryHive;
private string _registrySubName;
private readonly object _threadLock = new object();
private Thread _thread;
private bool _disposed = false;
private readonly ManualResetEvent _eventTerminate = new ManualResetEvent(false);
private RegChangeNotifyFilter _regFilter = RegChangeNotifyFilter.Key | RegChangeNotifyFilter.Attribute | RegChangeNotifyFilter.Value | RegChangeNotifyFilter.Security;
#endregion
/// <summary>
/// Initializes a new instance of the <see cref="RegistryMonitor"/> class.
/// </summary>
/// <param name="registryKey">The registry key to monitor.</param>
public RegistryMonitor(RegistryKey registryKey)
{
InitRegistryKey(registryKey.Name);
}
/// <summary>
/// Initializes a new instance of the <see cref="RegistryMonitor"/> class.
/// </summary>
/// <param name="name">The name.</param>
public RegistryMonitor(string name)
{
if (name == null || name.Length == 0)
throw new ArgumentNullException("name");
InitRegistryKey(name);
}
/// <summary>
/// Initializes a new instance of the <see cref="RegistryMonitor"/> class.
/// </summary>
/// <param name="registryHive">The registry hive.</param>
/// <param name="subKey">The sub key.</param>
public RegistryMonitor(RegistryHive registryHive, string subKey)
{
InitRegistryKey(registryHive, subKey);
}
/// <summary>
/// Disposes this object.
/// </summary>
public void Dispose()
{
Stop();
_disposed = true;
GC.SuppressFinalize(this);
}
/// <summary>
/// Gets or sets the <see cref="RegChangeNotifyFilter">RegChangeNotifyFilter</see>.
/// </summary>
public RegChangeNotifyFilter RegChangeNotifyFilter
{
get { return _regFilter; }
set
{
lock (_threadLock)
{
if (IsMonitoring)
throw new InvalidOperationException("Monitoring thread is already running");
_regFilter = value;
}
}
}
#region Initialization
private void InitRegistryKey(RegistryHive hive, string name)
{
_registryHive = hive switch
{
RegistryHive.ClassesRoot => HKEY_CLASSES_ROOT,
RegistryHive.CurrentConfig => HKEY_CURRENT_CONFIG,
RegistryHive.CurrentUser => HKEY_CURRENT_USER,
RegistryHive.DynData => HKEY_DYN_DATA,
RegistryHive.LocalMachine => HKEY_LOCAL_MACHINE,
RegistryHive.PerformanceData => HKEY_PERFORMANCE_DATA,
RegistryHive.Users => HKEY_USERS,
_ => throw new InvalidEnumArgumentException("hive", (int)hive, typeof(RegistryHive)),
};
_registrySubName = name;
}
private void InitRegistryKey(string name)
{
string[] nameParts = name.Split('\\');
switch (nameParts[0])
{
case "HKEY_CLASSES_ROOT":
case "HKCR":
_registryHive = HKEY_CLASSES_ROOT;
break;
case "HKEY_CURRENT_USER":
case "HKCU":
_registryHive = HKEY_CURRENT_USER;
break;
case "HKEY_LOCAL_MACHINE":
case "HKLM":
_registryHive = HKEY_LOCAL_MACHINE;
break;
case "HKEY_USERS":
_registryHive = HKEY_USERS;
break;
case "HKEY_CURRENT_CONFIG":
_registryHive = HKEY_CURRENT_CONFIG;
break;
default:
_registryHive = IntPtr.Zero;
throw new ArgumentException("The registry hive '" + nameParts[0] + "' is not supported", "value");
}
_registrySubName = String.Join("\\", nameParts, 1, nameParts.Length - 1);
}
#endregion
/// <summary>
/// <b>true</b> if this <see cref="RegistryMonitor"/> object is currently monitoring;
/// otherwise, <b>false</b>.
/// </summary>
public bool IsMonitoring
{
get { return _thread != null; }
}
/// <summary>
/// Start monitoring.
/// </summary>
public void Start()
{
if (_disposed)
throw new ObjectDisposedException(null, "This instance is already disposed");
lock (_threadLock)
{
if (!IsMonitoring)
{
_eventTerminate.Reset();
_thread = new Thread(new ThreadStart(MonitorThread)) { IsBackground = true };
_thread.Start();
}
}
}
/// <summary>
/// Stops the monitoring thread.
/// </summary>
public void Stop()
{
if (_disposed)
throw new ObjectDisposedException(null, "This instance is already disposed");
lock (_threadLock)
{
Thread thread = _thread;
if (thread != null)
{
_eventTerminate.Set();
thread.Join();
}
}
}
private void MonitorThread()
{
try
{
ThreadLoop();
}
catch (Exception e)
{
OnError(e);
}
_thread = null;
}
private void ThreadLoop()
{
int result = RegOpenKeyEx(_registryHive, _registrySubName, 0, STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_NOTIFY, out IntPtr registryKey);
if (result != 0)
{
throw new Win32Exception(result);
}
try
{
AutoResetEvent _eventNotify = new AutoResetEvent(false);
WaitHandle[] waitHandles = new WaitHandle[] { _eventNotify, _eventTerminate };
while (!_eventTerminate.WaitOne(0, true))
{
result = RegNotifyChangeKeyValue(registryKey, true, _regFilter, _eventNotify.SafeWaitHandle.DangerousGetHandle(), true);
if (result != 0)
{
throw new Win32Exception(result);
}
if (WaitHandle.WaitAny(waitHandles) == 0)
{
OnRegChanged();
}
}
}
finally
{
if (registryKey != IntPtr.Zero)
{
RegCloseKey(registryKey);
}
}
}
}
/// <summary>
/// Filter for notifications reported by <see cref="RegistryMonitor"/>.
/// </summary>
[Flags]
public enum RegChangeNotifyFilter
{
/// <summary>Notify the caller if a subkey is added or deleted.</summary>
Key = 1,
/// <summary>Notify the caller of changes to the attributes of the key,
/// such as the security descriptor information.</summary>
Attribute = 2,
/// <summary>Notify the caller of changes to a value of the key. This can
/// include adding or deleting a value, or changing an existing value.</summary>
Value = 4,
/// <summary>Notify the caller of changes to the security descriptor
/// of the key.</summary>
Security = 8,
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 231ce51d1620ff843ba99ae307c881e2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,307 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
#nullable enable
namespace Meryel.UnityCodeAssist.Editor
{
public class ScriptFinder //: MonoBehaviour
{
static Type? GetType123(string typeName)
{
//**--
//**--
/*
* for performance,
* check assembly-csharp, assembly-csharp-editor, assembly-csharp-first-pass,assembly-csharp-editor-first-pass
* first, (then maybe asmdef dlls), then check mscorlib and other referenced dlls
*/
//**--use typecache???
//TypeCache
//**--check this again
//https://github.com/Unity-Technologies/SuperScience/blob/main/Editor/GlobalNamespaceWatcher.cs
// Try Type.GetType() first. This will work with types defined
// by the Mono runtime, in the same assembly as the caller, etc.
Type type = Type.GetType(typeName);
// If it worked, then we're done here
if (type != null)
{
return type;
}
// Attempt to search for type on the loaded assemblies
Assembly[] currentAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in currentAssemblies)
{
type = assembly.GetType(typeName);
if (type != null)
{
return type;
}
}
// If we still haven't found the proper type, we can enumerate all of the
// loaded assemblies and see if any of them define the type
var currentAssembly = Assembly.GetExecutingAssembly();
var referencedAssemblies = currentAssembly.GetReferencedAssemblies();
foreach (var assemblyName in referencedAssemblies)
{
// Load the referenced assembly
var assembly = Assembly.Load(assemblyName);
if (assembly != null)
{
// See if that assembly defines the named type
type = assembly.GetType(typeName);
if (type != null)
{
return type;
}
}
}
// The type just couldn't be found...
return null;
}
public static bool FindInstanceOfType(string typeName, string docPath, out GameObject? gameObjectInstanceOfType, out ScriptableObject? scriptableObjectInstanceOfType)
{
gameObjectInstanceOfType = null;
scriptableObjectInstanceOfType = null;
var type = GetType123(typeName);
if (type == null)
{
// Possibly a class has been created in Visual Studio, and these changes are not reflected in Unity domain yet
// We can force Unity to recompile and get the type, but since there will be no instance of that type, it won't be of any use, will be just a performance burden
Serilog.Log.Debug("{Type} type couldn't be found", typeName);
return false;
}
var obj = GetObjectOfType(type, out var requestVerboseType);
if (requestVerboseType)
NetMQInitializer.Publisher?.SendRequestVerboseType(typeName, docPath);
if (obj != null && obj is GameObject go)
{
gameObjectInstanceOfType = go;
return true;
}
else if (obj != null && obj is ScriptableObject so)
{
scriptableObjectInstanceOfType = so;
return true;
}
Serilog.Log.Debug("Instance of {Type} type couldn't be found", typeName);
return false;
}
static UnityEngine.Object? GetObjectOfType(Type type, out bool requestVerboseType)
{
requestVerboseType = false;
var isMonoBehaviour = type.IsSubclassOf(typeof(MonoBehaviour));
var isScriptableObject = type.IsSubclassOf(typeof(ScriptableObject));
if (!isMonoBehaviour && !isScriptableObject)
{
// Possibly a class's base class changed from none to MonoBehaviour in Visual Studio, and these changes are not reflected in Unity domain yet
// We can force Unity to recompile and get the type correctly, but since there will be no instance of that type, it won't be of any use, will be just a performance burden
Serilog.Log.Debug("{Type} is not a valid Unity object", type.ToString());
//requestVerboseType = true;
return null;
}
UnityEngine.Object? obj;
obj = getObjectToSend(Selection.activeGameObject, type);
if (obj != null)
return obj;
obj = getObjectToSend(Selection.activeTransform, type);
if (obj != null)
return obj;
obj = getObjectToSend(Selection.activeObject, type);
if (obj != null)
return obj;
//**--check source code of this, for sorting
var filteredArray = Selection.GetFiltered(type, SelectionMode.Unfiltered);
if (filteredArray != null)
{
//**--sort
foreach (var filtered in filteredArray)
{
obj = getObjectToSend(filtered, type);
if (obj != null)
return obj;
}
}
//**--rest can be slow, try avoiding them, make own db etc
//**--can add a stop-wacher and add warning if slow as well
//**--can also cache the result
try
{
// UnityEngine.Object.FindObjectOfType is deprecated in new versions of Unity
#if UNITY_2022_3 || UNITY_2023_1_OR_NEWER
// Object.FindAnyObjectOfType doesn't return Assets (for example meshes, textures, or prefabs), or inactive objects. It also doesn't return objects that have HideFlags.DontSave set.
obj = UnityEngine.Object.FindAnyObjectByType(type);
#else
// Object.FindObjectOfType will not return Assets (meshes, textures, prefabs, ...) or inactive objects. It will not return an object that has HideFlags.DontSave set.
obj = UnityEngine.Object.FindObjectOfType(type);
#endif
}
catch (Exception ex)
{
Serilog.Log.Warning(ex, "FindObjectOfType/FindAnyObjectByType failed for {Type}, mb:{isMB}, so:{isSO}", type.ToString(), isMonoBehaviour, isScriptableObject);
}
obj = getObjectToSend(obj, type);
if (obj != null)
return obj;
UnityEngine.Object[]? arr = null;
try
{
// This function can return any type of Unity object that is loaded, including game objects, prefabs, materials, meshes, textures, etc.
// Contrary to Object.FindObjectsOfType this function will also list disabled objects.
arr = Resources.FindObjectsOfTypeAll(type);
}
catch (Exception ex)
{
//var isMonoBehaviour = type.IsSubclassOf(typeof(MonoBehaviour));
//var isScriptableObject = type.IsSubclassOf(typeof(ScriptableObject));
Serilog.Log.Warning(ex, "FindObjectsOfTypeAll failed for {Type}, mb:{isMB}, so:{isSO}", type.ToString(), isMonoBehaviour, isScriptableObject);
}
if (arr != null)
{
//**--sort
foreach (var item in arr)
{
obj = getObjectToSend(item, type);
if (obj != null)
return obj;
}
}
return obj;
static UnityEngine.Object? getObjectToSend(UnityEngine.Object? obj, Type type)
{
if (obj == null || !obj)
return null;
if (obj is GameObject go)
{
if (!go)
return null;
if (isTypeComponent(type) && go.GetComponent(type) != null)
return go;
}
else if (obj is Transform transform)
{
go = transform.gameObject;
if (!go)
return null;
if (isTypeComponent(type) && go.GetComponent(type) != null)
return go;
}
else if (obj is Component comp)
{
go = comp.gameObject;
if (!go)
return null;
else
return go;
}
else if (obj is ScriptableObject so)
{
if (!so)
return null;
else
return so;
}
return null;
}
static bool isTypeComponent(Type type)
{
var componentType = typeof(Component);//**--cache these types
if (type == componentType || type.IsSubclassOf(componentType))
return true;
// MonoBehaviour is Component, so below is unnecessary
//var monoBehaviourType = typeof(MonoBehaviour);
//if (type == monoBehaviourType || type.IsSubclassOf(monoBehaviourType))
// return true;
//else if(type is interface)//**--
return false;
}
}
public static void DENEMEEEE()
{
//UnityEditor.SceneManagement.EditorSceneManager.all
//AssetDatabase.get
foreach (var sceneGUID in AssetDatabase.FindAssets("t:Scene", new string[] { "Assets" }))
{
var scenePath = AssetDatabase.GUIDToAssetPath(sceneGUID);
Debug.Log("scenePath: " + scenePath);
//EditorSceneManager.OpenScene(scenePath);
//var scene = EditorSceneManager.GetActiveScene();
}
var assets = AssetDatabase.FindAssets("Deneme_OtherScene");
Debug.Log("Assets: " + assets.Length);
foreach (var assetGuid in assets)
{
var assetPath = AssetDatabase.GUIDToAssetPath(assetGuid);
Debug.Log("Asset: " + assetGuid + " " + assetPath);
}
}
public static bool GetActiveGameObject(out GameObject activeGameObject)
{
activeGameObject = Selection.activeGameObject;
return activeGameObject ? true : false;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c8633a6fdc230b446985c424ba228a05
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,81 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
#nullable enable
namespace Meryel.UnityCodeAssist.Editor
{
public class StatusWindow : EditorWindow
{
GUIStyle? styleLabel;
public static void Display()
{
// Get existing open window or if none, make a new one:
var window = GetWindow<StatusWindow>();
window.Show();
NetMQInitializer.Publisher?.SendConnectionInfo();
Serilog.Log.Debug("Displaying status window");
NetMQInitializer.Publisher?.SendAnalyticsEvent("Gui", "StatusWindow_Display");
}
private void OnEnable()
{
//**--icon
//var icon = AssetDatabase.LoadAssetAtPath<Texture>("Assets/Sprites/Gear.png");
//titleContent = new GUIContent("Code Assist", icon);
titleContent = new GUIContent(Assister.Title);
}
private void OnGUI()
{
var hasAnyClient = NetMQInitializer.Publisher?.clients.Any() == true;
styleLabel ??= new GUIStyle(GUI.skin.label)
{
wordWrap = true,
alignment = TextAnchor.MiddleLeft,
};
if (hasAnyClient)
{
EditorGUILayout.LabelField($"Code Assist is working!", styleLabel, GUILayout.ExpandWidth(true));
foreach (var client in NetMQInitializer.Publisher!.clients)
{
EditorGUILayout.LabelField($"Connected to {client.ContactInfo}", styleLabel, GUILayout.ExpandWidth(true));
}
}
else
{
EditorGUILayout.LabelField($"Code Assist isn't working!", styleLabel, GUILayout.ExpandWidth(true));
EditorGUILayout.LabelField($"No IDE found", styleLabel, GUILayout.ExpandWidth(true));
}
#if MERYEL_UCA_LITE_VERSION
EditorGUILayout.LabelField($"", styleLabel, GUILayout.ExpandWidth(true));
EditorGUILayout.LabelField($"This is the lite version of Code Assist with limited features.", styleLabel, GUILayout.ExpandWidth(true));
EditorGUILayout.LabelField($"To unlock all of the features, get the full version.", styleLabel, GUILayout.ExpandWidth(true));
if (GUILayout.Button("Get full version"))
{
Application.OpenURL("http://u3d.as/2N2H");
}
#endif // MERYEL_UCA_LITE_VERSION
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: deeb8a5dd8f358c48aaaf8edc44efa59
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b66a6756b1d4b2c49b08193ba0d4551a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,376 @@
// copied from
// https://github.com/zanders3/json/blob/master/src/JSONParser.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
//namespace TinyJson
namespace Meryel.UnityCodeAssist.Editor.TinyJson
{
// Really simple JSON parser in ~300 lines
// - Attempts to parse JSON files with minimal GC allocation
// - Nice and simple "[1,2,3]".FromJson<List<int>>() API
// - Classes and structs can be parsed too!
// class Foo { public int Value; }
// "{\"Value\":10}".FromJson<Foo>()
// - Can parse JSON without type information into Dictionary<string,object> and List<object> e.g.
// "[1,2,3]".FromJson<object>().GetType() == typeof(List<object>)
// "{\"Value\":10}".FromJson<object>().GetType() == typeof(Dictionary<string,object>)
// - No JIT Emit support to support AOT compilation on iOS
// - Attempts are made to NOT throw an exception if the JSON is corrupted or invalid: returns null instead.
// - Only public fields and property setters on classes/structs will be written to
//
// Limitations:
// - No JIT Emit support to parse structures quickly
// - Limited to parsing <2GB JSON files (due to int.MaxValue)
// - Parsing of abstract classes or interfaces is NOT supported and will throw an exception.
public static class JsonParser
{
[ThreadStatic] static Stack<List<string>> splitArrayPool;
[ThreadStatic] static StringBuilder stringBuilder;
[ThreadStatic] static Dictionary<Type, Dictionary<string, FieldInfo>> fieldInfoCache;
[ThreadStatic] static Dictionary<Type, Dictionary<string, PropertyInfo>> propertyInfoCache;
//public static T FromJson<T>(this string json)
public static T FromJson<T>(string json)
{
// Initialize, if needed, the ThreadStatic variables
propertyInfoCache ??= new Dictionary<Type, Dictionary<string, PropertyInfo>>();
fieldInfoCache ??= new Dictionary<Type, Dictionary<string, FieldInfo>>();
stringBuilder ??= new StringBuilder();
splitArrayPool ??= new Stack<List<string>>();
//Remove all whitespace not within strings to make parsing simpler
stringBuilder.Length = 0;
for (int i = 0; i < json.Length; i++)
{
char c = json[i];
if (c == '"')
{
i = AppendUntilStringEnd(true, i, json);
continue;
}
if (char.IsWhiteSpace(c))
continue;
stringBuilder.Append(c);
}
//Parse the thing!
return (T)ParseValue(typeof(T), stringBuilder.ToString());
}
static int AppendUntilStringEnd(bool appendEscapeCharacter, int startIdx, string json)
{
stringBuilder.Append(json[startIdx]);
for (int i = startIdx + 1; i < json.Length; i++)
{
if (json[i] == '\\')
{
if (appendEscapeCharacter)
stringBuilder.Append(json[i]);
stringBuilder.Append(json[i + 1]);
i++;//Skip next character as it is escaped
}
else if (json[i] == '"')
{
stringBuilder.Append(json[i]);
return i;
}
else
stringBuilder.Append(json[i]);
}
return json.Length - 1;
}
//Splits { <value>:<value>, <value>:<value> } and [ <value>, <value> ] into a list of <value> strings
static List<string> Split(string json)
{
List<string> splitArray = splitArrayPool.Count > 0 ? splitArrayPool.Pop() : new List<string>();
splitArray.Clear();
if (json.Length == 2)
return splitArray;
int parseDepth = 0;
stringBuilder.Length = 0;
for (int i = 1; i < json.Length - 1; i++)
{
switch (json[i])
{
case '[':
case '{':
parseDepth++;
break;
case ']':
case '}':
parseDepth--;
break;
case '"':
i = AppendUntilStringEnd(true, i, json);
continue;
case ',':
case ':':
if (parseDepth == 0)
{
splitArray.Add(stringBuilder.ToString());
stringBuilder.Length = 0;
continue;
}
break;
}
stringBuilder.Append(json[i]);
}
splitArray.Add(stringBuilder.ToString());
return splitArray;
}
internal static object ParseValue(Type type, string json)
{
if (type == typeof(string))
{
if (json.Length <= 2)
return string.Empty;
StringBuilder parseStringBuilder = new StringBuilder(json.Length);
for (int i = 1; i < json.Length - 1; ++i)
{
if (json[i] == '\\' && i + 1 < json.Length - 1)
{
int j = "\"\\nrtbf/".IndexOf(json[i + 1]);
if (j >= 0)
{
parseStringBuilder.Append("\"\\\n\r\t\b\f/"[j]);
++i;
continue;
}
if (json[i + 1] == 'u' && i + 5 < json.Length - 1)
{
//UInt32 c = 0;
if (UInt32.TryParse(json.Substring(i + 2, 4), System.Globalization.NumberStyles.AllowHexSpecifier, null, out UInt32 c))
{
parseStringBuilder.Append((char)c);
i += 5;
continue;
}
}
}
parseStringBuilder.Append(json[i]);
}
return parseStringBuilder.ToString();
}
if (type.IsPrimitive)
{
var result = Convert.ChangeType(json, type, System.Globalization.CultureInfo.InvariantCulture);
return result;
}
if (type == typeof(decimal))
{
decimal.TryParse(json, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out decimal result);
return result;
}
if (type == typeof(DateTime))
{
DateTime.TryParse(json.Replace("\"", ""), System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out DateTime result);
return result;
}
if (json == "null")
{
return null;
}
if (type.IsEnum)
{
if (json[0] == '"')
json = json.Substring(1, json.Length - 2);
try
{
return Enum.Parse(type, json, false);
}
catch
{
return 0;
}
}
if (type.IsArray)
{
Type arrayType = type.GetElementType();
if (json[0] != '[' || json[json.Length - 1] != ']')
return null;
List<string> elems = Split(json);
Array newArray = Array.CreateInstance(arrayType, elems.Count);
for (int i = 0; i < elems.Count; i++)
newArray.SetValue(ParseValue(arrayType, elems[i]), i);
splitArrayPool.Push(elems);
return newArray;
}
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
{
Type listType = type.GetGenericArguments()[0];
if (json[0] != '[' || json[json.Length - 1] != ']')
return null;
List<string> elems = Split(json);
var list = (IList)type.GetConstructor(new Type[] { typeof(int) }).Invoke(new object[] { elems.Count });
for (int i = 0; i < elems.Count; i++)
list.Add(ParseValue(listType, elems[i]));
splitArrayPool.Push(elems);
return list;
}
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
{
Type keyType, valueType;
{
Type[] args = type.GetGenericArguments();
keyType = args[0];
valueType = args[1];
}
//Refuse to parse dictionary keys that aren't of type string
if (keyType != typeof(string))
return null;
//Must be a valid dictionary element
if (json[0] != '{' || json[json.Length - 1] != '}')
return null;
//The list is split into key/value pairs only, this means the split must be divisible by 2 to be valid JSON
List<string> elems = Split(json);
if (elems.Count % 2 != 0)
return null;
var dictionary = (IDictionary)type.GetConstructor(new Type[] { typeof(int) }).Invoke(new object[] { elems.Count / 2 });
for (int i = 0; i < elems.Count; i += 2)
{
if (elems[i].Length <= 2)
continue;
string keyValue = elems[i].Substring(1, elems[i].Length - 2);
object val = ParseValue(valueType, elems[i + 1]);
dictionary[keyValue] = val;
}
return dictionary;
}
if (type == typeof(object))
{
return ParseAnonymousValue(json);
}
if (json[0] == '{' && json[json.Length - 1] == '}')
{
return ParseObject(type, json);
}
return null;
}
static object ParseAnonymousValue(string json)
{
if (json.Length == 0)
return null;
if (json[0] == '{' && json[json.Length - 1] == '}')
{
List<string> elems = Split(json);
if (elems.Count % 2 != 0)
return null;
var dict = new Dictionary<string, object>(elems.Count / 2);
for (int i = 0; i < elems.Count; i += 2)
dict[elems[i].Substring(1, elems[i].Length - 2)] = ParseAnonymousValue(elems[i + 1]);
return dict;
}
if (json[0] == '[' && json[json.Length - 1] == ']')
{
List<string> items = Split(json);
var finalList = new List<object>(items.Count);
for (int i = 0; i < items.Count; i++)
finalList.Add(ParseAnonymousValue(items[i]));
return finalList;
}
if (json[0] == '"' && json[json.Length - 1] == '"')
{
string str = json.Substring(1, json.Length - 2);
return str.Replace("\\", string.Empty);
}
if (char.IsDigit(json[0]) || json[0] == '-')
{
if (json.Contains("."))
{
double.TryParse(json, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out double result);
return result;
}
else
{
int.TryParse(json, out int result);
return result;
}
}
if (json == "true")
return true;
if (json == "false")
return false;
// handles json == "null" as well as invalid JSON
return null;
}
static Dictionary<string, T> CreateMemberNameDictionary<T>(T[] members) where T : MemberInfo
{
Dictionary<string, T> nameToMember = new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);
for (int i = 0; i < members.Length; i++)
{
T member = members[i];
if (member.IsDefined(typeof(IgnoreDataMemberAttribute), true))
continue;
string name = member.Name;
if (member.IsDefined(typeof(DataMemberAttribute), true))
{
DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute), true);
if (!string.IsNullOrEmpty(dataMemberAttribute.Name))
name = dataMemberAttribute.Name;
}
nameToMember.Add(name, member);
}
return nameToMember;
}
static object ParseObject(Type type, string json)
{
object instance = FormatterServices.GetUninitializedObject(type);
//The list is split into key/value pairs only, this means the split must be divisible by 2 to be valid JSON
List<string> elems = Split(json);
if (elems.Count % 2 != 0)
return instance;
//Dictionary<string, FieldInfo> nameToField;
//Dictionary<string, PropertyInfo> nameToProperty;
if (!fieldInfoCache.TryGetValue(type, out var nameToField))
{
nameToField = CreateMemberNameDictionary(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy));
fieldInfoCache.Add(type, nameToField);
}
if (!propertyInfoCache.TryGetValue(type, out var nameToProperty))
{
nameToProperty = CreateMemberNameDictionary(type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy));
propertyInfoCache.Add(type, nameToProperty);
}
for (int i = 0; i < elems.Count; i += 2)
{
if (elems[i].Length <= 2)
continue;
string key = elems[i].Substring(1, elems[i].Length - 2);
string value = elems[i + 1];
if (nameToField.TryGetValue(key, out FieldInfo fieldInfo))
fieldInfo.SetValue(instance, ParseValue(fieldInfo.FieldType, value));
else if (nameToProperty.TryGetValue(key, out PropertyInfo propertyInfo))
propertyInfo.SetValue(instance, ParseValue(propertyInfo.PropertyType, value), null);
}
return instance;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c8d46955f81c1d4e9ab1586f1b9eb1e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,202 @@
// copied from
// https://github.com/zanders3/json/blob/master/src/JSONWriter.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
//namespace TinyJson
namespace Meryel.UnityCodeAssist.Editor.TinyJson
{
//Really simple JSON writer
//- Outputs JSON structures from an object
//- Really simple API (new List<int> { 1, 2, 3 }).ToJson() == "[1,2,3]"
//- Will only output public fields and property getters on objects
//public static class JSONWriter
public static class JsonWriter
{
//public static string ToJson(this object item)
public static string ToJson(object item)
{
StringBuilder stringBuilder = new StringBuilder();
AppendValue(stringBuilder, item);
return stringBuilder.ToString();
}
static void AppendValue(StringBuilder stringBuilder, object item)
{
if (item == null)
{
stringBuilder.Append("null");
return;
}
Type type = item.GetType();
if (type == typeof(string) || type == typeof(char))
{
stringBuilder.Append('"');
string str = item.ToString();
for (int i = 0; i < str.Length; ++i)
if (str[i] < ' ' || str[i] == '"' || str[i] == '\\')
{
stringBuilder.Append('\\');
int j = "\"\\\n\r\t\b\f".IndexOf(str[i]);
if (j >= 0)
stringBuilder.Append("\"\\nrtbf"[j]);
else
stringBuilder.AppendFormat("u{0:X4}", (UInt32)str[i]);
}
else
stringBuilder.Append(str[i]);
stringBuilder.Append('"');
}
else if (type == typeof(byte) || type == typeof(sbyte))
{
stringBuilder.Append(item.ToString());
}
else if (type == typeof(short) || type == typeof(ushort))
{
stringBuilder.Append(item.ToString());
}
else if (type == typeof(int) || type == typeof(uint))
{
stringBuilder.Append(item.ToString());
}
else if (type == typeof(long) || type == typeof(ulong))
{
stringBuilder.Append(item.ToString());
}
else if (type == typeof(float))
{
stringBuilder.Append(((float)item).ToString(System.Globalization.CultureInfo.InvariantCulture));
}
else if (type == typeof(double))
{
stringBuilder.Append(((double)item).ToString(System.Globalization.CultureInfo.InvariantCulture));
}
else if (type == typeof(decimal))
{
stringBuilder.Append(((decimal)item).ToString(System.Globalization.CultureInfo.InvariantCulture));
}
else if (type == typeof(bool))
{
stringBuilder.Append(((bool)item) ? "true" : "false");
}
else if (type == typeof(DateTime))
{
stringBuilder.Append('"');
stringBuilder.Append(((DateTime)item).ToString(System.Globalization.CultureInfo.InvariantCulture));
stringBuilder.Append('"');
}
else if (type.IsEnum)
{
stringBuilder.Append('"');
stringBuilder.Append(item.ToString());
stringBuilder.Append('"');
}
else if (item is IList)
{
stringBuilder.Append('[');
bool isFirst = true;
IList list = item as IList;
for (int i = 0; i < list.Count; i++)
{
if (isFirst)
isFirst = false;
else
stringBuilder.Append(',');
AppendValue(stringBuilder, list[i]);
}
stringBuilder.Append(']');
}
else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
{
Type keyType = type.GetGenericArguments()[0];
//Refuse to output dictionary keys that aren't of type string
if (keyType != typeof(string))
{
stringBuilder.Append("{}");
return;
}
stringBuilder.Append('{');
IDictionary dict = item as IDictionary;
bool isFirst = true;
foreach (object key in dict.Keys)
{
if (isFirst)
isFirst = false;
else
stringBuilder.Append(',');
stringBuilder.Append('\"');
stringBuilder.Append((string)key);
stringBuilder.Append("\":");
AppendValue(stringBuilder, dict[key]);
}
stringBuilder.Append('}');
}
else
{
stringBuilder.Append('{');
bool isFirst = true;
FieldInfo[] fieldInfos = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
for (int i = 0; i < fieldInfos.Length; i++)
{
if (fieldInfos[i].IsDefined(typeof(IgnoreDataMemberAttribute), true))
continue;
object value = fieldInfos[i].GetValue(item);
if (value != null)
{
if (isFirst)
isFirst = false;
else
stringBuilder.Append(',');
stringBuilder.Append('\"');
stringBuilder.Append(GetMemberName(fieldInfos[i]));
stringBuilder.Append("\":");
AppendValue(stringBuilder, value);
}
}
PropertyInfo[] propertyInfo = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
for (int i = 0; i < propertyInfo.Length; i++)
{
if (!propertyInfo[i].CanRead || propertyInfo[i].IsDefined(typeof(IgnoreDataMemberAttribute), true))
continue;
object value = propertyInfo[i].GetValue(item, null);
if (value != null)
{
if (isFirst)
isFirst = false;
else
stringBuilder.Append(',');
stringBuilder.Append('\"');
stringBuilder.Append(GetMemberName(propertyInfo[i]));
stringBuilder.Append("\":");
AppendValue(stringBuilder, value);
}
}
stringBuilder.Append('}');
}
}
static string GetMemberName(MemberInfo member)
{
if (member.IsDefined(typeof(DataMemberAttribute), true))
{
DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute), true);
if (!string.IsNullOrEmpty(dataMemberAttribute.Name))
return dataMemberAttribute.Name;
}
return member.Name;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dd90a580cb9f06e4788c4241a5968891
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,322 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEditor;
#nullable enable
namespace Meryel.UnityCodeAssist.Editor
{
internal static class UnityClassExtensions
{
static GameObject? GetParentGO(GameObject go)
{
if (!go)
return null;
var parentTransform = go.transform.parent;
if (parentTransform && parentTransform.gameObject)
return parentTransform.gameObject;
else
return null;
}
static string GetId(UnityEngine.Object? obj)
{
try
{
// obj can be null
var globalObjectId = GlobalObjectId.GetGlobalObjectIdSlow(obj);
var objectGuid = globalObjectId.ToString();
return objectGuid;
}
catch (Exception ex)
{
// OnBeforeSerialize of user scripts may raise exception
Serilog.Log.Warning(ex, "GetGlobalObjectIdSlow failed for obj {Obj}", obj);
return "GlobalObjectId_V1-0-00000000000000000000000000000000-0-0";
}
}
internal static Synchronizer.Model.GameObject? ToSyncModel(this GameObject go, int priority = 0)
{
if (!go)
return null;
var data = new Synchronizer.Model.GameObject()
{
Id = GetId(go),
Name = go.name,
Layer = go.layer.ToString(),
Tag = go.tag,
Scene = go.scene.name,
ParentId = GetId(GetParentGO(go)),
ChildrenIds = getChildrenIds(go),
Components = getComponents(go),
Priority = priority,
};
return data;
static string[] getChildrenIds(GameObject g)
{
var ids = new List<string>();
var limit = 10;//**--
foreach (Transform child in g.transform)
{
if (!child || !child.gameObject)
continue;
ids.Add(GetId(child.gameObject));
if (--limit <= 0)
break;
}
return ids.ToArray();
}
//**--limit/10
static string[] getComponents(GameObject g) =>
g.GetComponents<Component>().Where(c => c).Select(c => c.GetType().FullName).Take(10).ToArray();
/*(string[] componentNames, Synchronizer.Model.ComponentData[] componentData) getComponents(GameObject g)
{
var components = g.GetComponents<Component>();
var names = components.Select(c => c.name).ToArray();
var data = new List<Synchronizer.Model.ComponentData>();
foreach (var comp in components)
{
var name = comp.name;
}
return (names, data.ToArray());
}*/
}
internal static Synchronizer.Model.GameObject[]? ToSyncModelOfHierarchy(this GameObject go)
{
if (!go)
return null;
var list = new List<Synchronizer.Model.GameObject>();
var parent = GetParentGO(go);
if (parent != null && parent)
{
var parentModel = parent.ToSyncModel();
if (parentModel != null)
list.Add(parentModel);
}
int limit = 10;
foreach (Transform child in go.transform)
{
if (!child || !child.gameObject)
continue;
var childModel = child.gameObject.ToSyncModel();
if (childModel == null)
continue;
list.Add(childModel);
if (--limit <= 0)
break;
}
return list.ToArray();
}
internal static Synchronizer.Model.ComponentData[]? ToSyncModelOfComponents(this GameObject go)
{
if (!go)
return null;
var limit = 10;//**--
return go.GetComponents<Component>().Where(c => c).Select(c => c.ToSyncModel(go)).Where(cd => cd != null).Take(limit).ToArray()!;
/*
var components = go.GetComponents<Component>();
var len = components.Count(c => c != null);
len = Math.Min(len, limit);//**--limit
var array = new Synchronizer.Model.ComponentData[len];
var arrayIndex = 0;
foreach (var component in components)
{
if (component == null)
continue;
array[arrayIndex++] = component.ToSyncModel(go);
if (arrayIndex >= len)
break;
}
return array;
*/
}
internal static Synchronizer.Model.ComponentData? ToSyncModel(this Component component, GameObject go)
{
if (!component || !go)
return null;
Type type = component.GetType();
var list = new List<(string, string)>();
ShowFieldInfo(type, component, list);
var data = new Synchronizer.Model.ComponentData()
{
GameObjectId = GetId(go),
Component = component.GetType().FullName,
Type = Synchronizer.Model.ComponentData.DataType.Component,
Data = list.ToArray(),
};
return data;
}
internal static Synchronizer.Model.ComponentData? ToSyncModel(this ScriptableObject so)
{
if (!so)
return null;
Type type = so.GetType();
var list = new List<(string, string)>();
ShowFieldInfo(type, so, list);
var data = new Synchronizer.Model.ComponentData()
{
GameObjectId = GetId(so),
Component = so.GetType().FullName,
Type = Synchronizer.Model.ComponentData.DataType.ScriptableObject,
Data = list.ToArray(),
};
return data;
}
static bool IsTypeCompatible(Type type)
{
if (type == null || !(type.IsSubclassOf(typeof(MonoBehaviour)) || type.IsSubclassOf(typeof(ScriptableObject))))
return false;
return true;
}
static void ShowFieldInfo(Type type)//, MonoImporter importer, List<string> names, List<Object> objects, ref bool didModify)
{
// Only show default properties for types that support it (so far only MonoBehaviour derived types)
if (!IsTypeCompatible(type))
return;
ShowFieldInfo(type.BaseType);//, importer, names, objects, ref didModify);
FieldInfo[] infos = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
foreach (FieldInfo field in infos)
{
if (!field.IsPublic)
{
object[] attr = field.GetCustomAttributes(typeof(SerializeField), true);
if (attr == null || attr.Length == 0)
continue;
}
/*
if (field.FieldType.IsSubclassOf(typeof(Object)) || field.FieldType == typeof(Object))
{
Object oldTarget = importer.GetDefaultReference(field.Name);
Object newTarget = EditorGUILayout.ObjectField(ObjectNames.NicifyVariableName(field.Name), oldTarget, field.FieldType, false);
names.Add(field.Name);
objects.Add(newTarget);
if (oldTarget != newTarget)
didModify = true;
}
*/
if (field.FieldType.IsValueType && field.FieldType.IsPrimitive && !field.FieldType.IsEnum)
{
}
else if (field.FieldType == typeof(string))
{
}
}
}
static void ShowFieldInfo(Type type, UnityEngine.Object unityObjectInstance, List<(string, string)> fields)//, MonoImporter importer, List<string> names, List<Object> objects, ref bool didModify)
{
// Only show default properties for types that support it (so far only MonoBehaviour derived types)
if (!IsTypeCompatible(type))
return;
if (!unityObjectInstance)
return;
ShowFieldInfo(type.BaseType, unityObjectInstance, fields);//, importer, names, objects, ref didModify);
FieldInfo[] infos = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
foreach (FieldInfo field in infos)
{
if (!field.IsPublic)
{
object[] attr = field.GetCustomAttributes(typeof(SerializeField), true);
if (attr == null || attr.Length == 0)
continue;
}
// check attribute [HideInInspector]
{
object[] attr = field.GetCustomAttributes(typeof(HideInInspector), true);
if (attr != null && attr.Length > 0)
continue;
}
// readonly
if (field.IsInitOnly)
continue;
/*
if (field.FieldType.IsSubclassOf(typeof(Object)) || field.FieldType == typeof(Object))
{
Object oldTarget = importer.GetDefaultReference(field.Name);
Object newTarget = EditorGUILayout.ObjectField(ObjectNames.NicifyVariableName(field.Name), oldTarget, field.FieldType, false);
names.Add(field.Name);
objects.Add(newTarget);
if (oldTarget != newTarget)
didModify = true;
}
*/
if (field.FieldType.IsValueType && field.FieldType.IsPrimitive && !field.FieldType.IsEnum)
{
var val = field.GetValue(unityObjectInstance);
fields.Add((field.Name, val.ToString()));//**--culture
}
else if (field.FieldType == typeof(string))
{
var val = (string)field.GetValue(unityObjectInstance);
fields.Add((field.Name, val));
}
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fe36fd2d16ea185469af4cb6bee3ea30
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1 @@
-define:MERYEL_UCA_LITE_VERSION

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 0768f51b234ca9a4e85c96e1b4a6f925
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: