From 84bd166cd310f1657ce96440a713372f5899bac3 Mon Sep 17 00:00:00 2001
From: moddedmcplayer <76910334+moddedmcplayer@users.noreply.github.com>
Date: Fri, 30 May 2025 19:08:50 +0200
Subject: [PATCH 1/7] enable nullable + properly handle non-xp players
---
XPSystem/API/Badge.cs | 5 +-
.../DisplayProviders/IXPDisplayProvider.cs | 8 +-
.../SyncVarXPDisplayProvider.cs | 71 ++---
.../API/DisplayProviders/XPDisplayProvider.cs | 81 ++++--
.../XPDisplayProviderCollection.cs | 30 +--
.../Exceptions/InvalidPlayerIdException.cs | 11 -
XPSystem/API/IMessagingProvider.cs | 4 +-
XPSystem/API/Legacy/LiteDBMigrator.cs | 2 +-
XPSystem/API/Legacy/PlayerLog.cs | 2 +-
XPSystem/API/LevelCalculator.cs | 6 +-
XPSystem/API/LoaderSpecific.cs | 2 +-
.../{XPPlayer.cs => Player/BaseXPPlayer.cs} | 250 +++---------------
XPSystem/API/Player/XPPlayer.cs | 171 ++++++++++++
.../API/StorageProviders/IStorageProvider.cs | 3 +-
.../API/StorageProviders/Models/PlayerInfo.cs | 4 +-
.../API/StorageProviders/PlayerInfoWrapper.cs | 2 +-
.../API/StorageProviders/StorageProvider.cs | 79 +++---
XPSystem/API/Variables/Variable.cs | 4 +-
XPSystem/API/Variables/VariableCollection.cs | 17 +-
XPSystem/API/XPAPI.cs | 136 ++++------
XPSystem/API/XPExtensions.cs | 12 +-
.../Patch/MyNickPatchXPDisplayProvider.cs | 7 +-
.../Patch/NickPatchXPDisplayProvider.cs | 16 +-
.../Display/Patch/RankSetXPDisplayProvider.cs | 48 ++--
.../Display/SyncVar/NickXPDisplayProvider.cs | 20 +-
.../Display/SyncVar/RankXPDisplayProvider.cs | 36 +--
.../LiteDB/LiteDBPlayerInfo.cs | 4 +-
.../BuiltInProviders/LiteDB/LiteDBProvider.cs | 59 +++--
.../BuiltInProviders/MySql/MySqlProvider.cs | 11 +-
.../Commands/Admin/DatabasePlayerCommand.cs | 32 ++-
.../Admin/Subcommands/GetCommandAdmin.cs | 8 +-
.../Commands/Admin/Subcommands/GiveCommand.cs | 10 +-
.../Subcommands/LeaderboardCommandAdmin.cs | 2 +-
.../Admin/Subcommands/MultiplierCommand.cs | 14 +-
.../Commands/Admin/Subcommands/SetCommand.cs | 10 +-
.../Admin/Subcommands/SetLevelCommand.cs | 10 +-
.../Admin/Subcommands/ShowMessageCommand.cs | 9 +-
.../Admin/Subcommands/VariablesCommand.cs | 3 +-
.../Client/Subcommands/GetCommandClient.cs | 4 +-
.../Subcommands/LeaderboardCommandClient.cs | 2 +-
.../Subcommands/DeleteEverythingCommand.cs | 2 +-
.../Subcommands/ReloadConfigsCommand.cs | 2 +-
XPSystem/Config/Config.cs | 31 ++-
XPSystem/Config/Events/Types/XPECDictFile.cs | 11 +-
XPSystem/Config/Events/Types/XPECFile.cs | 21 +-
.../Config/Events/Types/XPECFileCollection.cs | 2 +-
XPSystem/Config/Events/Types/XPECItem.cs | 2 +-
XPSystem/Config/Events/Types/XPECItemFile.cs | 4 +-
XPSystem/Config/Events/XPECLimitTracker.cs | 5 +-
XPSystem/Config/Events/XPECManager.cs | 24 +-
.../CommentGatheringTypeInspector.cs | 3 +-
.../ValidatingNodeDeserializer.cs | 4 +-
.../YamlConverters/XPECFileYamlConverter.cs | 8 +-
.../EventHandlers/UnifiedEventHandlers.cs | 84 +++---
XPSystem/Main.cs | 16 +-
XPSystem/MessagingProviders.cs | 9 +-
XPSystem/NotNullWhenAttribute.cs | 18 ++
XPSystem/XPSystem.csproj | 7 +-
58 files changed, 780 insertions(+), 678 deletions(-)
delete mode 100644 XPSystem/API/Exceptions/InvalidPlayerIdException.cs
rename XPSystem/API/{XPPlayer.cs => Player/BaseXPPlayer.cs} (58%)
create mode 100644 XPSystem/API/Player/XPPlayer.cs
create mode 100644 XPSystem/NotNullWhenAttribute.cs
diff --git a/XPSystem/API/Badge.cs b/XPSystem/API/Badge.cs
index c81842b..74d7c67 100644
--- a/XPSystem/API/Badge.cs
+++ b/XPSystem/API/Badge.cs
@@ -1,7 +1,8 @@
-namespace XPSystem.Config.Models
+// ReSharper disable PropertyCanBeMadeInitOnly.Global
+#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
+namespace XPSystem.API
{
using System;
- using XPSystem.API;
public class Badge
{
diff --git a/XPSystem/API/DisplayProviders/IXPDisplayProvider.cs b/XPSystem/API/DisplayProviders/IXPDisplayProvider.cs
index 6c7b4ea..a246afa 100644
--- a/XPSystem/API/DisplayProviders/IXPDisplayProvider.cs
+++ b/XPSystem/API/DisplayProviders/IXPDisplayProvider.cs
@@ -1,6 +1,6 @@
namespace XPSystem.API
{
- using System;
+ using XPSystem.API.Player;
using XPSystem.API.StorageProviders;
///
@@ -10,10 +10,8 @@ public interface IXPDisplayProvider
{
void Enable();
void Disable();
- void RefreshTo(XPPlayer player);
- void RefreshOf(XPPlayer player, PlayerInfoWrapper playerInfo);
+ void RefreshTo(BaseXPPlayer player);
+ void RefreshOf(BaseXPPlayer player, PlayerInfoWrapper? playerInfo = null);
void RefreshAll();
- IXPDisplayProviderConfig ConfigPropertyInternal { get; set; }
- Type ConfigTypeInternal { get; }
}
}
\ No newline at end of file
diff --git a/XPSystem/API/DisplayProviders/SyncVarXPDisplayProvider.cs b/XPSystem/API/DisplayProviders/SyncVarXPDisplayProvider.cs
index 3d8100b..aa5b56f 100644
--- a/XPSystem/API/DisplayProviders/SyncVarXPDisplayProvider.cs
+++ b/XPSystem/API/DisplayProviders/SyncVarXPDisplayProvider.cs
@@ -1,6 +1,7 @@
namespace XPSystem.API.DisplayProviders
{
using System;
+ using XPSystem.API.Player;
using XPSystem.API.StorageProviders;
using static XPAPI;
@@ -19,7 +20,7 @@
///
/// Gets the sync vars to use.
///
- protected abstract (Type typeName, string methodName, Func getFakeSyncVar, Func getResyncVar)[] SyncVars { get; }
+ protected abstract (Type typeName, string methodName, Func getFakeSyncVar, Func getResyncVar)[] SyncVars { get; }
///
/// Creates the object for the player.
@@ -27,28 +28,30 @@
/// The player to create the object for.
/// The player's info to create the object from.
/// The created object.
- protected abstract TObject CreateObject(XPPlayer player, PlayerInfoWrapper playerInfo);
+ protected abstract TObject? CreateObject(BaseXPPlayer player, PlayerInfoWrapper? playerInfo);
- protected override void RefreshToEnabled(XPPlayer player)
+ protected override void RefreshToEnabled(BaseXPPlayer player)
{
int count = 0;
- foreach (var kvp in XPPlayer.Players)
+ foreach (BaseXPPlayer player2 in XPPlayer.PlayersRealConnected)
{
- if (kvp.Value.IsNPC)
+ if (player == player2)
continue;
- if (player == kvp.Value)
- continue;
- if (!ShouldEdit(kvp.Value))
+ if (!ShouldEdit(player2))
continue;
- TObject obj = GetObject(kvp.Value);
+ TObject? obj = GetObject(player2);
if (obj == null)
continue;
foreach (var syncVar in SyncVars)
{
- player.SendFakeSyncVar(kvp.Value.NetworkIdentity, syncVar.typeName, syncVar.methodName, syncVar.getFakeSyncVar(kvp.Value, obj));
+ object? value = syncVar.getFakeSyncVar(player2, obj);
+ if (value == null)
+ continue;
+
+ player.SendFakeSyncVar(player2.NetworkIdentity, syncVar.typeName, syncVar.methodName, value);
count++;
}
}
@@ -56,22 +59,24 @@ protected override void RefreshToEnabled(XPPlayer player)
LogDebug(GetType().Name + " sent all (" + count + ") to " + player.Nickname + " (enabled)");
}
- protected override void RefreshToDisabled(XPPlayer player)
+ protected override void RefreshToDisabled(BaseXPPlayer player)
{
int count = 0;
- foreach (var kvp in XPPlayer.Players)
+ foreach (BaseXPPlayer player2 in XPPlayer.PlayersRealConnected)
{
- if (kvp.Value.IsNPC)
- continue;
- if (player == kvp.Value)
+ if (player == player2)
continue;
- if (!ShouldEdit(kvp.Value))
+ if (!ShouldEdit(player2))
continue;
foreach (var syncVar in SyncVars)
{
- player.SendFakeSyncVar(kvp.Value.NetworkIdentity, syncVar.typeName, syncVar.methodName, syncVar.getResyncVar(player));
+ object? value = syncVar.getResyncVar(player2);
+ if (value == null)
+ continue;
+
+ player.SendFakeSyncVar(player2.NetworkIdentity, syncVar.typeName, syncVar.methodName, value);
count++;
}
}
@@ -79,23 +84,29 @@ protected override void RefreshToDisabled(XPPlayer player)
LogDebug(GetType().Name + " sent all (" + count + ") to " + player.Nickname + " (disabled)");
}
- protected override void RefreshOfEnabled(XPPlayer player, PlayerInfoWrapper playerInfo)
+ protected override void RefreshOfEnabled(BaseXPPlayer player, PlayerInfoWrapper? playerInfo)
{
if (!ShouldEdit(player))
return;
- TObject obj = GetObject(player, playerInfo, true);
+ TObject? obj = GetObject(player, playerInfo, true);
if (obj == null)
return;
int count = 0;
foreach (var syncVar in SyncVars)
- count += player.SendFakeSyncVars(x => ShouldShowTo(player, x), syncVar.typeName, syncVar.methodName, syncVar.getFakeSyncVar(player, obj));
+ {
+ object? value = syncVar.getFakeSyncVar(player, obj);
+ if (value == null)
+ continue;
+
+ count += player.SendFakeSyncVars(x => ShouldShowTo(player, x), syncVar.typeName, syncVar.methodName, value);
+ }
LogDebug(GetType().Name + " of " + player.Nickname + " sent to all (" + count + ") (enabled)");
}
- protected override void RefreshOfDisabled(XPPlayer player)
+ protected override void RefreshOfDisabled(BaseXPPlayer player)
{
if (!ShouldEdit(player))
return;
@@ -113,15 +124,15 @@ protected override void RefreshOfDisabled(XPPlayer player)
/// The player's info to create the object from.
/// Whether or not to force creation of a new object, even if one already exists.
/// The object for the player.
- protected virtual TObject GetObject(XPPlayer player, PlayerInfoWrapper playerInfo = null, bool update = false)
+ protected virtual TObject? GetObject(BaseXPPlayer player, PlayerInfoWrapper? playerInfo = null, bool update = false)
{
- if (VariableKey == null)
- return default;
-
- if (update || !player.Variables.TryGet(VariableKey, out TObject obj))
+ if (update || !player.Variables.TryGet(VariableKey, out TObject? obj))
{
- obj = CreateObject(player, playerInfo ?? player.GetPlayerInfo());
- player.Variables.Set(VariableKey, obj);
+ if (playerInfo == null && player is XPPlayer xpPlayer)
+ playerInfo = xpPlayer.GetPlayerInfo();
+
+ obj = CreateObject(player, playerInfo);
+ player.Variables.Set(VariableKey, obj!);
}
return obj;
@@ -132,7 +143,7 @@ protected virtual TObject GetObject(XPPlayer player, PlayerInfoWrapper playerInf
///
/// The player to check.
/// Whether or not the player's data should be displayed.
- protected virtual bool ShouldEdit(XPPlayer player) => true;
+ protected virtual bool ShouldEdit(BaseXPPlayer player) => true;
///
/// Whether or not the player's data should be shown to the target player.
@@ -140,6 +151,6 @@ protected virtual TObject GetObject(XPPlayer player, PlayerInfoWrapper playerInf
/// The player to show the data of.
/// The target player to show the data to.
/// Whether or not the player's data should be shown to the target player.
- protected virtual bool ShouldShowTo(XPPlayer player, XPPlayer target) => !target.IsNPC;
+ protected virtual bool ShouldShowTo(BaseXPPlayer player, BaseXPPlayer target) => true;
}
}
\ No newline at end of file
diff --git a/XPSystem/API/DisplayProviders/XPDisplayProvider.cs b/XPSystem/API/DisplayProviders/XPDisplayProvider.cs
index 92be478..0e56f5e 100644
--- a/XPSystem/API/DisplayProviders/XPDisplayProvider.cs
+++ b/XPSystem/API/DisplayProviders/XPDisplayProvider.cs
@@ -1,24 +1,27 @@
namespace XPSystem.API
{
using System;
+ using System.IO;
using XPSystem.API.DisplayProviders;
+ using XPSystem.API.Player;
using XPSystem.API.StorageProviders;
+ using static XPAPI;
///
- /// Base class for xp display providers, shows the xp/lvl of a player to others.
+ /// Base class for XP display providers, shows the XP/level of a player to others.
///
- public abstract class XPDisplayProvider : IXPDisplayProvider where T : IXPDisplayProviderConfig, new()
+ public abstract class XPDisplayProvider : ConfigXPDisplayProvider where T : IXPDisplayProviderConfig, new()
{
///
/// Gets the patch category.
/// Null to disable patching.
///
- protected virtual string PatchCategory => null;
+ protected virtual string? PatchCategory => null;
///
/// Enables the display provider.
///
- public virtual void Enable()
+ public override void Enable()
{
if (PatchCategory != null)
{
@@ -32,7 +35,7 @@ public virtual void Enable()
///
/// Disables the display provider.
///
- public virtual void Disable()
+ public override void Disable()
{
if (PatchCategory != null)
{
@@ -50,20 +53,18 @@ public virtual void Disable()
///
/// implementation when enabled.
///
- protected virtual void RefreshToEnabled(XPPlayer player) {}
+ protected virtual void RefreshToEnabled(BaseXPPlayer player) {}
///
/// implementation when disabled.
///
- protected virtual void RefreshToDisabled(XPPlayer player) {}
+ protected virtual void RefreshToDisabled(BaseXPPlayer player) {}
/// See .
- public virtual void RefreshTo(XPPlayer player)
+ public override void RefreshTo(BaseXPPlayer player)
{
if (!Config.Enabled && !HasSet)
return;
- if (player.IsNPC)
- return;
if (Config.Enabled)
{
@@ -79,22 +80,24 @@ public virtual void RefreshTo(XPPlayer player)
///
/// implementation when enabled.
///
- protected virtual void RefreshOfEnabled(XPPlayer player, PlayerInfoWrapper playerInfo) {}
+ protected virtual void RefreshOfEnabled(BaseXPPlayer player, PlayerInfoWrapper? playerInfo) {}
///
/// implementation when disabled.
///
- protected virtual void RefreshOfDisabled(XPPlayer player) {}
+ protected virtual void RefreshOfDisabled(BaseXPPlayer player) {}
/// See .
- public virtual void RefreshOf(XPPlayer player, PlayerInfoWrapper playerInfo = null)
+ public override void RefreshOf(BaseXPPlayer player, PlayerInfoWrapper? playerInfo = null)
{
if (!Config.Enabled && !HasSet)
return;
if (Config.Enabled)
{
- RefreshOfEnabled(player, playerInfo ?? player.GetPlayerInfo());
+ if (player is XPPlayer xpPlayer)
+ playerInfo ??= xpPlayer.GetPlayerInfo();
+ RefreshOfEnabled(player, playerInfo);
HasSet = true;
}
else
@@ -106,13 +109,13 @@ public virtual void RefreshOf(XPPlayer player, PlayerInfoWrapper playerInfo = nu
///
/// Refreshes the displays of all players.
///
- public virtual void RefreshAll()
+ public override void RefreshAll()
{
if (!Config.Enabled && !HasSet)
return;
- foreach (var kvp in XPPlayer.Players)
- RefreshOf(kvp.Value, kvp.Value.GetPlayerInfo());
+ foreach (BaseXPPlayer player in XPPlayer.PlayersRealConnected)
+ RefreshOf(player, player is XPPlayer xpPlayer ? xpPlayer.GetPlayerInfo() : null);
HasSet = Config.Enabled;
}
@@ -120,21 +123,45 @@ public virtual void RefreshAll()
///
/// The config instance.
///
- protected T Config { get; set; }
+ public T Config { get; private set; } = default!;
+ internal override IXPDisplayProviderConfig ConfigPropertyInternal => Config;
///
- /// Ignore this, used by loader.
+ /// Ignore, used by loader.
///
- IXPDisplayProviderConfig IXPDisplayProvider.ConfigPropertyInternal
+ internal override void LoadConfig(string folder)
{
- get => Config ?? new T();
- set => Config = (T)value;
+ string name = GetType().Name;
+ string file = Path.Combine(folder, name + ".yml");
+
+ if (File.Exists(file))
+ {
+ try
+ {
+ Config = Deserializer.Deserialize(File.ReadAllText(file));
+ }
+ catch (Exception e)
+ {
+ LogError($"Error loading display provider config for {name}: {e}");
+ }
+ }
+ else
+ {
+ Config = new T();
+ File.WriteAllText(file, Serializer.Serialize(Config));
+ }
}
+ }
- ///
- /// Ignore this, used by loader.
- /// Type of .
- ///
- public Type ConfigTypeInternal { get; } = typeof(T);
+ public abstract class ConfigXPDisplayProvider : IXPDisplayProvider
+ {
+ public abstract void Enable();
+ public abstract void Disable();
+ public abstract void RefreshTo(BaseXPPlayer player);
+ public abstract void RefreshOf(BaseXPPlayer player, PlayerInfoWrapper? playerInfo = null);
+ public abstract void RefreshAll();
+
+ internal abstract IXPDisplayProviderConfig ConfigPropertyInternal { get; }
+ internal abstract void LoadConfig(string folder);
}
}
\ No newline at end of file
diff --git a/XPSystem/API/DisplayProviders/XPDisplayProviderCollection.cs b/XPSystem/API/DisplayProviders/XPDisplayProviderCollection.cs
index 0bb6408..42ae8ef 100644
--- a/XPSystem/API/DisplayProviders/XPDisplayProviderCollection.cs
+++ b/XPSystem/API/DisplayProviders/XPDisplayProviderCollection.cs
@@ -3,8 +3,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
- using System.IO;
using System.Linq;
+ using XPSystem.API.Player;
using XPSystem.API.StorageProviders;
using static XPAPI;
@@ -47,7 +47,7 @@ public void RefreshTo(XPPlayer player)
///
/// The player to refresh the displays of.
/// The player's info to refresh the displays using.
- public void RefreshOf(XPPlayer player, PlayerInfoWrapper playerInfo = null)
+ public void RefreshOf(XPPlayer player, PlayerInfoWrapper? playerInfo = null)
{
playerInfo ??= player.GetPlayerInfo();
@@ -97,7 +97,7 @@ public void Enable()
{
foreach (IXPDisplayProvider provider in _providers)
{
- if (provider.ConfigPropertyInternal?.Enabled == true)
+ if (provider is not ConfigXPDisplayProvider config || config.ConfigPropertyInternal?.Enabled == true)
{
try
{
@@ -119,28 +119,8 @@ public void LoadConfigs(string folder)
{
foreach (IXPDisplayProvider provider in Providers)
{
- string name = provider.GetType().Name;
- string file = Path.Combine(folder, name + ".yml");
-
- if (File.Exists(file))
- {
- try
- {
- provider.ConfigPropertyInternal = (IXPDisplayProviderConfig)
- Deserializer.Deserialize(File.ReadAllText(file), provider.ConfigTypeInternal);
- }
- catch (Exception e)
- {
- LogError($"Error loading xpdisplayprovider config for {name}: {e}");
- }
- }
- else
- {
- IXPDisplayProviderConfig obj = provider.ConfigPropertyInternal;
-
- File.WriteAllText(file, XPAPI.Serializer.Serialize(obj));
- provider.ConfigPropertyInternal = obj;
- }
+ if (provider is ConfigXPDisplayProvider config)
+ config.LoadConfig(folder);
}
}
diff --git a/XPSystem/API/Exceptions/InvalidPlayerIdException.cs b/XPSystem/API/Exceptions/InvalidPlayerIdException.cs
deleted file mode 100644
index 2277c8b..0000000
--- a/XPSystem/API/Exceptions/InvalidPlayerIdException.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace XPSystem.API.Exceptions
-{
- using System;
-
- public class InvalidPlayerIdException : Exception
- {
- public InvalidPlayerIdException() : base("The PlayerId for the specified player is invalid! Are you trying to modify the xp for the host?")
- {
- }
- }
-}
\ No newline at end of file
diff --git a/XPSystem/API/IMessagingProvider.cs b/XPSystem/API/IMessagingProvider.cs
index e0fb0b1..f6f6d85 100644
--- a/XPSystem/API/IMessagingProvider.cs
+++ b/XPSystem/API/IMessagingProvider.cs
@@ -1,10 +1,12 @@
namespace XPSystem.API
{
+ using XPSystem.API.Player;
+
///
/// Interface for a messaging provider, used to display messages to players.
///
public interface IMessagingProvider
{
- void DisplayMessage(XPPlayer player, string message, float duration);
+ void DisplayMessage(BaseXPPlayer player, string message, float duration);
}
}
\ No newline at end of file
diff --git a/XPSystem/API/Legacy/LiteDBMigrator.cs b/XPSystem/API/Legacy/LiteDBMigrator.cs
index 12fdf1e..4016ea6 100644
--- a/XPSystem/API/Legacy/LiteDBMigrator.cs
+++ b/XPSystem/API/Legacy/LiteDBMigrator.cs
@@ -76,7 +76,7 @@ public static int ImportLegacyDB(LiteDatabase db)
XP = log.XP + LevelCalculator.GetXP(log.LVL)
};
- StorageProvider.SetPlayerInfo(playerInfo);
+ StorageProvider!.SetPlayerInfo(playerInfo);
count++;
if (count % 100 == 0) LogInfo($"Imported {count}/{total} players.");
diff --git a/XPSystem/API/Legacy/PlayerLog.cs b/XPSystem/API/Legacy/PlayerLog.cs
index 4b6a152..68345ef 100644
--- a/XPSystem/API/Legacy/PlayerLog.cs
+++ b/XPSystem/API/Legacy/PlayerLog.cs
@@ -12,6 +12,6 @@ public class PlayerLog
public int LVL { get; set; }
public int XP { get; set; }
[BsonId]
- public string ID { get; set; }
+ public string ID { get; set; } = null!;
}
}
\ No newline at end of file
diff --git a/XPSystem/API/LevelCalculator.cs b/XPSystem/API/LevelCalculator.cs
index 8db1b27..e5fdb9b 100644
--- a/XPSystem/API/LevelCalculator.cs
+++ b/XPSystem/API/LevelCalculator.cs
@@ -14,17 +14,17 @@ public static class LevelCalculator
///
/// Gets the used for the s.
///
- public static ExpressionContext Context { get; private set; }
+ public static ExpressionContext Context { get; private set; } = null!;
///
/// Gets the used to calculate the level.
///
- public static IGenericExpression Expression { get; private set; }
+ public static IGenericExpression Expression { get; private set; } = null!;
///
/// Gets the inverse of the . Used to calculate the XP needed for a level.
///
- public static IGenericExpression InverseExpression { get; private set; }
+ public static IGenericExpression InverseExpression { get; private set; } = null!;
///
/// Gets the level of the specified .
diff --git a/XPSystem/API/LoaderSpecific.cs b/XPSystem/API/LoaderSpecific.cs
index 952f945..7448bde 100644
--- a/XPSystem/API/LoaderSpecific.cs
+++ b/XPSystem/API/LoaderSpecific.cs
@@ -33,7 +33,7 @@ public static class LoaderSpecific
///
/// The player's nickname, user ID, or player ID.
/// The if found, otherwise null.
- public static ReferenceHub GetHub(string data)
+ public static ReferenceHub? GetHub(string data)
{
if (string.IsNullOrWhiteSpace(data))
return null;
diff --git a/XPSystem/API/XPPlayer.cs b/XPSystem/API/Player/BaseXPPlayer.cs
similarity index 58%
rename from XPSystem/API/XPPlayer.cs
rename to XPSystem/API/Player/BaseXPPlayer.cs
index 7acf339..fd097ac 100644
--- a/XPSystem/API/XPPlayer.cs
+++ b/XPSystem/API/Player/BaseXPPlayer.cs
@@ -1,31 +1,22 @@
-namespace XPSystem.API
+namespace XPSystem.API.Player
{
using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using CommandSystem;
using Hints;
using Mirror;
using PlayerRoles;
- using RemoteAdmin;
- using XPSystem.API.StorageProviders.Models;
using XPSystem.API.Variables;
- using XPSystem.Config.Models;
using static LoaderSpecific;
///
- /// wrapper.
+ /// wrapper. Kinda like the one found in plugin frameworks.
///
- /// Multiple instances of this class can exist for the same player, instances are not saved, there is no constructor logic.
- public partial class XPPlayer
+ public class BaseXPPlayer
{
- public static IReadOnlyDictionary Players => PlayersValue;
- internal static readonly Dictionary PlayersValue = new();
-
///
/// The player's .
///
public readonly ReferenceHub Hub;
+ internal BaseXPPlayer(ReferenceHub hub) => Hub = hub;
///
/// Gets the player's user ID.
@@ -37,17 +28,6 @@ public partial class XPPlayer
///
public readonly VariableCollection Variables = new();
- ///
- /// Gets the player's .
- ///
- public IPlayerId PlayerId { get; }
-
- ///
- /// Gets whether or not the player is a npc.
- /// AddXP will return unless forced, if this is true.
- ///
- public bool IsNPC { get; }
-
///
/// Gets the player's nickname.
///
@@ -56,7 +36,7 @@ public partial class XPPlayer
///
/// Gets the player's display name.
///
- public string DisplayName => Hub.nicknameSync.Network_displayName;
+ public string? DisplayName => Hub.nicknameSync.Network_displayName;
///
/// Gets the name that will be displayed to other players.
@@ -71,17 +51,23 @@ public partial class XPPlayer
///
/// Gets whether or not the player is connected to the server.
///
- public bool IsConnected => GetIsConnectedSafe();
+ public bool IsConnected => Hub && Hub.gameObject;
+
+ ///
+ /// Gets whether or not the player is a NPC.
+ ///
+ public bool IsNPC => Hub.IsHost || CheckNPC(Hub);
///
/// Gets whether or not the player has do not track enabled.
+ /// Should always be false unless the creation was forced (which you shouldn't do).
///
public bool DNT => Hub.authManager.DoNotTrack;
///
/// Gets the player's group.
///
- public UserGroup Group => Hub.serverRoles.Group;
+ public UserGroup? Group => Hub.serverRoles.Group;
///
/// Gets whether or not the player has a badge.
@@ -101,12 +87,12 @@ public partial class XPPlayer
///
/// Gets the player's badge text.
///
- public string BadgeText => Hub.serverRoles.Network_myText;
+ public string? BadgeText => Hub.serverRoles.Network_myText;
///
/// Gets the player's badge color.
///
- public string BadgeColor => Hub.serverRoles.Network_myColor;
+ public string? BadgeColor => Hub.serverRoles.Network_myColor;
///
/// Gets whether or not the player can view hidden badges.
@@ -125,12 +111,6 @@ public partial class XPPlayer
///
public RoundSummary.LeadingTeam LeadingTeam => Role.GetTeam().GetLeadingTeam();
- ///
- /// Gets or sets the player's XP multiplier.
- /// All XP added by methods (all built-in except for directly via non-wrapper) that respect this value will be multiplied by this value.
- ///
- public float XPMultiplier { get; set; } = 1f;
-
public void ShowHint(string message, float duration = 3f)
{
Hub.hints.Show(new TextHint(message, new HintParameter[]
@@ -169,164 +149,8 @@ public bool CheckPermission(string permission)
{
return LoaderSpecific.CheckPermission(Hub, permission);
}
-
- ///
- /// Creates a new instance of for the specified .
- ///
- /// The player's to wrap.
- private XPPlayer(ReferenceHub referenceHub)
- {
- if (referenceHub == null)
- throw new ArgumentNullException(nameof(referenceHub));
-
-#if DEBUG
- if (referenceHub.IsHost)
- {
- var stackTrace = new StackTrace();
- LogInfo("XPPlayer for Dedicated Server was created: " + stackTrace);
- }
-#endif
-
- Hub = referenceHub;
- IsNPC = referenceHub.IsHost || CheckNPC(referenceHub);
-
- if (UserId.TryParseUserId(out var playerId))
- {
- PlayerId = playerId;
- }
- else if (!IsNPC)
- {
- LogWarn("PlayerId "
- + (string.IsNullOrWhiteSpace(UserId) ? "(empty)" : UserId)
- + " could not be parsed for player "
- + DisplayedName);
- }
-
- PlayersValue.Add(referenceHub, this);
- }
-
- ///
- /// Gets a player from their .
- ///
- /// The of the player.
- /// The player.
- public static XPPlayer Get(ReferenceHub hub)
- {
- if (Players.TryGetValue(hub, out XPPlayer player))
- return player;
-
- player = new XPPlayer(hub);
- return player;
- }
-
- ///
- /// Attempts to get a player using a .
- ///
- /// The of the player.
- /// The player, if on the server.
- /// Whether or not the playerid is valid and player is on the server.
- public static bool TryGet(IPlayerId playerId, out XPPlayer player)
- {
- return TryGet(playerId.ToString(), out player);
- }
-
- public static bool TryGet(ICommandSender sender, out XPPlayer player)
- {
- if (sender is not PlayerCommandSender playerSender)
- {
- player = null;
- return false;
- }
-
- player = Get(playerSender.ReferenceHub);
- return true;
- }
-
- ///
- /// Just with extra return.
- ///
- public static bool TryGet(string data, out XPPlayer player)
- {
- player = null;
- if (string.IsNullOrWhiteSpace(data))
- return false;
-
- ReferenceHub hub = GetHub(data);
- if (hub == null)
- return false;
-
- player = Get(hub);
- return true;
- }
-
- ///
- /// Attempts to get a player of a and checks if they have a permission.
- ///
- /// The sender to get the player from.
- /// The permission to check for.
- /// The player if found, otherwise null.
- /// Whether or not the player was found and has the permission.
- public static bool TryGetAndCheckPermission(ICommandSender sender, string permission, out XPPlayer player)
- {
- return TryGet(sender, out player) && player.CheckPermission(permission);
- }
-
- ///
- /// Sets the player's badge.
- ///
- /// The badge to set.
- /// Whether or not the badge is actually set on the server and not just synced to the clients.
- public void SetBadge(Badge badge, bool fakeSyncVar)
- {
- if (badge == null)
- throw new ArgumentNullException(nameof(badge));
-
- if (fakeSyncVar)
- {
- SendFakeSyncVars(typeof(ServerRoles), nameof(ServerRoles.Network_myText), badge.Text);
- SendFakeSyncVars(typeof(ServerRoles), nameof(ServerRoles.Network_myColor), badge.Color);
- }
- else
- {
- Hub.serverRoles.SetText(badge.Text);
- Hub.serverRoles.SetColor(badge.Color);
- }
- }
-
- ///
- /// Sets the player's nickname.
- ///
- /// The nickname to set.
- /// Whether or not the nickname is actually set on the server and not just synced to the clients.
- public void SetNick(string nick, bool fakeSyncVar)
- {
- if (string.IsNullOrWhiteSpace(nick))
- throw new ArgumentNullException(nameof(nick));
-
- if (fakeSyncVar)
- {
- SendFakeSyncVars(typeof(NicknameSync), nameof(NicknameSync.Network_displayName), nick);
- }
- else
- {
- Hub.nicknameSync.DisplayName = nick;
- }
- }
-
- // Because unity do be like that
- private bool GetIsConnectedSafe()
- {
- try
- {
- return Hub.gameObject != null;
- }
- catch (NullReferenceException)
- {
- return false;
- }
- }
-
-#region Exiled sync stuff (Source: https://github.com/Exiled-Team/EXILED/blob/master/Exiled.API/Extensions/MirrorExtensions.cs)
+
+ #region Exiled sync stuff (Source: https://github.com/Exiled-Team/EXILED/blob/master/Exiled.API/Extensions/MirrorExtensions.cs)
///
/// Send fake values to the client's .
///
@@ -380,12 +204,12 @@ public int SendFakeSyncVars(Type targetType, string propertyName, object value,
payload = writer.ToArraySegment(),
};
- foreach (ReferenceHub referenceHub in ReferenceHub.AllHubs)
+ foreach (ReferenceHub hub in ReferenceHub.AllHubs)
{
- if (skipSelf && referenceHub == Hub)
+ if (skipSelf && hub == Hub)
continue;
- referenceHub.connectionToClient.Send(message);
+ hub.connectionToClient.Send(message);
count++;
}
@@ -410,7 +234,7 @@ void CustomSyncVarGenerator(NetworkWriter targetWriter)
/// Value to send to people who don't match the condition.
/// Whether or not to skip sending to self.
/// The amount of people the first value was sent to.
- public int SendFakeSyncVars(Func condition, Type targetType, string propertyName, object value, object value2 = null, bool skipSelf = false)
+ public int SendFakeSyncVars(Func condition, Type targetType, string propertyName, object value, object value2 = null!, bool skipSelf = false)
{
if (!IsConnected)
return -1;
@@ -420,7 +244,7 @@ public int SendFakeSyncVars(Func condition, Type targetType, str
MakeCustomSyncWriter(Hub.networkIdentity, targetType, CustomSyncVarGenerator, writer);
EntityStateMessage message2 = default;
- if (value2 != null)
+ if (value2 != null!)
{
NetworkWriterPooled writer2 = NetworkWriterPool.Get();
MakeCustomSyncWriter(Hub.networkIdentity, targetType, CustomSyncVarGeneratorValue2, writer2);
@@ -440,19 +264,19 @@ public int SendFakeSyncVars(Func condition, Type targetType, str
payload = writer.ToArraySegment(),
};
- foreach (var kvp in Players)
+ foreach (BaseXPPlayer player in XPPlayer.PlayersRealConnected)
{
- if (skipSelf && kvp.Value == this)
+ if (skipSelf && player == this)
continue;
- if (condition(kvp.Value))
+ if (condition(player))
{
- kvp.Value.Hub.connectionToClient.Send(message);
+ player.Hub.connectionToClient.Send(message);
count++;
}
else if (value2 != null)
{
- kvp.Value.Hub.connectionToClient.Send(message2);
+ player.Hub.connectionToClient.Send(message2);
}
}
@@ -484,10 +308,10 @@ public void ResyncSyncVar(Type targetType, string propertyName) =>
Hub.gameObject.GetComponent(targetType),
new object[] { SyncVarDirtyBits[$"{targetType.Name}.{propertyName}"] });
- private static void MakeCustomSyncWriter(NetworkIdentity behaviorOwner, Type targetType, Action customSyncVar, NetworkWriter owner)
+ private static void MakeCustomSyncWriter(NetworkIdentity behaviorOwner, Type targetType, Action? customSyncVar, NetworkWriter owner)
{
ulong value = 0;
- NetworkBehaviour behaviour = null;
+ NetworkBehaviour behaviour = null!;
// Get NetworkBehaviors index (behaviorDirty use index)
for (int i = 0; i < behaviorOwner.NetworkBehaviours.Length; i++)
@@ -522,17 +346,17 @@ private static void MakeCustomSyncWriter(NetworkIdentity behaviorOwner, Type tar
}
#endregion
- ///
- public static implicit operator XPPlayer(ReferenceHub hub) => Get(hub);
-
+ ///
+ public static implicit operator BaseXPPlayer(ReferenceHub hub) => XPPlayer.Get(hub);
///
- public static implicit operator ReferenceHub(XPPlayer player) => player.Hub;
-
+ public static implicit operator ReferenceHub(BaseXPPlayer player) => player.Hub;
+
#if EXILED
- ///
- public static implicit operator XPPlayer(Exiled.API.Features.Player player) => Get(player.ReferenceHub);
+ ///
+ public static implicit operator BaseXPPlayer(Exiled.API.Features.Player player) => XPPlayer.Get(player.ReferenceHub);
#else
- public static implicit operator XPPlayer(LabApi.Features.Wrappers.Player player) => Get(player.ReferenceHub);
+ ///
+ public static implicit operator BaseXPPlayer(LabApi.Features.Wrappers.Player player) => Get(player.ReferenceHub);
#endif
}
}
\ No newline at end of file
diff --git a/XPSystem/API/Player/XPPlayer.cs b/XPSystem/API/Player/XPPlayer.cs
new file mode 100644
index 0000000..43819fd
--- /dev/null
+++ b/XPSystem/API/Player/XPPlayer.cs
@@ -0,0 +1,171 @@
+namespace XPSystem.API.Player
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Linq;
+ using CommandSystem;
+ using RemoteAdmin;
+ using XPSystem.API.StorageProviders.Models;
+ using static LoaderSpecific;
+
+ ///
+ /// Extended , for players that can actually gain XP.
+ ///
+ public class XPPlayer : BaseXPPlayer
+ {
+ public static IEnumerable Players => PlayersValue.Values;
+ public static IEnumerable PlayersRealConnected => PlayersValue.Values
+ .Where(x => x.IsConnected && !x.IsNPC);
+ public static IEnumerable XPPlayers => PlayersRealConnected
+ .Where(x => x is XPPlayer)
+ .Cast();
+
+ internal static readonly Dictionary PlayersValue = new();
+
+ ///
+ /// Gets the player's .
+ ///
+ public IPlayerId PlayerId { get; }
+
+ ///
+ /// Gets or sets the player's XP multiplier.
+ /// All XP added by methods (all built-in except for directly via non-wrapper) that respect this value will be multiplied by this value.
+ ///
+ public float XPMultiplier { get; set; } = 1f;
+
+ ///
+ /// Checks if the player can create an instance.
+ /// This is true if the player is not the host and is not an NPC and does not have DNT enabled.
+ ///
+ /// The player to check.
+ /// Whether or not the player can create an instance.
+ private static bool CanCreate(BaseXPPlayer player)
+ {
+ if (player is not { IsConnected: true })
+ return false;
+ return player is { IsNPC: false, DNT: false };
+ }
+
+ private XPPlayer(BaseXPPlayer player, IPlayerId playerId) : base(player.Hub) => PlayerId = playerId;
+ ///
+ /// Attempts to get a player using a .
+ /// Will return a if a cannot be created (see ).
+ ///
+ /// The of the player.
+ /// The (or derivative) instance of the player.
+ /// Thrown if is null.
+ public static BaseXPPlayer Get(ReferenceHub hub)
+ {
+ if (!hub)
+ throw new ArgumentNullException(nameof(hub));
+
+ if (PlayersValue.TryGetValue(hub, out BaseXPPlayer player))
+ return player;
+
+ player = new BaseXPPlayer(hub);
+ if (CanCreate(player))
+ {
+ if (player.UserId.TryParseUserId(out IPlayerId? playerId))
+ player = new XPPlayer(player, playerId);
+ else
+ LogError("Could not parse user ID of player to player ID: " + player.UserId);
+ }
+
+ PlayersValue.Add(hub, player);
+ return player;
+ }
+
+ ///
+ /// but with extra cast.
+ /// Also won't throw on null hub! (Dummies and patching do that)
+ ///
+ public static bool TryGetXP(ReferenceHub? hub, [NotNullWhen(true)] out XPPlayer? player)
+ {
+ player = null;
+ if (!hub)
+ return false;
+
+ player = Get(hub) as XPPlayer;
+ return player != null;
+ }
+
+ ///
+ /// Attempts to get a player using an .
+ ///
+ /// The of the player.
+ /// The player, if on the server.
+ /// Whether or not the player ID is valid and player is on the server.
+ public static bool TryGet(IPlayerId playerId, [NotNullWhen(true)] out BaseXPPlayer? player)
+ {
+ return TryGet(playerId.ToString(), out player);
+ }
+
+ ///
+ /// but extra cast.
+ ///
+ public static bool TryGetXP(IPlayerId playerId, [NotNullWhen(true)] out XPPlayer? player)
+ {
+ player = null;
+ return TryGet(playerId, out BaseXPPlayer? basePlayer)
+ && basePlayer is XPPlayer xpPlayer
+ && (player = xpPlayer) != null;
+ }
+
+ ///
+ /// I wonder...
+ ///
+ public static bool TryGet(ICommandSender sender, [NotNullWhen(true)] out BaseXPPlayer? player)
+ {
+ player = null;
+ if (sender is not PlayerCommandSender playerSender)
+ return false;
+
+ player = Get(playerSender.ReferenceHub);
+ return true;
+ }
+
+ ///
+ /// Just with extra conversion.
+ ///
+ public static bool TryGet(string data, [NotNullWhen(true)] out BaseXPPlayer? player)
+ {
+ player = null;
+ if (string.IsNullOrWhiteSpace(data))
+ return false;
+
+ ReferenceHub? hub = GetHub(data);
+ if (!hub)
+ return false;
+
+ player = Get(hub);
+ return true;
+ }
+
+ ///
+ /// Attempts to get a player of a and checks if they have a permission.
+ ///
+ /// The sender to get the player from.
+ /// The permission to check for.
+ /// The player if found, otherwise null.
+ /// Whether or not the player was found and has the permission.
+ public static bool TryGetAndCheckPermission(ICommandSender sender, string permission, [NotNullWhen(true)] out BaseXPPlayer? player)
+ {
+ return TryGet(sender, out player) && player.CheckPermission(permission);
+ }
+
+ ///
+ public static implicit operator XPPlayer?(ReferenceHub hub) => Get(hub) as XPPlayer;
+
+ ///
+ public static implicit operator ReferenceHub(XPPlayer player) => player.Hub;
+
+#if EXILED
+ ///
+ public static implicit operator XPPlayer?(Exiled.API.Features.Player player) => Get(player.ReferenceHub) as XPPlayer;
+#else
+ ///
+ public static implicit operator XPPlayer?(LabApi.Features.Wrappers.Player player) => Get(player.ReferenceHub) as XPPlayer;
+#endif
+ }
+}
\ No newline at end of file
diff --git a/XPSystem/API/StorageProviders/IStorageProvider.cs b/XPSystem/API/StorageProviders/IStorageProvider.cs
index 0f2dd5c..9b938ae 100644
--- a/XPSystem/API/StorageProviders/IStorageProvider.cs
+++ b/XPSystem/API/StorageProviders/IStorageProvider.cs
@@ -1,6 +1,7 @@
namespace XPSystem.API.StorageProviders
{
using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
using System.IO;
using XPSystem.API.StorageProviders.Models;
@@ -11,7 +12,7 @@ public interface IStorageProvider
{
void Initialize();
void Dispose();
- bool TryGetPlayerInfo(IPlayerId playerId, out PlayerInfoWrapper playerInfo);
+ bool TryGetPlayerInfo(IPlayerId playerId, [NotNullWhen(true)] out PlayerInfoWrapper? playerInfo);
PlayerInfoWrapper GetPlayerInfoAndCreateOfNotExist(IPlayerId playerId);
IEnumerable GetTopPlayers(int count);
void SetPlayerInfo(PlayerInfoWrapper playerInfo);
diff --git a/XPSystem/API/StorageProviders/Models/PlayerInfo.cs b/XPSystem/API/StorageProviders/Models/PlayerInfo.cs
index f4b6fd7..605f673 100644
--- a/XPSystem/API/StorageProviders/Models/PlayerInfo.cs
+++ b/XPSystem/API/StorageProviders/Models/PlayerInfo.cs
@@ -6,10 +6,10 @@
///
public class PlayerInfo
{
- public IPlayerId Player { get; set; }
+ public IPlayerId Player { get; set; } = null!;
public int XP { get; set; }
#if STORENICKS
- public string Nickname { get; set; }
+ public string? Nickname { get; set; }
#endif
}
}
\ No newline at end of file
diff --git a/XPSystem/API/StorageProviders/PlayerInfoWrapper.cs b/XPSystem/API/StorageProviders/PlayerInfoWrapper.cs
index 9334a02..fe631d3 100644
--- a/XPSystem/API/StorageProviders/PlayerInfoWrapper.cs
+++ b/XPSystem/API/StorageProviders/PlayerInfoWrapper.cs
@@ -23,7 +23,7 @@ public class PlayerInfoWrapper
///
/// Gets the stored nickname of the player.
///
- public string Nickname => PlayerInfo.Nickname;
+ public string? Nickname => PlayerInfo.Nickname;
///
/// Gets or sets the amount of XP of the player has.
diff --git a/XPSystem/API/StorageProviders/StorageProvider.cs b/XPSystem/API/StorageProviders/StorageProvider.cs
index 8deedf1..ed08fb5 100644
--- a/XPSystem/API/StorageProviders/StorageProvider.cs
+++ b/XPSystem/API/StorageProviders/StorageProvider.cs
@@ -1,9 +1,12 @@
-namespace XPSystem.API.StorageProviders
+// ReSharper disable MemberCanBePrivate.Global
+namespace XPSystem.API.StorageProviders
{
using System;
using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.IO;
+ using XPSystem.API.Player;
using XPSystem.API.StorageProviders.Models;
- using XPSystem.EventHandlers;
using static XPAPI;
///
@@ -18,19 +21,19 @@ public abstract class StorageProvider : IStorageProvider
///
public virtual void Dispose() => ClearCache();
- public virtual bool TryGetPlayerInfo(IPlayerId playerId, out PlayerInfoWrapper playerInfo)
+ public virtual bool TryGetPlayerInfo(IPlayerId playerId, [NotNullWhen(true)] out PlayerInfoWrapper? playerInfo)
{
if (TryGetFromCache(playerId, out playerInfo))
return true;
- bool result = TryGetPlayerInfoNoCache(playerId, out PlayerInfo playerInfo3);
- playerInfo = playerInfo3;
+ bool result = TryGetPlayerInfoNoCache(playerId, out PlayerInfo? playerInfo3);
+ playerInfo = playerInfo3!;
return result;
}
public virtual PlayerInfoWrapper GetPlayerInfoAndCreateOfNotExist(IPlayerId playerId)
{
- if (TryGetFromCache(playerId, out PlayerInfoWrapper playerInfo))
+ if (TryGetFromCache(playerId, out PlayerInfoWrapper? playerInfo))
return playerInfo;
return GetPlayerInfoAndCreateOfNotExistNoCache(playerId);
@@ -38,7 +41,7 @@ public virtual PlayerInfoWrapper GetPlayerInfoAndCreateOfNotExist(IPlayerId play
public virtual void SetPlayerInfo(PlayerInfoWrapper playerInfo)
{
- if (XPPlayer.TryGet(playerInfo.Player, out XPPlayer player))
+ if (XPPlayer.TryGetXP(playerInfo.Player, out XPPlayer? player))
player.Variables.Set(VariableKey, playerInfo);
SetPlayerInfoNoCache(playerInfo);
@@ -46,7 +49,7 @@ public virtual void SetPlayerInfo(PlayerInfoWrapper playerInfo)
public virtual bool DeletePlayerInfo(IPlayerId playerId)
{
- if (XPPlayer.TryGet(playerId, out XPPlayer player))
+ if (XPPlayer.TryGetXP(playerId, out XPPlayer? player))
player.Variables.Remove(VariableKey);
return DeletePlayerInfoNoCache(playerId);
@@ -58,18 +61,18 @@ public virtual void DeleteAllPlayerInfo()
DeleteAllPlayerInfoNoCache();
}
- protected virtual bool TryGetFromCache(IPlayerId playerId, out PlayerInfoWrapper playerInfo)
+ protected virtual bool TryGetFromCache(IPlayerId playerId, [NotNullWhen(true)] out PlayerInfoWrapper? playerInfo)
{
playerInfo = null;
- if (!XPPlayer.TryGet(playerId, out XPPlayer player))
+ if (!XPPlayer.TryGetXP(playerId, out XPPlayer? player))
{
LogDebug("Player not in server: " + playerId);
playerInfo = null;
return false;
}
- if (!player.Variables.TryGet(VariableKey, out object playerInfoObj))
+ if (!player.Variables.TryGet(VariableKey, out object? playerInfoObj))
{
LogDebug("Player variable cache not found - adding " + playerId);
@@ -89,48 +92,58 @@ protected virtual bool TryGetFromCache(IPlayerId playerId, out PlayerInfoWrapper
public virtual void ClearCache()
{
- foreach (var kvp in XPPlayer.Players)
- kvp.Value.Variables.Remove(VariableKey);
+ foreach (BaseXPPlayer player in XPPlayer.PlayersRealConnected)
+ player.Variables.Remove(VariableKey);
}
public abstract void Initialize();
public abstract IEnumerable GetTopPlayers(int count);
- protected abstract bool TryGetPlayerInfoNoCache(IPlayerId playerId, out PlayerInfo playerInfo);
+ protected abstract bool TryGetPlayerInfoNoCache(IPlayerId playerId, [NotNullWhen(true)] out PlayerInfo? playerInfo);
protected abstract PlayerInfo GetPlayerInfoAndCreateOfNotExistNoCache(IPlayerId playerId);
protected abstract void SetPlayerInfoNoCache(PlayerInfo playerInfo);
protected abstract bool DeletePlayerInfoNoCache(IPlayerId playerId);
protected abstract void DeleteAllPlayerInfoNoCache();
///
- /// Ignore this.
- /// Used by loader when combined with .
- ///
- public virtual object ConfigPropertyInternal { get; set; }
-
- ///
- /// Ignore this.
- /// Used by loader when combined with .
- /// Type of .
+ /// Ignore, used by loader in case of .
///
- public virtual Type ConfigTypeInternal { get; } = null;
+ internal virtual void LoadConfig()
+ {
+ }
}
///
/// template with config.
///
/// The type of the config.
- public abstract class StorageProvider : StorageProvider where T : new()
+ public abstract class StorageProvider : StorageProvider where T : class, new()
{
- public T Config;
+ public T Config { get; private set; } = null!;
- ///
- public override object ConfigPropertyInternal
+ ///
+ /// Ignore, used by loader.
+ ///
+ internal override void LoadConfig()
{
- get => Config ?? new T();
- set => Config = (T)value;
- }
+ string name = GetType().Name;
+ string file = Path.Combine(XPAPI.Config.ExtendedConfigPath, name + ".yml");
- ///
- public override Type ConfigTypeInternal { get; } = typeof(T);
+ if (File.Exists(file))
+ {
+ try
+ {
+ Config = Deserializer.Deserialize(File.ReadAllText(file));
+ }
+ catch (Exception e)
+ {
+ LogError($"Error loading storageprovider config for {name}: {e}");
+ }
+ }
+ else
+ {
+ Config = new T();
+ File.WriteAllText(file, Serializer.Serialize(Config));
+ }
+ }
}
}
\ No newline at end of file
diff --git a/XPSystem/API/Variables/Variable.cs b/XPSystem/API/Variables/Variable.cs
index e6bbc3f..e0269f5 100644
--- a/XPSystem/API/Variables/Variable.cs
+++ b/XPSystem/API/Variables/Variable.cs
@@ -10,7 +10,7 @@ public class Variable
///
/// Gets or sets value of the variable.
///
- public object Value { get; set; }
+ public object? Value { get; set; }
///
/// Gets or sets the expiry time of the variable.
@@ -61,7 +61,7 @@ public T As()
if (Value is T value)
return value;
- throw new InvalidCastException($"Cannot cast {Value.GetType()} to {typeof(T)}");
+ throw new InvalidCastException($"Cannot cast {Value?.GetType().ToString() ?? "null"} to {typeof(T)}");
}
///
diff --git a/XPSystem/API/Variables/VariableCollection.cs b/XPSystem/API/Variables/VariableCollection.cs
index da8c40b..c8dc037 100644
--- a/XPSystem/API/Variables/VariableCollection.cs
+++ b/XPSystem/API/Variables/VariableCollection.cs
@@ -3,13 +3,14 @@
using System;
using System.Collections;
using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
///
/// Represents a collection of variables.
///
public class VariableCollection : IEnumerable>
{
- private Dictionary _variables = new();
+ private readonly Dictionary _variables = new();
///
/// Gets the number of variables in the collection.
@@ -69,7 +70,7 @@ public void Set(string key, object value, DateTime? expiryTime = null)
/// The key of the variable.
/// The variable with the specified key, if found; otherwise, null.
/// Whether or not the variable was found.
- public bool TryGet(string key, out Variable variable)
+ public bool TryGet(string key, [NotNullWhen(true)] out Variable? variable)
{
if (_variables.TryGetValue(key, out variable))
{
@@ -91,11 +92,11 @@ public bool TryGet(string key, out Variable variable)
/// The type to cast the variable to.
/// Whether or not the variable was found and casted.
/// Will throw if the cast fails.
- public bool TryGet(string key, out T value)
+ public bool TryGet(string key, [NotNullWhen(true)] out T? value)
{
- if (TryGet(key, out Variable variable))
+ if (TryGet(key, out Variable? variable))
{
- value = variable.As();
+ value = variable.As()!;
return true;
}
@@ -114,12 +115,12 @@ public bool TryGet(string key, out T value)
/// Gets or sets the variable with the specified key.
///
/// The key of the variable.
- public Variable this[string key]
+ public Variable? this[string key]
{
- get => TryGet(key, out Variable variable)
+ get => TryGet(key, out Variable? variable)
? variable
: null;
- set => _variables[key] = value;
+ set => _variables[key] = value!;
}
///
diff --git a/XPSystem/API/XPAPI.cs b/XPSystem/API/XPAPI.cs
index d1db833..e377acd 100644
--- a/XPSystem/API/XPAPI.cs
+++ b/XPSystem/API/XPAPI.cs
@@ -1,14 +1,16 @@
-namespace XPSystem.API
+// ReSharper disable MemberCanBePrivate.Global
+namespace XPSystem.API
{
using System;
using System.Collections.Generic;
- using System.IO;
+ using System.Diagnostics.CodeAnalysis;
using System.Text;
using NorthwoodLib.Pools;
using PlayerRoles;
using XPSystem.API.DisplayProviders;
using XPSystem.API.Enums;
using XPSystem.API.Exceptions;
+ using XPSystem.API.Player;
using XPSystem.API.StorageProviders;
using XPSystem.API.StorageProviders.Models;
using XPSystem.Config;
@@ -26,6 +28,7 @@ public static class XPAPI
///
/// Whether the plugin is enabled or not.
///
+ // ReSharper disable once UnusedAutoPropertyAccessor.Global
public static bool PluginEnabled { get; internal set; } = false;
///
@@ -35,18 +38,18 @@ public static class XPAPI
///
/// The display provider for the plugin.
- /// May be null ( or uninitialized).
+ /// Null if ( or uninitialized).
///
- public static IMessagingProvider MessagingProvider;
+ public static IMessagingProvider? MessagingProvider;
///
/// The storage provider for the plugin.
/// Change it using .
///
- public static IStorageProvider StorageProvider { get; private set; }
+ public static IStorageProvider? StorageProvider { get; private set; }
///
- /// The xp display providers.
+ /// The XP display providers.
///
public static readonly XPDisplayProviderCollection DisplayProviders = new();
@@ -122,13 +125,12 @@ public static class XPAPI
///
public static Action LogError = LoaderSpecific.LogError;
#endregion
-
#region Storage
///
/// Sets the storage provider for the plugin.
///
/// The storage provider to set.
- public static void SetStorageProvider(IStorageProvider provider)
+ public static void SetStorageProvider(IStorageProvider? provider)
{
try
{
@@ -141,14 +143,15 @@ public static void SetStorageProvider(IStorageProvider provider)
if (provider == null)
{
- LogDebug("Disposed storage provider. No data will be read or saved!");
+ StorageProvider = null;
+ LogInfo("Storage provider set to null, no data will be read or saved!");
return;
}
try
{
- if (provider is StorageProvider { ConfigTypeInternal: not null } storageProvider)
- LoadStorageProviderConfig(storageProvider);
+ if (provider is StorageProvider storageProvider)
+ storageProvider.LoadConfig();
provider.Initialize();
}
@@ -175,7 +178,7 @@ public static void SetStorageProvider(string typeName)
return;
}
- if (Main.TryCreate(typeName, out Exception e, out IStorageProvider provider))
+ if (Main.TryCreate(typeName, out Exception? e, out IStorageProvider? provider))
{
SetStorageProvider(provider);
return;
@@ -185,36 +188,6 @@ public static void SetStorageProvider(string typeName)
LogError("No data will be read or saved!");
}
- ///
- /// Loads the storage providers config.
- ///
- /// The storage provider whose config is to be loaded.
- public static void LoadStorageProviderConfig(StorageProvider provider)
- {
- string name = provider.GetType().Name;
- string file = Path.Combine(Config.ExtendedConfigPath, name + ".yml");
-
- if (File.Exists(file))
- {
- try
- {
- provider.ConfigPropertyInternal =
- Deserializer.Deserialize(File.ReadAllText(file), provider.ConfigTypeInternal);
- }
- catch (Exception e)
- {
- LogError($"Error loading storageprovider config for {name}: {e}");
- }
- }
- else
- {
- object obj = provider.ConfigPropertyInternal;
-
- File.WriteAllText(file, Serializer.Serialize(obj));
- provider.ConfigPropertyInternal = obj;
- }
- }
-
///
/// Ensures that the storage provider is valid.
///
@@ -234,7 +207,7 @@ public static void EnsureStorageProviderValid()
public static PlayerInfoWrapper GetPlayerInfo(IPlayerId playerId)
{
EnsureStorageProviderValid();
- return StorageProvider.GetPlayerInfoAndCreateOfNotExist(playerId);
+ return StorageProvider!.GetPlayerInfoAndCreateOfNotExist(playerId);
}
///
@@ -253,12 +226,12 @@ public static PlayerInfoWrapper GetPlayerInfo(XPPlayer player)
///
/// The player to update the nickname of.
/// The player's . Optional, only pass if you already have it, saves barely any time.
- public static void UpdateNickname(XPPlayer player, PlayerInfoWrapper playerInfo = null)
+ public static void UpdateNickname(XPPlayer player, PlayerInfoWrapper? playerInfo = null)
{
playerInfo ??= GetPlayerInfo(player);
playerInfo.PlayerInfo.Nickname = player.DisplayedName;
- StorageProvider.SetPlayerInfo(playerInfo);
+ StorageProvider!.SetPlayerInfo(playerInfo);
LogDebug("Updated nick of " + player.PlayerId + " to " + player.DisplayedName);
}
#endif
@@ -268,38 +241,32 @@ public static void UpdateNickname(XPPlayer player, PlayerInfoWrapper playerInfo
///
/// The player to add XP to.
/// The amount of XP to add.
- /// Whether to force the addition of XP,
- /// even if
- /// or the player has enabled
- /// or is true.
+ /// Whether to force the addition of XP, even if .
/// The player's . Optional, only pass if you already have it, saves barely any time.
- /// Whether or not the XP was added.
- public static bool AddXP(XPPlayer player, int amount, bool force = false, PlayerInfoWrapper playerInfo = null)
+ /// Whether or not the player leveled up.
+ public static bool AddXP(XPPlayer player, int amount, bool force = false, PlayerInfoWrapper? playerInfo = null)
{
if (amount == 0)
return false;
- if (!force && (XPGainPaused || player.DNT) || player.IsNPC)
+ if (!force && (XPGainPaused || player.DNT))
return false;
- playerInfo ??= GetPlayerInfo(player.PlayerId);
-
- AddXP(playerInfo, amount, player);
- return true;
+ return AddXP(playerInfo ?? GetPlayerInfo(player.PlayerId), amount, player);
}
///
/// The method that actually adds XP to a player.
/// Beware: No checks!
///
- internal static bool AddXP(PlayerInfoWrapper playerInfo, int amount, XPPlayer player = null)
+ internal static bool AddXP(PlayerInfoWrapper playerInfo, int amount, XPPlayer? player = null)
{
if (player == null)
- XPPlayer.TryGet(playerInfo.Player, out player);
+ XPPlayer.TryGetXP(playerInfo.Player, out player);
int prevLevel = playerInfo.Level;
float floatAmount = amount;
- bool connected = player?.IsConnected ?? false;
+ bool connected = player != null;
if (amount > 0 || Config.XPMultiplierForXPLoss)
{
@@ -313,12 +280,16 @@ internal static bool AddXP(PlayerInfoWrapper playerInfo, int amount, XPPlayer pl
amount = (int)floatAmount;
playerInfo.PlayerInfo.XP += amount;
- StorageProvider.SetPlayerInfo(playerInfo);
+ StorageProvider!.SetPlayerInfo(playerInfo);
+
+ if (playerInfo.Level == prevLevel)
+ return false;
- if (connected && playerInfo.Level != prevLevel)
- HandleLevelUp(player, playerInfo, prevLevel);
+ if (connected)
+ HandleLevelUp(player!, playerInfo, prevLevel);
return true;
+
}
#endregion
#region Translations
@@ -328,9 +299,9 @@ internal static bool AddXP(PlayerInfoWrapper playerInfo, int amount, XPPlayer pl
/// The player to display the message to.
/// The message to display.
/// If is null, it will not be displayed.
- public static void DisplayMessage(XPPlayer player, string message)
+ public static void DisplayMessage(BaseXPPlayer player, string? message)
{
- if (string.IsNullOrWhiteSpace(message) || player.IsNPC)
+ if (string.IsNullOrWhiteSpace(message))
return;
MessagingProvider?.DisplayMessage(player, Config.TextPrefix + message + Config.TextSuffix, Config.DisplayDuration);
@@ -339,16 +310,16 @@ public static void DisplayMessage(XPPlayer player, string message)
#region Mixed
///
/// Adds XP to a player and displays it's corresponding message.
- /// Respects role overrides, DNT, and .
+ /// Respects role overrides and .
///
/// The player to affect.
/// The key of the .
/// The subkeys of the .
/// Whether or not the XP was added and the message was sent (can be forced with
- ///
+ ///
/// and ).
/// Uses .
- public static bool AddXPAndDisplayMessage(XPPlayer player, string key, params object[] subkeys)
+ public static bool AddXPAndDisplayMessage(XPPlayer player, string key, params object?[] subkeys)
{
return AddXPAndDisplayMessage(player, XPECManager.GetItem(key, player.Role, subkeys));
}
@@ -359,13 +330,13 @@ public static bool AddXPAndDisplayMessage(XPPlayer player, string key, params ob
///
///
/// Uses .
- public static bool TryAddXPAndDisplayMessage(XPPlayer player, string key, params object[] subkeys)
+ public static bool TryAddXPAndDisplayMessage(XPPlayer? player, string key, params object?[] subkeys)
{
- if (!player.IsConnected)
+ if (player is not { IsConnected: true })
return false;
- XPECFile file = XPECManager.GetFile(key, player.Role);
- XPECItem item = file?.Get(subkeys);
+ XPECFile? file = XPECManager.GetFile(key, player.Role);
+ XPECItem? item = file?.Get(subkeys);
if (item == null)
return false;
@@ -384,18 +355,21 @@ public static bool TryAddXPAndDisplayMessage(XPPlayer player, string key, params
///
/// The player to affect.
/// The containing the amount and the message.
- /// Whether or not the XP was added and the message was sent (can be forced with
- ///
- /// and ).
- public static bool AddXPAndDisplayMessage(XPPlayer player, XPECItem xpecItem)
+ /// Whether or not the XP was added and the message was sent (can be forced with
+ /// and
+ /// ).
+ public static bool AddXPAndDisplayMessage(XPPlayer player, XPECItem? xpecItem)
{
if (xpecItem == null || xpecItem.Amount == 0 || player.DNT || XPGainPaused)
return false;
PlayerInfoWrapper playerInfo = GetPlayerInfo(player.PlayerId);
- AddXP(player, xpecItem.Amount, playerInfo: playerInfo);
+ bool levelup = AddXP(player, xpecItem.Amount, playerInfo: playerInfo);
+
+ if (levelup && !Config.ShowXPOnLevelUp)
+ return true;
- string message = xpecItem.Translation;
+ string? message = xpecItem.Translation;
if (message != null && Config.UseAddedXPTemplate)
{
message = Config.AddedXPTemplate
@@ -451,9 +425,9 @@ public static void HandleLevelUp(XPPlayer player, PlayerInfoWrapper wrapper, int
/// The id value.
/// The of the id.
/// If successful, the created. Otherwise, null.
- public static IPlayerId CreateUserId(object id, AuthType authType)
+ public static IPlayerId? CreateUserId(object id, AuthType authType)
{
- bool EnsureIs(out T obj)
+ bool EnsureIs([NotNullWhen(true)] out T? obj)
{
if (id is T t)
{
@@ -482,7 +456,7 @@ bool EnsureIs(out T obj)
LogDebug("UserId creating failed (not ulong)");
return null;
case AuthType.Northwood:
- if (EnsureIs(out string stringId))
+ if (EnsureIs(out string? stringId))
return new StringPlayerId(stringId, authType);
LogDebug("UserId creating failed (not string)");
@@ -498,7 +472,7 @@ bool EnsureIs(out T obj)
/// The string to parse.
/// The equivalent .
/// Whether or not the parsing was successful.
- public static bool TryParseUserId(string @string, out IPlayerId playerId)
+ public static bool TryParseUserId(string? @string, [NotNullWhen(true)] out IPlayerId? playerId)
{
playerId = null;
if (@string == null)
diff --git a/XPSystem/API/XPExtensions.cs b/XPSystem/API/XPExtensions.cs
index e934bc4..651c33d 100644
--- a/XPSystem/API/XPExtensions.cs
+++ b/XPSystem/API/XPExtensions.cs
@@ -2,7 +2,9 @@
{
using System;
using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
using CommandSystem;
+ using XPSystem.API.Player;
using XPSystem.API.StorageProviders;
using XPSystem.API.StorageProviders.Models;
using XPSystem.Config.Events.Types;
@@ -10,7 +12,7 @@
public static class XPExtensions
{
///
- public static bool TryParseUserId(this string @string, out IPlayerId playerId) => XPAPI.TryParseUserId(@string, out playerId);
+ public static bool TryParseUserId(this string @string, [NotNullWhen(true)] out IPlayerId? playerId) => XPAPI.TryParseUserId(@string, out playerId);
///
public static string FormatLeaderboard(this IEnumerable players) => XPAPI.FormatLeaderboard(players);
@@ -27,17 +29,17 @@ public static class XPExtensions
///
public static PlayerInfoWrapper GetPlayerInfo(this XPPlayer player) => XPAPI.GetPlayerInfo(player);
- ///
- public static void DisplayMessage(this XPPlayer player, string message) => XPAPI.DisplayMessage(player, message);
+ ///
+ public static void DisplayMessage(this BaseXPPlayer player, string message) => XPAPI.DisplayMessage(player, message);
///
public static bool AddXP(this XPPlayer player, int amount, bool notify = false) => XPAPI.AddXP(player, amount, notify);
///
- public static void AddXPAndDisplayMessage(this XPPlayer player, XPECItem item) => XPAPI.AddXPAndDisplayMessage(player, item);
+ public static void TryAddXPAndDisplayMessage(this XPPlayer player, XPECItem? item) => XPAPI.AddXPAndDisplayMessage(player, item);
///
- public static void TryAddXPAndDisplayMessage(this XPPlayer player, string key, params object[] args) => XPAPI.TryAddXPAndDisplayMessage(player, key, args);
+ public static void TryAddXPAndDisplayMessage(this XPPlayer? player, string key, params object?[] args) => XPAPI.TryAddXPAndDisplayMessage(player, key, args);
///
public static string FormatType(this Type type) => XPAPI.FormatType(type);
diff --git a/XPSystem/BuiltInProviders/Display/Patch/MyNickPatchXPDisplayProvider.cs b/XPSystem/BuiltInProviders/Display/Patch/MyNickPatchXPDisplayProvider.cs
index 068aec5..ea63375 100644
--- a/XPSystem/BuiltInProviders/Display/Patch/MyNickPatchXPDisplayProvider.cs
+++ b/XPSystem/BuiltInProviders/Display/Patch/MyNickPatchXPDisplayProvider.cs
@@ -3,18 +3,19 @@
using System.ComponentModel;
using HarmonyLib;
using XPSystem.API;
+ using XPSystem.API.Player;
public class MyNickPatchXPDisplayProvider : XPDisplayProvider
{
public override void RefreshAll() {}
- public override void RefreshTo(XPPlayer player) {}
+ public override void RefreshTo(BaseXPPlayer player) {}
[HarmonyPatch(typeof(NicknameSync), nameof(NicknameSync.Network_myNickSync), MethodType.Setter)]
internal static class NicknamePatch
{
public static void Prefix(NicknameSync __instance, ref string value)
{
- if (!__instance._hub || __instance._hub.IsHost || __instance._hub.IsDummy)
+ if (!XPPlayer.TryGetXP(__instance._hub, out XPPlayer? player))
return;
foreach (IXPDisplayProvider provider in XPAPI.DisplayProviders)
@@ -25,7 +26,7 @@ public static void Prefix(NicknameSync __instance, ref string value)
return;
value = nickProvider.Config.NickStructure
- .Replace("%lvl%", XPAPI.GetPlayerInfo(__instance._hub).Level.ToString())
+ .Replace("%lvl%", XPAPI.GetPlayerInfo(player).Level.ToString())
.Replace("%name%", value);
return;
diff --git a/XPSystem/BuiltInProviders/Display/Patch/NickPatchXPDisplayProvider.cs b/XPSystem/BuiltInProviders/Display/Patch/NickPatchXPDisplayProvider.cs
index e929a98..0439ba3 100644
--- a/XPSystem/BuiltInProviders/Display/Patch/NickPatchXPDisplayProvider.cs
+++ b/XPSystem/BuiltInProviders/Display/Patch/NickPatchXPDisplayProvider.cs
@@ -4,17 +4,21 @@
using System.ComponentModel;
using HarmonyLib;
using XPSystem.API;
+ using XPSystem.API.Player;
using XPSystem.API.StorageProviders;
public class NickPatchXPDisplayProvider : XPDisplayProvider
{
public Dictionary DisplayNameOverrides { get; } = new();
- protected override void RefreshOfEnabled(XPPlayer player, PlayerInfoWrapper playerInfo) => Refresh(player);
- protected override void RefreshOfDisabled(XPPlayer player) => Refresh(player);
+ protected override void RefreshOfEnabled(BaseXPPlayer player, PlayerInfoWrapper? playerInfo) => Refresh(player);
+ protected override void RefreshOfDisabled(BaseXPPlayer player) => Refresh(player);
- private void Refresh(XPPlayer player)
+ private void Refresh(BaseXPPlayer player)
{
+ if (player is not XPPlayer)
+ return;
+
player.Hub.nicknameSync.DisplayName = DisplayNameOverrides.TryGetValue(player.UserId, out string cached)
? cached
: player.DisplayedName;
@@ -25,7 +29,7 @@ internal static class DisplayNamePatch
{
public static void Prefix(NicknameSync __instance, ref string value)
{
- if (!__instance._hub || __instance._hub.IsHost || __instance._hub.IsDummy)
+ if (!XPPlayer.TryGetXP(__instance._hub, out XPPlayer? player))
return;
foreach (IXPDisplayProvider provider in XPAPI.DisplayProviders)
@@ -35,9 +39,9 @@ public static void Prefix(NicknameSync __instance, ref string value)
if (!nickProvider.Config.Enabled)
return;
- nickProvider.DisplayNameOverrides[__instance._hub.authManager.UserId] = value;
+ nickProvider.DisplayNameOverrides[player.UserId] = value;
value = nickProvider.Config.NickStructure
- .Replace("%lvl%", XPAPI.GetPlayerInfo(__instance._hub).Level.ToString())
+ .Replace("%lvl%", XPAPI.GetPlayerInfo(player).Level.ToString())
.Replace("%name%", value);
return;
diff --git a/XPSystem/BuiltInProviders/Display/Patch/RankSetXPDisplayProvider.cs b/XPSystem/BuiltInProviders/Display/Patch/RankSetXPDisplayProvider.cs
index 5a35fd9..f1bfad3 100644
--- a/XPSystem/BuiltInProviders/Display/Patch/RankSetXPDisplayProvider.cs
+++ b/XPSystem/BuiltInProviders/Display/Patch/RankSetXPDisplayProvider.cs
@@ -6,17 +6,17 @@
using HarmonyLib;
using MEC;
using XPSystem.API;
+ using XPSystem.API.Player;
using XPSystem.API.StorageProviders;
- using XPSystem.Config.Models;
using XPSystem.EventHandlers;
using YamlDotNet.Serialization;
public class RankSetXPDisplayProvider : XPDisplayProvider
{
public override void RefreshAll() {}
- public override void RefreshTo(XPPlayer player) {}
+ public override void RefreshTo(BaseXPPlayer player) {}
- private Badge GetBadge(XPPlayer player, PlayerInfoWrapper playerInfo)
+ private Badge? GetBadge(BaseXPPlayer player, PlayerInfoWrapper? playerInfo)
{
if (Config.SkipGlobalBadges && player.HasGlobalBadge)
return null;
@@ -26,17 +26,20 @@ private Badge GetBadge(XPPlayer player, PlayerInfoWrapper playerInfo)
if (player.DNT)
{
- if (player.HasBadge && !player.HasHiddenBadge)
- return null;
-
- return Config.DNTBadge;
+ return player is { HasBadge: true, HasHiddenBadge: false }
+ ? null
+ : Config.DNTBadge;
}
- Badge badge = null;
+ if (player is not XPPlayer xpPlayer)
+ return null;
+ playerInfo ??= xpPlayer.GetPlayerInfo();
+
+ Badge? badge = null;
string format = !player.HasBadge || player.HasHiddenBadge
? Config.BadgeStructureNoBadge
: Config.BadgeStructure;
- string color = "default";
+ string? color = null;
foreach (var kvp in Config.SortedBadges)
{
@@ -59,13 +62,13 @@ private Badge GetBadge(XPPlayer player, PlayerInfoWrapper playerInfo)
.Replace("%lvl%", playerInfo.Level.ToString())
.Replace("%badge%", badge.Text)
.Replace("%oldbadge%", player.BadgeText),
- Color = color
+ Color = color ?? "default"
};
}
- private void Refresh(XPPlayer player, PlayerInfoWrapper playerInfo = null)
+ private void Refresh(BaseXPPlayer player, PlayerInfoWrapper? playerInfo = null)
{
- Badge badge = GetBadge(player, playerInfo ?? XPAPI.GetPlayerInfo(player));
+ Badge? badge = GetBadge(player, playerInfo);
if (badge == null)
return;
@@ -114,13 +117,16 @@ public class HiddenBadgePatchHide
private static void Refresh(ServerRoles instance)
{
+ if (!XPPlayer.TryGetXP(instance._hub, out XPPlayer? player))
+ return;
+
foreach (IXPDisplayProvider provider in XPAPI.DisplayProviders)
{
- if (provider is RankSetXPDisplayProvider rankProvider && rankProvider.Config.Enabled && rankProvider.Config.PatchBadgeCommands)
+ if (provider is RankSetXPDisplayProvider { Config: { Enabled: true, PatchBadgeCommands: true } } rankProvider)
{
Timing.CallDelayed(.5f + XPAPI.Config.ExtraDelay, () =>
{
- rankProvider.Refresh(instance._hub);
+ rankProvider.Refresh(player);
});
return;
@@ -156,10 +162,10 @@ public class RankConfig : IXPDisplayProviderConfig
public Badge DNTBadge { get; set; } = new()
{
Text = "(DNT) anonymous man????",
- Color = Misc.PlayerInfoColorTypes.Nickel.ToString()
+ Color = nameof(Misc.PlayerInfoColorTypes.Nickel)
};
- private Dictionary _sortedBadges;
+ private Dictionary? _sortedBadges = null!;
[YamlIgnore]
public Dictionary SortedBadges => _sortedBadges ??= Badges
.OrderBy(x => x.Key)
@@ -171,27 +177,27 @@ public class RankConfig : IXPDisplayProviderConfig
[0] = new Badge
{
Text = "Visitor",
- Color = Misc.PlayerInfoColorTypes.Cyan.ToString().ToLower()
+ Color = nameof(Misc.PlayerInfoColorTypes.Cyan).ToLower()
},
[1] = new Badge
{
Text = "Junior",
- Color = Misc.PlayerInfoColorTypes.Orange.ToString().ToLower()
+ Color = nameof(Misc.PlayerInfoColorTypes.Orange).ToLower()
},
[5] = new Badge
{
Text = "Senior",
- Color = Misc.PlayerInfoColorTypes.Yellow.ToString().ToLower()
+ Color = nameof(Misc.PlayerInfoColorTypes.Yellow).ToLower()
},
[10] = new Badge
{
Text = "Veteran",
- Color = Misc.PlayerInfoColorTypes.Red.ToString().ToLower()
+ Color = nameof(Misc.PlayerInfoColorTypes.Red).ToLower()
},
[50] = new Badge
{
Text = "Nerd",
- Color = Misc.PlayerInfoColorTypes.Lime.ToString().ToLower()
+ Color = nameof(Misc.PlayerInfoColorTypes.Lime).ToLower()
}
};
}
diff --git a/XPSystem/BuiltInProviders/Display/SyncVar/NickXPDisplayProvider.cs b/XPSystem/BuiltInProviders/Display/SyncVar/NickXPDisplayProvider.cs
index cbd7977..630d24a 100644
--- a/XPSystem/BuiltInProviders/Display/SyncVar/NickXPDisplayProvider.cs
+++ b/XPSystem/BuiltInProviders/Display/SyncVar/NickXPDisplayProvider.cs
@@ -6,19 +6,28 @@
using MEC;
using XPSystem.API;
using XPSystem.API.DisplayProviders;
+ using XPSystem.API.Player;
using XPSystem.API.StorageProviders;
public class NickXPDisplayProvider : SyncVarXPDisplayProvider
{
protected override string VariableKey { get; } = "NickXPDisplayProvider_nick";
- protected override (Type typeName, string methodName, Func getFakeSyncVar, Func getResyncVar)[] SyncVars { get; } =
+ protected override (Type typeName, string methodName, Func getFakeSyncVar, Func getResyncVar)[] SyncVars { get; } =
{
(typeof(NicknameSync), nameof(NicknameSync.Network_displayName), (_, obj) => obj, player => player.DisplayedName)
};
- protected override string CreateObject(XPPlayer player, PlayerInfoWrapper playerInfo)
+ protected override string? CreateObject(BaseXPPlayer player, PlayerInfoWrapper? playerInfo)
{
+ if (playerInfo == null)
+ {
+ if (player is XPPlayer xpPlayer)
+ playerInfo = xpPlayer.GetPlayerInfo();
+ else
+ return null;
+ }
+
return Config.NickStructure
.Replace("%lvl%", playerInfo.Level.ToString())
.Replace("%name%", player.DisplayedName);
@@ -29,16 +38,17 @@ internal static class NickCommandPatch
{
public static void Postfix(NicknameSync __instance)
{
- if (__instance._hub.IsDummy)
+ XPPlayer? player = __instance._hub;
+ if (player == null)
return;
foreach (IXPDisplayProvider provider in XPAPI.DisplayProviders)
{
- if (provider is NickXPDisplayProvider nickProvider && nickProvider.Config.PatchNickCommand)
+ if (provider is NickXPDisplayProvider { Config: { PatchNickCommand: true } } nickProvider)
{
Timing.CallDelayed(.5f + XPAPI.Config.ExtraDelay, () =>
{
- nickProvider.RefreshOf(__instance._hub);
+ nickProvider.RefreshOf(player);
});
return;
diff --git a/XPSystem/BuiltInProviders/Display/SyncVar/RankXPDisplayProvider.cs b/XPSystem/BuiltInProviders/Display/SyncVar/RankXPDisplayProvider.cs
index d856383..41ec43c 100644
--- a/XPSystem/BuiltInProviders/Display/SyncVar/RankXPDisplayProvider.cs
+++ b/XPSystem/BuiltInProviders/Display/SyncVar/RankXPDisplayProvider.cs
@@ -8,35 +8,39 @@
using MEC;
using XPSystem.API;
using XPSystem.API.DisplayProviders;
+ using XPSystem.API.Player;
using XPSystem.API.StorageProviders;
- using XPSystem.Config.Models;
using YamlDotNet.Serialization;
public class RankXPDisplayProvider : SyncVarXPDisplayProvider
{
protected override string VariableKey { get; } = "RankXPDisplayProvider_badge";
- protected override (Type typeName, string methodName, Func getFakeSyncVar, Func getResyncVar)[] SyncVars { get; } =
+ protected override (Type typeName, string methodName, Func getFakeSyncVar, Func getResyncVar)[] SyncVars { get; } =
{
(typeof(ServerRoles), nameof(ServerRoles.Network_myText), (_, obj) => obj.Text, player => player.BadgeText),
(typeof(ServerRoles), nameof(ServerRoles.Network_myColor), (_, obj) => obj.Color, player => player.BadgeColor)
};
- protected override Badge CreateObject(XPPlayer player, PlayerInfoWrapper playerInfo)
+ protected override Badge? CreateObject(BaseXPPlayer player, PlayerInfoWrapper? playerInfo)
{
if (player.DNT)
{
- if (player.HasBadge && !player.HasHiddenBadge)
+ if (player is { HasBadge: true, HasHiddenBadge: false })
return null;
return Config.DNTBadge;
}
- Badge badge = null;
+ if (player is not XPPlayer xpPlayer)
+ return null;
+ playerInfo ??= xpPlayer.GetPlayerInfo();
+
+ Badge? badge = null;
string format = !player.HasBadge || player.HasHiddenBadge
? Config.BadgeStructureNoBadge
: Config.BadgeStructure;
- string color = "default";
+ string? color = null;
foreach (var kvp in Config.SortedBadges)
{
@@ -59,11 +63,11 @@ protected override Badge CreateObject(XPPlayer player, PlayerInfoWrapper playerI
.Replace("%lvl%", playerInfo.Level.ToString())
.Replace("%badge%", badge.Text)
.Replace("%oldbadge%", player.BadgeText),
- Color = color
+ Color = color ?? "default"
};
}
- protected override bool ShouldEdit(XPPlayer player)
+ protected override bool ShouldEdit(BaseXPPlayer player)
{
if (Config.SkipGlobalBadges && player.HasGlobalBadge)
return false;
@@ -74,7 +78,7 @@ protected override bool ShouldEdit(XPPlayer player)
return true;
}
- protected override bool ShouldShowTo(XPPlayer player, XPPlayer target)
+ protected override bool ShouldShowTo(BaseXPPlayer player, BaseXPPlayer target)
{
if (!base.ShouldShowTo(player, target))
return false;
@@ -151,10 +155,10 @@ public class RankConfig : IXPDisplayProviderConfig
public Badge DNTBadge { get; set; } = new()
{
Text = "(DNT) anonymous man????",
- Color = Misc.PlayerInfoColorTypes.Nickel.ToString()
+ Color = nameof(Misc.PlayerInfoColorTypes.Nickel)
};
- private Dictionary _sortedBadges;
+ private Dictionary _sortedBadges = null!;
[YamlIgnore]
public Dictionary SortedBadges => _sortedBadges ??= Badges
.OrderBy(x => x.Key)
@@ -166,27 +170,27 @@ public class RankConfig : IXPDisplayProviderConfig
[0] = new Badge
{
Text = "Visitor",
- Color = Misc.PlayerInfoColorTypes.Cyan.ToString().ToLower()
+ Color = nameof(Misc.PlayerInfoColorTypes.Cyan).ToLower()
},
[1] = new Badge
{
Text = "Junior",
- Color = Misc.PlayerInfoColorTypes.Orange.ToString().ToLower()
+ Color = nameof(Misc.PlayerInfoColorTypes.Orange).ToLower()
},
[5] = new Badge
{
Text = "Senior",
- Color = Misc.PlayerInfoColorTypes.Yellow.ToString().ToLower()
+ Color = nameof(Misc.PlayerInfoColorTypes.Yellow).ToLower()
},
[10] = new Badge
{
Text = "Veteran",
- Color = Misc.PlayerInfoColorTypes.Red.ToString().ToLower()
+ Color = nameof(Misc.PlayerInfoColorTypes.Red).ToLower()
},
[50] = new Badge
{
Text = "Nerd",
- Color = Misc.PlayerInfoColorTypes.Lime.ToString().ToLower()
+ Color = nameof(Misc.PlayerInfoColorTypes.Lime).ToLower()
}
};
}
diff --git a/XPSystem/BuiltInProviders/LiteDB/LiteDBPlayerInfo.cs b/XPSystem/BuiltInProviders/LiteDB/LiteDBPlayerInfo.cs
index a9110ea..a8ed596 100644
--- a/XPSystem/BuiltInProviders/LiteDB/LiteDBPlayerInfo.cs
+++ b/XPSystem/BuiltInProviders/LiteDB/LiteDBPlayerInfo.cs
@@ -25,7 +25,7 @@ public override T SetId(IPlayerId id)
public class LiteDBStringPlayerInfo : LiteDBPlayerInfo
{
[BsonId]
- public string Id { get; set; }
+ public string Id { get; set; } = null!;
public override T SetId(IPlayerId id)
{
@@ -43,7 +43,7 @@ public abstract class LiteDBPlayerInfo
{
public int XP { get; set; }
#if STORENICKS
- public string Nickname { get; set; }
+ public string? Nickname { get; set; }
#endif
public abstract T SetId(IPlayerId id) where T : LiteDBPlayerInfo;
diff --git a/XPSystem/BuiltInProviders/LiteDB/LiteDBProvider.cs b/XPSystem/BuiltInProviders/LiteDB/LiteDBProvider.cs
index 72cdd98..34cdaf7 100644
--- a/XPSystem/BuiltInProviders/LiteDB/LiteDBProvider.cs
+++ b/XPSystem/BuiltInProviders/LiteDB/LiteDBProvider.cs
@@ -2,6 +2,7 @@
{
using System;
using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using global::LiteDB;
using XPSystem.API.Enums;
@@ -10,11 +11,11 @@
public class LiteDBProvider : StorageProvider
{
- public ILiteCollection SteamCollection { get; private set; }
- public ILiteCollection DiscordCollection { get; private set; }
- public ILiteCollection NWCollection { get; private set; }
+ public ILiteCollection? SteamCollection { get; private set; }
+ public ILiteCollection? DiscordCollection { get; private set; }
+ public ILiteCollection? NWCollection { get; private set; }
- private LiteDatabase database;
+ private LiteDatabase? database;
public override void Initialize()
{
@@ -45,30 +46,30 @@ public override void Dispose()
DiscordCollection = null;
NWCollection = null;
- database.Dispose();
+ database?.Dispose();
database = null;
}
- public LiteDBPlayerInfo TryGetPlayerInfo(IPlayerId playerId) => TryGetPlayerInfo(playerId);
- public T TryGetPlayerInfo(IPlayerId playerId) where T : LiteDBPlayerInfo
+ public LiteDBPlayerInfo? TryGetPlayerInfo(IPlayerId playerId) => TryGetPlayerInfo(playerId);
+ public T? TryGetPlayerInfo(IPlayerId playerId) where T : LiteDBPlayerInfo
{
switch (playerId.AuthType)
{
// FindById no work; BsonValue converts ulong to decimal, key is different; retarded conversion
case AuthType.Steam when playerId is NumberPlayerId numberPlayerId:
- return (T)(object)SteamCollection.FindOne(x => x.Id == numberPlayerId.IdNumber);
+ return (T)(object)SteamCollection!.FindOne(x => x.Id == numberPlayerId.IdNumber);
case AuthType.Discord when playerId is NumberPlayerId numberPlayerId:
- return (T)(object)DiscordCollection.FindOne(x => x.Id == numberPlayerId.IdNumber);
+ return (T)(object)DiscordCollection!.FindOne(x => x.Id == numberPlayerId.IdNumber);
case AuthType.Northwood when playerId is StringPlayerId stringPlayerId:
- return (T)(object)NWCollection.FindOne(x => x.Id == stringPlayerId.IdString);
+ return (T)(object)NWCollection!.FindOne(x => x.Id == stringPlayerId.IdString);
default:
throw new ArgumentOutOfRangeException(nameof(playerId.AuthType), playerId.AuthType, null);
}
}
- protected override bool TryGetPlayerInfoNoCache(IPlayerId playerId, out PlayerInfo playerInfo)
+ protected override bool TryGetPlayerInfoNoCache(IPlayerId playerId, [NotNullWhen(true)] out PlayerInfo? playerInfo)
{
- LiteDBPlayerInfo existing = TryGetPlayerInfo(playerId);
+ LiteDBPlayerInfo? existing = TryGetPlayerInfo(playerId);
if (existing == null)
{
playerInfo = null;
@@ -81,18 +82,18 @@ protected override bool TryGetPlayerInfoNoCache(IPlayerId playerId, out PlayerIn
protected override PlayerInfo GetPlayerInfoAndCreateOfNotExistNoCache(IPlayerId playerId)
{
- LiteDBPlayerInfo existing = TryGetPlayerInfo(playerId);
+ LiteDBPlayerInfo? existing = TryGetPlayerInfo(playerId);
if (existing != null)
return existing.ToPlayerInfo(playerId.AuthType);
switch (playerId.AuthType)
{
case AuthType.Steam:
- return GetPlayerInfoAndCreateOfNotExistNoCache(playerId, SteamCollection);
+ return GetPlayerInfoAndCreateOfNotExistNoCache(playerId, SteamCollection!);
case AuthType.Discord:
- return GetPlayerInfoAndCreateOfNotExistNoCache(playerId, DiscordCollection);
+ return GetPlayerInfoAndCreateOfNotExistNoCache(playerId, DiscordCollection!);
case AuthType.Northwood:
- return GetPlayerInfoAndCreateOfNotExistNoCache(playerId, NWCollection);
+ return GetPlayerInfoAndCreateOfNotExistNoCache(playerId, NWCollection!);
default:
throw new ArgumentOutOfRangeException(nameof(playerId.AuthType), playerId.AuthType, null);
}
@@ -109,20 +110,20 @@ protected override PlayerInfo GetPlayerInfoAndCreateOfNotExistNoCache(IPlayerId
public override IEnumerable GetTopPlayers(int count)
{
- var result = SteamCollection.Query()
+ var result = SteamCollection!.Query()
.OrderByDescending(x => x.XP)
.Limit(count)
.ToEnumerable()
.Select(x => x.ToPlayerInfo(AuthType.Steam))
.ToList();
- result.AddRange(DiscordCollection.Query()
+ result.AddRange(DiscordCollection!.Query()
.OrderByDescending(x => x.XP)
.Limit(count)
.ToEnumerable()
.Select(x => x.ToPlayerInfo(AuthType.Discord)));
- result.AddRange(NWCollection.Query()
+ result.AddRange(NWCollection!.Query()
.OrderByDescending(x => x.XP)
.Limit(count)
.ToEnumerable()
@@ -139,13 +140,13 @@ protected override void SetPlayerInfoNoCache(PlayerInfo playerInfo)
switch (playerInfo.Player.AuthType)
{
case AuthType.Steam:
- SetPlayerInfoNoCache(playerInfo, SteamCollection);
+ SetPlayerInfoNoCache(playerInfo, SteamCollection!);
break;
case AuthType.Discord:
- SetPlayerInfoNoCache(playerInfo, DiscordCollection);
+ SetPlayerInfoNoCache(playerInfo, DiscordCollection!);
break;
case AuthType.Northwood:
- SetPlayerInfoNoCache(playerInfo, NWCollection);
+ SetPlayerInfoNoCache(playerInfo, NWCollection!);
break;
default:
throw new ArgumentOutOfRangeException(nameof(playerInfo.Player.AuthType), playerInfo.Player.AuthType, null);
@@ -153,7 +154,7 @@ protected override void SetPlayerInfoNoCache(PlayerInfo playerInfo)
}
protected void SetPlayerInfoNoCache(PlayerInfo playerInfo, ILiteCollection collection) where T : LiteDBPlayerInfo, new()
{
- T existing = TryGetPlayerInfo(playerInfo.Player);
+ T? existing = TryGetPlayerInfo(playerInfo.Player);
if (existing == null)
{
collection.Insert(new T()
@@ -179,11 +180,11 @@ protected override bool DeletePlayerInfoNoCache(IPlayerId playerId)
switch (playerId.AuthType)
{
case AuthType.Steam when playerId is NumberPlayerId numberPlayerId:
- return SteamCollection.Delete(numberPlayerId.IdNumber);
+ return SteamCollection!.Delete(numberPlayerId.IdNumber);
case AuthType.Discord when playerId is NumberPlayerId numberPlayerId:
- return DiscordCollection.Delete(numberPlayerId.IdNumber);
+ return DiscordCollection!.Delete(numberPlayerId.IdNumber);
case AuthType.Northwood when playerId is StringPlayerId stringPlayerId:
- return NWCollection.Delete(stringPlayerId.IdString);
+ return NWCollection!.Delete(stringPlayerId.IdString);
default:
throw new ArgumentOutOfRangeException(nameof(playerId.AuthType), playerId.AuthType, null);
}
@@ -191,9 +192,9 @@ protected override bool DeletePlayerInfoNoCache(IPlayerId playerId)
protected override void DeleteAllPlayerInfoNoCache()
{
- SteamCollection.DeleteAll();
- DiscordCollection.DeleteAll();
- NWCollection.DeleteAll();
+ SteamCollection!.DeleteAll();
+ DiscordCollection!.DeleteAll();
+ NWCollection!.DeleteAll();
}
public class LiteDBProviderConfig
diff --git a/XPSystem/BuiltInProviders/MySql/MySqlProvider.cs b/XPSystem/BuiltInProviders/MySql/MySqlProvider.cs
index 2bb7f5b..02ac72b 100644
--- a/XPSystem/BuiltInProviders/MySql/MySqlProvider.cs
+++ b/XPSystem/BuiltInProviders/MySql/MySqlProvider.cs
@@ -2,6 +2,7 @@
{
using System;
using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using MySqlConnector;
using XPSystem.API;
@@ -68,7 +69,7 @@ public override IEnumerable GetTopPlayers(int count)
.Select(x => new PlayerInfoWrapper(x));
}
- protected override bool TryGetPlayerInfoNoCache(IPlayerId playerId, out PlayerInfo playerInfo)
+ protected override bool TryGetPlayerInfoNoCache(IPlayerId playerId, [NotNullWhen(true)] out PlayerInfo? playerInfo)
{
using MySqlConnection connection = GetConnection();
using MySqlCommand command = connection.CreateCommand();
@@ -159,14 +160,16 @@ protected override void DeleteAllPlayerInfoNoCache()
}
}
- private PlayerInfo FromReader(MySqlDataReader reader, IPlayerId playerId = null)
+ private PlayerInfo FromReader(MySqlDataReader reader, IPlayerId playerId)
{
return new PlayerInfo
{
Player = playerId,
XP = reader.GetInt32(1),
#if STORENICKS
- Nickname = reader.IsDBNull(2) ? null : reader.GetString(2)
+ Nickname = reader.IsDBNull(2)
+ ? null
+ : reader.GetString(2)
#endif
};
}
@@ -195,7 +198,7 @@ public MySqlConnection GetConnection()
public class MySqlProviderConfig
{
- public string ConnectionString { get; set; }
+ public string ConnectionString { get; set; } = null!;
public bool LogQueries { get; set; } = false;
}
}
diff --git a/XPSystem/Commands/Admin/DatabasePlayerCommand.cs b/XPSystem/Commands/Admin/DatabasePlayerCommand.cs
index 8180f06..f551a5c 100644
--- a/XPSystem/Commands/Admin/DatabasePlayerCommand.cs
+++ b/XPSystem/Commands/Admin/DatabasePlayerCommand.cs
@@ -1,8 +1,8 @@
namespace XPSystem.Commands.Admin
{
using System;
- using CommandSystem;
using XPSystem.API;
+ using XPSystem.API.Player;
using XPSystem.API.StorageProviders;
using XPSystem.API.StorageProviders.Models;
@@ -17,49 +17,55 @@ public abstract class DatabasePlayerCommand : SanitizedInputCommand
///
/// The command arguments.
/// The index of the argument that would specify the targeted player.
- /// The player that executed the command.
+ /// The player that executed the command.
/// The response to be sent back to the player.
/// The targeted player's info, if found, otherwise default.
/// The targeted player's id, if found, otherwise 0.
/// Whether the operation was successful (whether or not to return immediately after).
/// If the argument count and the target player's argument position don't match, the sender will become the targeted player.
- protected bool DoThingWithArgs(ref ArraySegment arguments, byte targetPlayerArgumentIndex, XPPlayer player, ref string response, out PlayerInfoWrapper playerInfo, out IPlayerId playerId)
+ protected bool DoThingWithArgs(ref ArraySegment arguments, byte targetPlayerArgumentIndex, BaseXPPlayer? sender, ref string response, out PlayerInfoWrapper playerInfo, out IPlayerId playerId)
{
- playerId = default;
- playerInfo = default;
+ playerId = null!;
+ playerInfo = null!;
// If player is specified.
if (arguments.Count > targetPlayerArgumentIndex)
{
string arg = arguments.At(targetPlayerArgumentIndex);
// Try to get player by name or user id.
- if (XPPlayer.TryGet(arg, out player))
+ if (XPPlayer.TryGet(arg, out BaseXPPlayer? basePlayer))
{
+ if (basePlayer is not XPPlayer player)
+ {
+ response = "Player is not a xp player, can't do that.";
+ return false;
+ }
+
playerId = player.PlayerId;
}
else
{
// if player isn't online, try to parse user id.
- if (!arg.TryParseUserId(out playerId))
+ if (!arg.TryParseUserId(out playerId!))
{
- response = "Invalid player.";
+ response = "Invalid player ID.";
return false;
}
}
}
// Player not specified, use sender.
- else
+ else if (sender is XPPlayer player)
{
playerId = player.PlayerId;
}
-
- if (player.PlayerId == playerId && player.IsNPC)
+ else
{
- response = "Player is not actual player, can't do that.";
+ response = "You must specify a player (sender not a XPPlayer).";
return false;
}
- if (XPAPI.StorageProvider.TryGetPlayerInfo(playerId, out playerInfo))
+ // XPAPI.EnsureStorageProviderValid(); - called in calling command(s)
+ if (XPAPI.StorageProvider!.TryGetPlayerInfo(playerId, out playerInfo!))
return true;
response = "Player does not exist within database.";
diff --git a/XPSystem/Commands/Admin/Subcommands/GetCommandAdmin.cs b/XPSystem/Commands/Admin/Subcommands/GetCommandAdmin.cs
index a6112f1..86ba558 100644
--- a/XPSystem/Commands/Admin/Subcommands/GetCommandAdmin.cs
+++ b/XPSystem/Commands/Admin/Subcommands/GetCommandAdmin.cs
@@ -3,22 +3,24 @@
using System;
using CommandSystem;
using XPSystem.API;
+ using XPSystem.API.Player;
using XPSystem.API.StorageProviders;
+ using XPSystem.API.StorageProviders.Models;
public class GetCommandAdmin : DatabasePlayerCommand
{
public override bool Execute(ArraySegment arguments, ICommandSender sender, out string response)
{
- if (!XPPlayer.TryGetAndCheckPermission(sender, "xps.get", out XPPlayer player))
+ if (!XPPlayer.TryGetAndCheckPermission(sender, "xps.get", out BaseXPPlayer? player))
{
response = "You don't have permission (xps.get) to use this command.";
return false;
}
+ response = "temp";
XPAPI.EnsureStorageProviderValid();
- response = null;
- if (!DoThingWithArgs(ref arguments, 0, player, ref response, out PlayerInfoWrapper playerInfo, out var playerId))
+ if (!DoThingWithArgs(ref arguments, 0, player, ref response, out PlayerInfoWrapper playerInfo, out IPlayerId playerId))
return false;
response = $"{playerId.ToString()} ({playerInfo.Nickname})] XP: {playerInfo.XP} (Level {playerInfo.Level})";
diff --git a/XPSystem/Commands/Admin/Subcommands/GiveCommand.cs b/XPSystem/Commands/Admin/Subcommands/GiveCommand.cs
index d41f674..5eb3255 100644
--- a/XPSystem/Commands/Admin/Subcommands/GiveCommand.cs
+++ b/XPSystem/Commands/Admin/Subcommands/GiveCommand.cs
@@ -3,13 +3,15 @@
using System;
using CommandSystem;
using XPSystem.API;
+ using XPSystem.API.Player;
using XPSystem.API.StorageProviders;
+ using XPSystem.API.StorageProviders.Models;
public class GiveCommand : DatabasePlayerCommand
{
public override bool Execute(ArraySegment arguments, ICommandSender sender, out string response)
{
- if (!XPPlayer.TryGetAndCheckPermission(sender, "xps.give", out XPPlayer player))
+ if (!XPPlayer.TryGetAndCheckPermission(sender, "xps.give", out BaseXPPlayer? player))
{
response = "You don't have permission (xps.give) to use this command.";
return false;
@@ -28,8 +30,10 @@ public override bool Execute(ArraySegment arguments, ICommandSender send
return false;
}
- response = null;
- if (!DoThingWithArgs(ref arguments, 1, player, ref response, out PlayerInfoWrapper playerInfo, out var playerId))
+ response = "temp";
+ XPAPI.EnsureStorageProviderValid();
+
+ if (!DoThingWithArgs(ref arguments, 1, player, ref response, out PlayerInfoWrapper playerInfo, out IPlayerId playerId))
return false;
playerInfo.XP += amount;
diff --git a/XPSystem/Commands/Admin/Subcommands/LeaderboardCommandAdmin.cs b/XPSystem/Commands/Admin/Subcommands/LeaderboardCommandAdmin.cs
index d59726d..0e74f36 100644
--- a/XPSystem/Commands/Admin/Subcommands/LeaderboardCommandAdmin.cs
+++ b/XPSystem/Commands/Admin/Subcommands/LeaderboardCommandAdmin.cs
@@ -32,7 +32,7 @@ public override bool Execute(ArraySegment arguments, ICommandSender send
}
}
- response = XPAPI.StorageProvider.GetTopPlayers(amount)
+ response = XPAPI.StorageProvider!.GetTopPlayers(amount)
.FormatLeaderboard();
return true;
}
diff --git a/XPSystem/Commands/Admin/Subcommands/MultiplierCommand.cs b/XPSystem/Commands/Admin/Subcommands/MultiplierCommand.cs
index c003ba5..0c58b68 100644
--- a/XPSystem/Commands/Admin/Subcommands/MultiplierCommand.cs
+++ b/XPSystem/Commands/Admin/Subcommands/MultiplierCommand.cs
@@ -3,6 +3,7 @@
using System;
using CommandSystem;
using XPSystem.API;
+ using XPSystem.API.Player;
public class MultiplierCommand : SanitizedInputCommand
{
@@ -36,20 +37,19 @@ public override bool Execute(ArraySegment arguments, ICommandSender send
return true;
case "p":
case "player":
- XPPlayer player;
+ BaseXPPlayer? basePlayer;
int intArgLocation;
if (arguments.Count > 2)
{
intArgLocation = 2;
-
- if (!XPPlayer.TryGet(arguments.At(1), out player))
+ if (!XPPlayer.TryGet(arguments.At(1), out basePlayer))
{
response = "Invalid player.";
return false;
}
}
- else if (XPPlayer.TryGet(sender, out player))
+ else if (XPPlayer.TryGet(sender, out basePlayer))
{
intArgLocation = 1;
}
@@ -59,6 +59,12 @@ public override bool Execute(ArraySegment arguments, ICommandSender send
return false;
}
+ if (basePlayer is not XPPlayer player)
+ {
+ response = "Player not XPPlayer.";
+ return false;
+ }
+
if (!float.TryParse(arguments.At(intArgLocation), out multiplier))
{
response = $"{player.DisplayedName}'s multiplier: {player.XPMultiplier}.";
diff --git a/XPSystem/Commands/Admin/Subcommands/SetCommand.cs b/XPSystem/Commands/Admin/Subcommands/SetCommand.cs
index 5844729..751fbf9 100644
--- a/XPSystem/Commands/Admin/Subcommands/SetCommand.cs
+++ b/XPSystem/Commands/Admin/Subcommands/SetCommand.cs
@@ -3,13 +3,15 @@
using System;
using CommandSystem;
using XPSystem.API;
+ using XPSystem.API.Player;
using XPSystem.API.StorageProviders;
+ using XPSystem.API.StorageProviders.Models;
public class SetCommand : DatabasePlayerCommand
{
public override bool Execute(ArraySegment arguments, ICommandSender sender, out string response)
{
- if (!XPPlayer.TryGetAndCheckPermission(sender, "xps.set", out XPPlayer player))
+ if (!XPPlayer.TryGetAndCheckPermission(sender, "xps.set", out BaseXPPlayer? player))
{
response = "You don't have permission (xps.set) to use this command.";
return false;
@@ -30,8 +32,10 @@ public override bool Execute(ArraySegment arguments, ICommandSender send
return false;
}
- response = null;
- if (!DoThingWithArgs(ref arguments, 1, player, ref response, out PlayerInfoWrapper playerInfo, out var playerId))
+ response = "temp";
+ XPAPI.EnsureStorageProviderValid();
+
+ if (!DoThingWithArgs(ref arguments, 1, player, ref response, out PlayerInfoWrapper playerInfo, out IPlayerId playerId))
return false;
playerInfo.XP = amount;
diff --git a/XPSystem/Commands/Admin/Subcommands/SetLevelCommand.cs b/XPSystem/Commands/Admin/Subcommands/SetLevelCommand.cs
index fe07b81..5d99973 100644
--- a/XPSystem/Commands/Admin/Subcommands/SetLevelCommand.cs
+++ b/XPSystem/Commands/Admin/Subcommands/SetLevelCommand.cs
@@ -3,13 +3,15 @@
using System;
using CommandSystem;
using XPSystem.API;
+ using XPSystem.API.Player;
using XPSystem.API.StorageProviders;
+ using XPSystem.API.StorageProviders.Models;
public class SetLevelCommand : DatabasePlayerCommand
{
public override bool Execute(ArraySegment arguments, ICommandSender sender, out string response)
{
- if (!XPPlayer.TryGetAndCheckPermission(sender, "xps.setlevel", out XPPlayer player))
+ if (!XPPlayer.TryGetAndCheckPermission(sender, "xps.setlevel", out BaseXPPlayer? player))
{
response = "You don't have permission (xps.setlevel) to use this command.";
return false;
@@ -30,8 +32,10 @@ public override bool Execute(ArraySegment arguments, ICommandSender send
return false;
}
- response = null;
- if (!DoThingWithArgs(ref arguments, 1, player, ref response, out PlayerInfoWrapper playerInfo, out var playerId))
+ response = "temp";
+ XPAPI.EnsureStorageProviderValid();
+
+ if (!DoThingWithArgs(ref arguments, 1, player, ref response, out PlayerInfoWrapper playerInfo, out IPlayerId playerId))
return false;
playerInfo.Level = level;
diff --git a/XPSystem/Commands/Admin/Subcommands/ShowMessageCommand.cs b/XPSystem/Commands/Admin/Subcommands/ShowMessageCommand.cs
index afa23c1..1192bad 100644
--- a/XPSystem/Commands/Admin/Subcommands/ShowMessageCommand.cs
+++ b/XPSystem/Commands/Admin/Subcommands/ShowMessageCommand.cs
@@ -9,6 +9,7 @@
using NorthwoodLib.Pools;
using PlayerRoles;
using XPSystem.API;
+ using XPSystem.API.Player;
using XPSystem.Config.Events;
using XPSystem.Config.Events.Types;
@@ -33,10 +34,10 @@ public override bool Execute(ArraySegment arguments, ICommandSender send
if (key.StartsWith("default_"))
key = key.Substring(8);
- else if (XPPlayer.TryGet(sender, out XPPlayer player))
+ else if (XPPlayer.TryGet(sender, out BaseXPPlayer? player))
role = player.Role;
- XPECFile file = XPECManager.GetFile(key, role);
+ XPECFile? file = XPECManager.GetFile(key, role);
if (file == null)
{
response = "No such XPEC file.";
@@ -61,7 +62,7 @@ public override bool Execute(ArraySegment arguments, ICommandSender send
}
bool success = false;
- Exception e = null;
+ Exception? e = null;
var argTypes = types[i];
foreach (Type type in argTypes)
{
@@ -93,7 +94,7 @@ public override bool Execute(ArraySegment arguments, ICommandSender send
}
}
- XPECItem item = file.Get(subkeys.ToArray());
+ XPECItem? item = file.Get(subkeys.ToArray());
if (item == null)
{
response = "Item null.";
diff --git a/XPSystem/Commands/Admin/Subcommands/VariablesCommand.cs b/XPSystem/Commands/Admin/Subcommands/VariablesCommand.cs
index 0848f1b..ee494c6 100644
--- a/XPSystem/Commands/Admin/Subcommands/VariablesCommand.cs
+++ b/XPSystem/Commands/Admin/Subcommands/VariablesCommand.cs
@@ -5,12 +5,13 @@
using CommandSystem;
using NorthwoodLib.Pools;
using XPSystem.API;
+ using XPSystem.API.Player;
public class VariablesCommand : SanitizedInputCommand
{
public override bool Execute(ArraySegment arguments, ICommandSender sender, out string response)
{
- if (!XPPlayer.TryGetAndCheckPermission(sender, "xps.variables", out XPPlayer player))
+ if (!XPPlayer.TryGetAndCheckPermission(sender, "xps.variables", out BaseXPPlayer? player))
{
response = "You don't have permission (xps.variables) to use this command.";
return false;
diff --git a/XPSystem/Commands/Client/Subcommands/GetCommandClient.cs b/XPSystem/Commands/Client/Subcommands/GetCommandClient.cs
index 53dcffe..605981c 100644
--- a/XPSystem/Commands/Client/Subcommands/GetCommandClient.cs
+++ b/XPSystem/Commands/Client/Subcommands/GetCommandClient.cs
@@ -3,20 +3,20 @@
using System;
using CommandSystem;
using XPSystem.API;
+ using XPSystem.API.Player;
using XPSystem.API.StorageProviders;
public class GetCommandClient : SanitizedInputCommand, IAliasableCommand
{
public override bool Execute(ArraySegment arguments, ICommandSender sender, out string response)
{
- if (!XPPlayer.TryGet(sender, out XPPlayer player))
+ if (!XPPlayer.TryGet(sender, out BaseXPPlayer? basePlayer) || basePlayer is not XPPlayer player)
{
response = "This command is player only.";
return false;
}
PlayerInfoWrapper log = player.GetPlayerInfo();
-
response = $"LVL: {log.Level} | XP: {log.XP} | Needed XP: {log.NeededXPNext}";
return true;
}
diff --git a/XPSystem/Commands/Client/Subcommands/LeaderboardCommandClient.cs b/XPSystem/Commands/Client/Subcommands/LeaderboardCommandClient.cs
index 3366e2d..07fc72c 100644
--- a/XPSystem/Commands/Client/Subcommands/LeaderboardCommandClient.cs
+++ b/XPSystem/Commands/Client/Subcommands/LeaderboardCommandClient.cs
@@ -32,7 +32,7 @@ public override bool Execute(ArraySegment arguments, ICommandSender send
}
}
- response = XPAPI.StorageProvider.GetTopPlayers(amount)
+ response = XPAPI.StorageProvider!.GetTopPlayers(amount)
.FormatLeaderboard();
return true;
}
diff --git a/XPSystem/Commands/Console/Subcommands/DeleteEverythingCommand.cs b/XPSystem/Commands/Console/Subcommands/DeleteEverythingCommand.cs
index 47b9aca..dcff76a 100644
--- a/XPSystem/Commands/Console/Subcommands/DeleteEverythingCommand.cs
+++ b/XPSystem/Commands/Console/Subcommands/DeleteEverythingCommand.cs
@@ -20,7 +20,7 @@ public override bool Execute(ArraySegment arguments, ICommandSender send
if ((DateTime.Now - _lastUsed).TotalSeconds < 10)
{
- XPAPI.StorageProvider.DeleteAllPlayerInfo();
+ XPAPI.StorageProvider!.DeleteAllPlayerInfo();
response = "Everything deleted.";
return true;
diff --git a/XPSystem/Commands/Console/Subcommands/ReloadConfigsCommand.cs b/XPSystem/Commands/Console/Subcommands/ReloadConfigsCommand.cs
index 7578b04..25a1bd8 100644
--- a/XPSystem/Commands/Console/Subcommands/ReloadConfigsCommand.cs
+++ b/XPSystem/Commands/Console/Subcommands/ReloadConfigsCommand.cs
@@ -7,7 +7,7 @@ public class ReloadConfigsCommand : SanitizedInputCommand
{
public override bool Execute(ArraySegment arguments, ICommandSender sender, out string response)
{
- Main.Instance.LoadExtraConfigs();
+ Main.Instance!.LoadExtraConfigs();
response = "Extra (non main, use your pluginloader to reload) configs reloaded.";
return true;
}
diff --git a/XPSystem/Config/Config.cs b/XPSystem/Config/Config.cs
index 9f4a214..dba94d2 100644
--- a/XPSystem/Config/Config.cs
+++ b/XPSystem/Config/Config.cs
@@ -9,34 +9,34 @@ public abstract class Config
[Description("Print debug messages?")]
public bool Debug { get; set; } = false;
- [Description("The function to calculate level for given xp. Parameter: xp. Available functions: https://learn.microsoft.com/en-us/dotnet/api/system.math?view=net-8.0#methods.")]
- public string LevelFunction { get; set; } = "Ceiling(-50 + Sqrt((4 * xp / a) + 9800) / 2)";
+ [Description("The function to calculate level for given XP. Parameter: XP. Available functions: https://learn.microsoft.com/en-us/dotnet/api/system.math?view=net-8.0#methods.")]
+ public string LevelFunction { get; set; } = "Ceiling(-50 + Sqrt((4 * XP / a) + 9800) / 2)";
- [Description("The function to calculate xp needed for a level. The inverse of the LevelFunction. Parameter: level. Available functions: https://learn.microsoft.com/en-us/dotnet/api/system.math?view=net-8.0#methods.")]
+ [Description("The function to calculate XP needed for a level. The inverse of the LevelFunction. Parameter: level. Available functions: https://learn.microsoft.com/en-us/dotnet/api/system.math?view=net-8.0#methods.")]
public string XPFunction { get; set; } = "Ceiling((level^2 + 100 * level + 50) * a)";
- [Description("Additional parameters for the level/xp functions.")]
+ [Description("Additional parameters for the level/XP functions.")]
public Dictionary AdditionalFunctionParameters { get; set; } = new()
{
{ "a", 1 },
};
- [Description("Override xp required for specific levels. Key: level, Value: xp.")]
+ [Description("Override XP required for specific levels. Key: level, Value: XP.")]
public Dictionary LevelXPOverrides { get; set; } = new()
{
[0] = 0
};
- [Description("Whether or not to pause xp gain when the round ends.")]
+ [Description("Whether or not to pause XP gain when the round ends.")]
public bool XPAfterRoundEnd { get; set; } = true;
[Description("A global XP multiplier.")]
public float GlobalXPMultiplier { get; set; } = 1f;
- [Description("Whether or not the global XP multiplier should apply to xp given to people that aren't online (via commands, etc.).")]
+ [Description("Whether or not the global XP multiplier should apply to XP given to people that aren't online (via commands, etc.).")]
public bool GlobalXPMultiplierForNonOnline { get; set; } = true;
- [Description("Whether or not the global XP multiplier should apply to xp removed.")]
+ [Description("Whether or not the global XP multiplier should apply to XP removed.")]
public bool XPMultiplierForXPLoss { get; set; } = false;
[Description("Path to the event configs folder, relative to the ExtendedConfigPath.")]
@@ -58,22 +58,25 @@ public abstract class Config
public string DNTMessage { get; set; } =
"We can't track your stats while you have DNT enabled in your game options!";
- [Description("Whether or not to format a message according to a template when adding xp.")]
+ [Description("Whether or not to format a message according to a template when adding XP.")]
public bool UseAddedXPTemplate { get; set; } = true;
- [Description("When enabled, template used for messages that modify xp. Parameters: %message%, %currentxp%, %currentlevel%, %neededxp%, %nextlevel.")]
+ [Description("When enabled, template used for messages that modify XP. Parameters: %message%, %currentxp%, %currentlevel%, %neededxp%, %nextlevel.")]
public string AddedXPTemplate { get; set; } = "%message%, (%currentxp% / %neededxp%)";
- [Description("Whether or not to use the total xp instead of only the xp required for the next level. Requires extra calculations if false.")]
+ [Description("Whether or not to use the total XP instead of only the XP required for the next level. Requires extra calculations if false.")]
public bool UseTotalXP { get; set; } = true;
[Description("Whether or not to show a message to a player if they advance a level.")]
public bool ShowAddedLVL { get; set; } = true;
+ [Description("Whether or not to show the XP gain message on level up. If false, only the level will be shown.")]
+ public bool ShowXPOnLevelUp { get; set; } = true;
+
[Description("When enabled, what message to show if player advances a level.")]
public string AddedLVLMessage { get; set; } = "NEW LEVEL: %level%";
- [Description("Decide how messages (ex. xp gain, level up) are displayed.")]
+ [Description("Decide how messages (ex. XP gain, level up) are displayed.")]
public DisplayMode DisplayMode { get; set; } = DisplayMode.Hint;
[Description("The duration of the message, if applicable.")]
@@ -85,7 +88,7 @@ public abstract class Config
[Description("Prepended to all messages.")]
public string TextSuffix { get; set; } = "";
- [Description("An alias for the client to view their own xp. Empty to disable.")]
+ [Description("An alias for the client to view their own XP. Empty to disable.")]
public string ClientGetCommandAlias { get; set; } = "";
[Description("An alias for the client to view the leaderboard. Empty to disable.")]
@@ -95,7 +98,7 @@ public abstract class Config
"Available, but I will not help you with: XPSystem.BuiltInProviders.MySql.MySqlProvider")]
public string StorageProvider { get; set; } = "XPSystem.BuiltInProviders.LiteDB.LiteDBProvider";
- [Description("The assembly qualified type names of xp display providers to load, in addition to the built-in rank and nick ones.")]
+ [Description("The assembly qualified type names of XP display providers to load, in addition to the built-in rank and nick ones.")]
public List AdditionalDisplayProviders { get; set; } = new()
{
};
diff --git a/XPSystem/Config/Events/Types/XPECDictFile.cs b/XPSystem/Config/Events/Types/XPECDictFile.cs
index 8e927f5..cc40d80 100644
--- a/XPSystem/Config/Events/Types/XPECDictFile.cs
+++ b/XPSystem/Config/Events/Types/XPECDictFile.cs
@@ -10,22 +10,21 @@
/// The type of the subkeys.
public class XPECDictFile : XPECFile
{
- public XPECItem Default { get; set; }
-
+ public XPECItem Default { get; set; } = null!;
public Dictionary Items { get; set; } = new();
///
- public override XPECItem Get(params object[] keys)
+ public override XPECItem Get(params object?[]? keys)
{
base.Get(keys);
if (keys == null || keys.Length == 0)
return Default;
- object keyObj = keys[0];
+ object? keyObj = keys[0];
if (keyObj is not T key)
- throw new InvalidCastException($"Key is not of the correct type (was: {keyObj.GetType().FormatType()}, expected: {typeof(T).FormatType()})");
+ throw new InvalidCastException($"Key is not of the correct type (was: {keyObj?.GetType().FormatType() ?? "null"}, expected: {typeof(T).FormatType()})");
- if (Items == null)
+ if (Items == null!)
{
XPAPI.LogDebug("Items null in XPECDictFile with keytype " + typeof(T).FormatType());
return Default;
diff --git a/XPSystem/Config/Events/Types/XPECFile.cs b/XPSystem/Config/Events/Types/XPECFile.cs
index c855052..ca0cb6c 100644
--- a/XPSystem/Config/Events/Types/XPECFile.cs
+++ b/XPSystem/Config/Events/Types/XPECFile.cs
@@ -1,7 +1,10 @@
-namespace XPSystem.Config.Events.Types
+#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
+namespace XPSystem.Config.Events.Types
{
using System;
+ using System.Collections.Generic;
using System.IO;
+ using System.Linq;
using System.Reflection;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
@@ -16,7 +19,7 @@ public abstract class XPECFile
/// The key of the file.
/// Set by upon loading.
///
- internal string Key;
+ internal string Key = null!;
///
/// Gets an item with the specified keys.
@@ -24,11 +27,15 @@ public abstract class XPECFile
///
/// The keys of the item.
/// The item.
- public virtual XPECItem Get(params object[] keys)
+ public virtual XPECItem? Get(params object?[]? keys)
{
if (Config.Debug || Config.LogXPGainedMethods)
{
- string fullKey = $"{Key}/{string.Join("/", keys ?? Array.Empty