Initial commit
This commit is contained in:
8
Assets/Pacore/Runtime/Attributes.meta
Normal file
8
Assets/Pacore/Runtime/Attributes.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c338628804072394784eaf9cd5b58660
|
||||
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,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 01a51f48470f91f49bad28eb4495aae5
|
||||
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,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6221a3b81100d9f46b6e994a1e7bff20
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1c2ef00bd0b8434e947092a95e39f13
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0be78274c3c8f3746b0c073b602a914b
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc01f3f6c0d2ab84caa6ff319ae31142
|
||||
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,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0614582475885c44bb1b9dabb0104a4
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Pacore/Runtime/ClassAttributeCache.cs.meta
Normal file
2
Assets/Pacore/Runtime/ClassAttributeCache.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: efbe1f751ce7f8f428d2bca9dd90b69d
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0c94b5e5e59ce542857f241f24c38f3
|
||||
8
Assets/Pacore/Runtime/DevTools.meta
Normal file
8
Assets/Pacore/Runtime/DevTools.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1a11d87cd2e25d43bc7fb1eef3abc13
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Pacore/Runtime/DevTools/CodeProfiler.cs.meta
Normal file
2
Assets/Pacore/Runtime/DevTools/CodeProfiler.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ae7b0391b9731240a3b86fc0c2e03aa
|
||||
14
Assets/Pacore/Runtime/Pacore.asmdef
Normal file
14
Assets/Pacore/Runtime/Pacore.asmdef
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "PashaBibko.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: 57c37ac5cc9f0e44283e4295553368a4
|
||||
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: 38a9123c2b646114e84d7095a8fb204f
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Pacore/Runtime/Threading/ThreadDispatcher.cs.meta
Normal file
2
Assets/Pacore/Runtime/Threading/ThreadDispatcher.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dea7202d2750b6d4fb932511a26c0b50
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Pacore/Runtime/Threading/ThreadEnforcer.cs.meta
Normal file
2
Assets/Pacore/Runtime/Threading/ThreadEnforcer.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ef7f28549bf1594eab8e277cfcdfab7
|
||||
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(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Pacore/Runtime/Threading/ThreadSafeTry.cs.meta
Normal file
2
Assets/Pacore/Runtime/Threading/ThreadSafeTry.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c8f7e0d910af34342aefbb654c8b7acf
|
||||
Reference in New Issue
Block a user