Compare commits

..

24 Commits

Author SHA1 Message Date
102b4fb7bf Revert "Merge branch 'main' of https://git.bibko.uk/Pasha/Pacore"
This reverts commit 0dc0896291, reversing
changes made to 1524422b09.
2026-02-02 10:26:54 +00:00
0dc0896291 Merge branch 'main' of https://git.bibko.uk/Pasha/Pacore 2026-02-02 10:19:51 +00:00
1524422b09 Updated workflow for correct folder layout 2026-02-02 10:19:43 +00:00
Pasha Bibko
fdab098562 tmp 2026-01-29 15:59:27 +00:00
e9f627662c Fixed workflow 2026-01-28 22:47:58 +00:00
1cd8d611e2 Added automated release action 2026-01-28 22:35:33 +00:00
03086fab06 Updated .gitignore for rider files 2026-01-28 22:09:28 +00:00
294033268a Removed IDEA folder 2026-01-28 22:07:17 +00:00
c5f0f63efa Started adding markdown resources 2026-01-28 22:05:06 +00:00
2740b40d81 Removed Jetbrains.Annotations 2026-01-28 21:09:54 +00:00
2034818678 Updated rider package 2026-01-28 21:07:16 +00:00
a5b6e6110d Removed static field support 2026-01-28 20:54:53 +00:00
728464a327 Made serilaized static values be set 2026-01-26 16:00:04 +00:00
edc272945c Made classes require AllowStaticInspectorFields for static inspector fields 2026-01-26 15:39:09 +00:00
e286ea971d Started making static fields be unserialized 2026-01-26 13:11:48 +00:00
c61a293945 Made modified static fields serialize to disk 2026-01-26 00:29:43 +00:00
eacff3fc65 Made static fields have different values 2026-01-25 15:01:31 +00:00
4d22a379ab Started adding StaticInspectorField 2026-01-25 14:39:20 +00:00
f8b49427f1 Moved editor caches to seperate folder 2026-01-25 13:57:25 +00:00
abe3a4149b Added Thread enforcers 2026-01-25 13:49:05 +00:00
24820fe646 Added ThreadDispatcher
And ThreadSafe.Try
2026-01-25 00:04:16 +00:00
752b440e68 Optimised ClassAttributeClass a bit more 2026-01-24 22:26:59 +00:00
e905c1f7c5 Removed some LINQ 2026-01-24 22:08:59 +00:00
434b902814 Added ProfilerWindow 2026-01-24 21:33:24 +00:00
48 changed files with 550 additions and 262 deletions

40
.github/workflows/create-release.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: Create Unity package from repo
on:
workflow_dispatch:
inputs:
version:
description: 'Release version (e.g. 1.0.0)'
required: true
jobs:
build-package:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Setup Unity package folder
run: |
mkdir -p package
rsync -av --exclude='*.meta' Assets/Pacore/ package/
- name: Create tarball
run: |
VERSION=${{ github.event.inputs.version }}
PACKAGE_NAME="com.pashabibko.pacore-${VERSION}.tgz"
tar -czf "$PACKAGE_NAME" package
ls -1h "$PACKAGE_NAME"
- name: Create Github Release
uses: ncipollo/release-action@v1
with:
tag: ${{ github.event.inputs.version }}
name: Release ${{ github.event.inputs.version }}
body: "Automated release of Pacore package"
artifacts: "com.pashabibko.pacore-${{ github.event.inputs.version }}.tgz"
draft: false
prerelease: false
token: ${{ secrets.GITHUB_TOKEN }}

3
.gitignore vendored
View File

@@ -22,8 +22,9 @@
# Uncomment this line if you wish to ignore the asset store tools plugin
# /[Aa]ssets/AssetStoreTools*
# Autogenerated Jetbrains Rider plugin
# Rider files
/[Aa]ssets/Plugins/Editor/JetBrains*
/.idea/
# Visual Studio cache directory
.vs/

15
.idea/.idea.Pacore/.idea/.gitignore generated vendored
View File

@@ -1,15 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/contentModel.xml
/.idea.Pacore.iml
/modules.xml
/projectSettingsUpdater.xml
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

3
Assets/Pacore/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
# Files are generated/modified at runtime and do not need backing up #
Resources.meta
Resources

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9eace838af5041fd9d8cb05c8fbf9ef4
timeCreated: 1769349074

View 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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2574e8cf9f6b45dbb4aa22f0e62b23df
timeCreated: 1769349096

View 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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 96c81b83f299432fbf183bc40b69c061
timeCreated: 1769349335

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 9a1f5463ef6a4249af45548d72f211c6
timeCreated: 1769189971

View File

@@ -1,11 +0,0 @@
using UnityEngine;
using System;
namespace PashaBibko.Pacore.Editor.Drawers
{
public sealed class DisabledGUIBlock : IDisposable
{
public DisabledGUIBlock() => GUI.enabled = false;
public void Dispose() => GUI.enabled = true;
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 3d8664e5ad044a248710d9b31044d2fc
timeCreated: 1769189984

View File

@@ -11,11 +11,12 @@ namespace PashaBibko.Pacore.Editor.Drawers
{
if (attribute is InspectorReadOnlyAttribute readOnlyAttribute)
{
using (new DisabledGUIBlock())
{
label.text = readOnlyAttribute.Name ?? label.text; // Uses custom name if it exists
EditorGUI.PropertyField(position, property, label);
}
GUI.enabled = false;
label.text = readOnlyAttribute.Name ?? label.text; // Uses custom name if it exists
EditorGUI.PropertyField(position, property, label);
GUI.enabled = true;
}
}
}

View File

@@ -1,62 +1,11 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using PashaBibko.Pacore.Attributes;
using PashaBibko.Pacore.Editor.Caches;
using UnityEditor;
using UnityEngine;
using System;
using Object = UnityEngine.Object;
namespace PashaBibko.Pacore.Editor.Drawers
{
public static class InspectorCallableAttributeCache
{
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[] GetAllAttributes(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;
}
}
[CustomEditor(typeof(MonoBehaviour), editorForChildClasses: true)]
public class MonoBehaviourDrawer : UnityEditor.Editor
{
@@ -69,8 +18,8 @@ namespace PashaBibko.Pacore.Editor.Drawers
public static void DrawFunctionButtons(Object target)
{
Type type = target.GetType();
InspectorCallableAttributeCache.AttributeInfo[] buttons
= InspectorCallableAttributeCache.GetAllAttributes(type);
InspectorCallableCache.AttributeInfo[] buttons
= InspectorCallableCache.GetAllAttributesOfType(type);
if (buttons.Length == 0)
{
@@ -80,7 +29,7 @@ namespace PashaBibko.Pacore.Editor.Drawers
EditorGUILayout.Space();
EditorGUILayout.LabelField("Functions", EditorStyles.boldLabel);
foreach (InspectorCallableAttributeCache.AttributeInfo button in buttons)
foreach (InspectorCallableCache.AttributeInfo button in buttons)
{
string name = button.Attribute.ButtonName;
if (GUILayout.Button(name))

View File

@@ -1,38 +1,11 @@
using PashaBibko.Pacore.Attributes;
using System.Collections.Generic;
using PashaBibko.Pacore.Editor.Caches;
using PashaBibko.Pacore.Attributes;
using UnityEditor;
using UnityEngine;
using System;
namespace PashaBibko.Pacore.Editor.Drawers
{
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;
}
}
[CustomPropertyDrawer(typeof(MonoScriptAttribute))]
public class MonoScriptDrawer : PropertyDrawer
{

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d9b025c89af342a3b3cfc696a32fdb2e
timeCreated: 1769285442

View 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();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5bd8cdd7db4d4436978e97bda9cdc0cb
timeCreated: 1769285452

View File

@@ -1,15 +1,14 @@
using JetBrains.Annotations;
using UnityEngine;
using UnityEngine;
using System;
namespace PashaBibko.Pacore.Attributes
{
[MeansImplicitUse, AttributeUsage(validOn: AttributeTargets.Field)]
[AttributeUsage(validOn: AttributeTargets.Field)]
public sealed class DetectInspectorChangesAttribute : PropertyAttribute
{
public string ActionName { get; }
public DetectInspectorChangesAttribute([NotNull] string function)
public DetectInspectorChangesAttribute(string function)
{
ActionName = function;
}

View File

@@ -1,9 +1,8 @@
using JetBrains.Annotations;
using System;
using System;
namespace PashaBibko.Pacore.Attributes
{
[MeansImplicitUse, AttributeUsage(AttributeTargets.Method)]
[AttributeUsage(AttributeTargets.Method)]
public class InspectorCallableAttribute : Attribute
{
public string ButtonName { get; }

View File

@@ -1,11 +1,9 @@
using JetBrains.Annotations;
using UnityEngine;
using System;
namespace PashaBibko.Pacore.Attributes
{
#if UNITY_EDITOR
[MeansImplicitUse, AttributeUsage(validOn: AttributeTargets.Field)]
[AttributeUsage(validOn: AttributeTargets.Field)]
public sealed class InspectorReadOnlyAttribute : PropertyAttribute
{
public string Name { get; }
@@ -15,11 +13,4 @@ namespace PashaBibko.Pacore.Attributes
Name = name;
}
}
#else // #if UNITY_EDITOR
[MeansImplicitUse, AttributeUsage(validOn: AttributeTargets.Field)]
public sealed class InspectorReadOnlyAttribute : Attribute
{
public InspectorReadOnlyAttribute(string _name = null) { }
}
#endif // UNITY_EDITOR
}

View File

@@ -0,0 +1,7 @@
using System;
namespace PashaBibko.Pacore.Attributes
{
[AttributeUsage(validOn: AttributeTargets.Field)]
public class StaticInspectorFieldAttribute : Attribute { }
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f4bdb9ab5a544b57a8e8ec1dae246433
timeCreated: 1769349847

View File

@@ -1,6 +1,5 @@
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System;
using UnityEngine;
@@ -13,9 +12,9 @@ namespace PashaBibko.Pacore
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");
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)]
@@ -23,18 +22,7 @@ namespace PashaBibko.Pacore
{
/* Fetches all the class types in all loaded assemblies */
Type[] classes = AppDomain.CurrentDomain.GetAssemblies() // Assembly[]
.SelectMany(assembly =>
{
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException err)
{
return err.Types.Where(t => t != null);
}
}) // IEnumerable<Type>
.SelectMany(assembly => assembly.GetTypes()) // IEnumerable<Type>
.Where(type => type.IsClass && !type.IsAbstract) // IEnumerable<Type>
.ToArray();
@@ -48,18 +36,20 @@ namespace PashaBibko.Pacore
); // Dictionary<Type, List<Type>>
/* Finds which attributes are attached to what classes */
HashSet<Type> seen = new();
foreach (Type current in classes)
{
/* Finds all the attributes of the current class */
Type[] attached = current // Type
.GetCustomAttributes(inherit: true) // object[]
.Select(attribute => attribute.GetType()) // IEnumerable<Type>
.Distinct().ToArray();
/* 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 attribute in attached)
foreach (Type type in seen)
{
AttributeCache[attribute].Add(current);
AttributeCache[type].Add(current);
}
}
}

View File

@@ -1,9 +1,7 @@
{
"name": "Pacore",
"rootNamespace": "",
"references": [
"GUID:73bc4dcce6d82e44c8fe9738b987d694"
],
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d359635ad84f4f6eaf0635d203692066
timeCreated: 1769295079

View 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
}
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1ef64778d77647d9991a3b56a831dc5e
timeCreated: 1769293819

View 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");
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a4aad0735c2147beb2b087872fe4c364
timeCreated: 1769347995

View 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(); }
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0ffd3b00465e46ae99836711cf00f884
timeCreated: 1769295315

View File

@@ -1,7 +1,7 @@
{
"name": "com.pashabibko.pacore",
"displayName": "Pacore",
"version": "1.0.0",
"version": "1.1.1-alpha",
"unity": "2022.3",
"description" : "Small Unity Util library",
"keywords": [ "tool", "script", "runtime" ],

View File

@@ -133,7 +133,6 @@ GameObject:
- component: {fileID: 330585546}
- component: {fileID: 330585545}
- component: {fileID: 330585544}
- component: {fileID: 330585548}
- component: {fileID: 330585547}
m_Layer: 0
m_Name: Main Camera
@@ -260,21 +259,6 @@ MonoBehaviour:
m_MipBias: 0
m_VarianceClampScale: 0.9
m_ContrastAdaptiveSharpening: 0
--- !u!114 &330585548
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 330585543}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a411a69d284bfd04bafc210a81b43e88, type: 3}
m_Name:
m_EditorClassIdentifier:
go: {fileID: 0}
Spawnable: PashaBibko.Pacore.Editor.Drawers.DetectInspectorChangesDrawer
Test: 0
--- !u!1 &410087039
GameObject:
m_ObjectHideFlags: 0

View File

@@ -1,30 +0,0 @@
using PashaBibko.Pacore.Attributes;
using UnityEngine;
[CreateInstanceOnStart] public class TestMonoBehaviour : MonoBehaviour
{
[InspectorReadOnly, SerializeField] private GameObject go;
[MonoScript(inherited: typeof(MonoBehaviour))] public string Spawnable;
[DetectInspectorChanges("OnTestChange")]
public int Test;
private void OnTestChange() => LogTestValue();
[InspectorCallable(nameof(LogSpawnableType))]
public void LogSpawnableType()
{
Debug.Log(Spawnable);
}
[InspectorCallable("Test button")] public void LogTestValue()
{
Debug.Log($"Test value [{Test}]");
}
[InspectorCallable("Other Test button")] public void DontLogTestValue()
{
Debug.Log("Test");
}
}

View File

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

0
Features.md Normal file
View File

View File

@@ -1,7 +1,7 @@
{
"dependencies": {
"com.unity.collab-proxy": "2.11.3",
"com.unity.ide.rider": "3.0.36",
"com.unity.ide.rider": "3.0.39",
"com.unity.ide.visualstudio": "2.0.22",
"com.unity.ide.vscode": "1.2.5",
"com.unity.render-pipelines.universal": "14.0.12",

View File

@@ -25,7 +25,7 @@
"url": "https://packages.unity.com"
},
"com.unity.ide.rider": {
"version": "3.0.36",
"version": "3.0.39",
"depth": 0,
"source": "registry",
"dependencies": {

View File

@@ -3,33 +3,45 @@
--- !u!159 &1
EditorSettings:
m_ObjectHideFlags: 0
serializedVersion: 9
m_ExternalVersionControlSupport: Visible Meta Files
serializedVersion: 12
m_SerializationMode: 2
m_LineEndingsForNewScripts: 2
m_DefaultBehaviorMode: 0
m_PrefabRegularEnvironment: {fileID: 0}
m_PrefabUIEnvironment: {fileID: 0}
m_SpritePackerMode: 0
m_SpritePackerCacheSize: 10
m_SpritePackerPaddingPower: 1
m_Bc7TextureCompressor: 0
m_EtcTextureCompressorBehavior: 1
m_EtcTextureFastCompressor: 1
m_EtcTextureNormalCompressor: 2
m_EtcTextureBestCompressor: 4
m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;asmref;rsp
m_ProjectGenerationRootNamespace:
m_CollabEditorSettings:
inProgressEnabled: 1
m_EnableTextureStreamingInEditMode: 1
m_EnableTextureStreamingInPlayMode: 1
m_EnableEditorAsyncCPUTextureLoading: 0
m_AsyncShaderCompilation: 1
m_EnterPlayModeOptionsEnabled: 0
m_PrefabModeAllowAutoSave: 1
m_EnterPlayModeOptionsEnabled: 1
m_EnterPlayModeOptions: 3
m_ShowLightmapResolutionOverlay: 1
m_GameObjectNamingDigits: 1
m_GameObjectNamingScheme: 0
m_AssetNamingUsesSpace: 1
m_InspectorUseIMGUIDefaultInspector: 0
m_UseLegacyProbeSampleCount: 0
m_SerializeInlineMappingsOnOneLine: 0
m_DisableCookiesInLightmapper: 1
m_AssetPipelineMode: 1
m_RefreshImportMode: 0
m_CacheServerMode: 0
m_CacheServerEndpoint:
m_CacheServerNamespacePrefix: default
m_CacheServerEnableDownload: 1
m_CacheServerEnableUpload: 1
m_CacheServerEnableAuth: 0
m_CacheServerEnableTls: 0
m_CacheServerValidationMode: 2
m_CacheServerDownloadBatchSize: 128
m_EnableEnlightenBakedGI: 0

19
README.md Normal file
View File

@@ -0,0 +1,19 @@
# Pacore
Pacore is a small unity coding QoL library mainly focused on improving accesability within the editor.
You can view a list of the features with their documentation [here](Features.md)
and all planned features [here](Todo.md).
> NOTE: You may be viewing this repository on the github mirror,
> the repository is hosted on [git.bibko.uk](https://git.bibko.uk/Pasha/Pacore)
### Add to project
#### Requirements
- Unity 2022.3 or later (earlier versions may still work)
- No other dependencies or software is required
#### Instillation guide
WIP

8
Todo.md Normal file
View File

@@ -0,0 +1,8 @@
### Planned features
##### Static variable inspector tooling
- Ability to view static fields
- Change static values within the inspector
##### Instillation guide
- Instructions to download and include the library within Unity projects