Skip to content

Commit

Permalink
Merge branch 'dev' into TrelloIsNoMore
Browse files Browse the repository at this point in the history
  • Loading branch information
louis1706 authored Aug 8, 2024
2 parents 2ebf72a + 03d6bd7 commit a82c610
Show file tree
Hide file tree
Showing 53 changed files with 967 additions and 184 deletions.
5 changes: 5 additions & 0 deletions EXILED/Exiled.API/Enums/AuthenticationType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,10 @@ public enum AuthenticationType
/// Indicates that the player has been authenticated as DedicatedServer.
/// </summary>
DedicatedServer,

/// <summary>
/// Indicates that the player has been authenticated during Offline mode.
/// </summary>
Offline,
}
}
9 changes: 4 additions & 5 deletions EXILED/Exiled.API/Extensions/MirrorExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -435,12 +435,11 @@ public static void SendFakeTargetRpc(Player target, NetworkIdentity behaviorOwne
/// <example>
/// EffectOnlySCP207.
/// <code>
/// MirrorExtensions.SendCustomSync(player, player.ReferenceHub.networkIdentity, typeof(PlayerEffectsController), (writer) => {
/// writer.WriteUInt64(1ul); // DirtyObjectsBit
/// writer.WriteUInt32(1); // DirtyIndexCount
/// MirrorExtensions.SendFakeSyncObject(player, player.NetworkIdentity, typeof(PlayerEffectsController), (writer) => {
/// writer.WriteULong(1ul); // DirtyObjectsBit
/// writer.WriteUInt(1); // DirtyIndexCount
/// writer.WriteByte((byte)SyncList&lt;byte&gt;.Operation.OP_SET); // Operations
/// writer.WriteUInt32(17); // EditIndex
/// writer.WriteByte(1); // Value
/// writer.WriteUInt(17); // EditIndex
/// });
/// </code>
/// </example>
Expand Down
6 changes: 5 additions & 1 deletion EXILED/Exiled.API/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,11 @@ public static string GetBefore(this string input, char symbol)
/// </summary>
/// <param name="userId">The user id.</param>
/// <returns>Returns the raw user id.</returns>
public static string GetRawUserId(this string userId) => userId.Substring(0, userId.LastIndexOf('@'));
public static string GetRawUserId(this string userId)
{
int index = userId.IndexOf('@');
return index == -1 ? userId : userId.Substring(0, index);
}

/// <summary>
/// Gets a SHA256 hash of a player's user id without the authentication.
Expand Down
2 changes: 1 addition & 1 deletion EXILED/Exiled.API/Features/Camera.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class Camera : IWrapper<Scp079Camera>, IWorldSpace
/// <summary>
/// A <see cref="Dictionary{TKey,TValue}"/> containing all known <see cref="Scp079Camera"/>s and their corresponding <see cref="Camera"/>.
/// </summary>
internal static readonly Dictionary<Scp079Camera, Camera> Camera079ToCamera = new(250);
internal static readonly Dictionary<Scp079Camera, Camera> Camera079ToCamera = new(250, new ComponentsEqualityComparer());

private static readonly Dictionary<string, CameraType> NameToCameraType = new()
{
Expand Down
2 changes: 1 addition & 1 deletion EXILED/Exiled.API/Features/Doors/AirlockController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class AirlockController
/// <summary>
/// A <see cref="Dictionary{TKey,TValue}"/> containing all known <see cref="BaseController"/>'s and their corresponding <see cref="AirlockController"/>.
/// </summary>
internal static readonly Dictionary<BaseController, AirlockController> BaseToExiledControllers = new();
internal static readonly Dictionary<BaseController, AirlockController> BaseToExiledControllers = new(new ComponentsEqualityComparer());

/// <summary>
/// Initializes a new instance of the <see cref="AirlockController"/> class.
Expand Down
2 changes: 1 addition & 1 deletion EXILED/Exiled.API/Features/Doors/Door.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class Door : TypeCastObject<Door>, IWrapper<DoorVariant>, IWorldSpace
/// <summary>
/// A <see cref="Dictionary{TKey,TValue}"/> containing all known <see cref="DoorVariant"/>'s and their corresponding <see cref="Door"/>.
/// </summary>
internal static readonly Dictionary<DoorVariant, Door> DoorVariantToDoor = new();
internal static readonly Dictionary<DoorVariant, Door> DoorVariantToDoor = new(new ComponentsEqualityComparer());

/// <summary>
/// Initializes a new instance of the <see cref="Door"/> class.
Expand Down
2 changes: 1 addition & 1 deletion EXILED/Exiled.API/Features/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class Generator : IWrapper<Scp079Generator>, IWorldSpace
/// <summary>
/// A <see cref="List{T}"/> of <see cref="Generator"/> on the map.
/// </summary>
internal static readonly Dictionary<Scp079Generator, Generator> Scp079GeneratorToGenerator = new();
internal static readonly Dictionary<Scp079Generator, Generator> Scp079GeneratorToGenerator = new(new ComponentsEqualityComparer());
private Room room;

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion EXILED/Exiled.API/Features/Hazards/Hazard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class Hazard : TypeCastObject<Hazard>, IWrapper<EnvironmentalHazard>
/// <summary>
/// <see cref="Dictionary{TKey,TValue}"/> with <see cref="EnvironmentalHazard"/> to it's <see cref="Hazard"/>.
/// </summary>
internal static readonly Dictionary<EnvironmentalHazard, Hazard> EnvironmentalHazardToHazard = new();
internal static readonly Dictionary<EnvironmentalHazard, Hazard> EnvironmentalHazardToHazard = new(new ComponentsEqualityComparer());

/// <summary>
/// Initializes a new instance of the <see cref="Hazard"/> class.
Expand Down
2 changes: 1 addition & 1 deletion EXILED/Exiled.API/Features/Items/Ammo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class Ammo : Item, IWrapper<AmmoItem>
/// <summary>
/// Gets the absolute maximum amount of ammo that may be held at one time, if ammo is forcefully given to the player (regardless of worn armor or server configuration).
/// <para>
/// For accessing the maximum amount of ammo that may be held based on worn armor and server settings, see <see cref="Player.GetAmmoLimit(AmmoType)"/>.
/// For accessing the maximum amount of ammo that may be held based on worn armor and server settings, see <see cref="Player.GetAmmoLimit(AmmoType, bool)"/>.
/// </para>
/// </summary>
public const ushort AmmoLimit = ushort.MaxValue;
Expand Down
2 changes: 1 addition & 1 deletion EXILED/Exiled.API/Features/Items/Item.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class Item : TypeCastObject<Item>, IWrapper<ItemBase>
/// <summary>
/// A dictionary of all <see cref="ItemBase"/>'s that have been converted into <see cref="Item"/>.
/// </summary>
internal static readonly Dictionary<ItemBase, Item> BaseToItem = new();
internal static readonly Dictionary<ItemBase, Item> BaseToItem = new(new ComponentsEqualityComparer());

/// <summary>
/// Initializes a new instance of the <see cref="Item"/> class.
Expand Down
2 changes: 1 addition & 1 deletion EXILED/Exiled.API/Features/Lift.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class Lift : IWrapper<ElevatorChamber>, IWorldSpace
/// <summary>
/// A <see cref="Dictionary{TKey,TValue}"/> containing all known <see cref="ElevatorChamber"/>s and their corresponding <see cref="Lift"/>.
/// </summary>
internal static readonly Dictionary<ElevatorChamber, Lift> ElevatorChamberToLift = new(8);
internal static readonly Dictionary<ElevatorChamber, Lift> ElevatorChamberToLift = new(8, new ComponentsEqualityComparer());

/// <summary>
/// Internal list that contains all ElevatorDoor for current group.
Expand Down
176 changes: 168 additions & 8 deletions EXILED/Exiled.API/Features/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,16 @@ public class Player : TypeCastObject<Player>, IEntity, IWorldSpace
/// A list of the player's items.
/// </summary>
internal readonly List<Item> ItemsValue = new(8);

/// <summary>
/// A dictionary of custom item category limits.
/// </summary>
internal Dictionary<ItemCategory, sbyte> CustomCategoryLimits = new();

/// <summary>
/// A dictionary of custom ammo limits.
/// </summary>
internal Dictionary<AmmoType, ushort> CustomAmmoLimits = new();
#pragma warning restore SA1401

private readonly HashSet<EActor> componentsInChildren = new();
Expand Down Expand Up @@ -282,6 +292,7 @@ public AuthenticationType AuthenticationType
"northwood" => AuthenticationType.Northwood,
"localhost" => AuthenticationType.LocalHost,
"ID_Dedicated" => AuthenticationType.DedicatedServer,
"offline" => AuthenticationType.Offline,
_ => AuthenticationType.Unknown,
};
}
Expand Down Expand Up @@ -1300,7 +1311,7 @@ public static Player Get(string args)
if (int.TryParse(args, out int id))
return Get(id);

if (args.EndsWith("@steam") || args.EndsWith("@discord") || args.EndsWith("@northwood"))
if (args.EndsWith("@steam") || args.EndsWith("@discord") || args.EndsWith("@northwood") || args.EndsWith("@offline"))
{
foreach (Player player in Dictionary.Values)
{
Expand Down Expand Up @@ -2382,21 +2393,170 @@ public bool DropAmmo(AmmoType ammoType, ushort amount, bool checkMinimals = fals

/// <summary>
/// Gets the maximum amount of ammo the player can hold, given the ammo <see cref="AmmoType"/>.
/// This method factors in the armor the player is wearing, as well as server configuration.
/// For the maximum amount of ammo that can be given regardless of worn armor and server configuration, see <see cref="ServerConfigSynchronizer.AmmoLimit"/>.
/// </summary>
/// <param name="type">The <see cref="AmmoType"/> of the ammo to check.</param>
/// <returns>The maximum amount of ammo this player can carry. Guaranteed to be between <c>0</c> and <see cref="ServerConfigSynchronizer.AmmoLimit"/>.</returns>
public int GetAmmoLimit(AmmoType type) =>
InventorySystem.Configs.InventoryLimits.GetAmmoLimit(type.GetItemType(), referenceHub);
/// <param name="ignoreArmor">If the method should ignore the armor the player is wearing.</param>
/// <returns>The maximum amount of ammo this player can carry.</returns>
public ushort GetAmmoLimit(AmmoType type, bool ignoreArmor = false)
{
if (ignoreArmor)
{
if (CustomAmmoLimits.TryGetValue(type, out ushort limit))
return limit;

ItemType itemType = type.GetItemType();
return ServerConfigSynchronizer.Singleton.AmmoLimitsSync.FirstOrDefault(x => x.AmmoType == itemType).Limit;
}

return InventorySystem.Configs.InventoryLimits.GetAmmoLimit(type.GetItemType(), referenceHub);
}

/// <summary>
/// Gets the maximum amount of ammo the player can hold, given the ammo <see cref="AmmoType"/>.
/// This limit will scale with the armor the player is wearing.
/// For armor ammo limits, see <see cref="Armor.AmmoLimits"/>.
/// </summary>
/// <param name="ammoType">The <see cref="AmmoType"/> of the ammo to check.</param>
/// <param name="limit">The <see cref="ushort"/> number that will define the new limit.</param>
public void SetAmmoLimit(AmmoType ammoType, ushort limit)
{
CustomAmmoLimits[ammoType] = limit;

ItemType itemType = ammoType.GetItemType();
int index = ServerConfigSynchronizer.Singleton.AmmoLimitsSync.FindIndex(x => x.AmmoType == itemType);
MirrorExtensions.SendFakeSyncObject(this, ServerConfigSynchronizer.Singleton.netIdentity, typeof(ServerConfigSynchronizer), writer =>
{
writer.WriteULong(2ul);
writer.WriteUInt(1);
writer.WriteByte((byte)SyncList<ServerConfigSynchronizer.AmmoLimit>.Operation.OP_SET);
writer.WriteInt(index);
writer.WriteAmmoLimit(new() { Limit = limit, AmmoType = itemType, });
});
}

/// <summary>
/// Reset a custom <see cref="AmmoType"/> limit.
/// </summary>
/// <param name="ammoType">The <see cref="AmmoType"/> of the ammo to reset.</param>
public void ResetAmmoLimit(AmmoType ammoType)
{
if (!HasCustomAmmoLimit(ammoType))
{
Log.Error($"{nameof(Player)}.{nameof(ResetAmmoLimit)}(AmmoType): AmmoType.{ammoType} does not have a custom limit.");
return;
}

CustomAmmoLimits.Remove(ammoType);

ItemType itemType = ammoType.GetItemType();
int index = ServerConfigSynchronizer.Singleton.AmmoLimitsSync.FindIndex(x => x.AmmoType == itemType);
MirrorExtensions.SendFakeSyncObject(this, ServerConfigSynchronizer.Singleton.netIdentity, typeof(ServerConfigSynchronizer), writer =>
{
writer.WriteULong(2ul);
writer.WriteUInt(1);
writer.WriteByte((byte)SyncList<ServerConfigSynchronizer.AmmoLimit>.Operation.OP_SET);
writer.WriteInt(index);
writer.WriteAmmoLimit(ServerConfigSynchronizer.Singleton.AmmoLimitsSync[index]);
});
}

/// <summary>
/// Check if the player has a custom limit for a specific <see cref="AmmoType"/>.
/// </summary>
/// <param name="ammoType">The <see cref="AmmoType"/> to check.</param>
/// <returns>If the player has a custom limit for the specific <see cref="AmmoType"/>.</returns>
public bool HasCustomAmmoLimit(AmmoType ammoType) => CustomAmmoLimits.ContainsKey(ammoType);

/// <summary>
/// Gets the maximum amount of an <see cref="ItemCategory"/> the player can hold, based on the armor the player is wearing, as well as server configuration.
/// </summary>
/// <param name="category">The <see cref="ItemCategory"/> to check.</param>
/// <param name="ignoreArmor">If the method should ignore the armor the player is wearing.</param>
/// <returns>The maximum amount of items in the category that the player can hold.</returns>
public int GetCategoryLimit(ItemCategory category) =>
InventorySystem.Configs.InventoryLimits.GetCategoryLimit(category, referenceHub);
public sbyte GetCategoryLimit(ItemCategory category, bool ignoreArmor = false)
{
int index = InventorySystem.Configs.InventoryLimits.StandardCategoryLimits.Where(x => x.Value >= 0).OrderBy(x => x.Key).ToList().FindIndex(x => x.Key == category);

if (ignoreArmor && index != -1)
{
if (CustomCategoryLimits.TryGetValue(category, out sbyte customLimit))
return customLimit;

return ServerConfigSynchronizer.Singleton.CategoryLimits[index];
}

sbyte limit = InventorySystem.Configs.InventoryLimits.GetCategoryLimit(category, referenceHub);

return limit == -1 ? (sbyte)1 : limit;
}

/// <summary>
/// Set the maximum amount of an <see cref="ItemCategory"/> the player can hold. Only works with <see cref="ItemCategory.Keycard"/>, <see cref="ItemCategory.Medical"/>, <see cref="ItemCategory.Firearm"/>, <see cref="ItemCategory.Grenade"/> and <see cref="ItemCategory.SCPItem"/>.
/// This limit will scale with the armor the player is wearing.
/// For armor category limits, see <see cref="Armor.CategoryLimits"/>.
/// </summary>
/// <param name="category">The <see cref="ItemCategory"/> to check.</param>
/// <param name="limit">The <see cref="int"/> number that will define the new limit.</param>
public void SetCategoryLimit(ItemCategory category, sbyte limit)
{
int index = InventorySystem.Configs.InventoryLimits.StandardCategoryLimits.Where(x => x.Value >= 0).OrderBy(x => x.Key).ToList().FindIndex(x => x.Key == category);

if (index == -1)
{
Log.Error($"{nameof(Player)}.{nameof(SetCategoryLimit)}(ItemCategory, sbyte): Cannot set category limit for ItemCategory.{category}.");
return;
}

CustomCategoryLimits[category] = limit;

MirrorExtensions.SendFakeSyncObject(this, ServerConfigSynchronizer.Singleton.netIdentity, typeof(ServerConfigSynchronizer), writer =>
{
writer.WriteULong(1ul);
writer.WriteUInt(1);
writer.WriteByte((byte)SyncList<sbyte>.Operation.OP_SET);
writer.WriteInt(index);
writer.WriteSByte(limit);
});
}

/// <summary>
/// Reset a custom <see cref="ItemCategory"/> limit. Only works with <see cref="ItemCategory.Keycard"/>, <see cref="ItemCategory.Medical"/>, <see cref="ItemCategory.Firearm"/>, <see cref="ItemCategory.Grenade"/> and <see cref="ItemCategory.SCPItem"/>.
/// </summary>
/// <param name="category">The <see cref="ItemCategory"/> of the category to reset.</param>
public void ResetCategoryLimit(ItemCategory category)
{
int index = InventorySystem.Configs.InventoryLimits.StandardCategoryLimits.Where(x => x.Value >= 0).OrderBy(x => x.Key).ToList().FindIndex(x => x.Key == category);

if (index == -1)
{
Log.Error($"{nameof(Player)}.{nameof(ResetCategoryLimit)}(ItemCategory, sbyte): Cannot reset category limit for ItemCategory.{category}.");
return;
}

if (!HasCustomCategoryLimit(category))
{
Log.Error($"{nameof(Player)}.{nameof(ResetCategoryLimit)}(ItemCategory): ItemCategory.{category} does not have a custom limit.");
return;
}

CustomCategoryLimits.Remove(category);

MirrorExtensions.SendFakeSyncObject(this, ServerConfigSynchronizer.Singleton.netIdentity, typeof(ServerConfigSynchronizer), writer =>
{
writer.WriteULong(1ul);
writer.WriteUInt(1);
writer.WriteByte((byte)SyncList<sbyte>.Operation.OP_SET);
writer.WriteInt(index);
writer.WriteSByte(ServerConfigSynchronizer.Singleton.CategoryLimits[index]);
});
}

/// <summary>
/// Check if the player has a custom limit for a specific <see cref="ItemCategory"/>.
/// </summary>
/// <param name="category">The <see cref="ItemCategory"/> to check.</param>
/// <returns>If the player has a custom limit for the specific <see cref="ItemCategory"/>.</returns>
public bool HasCustomCategoryLimit(ItemCategory category) => CustomCategoryLimits.ContainsKey(category);

/// <summary>
/// Adds an item of the specified type with default durability(ammo/charge) and no mods to the player's inventory.
Expand Down
2 changes: 1 addition & 1 deletion EXILED/Exiled.API/Features/Ragdoll.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class Ragdoll : IWrapper<BasicRagdoll>, IWorldSpace
/// <summary>
/// A <see cref="Dictionary{TKey,TValue}"/> containing all known <see cref="BasicRagdoll"/>s and their corresponding <see cref="Ragdoll"/>.
/// </summary>
internal static readonly Dictionary<BasicRagdoll, Ragdoll> BasicRagdollToRagdoll = new(250);
internal static readonly Dictionary<BasicRagdoll, Ragdoll> BasicRagdollToRagdoll = new(250, new ComponentsEqualityComparer());

/// <summary>
/// Initializes a new instance of the <see cref="Ragdoll"/> class.
Expand Down
5 changes: 5 additions & 0 deletions EXILED/Exiled.API/Features/Recontainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ public static class Recontainer
/// </summary>
public static bool IsCassieBusy => Base.CassieBusy;

/// <summary>
/// Gets a value about how many generator have been activated.
/// </summary>
public static int EngagedGeneratorCount => Base._prevEngaged;

/// <summary>
/// Gets or sets a value indicating whether the containment zone is open.
/// </summary>
Expand Down
Loading

0 comments on commit a82c610

Please sign in to comment.