Skip to content

Commit

Permalink
Fixed:
Browse files Browse the repository at this point in the history
 - `RespawnManager`
 - Custom Team commands
 - `InventoryManager` being always null
 - `ModulePointer` not being interpreted with any type of collections

Changes:
- Increased the dispatch operation delay to 2.5 seconds.
  • Loading branch information
NaoUnderscore committed Oct 7, 2024
1 parent 68ec3a6 commit bad7226
Show file tree
Hide file tree
Showing 16 changed files with 196 additions and 102 deletions.
28 changes: 28 additions & 0 deletions Exiled.API/Extensions/ReflectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,5 +142,33 @@ public static Dictionary<string, object> GetFieldsWithValue(this object obj, Bin

return propertyValues;
}

/// <summary>
/// Copies elements from the source list to the target list by invoking the Add method on the target list.
/// </summary>
/// <param name="targetList">The target list where elements will be added.</param>
/// <param name="sourceList">The source list containing the elements to be copied.</param>
/// <remarks>
/// The method uses reflection to find and invoke the Add method of the target list.
/// </remarks>
public static void CopyListElements(this object targetList, object sourceList)
{
foreach (object item in (System.Collections.IEnumerable)sourceList)
targetList.GetType().GetMethod("Add").Invoke(targetList, new[] { item });
}

/// <summary>
/// Copies elements from the source dictionary to the target dictionary by invoking the Add method on the target dictionary.
/// </summary>
/// <param name="targetDict">The target dictionary where key-value pairs will be added.</param>
/// <param name="sourceDict">The source dictionary containing the key-value pairs to be copied.</param>
/// <remarks>
/// The method uses reflection to find and invoke the Add method of the target dictionary.
/// </remarks>
public static void CopyDictionaryElements(this object targetDict, object sourceDict)
{
foreach (System.Collections.DictionaryEntry entry in (System.Collections.IDictionary)sourceDict)
targetDict.GetType().GetMethod("Add").Invoke(targetDict, new[] { entry.Key, entry.Value });
}
}
}
12 changes: 0 additions & 12 deletions Exiled.CustomModules/API/Features/CustomItems/CustomItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,18 +236,6 @@ public static bool TryGet(object id, out CustomItem customItem)
return customItem is not null;
}

/// <summary>
/// Tries to retrieve a <see cref="CustomItem"/> instance based on the specified custom item id.
/// </summary>
/// <param name="id">The custom item id to retrieve.</param>
/// <param name="customItem">The retrieved <see cref="CustomItem"/> instance, if successful; otherwise, <see langword="null"/>.</param>
/// <returns><see langword="true"/> if the retrieval is successful; otherwise, <see langword="false"/>.</returns>
public static bool TryGet(uint id, out CustomItem customItem)
{
customItem = Get(id);
return customItem is not null;
}

/// <summary>
/// Tries to retrieve a <see cref="CustomItem"/> instance based on the specified item name.
/// </summary>
Expand Down
28 changes: 14 additions & 14 deletions Exiled.CustomModules/API/Features/CustomRoles/CustomTeam.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ public virtual bool EvaluateConditions
}
}

return (RequiredCustomTeamToSpawn > 0 && CustomTeam.TryGet(RequiredCustomTeamToSpawn, out CustomTeam team) && !team.Owners.IsEmpty()) ||
return (RequiredCustomTeamToSpawn > 0 && TryGet(RequiredCustomTeamToSpawn, out CustomTeam team) && !team.Owners.IsEmpty()) ||
(RequiredCustomRoleToSpawn > 0 && CustomRole.TryGet(RequiredCustomRoleToSpawn, out CustomRole role) && !role.Owners.IsEmpty());
}
}
Expand Down Expand Up @@ -283,45 +283,45 @@ public virtual bool EvaluateConditions
/// </summary>
/// <param name="id">The id to check.</param>
/// <returns>The <see cref="CustomTeam"/> matching the search, or <see langword="null"/> if not registered.</returns>
public static CustomTeam Get(uint id) => IdLookupTable[id];
public static CustomTeam Get(uint id) => IdLookupTable.ContainsKey(id) ? IdLookupTable[id] : null;

/// <summary>
/// Gets a <see cref="CustomTeam"/> instance based on the specified name.
/// </summary>
/// <param name="name">The specified name.</param>
/// <returns>The <see cref="CustomTeam"/> matching the search, or <see langword="null"/> if not registered.</returns>
public static CustomTeam Get(string name) => NameLookupTable[name];
public static CustomTeam Get(string name) => NameLookupTable.ContainsKey(name) ? NameLookupTable[name] : null;

/// <summary>
/// Gets a <see cref="CustomTeam"/> instance associated with a specific <see cref="Player"/>.
/// </summary>
/// <param name="player">The <see cref="Player"/> to check.</param>
/// <returns>The <see cref="CustomTeam"/> matching the search, or <see langword="null"/> if not registered.</returns>
public static CustomTeam Get(Player player) => !PlayersValue.TryGetValue(player, out CustomTeam customTeam) ? null : customTeam;
public static CustomTeam Get(Player player) => PlayersValue.TryGetValue(player, out CustomTeam customTeam) ? customTeam : null;

/// <summary>
/// Attempts to retrieve a <see cref="CustomTeam"/> based on the provided id or <see cref="UUCustomTeamType"/>.
/// </summary>
/// <param name="id">The id or <see cref="UUCustomTeamType"/> of the custom team.</param>
/// <param name="customTeam">When this method returns, contains the <see cref="CustomTeam"/> associated with the specified id, if the id was found; otherwise, <see langword="null"/>.</param>
/// <returns><see langword="true"/> if a <see cref="CustomTeam"/> was found; otherwise, <see langword="false"/>.</returns>
public static bool TryGet(object id, out CustomTeam customTeam) => customTeam = Get(id);

/// <summary>
/// Tries to get a <see cref="CustomTeam"/> given the specified id.
/// </summary>
/// <param name="id">The id to look for.</param>
/// <param name="customTeam">The found <see cref="CustomTeam"/>, null if not registered.</param>
/// <returns><see langword="true"/> if a <see cref="CustomTeam"/> is found; otherwise, <see langword="false"/>.</returns>
public static bool TryGet(uint id, out CustomTeam customTeam) => customTeam = Get(id);
public static bool TryGet(object id, out CustomTeam customTeam)
{
customTeam = Get(id);
return customTeam is not null;
}

/// <summary>
/// Tries to get a <see cref="CustomTeam"/> given the specified name.
/// </summary>
/// <param name="name">The name to look for.</param>
/// <param name="customTeam">The found <see cref="CustomTeam"/>, null if not registered.</param>
/// <returns><see langword="true"/> if a <see cref="CustomTeam"/> is found; otherwise, <see langword="false"/>.</returns>
public static bool TryGet(string name, out CustomTeam customTeam) => customTeam = Get(name);
public static bool TryGet(string name, out CustomTeam customTeam)
{
customTeam = Get(name);
return customTeam is not null;
}

/// <summary>
/// Tries to get a <see cref="CustomTeam"/> belonging to the specified <see cref="Player"/>.
Expand Down
59 changes: 31 additions & 28 deletions Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ namespace Exiled.CustomModules.API.Features.CustomRoles
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

using Exiled.API.Enums;
Expand Down Expand Up @@ -112,14 +111,14 @@ static bool EvalSpawnPoint(IEnumerable<SpawnPoint> spawnpoints, out Vector3 outP
}

/// <summary>
/// Gets the <see cref="InventoryManager"/>.
/// Gets or sets the <see cref="InventoryManager"/>.
/// </summary>
protected virtual InventoryManager Inventory { get; }
public virtual InventoryManager Inventory { get; set; }

/// <summary>
/// Gets or sets the <see cref="RoleTypeId"/> of the fake appearance applied by this <see cref="RoleBehaviour"/> component.
/// </summary>
protected virtual RoleTypeId FakeAppearance
public virtual RoleTypeId FakeAppearance
{
get => fakeAppearance;
set
Expand All @@ -132,22 +131,27 @@ protected virtual RoleTypeId FakeAppearance
/// <summary>
/// Gets or sets a <see cref="IEnumerable{T}"/> of <see cref="EffectType"/> which should be permanently given to the player.
/// </summary>
protected virtual IEnumerable<Effect> PermanentEffects { get; set; }
public virtual IEnumerable<Effect> PermanentEffects { get; set; }

/// <summary>
/// Gets a value indicating whether <see cref="FakeAppearance"/> should be used.
/// </summary>
protected virtual bool UseFakeAppearance { get; }
public virtual bool UseFakeAppearance { get; }

/// <summary>
/// Gets a value indicating whether an existing spawnpoint should be used.
/// </summary>
protected virtual bool UseCustomSpawnpoint => true;
public virtual bool UseCustomSpawnpoint => true;

/// <summary>
/// Gets or sets a value indicating whether the effects should persistently remain active.
/// </summary>
protected virtual bool SustainEffects { get; set; }
public virtual bool SustainEffects { get; set; }

/// <summary>
/// Gets or sets the the escape settings.
/// </summary>
public virtual List<EscapeSettings> EscapeSettings { get; set; } = new();

/// <summary>
/// Gets or sets the <see cref="RoleTypeId"/> of this <see cref="RoleBehaviour"/> component.
Expand All @@ -159,11 +163,6 @@ protected virtual RoleTypeId FakeAppearance
/// </summary>
protected float CurrentSpeed { get; private set; }

/// <summary>
/// Gets or sets the the escape settings.
/// </summary>
protected virtual List<EscapeSettings> EscapeSettings { get; set; } = new();

/// <summary>
/// Gets or sets the <see cref="TDynamicEventDispatcher{T}"/> handling all bound delegates to be fired before escaping.
/// </summary>
Expand Down Expand Up @@ -316,20 +315,29 @@ protected override void PostInitialize()
if (Settings.HideInfoArea)
Owner.InfoArea = Owner.InfoArea.RemoveFlags(PlayerInfoArea.UnitName, PlayerInfoArea.Role);

if (isHuman && !Settings.PreserveInventory)
if (isHuman && !Settings.PreserveInventory && Inventory is not null)
{
Owner.ClearInventory();

if (Inventory.Items is not null)
Owner.AddItem(Inventory.Items);
if (Inventory.Chance == 0 || (Inventory.Chance > 0 && Inventory.Chance.EvaluateProbability()))
{
if (Inventory.Items is not null)
Owner.AddItem(Inventory.Items);

if (Inventory.CustomItems is not null)
Owner.Cast<Pawn>().AddItem(Inventory.CustomItems);

if (Inventory.CustomItems is not null)
Owner.Cast<Pawn>().AddItem(Inventory.CustomItems);
if (Inventory.AmmoBox is not null)
{
foreach (KeyValuePair<AmmoType, ushort> kvp in Inventory.AmmoBox)
Owner.AddAmmo(kvp.Key, kvp.Value);
}

if (Inventory.AmmoBox is not null)
{
foreach (KeyValuePair<AmmoType, ushort> kvp in Inventory.AmmoBox)
Owner.AddAmmo(kvp.Key, kvp.Value);
if (Inventory.CustomAmmoBox is not null)
{
foreach (KeyValuePair<uint, ushort> kvp in Inventory.CustomAmmoBox)
Owner.Cast<Pawn>().AddAmmo(kvp.Key, kvp.Value);
}
}
}

Expand Down Expand Up @@ -647,11 +655,6 @@ protected virtual void OverrideSpawnPoint(SpawningEventArgs ev)
/// <inheritdoc cref="Exiled.Events.Handlers.Player.OnSearchPickupRequest(SearchingPickupEventArgs)"/>
protected virtual void PickingUpItemBehavior(SearchingPickupEventArgs ev)
{
if (ev.Pickup is null)
{
Log.Error("Pickup is null");
}

if (!Check(ev.Player) || Settings.CanPickupItems)
return;

Expand All @@ -661,7 +664,7 @@ protected virtual void PickingUpItemBehavior(SearchingPickupEventArgs ev)
/// <inheritdoc cref="Exiled.Events.Handlers.Player.OnEscaping(Exiled.Events.EventArgs.Player.EscapingEventArgs)"/>
protected virtual void PreventPlayerFromEscaping(Exiled.Events.EventArgs.Player.EscapingEventArgs ev)
{
if (!Check(ev.Player) || useCustomEscape || wasEscaped || !EscapeSettings.IsEmpty())
if (!Check(ev.Player) || useCustomEscape || wasEscaped || (EscapeSettings is not null && !EscapeSettings.IsEmpty()))
return;

ev.IsAllowed = false;
Expand Down
68 changes: 59 additions & 9 deletions Exiled.CustomModules/API/Features/Generic/ModuleBehaviour.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
namespace Exiled.CustomModules.API.Features.Generic
{
using System;
using System.Collections.Generic;
using System.Reflection;

using Exiled.API.Extensions;
using Exiled.API.Features.Core;
using Exiled.API.Features.Core.Generic;

Expand All @@ -30,8 +32,7 @@ public abstract class ModuleBehaviour<TEntity> : EBehaviour<TEntity>
/// The binding flags in use to implement configs through reflection.
/// </summary>
internal const BindingFlags CONFIG_IMPLEMENTATION_BINDING_FLAGS =
BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.DeclaredOnly;
BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Instance;

/// <summary>
/// The behaviour's config.
Expand All @@ -56,6 +57,9 @@ public virtual ModulePointer Config
/// <param name="config">The <see cref="ModulePointer"/> or any config object to be implemented.</param>
public static void ImplementConfigs_DefaultImplementation(object instance, object config)
{
if (instance is null || config is null)
return;

Type inType = instance.GetType();

foreach (PropertyInfo propertyInfo in config.GetType().GetProperties(CONFIG_IMPLEMENTATION_BINDING_FLAGS))
Expand All @@ -65,16 +69,41 @@ public static void ImplementConfigs_DefaultImplementation(object instance, objec
if (targetProperty is null || targetProperty.PropertyType != propertyInfo.PropertyType)
continue;

if (targetProperty.PropertyType.IsClass && targetProperty.PropertyType != typeof(string))
if (typeof(System.Collections.IEnumerable).IsAssignableFrom(targetProperty.PropertyType))
{
object sourceCollection = propertyInfo.GetValue(config);
object targetCollection = targetProperty.GetValue(instance);

if (targetCollection is not null && sourceCollection is not null)
{
if (targetProperty.PropertyType.IsGenericType)
{
Type genericType = targetProperty.PropertyType.GetGenericTypeDefinition();

if (genericType == typeof(List<>))
targetCollection.CopyListElements(sourceCollection);
else if (genericType == typeof(Dictionary<,>))
targetCollection.CopyDictionaryElements(sourceCollection);
}
}
}
else if (targetProperty.PropertyType.IsClass && targetProperty.PropertyType != typeof(string))
{
object value = propertyInfo.GetValue(config);
object targetInstance = targetProperty.GetValue(instance);

if (targetInstance is not null)
ApplyConfig_DefaultImplementation(propertyInfo.GetValue(config), targetInstance);
if (targetInstance is null)
{
targetInstance = Activator.CreateInstance(targetProperty.PropertyType);
targetProperty.SetValue(instance, targetInstance);
}

if (value is not null)
ApplyConfig_DefaultImplementation(value, targetInstance);
}
else
{
if (targetProperty.CanWrite)
if (targetProperty.SetMethod is not null)
targetProperty.SetValue(instance, propertyInfo.GetValue(config));
}
}
Expand All @@ -97,12 +126,33 @@ public static void ApplyConfig_DefaultImplementation(object source, object targe
{
PropertyInfo targetProperty = targetType.GetProperty(sourceProperty.Name);

if (targetProperty is null || !targetProperty.CanWrite || targetProperty.PropertyType != sourceProperty.PropertyType)
if (targetProperty is null || !targetProperty.CanWrite ||
targetProperty.PropertyType != sourceProperty.PropertyType)
continue;

object value = sourceProperty.GetValue(source);

if (value is not null && targetProperty.PropertyType.IsClass && targetProperty.PropertyType != typeof(string))
if (value is null)
continue;

if (typeof(System.Collections.IEnumerable).IsAssignableFrom(targetProperty.PropertyType))
{
object targetCollection = targetProperty.GetValue(target);

if (targetCollection is not null)
{
if (!targetProperty.PropertyType.IsGenericType)
continue;

Type genericType = targetProperty.PropertyType.GetGenericTypeDefinition();

if (genericType == typeof(List<>))
targetCollection.CopyListElements(value);
else if (genericType == typeof(Dictionary<,>))
targetCollection.CopyDictionaryElements(value);
}
}
else if (targetProperty.PropertyType.IsClass && targetProperty.PropertyType != typeof(string))
{
object targetInstance = targetProperty.GetValue(target);

Expand Down Expand Up @@ -141,4 +191,4 @@ protected virtual void ImplementConfigs()
/// <param name="target">The target property to which the value will be copied.</param>
protected virtual void ApplyConfig(object source, object target) => ApplyConfig_DefaultImplementation(source, target);
}
}
}
Loading

0 comments on commit bad7226

Please sign in to comment.