1
0
Fork 0

Version 1.0.0

This commit is contained in:
Roz K 2023-07-18 22:01:31 +02:00
parent ee28ffc636
commit 4ce513638e
Signed by: roz
GPG Key ID: 51FBF4E483E1C822
24 changed files with 1351 additions and 0 deletions

7
CHANGELOG.md.meta Normal file
View File

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

7
LICENSE.md.meta Normal file
View File

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

7
README.md.meta Normal file
View File

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

8
Runtime.meta Normal file
View File

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

214
Runtime/MicroActions.cs Normal file
View File

@ -0,0 +1,214 @@
// Copyright 2023 RozK
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
// OF SUCH DAMAGE.
using System;
using System.Collections.Generic;
using UnityEngine.Assertions;
namespace RK.MicroComponents {
public delegate void MicroFunc<_Param>(_Param param);
internal readonly struct MicroAction {
internal readonly ulong Guid; // -> MicroActions
internal readonly Delegate Func; // -> MicroActions
private readonly object _param;
internal static MicroAction Create<_Param>(ulong guid, MicroFunc<_Param> func) {
return new MicroAction(guid, func, null);
}
internal static MicroAction Bind<_Param>(MicroAction action, _Param param) {
return new MicroAction(action.Guid, action.Func, param);
}
internal static MicroAction Create<_Param>(ulong guid, MicroFunc<_Param> func, _Param param) {
return new MicroAction(guid, func, param);
}
private MicroAction(ulong guid, Delegate func, object param) {
Guid = guid;
Func = func;
_param = param;
}
internal void Invoke() {
Func.DynamicInvoke(_param);
}
}
public class MicroActions {
internal List<MicroAction> ParallelActions; // -> MicroSystem
internal List<MicroAction> SyncActions; // -> MicroSystem
internal bool IsEmpty {
get => (
(ParallelActions is null || ParallelActions.Count == 0) &&
(SyncActions is null || SyncActions.Count == 0)
);
}
private void AddParallel(MicroAction action) {
if (ParallelActions is null) {
ParallelActions = new List<MicroAction>(1);
}
ParallelActions.Add(action);
}
internal void AddParallel<_Param>(MicroFunc<_Param> func) {
MicroInstance instance = func.Target as MicroInstance;
Assert.IsNotNull(instance, "Must be a MicroInstance.");
if (instance.IsValid) {
AddParallel(MicroAction.Create(instance.Guid, func));
}
}
internal void AddParallel<_Param>(MicroFunc<_Param> func, _Param param) {
MicroInstance instance = func.Target as MicroInstance;
Assert.IsNotNull(instance, "Must be a MicroInstance.");
if (instance.IsValid) {
AddParallel(MicroAction.Create(instance.Guid, func, param));
}
}
private void AddSync(MicroAction action) {
if (SyncActions is null) {
SyncActions = new List<MicroAction>(1);
}
SyncActions.Add(action);
}
internal void AddSync<_Param>(MicroFunc<_Param> func) {
MicroInstance instance = func.Target as MicroInstance;
Assert.IsNotNull(instance, "Must be a MicroInstance.");
if (instance.IsValid) {
AddSync(MicroAction.Create(instance.Guid, func));
}
}
internal void AddSync<_Param>(MicroFunc<_Param> func, _Param param) {
MicroInstance instance = func.Target as MicroInstance;
Assert.IsNotNull(instance, "Must be a MicroInstance.");
if (instance.IsValid) {
AddSync(MicroAction.Create(instance.Guid, func, param));
}
}
internal void AddActions(MicroActions actions) {
if (actions.ParallelActions is not null && actions.ParallelActions.Count > 0) {
if (ParallelActions is null) {
ParallelActions = new List<MicroAction>(actions.ParallelActions.Count);
}
else {
Preallocate(ParallelActions, actions.ParallelActions.Count);
}
ParallelActions.AddRange(actions.ParallelActions);
}
if (actions.SyncActions is not null && actions.SyncActions.Count > 0) {
if (SyncActions is null) {
SyncActions = new List<MicroAction>(actions.SyncActions.Count);
}
else {
Preallocate(SyncActions, actions.SyncActions.Count);
}
SyncActions.AddRange(actions.SyncActions);
}
}
internal void AddEvent<_Param>(MicroEvent<_Param> _event, _Param param) {
_event.Mutex.Acquire();
if (_event.ParallelActions is not null && _event.ParallelActions.Count > 0) {
if (ParallelActions is null) {
ParallelActions = new List<MicroAction>(_event.ParallelActions.Count);
}
else {
Preallocate(ParallelActions, _event.ParallelActions.Count);
}
foreach (MicroAction action in _event.ParallelActions) {
ParallelActions.Add(MicroAction.Bind(action, param));
}
}
if (_event.SyncActions is not null && _event.SyncActions.Count > 0) {
if (SyncActions is null) {
SyncActions = new List<MicroAction>(_event.SyncActions.Count);
}
else {
Preallocate(SyncActions, _event.SyncActions.Count);
}
foreach (MicroAction action in _event.SyncActions) {
SyncActions.Add(MicroAction.Bind(action, param));
}
}
_event.Mutex.Release();
}
private void Preallocate(List<MicroAction> actions, int count) {
int capacity = actions.Count + count;
if (actions.Capacity < capacity) {
actions.Capacity = capacity;
}
}
internal void RemoveParallel<_Param>(MicroFunc<_Param> func) {
if (ParallelActions is not null) {
ParallelActions.RemoveAll(
action => action.Func.Equals(func)
);
}
}
internal void RemoveSync<_Param>(MicroFunc<_Param> func) {
if (SyncActions is not null) {
SyncActions.RemoveAll(
action => action.Func.Equals(func)
);
}
}
internal void RemoveInstance(MicroInstance instance) {
ulong guid = instance.Guid;
if (ParallelActions is not null) {
ParallelActions.RemoveAll(
action => (action.Guid == guid)
);
}
if (SyncActions is not null) {
SyncActions.RemoveAll(
action => (action.Guid == guid)
);
}
}
internal void Clear() {
if (ParallelActions is not null) {
ParallelActions.Clear();
}
if (SyncActions is not null) {
SyncActions.Clear();
}
}
}
} // namespace RK.MicroComponents

View File

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

134
Runtime/MicroBehaviour.cs Normal file
View File

@ -0,0 +1,134 @@
// Copyright 2023 RozK
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
// OF SUCH DAMAGE.
// TODO: Start/Stop by priority?
using System.Collections.Generic;
using UnityEngine;
namespace RK.MicroComponents {
[DefaultExecutionOrder(2)]
[DisallowMultipleComponent]
[AddComponentMenu("MicroComponents/MicroBehaviour")]
public sealed class MicroBehaviour : MonoBehaviour {
[SerializeField]
private List<MicroComponent> _microComponents;
private bool _waitStart;
private MicroInstance[] _instances;
private void Awake() {
if (_microComponents is null) {
Debug.LogError("[MicroComponents] Null component list.");
Destroy(this);
return;
}
for (int index = _microComponents.Count - 1; index >= 0; index--) {
if (!_microComponents[index]) {
Debug.LogWarning("[MicroComponents] Null component.");
_microComponents.RemoveAt(index);
}
}
if (_microComponents.Count == 0) {
Debug.LogError("[MicroComponents] Empty component list.");
Destroy(this);
return;
}
_waitStart = enabled;
_instances = new MicroInstance[_microComponents.Count];
for (int index = 0; index < _microComponents.Count; index++) {
MicroComponent component = _microComponents[index];
_instances[index] = component.Manager.Allocate(this, component);
}
}
private void Start() {
if (_waitStart) {
_waitStart = false;
OnEnable();
}
}
private void OnEnable() {
if (!_waitStart) {
for (int index = 0; index < _microComponents.Count; index++) {
MicroInstance instance = _instances[index];
if (instance.IsValid) {
MicroComponent component = _microComponents[index];
component.Manager.Start(instance);
}
}
}
}
private void OnDisable() {
for (int index = 0; index < _microComponents.Count; index++) {
MicroInstance instance = _instances[index];
if (instance.IsValid) {
MicroComponent component = _microComponents[index];
component.Manager.Stop(instance);
}
}
}
private void OnDestroy() {
for (int index = 0; index < _microComponents.Count; index++) {
MicroInstance instance = _instances[index];
if (instance.IsValid) {
MicroComponent component = _microComponents[index];
component.Manager.Deallocate(instance);
}
_instances[index] = null;
}
_instances = null;
}
internal _Instance GetInstance<_Component, _Instance>()
where _Component : MicroComponent
where _Instance : MicroInstance<_Component, _Instance>, new() {
for (int index = 0; index < _microComponents.Count; index++) {
MicroComponent component = _microComponents[index];
if (component is _Component) {
return _instances[index] as _Instance;
}
}
return null;
}
internal _Instance GetInstance<_Component, _Instance>(_Component _component)
where _Component : MicroComponent
where _Instance : MicroInstance<_Component, _Instance>, new() {
for (int index = 0; index < _microComponents.Count; index++) {
MicroComponent component = _microComponents[index];
if (component == _component) {
return _instances[index] as _Instance;
}
}
return null;
}
}
} // namespace RK.MicroComponents

View File

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

42
Runtime/MicroComponent.cs Normal file
View File

@ -0,0 +1,42 @@
// Copyright 2023 RozK
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
// OF SUCH DAMAGE.
using UnityEngine;
namespace RK.MicroComponents {
public abstract class MicroComponent : ScriptableObject {
internal abstract MicroManager Manager { get; }
}
public abstract class MicroComponent<_Component, _Instance> : MicroComponent
where _Component : MicroComponent
where _Instance : MicroInstance<_Component, _Instance>, new() {
private static MicroManager<_Component, _Instance> _manager = new();
internal sealed override MicroManager Manager { get => _manager; }
}
} // namespace RK.MicroComponents

View File

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

67
Runtime/MicroEvent.cs Normal file
View File

@ -0,0 +1,67 @@
// Copyright 2023 RozK
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
// OF SUCH DAMAGE.
namespace RK.MicroComponents {
public class MicroEvent<_Param> : MicroActions {
internal MicroMutex Mutex; // <- MicroActions
public void AddParallel(MicroFunc<_Param> func) {
Mutex.Acquire();
base.AddParallel(func);
Mutex.Release();
}
public void RemoveParallel(MicroFunc<_Param> func) {
Mutex.Acquire();
base.RemoveParallel(func);
Mutex.Release();
}
public void AddSync(MicroFunc<_Param> func) {
Mutex.Acquire();
base.AddSync(func);
Mutex.Release();
}
public void RemoveSync(MicroFunc<_Param> func) {
Mutex.Acquire();
base.RemoveSync(func);
Mutex.Release();
}
public new void RemoveInstance(MicroInstance instance) {
Mutex.Acquire();
base.RemoveInstance(instance);
Mutex.Release();
}
public void Terminate() {
Clear();
ParallelActions = null;
SyncActions = null;
}
}
} // namespace RK.MicroComponents

View File

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

206
Runtime/MicroInstance.cs Normal file
View File

@ -0,0 +1,206 @@
// Copyright 2023 RozK
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
// OF SUCH DAMAGE.
using System;
using System.Collections.Generic;
using UnityEngine;
namespace RK.MicroComponents {
[Flags]
public enum MicroUpdates {
None = 0,
ParallelFixedUpdate = 1,
SyncFixedUpdate = 2,
ParallelUpdate = 4,
SyncUpdate = 8,
ParallelLateUpdate = 16,
SyncLateUpdate = 32
}
public abstract class MicroInstance {
private static ulong _nextGuid = 1;
internal ulong Guid; // -> MicroActions
internal int InstanceId; // <- MicroManager
internal MicroActions Actions; // <- MicroManager
private MicroBehaviour _behaviour;
public MicroBehaviour Behaviour { get => _behaviour; }
internal bool IsValid { get => (Guid > 0); }
internal protected MicroInstance() {
Guid = 0;
InstanceId = -1;
}
internal void Allocate(int instanceId, MicroBehaviour behaviour) {
Guid = _nextGuid++;
InstanceId = instanceId;
_behaviour = behaviour;
}
internal void Deallocate() {
Guid = 0;
InstanceId = -1;
_behaviour = null;
}
internal protected virtual void Initialize() {
}
internal protected virtual void Terminate() {
}
internal protected virtual void Start() {
}
internal protected virtual void Stop() {
}
internal protected virtual void ParallelFixedUpdate() {
}
internal protected virtual void SyncFixedUpdate() {
}
internal protected virtual void ParallelUpdate() {
}
internal protected virtual void SyncUpdate() {
}
internal protected virtual void ParallelLateUpdate() {
}
internal protected virtual void SyncLateUpdate() {
}
protected void ParallelCall<_Param>(MicroFunc<_Param> func, _Param param) {
Actions.AddParallel(func, param);
}
protected void SyncCall<_Param>(MicroFunc<_Param> func, _Param param) {
Actions.AddSync(func, param);
}
protected void InvokeEvent<_Param>(MicroEvent<_Param> _event, _Param param) {
Actions.AddEvent(_event, param);
}
}
public abstract class MicroInstance<_Component, _Instance> : MicroInstance
where _Component : MicroComponent
where _Instance : MicroInstance<_Component, _Instance>, new() {
private static MicroConfiguration _configuration;
internal MicroConfiguration Configuration { get => _configuration; }
protected static void Configure(
MicroUpdates updates,
int parallelBatchSize = 1,
int poolMinimumSize = 0,
int poolMaximumSize = 0,
int poolGranularity = 1) {
_configuration.Updates = updates;
_configuration.ParallelBatchSize = (parallelBatchSize > 0) ? parallelBatchSize : 1;
_configuration.PoolMinimumSize = (poolMinimumSize >= 0) ? poolMinimumSize : 0;
_configuration.PoolMaximumSize = (poolMaximumSize >= 0) ? poolMaximumSize : 0;
_configuration.PoolGranularity = (poolGranularity > 0) ? poolGranularity : 1;
if (_configuration.PoolMaximumSize > 0 &&
_configuration.PoolMaximumSize < _configuration.PoolMinimumSize) {
Debug.LogError("[MicroComponents] Pool maximum size must be >= minimum size.");
_configuration.PoolMaximumSize = _configuration.PoolMinimumSize;
}
}
public static void UpdateAfter<_Dependency>()
where _Dependency : MicroInstance {
if (_configuration.Dependencies is null) {
_configuration.Dependencies = new HashSet<Type>(1);
}
_configuration.Dependencies.Add(typeof(_Dependency));
}
public static Reference From(MicroBehaviour behaviour) {
return behaviour.GetInstance<_Component, _Instance>();
}
public static Reference From(MicroBehaviour behaviour, _Component component) {
return behaviour.GetInstance<_Component, _Instance>(component);
}
private _Component _component;
public _Component Component { get => _component; }
internal void Allocate(int instanceId, MicroBehaviour behaviour, _Component component) {
base.Allocate(instanceId, behaviour);
_component = component;
}
internal new void Deallocate() {
base.Deallocate();
_component = null;
}
public struct Reference {
private ulong _guid;
private _Instance _instance;
public _Instance Instance {
get {
if (_instance is not null && _instance.Guid != _guid) {
_guid = 0;
_instance = null;
}
return _instance;
}
set {
if (value is not null && value.IsValid) {
_guid = value.Guid;
_instance = value;
}
else {
_guid = 0;
_instance = null;
}
}
}
public static implicit operator Reference(_Instance instance) {
Reference reference = new Reference();
reference.Instance = instance;
return reference;
}
public static implicit operator _Instance(Reference reference) {
return reference.Instance;
}
}
}
} // namespace RK.MicroComponents

View File

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

262
Runtime/MicroManager.cs Normal file
View File

@ -0,0 +1,262 @@
// Copyright 2023 RozK
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
// OF SUCH DAMAGE.
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using UnityEngine;
namespace RK.MicroComponents {
internal struct MicroConfiguration {
internal MicroUpdates Updates;
internal int ParallelBatchSize;
internal int PoolMinimumSize;
internal int PoolMaximumSize;
internal int PoolGranularity;
internal HashSet<Type> Dependencies;
}
internal abstract class MicroManager {
internal int Priority; // <- MicroSystem
internal MicroConfiguration Configuration; // -> MicroSystem
internal ConcurrentQueue<MicroActions> ThreadsActions; // <- MicroSystem
protected int _startedCount;
protected int _totalCount;
internal abstract Type InstanceType { get; }
internal abstract MicroInstance Allocate(MicroBehaviour behaviour, MicroComponent component);
internal abstract void Deallocate(MicroInstance instance);
internal abstract void Start(MicroInstance instance);
internal abstract void Stop(MicroInstance instance);
internal abstract void ParallelFixedUpdate();
internal abstract void SyncFixedUpdate();
internal abstract void ParallelUpdate();
internal abstract void SyncUpdate();
internal abstract void ParallelLateUpdate();
internal abstract void SyncLateUpdate();
}
internal sealed class MicroManager<_Component, _Instance> : MicroManager
where _Component : MicroComponent
where _Instance : MicroInstance<_Component, _Instance>, new() {
private List<_Instance> _instances;
internal sealed override Type InstanceType { get => typeof(_Instance); }
internal MicroManager() {
_Instance instance = new _Instance();
Configuration = instance.Configuration;
_startedCount = 0;
_totalCount = 0;
int capacity = Configuration.PoolGranularity;
if (Configuration.PoolMinimumSize > capacity) {
int top = Configuration.PoolMinimumSize + Configuration.PoolGranularity - 1;
capacity = (top / Configuration.PoolGranularity) * Configuration.PoolGranularity;
}
_instances = new List<_Instance>(capacity);
if (Configuration.PoolMinimumSize > 0) {
_instances.Add(instance);
for (int index = 1; index < Configuration.PoolMinimumSize; index++) {
_instances.Add(new _Instance());
}
}
MicroSystem.RegisterManager(this);
}
internal sealed override MicroInstance Allocate(MicroBehaviour behaviour, MicroComponent _component) {
if (Configuration.PoolMaximumSize > 0 && _totalCount == Configuration.PoolMaximumSize) {
Debug.LogError("[MicroComponents] Pool reached maximum size.");
return null;
}
_Component component = _component as _Component;
if (!component) {
Debug.LogError("[MicroComponents] Null component.");
return null;
}
_Instance instance;
if (_totalCount < _instances.Count) {
instance = _instances[_totalCount];
}
else {
if (_totalCount == _instances.Capacity) {
_instances.Capacity = _totalCount + Configuration.PoolGranularity;
}
instance = new _Instance();
_instances.Add(instance);
}
instance.Allocate(_totalCount++, behaviour, component);
instance.Initialize();
return instance;
}
internal sealed override void Deallocate(MicroInstance _instance) {
int instanceId = _instance.InstanceId;
if (instanceId >= _startedCount && instanceId < _totalCount) {
_Instance instance = _instances[instanceId];
Swap(instanceId, --_totalCount);
instance.Terminate();
instance.Deallocate();
int count = _instances.Count;
if (count > Configuration.PoolMinimumSize) {
_instances.RemoveAt(--count);
}
int capacity = _instances.Capacity;
if ((capacity - count) >= Configuration.PoolGranularity) {
_instances.Capacity = capacity - Configuration.PoolGranularity;
}
}
else {
Debug.LogError("[MicroComponents] InstanceId is invalid.");
}
}
internal sealed override void Start(MicroInstance instance) {
int instanceId = instance.InstanceId;
if (instanceId >= _startedCount && instanceId < _totalCount) {
Swap(instanceId, _startedCount++);
instance.Start();
}
else {
Debug.LogError("[MicroComponents] Instance is invalid or already started.");
}
}
internal sealed override void Stop(MicroInstance instance) {
int instanceId = instance.InstanceId;
if (instanceId >= 0 && instanceId < _startedCount) {
instance.Stop();
Swap(instanceId, --_startedCount);
}
else {
Debug.LogError("[MicroComponents] Instance is invalid or already stopped.");
}
}
private void Swap(int instanceId, int otherInstanceId) {
if (instanceId != otherInstanceId) {
_Instance instance = _instances[instanceId];
_Instance otherInstance = _instances[otherInstanceId];
_instances[instanceId] = otherInstance;
_instances[otherInstanceId] = instance;
otherInstance.InstanceId = instanceId;
instance.InstanceId = otherInstanceId;
}
}
internal sealed override void ParallelFixedUpdate() {
ParallelInvokeUpdate(instance => instance.ParallelFixedUpdate());
}
internal sealed override void SyncFixedUpdate() {
SyncInvokeUpdate(instance => instance.SyncFixedUpdate());
}
internal sealed override void ParallelUpdate() {
ParallelInvokeUpdate(instance => instance.ParallelUpdate());
}
internal sealed override void SyncUpdate() {
SyncInvokeUpdate(instance => instance.SyncUpdate());
}
internal sealed override void ParallelLateUpdate() {
ParallelInvokeUpdate(instance => instance.ParallelLateUpdate());
}
internal sealed override void SyncLateUpdate() {
SyncInvokeUpdate(instance => instance.SyncLateUpdate());
}
private void InvokeUpdate(int instanceId, Action<_Instance> invokeUpdate, MicroActions actions) {
_Instance instance = _instances[instanceId];
instance.Actions = actions;
invokeUpdate(instance);
instance.Actions = null;
}
private void InvokeUpdate(int firstId, int lastId, Action<_Instance> invokeUpdate, MicroActions actions) {
for (int instanceId = firstId; instanceId < lastId; instanceId++) {
InvokeUpdate(instanceId, invokeUpdate, actions);
}
}
private void ParallelInvokeUpdate(Action<_Instance> invokeUpdate) {
if (_startedCount > 0) {
int batchSize = Configuration.ParallelBatchSize;
if (batchSize == 1) {
Parallel.For(0, _startedCount, MicroSystem.ParallelOptions,
() => {
ThreadsActions.TryDequeue(out MicroActions actions);
return actions;
},
(instanceId, _, actions) => {
InvokeUpdate(instanceId, invokeUpdate, actions);
return actions;
},
actions => {
ThreadsActions.Enqueue(actions);
}
);
}
else {
int batchCount = (_startedCount + batchSize - 1) / batchSize;
Parallel.For(0, batchCount, MicroSystem.ParallelOptions,
() => {
if (!ThreadsActions.TryDequeue(out MicroActions actions)) {
Debug.LogError("[MicroComponents] Too many threads.");
actions = new MicroActions();
}
return actions;
},
(batchIndex, _, actions) => {
int firstId = batchIndex * batchSize;
int lastId = firstId + batchSize;
if (lastId > _startedCount) {
lastId = _startedCount;
}
InvokeUpdate(firstId, lastId, invokeUpdate, actions);
return actions;
},
actions => {
ThreadsActions.Enqueue(actions);
}
);
}
}
}
private void SyncInvokeUpdate(Action<_Instance> invokeUpdate) {
ThreadsActions.TryDequeue(out MicroActions actions);
InvokeUpdate(0, _startedCount, invokeUpdate, actions);
ThreadsActions.Enqueue(actions);
}
}
} // namespace RK.MicroComponents

View File

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

41
Runtime/MicroMutex.cs Normal file
View File

@ -0,0 +1,41 @@
// Copyright 2023 RozK
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
// OF SUCH DAMAGE.
using System.Threading;
namespace RK.MicroComponents {
public struct MicroMutex {
private int _flag;
public void Acquire() {
while (Interlocked.CompareExchange(ref _flag, 1, 0) != 0);
}
public void Release() {
Interlocked.Exchange(ref _flag, 0);
}
}
} // namespace RK.MicroComponents

View File

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

222
Runtime/MicroSystem.cs Normal file
View File

@ -0,0 +1,222 @@
// Copyright 2023 RozK
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
// OF SUCH DAMAGE.
// TODO: parallel/sync calls by priority?
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System.Linq;
using UnityEngine;
namespace RK.MicroComponents {
[DefaultExecutionOrder(1)]
[DisallowMultipleComponent]
[AddComponentMenu("MicroComponents/MicroSystem")]
public sealed class MicroSystem : MonoBehaviour {
internal static readonly ParallelOptions ParallelOptions = new ParallelOptions {
MaxDegreeOfParallelism = Environment.ProcessorCount
};
private static MicroSystem _instance;
private static List<MicroManager> _managers;
internal static void RegisterManager(MicroManager manager) {
if (_managers is null) {
_managers = new List<MicroManager>();
}
_managers.Add(manager);
}
private Dictionary<Type, MicroManager> _managersByType;
private List<MicroManager> _parallelFixedUpdateManagers;
private List<MicroManager> _syncFixedUpdateManagers;
private List<MicroManager> _parallelUpdateManagers;
private List<MicroManager> _syncUpdateManagers;
private List<MicroManager> _parallelLateUpdateManagers;
private List<MicroManager> _syncLateUpdateManagers;
private ConcurrentQueue<MicroActions> _threadsActions;
private MicroActions _actions;
private void EvaluatePriorities() {
foreach (MicroManager manager in _managers) {
FixCyclicDependencies(manager);
manager.Priority = -1;
}
foreach (MicroManager manager in _managers) {
if (manager.Priority < 0) {
EvaluatePriority(manager);
}
}
}
private void EvaluatePriority(MicroManager manager) {
manager.Priority = 0;
HashSet<Type> dependencies = manager.Configuration.Dependencies;
if (dependencies is not null) {
foreach (Type dependencyType in dependencies) {
if (_managersByType.TryGetValue(dependencyType, out MicroManager dependency)) {
if (dependency.Priority < 0) {
EvaluatePriority(dependency);
}
if (dependency.Priority > manager.Priority) {
manager.Priority = dependency.Priority;
}
}
}
manager.Priority += 1;
}
}
private void FixCyclicDependencies(MicroManager manager) {
Type instanceType = manager.InstanceType;
List<MicroManager> cyclicDependencies = new List<MicroManager>();
FindDependenciesWithDependency(manager, instanceType, cyclicDependencies);
foreach (MicroManager dependency in cyclicDependencies) {
Type dependencyType = dependency.InstanceType;
Debug.LogError(
$"[MicroComponents] Cyclic dependency: {dependencyType} -> {instanceType}");
dependency.Configuration.Dependencies.Remove(instanceType);
}
}
private void FindDependenciesWithDependency(MicroManager manager, Type type, List<MicroManager> found) {
HashSet<Type> dependencies = manager.Configuration.Dependencies;
if (dependencies is not null) {
if (dependencies.Contains(type)) {
found.Add(manager);
}
foreach (Type dependencyType in dependencies) {
if (_managersByType.TryGetValue(dependencyType, out MicroManager dependency)) {
FindDependenciesWithDependency(dependency, type, found);
}
}
}
}
private void Awake() {
if (_instance) {
Debug.LogError(
"[MicroComponents] Only one MicroSystem is allowed at any given time.");
Destroy(this);
return;
}
_instance = this;
_managersByType = new Dictionary<Type, MicroManager>();
foreach (MicroManager manager in _managers) {
_managersByType.Add(manager.InstanceType, manager);
}
EvaluatePriorities();
_managers.Sort((a, b) => a.Priority.CompareTo(b.Priority));
_parallelFixedUpdateManagers = new List<MicroManager>();
_syncFixedUpdateManagers = new List<MicroManager>();
_parallelUpdateManagers = new List<MicroManager>();
_syncUpdateManagers = new List<MicroManager>();
_parallelLateUpdateManagers = new List<MicroManager>();
_syncLateUpdateManagers = new List<MicroManager>();
foreach (MicroManager manager in _managers) {
MicroUpdates updates = manager.Configuration.Updates;
if ((updates & MicroUpdates.ParallelFixedUpdate) != MicroUpdates.None) {
_parallelFixedUpdateManagers.Add(manager);
}
if ((updates & MicroUpdates.SyncFixedUpdate) != MicroUpdates.None) {
_syncFixedUpdateManagers.Add(manager);
}
if ((updates & MicroUpdates.ParallelUpdate) != MicroUpdates.None) {
_parallelUpdateManagers.Add(manager);
}
if ((updates & MicroUpdates.SyncUpdate) != MicroUpdates.None) {
_syncUpdateManagers.Add(manager);
}
if ((updates & MicroUpdates.ParallelLateUpdate) != MicroUpdates.None) {
_parallelLateUpdateManagers.Add(manager);
}
if ((updates & MicroUpdates.SyncLateUpdate) != MicroUpdates.None) {
_syncLateUpdateManagers.Add(manager);
}
}
_threadsActions = new ConcurrentQueue<MicroActions>();
for (int index = 0; index < ParallelOptions.MaxDegreeOfParallelism; index++) {
_threadsActions.Enqueue(new MicroActions());
}
_actions = new MicroActions();
}
private void OnDestroy() {
_instance = null;
}
private void FixedUpdate() {
InvokeUpdate(_parallelFixedUpdateManagers, manager => manager.ParallelFixedUpdate());
InvokeUpdate(_syncFixedUpdateManagers, manager => manager.SyncFixedUpdate());
}
private void Update() {
InvokeUpdate(_parallelUpdateManagers, manager => manager.ParallelUpdate());
InvokeUpdate(_syncUpdateManagers, manager => manager.SyncUpdate());
}
private void LateUpdate() {
InvokeUpdate(_parallelLateUpdateManagers, manager => manager.ParallelLateUpdate());
InvokeUpdate(_syncLateUpdateManagers, manager => manager.SyncLateUpdate());
}
private void InvokeUpdate(List<MicroManager> managers, Action<MicroManager> invokeUpdate) {
if (managers.Count > 0) {
foreach (MicroManager manager in managers) {
manager.ThreadsActions = _threadsActions;
invokeUpdate(manager);
manager.ThreadsActions = null;
}
foreach (MicroActions actions in _threadsActions) {
_actions.AddActions(actions);
actions.Clear();
}
if (_actions.ParallelActions is not null && _actions.ParallelActions.Count > 0) {
ParallelQuery<MicroAction> actions = _actions.ParallelActions.AsParallel()
.WithExecutionMode(ParallelExecutionMode.ForceParallelism)
.WithDegreeOfParallelism(Environment.ProcessorCount);
(from action in actions group action by action.Guid).ForAll(
group => {
foreach (MicroAction action in group) {
action.Invoke();
}
}
);
_actions.ParallelActions.Clear();
}
if (_actions.SyncActions is not null && _actions.SyncActions.Count > 0) {
foreach(MicroAction action in _actions.SyncActions) {
action.Invoke();
}
_actions.SyncActions.Clear();
}
}
}
}
} // namespace RK.MicroComponents

View File

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

View File

@ -0,0 +1,13 @@
{
"name": "RK.MicroComponents",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

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

19
package.json Normal file
View File

@ -0,0 +1,19 @@
{
"name": "net.rozk.microcomponents",
"version": "1.0.0",
"displayName": "RK.MicroComponents",
"description": "Multi-threaded sub-components system.",
"keywords": [
"multi-threaded",
"components"
],
"author": {
"name": "RozK",
"email": "contact@rozk.net",
"url": "https://code.rozk.net"
},
"licensesUrl": "https://code.rozk.net/RK/rk_microcomponents/src/branch/main/LICENSE.md",
"changelogUrl": "https://code.rozk.net/RK/rk_microcomponents/src/branch/main/CHANGELOG.md",
"dependencies": {
}
}

7
package.json.meta Normal file
View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 394ef4b1864ef4275a283945727b5bde
PackageManifestImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: