Skip to content
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

feat: Firearm QOL stuff #327

Open
wants to merge 8 commits into
base: dev
Choose a base branch
from

Conversation

Banalny-Banan
Copy link

@Banalny-Banan Banalny-Banan commented Dec 23, 2024

Description

Describe the changes
Things that got left out from #255

What is the current behavior? (You can also link to an open issue here)
New magazine API has sacrificed dev convenience

What is the new behavior? (if this is a feature change)
Ammo & MaxAmmo setters are back, TryGetModule methods added, AddPreference methods made static

Does this PR introduce a breaking change? (What changes might users need to make in their application due to this PR?)
It does, with making Preferences methods static.


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

@Banalny-Banan Banalny-Banan marked this pull request as ready for review December 23, 2024 14:03
@Banalny-Banan Banalny-Banan changed the title Firearm QOL stuff feat: Firearm QOL stuff Dec 23, 2024
Comment on lines +375 to +525
/// <param name="players">The <see cref="IEnumerable{T}"/> of <see cref="Player"/> of which must be added.</param>
/// <param name="preference">The <see cref="KeyValuePair{TKey, TValue}"/> of <see cref="Enums.FirearmType"/> and <see cref="AttachmentIdentifier"/>[] to add.</param>
public static void AddPreference(IEnumerable<Player> players, KeyValuePair<FirearmType, AttachmentIdentifier[]> preference)
{
foreach (Player player in players)
AddPreference(player, preference.Key, preference.Value);
}

/// <summary>
/// Adds or replaces an existing preference to the <see cref="PlayerPreferences"/>.
/// </summary>
/// <param name="players">The <see cref="IEnumerable{T}"/> of <see cref="Player"/> of which must be added.</param>
/// <param name="preference">The <see cref="Dictionary{TKey, TValue}"/> of <see cref="Enums.FirearmType"/> and <see cref="AttachmentIdentifier"/>[] to add.</param>
public static void AddPreference(IEnumerable<Player> players, Dictionary<FirearmType, AttachmentIdentifier[]> preference)
{
foreach ((Player player, KeyValuePair<FirearmType, AttachmentIdentifier[]> kvp) in players.SelectMany(player => preference.Select(kvp => (player, kvp))))
AddPreference(player, kvp);
}

/// <summary>
/// Removes a preference from the <see cref="PlayerPreferences"/> if it already exists.
/// </summary>
/// <param name="player">The <see cref="Player"/> of which must be removed.</param>
/// <param name="type">The <see cref="Enums.FirearmType"/> to remove.</param>
public static void RemovePreference(Player player, FirearmType type)
{
foreach (KeyValuePair<Player, Dictionary<FirearmType, AttachmentIdentifier[]>> kvp in PlayerPreferences)
{
if (kvp.Key != player)
continue;

if (AttachmentsServerHandler.PlayerPreferences.TryGetValue(player.ReferenceHub, out Dictionary<ItemType, uint> dictionary))
dictionary[type.GetItemType()] = type.GetBaseCode();
}
}

/// <summary>
/// Removes a preference from the <see cref="PlayerPreferences"/> if it already exists.
/// </summary>
/// <param name="players">The <see cref="IEnumerable{T}"/> of <see cref="Player"/> of which must be removed.</param>
/// <param name="type">The <see cref="Enums.FirearmType"/> to remove.</param>
public static void RemovePreference(IEnumerable<Player> players, FirearmType type)
{
foreach (Player player in players)
RemovePreference(player, type);
}

/// <summary>
/// Removes a preference from the <see cref="PlayerPreferences"/> if it already exists.
/// </summary>
/// <param name="player">The <see cref="Player"/> of which must be removed.</param>
/// <param name="types">The <see cref="IEnumerable{T}"/> of <see cref="Enums.FirearmType"/> to remove.</param>
public static void RemovePreference(Player player, IEnumerable<FirearmType> types)
{
foreach (FirearmType itemType in types)
RemovePreference(player, itemType);
}

/// <summary>
/// Removes a preference from the <see cref="PlayerPreferences"/> if it already exists.
/// </summary>
/// <param name="players">The <see cref="IEnumerable{T}"/> of <see cref="Player"/> of which must be removed.</param>
/// <param name="types">The <see cref="IEnumerable{T}"/> of <see cref="Enums.FirearmType"/> to remove.</param>
public static void RemovePreference(IEnumerable<Player> players, IEnumerable<FirearmType> types)
{
foreach ((Player player, FirearmType firearmType) in players.SelectMany(player => types.Select(itemType => (player, itemType))))
RemovePreference(player, firearmType);
}

/// <summary>
/// Clears all the existing preferences from <see cref="PlayerPreferences"/>.
/// </summary>
/// <param name="player">The <see cref="Player"/> of which must be cleared.</param>
public static void ClearPreferences(Player player)
{
if (AttachmentsServerHandler.PlayerPreferences.TryGetValue(player.ReferenceHub, out Dictionary<ItemType, uint> dictionary))
{
foreach (KeyValuePair<ItemType, uint> kvp in dictionary)
dictionary[kvp.Key] = kvp.Key.GetFirearmType().GetBaseCode();
}
}

/// <summary>
/// Clears all the existing preferences from <see cref="PlayerPreferences"/>.
/// </summary>
/// <param name="players">The <see cref="IEnumerable{T}"/> of <see cref="Player"/> of which must be cleared.</param>
public static void ClearPreferences(IEnumerable<Player> players)
{
foreach (Player player in players)
ClearPreferences(player);
}

/// <summary>
/// Clears all the existing preferences from <see cref="PlayerPreferences"/>.
/// </summary>
public static void ClearPreferences()
{
foreach (Player player in Player.List)
ClearPreferences(player);
}

Choose a reason for hiding this comment

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

Very big breaking change

Copy link
Author

Choose a reason for hiding this comment

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

Still smaller than renaming Ammo to TotalAmmo

Copy link

Choose a reason for hiding this comment

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

Very big breaking change

still valid, non-static Preference has -inf meaning

/// Gets or sets the maximum amount of ammo in the firearm.
/// </summary>
[Obsolete("Use MaxAmmo instead.")]
public int TotalMaxAmmo

Choose a reason for hiding this comment

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

Why we need to rename it?

Copy link
Author

Choose a reason for hiding this comment

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

It was renamed to TotalMaxAmmo in 9.0.1 for no practical reason, so I renamed it back and added backward compatibility

Copy link
Author

Choose a reason for hiding this comment

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

This is so people who haven't yet rewrote their plugins to use TotalMaxAmmo and TotalAmmo won't have any problems

/// Gets or sets the amount of ammo in the firearm.
/// </summary>
[Obsolete("Use Ammo instead.")]
public int TotalAmmo

Choose a reason for hiding this comment

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

Same as above

@IRacle1
Copy link

IRacle1 commented Dec 24, 2024

Okay, i see youre still burning with the idea of writing a union setter for ammos.
Firstly what i want to ask, do you have other arguments to your code, except backward compatibility?

Bcz in my opinion, that code in current realization is very compex and unstable, like setting all ammos to 0 before main code, if/else to different barrel components and processing barrel action itself. by unstable i mean you use too many tools for such a task, and logically, then from any custom patch the logic can get pretty weird

Why shotgun is pumped when i set Ammo to any value?
I patch ammo setters to make my custom ammo logic, why there a 2 calls for setting ammo from primary magazine?

Don't think of these as actual flaws ofc, but rather examples of what could potentially go wrong

I understand we want to make dev life easier, but actually a method with such complex logic can backfire on them altogether.

But still, I was wrong in terms of it being unrealistic to do generic Ammo setter, you could very well make a new Ammo setter that depends on abstractions in the right magazine classes, (most simple case like internal virtual SetAmmo method in every magazine class, and then some math in main ammo setter to call it properly, there are infinity ways to organize it), without multiple calls and reload methods - and I'd have no actual complaints about it

@IRacle1
Copy link

IRacle1 commented Dec 24, 2024

The naming issue is simpler, we already have Barrel... and Magazine... ammo setters/getters, but if youll keep old name(just Ammo or MaxAmmo), there will be a little misunderstanding, like ammo what? so I clarified the naming and added Total to make it clearer to people. Plus, I wanted to make people aware that what they used to do with ammo doesn't work like it was. So that they could at least read the documentation and rewrite their code as they really need it
Even if we generalize Ammo setter, still people should understand what it actually does, like it is much higher level than simple ammo related stuff
I think that's reason enough to rename the method and broke plugins😔

@IRacle1
Copy link

IRacle1 commented Dec 24, 2024

@VALERA771 @BoltonDev @Misaka-ZeroTwo @louis1706 @Misfiy I would also like to hear your opinion on this, its actually an important thing

@VALERA771
Copy link

VALERA771 commented Dec 24, 2024

I agree with IRacle. By changing names we force devs to rewrite their stuff/read docs on new methods/see how they're working. Also I strongly disagree with changing preference methods to static now. Such breaking changes (changing stuff that is working) are not good for stable releases. And current ammo setter looks too much complicated. Setting ammo to 0, then setting it to actual required value. At least it can be done easily with using BarrelAmmo and MagazineAmmo

@IRacle1
Copy link

IRacle1 commented Dec 24, 2024

I disagree about the preference methods, non static prefs are pure nonsense that made 0 sense when used, I agree with that change

@Banalny-Banan
Copy link
Author

Okay, i see youre still burning with the idea of writing a union setter for ammos.
Firstly what i want to ask, do you have other arguments to your code, except backward compatibility?

Bcz in my opinion, that code in current realization is very compex and unstable, like setting all ammos to 0 before main code, if/else to different barrel components and processing barrel action itself. by unstable i mean you use too many tools for such a task, and logically, then from any custom patch the logic can get pretty weird

Why shotgun is pumped when i set Ammo to any value?
I patch ammo setters to make my custom ammo logic, why there a 2 calls for setting ammo from primary magazine?

Don't think of these as actual flaws ofc, but rather examples of what could potentially go wrong

I understand we want to make dev life easier, but actually a method with such complex logic can backfire on them altogether.

But still, I was wrong in terms of it being unrealistic to do generic Ammo setter, you could very well make a new Ammo setter that depends on abstractions in the right magazine classes, (most simple case like internal virtual SetAmmo method in every magazine class, and then some math in main ammo setter to call it properly, there are infinity ways to organize it), without multiple calls and reload methods - and I'd have no actual complaints about it

As an example on my server I have a weapon that players can't reload, but it slowly starts reloading automatically after not being fired for some time. If there are 2 different setters I will have to manage the distribution myself, instead of exiled doing it internally.

Another example: I want to empty the firearm. With the Ammo setter I can do it in one line with firearm.Ammo = 0. With 2 setters I have to set both. And if do it frequently in multiple places it starts to go against DRY.

Example 3: I have a weapon that gets ammo when it deals damage. I can do it with firearm.Ammo += amount. With two setters I will have to manually check if there is no ammo in the barrel, and if so add it there, and then add remaining to the primary mag. So basically replicate the whole setter just to add ammo to the firearm.

I can go on and on with all the examples, but I think you get the idea.

@Banalny-Banan
Copy link
Author

Banalny-Banan commented Dec 24, 2024

what they used to do with ammo doesn't work like it was

Except for shotguns that get auto-pumped to avoid desync, it does. And if I find a way to make it work the same it did for shotguns, it will be a 1 to 1 replacement.

And I believe that we should work on solving the problem, instead of saying "nah, we tried" and making the devs carry out the 2-different-setters fuckery that NW created. So I encourage you to try to find a better solution instead of arguing how the one I made is not as perfect as it can be, because this is what Exiled exists for - simplify the interactions with base-game stuff.

@Rysik5318
Copy link

The changes are fully vetted by me, I can say it's not a bad PR

@louis1706 louis1706 changed the base branch from xmas-2024 to dev January 16, 2025 18:24
@Trevlouw
Copy link

Trevlouw commented Feb 9, 2025

I personally like this PR, but why did you use var Banalny 😭

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.

6 participants