Repository navigation
feat: Lots of new useful features and fix #864
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| // ----------------------------------------------------------------------- | ||
| // <copyright file="IEnumerableExtensions.cs" company="ExMod Team"> | ||
| // Copyright (c) ExMod Team. All rights reserved. | ||
| // Licensed under the CC BY-SA 3.0 license. | ||
| // </copyright> | ||
| // ----------------------------------------------------------------------- | ||
|
|
||
| namespace Exiled.API.Extensions | ||
| { | ||
| using System; | ||
| using System.Collections.Generic; | ||
|
|
||
| /// <summary> | ||
| /// A set of extensions for <see cref="IEnumerable{T}"/>. | ||
| /// </summary> | ||
| public static class IEnumerableExtensions | ||
| { | ||
| /// <summary> | ||
| /// Perform an action on each element of a collection. | ||
| /// </summary> | ||
| /// <typeparam name="T">Type of <see cref="IEnumerable{T}"/> elements.</typeparam> | ||
| /// <param name="enumerable"><see cref="IEnumerable{T}"/> in this collection, the elements will perform actions.</param> | ||
| /// <param name="action">Action that needs to be performed.</param> | ||
| public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action) | ||
| { | ||
| if (enumerable is null || action is null) | ||
| return; | ||
|
|
||
| foreach (T e in enumerable) | ||
| action(e); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| // ----------------------------------------------------------------------- | ||
| // <copyright file="PlayerPermissionsExtensions.cs" company="ExMod Team"> | ||
| // Copyright (c) ExMod Team. All rights reserved. | ||
| // Licensed under the CC BY-SA 3.0 license. | ||
| // </copyright> | ||
| // ----------------------------------------------------------------------- | ||
|
|
||
| namespace Exiled.API.Extensions | ||
| { | ||
| using System.Collections.Generic; | ||
|
|
||
| /// <summary> | ||
| /// A set of extensions for <see cref="PlayerPermissions"/>. | ||
| /// </summary> | ||
| public static class PlayerPermissionsExtensions | ||
| { | ||
| /// <summary> | ||
| /// Checks whether the current permissions contain any of the permissions specified in the mask. | ||
| /// </summary> | ||
| /// <param name="playerPermissions">The current permissions to check.</param> | ||
| /// <param name="mask">The mask of permissions to test against.</param> | ||
| /// <returns><see langword="true"/> if the current permissions contain at least one permission from the mask; otherwise, <see langword="false"/>.</returns> | ||
| public static bool HasAnyPermission(this PlayerPermissions playerPermissions, PlayerPermissions mask) | ||
| { | ||
| return (playerPermissions & mask) != 0; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Checks whether the current permissions contain any of the permissions specified in the collection. | ||
| /// </summary> | ||
| /// <param name="playerPermissions">The current permissions to check.</param> | ||
| /// <param name="collectionPlayerPermissions">The collection of permissions to test against.</param> | ||
| /// <returns><see langword="true"/> if the current permissions contain at least one permission from the collection; otherwise, <see langword="false"/>.</returns> | ||
| public static bool HasAnyPermission(this PlayerPermissions playerPermissions, IEnumerable<PlayerPermissions> collectionPlayerPermissions) | ||
| { | ||
| if (collectionPlayerPermissions is null) | ||
| return false; | ||
|
|
||
| foreach (PlayerPermissions perm in collectionPlayerPermissions) | ||
| { | ||
| if (playerPermissions.HasAnyPermission(perm)) | ||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| // ----------------------------------------------------------------------- | ||
| // <copyright file="RandomExtensions.cs" company="ExMod Team"> | ||
| // Copyright (c) ExMod Team. All rights reserved. | ||
| // Licensed under the CC BY-SA 3.0 license. | ||
| // </copyright> | ||
| // ----------------------------------------------------------------------- | ||
|
|
||
| namespace Exiled.API.Extensions | ||
| { | ||
| using System; | ||
|
|
||
| /// <summary> | ||
| /// A set of extensions for <see cref="Random"/>. | ||
| /// </summary> | ||
| public static class RandomExtensions | ||
| { | ||
| /// <summary> | ||
| /// Generate a random bool. | ||
| /// </summary> | ||
| /// <param name="rnd"><see cref="Random"/> object.</param> | ||
| /// <returns>Random boolean value.</returns> | ||
| public static bool NextBool(this Random rnd) | ||
| { | ||
| return (rnd.Next() & 1) == 0; | ||
| } | ||
|
Comment on lines
+22
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pretty sure this is very badly optimised
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't see any particular problem here, but you can do it like this: |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| // ----------------------------------------------------------------------- | ||
| // <copyright file="TimeSpanExtensions.cs" company="ExMod Team"> | ||
| // Copyright (c) ExMod Team. All rights reserved. | ||
| // Licensed under the CC BY-SA 3.0 license. | ||
| // </copyright> | ||
| // ----------------------------------------------------------------------- | ||
|
|
||
| namespace Exiled.API.Extensions | ||
| { | ||
| using System; | ||
|
|
||
| /// <summary> | ||
| /// A set of extensions for <see cref="TimeSpan"/>. | ||
| /// </summary> | ||
| public static class TimeSpanExtensions | ||
| { | ||
| /// <summary> | ||
| /// Converts a TimeSpan object to a human-readable format. | ||
| /// </summary> | ||
| /// <param name="timeSpan"><see cref="TimeSpan"/> object.</param> | ||
| /// <returns>A <see cref="TimeSpan"/> object in string representation.</returns> | ||
| public static string ToHumanReadable(this TimeSpan timeSpan) | ||
| { | ||
| if (timeSpan < TimeSpan.Zero) | ||
| return "-" + timeSpan.Negate().ToHumanReadable(); | ||
|
|
||
| int days = timeSpan.Days; | ||
| int hours = timeSpan.Hours; | ||
| int minutes = timeSpan.Minutes; | ||
| int seconds = timeSpan.Seconds; | ||
|
|
||
| if (days > 0) | ||
| return $"{days}:{hours:D2}:{minutes:D2}:{seconds:D2}"; | ||
| else if (hours > 0) | ||
| return $"{hours:D2}:{minutes:D2}:{seconds:D2}"; | ||
| else | ||
| return $"{minutes}:{seconds:D2}"; | ||
| } | ||
|
Comment on lines
+22
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How does it handle differences like language/ 24h or 12h Am/Pm
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Normal people use the 24-hour clock. In any case, I didn't intend for this method to be universal. If someone needs it, they can always add their own extension method to this class with a different name or a different overload. I don't see any problem with that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It's an API meant for many people to use. A lot of this code is pretty pointless or not helpful to many people.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Write your own method and add it. Be sure to take into account the 250 official languages. Keep in mind that each language has its own vocabulary and grammar. I used one specific method for 99% of tasks. But you can add your own. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that why i don't see a point for HumanReadable on this one There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't really see why we need a TimeSpanExtensions, this is basic ToString stuff
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
And I repeat once again that the default var timeSpan = TimeSpan.FromMinutes(1);
Log.Debug(timeSpan.ToString());
Log.Debug(timeSpan.ToString("c"));
Log.Debug(timeSpan.ToString("g"));
Log.Debug(timeSpan.ToString("G"));
Log.Debug(timeSpan.ToString(@"h\:mm\:ss"));
Log.Debug(timeSpan.ToString(@"m\:ss"));
Log.Debug(timeSpan.ToHumanReadable());[2026-09-26 13:24:16.581 +03:00] [DEBUG] [ToolTestHelper] 00:01:00
[2026-09-26 13:24:16.596 +03:00] [DEBUG] [ToolTestHelper] 00:01:00
[2026-09-26 13:24:16.612 +03:00] [DEBUG] [ToolTestHelper] 0:01:00
[2026-09-26 13:24:16.628 +03:00] [DEBUG] [ToolTestHelper] 0:00:01:00.0000000
[2026-09-26 13:24:16.643 +03:00] [DEBUG] [ToolTestHelper] 0:01:00
[2026-09-26 13:24:16.658 +03:00] [DEBUG] [ToolTestHelper] 1:00
[2026-09-26 13:24:16.674 +03:00] [DEBUG] [ToolTestHelper] 01:00
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’ve slightly updated this method. Log.Debug(TimeSpan.FromTicks(1).ToHumanReadable());
Log.Debug(TimeSpan.FromMilliseconds(1).ToHumanReadable());
Log.Debug(TimeSpan.FromSeconds(1).ToHumanReadable());
Log.Debug(TimeSpan.FromMinutes(1).ToHumanReadable());
Log.Debug(TimeSpan.FromHours(1).ToHumanReadable());
Log.Debug(TimeSpan.FromDays(1).ToHumanReadable());
Log.Debug(TimeSpan.FromDays(1).Negate().ToHumanReadable());[2026-09-26 14:58:43.668 +03:00] [DEBUG] [ToolTestHelper] 0:00
[2026-09-26 14:58:43.683 +03:00] [DEBUG] [ToolTestHelper] 0:00
[2026-09-26 14:58:43.700 +03:00] [DEBUG] [ToolTestHelper] 0:01
[2026-09-26 14:58:43.715 +03:00] [DEBUG] [ToolTestHelper] 1:00
[2026-09-26 14:58:43.731 +03:00] [DEBUG] [ToolTestHelper] 01:00:00
[2026-09-26 14:58:43.746 +03:00] [DEBUG] [ToolTestHelper] 1:00:00:00
[2026-09-26 14:58:43.761 +03:00] [DEBUG] [ToolTestHelper] -1:00:00:00There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I still think this should live outside of EXILED, and "ToString override isn't working as it should." is false, and I'm pretty sure you can use Cultures and DateTime to better represent what you need
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| // ----------------------------------------------------------------------- | ||
| // <copyright file="Vector3Extensions.cs" company="ExMod Team"> | ||
| // Copyright (c) ExMod Team. All rights reserved. | ||
| // Licensed under the CC BY-SA 3.0 license. | ||
| // </copyright> | ||
| // ----------------------------------------------------------------------- | ||
|
|
||
| namespace Exiled.API.Extensions | ||
| { | ||
| using Exiled.API.Enums; | ||
| using Exiled.API.Features; | ||
|
|
||
| using UnityEngine; | ||
|
|
||
| /// <summary> | ||
| /// A set of extensions for <see cref="Vector3"/> that provide conversions between world space and | ||
| /// room‑relative local space. | ||
| /// </summary> | ||
| public static class Vector3Extensions | ||
| { | ||
| /// <summary> | ||
| /// Converts a world position to a position relative to the specified room's local coordinate system. | ||
| /// </summary> | ||
| /// <param name="worldPos">The world‑space position to convert.</param> | ||
| /// <param name="room">The room whose local space will be used as the reference.</param> | ||
| /// <returns> | ||
| /// The position expressed in the room's local space. | ||
| /// If the room is the <see cref="RoomType.Surface"/>, the original world position is returned unchanged. | ||
| /// </returns> | ||
| public static Vector3 FromWorldToRelativePos(this Vector3 worldPos, Room room) | ||
| { | ||
| if (room.Type == RoomType.Surface) | ||
| return worldPos; | ||
| return room.Transform.InverseTransformPoint(worldPos); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Converts a position relative to the specified room's local space back to world space. | ||
| /// </summary> | ||
| /// <param name="relativePos">The local‑space position to convert.</param> | ||
| /// <param name="room">The room whose local space was used as the reference.</param> | ||
| /// <returns> | ||
| /// The position expressed in world space. | ||
| /// If the room is the <see cref="RoomType.Surface"/>, the original local position is returned unchanged | ||
| /// (since surface uses world coordinates directly). | ||
| /// </returns> | ||
| public static Vector3 FromRelativeToWorldPos(this Vector3 relativePos, Room room) | ||
| { | ||
| if (room.Type == RoomType.Surface) | ||
| return relativePos; | ||
|
Comment on lines
+49
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Surface is a functional room technically i only see the purpose of this for Unknown Room Or if you do it with the surface let's do it to all non dynamic rooms (i prefer to only do it on null that would be unknown) Surface and pocket
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't understand what you didn't like about this code. |
||
| return room.Transform.TransformPoint(relativePos); | ||
| } | ||
| } | ||
| } | ||

Uh oh!
There was an error while loading. Please reload this page.