Version 1.0.0
This commit is contained in:
parent
ee28ffc636
commit
4ce513638e
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 760473b30b427a14fb2978d0d3c8570d
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c2ad0c15006fa3922bd8f18186ef7746
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 079513330e3254dc7a157d92b8e6882f
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 448103e32c776a0dc85a697fbf43481f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b715d17cded5d419fa964a28a4b76699
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 79d1fdd4ce1484de097dea9143b4fbcd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3d8a7f699d3a7d3adb40d82dd6a34d8a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f267dcb971ae787e4b976f06f15932d6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 53d0a0328db2d93dcb8cf83888e7c867
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5981a4bb0de2fdf09829d56d28201537
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 48e490574c906bc62bef9ec512ec56e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3bb15b25ddfc4628b9eb6a2984c4460c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "RK.MicroComponents",
|
||||
"references": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7b0e8cf4be6933ca4aafdceea282e3e3
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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": {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 394ef4b1864ef4275a283945727b5bde
|
||||
PackageManifestImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Reference in New Issue