Added Pacore
This commit is contained in:
15
.idea/.idea.Fruitomation/.idea/.gitignore
generated
vendored
Normal file
15
.idea/.idea.Fruitomation/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Rider ignored files
|
||||||
|
/modules.xml
|
||||||
|
/.idea.Fruitomation.iml
|
||||||
|
/projectSettingsUpdater.xml
|
||||||
|
/contentModel.xml
|
||||||
|
# Ignored default folder with query files
|
||||||
|
/queries/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
4
.idea/.idea.Fruitomation/.idea/encodings.xml
generated
Normal file
4
.idea/.idea.Fruitomation/.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||||
|
</project>
|
||||||
8
.idea/.idea.Fruitomation/.idea/indexLayout.xml
generated
Normal file
8
.idea/.idea.Fruitomation/.idea/indexLayout.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="UserContentModel">
|
||||||
|
<attachedFolders />
|
||||||
|
<explicitIncludes />
|
||||||
|
<explicitExcludes />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/.idea.Fruitomation/.idea/vcs.xml
generated
Normal file
6
.idea/.idea.Fruitomation/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
8
Assets/Pacore.meta
Normal file
8
Assets/Pacore.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8963d3aaf49fdce418a1779fdb04b311
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Pacore/Editor.meta
Normal file
8
Assets/Pacore/Editor.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2b0add55bcd6e65469713ef2482353e4
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Pacore/Editor/Caches.meta
Normal file
8
Assets/Pacore/Editor/Caches.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5feaa457922d235419f2e6d3f8f01c33
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
58
Assets/Pacore/Editor/Caches/InspectorCallableCache.cs
Normal file
58
Assets/Pacore/Editor/Caches/InspectorCallableCache.cs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
using PashaBibko.Pacore.Attributes;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PashaBibko.Pacore.Editor.Caches
|
||||||
|
{
|
||||||
|
public static class InspectorCallableCache
|
||||||
|
{
|
||||||
|
public struct AttributeInfo
|
||||||
|
{
|
||||||
|
public InspectorCallableAttribute Attribute;
|
||||||
|
public MethodInfo AttachedMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
private const BindingFlags BINDING_FLAGS =
|
||||||
|
BindingFlags.Public |
|
||||||
|
BindingFlags.NonPublic |
|
||||||
|
BindingFlags.Instance;
|
||||||
|
|
||||||
|
private static Dictionary<Type, AttributeInfo[]> Cache { get; } = new();
|
||||||
|
|
||||||
|
public static AttributeInfo[] GetAllAttributesOfType(Type type)
|
||||||
|
{
|
||||||
|
/* Checks the cache for the type */
|
||||||
|
if (Cache.TryGetValue(type, out AttributeInfo[] attributes))
|
||||||
|
{
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finds all the functions with the attribute */
|
||||||
|
MethodInfo[] methods = type.GetMethods(BINDING_FLAGS);
|
||||||
|
List<AttributeInfo> buttons = new();
|
||||||
|
|
||||||
|
foreach (MethodInfo method in methods)
|
||||||
|
{
|
||||||
|
InspectorCallableAttribute attribute
|
||||||
|
= method.GetCustomAttribute<InspectorCallableAttribute>();
|
||||||
|
|
||||||
|
if (attribute != null)
|
||||||
|
{
|
||||||
|
AttributeInfo info = new()
|
||||||
|
{
|
||||||
|
Attribute = attribute,
|
||||||
|
AttachedMethod = method,
|
||||||
|
};
|
||||||
|
|
||||||
|
buttons.Add(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adds it to the cache before returning */
|
||||||
|
AttributeInfo[] array = buttons.ToArray();
|
||||||
|
Cache.Add(type, array);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Pacore/Editor/Caches/InspectorCallableCache.cs.meta
Normal file
11
Assets/Pacore/Editor/Caches/InspectorCallableCache.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 95392750cbc11ce4b9b9e6717c9e6588
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
34
Assets/Pacore/Editor/Caches/MonoScriptCache.cs
Normal file
34
Assets/Pacore/Editor/Caches/MonoScriptCache.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PashaBibko.Pacore.Editor.Caches
|
||||||
|
{
|
||||||
|
public static class MonoScriptCache
|
||||||
|
{
|
||||||
|
private static Dictionary<string, MonoScript> Cache { get; } = new();
|
||||||
|
|
||||||
|
static MonoScriptCache()
|
||||||
|
{
|
||||||
|
/* Finds all MonoScripts and adds them to the dictionary by name */
|
||||||
|
MonoScript[] scripts = Resources.FindObjectsOfTypeAll<MonoScript>();
|
||||||
|
foreach (MonoScript script in scripts)
|
||||||
|
{
|
||||||
|
Type scriptType = script.GetClass();
|
||||||
|
if (scriptType is not null)
|
||||||
|
{
|
||||||
|
string name = scriptType.FullName;
|
||||||
|
Cache.TryAdd(name, script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MonoScript Get(string name)
|
||||||
|
{
|
||||||
|
/* Fetches the value (if there is one) without creating a default val */
|
||||||
|
Cache.TryGetValue(name, out MonoScript script);
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Pacore/Editor/Caches/MonoScriptCache.cs.meta
Normal file
11
Assets/Pacore/Editor/Caches/MonoScriptCache.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 668c2ebd0824f1d4c9b324be7142823c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Pacore/Editor/Drawers.meta
Normal file
8
Assets/Pacore/Editor/Drawers.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 47d063734da388944b54ed5a6ca386a7
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
46
Assets/Pacore/Editor/Drawers/DetectInspectorChangesDrawer.cs
Normal file
46
Assets/Pacore/Editor/Drawers/DetectInspectorChangesDrawer.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
using PashaBibko.Pacore.Attributes;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace PashaBibko.Pacore.Editor.Drawers
|
||||||
|
{
|
||||||
|
[CustomPropertyDrawer(typeof(DetectInspectorChangesAttribute))]
|
||||||
|
public sealed class DetectInspectorChangesDrawer : PropertyDrawer
|
||||||
|
{
|
||||||
|
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||||
|
{
|
||||||
|
/* Draws the property and checks for changes */
|
||||||
|
EditorGUI.BeginProperty(position, label, property);
|
||||||
|
EditorGUI.BeginChangeCheck();
|
||||||
|
|
||||||
|
EditorGUI.PropertyField(position, property, label);
|
||||||
|
if (EditorGUI.EndChangeCheck()) // Returns true if there were changes
|
||||||
|
{
|
||||||
|
property.serializedObject.ApplyModifiedProperties(); // Applies the changes
|
||||||
|
if (attribute is DetectInspectorChangesAttribute inspectorChangesAttribute)
|
||||||
|
{
|
||||||
|
const BindingFlags BINDING_FLAGS =
|
||||||
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
||||||
|
|
||||||
|
string methodName = inspectorChangesAttribute.ActionName;
|
||||||
|
|
||||||
|
/* Fetches the method and the object to call it on */
|
||||||
|
Object target = property.serializedObject.targetObject;
|
||||||
|
MethodInfo method = target.GetType().GetMethod(methodName, BINDING_FLAGS);
|
||||||
|
if (method == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Method not found [{methodName}]");
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
method.Invoke(target, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUI.EndProperty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a5fad12cc06f1a749bc75948a24e6306
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
23
Assets/Pacore/Editor/Drawers/InspectorReadOnlyDrawer.cs
Normal file
23
Assets/Pacore/Editor/Drawers/InspectorReadOnlyDrawer.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using PashaBibko.Pacore.Attributes;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace PashaBibko.Pacore.Editor.Drawers
|
||||||
|
{
|
||||||
|
[CustomPropertyDrawer(typeof(InspectorReadOnlyAttribute))]
|
||||||
|
public sealed class InspectorReadOnlyDrawer : PropertyDrawer
|
||||||
|
{
|
||||||
|
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||||
|
{
|
||||||
|
if (attribute is InspectorReadOnlyAttribute readOnlyAttribute)
|
||||||
|
{
|
||||||
|
GUI.enabled = false;
|
||||||
|
|
||||||
|
label.text = readOnlyAttribute.Name ?? label.text; // Uses custom name if it exists
|
||||||
|
EditorGUI.PropertyField(position, property, label);
|
||||||
|
|
||||||
|
GUI.enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Pacore/Editor/Drawers/InspectorReadOnlyDrawer.cs.meta
Normal file
11
Assets/Pacore/Editor/Drawers/InspectorReadOnlyDrawer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a90f608095c5b964fabd707859b141ed
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
42
Assets/Pacore/Editor/Drawers/MonoBehaviourDrawer.cs
Normal file
42
Assets/Pacore/Editor/Drawers/MonoBehaviourDrawer.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using PashaBibko.Pacore.Editor.Caches;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using System;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
|
namespace PashaBibko.Pacore.Editor.Drawers
|
||||||
|
{
|
||||||
|
[CustomEditor(typeof(MonoBehaviour), editorForChildClasses: true)]
|
||||||
|
public class MonoBehaviourDrawer : UnityEditor.Editor
|
||||||
|
{
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
DrawDefaultInspector();
|
||||||
|
DrawFunctionButtons(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DrawFunctionButtons(Object target)
|
||||||
|
{
|
||||||
|
Type type = target.GetType();
|
||||||
|
InspectorCallableCache.AttributeInfo[] buttons
|
||||||
|
= InspectorCallableCache.GetAllAttributesOfType(type);
|
||||||
|
|
||||||
|
if (buttons.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
EditorGUILayout.LabelField("Functions", EditorStyles.boldLabel);
|
||||||
|
|
||||||
|
foreach (InspectorCallableCache.AttributeInfo button in buttons)
|
||||||
|
{
|
||||||
|
string name = button.Attribute.ButtonName;
|
||||||
|
if (GUILayout.Button(name))
|
||||||
|
{
|
||||||
|
button.AttachedMethod.Invoke(target, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Pacore/Editor/Drawers/MonoBehaviourDrawer.cs.meta
Normal file
11
Assets/Pacore/Editor/Drawers/MonoBehaviourDrawer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a0f42304568f6f54f9f7cf0c6e3e62ad
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
80
Assets/Pacore/Editor/Drawers/MonoScriptDrawer.cs
Normal file
80
Assets/Pacore/Editor/Drawers/MonoScriptDrawer.cs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
using PashaBibko.Pacore.Editor.Caches;
|
||||||
|
using PashaBibko.Pacore.Attributes;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PashaBibko.Pacore.Editor.Drawers
|
||||||
|
{
|
||||||
|
[CustomPropertyDrawer(typeof(MonoScriptAttribute))]
|
||||||
|
public class MonoScriptDrawer : PropertyDrawer
|
||||||
|
{
|
||||||
|
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||||
|
{
|
||||||
|
/* I'm not sure if this will ever happen */
|
||||||
|
if (attribute is not MonoScriptAttribute attr)
|
||||||
|
{
|
||||||
|
return; // Stops NullReferenceExceptions
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Makes sure the type is a string */
|
||||||
|
if (property.propertyType != SerializedPropertyType.String)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Attribute [{nameof(MonoScriptAttribute)}] must be attached to a string");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draws the label of the script variable */
|
||||||
|
Rect rect = EditorGUI.PrefixLabel(position, label);
|
||||||
|
|
||||||
|
/* Fetches the script if there is a value assigned to the property */
|
||||||
|
string propValue = property.stringValue;
|
||||||
|
MonoScript script = null;
|
||||||
|
if (!string.IsNullOrEmpty(propValue))
|
||||||
|
{
|
||||||
|
script = MonoScriptCache.Get(propValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draws the selected script and checks for changes */
|
||||||
|
script = EditorGUI.ObjectField(rect, script, typeof(MonoScript), allowSceneObjects: false) as MonoScript;
|
||||||
|
if (!GUI.changed)
|
||||||
|
{
|
||||||
|
return; // No changes to check
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Makes sure the script is valid */
|
||||||
|
if (script is null)
|
||||||
|
{
|
||||||
|
property.stringValue = string.Empty; // No type means empty string
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fetches the type of the attached script, and checks if it is valid */
|
||||||
|
Type type = script.GetClass();
|
||||||
|
if (type is null || (attr.MonoType is not null && !attr.MonoType.IsAssignableFrom(type)))
|
||||||
|
{
|
||||||
|
Debug.LogError($"The script file [{script.name}] doesn't contain an assignable MonoBehaviour class");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If a forced inheritance has been set, checks its validity */
|
||||||
|
if (attr.InheritedFrom is not null && !attr.InheritedFrom.IsAssignableFrom(type))
|
||||||
|
{
|
||||||
|
property.stringValue = type.FullName; // Still applies the changes to make it appear like it is functioning
|
||||||
|
|
||||||
|
string attachedObject = property.serializedObject.targetObject.name;
|
||||||
|
string inherited = attr.InheritedFrom.FullName;
|
||||||
|
|
||||||
|
Debug.LogError
|
||||||
|
(
|
||||||
|
$"Field [{property.name}] as part of [{attachedObject}] is invalid.\n" +
|
||||||
|
$"The type must inherit from [{inherited}]. Currently it is [{type.FullName}]"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assigns the name of the type to the property so it can be created */
|
||||||
|
property.stringValue = type.FullName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Pacore/Editor/Drawers/MonoScriptDrawer.cs.meta
Normal file
11
Assets/Pacore/Editor/Drawers/MonoScriptDrawer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 21cdc55dcda1d9e40a1463929b020832
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Pacore/Editor/EditorWindows.meta
Normal file
8
Assets/Pacore/Editor/EditorWindows.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 534297012cecc0a4892f3262f829f411
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
97
Assets/Pacore/Editor/EditorWindows/ProfilerWindow.cs
Normal file
97
Assets/Pacore/Editor/EditorWindows/ProfilerWindow.cs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using PashaBibko.Pacore.DevTools;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Pacore.Editor.EditorWindows
|
||||||
|
{
|
||||||
|
public class ProfilerWindow : EditorWindow
|
||||||
|
{
|
||||||
|
private double LastRepaint = double.MinValue;
|
||||||
|
private Vector2 ScrollPosition;
|
||||||
|
|
||||||
|
[MenuItem("Pacore/Profiler")]
|
||||||
|
public static void OpenWindow() => GetWindow<ProfilerWindow>();
|
||||||
|
|
||||||
|
/* Makes sure the window is repainted often to show latest info */
|
||||||
|
private void OnDisable() => EditorApplication.update -= CheckForRepaint;
|
||||||
|
private void OnEnable() => EditorApplication.update += CheckForRepaint;
|
||||||
|
|
||||||
|
private void CheckForRepaint()
|
||||||
|
{
|
||||||
|
/* Triggers a repaint when it has been over 1 second since last repaint */
|
||||||
|
double now = EditorApplication.timeSinceStartup;
|
||||||
|
if (now - LastRepaint > 1f)
|
||||||
|
{
|
||||||
|
LastRepaint = now;
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawEmptyProfiler()
|
||||||
|
{
|
||||||
|
GUILayout.BeginVertical(style: "box");
|
||||||
|
|
||||||
|
GUILayout.Label(text: "No profiler snippets found", EditorStyles.boldLabel);
|
||||||
|
GUILayout.Label(text: "Try running the game to collect profile samples");
|
||||||
|
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawProfilerSnippet(string name, List<long> times)
|
||||||
|
{
|
||||||
|
GUILayout.BeginVertical(style: "box");
|
||||||
|
|
||||||
|
GUILayout.BeginHorizontal();
|
||||||
|
GUILayout.Label(text: name, EditorStyles.boldLabel);
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
float averageMs = times.Sum() / (float)times.Count;
|
||||||
|
GUILayout.Label("Average time: " + averageMs);
|
||||||
|
|
||||||
|
IGrouping<long, long>[] ordered = times // List<long>
|
||||||
|
.GroupBy(time => time) // IEnumerable<IGrouping<long, long>>
|
||||||
|
.OrderByDescending(time => time.Key) // IOrdererEnumerable<IGrouping<long, long>>
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
foreach (IGrouping<long, long> group in ordered)
|
||||||
|
{
|
||||||
|
string text = group.Count() > 1
|
||||||
|
? $"- {group.Key}ms {group.Count()}x"
|
||||||
|
: $"- {group.Key}ms";
|
||||||
|
|
||||||
|
GUILayout.Label(text, EditorStyles.label);
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draws the different snippets to the screen */
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
GUILayout.Space(5); // Stops the window rendering right at the top
|
||||||
|
IReadOnlyDictionary<string, List<long>> snippets = CodeProfiler.GetProfilerSnippets();
|
||||||
|
|
||||||
|
/* If there are no snippets, draws an inspector with instructions */
|
||||||
|
if (snippets.Count == 0)
|
||||||
|
{
|
||||||
|
DrawEmptyProfiler();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draws a quick overview of all snippets found */
|
||||||
|
GUILayout.BeginVertical(style: "box");
|
||||||
|
GUILayout.Label(text: $"[{snippets.Count}] different profiler snippets found");
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
|
||||||
|
/* Draws each profiler snippet */
|
||||||
|
ScrollPosition = EditorGUILayout.BeginScrollView(ScrollPosition);
|
||||||
|
foreach (KeyValuePair<string, List<long>> snippet in snippets)
|
||||||
|
{
|
||||||
|
DrawProfilerSnippet(snippet.Key, snippet.Value);
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Pacore/Editor/EditorWindows/ProfilerWindow.cs.meta
Normal file
11
Assets/Pacore/Editor/EditorWindows/ProfilerWindow.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a4aab8fc202fced4c8c8b1b346526ccb
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
16
Assets/Pacore/Editor/Pacore.Editor.asmdef
Normal file
16
Assets/Pacore/Editor/Pacore.Editor.asmdef
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "Pacore.Editor",
|
||||||
|
"rootNamespace": "",
|
||||||
|
"references": [
|
||||||
|
"GUID:4d6a1c1727d80284f86640644e0ea451"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
7
Assets/Pacore/Editor/Pacore.Editor.asmdef.meta
Normal file
7
Assets/Pacore/Editor/Pacore.Editor.asmdef.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8c0ae1ec093a9314e957ad337005e456
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Pacore/Runtime.meta
Normal file
8
Assets/Pacore/Runtime.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 922281d628f2a4343942e99abdeaa283
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Pacore/Runtime/Attributes.meta
Normal file
8
Assets/Pacore/Runtime/Attributes.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d31e288d186b4e249afbfe36c24abe04
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using UnityEngine.Scripting;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PashaBibko.Pacore.Attributes
|
||||||
|
{
|
||||||
|
[Preserve, AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public class CreateInstanceOnStartAttribute : Attribute { }
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b60c10877f46d5847919dd8f6d52d45a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
16
Assets/Pacore/Runtime/Attributes/DetectInspectorChanges.cs
Normal file
16
Assets/Pacore/Runtime/Attributes/DetectInspectorChanges.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PashaBibko.Pacore.Attributes
|
||||||
|
{
|
||||||
|
[AttributeUsage(validOn: AttributeTargets.Field)]
|
||||||
|
public sealed class DetectInspectorChangesAttribute : PropertyAttribute
|
||||||
|
{
|
||||||
|
public string ActionName { get; }
|
||||||
|
|
||||||
|
public DetectInspectorChangesAttribute(string function)
|
||||||
|
{
|
||||||
|
ActionName = function;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2280212742639854c946c532ca692fb8
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
15
Assets/Pacore/Runtime/Attributes/InspectorCallable.cs
Normal file
15
Assets/Pacore/Runtime/Attributes/InspectorCallable.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PashaBibko.Pacore.Attributes
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
|
public class InspectorCallableAttribute : Attribute
|
||||||
|
{
|
||||||
|
public string ButtonName { get; }
|
||||||
|
|
||||||
|
public InspectorCallableAttribute(string name)
|
||||||
|
{
|
||||||
|
ButtonName = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Pacore/Runtime/Attributes/InspectorCallable.cs.meta
Normal file
11
Assets/Pacore/Runtime/Attributes/InspectorCallable.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7650b3a0488876b43ad6b8472def84e8
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
16
Assets/Pacore/Runtime/Attributes/InspectorReadOnly.cs
Normal file
16
Assets/Pacore/Runtime/Attributes/InspectorReadOnly.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PashaBibko.Pacore.Attributes
|
||||||
|
{
|
||||||
|
[AttributeUsage(validOn: AttributeTargets.Field)]
|
||||||
|
public sealed class InspectorReadOnlyAttribute : PropertyAttribute
|
||||||
|
{
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public InspectorReadOnlyAttribute(string name = null)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Pacore/Runtime/Attributes/InspectorReadOnly.cs.meta
Normal file
11
Assets/Pacore/Runtime/Attributes/InspectorReadOnly.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d90a8dcab504ac04a9dd61f603bac921
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
24
Assets/Pacore/Runtime/Attributes/MonoScriptAttribute.cs
Normal file
24
Assets/Pacore/Runtime/Attributes/MonoScriptAttribute.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PashaBibko.Pacore.Attributes
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Field)]
|
||||||
|
public class MonoScriptAttribute : PropertyAttribute
|
||||||
|
{
|
||||||
|
public Type InheritedFrom { get; }
|
||||||
|
public Type MonoType { get; }
|
||||||
|
|
||||||
|
public MonoScriptAttribute(Type inherited, Type type = null)
|
||||||
|
{
|
||||||
|
InheritedFrom = inherited;
|
||||||
|
MonoType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MonoScriptAttribute(Type type = null)
|
||||||
|
{
|
||||||
|
InheritedFrom = null;
|
||||||
|
MonoType = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Pacore/Runtime/Attributes/MonoScriptAttribute.cs.meta
Normal file
11
Assets/Pacore/Runtime/Attributes/MonoScriptAttribute.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 53c17374ddf436d4095f8d8eb12d77fb
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
7
Assets/Pacore/Runtime/Attributes/StaticInspectorField.cs
Normal file
7
Assets/Pacore/Runtime/Attributes/StaticInspectorField.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PashaBibko.Pacore.Attributes
|
||||||
|
{
|
||||||
|
[AttributeUsage(validOn: AttributeTargets.Field)]
|
||||||
|
public class StaticInspectorFieldAttribute : Attribute { }
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1017ebd6495c20049a2e9c9e747c8f67
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
57
Assets/Pacore/Runtime/ClassAttributeCache.cs
Normal file
57
Assets/Pacore/Runtime/ClassAttributeCache.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace PashaBibko.Pacore
|
||||||
|
{
|
||||||
|
public static class ClassAttributeCache
|
||||||
|
{
|
||||||
|
private static Dictionary<Type, List<Type>> AttributeCache { get; set; }
|
||||||
|
|
||||||
|
public static ReadOnlyCollection<Type> GetAttributesOf<T>()
|
||||||
|
{
|
||||||
|
return AttributeCache.TryGetValue(typeof(T), out List<Type> classes)
|
||||||
|
? classes.AsReadOnly()
|
||||||
|
: throw new ArgumentException($"Attribute [{nameof(T)}] is not used by any classes");
|
||||||
|
}
|
||||||
|
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
|
||||||
|
private static void ScanAllAssemblies()
|
||||||
|
{
|
||||||
|
/* Fetches all the class types in all loaded assemblies */
|
||||||
|
Type[] classes = AppDomain.CurrentDomain.GetAssemblies() // Assembly[]
|
||||||
|
.SelectMany(assembly => assembly.GetTypes()) // IEnumerable<Type>
|
||||||
|
.Where(type => type.IsClass && !type.IsAbstract) // IEnumerable<Type>
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
/* Allocates space for the cache */
|
||||||
|
AttributeCache = classes // Type[]
|
||||||
|
.Where(type => typeof(Attribute).IsAssignableFrom(type)) // IEnumerable<Type>
|
||||||
|
.ToDictionary
|
||||||
|
(
|
||||||
|
keySelector: t => t,
|
||||||
|
elementSelector: _ => new List<Type>()
|
||||||
|
); // Dictionary<Type, List<Type>>
|
||||||
|
|
||||||
|
/* Finds which attributes are attached to what classes */
|
||||||
|
HashSet<Type> seen = new();
|
||||||
|
foreach (Type current in classes)
|
||||||
|
{
|
||||||
|
/* Tracks the seen attributes */
|
||||||
|
seen.Clear();
|
||||||
|
foreach (object attr in current.GetCustomAttributes(inherit: true))
|
||||||
|
{
|
||||||
|
seen.Add(attr.GetType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adds the class type to each attribute in the dictionary */
|
||||||
|
foreach (Type type in seen)
|
||||||
|
{
|
||||||
|
AttributeCache[type].Add(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Pacore/Runtime/ClassAttributeCache.cs.meta
Normal file
11
Assets/Pacore/Runtime/ClassAttributeCache.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0106f79bf6ab1134187b81144763795b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
33
Assets/Pacore/Runtime/CreateInstanceOnStartLoader.cs
Normal file
33
Assets/Pacore/Runtime/CreateInstanceOnStartLoader.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using PashaBibko.Pacore.Attributes;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using UnityEngine;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
|
namespace PashaBibko.Pacore
|
||||||
|
{
|
||||||
|
public static class CreateInstanceOnStartLoader
|
||||||
|
{
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||||
|
private static void CreateAllInstances()
|
||||||
|
{
|
||||||
|
/* Fetches all the types with the CreateInstanceOnStart attribute */
|
||||||
|
ReadOnlyCollection<Type> types =
|
||||||
|
ClassAttributeCache.GetAttributesOf<CreateInstanceOnStartAttribute>();
|
||||||
|
|
||||||
|
/* Creates a holder for all the other game objects to not clutter inspector */
|
||||||
|
GameObject holder = new("ScriptSpawnedInstances");
|
||||||
|
Object.DontDestroyOnLoad(holder); // Stops all the game objects from being destroyed
|
||||||
|
|
||||||
|
Transform parent = holder.transform;
|
||||||
|
|
||||||
|
/* Creates all the game objects for the selected types */
|
||||||
|
foreach (Type type in types)
|
||||||
|
{
|
||||||
|
GameObject go = new(type.Name, type);
|
||||||
|
go.transform.SetParent(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Pacore/Runtime/CreateInstanceOnStartLoader.cs.meta
Normal file
11
Assets/Pacore/Runtime/CreateInstanceOnStartLoader.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6af2fc25cdc5ad34e82f33536ab03372
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Pacore/Runtime/DevTools.meta
Normal file
8
Assets/Pacore/Runtime/DevTools.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: daccdbc0b2ee6714e9e5ff4a1d398f76
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
42
Assets/Pacore/Runtime/DevTools/CodeProfiler.cs
Normal file
42
Assets/Pacore/Runtime/DevTools/CodeProfiler.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PashaBibko.Pacore.DevTools
|
||||||
|
{
|
||||||
|
public static class CodeProfiler
|
||||||
|
{
|
||||||
|
private static Dictionary<string, List<long>> ProfilerSnippets { get; } = new();
|
||||||
|
public static IReadOnlyDictionary<string, List<long>> GetProfilerSnippets() => ProfilerSnippets;
|
||||||
|
|
||||||
|
private static void AddProfileSnippet(string name, long ms)
|
||||||
|
{
|
||||||
|
if (!ProfilerSnippets.ContainsKey(name))
|
||||||
|
{
|
||||||
|
ProfilerSnippets.Add(name, new List<long>());
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfilerSnippets[name].Add(ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ProfilerSnippetHandle Start(string name) => new(name);
|
||||||
|
|
||||||
|
public class ProfilerSnippetHandle : IDisposable
|
||||||
|
{
|
||||||
|
private Stopwatch Stopwatch { get; }
|
||||||
|
private string SnippetName { get; }
|
||||||
|
|
||||||
|
public ProfilerSnippetHandle(string name)
|
||||||
|
{
|
||||||
|
Stopwatch = Stopwatch.StartNew();
|
||||||
|
SnippetName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Stopwatch.Stop();
|
||||||
|
AddProfileSnippet(SnippetName, Stopwatch.ElapsedMilliseconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Pacore/Runtime/DevTools/CodeProfiler.cs.meta
Normal file
11
Assets/Pacore/Runtime/DevTools/CodeProfiler.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8ede419ddf0f2c64f89935e3d36ebeb7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
14
Assets/Pacore/Runtime/Pacore.asmdef
Normal file
14
Assets/Pacore/Runtime/Pacore.asmdef
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "Pacore",
|
||||||
|
"rootNamespace": "",
|
||||||
|
"references": [],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
7
Assets/Pacore/Runtime/Pacore.asmdef.meta
Normal file
7
Assets/Pacore/Runtime/Pacore.asmdef.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4d6a1c1727d80284f86640644e0ea451
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Pacore/Runtime/Threading.meta
Normal file
8
Assets/Pacore/Runtime/Threading.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8dbfd5bc438e9d546bd24c9a0192cf12
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
118
Assets/Pacore/Runtime/Threading/ThreadDispatcher.cs
Normal file
118
Assets/Pacore/Runtime/Threading/ThreadDispatcher.cs
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using PashaBibko.Pacore.Attributes;
|
||||||
|
using UnityEngine;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Debug = UnityEngine.Debug;
|
||||||
|
|
||||||
|
namespace PashaBibko.Pacore.Threading
|
||||||
|
{
|
||||||
|
[CreateInstanceOnStart] public class ThreadDispatcher : MonoBehaviour
|
||||||
|
{
|
||||||
|
private static ConcurrentQueue<IEnumerator> MainThreadMultistepQueue { get; } = new();
|
||||||
|
private static ConcurrentQueue<Action> MainThreadImmediateQueue { get; } = new();
|
||||||
|
private static ConcurrentQueue<Action> BackgroundQueue { get; } = new();
|
||||||
|
|
||||||
|
private static SemaphoreSlim Semaphore { get; } = new(initialCount: 4);
|
||||||
|
private static int IsBackgroundProcessing; // Pseudo boolean
|
||||||
|
|
||||||
|
private static IEnumerator ActiveMultistepRoutine { get; set; }
|
||||||
|
private static ThreadDispatcher Instance;
|
||||||
|
|
||||||
|
private const long MAIN_THREAD_MS_MAX = 5;
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
/* Makes sure there is only one instance */
|
||||||
|
if (Instance is not null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Cannot have multiple instances of [{nameof(ThreadDispatcher)}]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
Instance = null; // Allows the Dispatcher to be destroyed
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void QueueMultistep(IEnumerator routine) => MainThreadMultistepQueue.Enqueue(routine);
|
||||||
|
public static void QueueImmediate(Action action) => MainThreadImmediateQueue.Enqueue(action);
|
||||||
|
|
||||||
|
public static void QueueBackground(Action action)
|
||||||
|
{
|
||||||
|
/* Adds to the queue and runs if there are no active threads */
|
||||||
|
BackgroundQueue.Enqueue(action);
|
||||||
|
TriggerBackgroundProcessing();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TriggerBackgroundProcessing()
|
||||||
|
{
|
||||||
|
/* Makes sure there are not too many threads queued */
|
||||||
|
if (Interlocked.Exchange(ref IsBackgroundProcessing, 1) != 1)
|
||||||
|
{
|
||||||
|
Task.Run(ProcessBackgroundQueue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task ProcessBackgroundQueue()
|
||||||
|
{
|
||||||
|
/* Empties the queue of all tasks */
|
||||||
|
while (BackgroundQueue.TryDequeue(out Action task))
|
||||||
|
{
|
||||||
|
await Semaphore.WaitAsync();
|
||||||
|
_ = Task.Run(() =>
|
||||||
|
{
|
||||||
|
ThreadSafe.Try
|
||||||
|
(
|
||||||
|
action: task,
|
||||||
|
final: () => Semaphore.Release()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cleans up to allow for future procession */
|
||||||
|
Interlocked.Exchange(ref IsBackgroundProcessing, 0);
|
||||||
|
if (!BackgroundQueue.IsEmpty) // Items may be queued during cleanup
|
||||||
|
{
|
||||||
|
TriggerBackgroundProcessing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
/* Runs the Actions in the immediate queue */
|
||||||
|
Stopwatch sw = Stopwatch.StartNew(); // Used to make sure not too much processing is done in one go
|
||||||
|
while (MainThreadImmediateQueue.TryDequeue(out Action current) && sw.ElapsedMilliseconds < MAIN_THREAD_MS_MAX)
|
||||||
|
{
|
||||||
|
current.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Runs the multistep actions (if there is still time) */
|
||||||
|
while (sw.ElapsedMilliseconds < MAIN_THREAD_MS_MAX)
|
||||||
|
{
|
||||||
|
/* Gets a new routine if there is none active */
|
||||||
|
if (ActiveMultistepRoutine == null)
|
||||||
|
{
|
||||||
|
if (!MainThreadMultistepQueue.TryDequeue(out IEnumerator next))
|
||||||
|
{
|
||||||
|
return; // There is none left so we can return early
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveMultistepRoutine = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Runs the next step in the routine */
|
||||||
|
if (!ActiveMultistepRoutine.MoveNext())
|
||||||
|
{
|
||||||
|
ActiveMultistepRoutine = null; // [MoveNext() -> false] means the routine has ended
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Pacore/Runtime/Threading/ThreadDispatcher.cs.meta
Normal file
11
Assets/Pacore/Runtime/Threading/ThreadDispatcher.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 95b6b9f402ffb8d47851420ac464f93b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
44
Assets/Pacore/Runtime/Threading/ThreadEnforcer.cs
Normal file
44
Assets/Pacore/Runtime/Threading/ThreadEnforcer.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
using UnityEngine;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PashaBibko.Pacore.Threading
|
||||||
|
{
|
||||||
|
public static partial class ThreadSafe
|
||||||
|
{
|
||||||
|
private static SynchronizationContext MainThreadContext { get; set; }
|
||||||
|
|
||||||
|
public class IncorrectThreadException : Exception
|
||||||
|
{
|
||||||
|
public IncorrectThreadException(string message)
|
||||||
|
: base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
|
||||||
|
private static void CaptureMainThreadContext()
|
||||||
|
{
|
||||||
|
MainThreadContext = SynchronizationContext.Current;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void EnforceMainThread()
|
||||||
|
{
|
||||||
|
if (SynchronizationContext.Current != MainThreadContext)
|
||||||
|
{
|
||||||
|
throw new IncorrectThreadException("Main thread function was called on a background thread");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void EnforceBackgroundThread()
|
||||||
|
{
|
||||||
|
if (SynchronizationContext.Current == MainThreadContext)
|
||||||
|
{
|
||||||
|
throw new IncorrectThreadException("Background thread function was called on the main thread");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Pacore/Runtime/Threading/ThreadEnforcer.cs.meta
Normal file
11
Assets/Pacore/Runtime/Threading/ThreadEnforcer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 25d001fa0a9a7404c95304eab5106343
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
24
Assets/Pacore/Runtime/Threading/ThreadSafeTry.cs
Normal file
24
Assets/Pacore/Runtime/Threading/ThreadSafeTry.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using UnityEngine;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PashaBibko.Pacore.Threading
|
||||||
|
{
|
||||||
|
public static partial class ThreadSafe
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void Try(Action action, Action final = null)
|
||||||
|
{
|
||||||
|
try { action(); }
|
||||||
|
|
||||||
|
/* Makes sure any exceptions are caught and logged properly */
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ThreadDispatcher.QueueImmediate(() => Debug.Log($"Exception: [{ex.Message}]"));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
finally { final?.Invoke(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Pacore/Runtime/Threading/ThreadSafeTry.cs.meta
Normal file
11
Assets/Pacore/Runtime/Threading/ThreadSafeTry.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: edacac53aa5b21442a07379ce4087b92
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Reference in New Issue
Block a user