39 Commits

Author SHA1 Message Date
7c62440bba chore: added an experimental ordered fast list class 2025-10-14 12:06:47 +03:00
4bec7bce6e fix: fast list readonly mode not throwing exceptions 2025-10-14 11:42:05 +03:00
8d31372c24 refactor: universe and objects now use fast list 2025-10-13 12:40:43 +03:00
a2e704916e feat: fast list now implements IList 2025-10-13 12:39:49 +03:00
c7d170fad9 perf: significant performance optimizations on ordered behaviour collectors by using a sorted dictionary 2025-10-13 09:58:58 +03:00
9ccf7b754d perf: ordered behaviour collectors now use linked lists for performance 2025-10-11 16:07:26 +03:00
e3d4899112 refactor: renamed behaviour collectors from sorted to ordered 2025-10-11 16:05:47 +03:00
566c16d09c refactor: active behaviour collector base added 2025-10-11 15:36:58 +03:00
ae9d4f02ef chore: moved behaviour collectors into subdirectory 2025-10-11 15:36:06 +03:00
e77772cbc2 refactor: behaviour collector base added 2025-10-11 15:08:02 +03:00
4c542df401 perf: implemented fast list with index mapping 2025-10-10 14:58:40 +03:00
28ca343b43 perf: improved pool return method by using a hashset for searching if the returning item is already queued 2025-10-10 14:21:54 +03:00
651b0614c4 fix: index check on triangle batch flush 2025-10-10 11:43:04 +03:00
f47488c6f1 fix: registering/unregistering objects during universe enter/exit causing stack overflows 2025-10-10 10:59:39 +03:00
6d159330a1 refactor: moved client and server interfaces into their files 2025-08-31 23:09:02 +03:00
8e314f3269 feat: networking type hasher added 2025-08-19 21:17:47 +03:00
f5a7077570 perf: improved garbage created by tweens slightly
They still do generate a lot of garbage but with boxed value pools I made the boxes reusable, it still does generate garbage through the delegate creation, gotta find a solution for them later
2025-08-14 20:31:46 +03:00
746d29fb7a refactor: shortened IButtonInputs event declaration 2025-08-10 14:42:47 +03:00
cf68f6ca6f fix: first frame updates not calling first, they are now set to be a high priority 2025-08-09 22:29:44 +03:00
a4b83679b1 chore: added todo for a rare bug 2025-08-09 21:41:24 +03:00
a31b39fd1d fix: universe finalize not working properly 2025-08-09 21:36:28 +03:00
0205354202 fix: universe entrance manager using the wrong reference on universe exit 2025-08-09 21:09:36 +03:00
949dfeb3d9 fix: universe reverse for loop index doesn't start with count - 1 2025-08-09 21:03:45 +03:00
620ef911fa fix: parameter name typo 2025-08-09 21:01:25 +03:00
efed24de20 feat: rotating file logger added 2025-08-08 16:28:22 +03:00
3912706d27 chore: force .log extension to log files 2025-08-08 16:27:57 +03:00
d78c42a653 feat: update manager now calls last frames listeners on process exit as well 2025-08-05 20:57:03 +03:00
b04e0f81cd fix: triangle batch not drawing shapes because not setting rasterizer state properly 2025-08-05 20:43:54 +03:00
65dcb0c564 BREAKING CHANGE: moved yaml serialization from Engine.Serialization to Engine.Integration 2025-08-05 20:10:30 +03:00
3d183b21cd BREAKING CHANGE: renamed namespace & assembly names 2025-08-05 19:41:35 +03:00
1644a751bb feat: added LiteNetLib networking integration 2025-08-05 19:27:47 +03:00
6631cae7b0 feat: added networking system 2025-08-05 19:27:27 +03:00
3452194941 BREAKING CHANGE: removed IUniverseObject.SetParent and made Parent property settable 2025-08-05 10:46:25 +03:00
11612ff0db feat: removed IEnumerable from IUniverseObject for intellisense clarity
Use IUniverseObject.Children to access children
2025-08-05 09:11:46 +03:00
63bc94c7a6 fix: some factories not assigning fields correctly 2025-08-04 22:02:48 +03:00
e00319d7ff fix: active checks on behaviour base and universe object not working properly 2025-08-04 22:01:16 +03:00
11719440dc fix: behaviour controller extensions not null checking in case of uninitialized state 2025-08-04 20:26:45 +03:00
f246d68aa7 fix: remove behaviour not starting the reverse for loop from count - 1 2025-08-04 14:56:43 +03:00
6e87c67096 fix: wrong assert messages are corrected 2025-08-04 14:45:00 +03:00
275 changed files with 2768 additions and 829 deletions

4
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "Engine.Serializers/YamlDotNet"] [submodule "Engine.Integration/YamlDotNet"]
path = Engine.Serializers/YamlDotNet path = Engine.Integration/YamlDotNet
url = git@github.com:Syntriax/YamlDotNet.git url = git@github.com:Syntriax/YamlDotNet.git

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Indicates the class implementing it has Assignable fields that are necessary for the engine to work properly. /// Indicates the class implementing it has Assignable fields that are necessary for the engine to work properly.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IBehaviourController"/> field. /// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IBehaviourController"/> field.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IEntity"/> field. /// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IEntity"/> field.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IStateEnable"/> field. /// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IStateEnable"/> field.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IUniverse"/> field. /// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IUniverse"/> field.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IUniverseObject"/> field. /// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IUniverseObject"/> field.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents an entity which can be active or not. /// Represents an entity which can be active or not.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents a behaviour that any object in the engine that might use to interact with itself or other objects. /// Represents a behaviour that any object in the engine that might use to interact with itself or other objects.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
public interface IBehaviour2D : IBehaviour public interface IBehaviour2D : IBehaviour
{ {

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents a collector for the class type of <typeparamref name="T"/>. /// Represents a collector for the class type of <typeparamref name="T"/>.

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents a controller for managing <see cref="IBehaviour"/>s. Connected to an <see cref="IUniverseObject"/>. /// Represents a controller for managing <see cref="IBehaviour"/>s. Connected to an <see cref="IUniverseObject"/>.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents a 2D camera in the engine. /// Represents a 2D camera in the engine.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
public interface ICoroutineYield public interface ICoroutineYield
{ {

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents a basic entity in the engine. /// Represents a basic entity in the engine.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents an entity that can be initialized and finalized. This information is useful for objects we know that are not in use and can be either recycled or dropped for garbage collection. /// Represents an entity that can be initialized and finalized. This information is useful for objects we know that are not in use and can be either recycled or dropped for garbage collection.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents an entity with a name. /// Represents an entity with a name.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents an entity with an enable state that can be toggled. /// Represents an entity with an enable state that can be toggled.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents the transformation properties of an object such as position, scale, and rotation in 2D space. /// Represents the transformation properties of an object such as position, scale, and rotation in 2D space.

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents a universe responsible for managing <see cref="IUniverseObject"/>s. /// Represents a universe responsible for managing <see cref="IUniverseObject"/>s.

View File

@@ -1,13 +1,13 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents an <see cref="IEntity"/> that can enter and exit a universe within the <see cref="IUniverse"/> system. /// Represents an <see cref="IEntity"/> that can enter and exit a universe within the <see cref="IUniverse"/> system.
/// This interface allows for tracking the object's presence in the universe and provides events /// This interface allows for tracking the object's presence in the universe and provides events
/// for notifying when the see enters or exits the universe. /// for notifying when the see enters or exits the universe.
/// </summary> /// </summary>
public interface IUniverseObject : IEntity, IActive, INameable, IHasBehaviourController, IEnumerable<IUniverseObject> public interface IUniverseObject : IEntity, IActive, INameable, IHasBehaviourController
{ {
/// <summary> /// <summary>
/// Event triggered when the <see cref="IUniverseObject"/> enters the universe. /// Event triggered when the <see cref="IUniverseObject"/> enters the universe.
@@ -47,7 +47,7 @@ public interface IUniverseObject : IEntity, IActive, INameable, IHasBehaviourCon
/// <summary> /// <summary>
/// The parent <see cref="IUniverseObject"/> of the <see cref="IUniverseObject"/>. /// The parent <see cref="IUniverseObject"/> of the <see cref="IUniverseObject"/>.
/// </summary> /// </summary>
IUniverseObject? Parent { get; } IUniverseObject? Parent { get; set; }
/// <summary> /// <summary>
/// The <see cref="IUniverseObject"/>s that have this <see cref="IUniverseObject"/> as their <see cref="Parent"/>. /// The <see cref="IUniverseObject"/>s that have this <see cref="IUniverseObject"/> as their <see cref="Parent"/>.
@@ -75,12 +75,6 @@ public interface IUniverseObject : IEntity, IActive, INameable, IHasBehaviourCon
/// </returns> /// </returns>
internal bool ExitUniverse(); internal bool ExitUniverse();
/// <summary>
/// Sets the parent <see cref="IUniverseObject"/> of this <see cref="IUniverseObject"/>.
/// </summary>
/// <param name="universeObject">The parent <see cref="IUniverseObject"/> to set.</param>
void SetParent(IUniverseObject? universeObject);
/// <summary> /// <summary>
/// Adds a child <see cref="IUniverseObject"/> to this <see cref="IUniverseObject"/>. /// Adds a child <see cref="IUniverseObject"/> to this <see cref="IUniverseObject"/>.
/// </summary> /// </summary>

View File

@@ -1,67 +0,0 @@
using System;
using System.Collections.Generic;
namespace Syntriax.Engine.Core;
public class ActiveBehaviourCollectorSorted<T> : ActiveBehaviourCollector<T> where T : class, IBehaviour
{
private readonly Event<IBehaviour, IBehaviour.PriorityChangedArguments>.EventHandler delegateOnPriorityChanged = null!;
private IComparer<T>? _sortBy = null;
public IComparer<T>? SortBy
{
get => _sortBy;
set
{
_sortBy = value;
if (value is not null)
activeBehaviours.Sort(value);
}
}
protected override void AddBehaviour(T behaviour)
{
if (SortBy is null)
{
activeBehaviours.Add(behaviour);
return;
}
int insertionIndex = activeBehaviours.BinarySearch(behaviour, SortBy);
if (insertionIndex < 0)
insertionIndex = ~insertionIndex;
activeBehaviours.Insert(insertionIndex, behaviour);
}
protected override void OnBehaviourAdd(IBehaviour behaviour)
{
behaviour.OnPriorityChanged.AddListener(delegateOnPriorityChanged);
}
protected override void OnBehaviourRemove(IBehaviour behaviour)
{
behaviour.OnPriorityChanged.RemoveListener(delegateOnPriorityChanged);
}
private void OnPriorityChanged(IBehaviour sender, IBehaviour.PriorityChangedArguments args)
{
T behaviour = (T)sender;
activeBehaviours.Remove(behaviour);
AddBehaviour(behaviour);
}
public ActiveBehaviourCollectorSorted()
{
delegateOnPriorityChanged = OnPriorityChanged;
}
public ActiveBehaviourCollectorSorted(IUniverse universe, Comparison<T> sortBy) : base(universe)
{
delegateOnPriorityChanged = OnPriorityChanged;
SortBy = Comparer<T>.Create(sortBy);
}
}

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Syntriax.Engine.Core; namespace Engine.Core;
public abstract class BaseEntity : IEntity public abstract class BaseEntity : IEntity
{ {

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
public abstract class Behaviour : BehaviourBase public abstract class Behaviour : BehaviourBase
{ {

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
public abstract class Behaviour2D : Behaviour, IBehaviour2D public abstract class Behaviour2D : Behaviour, IBehaviour2D
{ {

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
[System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")] [System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")]
public abstract class BehaviourBase : BaseEntity, IBehaviour public abstract class BehaviourBase : BaseEntity, IBehaviour
@@ -44,6 +44,7 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
_behaviourController = behaviourController; _behaviourController = behaviourController;
OnAssign(behaviourController); OnAssign(behaviourController);
behaviourController.OnUniverseObjectAssigned.AddListener(delegateOnUniverseObjectAssigned); behaviourController.OnUniverseObjectAssigned.AddListener(delegateOnUniverseObjectAssigned);
behaviourController.StateEnable.OnEnabledChanged.AddListener(delegateOnStateEnabledChanged);
if (behaviourController.UniverseObject is not null) if (behaviourController.UniverseObject is not null)
OnUniverseObjectAssigned(behaviourController); OnUniverseObjectAssigned(behaviourController);
OnBehaviourControllerAssigned?.Invoke(this); OnBehaviourControllerAssigned?.Invoke(this);
@@ -68,6 +69,7 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
BehaviourController.UniverseObject.OnActiveChanged.RemoveListener(delegateOnUniverseObjectActiveChanged); BehaviourController.UniverseObject.OnActiveChanged.RemoveListener(delegateOnUniverseObjectActiveChanged);
StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged); StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged);
BehaviourController.OnUniverseObjectAssigned.RemoveListener(delegateOnUniverseObjectAssigned); BehaviourController.OnUniverseObjectAssigned.RemoveListener(delegateOnUniverseObjectAssigned);
BehaviourController.StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged);
base.UnassignInternal(); base.UnassignInternal();
_behaviourController = null!; _behaviourController = null!;
} }
@@ -76,6 +78,8 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
{ {
Debug.Assert.AssertBehaviourControllerAssigned(this); Debug.Assert.AssertBehaviourControllerAssigned(this);
Debug.Assert.AssertStateEnableAssigned(this); Debug.Assert.AssertStateEnableAssigned(this);
UpdateActive();
} }
private void OnStateEnabledChanged(IStateEnable sender, IStateEnable.EnabledChangedArguments args) => UpdateActive(); private void OnStateEnabledChanged(IStateEnable sender, IStateEnable.EnabledChangedArguments args) => UpdateActive();
@@ -84,7 +88,7 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
private void UpdateActive() private void UpdateActive()
{ {
bool previousActive = IsActive; bool previousActive = IsActive;
_isActive = StateEnable.Enabled && _behaviourController.UniverseObject.IsActive; _isActive = StateEnable.Enabled && _behaviourController.StateEnable.Enabled && _behaviourController.UniverseObject.IsActive;
if (previousActive != IsActive) if (previousActive != IsActive)
OnActiveChanged?.Invoke(this, new(previousActive)); OnActiveChanged?.Invoke(this, new(previousActive));

View File

@@ -1,67 +0,0 @@
using System;
using System.Collections.Generic;
namespace Syntriax.Engine.Core;
public class BehaviourCollectorSorted<T> : BehaviourCollector<T> where T : class
{
private readonly Event<IBehaviour, IBehaviour.PriorityChangedArguments>.EventHandler delegateOnPriorityChanged = null!;
private IComparer<T>? _sortBy = null;
public IComparer<T>? SortBy
{
get => _sortBy;
set
{
_sortBy = value;
if (value is not null)
behaviours.Sort(value);
}
}
protected override void AddBehaviour(T behaviour)
{
if (SortBy is null)
{
behaviours.Add(behaviour);
return;
}
int insertionIndex = behaviours.BinarySearch(behaviour, SortBy);
if (insertionIndex < 0)
insertionIndex = ~insertionIndex;
behaviours.Insert(insertionIndex, behaviour);
}
protected override void OnBehaviourAdd(IBehaviour behaviour)
{
behaviour.OnPriorityChanged.AddListener(delegateOnPriorityChanged);
}
protected override void OnBehaviourRemove(IBehaviour behaviour)
{
behaviour.OnPriorityChanged.RemoveListener(delegateOnPriorityChanged);
}
private void OnPriorityChanged(IBehaviour sender, IBehaviour.PriorityChangedArguments args)
{
T behaviour = (T)sender;
behaviours.Remove(behaviour);
AddBehaviour(behaviour);
}
public BehaviourCollectorSorted()
{
delegateOnPriorityChanged = OnPriorityChanged;
}
public BehaviourCollectorSorted(IUniverse universe, Comparison<T> sortBy) : base(universe)
{
delegateOnPriorityChanged = OnPriorityChanged;
SortBy = Comparer<T>.Create(sortBy);
}
}

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Syntriax.Engine.Core; namespace Engine.Core;
[System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")] [System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")]
public class BehaviourController : BaseEntity, IBehaviourController public class BehaviourController : BaseEntity, IBehaviourController
@@ -10,7 +10,7 @@ public class BehaviourController : BaseEntity, IBehaviourController
public Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments> OnBehaviourRemoved { get; } = new(); public Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments> OnBehaviourRemoved { get; } = new();
public Event<IHasUniverseObject> OnUniverseObjectAssigned { get; } = new(); public Event<IHasUniverseObject> OnUniverseObjectAssigned { get; } = new();
private readonly List<IBehaviour> behaviours = new(Constants.BEHAVIOURS_SIZE_INITIAL); private readonly FastList<IBehaviour> behaviours = new(Constants.BEHAVIOURS_SIZE_INITIAL);
private IUniverseObject _universeObject = null!; private IUniverseObject _universeObject = null!;
@@ -72,7 +72,7 @@ public class BehaviourController : BaseEntity, IBehaviourController
public void RemoveBehaviour<T>(bool removeAll = false) where T : class, IBehaviour public void RemoveBehaviour<T>(bool removeAll = false) where T : class, IBehaviour
{ {
for (int i = behaviours.Count; i >= 0; i--) for (int i = behaviours.Count - 1; i >= 0; i--)
{ {
if (behaviours[i] is not T behaviour) if (behaviours[i] is not T behaviour)
continue; continue;

View File

@@ -0,0 +1,16 @@
using System;
namespace Engine.Core;
public class ActiveBehaviourCollector<T> : ActiveBehaviourCollectorBase<T> where T : class, IBehaviour
{
protected readonly FastList<T> activeBehaviours = new(32);
public override T this[Index index] => activeBehaviours[index];
public override int Count => activeBehaviours.Count;
public ActiveBehaviourCollector() { }
public ActiveBehaviourCollector(IUniverse universe) : base(universe) { }
protected override void AddBehaviour(T behaviour) => activeBehaviours.Add(behaviour);
protected override bool RemoveBehaviour(T tBehaviour) => activeBehaviours.Remove(tBehaviour);
}

View File

@@ -1,14 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Syntriax.Engine.Core; namespace Engine.Core;
public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : class, IBehaviour public abstract class ActiveBehaviourCollectorBase<T> : IBehaviourCollector<T> where T : class, IBehaviour
{ {
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourCollectedArguments> OnCollected { get; } = new(); protected readonly Dictionary<IActive, T> monitoringActiveToBehaviour = new(32);
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourRemovedArguments> OnRemoved { get; } = new(); protected readonly FastList<T> monitoringBehaviours = new(32);
public Event<IHasUniverse> OnUniverseAssigned { get; } = new();
public Event<IAssignable>? OnUnassigned { get; } = new();
private readonly Event<IBehaviourController, IBehaviourController.BehaviourAddedArguments>.EventHandler delegateOnBehaviourAdded = null!; private readonly Event<IBehaviourController, IBehaviourController.BehaviourAddedArguments>.EventHandler delegateOnBehaviourAdded = null!;
private readonly Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments>.EventHandler delegateOnBehaviourRemoved = null!; private readonly Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments>.EventHandler delegateOnBehaviourRemoved = null!;
@@ -16,80 +14,16 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
private readonly Event<IUniverse, IUniverse.UniverseObjectRegisteredArguments>.EventHandler delegateOnUniverseObjectRegistered = null!; private readonly Event<IUniverse, IUniverse.UniverseObjectRegisteredArguments>.EventHandler delegateOnUniverseObjectRegistered = null!;
private readonly Event<IUniverse, IUniverse.UniverseObjectUnRegisteredArguments>.EventHandler delegateOnUniverseObjectUnregistered = null!; private readonly Event<IUniverse, IUniverse.UniverseObjectUnRegisteredArguments>.EventHandler delegateOnUniverseObjectUnregistered = null!;
private readonly List<T> monitoringBehaviours = new(32); public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourCollectedArguments> OnCollected { get; } = new();
protected readonly List<T> activeBehaviours = new(32); public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourRemovedArguments> OnRemoved { get; } = new();
protected readonly Dictionary<IActive, T> monitoringActiveToBehaviour = new(32); public Event<IHasUniverse> OnUniverseAssigned { get; } = new();
public Event<IAssignable>? OnUnassigned { get; } = new();
public abstract int Count { get; }
public abstract T this[Index index] { get; }
public IUniverse Universe { get; private set; } = null!; public IUniverse Universe { get; private set; } = null!;
private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args)
{
IUniverseObject universeObject = args.UniverseObjectRegistered;
universeObject.BehaviourController.OnBehaviourAdded.AddListener(delegateOnBehaviourAdded);
universeObject.BehaviourController.OnBehaviourRemoved.AddListener(delegateOnBehaviourRemoved);
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
OnBehaviourAdded(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
}
private void OnUniverseObjectUnregistered(IUniverse manager, IUniverse.UniverseObjectUnRegisteredArguments args)
{
IUniverseObject universeObject = args.UniverseObjectUnregistered;
universeObject.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded);
universeObject.BehaviourController.OnBehaviourRemoved.RemoveListener(delegateOnBehaviourRemoved);
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
OnBehaviourRemoved(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
}
protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args)
{
if (args.BehaviourAdded is not T tBehaviour)
return;
monitoringBehaviours.Add(tBehaviour);
monitoringActiveToBehaviour.Add(tBehaviour, tBehaviour);
tBehaviour.OnActiveChanged.AddListener(delegateOnBehaviourStateChanged);
OnBehaviourStateChanged(tBehaviour, new(!tBehaviour.IsActive));
}
protected virtual void AddBehaviour(T behaviour) => activeBehaviours.Add(behaviour);
private void OnBehaviourStateChanged(IActive sender, IActive.ActiveChangedArguments args)
{
T behaviour = monitoringActiveToBehaviour[sender];
if (sender.IsActive)
{
AddBehaviour(behaviour);
OnBehaviourAdd(behaviour);
OnCollected?.Invoke(this, new(behaviour));
}
else if (activeBehaviours.Remove(behaviour))
{
OnBehaviourRemove(behaviour);
OnRemoved?.Invoke(this, new(behaviour));
}
}
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args)
{
if (args.BehaviourRemoved is not T tBehaviour)
return;
if (!monitoringBehaviours.Remove(tBehaviour) || !monitoringActiveToBehaviour.Remove(tBehaviour))
return;
tBehaviour.OnActiveChanged.RemoveListener(delegateOnBehaviourStateChanged);
if (activeBehaviours.Remove(tBehaviour))
{
OnBehaviourRemove(tBehaviour);
OnRemoved?.Invoke(this, new(tBehaviour));
}
}
public bool Assign(IUniverse universe) public bool Assign(IUniverse universe)
{ {
if (Universe is not null) if (Universe is not null)
@@ -123,10 +57,75 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
return true; return true;
} }
public int Count => activeBehaviours.Count; protected abstract void AddBehaviour(T behaviour);
public T this[Index index] => activeBehaviours[index]; protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args)
{
if (args.BehaviourAdded is not T tBehaviour)
return;
public ActiveBehaviourCollector() monitoringBehaviours.Add(tBehaviour);
monitoringActiveToBehaviour.Add(tBehaviour, tBehaviour);
tBehaviour.OnActiveChanged.AddListener(delegateOnBehaviourStateChanged);
OnBehaviourStateChanged(tBehaviour, new(!tBehaviour.IsActive));
}
protected abstract bool RemoveBehaviour(T behaviour);
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args)
{
if (args.BehaviourRemoved is not T tBehaviour)
return;
if (!monitoringBehaviours.Remove(tBehaviour) || !monitoringActiveToBehaviour.Remove(tBehaviour))
return;
tBehaviour.OnActiveChanged.RemoveListener(delegateOnBehaviourStateChanged);
if (!RemoveBehaviour(tBehaviour))
return;
OnBehaviourRemove(tBehaviour);
OnRemoved?.Invoke(this, new(tBehaviour));
}
private void OnBehaviourStateChanged(IActive sender, IActive.ActiveChangedArguments args)
{
T behaviour = monitoringActiveToBehaviour[sender];
if (sender.IsActive)
{
AddBehaviour(behaviour);
OnBehaviourAdd(behaviour);
OnCollected?.Invoke(this, new(behaviour));
}
else if (RemoveBehaviour(behaviour))
{
OnBehaviourRemove(behaviour);
OnRemoved?.Invoke(this, new(behaviour));
}
}
private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args)
{
IUniverseObject universeObject = args.UniverseObjectRegistered;
universeObject.BehaviourController.OnBehaviourAdded.AddListener(delegateOnBehaviourAdded);
universeObject.BehaviourController.OnBehaviourRemoved.AddListener(delegateOnBehaviourRemoved);
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
OnBehaviourAdded(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
}
private void OnUniverseObjectUnregistered(IUniverse manager, IUniverse.UniverseObjectUnRegisteredArguments args)
{
IUniverseObject universeObject = args.UniverseObjectUnregistered;
universeObject.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded);
universeObject.BehaviourController.OnBehaviourRemoved.RemoveListener(delegateOnBehaviourRemoved);
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
OnBehaviourRemoved(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
}
public ActiveBehaviourCollectorBase()
{ {
delegateOnBehaviourAdded = OnBehaviourAdded; delegateOnBehaviourAdded = OnBehaviourAdded;
delegateOnBehaviourRemoved = OnBehaviourRemoved; delegateOnBehaviourRemoved = OnBehaviourRemoved;
@@ -135,7 +134,7 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
delegateOnUniverseObjectUnregistered = OnUniverseObjectUnregistered; delegateOnUniverseObjectUnregistered = OnUniverseObjectUnregistered;
} }
public ActiveBehaviourCollector(IUniverse universe) public ActiveBehaviourCollectorBase(IUniverse universe)
{ {
delegateOnBehaviourAdded = OnBehaviourAdded; delegateOnBehaviourAdded = OnBehaviourAdded;
delegateOnBehaviourRemoved = OnBehaviourRemoved; delegateOnBehaviourRemoved = OnBehaviourRemoved;

View File

@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
namespace Engine.Core;
public class ActiveBehaviourCollectorOrdered<TIndex, TItem> : ActiveBehaviourCollector<TItem> where TItem : class, IBehaviour where TIndex : IComparable
{
private readonly Event<IBehaviour, IBehaviour.PriorityChangedArguments>.EventHandler delegateOnPriorityChanged = null!;
private readonly SortedDictionary<TIndex, FastList<TItem>> behaviours = null!;
private readonly Func<TItem, TIndex> getIndexFunc = null!;
private readonly IComparer<TIndex> sortBy = null!;
private int count = 0;
public override int Count => count;
public override TItem this[Index index]
{
get
{
int actualIndex = index.IsFromEnd
? count - index.Value
: index.Value;
if (actualIndex < 0 || actualIndex >= count)
throw new IndexOutOfRangeException();
int leftIndex = actualIndex;
foreach ((TIndex i, FastList<TItem> list) in behaviours)
{
if (leftIndex < list.Count)
return list[leftIndex];
leftIndex -= list.Count;
}
throw new IndexOutOfRangeException();
}
}
protected override bool RemoveBehaviour(TItem tBehaviour)
{
TIndex index = getIndexFunc(tBehaviour);
if (!behaviours.TryGetValue(index, out FastList<TItem>? list))
throw new Exceptions.NotFoundException($"Index of '{index}' is not found in the collector");
if (!list.Remove(tBehaviour))
return false;
count--;
return true;
}
protected override void AddBehaviour(TItem behaviour)
{
TIndex key = getIndexFunc(behaviour);
if (!behaviours.TryGetValue(key, out FastList<TItem>? list))
behaviours[key] = list = [];
count++;
list.Add(behaviour);
}
protected override void OnBehaviourAdd(IBehaviour behaviour) => behaviour.OnPriorityChanged.AddListener(delegateOnPriorityChanged);
protected override void OnBehaviourRemove(IBehaviour behaviour) => behaviour.OnPriorityChanged.RemoveListener(delegateOnPriorityChanged);
private void OnPriorityChanged(IBehaviour sender, IBehaviour.PriorityChangedArguments args)
{
TItem behaviour = (TItem)sender;
RemoveBehaviour(behaviour);
AddBehaviour(behaviour);
}
public ActiveBehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy)
{
delegateOnPriorityChanged = OnPriorityChanged;
this.getIndexFunc = getIndexFunc;
this.sortBy = Comparer<TIndex>.Create(sortBy);
behaviours = new(this.sortBy);
}
public ActiveBehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy) : base(universe)
{
delegateOnPriorityChanged = OnPriorityChanged;
this.getIndexFunc = getIndexFunc;
this.sortBy = Comparer<TIndex>.Create(sortBy);
behaviours = new(this.sortBy);
}
public ActiveBehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy)
{
this.getIndexFunc = getIndexFunc;
delegateOnPriorityChanged = OnPriorityChanged;
this.sortBy = sortBy;
behaviours = new(sortBy);
}
public ActiveBehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy) : base(universe)
{
delegateOnPriorityChanged = OnPriorityChanged;
this.getIndexFunc = getIndexFunc;
this.sortBy = sortBy;
behaviours = new(sortBy);
}
}

View File

@@ -0,0 +1,17 @@
using System;
namespace Engine.Core;
public class BehaviourCollector<T> : BehaviourCollectorBase<T> where T : class
{
protected readonly FastList<T> behaviours = new(32);
public override T this[Index index] => behaviours[index];
public override int Count => behaviours.Count;
protected override void AddBehaviour(T behaviour) => behaviours.Add(behaviour);
protected override bool RemoveBehaviour(T tBehaviour) => behaviours.Remove(tBehaviour);
public BehaviourCollector() { }
public BehaviourCollector(IUniverse universe) : base(universe) { }
}

View File

@@ -1,72 +1,25 @@
using System; using System;
using System.Collections.Generic;
namespace Syntriax.Engine.Core; namespace Engine.Core;
public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class public abstract class BehaviourCollectorBase<T> : IBehaviourCollector<T> where T : class
{ {
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourCollectedArguments> OnCollected { get; } = new();
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourRemovedArguments> OnRemoved { get; } = new();
public Event<IHasUniverse> OnUniverseAssigned { get; } = new();
public Event<IAssignable>? OnUnassigned { get; } = new();
private readonly Event<IBehaviourController, IBehaviourController.BehaviourAddedArguments>.EventHandler delegateOnBehaviourAdded = null!; private readonly Event<IBehaviourController, IBehaviourController.BehaviourAddedArguments>.EventHandler delegateOnBehaviourAdded = null!;
private readonly Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments>.EventHandler delegateOnBehaviourRemoved = null!; private readonly Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments>.EventHandler delegateOnBehaviourRemoved = null!;
private readonly Event<IUniverse, IUniverse.UniverseObjectRegisteredArguments>.EventHandler delegateOnUniverseObjectRegistered = null!; private readonly Event<IUniverse, IUniverse.UniverseObjectRegisteredArguments>.EventHandler delegateOnUniverseObjectRegistered = null!;
private readonly Event<IUniverse, IUniverse.UniverseObjectUnRegisteredArguments>.EventHandler delegateOnUniverseObjectUnregistered = null!; private readonly Event<IUniverse, IUniverse.UniverseObjectUnRegisteredArguments>.EventHandler delegateOnUniverseObjectUnregistered = null!;
protected readonly List<T> behaviours = new(32); public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourCollectedArguments> OnCollected { get; } = new();
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourRemovedArguments> OnRemoved { get; } = new();
public Event<IHasUniverse> OnUniverseAssigned { get; } = new();
public Event<IAssignable>? OnUnassigned { get; } = new();
public IUniverse Universe { get; private set; } = null!; public IUniverse Universe { get; private set; } = null!;
private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args) public abstract int Count { get; }
{
IUniverseObject universeObject = args.UniverseObjectRegistered;
universeObject.BehaviourController.OnBehaviourAdded.AddListener(delegateOnBehaviourAdded); public abstract T this[Index index] { get; }
universeObject.BehaviourController.OnBehaviourRemoved.AddListener(delegateOnBehaviourRemoved);
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
OnBehaviourAdded(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
}
private void OnUniverseObjectUnregistered(IUniverse manager, IUniverse.UniverseObjectUnRegisteredArguments args)
{
IUniverseObject universeObject = args.UniverseObjectUnregistered;
universeObject.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded);
universeObject.BehaviourController.OnBehaviourRemoved.RemoveListener(delegateOnBehaviourRemoved);
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
OnBehaviourRemoved(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
}
protected virtual void AddBehaviour(T behaviour) => behaviours.Add(behaviour);
protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args)
{
if (args.BehaviourAdded is not T tBehaviour)
return;
AddBehaviour(tBehaviour);
OnBehaviourAdd(args.BehaviourAdded);
OnCollected?.Invoke(this, new(tBehaviour));
}
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args)
{
if (args.BehaviourRemoved is not T tBehaviour)
return;
if (!behaviours.Remove(tBehaviour))
return;
OnBehaviourRemove(args.BehaviourRemoved);
OnRemoved?.Invoke(this, new(tBehaviour));
}
protected virtual void OnAssign(IUniverse universe) { }
public bool Assign(IUniverse universe) public bool Assign(IUniverse universe)
{ {
if (Universe is not null) if (Universe is not null)
@@ -101,10 +54,57 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
return true; return true;
} }
public int Count => behaviours.Count; protected virtual void OnAssign(IUniverse universe) { }
public T this[Index index] => behaviours[index];
public BehaviourCollector() protected abstract void AddBehaviour(T behaviour);
protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args)
{
if (args.BehaviourAdded is not T tBehaviour)
return;
AddBehaviour(tBehaviour);
OnBehaviourAdd(args.BehaviourAdded);
OnCollected?.Invoke(this, new(tBehaviour));
}
protected abstract bool RemoveBehaviour(T tBehaviour);
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args)
{
if (args.BehaviourRemoved is not T tBehaviour)
return;
if (!RemoveBehaviour(tBehaviour))
return;
OnBehaviourRemove(args.BehaviourRemoved);
OnRemoved?.Invoke(this, new(tBehaviour));
}
private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args)
{
IUniverseObject universeObject = args.UniverseObjectRegistered;
universeObject.BehaviourController.OnBehaviourAdded.AddListener(delegateOnBehaviourAdded);
universeObject.BehaviourController.OnBehaviourRemoved.AddListener(delegateOnBehaviourRemoved);
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
OnBehaviourAdded(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
}
private void OnUniverseObjectUnregistered(IUniverse manager, IUniverse.UniverseObjectUnRegisteredArguments args)
{
IUniverseObject universeObject = args.UniverseObjectUnregistered;
universeObject.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded);
universeObject.BehaviourController.OnBehaviourRemoved.RemoveListener(delegateOnBehaviourRemoved);
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
OnBehaviourRemoved(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
}
public BehaviourCollectorBase()
{ {
delegateOnBehaviourAdded = OnBehaviourAdded; delegateOnBehaviourAdded = OnBehaviourAdded;
delegateOnBehaviourRemoved = OnBehaviourRemoved; delegateOnBehaviourRemoved = OnBehaviourRemoved;
@@ -112,7 +112,7 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
delegateOnUniverseObjectUnregistered = OnUniverseObjectUnregistered; delegateOnUniverseObjectUnregistered = OnUniverseObjectUnregistered;
} }
public BehaviourCollector(IUniverse universe) public BehaviourCollectorBase(IUniverse universe)
{ {
delegateOnBehaviourAdded = OnBehaviourAdded; delegateOnBehaviourAdded = OnBehaviourAdded;
delegateOnBehaviourRemoved = OnBehaviourRemoved; delegateOnBehaviourRemoved = OnBehaviourRemoved;

View File

@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
namespace Engine.Core;
public class BehaviourCollectorOrdered<TIndex, TItem> : BehaviourCollectorBase<TItem> where TItem : class where TIndex : IComparable
{
private readonly Event<IBehaviour, IBehaviour.PriorityChangedArguments>.EventHandler delegateOnPriorityChanged = null!;
private readonly SortedDictionary<TIndex, FastList<TItem>> behaviours = null!;
private readonly Func<TItem, TIndex> getIndexFunc = null!;
private readonly IComparer<TIndex> sortBy = null!;
private int count = 0;
public override int Count => count;
public override TItem this[Index index]
{
get
{
int actualIndex = index.IsFromEnd
? count - index.Value
: index.Value;
if (actualIndex < 0 || actualIndex >= count)
throw new IndexOutOfRangeException();
int leftIndex = actualIndex;
foreach ((TIndex i, FastList<TItem> list) in behaviours)
{
if (leftIndex < list.Count)
return list[leftIndex];
leftIndex -= list.Count;
}
throw new IndexOutOfRangeException();
}
}
protected override bool RemoveBehaviour(TItem tBehaviour)
{
TIndex index = getIndexFunc(tBehaviour);
if (!behaviours.TryGetValue(index, out FastList<TItem>? list))
throw new Exceptions.NotFoundException($"Index of '{index}' is not found in the collector");
if (!list.Remove(tBehaviour))
return false;
count--;
return true;
}
protected override void AddBehaviour(TItem behaviour)
{
TIndex key = getIndexFunc(behaviour);
if (!behaviours.TryGetValue(key, out FastList<TItem>? list))
behaviours[key] = list = [];
count++;
list.Add(behaviour);
}
protected override void OnBehaviourAdd(IBehaviour behaviour) => behaviour.OnPriorityChanged.AddListener(delegateOnPriorityChanged);
protected override void OnBehaviourRemove(IBehaviour behaviour) => behaviour.OnPriorityChanged.RemoveListener(delegateOnPriorityChanged);
private void OnPriorityChanged(IBehaviour sender, IBehaviour.PriorityChangedArguments args)
{
TItem behaviour = (TItem)sender;
RemoveBehaviour(behaviour);
AddBehaviour(behaviour);
}
public BehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy)
{
delegateOnPriorityChanged = OnPriorityChanged;
this.getIndexFunc = getIndexFunc;
this.sortBy = Comparer<TIndex>.Create(sortBy);
behaviours = new(this.sortBy);
}
public BehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy) : base(universe)
{
delegateOnPriorityChanged = OnPriorityChanged;
this.getIndexFunc = getIndexFunc;
this.sortBy = Comparer<TIndex>.Create(sortBy);
behaviours = new(this.sortBy);
}
public BehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy)
{
this.getIndexFunc = getIndexFunc;
delegateOnPriorityChanged = OnPriorityChanged;
this.sortBy = sortBy;
behaviours = new(sortBy);
}
public BehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy) : base(universe)
{
delegateOnPriorityChanged = OnPriorityChanged;
this.getIndexFunc = getIndexFunc;
this.sortBy = sortBy;
behaviours = new(sortBy);
}
}

View File

@@ -1,7 +1,7 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
namespace Syntriax.Engine.Core; namespace Engine.Core;
public class CoroutineManager : Behaviour, IUpdate public class CoroutineManager : Behaviour, IUpdate
{ {

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Syntriax.Engine.Core; namespace Engine.Core;
public class CoroutineYield(Func<bool> condition) : ICoroutineYield public class CoroutineYield(Func<bool> condition) : ICoroutineYield
{ {

View File

@@ -1,6 +1,6 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Syntriax.Engine.Core.Debug; namespace Engine.Core.Debug;
public static class Assert public static class Assert
{ {
@@ -10,21 +10,21 @@ public static class Assert
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertBehaviourControllerAssigned(IHasBehaviourController assignable) public static void AssertBehaviourControllerAssigned(IHasBehaviourController assignable)
=> System.Diagnostics.Debug.Assert(assignable.BehaviourController is not null, $"{assignable.GetType().Name} must be initialized"); => System.Diagnostics.Debug.Assert(assignable.BehaviourController is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IBehaviourController)}");
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertEntityAssigned(IHasEntity assignable) public static void AssertEntityAssigned(IHasEntity assignable)
=> System.Diagnostics.Debug.Assert(assignable.Entity is not null, $"{assignable.GetType().Name} must be initialized"); => System.Diagnostics.Debug.Assert(assignable.Entity is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IEntity)}");
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertUniverseAssigned(IHasUniverse assignable) public static void AssertUniverseAssigned(IHasUniverse assignable)
=> System.Diagnostics.Debug.Assert(assignable.Universe is not null, $"{assignable.GetType().Name} must be initialized"); => System.Diagnostics.Debug.Assert(assignable.Universe is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IUniverse)}");
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertUniverseObjectAssigned(IHasUniverseObject assignable) public static void AssertUniverseObjectAssigned(IHasUniverseObject assignable)
=> System.Diagnostics.Debug.Assert(assignable.UniverseObject is not null, $"{assignable.GetType().Name} must be initialized"); => System.Diagnostics.Debug.Assert(assignable.UniverseObject is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IUniverseObject)}");
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertStateEnableAssigned(IHasStateEnable assignable) public static void AssertStateEnableAssigned(IHasStateEnable assignable)
=> System.Diagnostics.Debug.Assert(assignable.StateEnable is not null, $"{assignable.GetType().Name} must be initialized"); => System.Diagnostics.Debug.Assert(assignable.StateEnable is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IStateEnable)}");
} }

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Syntriax.Engine.Core.Debug; namespace Engine.Core.Debug;
public class ConsoleLogger : LoggerBase public class ConsoleLogger : LoggerBase
{ {

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.IO; using System.IO;
namespace Syntriax.Engine.Core.Debug; namespace Engine.Core.Debug;
public class FileLogger : LoggerBase public class FileLogger : LoggerBase
{ {
@@ -14,6 +14,9 @@ public class FileLogger : LoggerBase
public FileLogger(string filePath) public FileLogger(string filePath)
{ {
if (!filePath.EndsWith(".log"))
filePath += ".log";
FilePath = filePath; FilePath = filePath;
bool isRelativePath = Path.GetFullPath(filePath).CompareTo(filePath) != 0; bool isRelativePath = Path.GetFullPath(filePath).CompareTo(filePath) != 0;

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Debug; namespace Engine.Core.Debug;
public interface ILogger public interface ILogger
{ {

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Syntriax.Engine.Core.Debug; namespace Engine.Core.Debug;
public abstract class LoggerBase : ILogger public abstract class LoggerBase : ILogger
{ {

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Debug; namespace Engine.Core.Debug;
public class LoggerContainer : Behaviour, ILogger public class LoggerContainer : Behaviour, ILogger
{ {

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
namespace Syntriax.Engine.Core.Debug; namespace Engine.Core.Debug;
public static class LoggerExtensions public static class LoggerExtensions
{ {

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Debug; namespace Engine.Core.Debug;
public class LoggerWrapper(ILogger firstLogger, ILogger secondLogger) : ILogger public class LoggerWrapper(ILogger firstLogger, ILogger secondLogger) : ILogger
{ {

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Debug; namespace Engine.Core.Debug;
public static class LoggerWrapperExtensions public static class LoggerWrapperExtensions
{ {

View File

@@ -0,0 +1,71 @@
using System;
using System.IO;
using System.Linq;
namespace Engine.Core.Debug;
public class RotatingFileLogger : ILogger
{
public readonly FileLogger FileLogger = null!;
public readonly string Directory = string.Empty;
public readonly int RotateLength = 3;
public RotatingFileLogger(string directory, string namePrefix, string nameSuffix = "", int rotateLength = 3)
{
RotateLength = rotateLength;
string fileName = Path.Combine(directory, namePrefix);
if (!string.IsNullOrWhiteSpace(nameSuffix))
fileName += $"_{nameSuffix}";
bool isRelativePath = Path.GetFullPath(fileName).CompareTo(fileName) != 0;
if (isRelativePath)
fileName = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName));
if (File.Exists($"{fileName}.log"))
RenameExistingLogs(fileName, RotateLength);
FileLogger = new(fileName);
Directory = Path.GetDirectoryName(fileName) ?? throw new("Unexpected error on getting directory of logger path");
RotateLastLogs(Directory, namePrefix, RotateLength);
}
private static void RenameExistingLogs(string filePath, int rotateLength)
{
for (int i = rotateLength - 1; i >= 0; i--)
{
string source = i == 0
? $"{filePath}.log"
: $"{filePath}_{i}.log";
string dest = $"{filePath}_{i + 1}.log";
if (!File.Exists(source))
continue;
if (File.Exists(dest))
File.Delete(dest);
File.Move(source, dest);
}
}
private static void RotateLastLogs(string directory, string prefix, int rotateLength)
{
IOrderedEnumerable<string> logs = System.IO.Directory.GetFiles(directory, $"{prefix}*.log")
.OrderBy(File.GetCreationTime);
foreach (string file in logs.Skip(rotateLength))
try
{
ILogger.Shared.Log($"Removing log file located at \"{file}\" during rotation.");
File.Delete(file);
}
catch (Exception e) { ILogger.Shared.LogException($"Failed to rotate log file at \"{file}\"", e); }
}
public ILogger.Level FilterLevel { get => FileLogger.FilterLevel; set => FileLogger.FilterLevel = value; }
public void Log(string message, ILogger.Level level = ILogger.Level.Info, bool force = false) => FileLogger.Log(message, level, force);
}

View File

@@ -4,7 +4,8 @@
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>false</ImplicitUsings> <ImplicitUsings>false</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<RootNamespace>Syntriax.Engine.Core</RootNamespace> <RootNamespace>Engine.Core</RootNamespace>
<AssemblyName>Engine.Core</AssemblyName>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Syntriax.Engine.Core.Exceptions; namespace Engine.Core.Exceptions;
public class AssignFailedException(string? message) : Exception(message) public class AssignFailedException(string? message) : Exception(message)
{ {

View File

@@ -1,3 +1,3 @@
namespace Syntriax.Engine.Core.Exceptions; namespace Engine.Core.Exceptions;
public class BehaviourNotFoundException(string? message) : NotFoundException(message); public class BehaviourNotFoundException(string? message) : NotFoundException(message);

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Syntriax.Engine.Core.Exceptions; namespace Engine.Core.Exceptions;
public class NotAssignedException(string? message) : Exception(message) public class NotAssignedException(string? message) : Exception(message)
{ {

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Syntriax.Engine.Core.Exceptions; namespace Engine.Core.Exceptions;
public class NotFoundException(string? message) : Exception(message) public class NotFoundException(string? message) : Exception(message)
{ {

View File

@@ -1,3 +1,3 @@
namespace Syntriax.Engine.Core.Exceptions; namespace Engine.Core.Exceptions;
public class UniverseObjectNotFoundException(string? message) : NotFoundException(message); public class UniverseObjectNotFoundException(string? message) : NotFoundException(message);

View File

@@ -1,9 +1,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Syntriax.Engine.Core.Exceptions; using Engine.Core.Exceptions;
namespace Syntriax.Engine.Core; namespace Engine.Core;
public static class BehaviourControllerExtensions public static class BehaviourControllerExtensions
{ {
@@ -27,7 +27,7 @@ public static class BehaviourControllerExtensions
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param> /// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns> /// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
public static T GetRequiredBehaviour<T>(this IBehaviourController behaviourController) where T : class public static T GetRequiredBehaviour<T>(this IBehaviourController behaviourController) where T : class
=> behaviourController.GetBehaviour<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName}"); => behaviourController.GetBehaviour<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject?.Name ?? "NULL"}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName}");
/// <summary> /// <summary>
/// Gets an existing <see cref="IBehaviour"/> of the specified type, or adds and returns a new one if it doesn't exist. /// Gets an existing <see cref="IBehaviour"/> of the specified type, or adds and returns a new one if it doesn't exist.
@@ -93,7 +93,7 @@ public static class BehaviourControllerExtensions
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param> /// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns> /// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
public static T GetRequiredBehaviourInParent<T>(this IBehaviourController behaviourController) where T : class public static T GetRequiredBehaviourInParent<T>(this IBehaviourController behaviourController) where T : class
=> behaviourController.GetBehaviourInParent<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any parent"); => behaviourController.GetBehaviourInParent<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject?.Name ?? "NULL"}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any parent");
/// <summary> /// <summary>
/// Gets all <see cref="IBehaviour"/>s of the specified type in it's <see cref="IUniverseObject"/>'s parents recursively and stores them in the provided list. /// Gets all <see cref="IBehaviour"/>s of the specified type in it's <see cref="IUniverseObject"/>'s parents recursively and stores them in the provided list.
@@ -140,7 +140,7 @@ public static class BehaviourControllerExtensions
if (behaviourController.GetBehaviour<T>() is T localBehaviour) if (behaviourController.GetBehaviour<T>() is T localBehaviour)
return localBehaviour; return localBehaviour;
foreach (IUniverseObject child in behaviourController.UniverseObject) foreach (IUniverseObject child in behaviourController.UniverseObject.Children)
if (GetBehaviourInChildren<T>(child.BehaviourController) is T behaviour) if (GetBehaviourInChildren<T>(child.BehaviourController) is T behaviour)
return behaviour; return behaviour;
@@ -154,7 +154,7 @@ public static class BehaviourControllerExtensions
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param> /// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns> /// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
public static T GetRequiredBehaviourInChildren<T>(this IBehaviourController behaviourController) where T : class public static T GetRequiredBehaviourInChildren<T>(this IBehaviourController behaviourController) where T : class
=> behaviourController.GetBehaviourInChildren<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any children "); => behaviourController.GetBehaviourInChildren<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject?.Name ?? "NULL"}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any children ");
/// <summary> /// <summary>
/// Gets all <see cref="IBehaviour"/>s of the specified type in it's <see cref="IUniverseObject"/>'s children recursively and stores them in the provided list. /// Gets all <see cref="IBehaviour"/>s of the specified type in it's <see cref="IUniverseObject"/>'s children recursively and stores them in the provided list.
@@ -176,7 +176,7 @@ public static class BehaviourControllerExtensions
foreach (T behaviour in cache) foreach (T behaviour in cache)
behaviours.Add(behaviour); behaviours.Add(behaviour);
foreach (IUniverseObject child in universeObject) foreach (IUniverseObject child in universeObject.Children)
TraverseChildrenForBehaviour(child, behaviours, cache); TraverseChildrenForBehaviour(child, behaviours, cache);
} }
} }

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Syntriax.Engine.Core; namespace Engine.Core;
public static class EnumExtensions public static class EnumExtensions
{ {

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
public static class FloatExtensions public static class FloatExtensions
{ {

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
public static class TransformExtensions public static class TransformExtensions
{ {

View File

@@ -1,6 +1,6 @@
using Syntriax.Engine.Core.Exceptions; using Engine.Core.Exceptions;
namespace Syntriax.Engine.Core; namespace Engine.Core;
public static class UniverseExtensions public static class UniverseExtensions
{ {

View File

@@ -1,9 +1,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Syntriax.Engine.Core.Exceptions; using Engine.Core.Exceptions;
namespace Syntriax.Engine.Core; namespace Engine.Core;
public static class UniverseObjectExtensions public static class UniverseObjectExtensions
{ {
@@ -12,7 +12,7 @@ public static class UniverseObjectExtensions
if (!string.IsNullOrWhiteSpace(name)) if (!string.IsNullOrWhiteSpace(name))
universeObject.Name = name; universeObject.Name = name;
if (parent is not null) if (parent is not null)
universeObject.SetParent(parent); universeObject.Parent = parent;
return universeObject; return universeObject;
} }
@@ -81,7 +81,7 @@ public static class UniverseObjectExtensions
/// <returns>The <see cref="IUniverseObject"/> of the specified type if found; otherwise, null.</returns> /// <returns>The <see cref="IUniverseObject"/> of the specified type if found; otherwise, null.</returns>
public static T? GetUniverseObjectInParent<T>(this IUniverseObject universeObject) where T : class public static T? GetUniverseObjectInParent<T>(this IUniverseObject universeObject) where T : class
{ {
if (universeObject.GetUniverseObject<T>() is T localUniverseObject) if (universeObject.Children.GetUniverseObject<T>() is T localUniverseObject)
return localUniverseObject; return localUniverseObject;
IUniverseObject? parent = universeObject; IUniverseObject? parent = universeObject;
@@ -129,10 +129,10 @@ public static class UniverseObjectExtensions
/// <returns>The <see cref="IUniverseObject"/> of the specified type if found; otherwise, null.</returns> /// <returns>The <see cref="IUniverseObject"/> of the specified type if found; otherwise, null.</returns>
public static T? GetUniverseObjectInChildren<T>(this IUniverseObject universeObject) where T : class public static T? GetUniverseObjectInChildren<T>(this IUniverseObject universeObject) where T : class
{ {
if (universeObject.GetUniverseObject<T>() is T localUniverseObject) if (universeObject.Children.GetUniverseObject<T>() is T localUniverseObject)
return localUniverseObject; return localUniverseObject;
foreach (IUniverseObject child in universeObject) foreach (IUniverseObject child in universeObject.Children)
if (GetUniverseObjectInChildren<T>(child) is T behaviour) if (GetUniverseObjectInChildren<T>(child) is T behaviour)
return behaviour; return behaviour;
@@ -246,7 +246,7 @@ public static class UniverseObjectExtensions
foreach (IUniverseObject universeObject in universeObjects) foreach (IUniverseObject universeObject in universeObjects)
{ {
universeObject.Find(cache); universeObject.Children.Find(cache);
foreach (T behaviour in cache) foreach (T behaviour in cache)
instances.Add(behaviour); instances.Add(behaviour);
} }

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Factory.Abstract; namespace Engine.Core.Factory.Abstract;
public interface IFactory<TInterface> where TInterface : class public interface IFactory<TInterface> where TInterface : class
{ {

View File

@@ -1,13 +1,13 @@
using Syntriax.Engine.Core.Exceptions; using Engine.Core.Exceptions;
namespace Syntriax.Engine.Core.Factory; namespace Engine.Core.Factory;
public class BehaviourControllerFactory public class BehaviourControllerFactory
{ {
public static IBehaviourController Instantiate(IUniverseObject universeObject) public static IBehaviourController Instantiate(IUniverseObject universeObject, IStateEnable? stateEnable = null)
=> Instantiate<BehaviourController>(universeObject); => Instantiate<BehaviourController>(universeObject, stateEnable);
public static T Instantiate<T>(IUniverseObject universeObject, params object?[]? args) public static T Instantiate<T>(IUniverseObject universeObject, IStateEnable? stateEnable = null, params object?[]? args)
where T : class, IBehaviourController where T : class, IBehaviourController
{ {
T behaviourController = TypeFactory.Get<T>(args); T behaviourController = TypeFactory.Get<T>(args);
@@ -18,6 +18,17 @@ public class BehaviourControllerFactory
if (!behaviourController.Assign(universeObject)) if (!behaviourController.Assign(universeObject))
throw AssignFailedException.From(behaviourController, universeObject); throw AssignFailedException.From(behaviourController, universeObject);
if (stateEnable is not null)
{
if (!stateEnable.Assign(behaviourController))
throw AssignFailedException.From(stateEnable, behaviourController);
if (!behaviourController.Assign(stateEnable))
throw AssignFailedException.From(behaviourController, stateEnable);
}
else
StateEnableFactory.Instantiate(behaviourController);
return behaviourController; return behaviourController;
} }
} }

View File

@@ -1,6 +1,6 @@
using Syntriax.Engine.Core.Exceptions; using Engine.Core.Exceptions;
namespace Syntriax.Engine.Core.Factory; namespace Engine.Core.Factory;
public class BehaviourFactory public class BehaviourFactory
{ {
@@ -12,12 +12,15 @@ public class BehaviourFactory
{ {
T behaviour = TypeFactory.Get<T>(args); T behaviour = TypeFactory.Get<T>(args);
stateEnable ??= TypeFactory.Get<StateEnable>(); if (stateEnable is not null)
{
if (!stateEnable.Assign(behaviour)) if (!stateEnable.Assign(behaviour))
throw AssignFailedException.From(stateEnable, behaviour); throw AssignFailedException.From(stateEnable, behaviour);
if (!behaviour.Assign(stateEnable)) if (!behaviour.Assign(stateEnable))
throw AssignFailedException.From(behaviour, stateEnable); throw AssignFailedException.From(behaviour, stateEnable);
}
else
StateEnableFactory.Instantiate(behaviour);
return behaviour; return behaviour;
} }

View File

@@ -1,7 +1,7 @@
using System; using System;
using Syntriax.Engine.Core.Factory.Abstract; using Engine.Core.Factory.Abstract;
namespace Syntriax.Engine.Core.Factory; namespace Engine.Core.Factory;
public abstract class FactoryBase<TInterface> : IFactory<TInterface> public abstract class FactoryBase<TInterface> : IFactory<TInterface>
where TInterface : class where TInterface : class

View File

@@ -1,6 +1,6 @@
using Syntriax.Engine.Core.Exceptions; using Engine.Core.Exceptions;
namespace Syntriax.Engine.Core.Factory; namespace Engine.Core.Factory;
public class StateEnableFactory public class StateEnableFactory
{ {

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Factory; namespace Engine.Core.Factory;
public class TransformFactory public class TransformFactory
{ {

View File

@@ -3,7 +3,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Syntriax.Engine.Core.Factory; namespace Engine.Core.Factory;
public static class TypeFactory public static class TypeFactory
{ {

View File

@@ -1,6 +1,6 @@
using Syntriax.Engine.Core.Exceptions; using Engine.Core.Exceptions;
namespace Syntriax.Engine.Core.Factory; namespace Engine.Core.Factory;
public class UniverseObjectFactory public class UniverseObjectFactory
{ {
@@ -18,18 +18,25 @@ public class UniverseObjectFactory
{ {
T universeObject = TypeFactory.Get<T>(args); T universeObject = TypeFactory.Get<T>(args);
behaviourController ??= TypeFactory.Get<BehaviourController>(); if (behaviourController is not null)
stateEnable ??= TypeFactory.Get<StateEnable>(); {
if (!behaviourController.Assign(universeObject)) if (!behaviourController.Assign(universeObject))
throw AssignFailedException.From(behaviourController, universeObject); throw AssignFailedException.From(behaviourController, universeObject);
if (!stateEnable.Assign(universeObject))
throw AssignFailedException.From(stateEnable, universeObject);
if (!universeObject.Assign(behaviourController)) if (!universeObject.Assign(behaviourController))
throw AssignFailedException.From(universeObject, behaviourController); throw AssignFailedException.From(universeObject, behaviourController);
}
else
BehaviourControllerFactory.Instantiate(universeObject);
if (stateEnable is not null)
{
if (!stateEnable.Assign(universeObject))
throw AssignFailedException.From(stateEnable, universeObject);
if (!universeObject.Assign(stateEnable)) if (!universeObject.Assign(stateEnable))
throw AssignFailedException.From(universeObject, stateEnable); throw AssignFailedException.From(universeObject, stateEnable);
}
else
StateEnableFactory.Instantiate(universeObject);
return universeObject; return universeObject;
} }

View File

@@ -1,9 +1,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Syntriax.Engine.Core.Debug; using Engine.Core.Debug;
namespace Syntriax.Engine.Core; namespace Engine.Core;
// TODO!: every reverse loop has a chance to have more than 1 unsubscription,
// for (int i = listeners.Count - 1; i >= 0; i--)
// can be replaced with
// for (int i = listeners.Count - 1; i >= 0; i = Math.Min(i - 1, listeners.Count - 1))
// but this would causes possible double calls on already called callbacks, find a better method.
/// <summary> /// <summary>
/// Represents a simple event with no parameters. /// Represents a simple event with no parameters.

View File

@@ -0,0 +1,102 @@
using System.Collections;
using System.Collections.Generic;
namespace Engine.Core;
public class FastList<T> : IList<T>, IReadOnlyList<T>, IEnumerable<T> where T : notnull
{
private readonly List<T> items = [];
private readonly Dictionary<T, int> indexMap = [];
public bool IsReadOnly { get; set; } = false;
public int Count => items.Count;
public T this[int index]
{
get => items[index];
set
{
if (IsReadOnly)
throw new System.Data.ReadOnlyException();
items[index] = value;
}
}
public void Add(T item)
{
if (IsReadOnly)
throw new System.Data.ReadOnlyException();
indexMap[item] = items.Count;
items.Add(item);
}
public void RemoveAt(int i) => Remove(items[i], i);
public bool Remove(T item)
{
if (IsReadOnly)
throw new System.Data.ReadOnlyException();
if (!indexMap.TryGetValue(item, out int index))
return false;
Remove(item, index);
return true;
}
private void Remove(T item, int index)
{
int lastIndex = items.Count - 1;
T lastItem = items[lastIndex];
items[index] = lastItem;
indexMap[lastItem] = index;
items.RemoveAt(lastIndex);
indexMap.Remove(item);
}
public void Insert(int index, T item)
{
if (IsReadOnly)
throw new System.Data.ReadOnlyException();
items.Insert(index, item);
for (int i = index; i < items.Count; i++)
indexMap[items[i]] = i;
}
public void Clear()
{
if (IsReadOnly)
throw new System.Data.ReadOnlyException();
items.Clear();
indexMap.Clear();
}
public bool Contains(T item) => indexMap.ContainsKey(item);
public int IndexOf(T item) => items.IndexOf(item);
public int BinarySearch(T item, IComparer<T>? comparer = null) => items.BinarySearch(item, comparer);
public void Sort(IComparer<T> comparer)
{
if (IsReadOnly)
throw new System.Data.ReadOnlyException();
items.Sort(comparer);
for (int i = 0; i < items.Count; i++)
indexMap[items[i]] = i;
}
public void CopyTo(T[] array, int arrayIndex) => items.CopyTo(array, arrayIndex);
public IEnumerator<T> GetEnumerator() => items.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public FastList() { }
public FastList(int count) { items.Capacity = count; }
}

View File

@@ -0,0 +1,172 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Engine.Core;
/// <summary>
/// TODO This is VEERY experimental, and doesn't work well with the indices access. Use with caution
/// </summary>
/// <typeparam name="TIndex"></typeparam>
/// <typeparam name="TItem"></typeparam>
public class FastListOrdered<TIndex, TItem> : IList<TItem>, IReadOnlyList<TItem>, IEnumerable<TItem> where TItem : notnull where TIndex : IComparable
{
private readonly SortedDictionary<TIndex, FastList<TItem>> items = null!;
private readonly Func<TItem, TIndex> getIndexFunc = null!;
private readonly IComparer<TIndex> sortBy = null!;
private int count = 0;
public int Count => count;
public bool IsReadOnly { get; set; } = false;
public TItem this[int index]
{
get { (TIndex tIndex, int i) = GetAt(index); return items[tIndex][i]; }
set
{
if (IsReadOnly)
throw new System.Data.ReadOnlyException();
(TIndex tIndex, int i) = GetAt(index); items[tIndex][i] = value;
}
}
private (TIndex TIndex, int i) GetAt(Index index)
{
int actualIndex = index.IsFromEnd
? count - index.Value
: index.Value;
if (actualIndex < 0 || actualIndex >= count)
throw new IndexOutOfRangeException();
int leftIndex = actualIndex;
foreach ((TIndex i, FastList<TItem> list) in items)
{
if (leftIndex < list.Count)
return (i, leftIndex);
leftIndex -= list.Count;
}
throw new IndexOutOfRangeException();
}
public int IndexOf(TItem item)
{
int indexCounter = 0;
foreach ((TIndex index, FastList<TItem> list) in items)
{
int i = list.IndexOf(item);
if (i != -1)
return indexCounter + i;
indexCounter += list.Count;
}
return -1;
}
public void Add(TItem item)
{
if (IsReadOnly)
throw new System.Data.ReadOnlyException();
TIndex key = getIndexFunc(item);
if (!items.TryGetValue(key, out FastList<TItem>? list))
items[key] = list = [];
list.Add(item);
count++;
}
public void Insert(int index, TItem item)
{
if (IsReadOnly)
throw new System.Data.ReadOnlyException();
TIndex tIndex = getIndexFunc(item);
if (!items.TryGetValue(tIndex, out FastList<TItem>? list))
items[tIndex] = list = [];
list.Insert(index, item);
count++;
}
public bool Remove(TItem item)
{
if (IsReadOnly)
throw new System.Data.ReadOnlyException();
TIndex index = getIndexFunc(item);
if (!items.TryGetValue(index, out FastList<TItem>? list))
throw new Exceptions.NotFoundException($"Index of '{index}' is not found in the collector");
if (!list.Remove(item))
return false;
count--;
return true;
}
public void RemoveAt(int index)
{
if (IsReadOnly)
throw new System.Data.ReadOnlyException();
(TIndex tIndex, int i) = GetAt(index);
items[tIndex].RemoveAt(i);
count--;
}
public void Clear()
{
if (IsReadOnly)
throw new System.Data.ReadOnlyException();
foreach ((TIndex index, FastList<TItem> list) in items)
list.Clear();
count = 0;
}
public bool Contains(TItem item)
{
foreach ((TIndex index, FastList<TItem> list) in items)
if (list.Contains(item))
return true;
return false;
}
public void CopyTo(TItem[] array, int arrayIndex)
{
int indexCounter = 0;
foreach ((TIndex index, FastList<TItem> list) in items)
{
list.CopyTo(array, indexCounter);
indexCounter += list.Count;
}
}
public IEnumerator<TItem> GetEnumerator()
{
foreach ((TIndex index, FastList<TItem> list) in items)
foreach (TItem item in list)
yield return item;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public FastListOrdered(Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy)
{
this.getIndexFunc = getIndexFunc;
this.sortBy = Comparer<TIndex>.Create(sortBy);
items = new(this.sortBy);
}
public FastListOrdered(Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy)
{
this.getIndexFunc = getIndexFunc;
this.sortBy = sortBy;
items = new(sortBy);
}
}

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
public interface IPool<T> public interface IPool<T>
{ {

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Syntriax.Engine.Core; namespace Engine.Core;
public class ListPool<T> : IPool<List<T>> public class ListPool<T> : IPool<List<T>>
{ {

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Syntriax.Engine.Core; namespace Engine.Core;
public class Pool<T> : IPool<T> public class Pool<T> : IPool<T>
{ {
@@ -10,22 +10,25 @@ public class Pool<T> : IPool<T>
private readonly Func<T> generator = null!; private readonly Func<T> generator = null!;
private readonly Queue<T> queue = new(); private readonly Queue<T> queue = new();
private readonly HashSet<T> queuedHashes = [];
public T Get() public T Get()
{ {
if (!queue.TryDequeue(out T? result)) if (!queue.TryDequeue(out T? result))
result = generator(); result = generator();
queuedHashes.Remove(result);
OnRemoved?.Invoke(this, result); OnRemoved?.Invoke(this, result);
return result; return result;
} }
public void Return(T item) public void Return(T item)
{ {
if (queue.Contains(item)) if (queuedHashes.Contains(item))
return; return;
queue.Enqueue(item); queue.Enqueue(item);
queuedHashes.Add(item);
OnReturned?.Invoke(this, item); OnReturned?.Invoke(this, item);
} }

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
public interface IProgressionTracker : IReadOnlyProgressionTracker public interface IProgressionTracker : IReadOnlyProgressionTracker
{ {

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
public interface IReadOnlyProgressionTracker public interface IReadOnlyProgressionTracker
{ {

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
public class ProgressionTracker : IProgressionTracker public class ProgressionTracker : IProgressionTracker
{ {

View File

@@ -1,6 +1,6 @@
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Syntriax.Engine.Core; namespace Engine.Core;
public record struct ProgressiveTask<T>(IReadOnlyProgressionTracker ProgressionTracker, Task<T> Task) public record struct ProgressiveTask<T>(IReadOnlyProgressionTracker ProgressionTracker, Task<T> Task)
{ {

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Numerics; using System.Numerics;
namespace Syntriax.Engine.Core; namespace Engine.Core;
public static class Math public static class Math
{ {

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Numerics; using System.Numerics;
namespace Syntriax.Engine.Core; namespace Engine.Core;
public static class MathExtensions public static class MathExtensions
{ {

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core namespace Engine.Core
{ {
// This is pretty much so the assembly gets loaded automatically because // This is pretty much so the assembly gets loaded automatically because
// the builds include the assembly but sometimes doesn't link load it at startup. // the builds include the assembly but sometimes doesn't link load it at startup.

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents an Axis-Aligned Bounding Box (AABB) in 2D space. /// Represents an Axis-Aligned Bounding Box (AABB) in 2D space.

View File

@@ -1,6 +1,6 @@
using System.Diagnostics; using System.Diagnostics;
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents a 2D circle. /// Represents a 2D circle.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents an HSV color. /// Represents an HSV color.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents an HSV color. /// Represents an HSV color.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents an RGB color. /// Represents an RGB color.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents an RGBA color. /// Represents an RGBA color.

View File

@@ -1,6 +1,6 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents a 2D line segment defined by two endpoints. /// Represents a 2D line segment defined by two endpoints.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents a <see cref="Line2DEquation"/> in the form y = mx + b. /// Represents a <see cref="Line2DEquation"/> in the form y = mx + b.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents a range of values along a single axis. /// Represents a range of values along a single axis.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents a 3D space rotation. /// Represents a 3D space rotation.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents an infinite ray in 2D space. /// Represents an infinite ray in 2D space.

View File

@@ -1,7 +1,7 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents a shape defined by a collection of vertices. /// Represents a shape defined by a collection of vertices.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
[System.Diagnostics.DebuggerDisplay("A: {A.ToString(), nq}, B: {B.ToString(), nq}, B: {C.ToString(), nq}")] [System.Diagnostics.DebuggerDisplay("A: {A.ToString(), nq}, B: {B.ToString(), nq}, B: {C.ToString(), nq}")]
public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C) public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents a two-dimensional vector. /// Represents a two-dimensional vector.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core; namespace Engine.Core;
/// <summary> /// <summary>
/// Represents a three-dimensional vector. /// Represents a three-dimensional vector.

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Syntriax.Engine.Core.Serialization; namespace Engine.Core.Serialization;
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Class)] [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Class)]
public class IgnoreSerializationAttribute : Attribute; public class IgnoreSerializationAttribute : Attribute;

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Syntriax.Engine.Core.Serialization; namespace Engine.Core.Serialization;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class SerializeAllAttribute : Attribute; public class SerializeAllAttribute : Attribute;

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Syntriax.Engine.Core.Serialization; namespace Engine.Core.Serialization;
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class SerializeAttribute : Attribute; public class SerializeAttribute : Attribute;

View File

@@ -1,3 +1,3 @@
namespace Syntriax.Engine.Core.Serialization; namespace Engine.Core.Serialization;
public record class EntityReference(string? Id = null); public record class EntityReference(string? Id = null);

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Syntriax.Engine.Core.Serialization; namespace Engine.Core.Serialization;
public class EntityRegistry public class EntityRegistry
{ {

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Syntriax.Engine.Core.Serialization; namespace Engine.Core.Serialization;
public interface ISerializer public interface ISerializer
{ {

Some files were not shown because too many files have changed in this diff Show More