Skip to content

feat: Lots of new useful features and fix - #864

Open
PUDGE133 wants to merge 1 commit into
ExMod-Team:masterfrom
PUDGE133:NewFeatures
Open

PUDGE133 wants to merge 1 commit into
ExMod-Team:masterfrom
PUDGE133:NewFeatures

Conversation

@PUDGE133

@PUDGE133 PUDGE133 commented Aug 26, 2026 •

Copy link
Copy Markdown

Description

Describe the changes

  • Several very useful extension methods.
  • Additional overloads of the Get method for Door.
  • Improving the Door.Get(GameObject) method
  • Implementation of new properties for Firearm
  • Created stubs for the Log class methods
  • Added several new teleportation methods for the Player
  • Implemented a new property Server.ReservedSlots
  • And also a few minor corrections in the code and in the documentation for methods and properties

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentations

Submission checklist

  • I have checked the project can be compiled
  • I have tested my changes and it worked as expected

Other

  • Still requires more testing

@PUDGE133

Copy link
Copy Markdown
Author

If you have any questions, you can write here or to me on Discord: scaletta2019

Comment on lines +213 to +220
/// <summary>
/// Gets or sets a value indicating whether the magazine is attached from the weapon.
/// </summary>
public bool IsMagazineDeattached
{
get => !IsMagazineAttached;
set => IsMagazineAttached = !value;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is useless

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is useless

If you think this section of code is useless, then let's think this code is useless too:
image

@Mrhootyhoot1 Mrhootyhoot1 Sep 6, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you think this section of code is useless, then let's think this code is useless too:

It may be useless, but it is not beneficial to remove it because that would be a breaking change. There is also no reason to add new junk code.

@PUDGE133 PUDGE133 Sep 6, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you think this section of code is useless, then let's think this code is useless too:

It may be useless, but it is not beneficial to remove it because that would be a breaking change. There is also no reason to add new junk code.

No, no, no... you've had enough of this "critical code." You shouldn't have added it and then been afraid to remove it. As long as these and similar properties exist in the code, the code I've submitted will comply with Exiled standards.

This is excellent code; it reduces the cognitive load when reading it in plugins. There is no problem with it.

Comment thread EXILED/Exiled.API/Features/Server.cs Outdated
Comment on lines +164 to +168

/// <inheritdoc cref="Player.Count"/>
public static int PlayerCount => Player.Count;
/// <summary>
/// Gets the number of players excluding NPCs.
/// </summary>
public static int PlayerCount => Player.Dictionary.Values.Count(x => !x.IsNPC);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No please don't break all plugin that would use that

@PUDGE133 PUDGE133 Sep 1, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No please don't break all plugin that would use that

Okay, I'll change it back. However, I don't understand why this property originally included NPCs. It once took me quite a while to figure out what was causing the bug. The problem was that someone misnamed the property. You should have named it something that would make it clear it included NPCs as well. If necessary, I can change the documentation for this property.

{
if (gameObject != null)
{
// ParentDoor requires enabling "unsafe code"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird comment?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird comment?

I will remove this comment.

Comment on lines +49 to +50
if (room.Type == RoomType.Surface)
return relativePos;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The 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

I don't understand what you didn't like about this code.

Comment on lines +22 to +33
public static string ToHumanReadable(this TimeSpan timeSpan)
{
if (timeSpan.TotalHours < 1)
return timeSpan.ToString(@"mm\:ss");

if (timeSpan.TotalDays < 1)
return timeSpan.ToString(@"hh\:mm\:ss");

string daysPart = timeSpan.Days == 1 ? "1 day" : $"{timeSpan.Days} days";
string timePart = timeSpan.ToString(@"hh\:mm\:ss");
return $"{daysPart}, {timePart}";
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does it handle differences like language/ 24h or 12h Am/Pm

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does it handle differences like language/ 24h or 12h Am/Pm

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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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.

It's an API meant for many people to use. A lot of this code is pretty pointless or not helpful to many people.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The 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.

It's an API meant for many people to use. A lot of this code is pretty pointless or not helpful to many people.

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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The 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

And I repeat once again that the default ToString override isn't working as it should.

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The 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:00

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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

@PUDGE133 PUDGE133 Oct 5, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The 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

image image image

There is no way to reproduce this method using IFormatProvider. You are delirious.

Comment on lines +70 to +73
public static bool NextBool(this Random rnd)
{
return rnd.Next(2) == 0;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure this is very badly optimised

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure this is very badly optimised

I don't see any particular problem here, but you can do it like this:
(rnd.Next() & 1) == 0

Comment on lines +17 to +63
/// <summary>
/// Generate a random float.
/// </summary>
/// <param name="rnd"><see cref="Random"/> object.</param>
/// <param name="min">Minimum value.</param>
/// <param name="max">Maximum value.</param>
/// <returns>Random value between minimum and maximum.</returns>
public static float NextFloat(this Random rnd, float min, float max)
{
return (float)((rnd.NextDouble() * (max - min)) + min);
}

/// <summary>
/// Generate a random float.
/// </summary>
/// <param name="rnd"><see cref="Random"/> object.</param>
/// <param name="min">Minimum value.</param>
/// <param name="max">Maximum value.</param>
/// <returns>Random value between minimum and maximum.</returns>
public static float NextFloat(this Random rnd, double min, float max)
{
return (float)((rnd.NextDouble() * (max - min)) + min);
}

/// <summary>
/// Generate a random float.
/// </summary>
/// <param name="rnd"><see cref="Random"/> object.</param>
/// <param name="min">Minimum value.</param>
/// <param name="max">Maximum value.</param>
/// <returns>Random value between minimum and maximum.</returns>
public static float NextFloat(this Random rnd, float min, double max)
{
return (float)((rnd.NextDouble() * (max - min)) + min);
}

/// <summary>
/// Generate a random float.
/// </summary>
/// <param name="rnd"><see cref="Random"/> object.</param>
/// <param name="min">Minimum value.</param>
/// <param name="max">Maximum value.</param>
/// <returns>Random value between minimum and maximum.</returns>
public static float NextFloat(this Random rnd, double min, double max)
{
return (float)((rnd.NextDouble() * (max - min)) + min);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't Unity Random already handle some of these ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't Unity Random already handle some of these ?

Because not everyone likes Random from unity.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because not everyone likes Random from unity.

??? What is wrong with unity randoms?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because not everyone likes Random from unity.

??? What is wrong with unity randoms?

What difference does it make to you? What changes for you? Use the randomness you want.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The difference is that there is a proper way to use the Engine and it's features, why do we need another random gen?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The difference is that there is a proper way to use the Engine and it's features, why do we need another random gen?

Alright, you've convinced me. I removed those overloads that return a float.

Comment thread EXILED/Exiled.API/Extensions/IEnumerableExtensions.cs
where T : Door => Get(doorVariant) as T;

/// <summary>
/// Gets the door object associated with a specific <see cref="ButtonVariant"/>, or creates a new one if there isn't one.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would you create a new door if there is not a door associated with that buttonvariant?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would you create a new door if there is not a door associated with that buttonvariant?

fixed

/// </summary>
/// <param name="message">The message to be sent.</param>
public static void Error(string message) => Send($"[{Assembly.GetCallingAssembly().GetName().Name}] {message}", Discord.LogLevel.Error, ConsoleColor.DarkRed);
public static void Error(string message = "") => Send($"[{Assembly.GetCallingAssembly().GetName().Name}] {message}", Discord.LogLevel.Error, ConsoleColor.DarkRed);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would anyone call a method meant to print to the console without passing a string to the method to be printed?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would anyone call a method meant to print to the console without passing a string to the method to be printed?

Have you ever split console logs to make them easier to read?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then you could just \n ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then you could just \n ?

Because I don't want to use \n. I want the methods in the Log class not to require any arguments from the user by default. I designed the code so that these changes wouldn't break any existing functionality.

@PUDGE133
PUDGE133 force-pushed the NewFeatures branch 2 times, most recently from 9fefd96 to 61f4978 Compare September 19, 2026 17:27
Comment thread EXILED/Exiled.API/Enums/FirearmType.cs
@louis1706

Copy link
Copy Markdown

why are you squashing all your commit ?

@PUDGE133

PUDGE133 commented Oct 5, 2026

Copy link
Copy Markdown
Author

I squash commits because I can't revert changes any other way. Which specific parts of all this do you consider "breaking changes"?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants