From 1841bbb35953e5a5e02138017317d1a413d5cb34 Mon Sep 17 00:00:00 2001 From: Jean THOMAS Date: Thu, 1 Aug 2024 17:39:19 +0200 Subject: [PATCH 01/16] Review --- .../Features/Core/Components/TickComponent.cs | 2 +- Exiled.API/Features/Core/ConstProperty.cs | 3 +- Exiled.API/Features/Core/GameEntity.cs | 5 -- .../Features/Core/Generic/EBehaviour.cs | 2 + Exiled.API/Features/Core/Generic/Singleton.cs | 17 +++-- .../Core/Generic/UniqueUnmanagedEnumClass.cs | 9 ++- .../Core/Generic/UnmanagedEnumClass.cs | 4 ++ Exiled.API/Features/Player.cs | 1 + Exiled.API/Features/Respawn.cs | 7 +- .../VirtualAssemblies/VirtualPlugin.cs | 2 +- .../Features/CustomEscapes/EscapeSettings.cs | 2 +- .../CustomGamemodes/GameModeSettings.cs | 2 +- .../API/Features/CustomRoles/CustomRole.cs | 3 +- .../API/Features/CustomRoles/CustomTeam.cs | 11 ++-- .../API/Features/CustomRoles/RoleBehaviour.cs | 66 +++++++++++-------- .../API/Features/RespawnManager.cs | 9 ++- 16 files changed, 89 insertions(+), 56 deletions(-) diff --git a/Exiled.API/Features/Core/Components/TickComponent.cs b/Exiled.API/Features/Core/Components/TickComponent.cs index 7e65d93a3b..1aec75ac52 100644 --- a/Exiled.API/Features/Core/Components/TickComponent.cs +++ b/Exiled.API/Features/Core/Components/TickComponent.cs @@ -24,7 +24,7 @@ public sealed class TickComponent : EObject /// The default fixed tick rate (60 per second). /// #pragma warning disable SA1310 - public const float DEFAULT_FIXED_TICK_RATE = 0.016f; + public const float DEFAULT_FIXED_TICK_RATE = 1f / 60f; #pragma warning restore SA1310 private readonly HashSet boundHandles; diff --git a/Exiled.API/Features/Core/ConstProperty.cs b/Exiled.API/Features/Core/ConstProperty.cs index 49dc60b7c5..f6c48f6317 100644 --- a/Exiled.API/Features/Core/ConstProperty.cs +++ b/Exiled.API/Features/Core/ConstProperty.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------- +// ----------------------------------------------------------------------- // // Copyright (c) Exiled Team. All rights reserved. // Licensed under the CC BY-SA 3.0 license. @@ -48,6 +48,7 @@ public ConstProperty(T constantValue, Type[] typesToPatch, MethodInfo[] skipMeth /// ~ConstProperty() { + // @Nao Never be call, it need to remove it reference from List beffor the dtor call List.Remove(this); foreach (MethodInfo methodInfo in PatchedMethods) diff --git a/Exiled.API/Features/Core/GameEntity.cs b/Exiled.API/Features/Core/GameEntity.cs index aa84602d36..5aa3184012 100644 --- a/Exiled.API/Features/Core/GameEntity.cs +++ b/Exiled.API/Features/Core/GameEntity.cs @@ -44,11 +44,6 @@ protected GameEntity(GameObject gameObject) // List.Add(this); } - /// - /// Finalizes an instance of the class. - /// - ~GameEntity() => List.Remove(this); - /// /// Gets all active instances. /// diff --git a/Exiled.API/Features/Core/Generic/EBehaviour.cs b/Exiled.API/Features/Core/Generic/EBehaviour.cs index 778f61b773..d9fb9a8c2b 100644 --- a/Exiled.API/Features/Core/Generic/EBehaviour.cs +++ b/Exiled.API/Features/Core/Generic/EBehaviour.cs @@ -60,6 +60,8 @@ protected EBehaviour() /// protected virtual void FindOwner() { + // @Nao T is a GameEntity, why not add in inisde of it the method a abstract. + // Or create an specific Interaface requesting to implement this method MethodInfo method = typeof(T).GetMethod("Get", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(GameObject) }, null); if (method != null) diff --git a/Exiled.API/Features/Core/Generic/Singleton.cs b/Exiled.API/Features/Core/Generic/Singleton.cs index a212b6f874..f0128aca4c 100644 --- a/Exiled.API/Features/Core/Generic/Singleton.cs +++ b/Exiled.API/Features/Core/Generic/Singleton.cs @@ -19,6 +19,9 @@ namespace Exiled.API.Features.Core.Generic public sealed class Singleton : TypeCastObject where T : class { + // @Nao, this is not realy a singleton if you have mutliple value. + // NB: each Singleton ave a new instance of dictionary. + // exemple Singleton.Instance is not the same Singleton.Instance private static readonly Dictionary> Instances = new(); /// @@ -35,6 +38,8 @@ public Singleton(T value) /// /// Finalizes an instance of the class. /// + // @Nao Probably never call, it self reference. Maybe an future issue doing memory leek + // it will get call only after the instance get remove form Instances ~Singleton() => Instances.Remove(Value); /// @@ -59,6 +64,10 @@ public Singleton(T value) /// The type of the object. /// The object instance. /// if the object instance is not null and can be casted as the specified type; otherwise, . + // @nao It will return the Instance. + // But with this curent class definition you can register derived class of T. + // They can be registred but not retrived. Also why make this method generic if this not to get + // other value than the instance. public static bool TryGet(out TObject instance) where TObject : class => (instance = Instance as TObject) is not null; @@ -72,13 +81,7 @@ public static bool TryGet(out TObject instance) /// if the instance was destroyed; otherwise, . public static bool Destroy(T @object) { - if (Instances.TryGetValue(@object, out Singleton _)) - { - Instances[@object] = null; - return Instances.Remove(@object); - } - - return false; + return Instances.Remove(@object); } } } \ No newline at end of file diff --git a/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs b/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs index 67f0b8331a..7547829299 100644 --- a/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs +++ b/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs @@ -40,6 +40,10 @@ public UniqueUnmanagedEnumClass() values ??= new(); TypeCode code = Convert.GetTypeCode(typeof(TSource).GetField("MinValue").GetValue(null)); + // @Nao If the value is not an Uxxxxx it will get an overflow. Like if it use an Int16, Byte or SByte + // Maybe use a long for nextValue and do "nextValue = typeof(TSource).GetField("MinValue").GetValue(null)" + // it also be a struct containing only unmanged struct. The best solution is proably to + // look for upper version of the framwork to resolve this with the new numeric interfaces. if (code is TypeCode.UInt16 or TypeCode.UInt32 or TypeCode.UInt64) nextValue = 0; @@ -53,6 +57,9 @@ public UniqueUnmanagedEnumClass() while (values.ContainsKey(value)); Value = value; + + // @nao If the value is register when a new instance is created. + // Maybe put the ctor in protected to avoid exteranal code (outisde the enum) to create new values values.Add(value, (TObject)this); } } @@ -285,7 +292,7 @@ public int CompareTo(object obj) => /// public int Compare(TObject x, TObject y) => x == null ? -1 : y == null ? 1 : x.Value.CompareTo(y.Value); -/// + /// TypeCode IConvertible.GetTypeCode() => Value.GetTypeCode(); /// diff --git a/Exiled.API/Features/Core/Generic/UnmanagedEnumClass.cs b/Exiled.API/Features/Core/Generic/UnmanagedEnumClass.cs index 1f01dc4328..d1b203a89b 100644 --- a/Exiled.API/Features/Core/Generic/UnmanagedEnumClass.cs +++ b/Exiled.API/Features/Core/Generic/UnmanagedEnumClass.cs @@ -22,6 +22,7 @@ namespace Exiled.API.Features.Core.Generic /// /// The type of the source object to handle the instance of. /// The type of the child object to handle the instance of. + // @Nao you can use Comparer.Default, it will result as using the IComparable.Compare public abstract class UnmanagedEnumClass : IComparable, IEquatable, IComparable, IComparer, IConvertible, IEnumClass where TSource : unmanaged, IComparable, IFormattable, IConvertible, IComparable, IEquatable where TObject : UnmanagedEnumClass @@ -253,6 +254,7 @@ public override bool Equals(object obj) => /// Zero This instance occurs in the same position in the sort order as other. /// Greater than zero This instance follows other in the sort order. /// + // @Nao if x and y is null it should return, 0. public int CompareTo(object obj) => obj == null ? -1 : obj is TSource value ? Value.CompareTo(value) : obj is TObject derived ? Value.CompareTo(derived.Value) : -1; @@ -269,6 +271,8 @@ public int CompareTo(object obj) => /// Zero This instance occurs in the same position in the sort order as other. /// Greater than zero This instance follows other in the sort order. /// + // @Nao if x and y is equal to null it should return, 0. + // Maybe use the default comparator, it handle it by default and if needed it can be replaced by a custom one. public int Compare(TObject x, TObject y) => x == null ? -1 : y == null ? 1 : x.Value.CompareTo(y.Value); /// diff --git a/Exiled.API/Features/Player.cs b/Exiled.API/Features/Player.cs index 423e910296..60d51088cb 100644 --- a/Exiled.API/Features/Player.cs +++ b/Exiled.API/Features/Player.cs @@ -4066,6 +4066,7 @@ public void PlayCassieAnnouncement(string words, bool makeHold = false, bool mak /// Same on 's isHeld. /// Same on 's isNoisy. /// Same on 's isSubtitles. + // @Nao, why not inherit the documentation for the param ? public void SendCassieAnnouncement(string words, string translation, bool makeHold = false, bool makeNoise = true, bool isSubtitles = true) { StringBuilder announcement = StringBuilderPool.Pool.Get(); diff --git a/Exiled.API/Features/Respawn.cs b/Exiled.API/Features/Respawn.cs index 59f4e0207b..69a49f8748 100644 --- a/Exiled.API/Features/Respawn.cs +++ b/Exiled.API/Features/Respawn.cs @@ -23,6 +23,9 @@ namespace Exiled.API.Features /// public static class Respawn { + private const string MtfChopperGameObjectName = "Chopper"; + private const string ChaosVanGameObjectName = "CIVanArrive"; + private static GameObject ntfHelicopterGameObject; private static GameObject chaosCarGameObject; @@ -34,7 +37,7 @@ public static GameObject NtfHelicopter get { if (ntfHelicopterGameObject == null) - ntfHelicopterGameObject = GameObject.Find("Chopper"); + ntfHelicopterGameObject = GameObject.Find(MtfChopperGameObjectName); return ntfHelicopterGameObject; } @@ -48,7 +51,7 @@ public static GameObject ChaosVan get { if (chaosCarGameObject == null) - chaosCarGameObject = GameObject.Find("CIVanArrive"); + chaosCarGameObject = GameObject.Find(ChaosVanGameObjectName); return chaosCarGameObject; } diff --git a/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs b/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs index cec6c97657..421c64de73 100644 --- a/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs +++ b/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs @@ -175,7 +175,7 @@ public static IEnumerable EnableAll() continue; VirtualPlugin vp = Activator.CreateInstance(type) as VirtualPlugin; - vp.TryRegister(type.GetCustomAttribute(typeof(VirtualPluginAttribute)) as VirtualPluginAttribute); + vp.TryRegister(type.GetCustomAttribute()); vps.Add(vp); } diff --git a/Exiled.CustomModules/API/Features/CustomEscapes/EscapeSettings.cs b/Exiled.CustomModules/API/Features/CustomEscapes/EscapeSettings.cs index 0b86d38bfa..0053591ef6 100644 --- a/Exiled.CustomModules/API/Features/CustomEscapes/EscapeSettings.cs +++ b/Exiled.CustomModules/API/Features/CustomEscapes/EscapeSettings.cs @@ -74,7 +74,7 @@ public EscapeSettings( Role = role; CustomRole = CustomRole.Get(customRole); Position = position == default ? DefaultPosition : position; - DistanceThreshold = distanceThreshold == DefaultMaxDistanceTolerance ? DefaultMaxDistanceTolerance : distanceThreshold; + DistanceThreshold = distanceThreshold; } /// diff --git a/Exiled.CustomModules/API/Features/CustomGamemodes/GameModeSettings.cs b/Exiled.CustomModules/API/Features/CustomGamemodes/GameModeSettings.cs index 0f562870d0..221576c1db 100644 --- a/Exiled.CustomModules/API/Features/CustomGamemodes/GameModeSettings.cs +++ b/Exiled.CustomModules/API/Features/CustomGamemodes/GameModeSettings.cs @@ -186,7 +186,7 @@ public class GameModeSettings : TypeCastObject, IAdditivePrope public virtual uint[] NonSpawnableCustomRoles { get; set; } /// - /// Gets or sets a [] containing all non spawnable custom teams. + /// Gets or sets a [] containing all non spawnable custom teams. /// /// If not empty, all undefined teams will be able to spawn. ///
diff --git a/Exiled.CustomModules/API/Features/CustomRoles/CustomRole.cs b/Exiled.CustomModules/API/Features/CustomRoles/CustomRole.cs index 74d0514900..d9e04ac8fe 100644 --- a/Exiled.CustomModules/API/Features/CustomRoles/CustomRole.cs +++ b/Exiled.CustomModules/API/Features/CustomRoles/CustomRole.cs @@ -239,8 +239,7 @@ public virtual bool EvaluateConditions { if (pawn.Role == RequiredRoleToSpawn) { - if ((RoleExtensions.GetTeam(RequiredRoleToSpawn) is Team.SCPs && !pawn.IsScp) || - (RoleExtensions.GetTeam(RequiredRoleToSpawn) is not Team.SCPs && pawn.IsScp)) + if ((RoleExtensions.GetTeam(RequiredRoleToSpawn) is Team.SCPs) != pawn.IsScp) continue; return true; diff --git a/Exiled.CustomModules/API/Features/CustomRoles/CustomTeam.cs b/Exiled.CustomModules/API/Features/CustomRoles/CustomTeam.cs index ed15c39d84..22fafe7ced 100644 --- a/Exiled.CustomModules/API/Features/CustomRoles/CustomTeam.cs +++ b/Exiled.CustomModules/API/Features/CustomRoles/CustomTeam.cs @@ -35,6 +35,9 @@ namespace Exiled.CustomModules.API.Features.CustomRoles /// public abstract class CustomTeam : CustomModule { + private const string VanilaConfigMtfMinTime = "minimum_MTF_time_to_spawn"; + private const string VanilaConfigMtfMaxTime = "maximum_MTF_time_to_spawn"; + private static readonly Dictionary PlayersValue = new(); private static readonly List Registered = new(); private static readonly Dictionary TypeLookupTable = new(); @@ -111,17 +114,18 @@ public abstract class CustomTeam : CustomModule /// This property provides access to a curated collection of objects, encapsulating all available custom role within the context of units. ///
The collection is designed to be both queried and modified as needed to accommodate dynamic scenarios within the game architecture. /// + // @Nao, adding the possiblity to have custom roles public virtual IEnumerable Units { get; set; } = new uint[] { }; /// /// Gets or sets the minimum amount time after which any team will be allowed to spawn. /// - public virtual float MinNextSequenceTime { get; set; } = GameCore.ConfigFile.ServerConfig.GetFloat("minimum_MTF_time_to_spawn", 280f); + public virtual float MinNextSequenceTime { get; set; } = GameCore.ConfigFile.ServerConfig.GetFloat(VanilaConfigMtfMinTime, 280f); /// /// Gets or sets the maximum amount time after which any team will be spawned. /// - public virtual float MaxNextSequenceTime { get; set; } = GameCore.ConfigFile.ServerConfig.GetFloat("maximum_MTF_time_to_spawn", 350f); + public virtual float MaxNextSequenceTime { get; set; } = GameCore.ConfigFile.ServerConfig.GetFloat(VanilaConfigMtfMaxTime, 350f); /// /// Gets or sets the relative spawn probability of the . @@ -226,8 +230,7 @@ public virtual bool EvaluateConditions { if (pawn.Role == RequiredRoleToSpawn) { - if ((RoleExtensions.GetTeam(RequiredRoleToSpawn) is Team.SCPs && !pawn.IsScp) || - (RoleExtensions.GetTeam(RequiredRoleToSpawn) is not Team.SCPs && pawn.IsScp)) + if ((RoleExtensions.GetTeam(RequiredRoleToSpawn) is Team.SCPs) != pawn.IsScp) continue; return true; diff --git a/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs b/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs index 83e69ca89a..540ac7f57d 100644 --- a/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs +++ b/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs @@ -178,37 +178,49 @@ protected virtual RoleTypeId FakeAppearance /// /// Evaluates the specified conditions affecting the round's ending conditions. /// - /// The corresponding evaluation. + /// + /// if the round should continue. + ///
Otherwise, if the round can end. + /// All other condition need to be to end the round. + ///
public virtual bool EvaluateEndingConditions() { - if (CustomRole.TeamsOwnership.Length == 1) - return true; - - SummaryInfo summaryInfo = World.Get().SummaryInfo; - - if (CustomRole.TeamsOwnership.Contains(Team.SCPs) && summaryInfo.FoundationForces <= 0 && summaryInfo.ChaosInsurgency <= 0) - return true; - - if (CustomRole.TeamsOwnership.Any(team => team is Team.ClassD or Team.ChaosInsurgency) && summaryInfo.FoundationForces <= 0 && summaryInfo.Anomalies <= 0) - return true; - - if (CustomRole.TeamsOwnership.Any(team => team is Team.FoundationForces or Team.Scientists) && summaryInfo.ChaosInsurgency <= 0 && summaryInfo.Anomalies <= 0) - return true; - - if (CustomRole.TeamsOwnership.IsEmpty()) + SummaryInfo summaryInfo; + switch (CustomRole.TeamsOwnership.Length) { - int uniqueFaction = 0; - if (summaryInfo.FoundationForces > 0) - ++uniqueFaction; - if (summaryInfo.ChaosInsurgency > 0) - ++uniqueFaction; - if (summaryInfo.Anomalies > 0) - ++uniqueFaction; - - return uniqueFaction <= 1; - } + case 0: + summaryInfo = World.Get().SummaryInfo; + int uniqueFaction = 0; + if (summaryInfo.FoundationForces > 0) + ++uniqueFaction; + if (summaryInfo.ChaosInsurgency > 0) + ++uniqueFaction; + if (summaryInfo.Anomalies > 0) + ++uniqueFaction; + return uniqueFaction <= 1; + + case 1: return true; + + default: + // @Nao, i mouve repeted code here to see better + // For me this following condition are always true. + // Expete if it existe a case where there is -1 Chaos + summaryInfo = World.Get().SummaryInfo; + + bool hasChaos = summaryInfo.ChaosInsurgency <= 0; + bool hasFondtion = summaryInfo.FoundationForces <= 0; + bool hasScp = summaryInfo.ChaosInsurgency <= 0; + + if (CustomRole.TeamsOwnership.Contains(Team.SCPs) && hasFondtion && hasChaos) + return true; // @Nao Why true, that mean the round can end, but if there is some fondation left, the round should not end... + + if (CustomRole.TeamsOwnership.Any(team => team is Team.ClassD or Team.ChaosInsurgency) && hasFondtion && hasScp) + return true; - return false; + if (CustomRole.TeamsOwnership.Any(team => team is Team.FoundationForces or Team.Scientists) && hasChaos && hasScp) + return true; + return false; + } } /// diff --git a/Exiled.CustomModules/API/Features/RespawnManager.cs b/Exiled.CustomModules/API/Features/RespawnManager.cs index 72ad9f9c44..be8b6f45a3 100644 --- a/Exiled.CustomModules/API/Features/RespawnManager.cs +++ b/Exiled.CustomModules/API/Features/RespawnManager.cs @@ -174,6 +174,7 @@ private void OnPreRespawningTeam(PreRespawningTeamEventArgs ev) { PreviousKnownTeam = NextKnownTeam; + // @Nao are you sur that is "or SpawnableTeamType" and not "or not SpawnableTeamType" if (NextKnownTeam is null or SpawnableTeamType) return; @@ -181,14 +182,16 @@ private void OnPreRespawningTeam(PreRespawningTeamEventArgs ev) if (customTeam is null) return; - if (customTeam.TeamsOwnership.Any(t => - t == (ev.NextKnownTeam is SpawnableTeamType.ChaosInsurgency ? - Team.ChaosInsurgency : Team.FoundationForces))) + if (customTeam.TeamsOwnership.Any(t => t == (Team)ev.NextKnownTeam)) { ev.MaxWaveSize = customTeam.Size; return; } + // @Nao, it cool to use the event system for this. But if an other plugin + // allready say "ev.IsAllowed = false". it will get ignore + // and the team will still spawn... + // And post event shoold be here to spawn. Or an EventHandler handling last ev.IsAllowed = false; Spawn(); Respawning.RespawnManager.Singleton?.RestartSequence(); From 2ca767ba41193dc4c5d8a6b31f70d4117bfcc110 Mon Sep 17 00:00:00 2001 From: Valentin Arthur Thomas Date: Wed, 11 Sep 2024 04:12:43 +0200 Subject: [PATCH 02/16] Rename UEBranchType Prealpha to PreAlpha --- Exiled.API/Enums/UEBranchType.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Exiled.API/Enums/UEBranchType.cs b/Exiled.API/Enums/UEBranchType.cs index 5433abd074..d04a7d189a 100644 --- a/Exiled.API/Enums/UEBranchType.cs +++ b/Exiled.API/Enums/UEBranchType.cs @@ -40,9 +40,9 @@ public class UEBranchType : UnmanagedEnumClass public static readonly UEBranchType Alpha = new(4); /// - /// The prealpha branch. + /// The pre alpha branch. /// - public static readonly UEBranchType Prealpha = new(5); + public static readonly UEBranchType PreAlpha = new(5); /// /// The unstable branch. From 0c47b98141d530306464eee174bbaccddcc45b4d Mon Sep 17 00:00:00 2001 From: Valentin Arthur Thomas Date: Wed, 11 Sep 2024 04:14:19 +0200 Subject: [PATCH 03/16] Simplifying Singleton implementation The code has been simplified to no longer use a dictionary to manage multiple instances of `Singleton`. Now a single instance of `T` is used via a static `Instance` property. The `Create` method has been changed to set the instance if it is not already set, and a new `Destroy` method has been added to reset the instance to `null`. The `TryGet` and `Destroy` methods have been simplified to work with the new single instance implementation. The calls to `Singleton.Create(this)` and `Singleton.Destroy(this)` in `VirtualPlugin.cs` have been commented out, with annotations suggesting that use of the class itself as a singleton might require a different approach to avoid overwriting other instances. --- Exiled.API/Features/Core/Generic/Singleton.cs | 76 ++++++++----------- .../VirtualAssemblies/VirtualPlugin.cs | 7 +- 2 files changed, 35 insertions(+), 48 deletions(-) diff --git a/Exiled.API/Features/Core/Generic/Singleton.cs b/Exiled.API/Features/Core/Generic/Singleton.cs index f0128aca4c..bbdbf925d3 100644 --- a/Exiled.API/Features/Core/Generic/Singleton.cs +++ b/Exiled.API/Features/Core/Generic/Singleton.cs @@ -19,69 +19,53 @@ namespace Exiled.API.Features.Core.Generic public sealed class Singleton : TypeCastObject where T : class { - // @Nao, this is not realy a singleton if you have mutliple value. - // NB: each Singleton ave a new instance of dictionary. - // exemple Singleton.Instance is not the same Singleton.Instance - private static readonly Dictionary> Instances = new(); - - /// - /// Initializes a new instance of the class. - /// - /// The branch to instantiate. - public Singleton(T value) - { - Destroy(value); - Value = value; - Instances.Add(value, this); - } - - /// - /// Finalizes an instance of the class. - /// - // @Nao Probably never call, it self reference. Maybe an future issue doing memory leek - // it will get call only after the instance get remove form Instances - ~Singleton() => Instances.Remove(Value); - /// /// Gets the relative value. /// - public static T Instance => Instances.FirstOrDefault(@object => @object.Key.GetType() == typeof(T)).Value; - - /// - /// Gets the singleton value. - /// - internal T Value { get; private set; } - - /// - /// Converts the given instance into . - /// - /// The instance to convert. - public static implicit operator T(Singleton instance) => instance?.Value; + public static T Instance { get; private set; } /// /// Tries to get the relative value. /// - /// The type of the object. /// The object instance. /// if the object instance is not null and can be casted as the specified type; otherwise, . - // @nao It will return the Instance. - // But with this curent class definition you can register derived class of T. - // They can be registred but not retrived. Also why make this method generic if this not to get - // other value than the instance. - public static bool TryGet(out TObject instance) - where TObject : class => (instance = Instance as TObject) is not null; + public static bool TryGet(out T instance) + => (instance = Instance) is not null; - /// - public static void Create(T @object) => new Singleton(@object); + /// + /// Define the value of the if not already defined. + /// If you want to replace first call . + /// + /// The object use to create the singleton. + public static void Create(T @object) + { + if (Instance is not null) + return; + Instance = @object; + } /// - /// Destroys the given instance. + /// Set to null if is the same. /// /// The object to destroy. /// if the instance was destroyed; otherwise, . public static bool Destroy(T @object) { - return Instances.Remove(@object); + if (Instance == @object) + { + Instance = null; + return true; + } + + return false; + } + + /// + /// Set to null . + /// + public static void Destroy() + { + Instance = null; } } } \ No newline at end of file diff --git a/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs b/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs index 421c64de73..217f1af12d 100644 --- a/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs +++ b/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs @@ -274,7 +274,7 @@ protected virtual void OnReloaded() /// protected virtual void CreateInstance() { - Singleton.Create(this); + // Singleton.Create(this); } /// @@ -283,7 +283,10 @@ protected virtual void CreateInstance() protected virtual void DestroyInstance() { Config = null; - Singleton.Destroy(this); + + // @Nao If by default the class set it self inside of a singleton. + // You will need to use a TSlef. This will avoid rewriting over an other class. + // Singleton.Destroy(this); } /// From 363913a2ac3d1d81172ad564fbb634af90dfce51 Mon Sep 17 00:00:00 2001 From: Valentin Arthur Thomas Date: Sat, 14 Sep 2024 02:46:25 +0200 Subject: [PATCH 04/16] Rewrite EvaluateEndingConditions varaibles names. --- .../API/Features/CustomRoles/RoleBehaviour.cs | 18 ++++++++---------- Exiled.CustomModules/API/Features/World.cs | 2 +- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs b/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs index 540ac7f57d..10054be849 100644 --- a/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs +++ b/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs @@ -202,23 +202,21 @@ public virtual bool EvaluateEndingConditions() case 1: return true; default: - // @Nao, i mouve repeted code here to see better - // For me this following condition are always true. - // Expete if it existe a case where there is -1 Chaos summaryInfo = World.Get().SummaryInfo; - bool hasChaos = summaryInfo.ChaosInsurgency <= 0; - bool hasFondtion = summaryInfo.FoundationForces <= 0; - bool hasScp = summaryInfo.ChaosInsurgency <= 0; + bool noChaos = summaryInfo.ChaosInsurgency <= 0; + bool noFondation = summaryInfo.FoundationForces <= 0; + bool noScp = summaryInfo.ChaosInsurgency <= 0; - if (CustomRole.TeamsOwnership.Contains(Team.SCPs) && hasFondtion && hasChaos) - return true; // @Nao Why true, that mean the round can end, but if there is some fondation left, the round should not end... + if (noFondation && noChaos && CustomRole.TeamsOwnership.Contains(Team.SCPs)) + return true; - if (CustomRole.TeamsOwnership.Any(team => team is Team.ClassD or Team.ChaosInsurgency) && hasFondtion && hasScp) + if (noFondation && noScp && CustomRole.TeamsOwnership.Any(team => team is Team.ClassD or Team.ChaosInsurgency)) return true; - if (CustomRole.TeamsOwnership.Any(team => team is Team.FoundationForces or Team.Scientists) && hasChaos && hasScp) + if (noChaos && noScp && CustomRole.TeamsOwnership.Any(team => team is Team.FoundationForces or Team.Scientists)) return true; + return false; } } diff --git a/Exiled.CustomModules/API/Features/World.cs b/Exiled.CustomModules/API/Features/World.cs index e78320654d..6fc2b42b9d 100644 --- a/Exiled.CustomModules/API/Features/World.cs +++ b/Exiled.CustomModules/API/Features/World.cs @@ -234,7 +234,7 @@ private void OnEndingRound(EndingRoundEventArgs ev) if (!ev.IsAllowed) return; - foreach (CustomRole role in CustomRole.Get(cr => cr.TeamsOwnership.Length != 1 && cr.Instances > 1)) + foreach (CustomRole role in CustomRole.Get(cr => cr.Instances > 1)) { if (role.Owners.First().RoleBehaviour.EvaluateEndingConditions()) continue; From 5e9f65f032e8776860e6d12e61d9a1bc0f9f235d Mon Sep 17 00:00:00 2001 From: Jean THOMAS Date: Thu, 1 Aug 2024 17:39:19 +0200 Subject: [PATCH 05/16] Review --- .../Features/Core/Components/TickComponent.cs | 2 +- Exiled.API/Features/Core/ConstProperty.cs | 3 +- Exiled.API/Features/Core/GameEntity.cs | 5 -- .../Features/Core/Generic/EBehaviour.cs | 2 + Exiled.API/Features/Core/Generic/Singleton.cs | 17 +++-- .../Core/Generic/UniqueUnmanagedEnumClass.cs | 9 ++- .../Core/Generic/UnmanagedEnumClass.cs | 4 ++ Exiled.API/Features/Player.cs | 1 + Exiled.API/Features/Respawn.cs | 7 +- .../VirtualAssemblies/VirtualPlugin.cs | 2 +- .../Features/CustomEscapes/EscapeSettings.cs | 2 +- .../CustomGamemodes/GameModeSettings.cs | 2 +- .../API/Features/CustomRoles/CustomRole.cs | 3 +- .../API/Features/CustomRoles/CustomTeam.cs | 11 ++-- .../API/Features/CustomRoles/RoleBehaviour.cs | 66 +++++++++++-------- .../API/Features/RespawnManager.cs | 9 ++- 16 files changed, 89 insertions(+), 56 deletions(-) diff --git a/Exiled.API/Features/Core/Components/TickComponent.cs b/Exiled.API/Features/Core/Components/TickComponent.cs index 7e65d93a3b..1aec75ac52 100644 --- a/Exiled.API/Features/Core/Components/TickComponent.cs +++ b/Exiled.API/Features/Core/Components/TickComponent.cs @@ -24,7 +24,7 @@ public sealed class TickComponent : EObject /// The default fixed tick rate (60 per second). /// #pragma warning disable SA1310 - public const float DEFAULT_FIXED_TICK_RATE = 0.016f; + public const float DEFAULT_FIXED_TICK_RATE = 1f / 60f; #pragma warning restore SA1310 private readonly HashSet boundHandles; diff --git a/Exiled.API/Features/Core/ConstProperty.cs b/Exiled.API/Features/Core/ConstProperty.cs index 49dc60b7c5..f6c48f6317 100644 --- a/Exiled.API/Features/Core/ConstProperty.cs +++ b/Exiled.API/Features/Core/ConstProperty.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------- +// ----------------------------------------------------------------------- // // Copyright (c) Exiled Team. All rights reserved. // Licensed under the CC BY-SA 3.0 license. @@ -48,6 +48,7 @@ public ConstProperty(T constantValue, Type[] typesToPatch, MethodInfo[] skipMeth /// ~ConstProperty() { + // @Nao Never be call, it need to remove it reference from List beffor the dtor call List.Remove(this); foreach (MethodInfo methodInfo in PatchedMethods) diff --git a/Exiled.API/Features/Core/GameEntity.cs b/Exiled.API/Features/Core/GameEntity.cs index aa84602d36..5aa3184012 100644 --- a/Exiled.API/Features/Core/GameEntity.cs +++ b/Exiled.API/Features/Core/GameEntity.cs @@ -44,11 +44,6 @@ protected GameEntity(GameObject gameObject) // List.Add(this); } - /// - /// Finalizes an instance of the class. - /// - ~GameEntity() => List.Remove(this); - /// /// Gets all active instances. /// diff --git a/Exiled.API/Features/Core/Generic/EBehaviour.cs b/Exiled.API/Features/Core/Generic/EBehaviour.cs index a2b3fd611e..913d05f62c 100644 --- a/Exiled.API/Features/Core/Generic/EBehaviour.cs +++ b/Exiled.API/Features/Core/Generic/EBehaviour.cs @@ -59,6 +59,8 @@ protected EBehaviour() /// protected virtual void FindOwner() { + // @Nao T is a GameEntity, why not add in inisde of it the method a abstract. + // Or create an specific Interaface requesting to implement this method MethodInfo method = typeof(T).GetMethod("Get", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(GameObject) }, null); if (method != null) diff --git a/Exiled.API/Features/Core/Generic/Singleton.cs b/Exiled.API/Features/Core/Generic/Singleton.cs index a212b6f874..f0128aca4c 100644 --- a/Exiled.API/Features/Core/Generic/Singleton.cs +++ b/Exiled.API/Features/Core/Generic/Singleton.cs @@ -19,6 +19,9 @@ namespace Exiled.API.Features.Core.Generic public sealed class Singleton : TypeCastObject where T : class { + // @Nao, this is not realy a singleton if you have mutliple value. + // NB: each Singleton ave a new instance of dictionary. + // exemple Singleton.Instance is not the same Singleton.Instance private static readonly Dictionary> Instances = new(); /// @@ -35,6 +38,8 @@ public Singleton(T value) /// /// Finalizes an instance of the class. /// + // @Nao Probably never call, it self reference. Maybe an future issue doing memory leek + // it will get call only after the instance get remove form Instances ~Singleton() => Instances.Remove(Value); /// @@ -59,6 +64,10 @@ public Singleton(T value) /// The type of the object. /// The object instance. /// if the object instance is not null and can be casted as the specified type; otherwise, . + // @nao It will return the Instance. + // But with this curent class definition you can register derived class of T. + // They can be registred but not retrived. Also why make this method generic if this not to get + // other value than the instance. public static bool TryGet(out TObject instance) where TObject : class => (instance = Instance as TObject) is not null; @@ -72,13 +81,7 @@ public static bool TryGet(out TObject instance) /// if the instance was destroyed; otherwise, . public static bool Destroy(T @object) { - if (Instances.TryGetValue(@object, out Singleton _)) - { - Instances[@object] = null; - return Instances.Remove(@object); - } - - return false; + return Instances.Remove(@object); } } } \ No newline at end of file diff --git a/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs b/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs index 0db2f2b9d7..02608ebbdf 100644 --- a/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs +++ b/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs @@ -41,6 +41,10 @@ public UniqueUnmanagedEnumClass() values ??= new(); TypeCode code = Convert.GetTypeCode(typeof(TSource).GetField("MinValue").GetValue(null)); + // @Nao If the value is not an Uxxxxx it will get an overflow. Like if it use an Int16, Byte or SByte + // Maybe use a long for nextValue and do "nextValue = typeof(TSource).GetField("MinValue").GetValue(null)" + // it also be a struct containing only unmanged struct. The best solution is proably to + // look for upper version of the framwork to resolve this with the new numeric interfaces. if (code is TypeCode.UInt16 or TypeCode.UInt32 or TypeCode.UInt64) nextValue = 0; @@ -54,6 +58,9 @@ public UniqueUnmanagedEnumClass() while (values.ContainsKey(value)); Value = value; + + // @nao If the value is register when a new instance is created. + // Maybe put the ctor in protected to avoid exteranal code (outisde the enum) to create new values values.Add(value, (TObject)this); } } @@ -289,7 +296,7 @@ public int CompareTo(object obj) => /// public int Compare(TObject x, TObject y) => x == null ? -1 : y == null ? 1 : x.Value.CompareTo(y.Value); -/// + /// TypeCode IConvertible.GetTypeCode() => Value.GetTypeCode(); /// diff --git a/Exiled.API/Features/Core/Generic/UnmanagedEnumClass.cs b/Exiled.API/Features/Core/Generic/UnmanagedEnumClass.cs index 2d396b1e6a..8cb1d22c0c 100644 --- a/Exiled.API/Features/Core/Generic/UnmanagedEnumClass.cs +++ b/Exiled.API/Features/Core/Generic/UnmanagedEnumClass.cs @@ -23,6 +23,7 @@ namespace Exiled.API.Features.Core.Generic /// /// The type of the source object to handle the instance of. /// The type of the child object to handle the instance of. + // @Nao you can use Comparer.Default, it will result as using the IComparable.Compare public abstract class UnmanagedEnumClass : IComparable, IEquatable, IComparable, IComparer, IConvertible, IEnumClass where TSource : unmanaged, IComparable, IFormattable, IConvertible, IComparable, IEquatable where TObject : UnmanagedEnumClass @@ -256,6 +257,7 @@ public override bool Equals(object obj) => /// Zero This instance occurs in the same position in the sort order as other. /// Greater than zero This instance follows other in the sort order. /// + // @Nao if x and y is null it should return, 0. public int CompareTo(object obj) => obj == null ? -1 : obj is TSource value ? Value.CompareTo(value) : obj is TObject derived ? Value.CompareTo(derived.Value) : -1; @@ -272,6 +274,8 @@ public int CompareTo(object obj) => /// Zero This instance occurs in the same position in the sort order as other. /// Greater than zero This instance follows other in the sort order. /// + // @Nao if x and y is equal to null it should return, 0. + // Maybe use the default comparator, it handle it by default and if needed it can be replaced by a custom one. public int Compare(TObject x, TObject y) => x == null ? -1 : y == null ? 1 : x.Value.CompareTo(y.Value); /// diff --git a/Exiled.API/Features/Player.cs b/Exiled.API/Features/Player.cs index f48708b733..e40fd06448 100644 --- a/Exiled.API/Features/Player.cs +++ b/Exiled.API/Features/Player.cs @@ -4072,6 +4072,7 @@ public void PlayCassieAnnouncement(string words, bool makeHold = false, bool mak /// Same on 's isHeld. /// Same on 's isNoisy. /// Same on 's isSubtitles. + // @Nao, why not inherit the documentation for the param ? public void SendCassieAnnouncement(string words, string translation, bool makeHold = false, bool makeNoise = true, bool isSubtitles = true) { StringBuilder announcement = StringBuilderPool.Pool.Get(); diff --git a/Exiled.API/Features/Respawn.cs b/Exiled.API/Features/Respawn.cs index 59f4e0207b..69a49f8748 100644 --- a/Exiled.API/Features/Respawn.cs +++ b/Exiled.API/Features/Respawn.cs @@ -23,6 +23,9 @@ namespace Exiled.API.Features /// public static class Respawn { + private const string MtfChopperGameObjectName = "Chopper"; + private const string ChaosVanGameObjectName = "CIVanArrive"; + private static GameObject ntfHelicopterGameObject; private static GameObject chaosCarGameObject; @@ -34,7 +37,7 @@ public static GameObject NtfHelicopter get { if (ntfHelicopterGameObject == null) - ntfHelicopterGameObject = GameObject.Find("Chopper"); + ntfHelicopterGameObject = GameObject.Find(MtfChopperGameObjectName); return ntfHelicopterGameObject; } @@ -48,7 +51,7 @@ public static GameObject ChaosVan get { if (chaosCarGameObject == null) - chaosCarGameObject = GameObject.Find("CIVanArrive"); + chaosCarGameObject = GameObject.Find(ChaosVanGameObjectName); return chaosCarGameObject; } diff --git a/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs b/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs index cec6c97657..421c64de73 100644 --- a/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs +++ b/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs @@ -175,7 +175,7 @@ public static IEnumerable EnableAll() continue; VirtualPlugin vp = Activator.CreateInstance(type) as VirtualPlugin; - vp.TryRegister(type.GetCustomAttribute(typeof(VirtualPluginAttribute)) as VirtualPluginAttribute); + vp.TryRegister(type.GetCustomAttribute()); vps.Add(vp); } diff --git a/Exiled.CustomModules/API/Features/CustomEscapes/EscapeSettings.cs b/Exiled.CustomModules/API/Features/CustomEscapes/EscapeSettings.cs index 0b86d38bfa..0053591ef6 100644 --- a/Exiled.CustomModules/API/Features/CustomEscapes/EscapeSettings.cs +++ b/Exiled.CustomModules/API/Features/CustomEscapes/EscapeSettings.cs @@ -74,7 +74,7 @@ public EscapeSettings( Role = role; CustomRole = CustomRole.Get(customRole); Position = position == default ? DefaultPosition : position; - DistanceThreshold = distanceThreshold == DefaultMaxDistanceTolerance ? DefaultMaxDistanceTolerance : distanceThreshold; + DistanceThreshold = distanceThreshold; } /// diff --git a/Exiled.CustomModules/API/Features/CustomGamemodes/GameModeSettings.cs b/Exiled.CustomModules/API/Features/CustomGamemodes/GameModeSettings.cs index 0f562870d0..221576c1db 100644 --- a/Exiled.CustomModules/API/Features/CustomGamemodes/GameModeSettings.cs +++ b/Exiled.CustomModules/API/Features/CustomGamemodes/GameModeSettings.cs @@ -186,7 +186,7 @@ public class GameModeSettings : TypeCastObject, IAdditivePrope public virtual uint[] NonSpawnableCustomRoles { get; set; } /// - /// Gets or sets a [] containing all non spawnable custom teams. + /// Gets or sets a [] containing all non spawnable custom teams. /// /// If not empty, all undefined teams will be able to spawn. ///
diff --git a/Exiled.CustomModules/API/Features/CustomRoles/CustomRole.cs b/Exiled.CustomModules/API/Features/CustomRoles/CustomRole.cs index b68c3e0484..9cff107c91 100644 --- a/Exiled.CustomModules/API/Features/CustomRoles/CustomRole.cs +++ b/Exiled.CustomModules/API/Features/CustomRoles/CustomRole.cs @@ -244,8 +244,7 @@ public virtual bool EvaluateConditions { if (pawn.Role == RequiredRoleToSpawn) { - if ((RoleExtensions.GetTeam(RequiredRoleToSpawn) is Team.SCPs && !pawn.IsScp) || - (RoleExtensions.GetTeam(RequiredRoleToSpawn) is not Team.SCPs && pawn.IsScp)) + if ((RoleExtensions.GetTeam(RequiredRoleToSpawn) is Team.SCPs) != pawn.IsScp) continue; return true; diff --git a/Exiled.CustomModules/API/Features/CustomRoles/CustomTeam.cs b/Exiled.CustomModules/API/Features/CustomRoles/CustomTeam.cs index ed15c39d84..22fafe7ced 100644 --- a/Exiled.CustomModules/API/Features/CustomRoles/CustomTeam.cs +++ b/Exiled.CustomModules/API/Features/CustomRoles/CustomTeam.cs @@ -35,6 +35,9 @@ namespace Exiled.CustomModules.API.Features.CustomRoles /// public abstract class CustomTeam : CustomModule { + private const string VanilaConfigMtfMinTime = "minimum_MTF_time_to_spawn"; + private const string VanilaConfigMtfMaxTime = "maximum_MTF_time_to_spawn"; + private static readonly Dictionary PlayersValue = new(); private static readonly List Registered = new(); private static readonly Dictionary TypeLookupTable = new(); @@ -111,17 +114,18 @@ public abstract class CustomTeam : CustomModule /// This property provides access to a curated collection of objects, encapsulating all available custom role within the context of units. ///
The collection is designed to be both queried and modified as needed to accommodate dynamic scenarios within the game architecture. /// + // @Nao, adding the possiblity to have custom roles public virtual IEnumerable Units { get; set; } = new uint[] { }; /// /// Gets or sets the minimum amount time after which any team will be allowed to spawn. /// - public virtual float MinNextSequenceTime { get; set; } = GameCore.ConfigFile.ServerConfig.GetFloat("minimum_MTF_time_to_spawn", 280f); + public virtual float MinNextSequenceTime { get; set; } = GameCore.ConfigFile.ServerConfig.GetFloat(VanilaConfigMtfMinTime, 280f); /// /// Gets or sets the maximum amount time after which any team will be spawned. /// - public virtual float MaxNextSequenceTime { get; set; } = GameCore.ConfigFile.ServerConfig.GetFloat("maximum_MTF_time_to_spawn", 350f); + public virtual float MaxNextSequenceTime { get; set; } = GameCore.ConfigFile.ServerConfig.GetFloat(VanilaConfigMtfMaxTime, 350f); /// /// Gets or sets the relative spawn probability of the . @@ -226,8 +230,7 @@ public virtual bool EvaluateConditions { if (pawn.Role == RequiredRoleToSpawn) { - if ((RoleExtensions.GetTeam(RequiredRoleToSpawn) is Team.SCPs && !pawn.IsScp) || - (RoleExtensions.GetTeam(RequiredRoleToSpawn) is not Team.SCPs && pawn.IsScp)) + if ((RoleExtensions.GetTeam(RequiredRoleToSpawn) is Team.SCPs) != pawn.IsScp) continue; return true; diff --git a/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs b/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs index 39a9fbd90f..4543396bf8 100644 --- a/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs +++ b/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs @@ -178,37 +178,49 @@ protected virtual RoleTypeId FakeAppearance /// /// Evaluates the specified conditions affecting the round's ending conditions. /// - /// The corresponding evaluation. + /// + /// if the round should continue. + ///
Otherwise, if the round can end. + /// All other condition need to be to end the round. + ///
public virtual bool EvaluateEndingConditions() { - if (CustomRole.TeamsOwnership.Length == 1) - return true; - - SummaryInfo summaryInfo = World.Get().SummaryInfo; - - if (CustomRole.TeamsOwnership.Contains(Team.SCPs) && summaryInfo.FoundationForces <= 0 && summaryInfo.ChaosInsurgency <= 0) - return true; - - if (CustomRole.TeamsOwnership.Any(team => team is Team.ClassD or Team.ChaosInsurgency) && summaryInfo.FoundationForces <= 0 && summaryInfo.Anomalies <= 0) - return true; - - if (CustomRole.TeamsOwnership.Any(team => team is Team.FoundationForces or Team.Scientists) && summaryInfo.ChaosInsurgency <= 0 && summaryInfo.Anomalies <= 0) - return true; - - if (CustomRole.TeamsOwnership.IsEmpty()) + SummaryInfo summaryInfo; + switch (CustomRole.TeamsOwnership.Length) { - int uniqueFaction = 0; - if (summaryInfo.FoundationForces > 0) - ++uniqueFaction; - if (summaryInfo.ChaosInsurgency > 0) - ++uniqueFaction; - if (summaryInfo.Anomalies > 0) - ++uniqueFaction; - - return uniqueFaction <= 1; - } + case 0: + summaryInfo = World.Get().SummaryInfo; + int uniqueFaction = 0; + if (summaryInfo.FoundationForces > 0) + ++uniqueFaction; + if (summaryInfo.ChaosInsurgency > 0) + ++uniqueFaction; + if (summaryInfo.Anomalies > 0) + ++uniqueFaction; + return uniqueFaction <= 1; + + case 1: return true; + + default: + // @Nao, i mouve repeted code here to see better + // For me this following condition are always true. + // Expete if it existe a case where there is -1 Chaos + summaryInfo = World.Get().SummaryInfo; + + bool hasChaos = summaryInfo.ChaosInsurgency <= 0; + bool hasFondtion = summaryInfo.FoundationForces <= 0; + bool hasScp = summaryInfo.ChaosInsurgency <= 0; + + if (CustomRole.TeamsOwnership.Contains(Team.SCPs) && hasFondtion && hasChaos) + return true; // @Nao Why true, that mean the round can end, but if there is some fondation left, the round should not end... + + if (CustomRole.TeamsOwnership.Any(team => team is Team.ClassD or Team.ChaosInsurgency) && hasFondtion && hasScp) + return true; - return false; + if (CustomRole.TeamsOwnership.Any(team => team is Team.FoundationForces or Team.Scientists) && hasChaos && hasScp) + return true; + return false; + } } /// diff --git a/Exiled.CustomModules/API/Features/RespawnManager.cs b/Exiled.CustomModules/API/Features/RespawnManager.cs index 72ad9f9c44..be8b6f45a3 100644 --- a/Exiled.CustomModules/API/Features/RespawnManager.cs +++ b/Exiled.CustomModules/API/Features/RespawnManager.cs @@ -174,6 +174,7 @@ private void OnPreRespawningTeam(PreRespawningTeamEventArgs ev) { PreviousKnownTeam = NextKnownTeam; + // @Nao are you sur that is "or SpawnableTeamType" and not "or not SpawnableTeamType" if (NextKnownTeam is null or SpawnableTeamType) return; @@ -181,14 +182,16 @@ private void OnPreRespawningTeam(PreRespawningTeamEventArgs ev) if (customTeam is null) return; - if (customTeam.TeamsOwnership.Any(t => - t == (ev.NextKnownTeam is SpawnableTeamType.ChaosInsurgency ? - Team.ChaosInsurgency : Team.FoundationForces))) + if (customTeam.TeamsOwnership.Any(t => t == (Team)ev.NextKnownTeam)) { ev.MaxWaveSize = customTeam.Size; return; } + // @Nao, it cool to use the event system for this. But if an other plugin + // allready say "ev.IsAllowed = false". it will get ignore + // and the team will still spawn... + // And post event shoold be here to spawn. Or an EventHandler handling last ev.IsAllowed = false; Spawn(); Respawning.RespawnManager.Singleton?.RestartSequence(); From b5eb7502b4dc57d01d304b669910b3e5016f6420 Mon Sep 17 00:00:00 2001 From: Valentin Arthur Thomas Date: Wed, 11 Sep 2024 04:12:43 +0200 Subject: [PATCH 06/16] Rename UEBranchType Prealpha to PreAlpha --- Exiled.API/Enums/UEBranchType.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Exiled.API/Enums/UEBranchType.cs b/Exiled.API/Enums/UEBranchType.cs index 5433abd074..d04a7d189a 100644 --- a/Exiled.API/Enums/UEBranchType.cs +++ b/Exiled.API/Enums/UEBranchType.cs @@ -40,9 +40,9 @@ public class UEBranchType : UnmanagedEnumClass public static readonly UEBranchType Alpha = new(4); /// - /// The prealpha branch. + /// The pre alpha branch. /// - public static readonly UEBranchType Prealpha = new(5); + public static readonly UEBranchType PreAlpha = new(5); /// /// The unstable branch. From 104c1130c09c740e4534e8bfcbf387a8d8ec6253 Mon Sep 17 00:00:00 2001 From: Valentin Arthur Thomas Date: Wed, 11 Sep 2024 04:14:19 +0200 Subject: [PATCH 07/16] Simplifying Singleton implementation The code has been simplified to no longer use a dictionary to manage multiple instances of `Singleton`. Now a single instance of `T` is used via a static `Instance` property. The `Create` method has been changed to set the instance if it is not already set, and a new `Destroy` method has been added to reset the instance to `null`. The `TryGet` and `Destroy` methods have been simplified to work with the new single instance implementation. The calls to `Singleton.Create(this)` and `Singleton.Destroy(this)` in `VirtualPlugin.cs` have been commented out, with annotations suggesting that use of the class itself as a singleton might require a different approach to avoid overwriting other instances. --- Exiled.API/Features/Core/Generic/Singleton.cs | 76 ++++++++----------- .../VirtualAssemblies/VirtualPlugin.cs | 7 +- 2 files changed, 35 insertions(+), 48 deletions(-) diff --git a/Exiled.API/Features/Core/Generic/Singleton.cs b/Exiled.API/Features/Core/Generic/Singleton.cs index f0128aca4c..bbdbf925d3 100644 --- a/Exiled.API/Features/Core/Generic/Singleton.cs +++ b/Exiled.API/Features/Core/Generic/Singleton.cs @@ -19,69 +19,53 @@ namespace Exiled.API.Features.Core.Generic public sealed class Singleton : TypeCastObject where T : class { - // @Nao, this is not realy a singleton if you have mutliple value. - // NB: each Singleton ave a new instance of dictionary. - // exemple Singleton.Instance is not the same Singleton.Instance - private static readonly Dictionary> Instances = new(); - - /// - /// Initializes a new instance of the class. - /// - /// The branch to instantiate. - public Singleton(T value) - { - Destroy(value); - Value = value; - Instances.Add(value, this); - } - - /// - /// Finalizes an instance of the class. - /// - // @Nao Probably never call, it self reference. Maybe an future issue doing memory leek - // it will get call only after the instance get remove form Instances - ~Singleton() => Instances.Remove(Value); - /// /// Gets the relative value. /// - public static T Instance => Instances.FirstOrDefault(@object => @object.Key.GetType() == typeof(T)).Value; - - /// - /// Gets the singleton value. - /// - internal T Value { get; private set; } - - /// - /// Converts the given instance into . - /// - /// The instance to convert. - public static implicit operator T(Singleton instance) => instance?.Value; + public static T Instance { get; private set; } /// /// Tries to get the relative value. /// - /// The type of the object. /// The object instance. /// if the object instance is not null and can be casted as the specified type; otherwise, . - // @nao It will return the Instance. - // But with this curent class definition you can register derived class of T. - // They can be registred but not retrived. Also why make this method generic if this not to get - // other value than the instance. - public static bool TryGet(out TObject instance) - where TObject : class => (instance = Instance as TObject) is not null; + public static bool TryGet(out T instance) + => (instance = Instance) is not null; - /// - public static void Create(T @object) => new Singleton(@object); + /// + /// Define the value of the if not already defined. + /// If you want to replace first call . + /// + /// The object use to create the singleton. + public static void Create(T @object) + { + if (Instance is not null) + return; + Instance = @object; + } /// - /// Destroys the given instance. + /// Set to null if is the same. /// /// The object to destroy. /// if the instance was destroyed; otherwise, . public static bool Destroy(T @object) { - return Instances.Remove(@object); + if (Instance == @object) + { + Instance = null; + return true; + } + + return false; + } + + /// + /// Set to null . + /// + public static void Destroy() + { + Instance = null; } } } \ No newline at end of file diff --git a/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs b/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs index 421c64de73..217f1af12d 100644 --- a/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs +++ b/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs @@ -274,7 +274,7 @@ protected virtual void OnReloaded() /// protected virtual void CreateInstance() { - Singleton.Create(this); + // Singleton.Create(this); } /// @@ -283,7 +283,10 @@ protected virtual void CreateInstance() protected virtual void DestroyInstance() { Config = null; - Singleton.Destroy(this); + + // @Nao If by default the class set it self inside of a singleton. + // You will need to use a TSlef. This will avoid rewriting over an other class. + // Singleton.Destroy(this); } /// From 4d736ff87054754fe16be5d20675a9e92f2c9ade Mon Sep 17 00:00:00 2001 From: Valentin Arthur Thomas Date: Sat, 14 Sep 2024 02:46:25 +0200 Subject: [PATCH 08/16] Rewrite EvaluateEndingConditions varaibles names. --- .../API/Features/CustomRoles/RoleBehaviour.cs | 18 ++++++++---------- Exiled.CustomModules/API/Features/World.cs | 2 +- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs b/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs index 4543396bf8..08ef7c8b90 100644 --- a/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs +++ b/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs @@ -202,23 +202,21 @@ public virtual bool EvaluateEndingConditions() case 1: return true; default: - // @Nao, i mouve repeted code here to see better - // For me this following condition are always true. - // Expete if it existe a case where there is -1 Chaos summaryInfo = World.Get().SummaryInfo; - bool hasChaos = summaryInfo.ChaosInsurgency <= 0; - bool hasFondtion = summaryInfo.FoundationForces <= 0; - bool hasScp = summaryInfo.ChaosInsurgency <= 0; + bool noChaos = summaryInfo.ChaosInsurgency <= 0; + bool noFondation = summaryInfo.FoundationForces <= 0; + bool noScp = summaryInfo.ChaosInsurgency <= 0; - if (CustomRole.TeamsOwnership.Contains(Team.SCPs) && hasFondtion && hasChaos) - return true; // @Nao Why true, that mean the round can end, but if there is some fondation left, the round should not end... + if (noFondation && noChaos && CustomRole.TeamsOwnership.Contains(Team.SCPs)) + return true; - if (CustomRole.TeamsOwnership.Any(team => team is Team.ClassD or Team.ChaosInsurgency) && hasFondtion && hasScp) + if (noFondation && noScp && CustomRole.TeamsOwnership.Any(team => team is Team.ClassD or Team.ChaosInsurgency)) return true; - if (CustomRole.TeamsOwnership.Any(team => team is Team.FoundationForces or Team.Scientists) && hasChaos && hasScp) + if (noChaos && noScp && CustomRole.TeamsOwnership.Any(team => team is Team.FoundationForces or Team.Scientists)) return true; + return false; } } diff --git a/Exiled.CustomModules/API/Features/World.cs b/Exiled.CustomModules/API/Features/World.cs index e78320654d..6fc2b42b9d 100644 --- a/Exiled.CustomModules/API/Features/World.cs +++ b/Exiled.CustomModules/API/Features/World.cs @@ -234,7 +234,7 @@ private void OnEndingRound(EndingRoundEventArgs ev) if (!ev.IsAllowed) return; - foreach (CustomRole role in CustomRole.Get(cr => cr.TeamsOwnership.Length != 1 && cr.Instances > 1)) + foreach (CustomRole role in CustomRole.Get(cr => cr.Instances > 1)) { if (role.Owners.First().RoleBehaviour.EvaluateEndingConditions()) continue; From 1e9555c34bf29ae17843099db454548f699aa09a Mon Sep 17 00:00:00 2001 From: Valentin Arthur Thomas Date: Sat, 14 Sep 2024 05:49:19 +0200 Subject: [PATCH 09/16] Remove dtor self removing from a list inside of the dtor --- Exiled.API/Features/Core/ConstProperty.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Exiled.API/Features/Core/ConstProperty.cs b/Exiled.API/Features/Core/ConstProperty.cs index f6c48f6317..20fc8ba15f 100644 --- a/Exiled.API/Features/Core/ConstProperty.cs +++ b/Exiled.API/Features/Core/ConstProperty.cs @@ -48,9 +48,6 @@ public ConstProperty(T constantValue, Type[] typesToPatch, MethodInfo[] skipMeth /// ~ConstProperty() { - // @Nao Never be call, it need to remove it reference from List beffor the dtor call - List.Remove(this); - foreach (MethodInfo methodInfo in PatchedMethods) { try From 9e3ef360ac7fc1feb1ab2ec0f692076af418bedf Mon Sep 17 00:00:00 2001 From: Valentin Arthur Thomas Date: Sat, 14 Sep 2024 06:01:03 +0200 Subject: [PATCH 10/16] Add check returning type for 'get'reflection call in EBehaviour --- Exiled.API/Features/Core/Generic/EBehaviour.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Exiled.API/Features/Core/Generic/EBehaviour.cs b/Exiled.API/Features/Core/Generic/EBehaviour.cs index 913d05f62c..7d95273864 100644 --- a/Exiled.API/Features/Core/Generic/EBehaviour.cs +++ b/Exiled.API/Features/Core/Generic/EBehaviour.cs @@ -59,18 +59,15 @@ protected EBehaviour() /// protected virtual void FindOwner() { - // @Nao T is a GameEntity, why not add in inisde of it the method a abstract. - // Or create an specific Interaface requesting to implement this method MethodInfo method = typeof(T).GetMethod("Get", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(GameObject) }, null); - if (method != null) - { - Owner = (T)method.Invoke(null, new object[] { Base }); - } - else - { + if (method == null) throw new MissingMethodException($"Method 'Get(GameObject)' not found in class '{typeof(T).Name}'."); - } + + if (typeof(T).IsAssignableFrom(method.ReturnType)) + throw new MissingMethodException($"Method 'Get(GameObject)' in class '{typeof(T).Name}' do not return an instance of {typeof(T).Name} but {method.ReturnType}."); + + Owner = (T)method.Invoke(null, new object[] { Base }); } /// From 934754ba04dfa389399b60d8dcc88a09658d047d Mon Sep 17 00:00:00 2001 From: Valentin Arthur Thomas Date: Sat, 14 Sep 2024 06:27:58 +0200 Subject: [PATCH 11/16] add cctor and rewrite the ctor of UniqueUnmanagedEnumClass --- .../Core/Generic/UniqueUnmanagedEnumClass.cs | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs b/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs index 02608ebbdf..311564eb6c 100644 --- a/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs +++ b/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs @@ -28,39 +28,33 @@ public abstract class UniqueUnmanagedEnumClass : IComparable, where TObject : UniqueUnmanagedEnumClass { private static SortedList values; - private static int nextValue = int.MinValue; + private static long nextValue; private static bool isDefined; private string name; + static UniqueUnmanagedEnumClass() + { + values = new SortedList(); + nextValue = (int)typeof(TSource).GetField("MinValue").GetValue(null); + } + /// /// Initializes a new instance of the class. /// - public UniqueUnmanagedEnumClass() + internal protected UniqueUnmanagedEnumClass() { - values ??= new(); - TypeCode code = Convert.GetTypeCode(typeof(TSource).GetField("MinValue").GetValue(null)); - - // @Nao If the value is not an Uxxxxx it will get an overflow. Like if it use an Int16, Byte or SByte - // Maybe use a long for nextValue and do "nextValue = typeof(TSource).GetField("MinValue").GetValue(null)" - // it also be a struct containing only unmanged struct. The best solution is proably to - // look for upper version of the framwork to resolve this with the new numeric interfaces. - if (code is TypeCode.UInt16 or TypeCode.UInt32 or TypeCode.UInt64) - nextValue = 0; - lock (values) { TSource value; do { - value = (TSource)Convert.ChangeType(nextValue++, code); + value = (TSource)Convert.ChangeType(nextValue++, Value.GetTypeCode()); } while (values.ContainsKey(value)); Value = value; - // @nao If the value is register when a new instance is created. - // Maybe put the ctor in protected to avoid exteranal code (outisde the enum) to create new values values.Add(value, (TObject)this); } } From 4439cfa9d92af95978b47bb1c08fe6ce75d0dd8c Mon Sep 17 00:00:00 2001 From: Valentin Arthur Thomas Date: Sat, 14 Sep 2024 06:30:37 +0200 Subject: [PATCH 12/16] Add comment inside the UniqueUnmanagedEnumClass --- Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs b/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs index 311564eb6c..5a8e64cb65 100644 --- a/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs +++ b/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs @@ -27,9 +27,10 @@ public abstract class UniqueUnmanagedEnumClass : IComparable, where TSource : unmanaged, IComparable, IFormattable, IConvertible, IComparable, IEquatable where TObject : UniqueUnmanagedEnumClass { - private static SortedList values; + // @Nao + private static SortedList values; // Binary search and comparing the index with the value can help to find the gap value faster private static long nextValue; - private static bool isDefined; + private static bool isDefined; // Can create issue for naming, no indirect heritage possible private string name; From c62918bc0ada64b586b2665fe35684a1869d664d Mon Sep 17 00:00:00 2001 From: Valentin Arthur Thomas Date: Sat, 14 Sep 2024 07:24:24 +0200 Subject: [PATCH 13/16] Remove IComparable and Rewrite Euqality/Comparaison form UnmanagedEnumClass, UniqueUnmagedEnumClass and EnumClass if a IComparable class i needed the Comparer.Default provide one. --- Exiled.API/Features/Core/Generic/EnumClass.cs | 43 +++++++++++------ .../Core/Generic/UniqueUnmanagedEnumClass.cs | 42 +++++++++++------ .../Core/Generic/UnmanagedEnumClass.cs | 46 ++++++++++++------- 3 files changed, 88 insertions(+), 43 deletions(-) diff --git a/Exiled.API/Features/Core/Generic/EnumClass.cs b/Exiled.API/Features/Core/Generic/EnumClass.cs index 951275ff9a..bf76de212a 100644 --- a/Exiled.API/Features/Core/Generic/EnumClass.cs +++ b/Exiled.API/Features/Core/Generic/EnumClass.cs @@ -23,7 +23,7 @@ namespace Exiled.API.Features.Core.Generic /// /// The type of the source object to handle the instance of. /// The type of the child object to handle the instance of. - public abstract class EnumClass : IComparable, IEquatable, IComparable, IComparer, IEnumClass + public abstract class EnumClass : IComparable, IEquatable, IComparable, IEnumClass where TSource : Enum where TObject : EnumClass { @@ -205,17 +205,29 @@ public static bool SafeCast(IEnumerable values, out IEnumerable /// Determines whether the specified object is equal to the current object. ///
- /// The object to compare. + /// The object to compare. /// if the object was equal; otherwise, . - public override bool Equals(object obj) => - obj != null && (obj is TSource value ? Value.Equals(value) : obj is TObject derived && Value.Equals(derived.Value)); + public bool Equals(TSource other) => Value.Equals(other); /// /// Determines whether the specified object is equal to the current object. /// /// The object to compare. /// if the object was equal; otherwise, . - public bool Equals(TObject other) => Value.Equals(other.Value); + public bool Equals(TObject other) + { + if (other is null) + return false; + + return Equals(other.Value); + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// The object to compare. + /// if the object was equal; otherwise, . + public override bool Equals(object obj) => obj is TSource value ? Equals(value) : Equals(obj as TObject); /// /// Returns a the 32-bit signed hash code of the current object instance. @@ -235,36 +247,41 @@ public override bool Equals(object obj) => /// Zero This instance occurs in the same position in the sort order as other. /// Greater than zero This instance follows other in the sort order. /// - public int CompareTo(TObject other) => Value.CompareTo(other.Value); + public int CompareTo(TSource other) => Comparer.Default.Compare(Value, other); /// /// Compares the current instance with another object of the same type and returns /// an integer that indicates whether the current instance precedes, follows, or /// occurs in the same position in the sort order as the other object. /// - /// An object to compare with this instance. + /// An object to compare with this instance. /// /// A value that indicates the relative order of the objects being compared. /// The return value has these meanings: Value Meaning Less than zero This instance precedes other in the sort order. /// Zero This instance occurs in the same position in the sort order as other. /// Greater than zero This instance follows other in the sort order. /// - public int CompareTo(object obj) => - obj == null ? -1 : obj is TSource value ? Value.CompareTo(value) : obj is TObject derived ? Value.CompareTo(derived.Value) : -1; + public int CompareTo(TObject other) + { + if (other is null) + return 1; + + return CompareTo(other.Value); + } /// - /// Compares the specified object instance with another object of the same type and returns + /// Compares the current instance with another object of the same type and returns /// an integer that indicates whether the current instance precedes, follows, or /// occurs in the same position in the sort order as the other object. /// - /// An object to compare. - /// Another object to compare. + /// An object to compare with this instance. /// /// A value that indicates the relative order of the objects being compared. /// The return value has these meanings: Value Meaning Less than zero This instance precedes other in the sort order. /// Zero This instance occurs in the same position in the sort order as other. /// Greater than zero This instance follows other in the sort order. /// - public int Compare(TObject x, TObject y) => x == null ? -1 : y == null ? 1 : x.Value.CompareTo(y.Value); + public int CompareTo(object obj) => obj is TSource value ? CompareTo(value) : CompareTo(obj as TObject); + } } diff --git a/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs b/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs index 5a8e64cb65..453aa1a823 100644 --- a/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs +++ b/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs @@ -23,7 +23,7 @@ namespace Exiled.API.Features.Core.Generic /// /// The type of the source object to handle the instance of. /// The type of the child object to handle the instance of. - public abstract class UniqueUnmanagedEnumClass : IComparable, IEquatable, IComparable, IComparer, IConvertible, IEnumClass + public abstract class UniqueUnmanagedEnumClass : IComparable, IEquatable, IComparable, IConvertible, IEnumClass where TSource : unmanaged, IComparable, IFormattable, IConvertible, IComparable, IEquatable where TObject : UniqueUnmanagedEnumClass { @@ -229,17 +229,29 @@ public static TObject Parse(string obj) /// /// Determines whether the specified object is equal to the current object. /// - /// The object to compare. + /// The object to compare. /// if the object was equal; otherwise, . - public override bool Equals(object obj) => - obj != null && (obj is TSource value ? Value.Equals(value) : obj is TObject derived && Value.Equals(derived.Value)); + public bool Equals(TSource other) => Value.Equals(other); /// /// Determines whether the specified object is equal to the current object. /// /// The object to compare. /// if the object was equal; otherwise, . - public bool Equals(TObject other) => Value.Equals(other.Value); + public bool Equals(TObject other) + { + if (other is null) + return false; + + return Equals(other.Value); + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// The object to compare. + /// if the object was equal; otherwise, . + public override bool Equals(object obj) => obj is TSource value ? Equals(value) : Equals(obj as TObject); /// /// Returns a the 32-bit signed hash code of the current object instance. @@ -259,37 +271,41 @@ public override bool Equals(object obj) => /// Zero This instance occurs in the same position in the sort order as other. /// Greater than zero This instance follows other in the sort order. /// - public int CompareTo(TObject other) => Value.CompareTo(other.Value); + public int CompareTo(TSource other) => Comparer.Default.Compare(Value, other); /// /// Compares the current instance with another object of the same type and returns /// an integer that indicates whether the current instance precedes, follows, or /// occurs in the same position in the sort order as the other object. /// - /// An object to compare with this instance. + /// An object to compare with this instance. /// /// A value that indicates the relative order of the objects being compared. /// The return value has these meanings: Value Meaning Less than zero This instance precedes other in the sort order. /// Zero This instance occurs in the same position in the sort order as other. /// Greater than zero This instance follows other in the sort order. /// - public int CompareTo(object obj) => - obj == null ? -1 : obj is TSource value ? Value.CompareTo(value) : obj is TObject derived ? Value.CompareTo(derived.Value) : -1; + public int CompareTo(TObject other) + { + if (other is null) + return 1; + + return CompareTo(other.Value); + } /// - /// Compares the specified object instance with another object of the same type and returns + /// Compares the current instance with another object of the same type and returns /// an integer that indicates whether the current instance precedes, follows, or /// occurs in the same position in the sort order as the other object. /// - /// An object to compare. - /// Another object to compare. + /// An object to compare with this instance. /// /// A value that indicates the relative order of the objects being compared. /// The return value has these meanings: Value Meaning Less than zero This instance precedes other in the sort order. /// Zero This instance occurs in the same position in the sort order as other. /// Greater than zero This instance follows other in the sort order. /// - public int Compare(TObject x, TObject y) => x == null ? -1 : y == null ? 1 : x.Value.CompareTo(y.Value); + public int CompareTo(object obj) => obj is TSource value ? CompareTo(value) : CompareTo(obj as TObject); /// TypeCode IConvertible.GetTypeCode() => Value.GetTypeCode(); diff --git a/Exiled.API/Features/Core/Generic/UnmanagedEnumClass.cs b/Exiled.API/Features/Core/Generic/UnmanagedEnumClass.cs index 8cb1d22c0c..96a0338592 100644 --- a/Exiled.API/Features/Core/Generic/UnmanagedEnumClass.cs +++ b/Exiled.API/Features/Core/Generic/UnmanagedEnumClass.cs @@ -23,8 +23,7 @@ namespace Exiled.API.Features.Core.Generic /// /// The type of the source object to handle the instance of. /// The type of the child object to handle the instance of. - // @Nao you can use Comparer.Default, it will result as using the IComparable.Compare - public abstract class UnmanagedEnumClass : IComparable, IEquatable, IComparable, IComparer, IConvertible, IEnumClass + public abstract class UnmanagedEnumClass : IComparable, IEquatable, IComparable, IConvertible, IEnumClass where TSource : unmanaged, IComparable, IFormattable, IConvertible, IComparable, IEquatable where TObject : UnmanagedEnumClass { @@ -213,17 +212,29 @@ public static TObject Parse(string obj) /// /// Determines whether the specified object is equal to the current object. /// - /// The object to compare. + /// The object to compare. /// if the object was equal; otherwise, . - public override bool Equals(object obj) => - obj != null && (obj is TSource value ? Value.Equals(value) : obj is TObject derived && Value.Equals(derived.Value)); + public bool Equals(TSource other) => Value.Equals(other); /// /// Determines whether the specified object is equal to the current object. /// /// The object to compare. /// if the object was equal; otherwise, . - public bool Equals(TObject other) => Value.Equals(other.Value); + public bool Equals(TObject other) + { + if (other is null) + return false; + + return Equals(other.Value); + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// The object to compare. + /// if the object was equal; otherwise, . + public override bool Equals(object obj) => obj is TSource value ? Equals(value) : Equals(obj as TObject); /// /// Returns a the 32-bit signed hash code of the current object instance. @@ -243,40 +254,41 @@ public override bool Equals(object obj) => /// Zero This instance occurs in the same position in the sort order as other. /// Greater than zero This instance follows other in the sort order. /// - public int CompareTo(TObject other) => Value.CompareTo(other.Value); + public int CompareTo(TSource other) => Value.CompareTo(other); /// /// Compares the current instance with another object of the same type and returns /// an integer that indicates whether the current instance precedes, follows, or /// occurs in the same position in the sort order as the other object. /// - /// An object to compare with this instance. + /// An object to compare with this instance. /// /// A value that indicates the relative order of the objects being compared. /// The return value has these meanings: Value Meaning Less than zero This instance precedes other in the sort order. /// Zero This instance occurs in the same position in the sort order as other. /// Greater than zero This instance follows other in the sort order. /// - // @Nao if x and y is null it should return, 0. - public int CompareTo(object obj) => - obj == null ? -1 : obj is TSource value ? Value.CompareTo(value) : obj is TObject derived ? Value.CompareTo(derived.Value) : -1; + public int CompareTo(TObject other) + { + if (other is null) + return 1; + + return CompareTo(other.Value); + } /// - /// Compares the specified object instance with another object of the same type and returns + /// Compares the current instance with another object of the same type and returns /// an integer that indicates whether the current instance precedes, follows, or /// occurs in the same position in the sort order as the other object. /// - /// An object to compare. - /// Another object to compare. + /// An object to compare with this instance. /// /// A value that indicates the relative order of the objects being compared. /// The return value has these meanings: Value Meaning Less than zero This instance precedes other in the sort order. /// Zero This instance occurs in the same position in the sort order as other. /// Greater than zero This instance follows other in the sort order. /// - // @Nao if x and y is equal to null it should return, 0. - // Maybe use the default comparator, it handle it by default and if needed it can be replaced by a custom one. - public int Compare(TObject x, TObject y) => x == null ? -1 : y == null ? 1 : x.Value.CompareTo(y.Value); + public int CompareTo(object obj) => obj is TSource value ? CompareTo(value) : CompareTo(obj as TObject); /// TypeCode IConvertible.GetTypeCode() => Value.GetTypeCode(); From c6a7cae094151e03e05c70eee9dad7ba60189efe Mon Sep 17 00:00:00 2001 From: Valentin Arthur Thomas Date: Sat, 14 Sep 2024 07:31:17 +0200 Subject: [PATCH 14/16] CustomTeam only spawn only if allowed --- .../API/Features/CustomRoles/CustomTeam.cs | 1 - Exiled.CustomModules/API/Features/RespawnManager.cs | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Exiled.CustomModules/API/Features/CustomRoles/CustomTeam.cs b/Exiled.CustomModules/API/Features/CustomRoles/CustomTeam.cs index 22fafe7ced..584c8d54b7 100644 --- a/Exiled.CustomModules/API/Features/CustomRoles/CustomTeam.cs +++ b/Exiled.CustomModules/API/Features/CustomRoles/CustomTeam.cs @@ -114,7 +114,6 @@ public abstract class CustomTeam : CustomModule /// This property provides access to a curated collection of objects, encapsulating all available custom role within the context of units. ///
The collection is designed to be both queried and modified as needed to accommodate dynamic scenarios within the game architecture. /// - // @Nao, adding the possiblity to have custom roles public virtual IEnumerable Units { get; set; } = new uint[] { }; /// diff --git a/Exiled.CustomModules/API/Features/RespawnManager.cs b/Exiled.CustomModules/API/Features/RespawnManager.cs index be8b6f45a3..7366dc9ca7 100644 --- a/Exiled.CustomModules/API/Features/RespawnManager.cs +++ b/Exiled.CustomModules/API/Features/RespawnManager.cs @@ -174,7 +174,9 @@ private void OnPreRespawningTeam(PreRespawningTeamEventArgs ev) { PreviousKnownTeam = NextKnownTeam; - // @Nao are you sur that is "or SpawnableTeamType" and not "or not SpawnableTeamType" + if (!ev.IsAllowed) + return; + if (NextKnownTeam is null or SpawnableTeamType) return; @@ -188,10 +190,6 @@ private void OnPreRespawningTeam(PreRespawningTeamEventArgs ev) return; } - // @Nao, it cool to use the event system for this. But if an other plugin - // allready say "ev.IsAllowed = false". it will get ignore - // and the team will still spawn... - // And post event shoold be here to spawn. Or an EventHandler handling last ev.IsAllowed = false; Spawn(); Respawning.RespawnManager.Singleton?.RestartSequence(); From cc01fdfc6fda73aead9874c2063fac8f4e6c1d66 Mon Sep 17 00:00:00 2001 From: Valentin Arthur Thomas Date: Sun, 22 Sep 2024 20:51:11 +0200 Subject: [PATCH 15/16] remove ligne --- Exiled.API/Features/Core/Generic/EnumClass.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Exiled.API/Features/Core/Generic/EnumClass.cs b/Exiled.API/Features/Core/Generic/EnumClass.cs index bf76de212a..9b8a7457b7 100644 --- a/Exiled.API/Features/Core/Generic/EnumClass.cs +++ b/Exiled.API/Features/Core/Generic/EnumClass.cs @@ -282,6 +282,5 @@ public int CompareTo(TObject other) /// Greater than zero This instance follows other in the sort order. /// public int CompareTo(object obj) => obj is TSource value ? CompareTo(value) : CompareTo(obj as TObject); - } } From f56d7612853dc1654f3596b606abd92f6e4e31ca Mon Sep 17 00:00:00 2001 From: Valentin Arthur Thomas Date: Fri, 15 Nov 2024 08:33:34 +0100 Subject: [PATCH 16/16] Remove comment @Nao, add inheritdoc for player cassie methods --- .../Core/Generic/UniqueUnmanagedEnumClass.cs | 6 +++--- Exiled.API/Features/Player.cs | 18 +++++++++--------- .../VirtualAssemblies/VirtualPlugin.cs | 4 ---- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs b/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs index 453aa1a823..b2dacf940f 100644 --- a/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs +++ b/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs @@ -15,6 +15,7 @@ namespace Exiled.API.Features.Core.Generic using Exiled.API.Features.Core.Generic.Pools; using Exiled.API.Interfaces; using LiteNetLib.Utils; + using YamlDotNet.Core.Tokens; using YamlDotNet.Serialization; /// @@ -27,10 +28,9 @@ public abstract class UniqueUnmanagedEnumClass : IComparable, where TSource : unmanaged, IComparable, IFormattable, IConvertible, IComparable, IEquatable where TObject : UniqueUnmanagedEnumClass { - // @Nao - private static SortedList values; // Binary search and comparing the index with the value can help to find the gap value faster + private static SortedList values; private static long nextValue; - private static bool isDefined; // Can create issue for naming, no indirect heritage possible + private static bool isDefined; private string name; diff --git a/Exiled.API/Features/Player.cs b/Exiled.API/Features/Player.cs index e40fd06448..e80f1a5191 100644 --- a/Exiled.API/Features/Player.cs +++ b/Exiled.API/Features/Player.cs @@ -4049,10 +4049,10 @@ public void ChangeAppearance(RoleTypeId type, IEnumerable playersToAffec /// /// Send CASSIE announcement that only can hear. /// - /// Announcement words. - /// Same on 's isHeld. - /// Same on 's isNoisy. - /// Same on 's isSubtitles. + /// + /// + /// + /// public void PlayCassieAnnouncement(string words, bool makeHold = false, bool makeNoise = true, bool isSubtitles = false) { foreach (RespawnEffectsController controller in RespawnEffectsController.AllControllers) @@ -4067,11 +4067,11 @@ public void PlayCassieAnnouncement(string words, bool makeHold = false, bool mak /// /// Send CASSIE announcement with custom subtitles for translation that only can hear and see it. /// - /// The message to be reproduced. - /// The translation should be show in the subtitles. - /// Same on 's isHeld. - /// Same on 's isNoisy. - /// Same on 's isSubtitles. + /// + /// + /// + /// + /// // @Nao, why not inherit the documentation for the param ? public void SendCassieAnnouncement(string words, string translation, bool makeHold = false, bool makeNoise = true, bool isSubtitles = true) { diff --git a/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs b/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs index 217f1af12d..186bc785ec 100644 --- a/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs +++ b/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs @@ -283,10 +283,6 @@ protected virtual void CreateInstance() protected virtual void DestroyInstance() { Config = null; - - // @Nao If by default the class set it self inside of a singleton. - // You will need to use a TSlef. This will avoid rewriting over an other class. - // Singleton.Destroy(this); } ///