diff --git a/.editorconfig b/.editorconfig index 486c411a6f..28dc3d60c5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -146,6 +146,7 @@ resharper_place_expr_property_on_single_line = true resharper_place_simple_initializer_on_single_line = false resharper_trailing_comma_in_multiline_lists = true resharper_wrap_array_initializer_style = chop_if_long +resharper_wrap_before_primary_constructor_declaration_rpar = true ############################### # VB Coding Conventions # ############################### diff --git a/Framework/Intersect.Framework.Core/Config/DatabaseOptions.cs b/Framework/Intersect.Framework.Core/Config/DatabaseOptions.cs index e22870d198..9717f4ceca 100644 --- a/Framework/Intersect.Framework.Core/Config/DatabaseOptions.cs +++ b/Framework/Intersect.Framework.Core/Config/DatabaseOptions.cs @@ -20,11 +20,11 @@ public partial class DatabaseOptions public ushort Port { get; set; } = 3306; - public string Database { get; set; } = ""; + public string Database { get; set; } = string.Empty; - public string Username { get; set; } = ""; + public string Username { get; set; } = string.Empty; - public string Password { get; set; } = ""; + public string Password { get; set; } = string.Empty; [JsonConverter(typeof(StringEnumConverter))] [JsonProperty( diff --git a/Framework/Intersect.Framework.Core/Config/Options.cs b/Framework/Intersect.Framework.Core/Config/Options.cs index 877b9b9229..8c4ab04d99 100644 --- a/Framework/Intersect.Framework.Core/Config/Options.cs +++ b/Framework/Intersect.Framework.Core/Config/Options.cs @@ -8,7 +8,7 @@ namespace Intersect; public partial class Options { //Caching Json - private static string optionsCompressed = ""; + private static string optionsCompressed = string.Empty; [JsonProperty("AdminOnly", Order = -3)] protected bool _adminOnly = false; diff --git a/Framework/Intersect.Framework.Core/Config/SmtpSettings.cs b/Framework/Intersect.Framework.Core/Config/SmtpSettings.cs index 9e0e357603..80423617e3 100644 --- a/Framework/Intersect.Framework.Core/Config/SmtpSettings.cs +++ b/Framework/Intersect.Framework.Core/Config/SmtpSettings.cs @@ -2,19 +2,19 @@ public partial class SmtpSettings { - public string FromAddress { get; set; } = ""; + public string FromAddress { get; set; } = string.Empty; - public string FromName { get; set; } = ""; + public string FromName { get; set; } = string.Empty; - public string Host { get; set; } = ""; + public string Host { get; set; } = string.Empty; public int Port { get; set; } = 587; public bool UseSsl { get; set; } = true; - public string Username { get; set; } = ""; + public string Username { get; set; } = string.Empty; - public string Password { get; set; } = ""; + public string Password { get; set; } = string.Empty; public bool IsValid() { diff --git a/Framework/Intersect.Framework.Core/Configuration/ClientConfiguration.cs b/Framework/Intersect.Framework.Core/Configuration/ClientConfiguration.cs index 1c4d6f87f1..27c9f8579c 100644 --- a/Framework/Intersect.Framework.Core/Configuration/ClientConfiguration.cs +++ b/Framework/Intersect.Framework.Core/Configuration/ClientConfiguration.cs @@ -202,12 +202,12 @@ public void Validate() /// /// The address for the update manifest file generated by the editor. /// - public string UpdateUrl { get; set; } = ""; + public string UpdateUrl { get; set; } = string.Empty; /// /// Sets a custom mouse cursor. /// - public string MouseCursor { get; set; } = ""; + public string MouseCursor { get; set; } = string.Empty; /// /// Determines the time it takes to fade-in or fade-out a song when no other instructions are given. diff --git a/Framework/Intersect.Framework.Core/Descriptors/GameObjectTypeExtensions.cs b/Framework/Intersect.Framework.Core/Descriptors/GameObjectTypeExtensions.cs index fca2b6af8b..bf13f7db54 100644 --- a/Framework/Intersect.Framework.Core/Descriptors/GameObjectTypeExtensions.cs +++ b/Framework/Intersect.Framework.Core/Descriptors/GameObjectTypeExtensions.cs @@ -53,6 +53,13 @@ public static DatabaseObjectLookup GetLookup(this GameObjectType gameObjectType) return DatabaseObjectLookup.GetLookup(GetObjectType(gameObjectType)); } + public static bool TryGetLookup(this GameObjectType gameObjectType, + [NotNullWhen(true)] out DatabaseObjectLookup? lookup) + { + lookup = GetLookup(gameObjectType); + return lookup != default; + } + public static IDatabaseObject CreateNew(this GameObjectType gameObjectType) { var instance = Activator.CreateInstance( diff --git a/Framework/Intersect.Framework.Core/GameObjects/AnimationBase.cs b/Framework/Intersect.Framework.Core/GameObjects/AnimationBase.cs index 6e08f5f72b..c7701d3422 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/AnimationBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/AnimationBase.cs @@ -77,5 +77,5 @@ public AnimationBase() public bool CompleteSound { get; set; } /// - public string Folder { get; set; } = ""; + public string Folder { get; set; } = string.Empty; } diff --git a/Framework/Intersect.Framework.Core/GameObjects/Annotations/EditorBooleanAttribute.cs b/Framework/Intersect.Framework.Core/GameObjects/Annotations/EditorBooleanAttribute.cs index 7e4ef30793..73c2778de1 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Annotations/EditorBooleanAttribute.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Annotations/EditorBooleanAttribute.cs @@ -23,6 +23,6 @@ public override string Format(Type stringsType, object value) throw new InvalidOperationException($"{stringsType.FullName}.{nameof(stringsType)} is missing."); } - return formatBooleanMethodInfo.Invoke(null, new[] { value, Style })?.ToString(); + return formatBooleanMethodInfo.Invoke(null, [value, Style])?.ToString(); } } diff --git a/Framework/Intersect.Framework.Core/GameObjects/Annotations/EditorDictionaryAttribute.cs b/Framework/Intersect.Framework.Core/GameObjects/Annotations/EditorDictionaryAttribute.cs index fae1e433eb..18d53852da 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Annotations/EditorDictionaryAttribute.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Annotations/EditorDictionaryAttribute.cs @@ -129,7 +129,7 @@ private static GetStringFromLocaleDictionaryGeneric CreateWeaklyTypedDelegate()); + var createdDelegate = _methodInfoCreateWeaklyTypedDelegate.MakeGenericMethod(key.GetType()).Invoke(null, []); if (createdDelegate is not GetStringFromLocaleDictionaryGeneric genericFormatter) { throw new InvalidOperationException(); diff --git a/Framework/Intersect.Framework.Core/GameObjects/Annotations/EditorFormattedAttribute.cs b/Framework/Intersect.Framework.Core/GameObjects/Annotations/EditorFormattedAttribute.cs index 1e3d10b6d4..d69cf48ec2 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Annotations/EditorFormattedAttribute.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Annotations/EditorFormattedAttribute.cs @@ -84,7 +84,7 @@ private static object InvokeFormatterMethod(IEnumerable methodInfos, if (formatterMethodInfo.GetParameters().Length == 1) { - return formatterMethodInfo.Invoke(default, new[] { value }); + return formatterMethodInfo.Invoke(default, [value]); } return formatterMethodInfo.Invoke( diff --git a/Framework/Intersect.Framework.Core/GameObjects/ClassBase.cs b/Framework/Intersect.Framework.Core/GameObjects/ClassBase.cs index ad7301fd6f..b79a375075 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/ClassBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/ClassBase.cs @@ -16,17 +16,36 @@ public partial class ClassBase : DatabaseObject, IFolderable public const long DEFAULT_EXPERIENCE_INCREASE = 50; - [NotMapped] - public int[] BaseStat = new int[Enum.GetValues().Length]; + [NotMapped, JsonIgnore] + public int[] BaseStat { get; set; } = new int[Enum.GetValues().Length]; - [NotMapped] - public long[] BaseVital = new long[Enum.GetValues().Length]; + [NotMapped, JsonIgnore] + public long[] BaseVital { get; set; } = new long[Enum.GetValues().Length]; - [NotMapped] - public Dictionary ExperienceOverrides = new Dictionary(); + [JsonProperty(nameof(BaseVital)), NotMapped] + public IReadOnlyDictionary BaseVitalLookup => BaseVital.Select((value, index) => (value, index)) + .ToDictionary(t => (Vital)t.index, t => t.value).AsReadOnly(); + + [JsonProperty(nameof(VitalIncrease)), NotMapped] + public IReadOnlyDictionary VitalIncreaseLookup => VitalIncrease.Select((value, index) => (value, index)) + .ToDictionary(t => (Vital)t.index, t => t.value).AsReadOnly(); + + [JsonProperty(nameof(VitalRegen)), NotMapped] + public IReadOnlyDictionary VitalRegenLookup => VitalRegen.Select((value, index) => (value, index)) + .ToDictionary(t => (Vital)t.index, t => t.value).AsReadOnly(); + + [JsonProperty(nameof(BaseStat)), NotMapped] + public IReadOnlyDictionary BaseStatLookup => BaseStat.Select((statValue, index) => (statValue, index)) + .ToDictionary(t => (Stat)t.index, t => t.statValue).AsReadOnly(); + + [JsonProperty(nameof(StatIncrease)), NotMapped] + public IReadOnlyDictionary StatIncreaseLookup => StatIncrease.Select((statValue, index) => (statValue, index)) + .ToDictionary(t => (Stat)t.index, t => t.statValue).AsReadOnly(); + + [NotMapped] public Dictionary ExperienceOverrides { get; set; } = []; [NotMapped] - public List Items = new List(); + public List Items { get; set; } = []; [JsonIgnore] private long mBaseExp; @@ -35,19 +54,19 @@ public partial class ClassBase : DatabaseObject, IFolderable private long mExpIncrease; [NotMapped] - public List Spells = new List(); + public List Spells { get; set; } = []; [NotMapped] - public List Sprites = new List(); + public List Sprites { get; set; } = []; - [NotMapped] - public int[] StatIncrease = new int[Enum.GetValues().Length]; + [NotMapped, JsonIgnore] + public int[] StatIncrease { get; set; } = new int[Enum.GetValues().Length]; - [NotMapped] - public long[] VitalIncrease = new long[Enum.GetValues().Length]; + [NotMapped, JsonIgnore] + public long[] VitalIncrease { get; set; } = new long[Enum.GetValues().Length]; - [NotMapped] - public long[] VitalRegen = new long[Enum.GetValues().Length]; + [NotMapped, JsonIgnore] + public long[] VitalRegen { get; set; } = new long[Enum.GetValues().Length]; [JsonConstructor] public ClassBase(Guid id) : base(id) @@ -243,7 +262,7 @@ public string ExpOverridesJson } /// - public string Folder { get; set; } = ""; + public string Folder { get; set; } = string.Empty; public long ExperienceToNextLevel(int level) { @@ -284,9 +303,9 @@ public SpellBase Get() public partial class ClassSprite { - public string Face = ""; + public string Face = string.Empty; public Gender Gender; - public string Sprite = ""; + public string Sprite = string.Empty; } diff --git a/Framework/Intersect.Framework.Core/GameObjects/Conditions/ConditionList.cs b/Framework/Intersect.Framework.Core/GameObjects/Conditions/ConditionList.cs index ed94786251..9e1dab36f0 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Conditions/ConditionList.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Conditions/ConditionList.cs @@ -7,9 +7,9 @@ namespace Intersect.GameObjects.Conditions; public partial class ConditionList { - public List Conditions = new List(); //Long story.. just go with it.. okay? + public List Conditions { get; set; } = []; //Long story.. just go with it.. okay? - public string Name = "New Condition List"; + public string Name { get; set; } = "New Condition List"; public ConditionList() { diff --git a/Framework/Intersect.Framework.Core/GameObjects/Conditions/ConditionLists.cs b/Framework/Intersect.Framework.Core/GameObjects/Conditions/ConditionLists.cs index e52fce7d58..03921da416 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Conditions/ConditionLists.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Conditions/ConditionLists.cs @@ -7,7 +7,7 @@ namespace Intersect.GameObjects.Conditions; [JsonConverter(typeof(ConditionListsSerializer))] public partial class ConditionLists { - public List Lists = new List(); + public List Lists { get; set; } = []; public ConditionLists() { diff --git a/Framework/Intersect.Framework.Core/GameObjects/Crafting/CraftBase.cs b/Framework/Intersect.Framework.Core/GameObjects/Crafting/CraftBase.cs index 017faabf65..c04eea947f 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Crafting/CraftBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Crafting/CraftBase.cs @@ -11,7 +11,7 @@ namespace Intersect.GameObjects.Crafting; public partial class CraftBase : DatabaseObject, IFolderable { [NotMapped] - public List Ingredients = new List(); + public List Ingredients { get; set; } = []; [JsonConstructor] public CraftBase(Guid id) : base(id) @@ -49,7 +49,7 @@ public string IngredientsJson public int Time { get; set; } /// - public string Folder { get; set; } = ""; + public string Folder { get; set; } = string.Empty; [Column("Event")] [JsonProperty] @@ -64,7 +64,7 @@ public EventBase Event } [NotMapped] - public ConditionLists CraftingRequirements = new ConditionLists(); + public ConditionLists CraftingRequirements { get; set; } = new(); //Requirements [Column("CraftingRequirements")] @@ -78,9 +78,9 @@ public string JsonCraftingRequirements public partial class CraftIngredient { - public Guid ItemId; + public Guid ItemId { get; set; } - public int Quantity = 1; + public int Quantity { get; set; } public CraftIngredient(Guid itemId, int quantity) { diff --git a/Framework/Intersect.Framework.Core/GameObjects/Crafting/CraftingTableBase.cs b/Framework/Intersect.Framework.Core/GameObjects/Crafting/CraftingTableBase.cs index 2696e1142b..da468b05a2 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Crafting/CraftingTableBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Crafting/CraftingTableBase.cs @@ -10,7 +10,7 @@ namespace Intersect.GameObjects; public partial class CraftingTableBase : DatabaseObject, IFolderable { [NotMapped] - public DbList Crafts = new DbList(); + public DbList Crafts { get; set; } = []; [JsonConstructor] public CraftingTableBase(Guid id) : base(id) @@ -33,5 +33,5 @@ public string CraftsJson } /// - public string Folder { get; set; } = ""; + public string Folder { get; set; } = string.Empty; } diff --git a/Framework/Intersect.Framework.Core/GameObjects/EquipmentProperties.ShouldBeGenerated.cs b/Framework/Intersect.Framework.Core/GameObjects/EquipmentProperties.ShouldBeGenerated.cs index afb6584787..b8d6016b57 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/EquipmentProperties.ShouldBeGenerated.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/EquipmentProperties.ShouldBeGenerated.cs @@ -1,5 +1,6 @@ using Intersect.Enums; using Intersect.GameObjects.Ranges; +using Newtonsoft.Json; // ReSharper disable InconsistentNaming @@ -7,12 +8,14 @@ namespace Intersect.GameObjects; public partial class EquipmentProperties { + [JsonIgnore] public ItemRange StatRange_Attack { get => StatRanges.TryGetValue(Stat.Attack, out var range) ? range : StatRange_Attack = new ItemRange(); set => StatRanges[Stat.Attack] = value; } + [JsonIgnore] public ItemRange StatRange_AbilityPower { get => @@ -20,12 +23,14 @@ public ItemRange StatRange_AbilityPower set => StatRanges[Stat.AbilityPower] = value; } + [JsonIgnore] public ItemRange StatRange_Defense { get => StatRanges.TryGetValue(Stat.Defense, out var range) ? range : StatRange_Defense = new ItemRange(); set => StatRanges[Stat.Defense] = value; } + [JsonIgnore] public ItemRange StatRange_MagicResist { get => @@ -33,6 +38,7 @@ public ItemRange StatRange_MagicResist set => StatRanges[Stat.MagicResist] = value; } + [JsonIgnore] public ItemRange StatRange_Speed { get => StatRanges.TryGetValue(Stat.Speed, out var range) ? range : StatRange_Speed = new ItemRange(); diff --git a/Framework/Intersect.Framework.Core/GameObjects/Events/Commands/EventCommands.cs b/Framework/Intersect.Framework.Core/GameObjects/Events/Commands/EventCommands.cs index ab5c3fbe1e..18a3f6507f 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Events/Commands/EventCommands.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Events/Commands/EventCommands.cs @@ -37,9 +37,9 @@ public partial class ShowTextCommand : EventCommand { public override EventCommandType Type { get; } = EventCommandType.ShowText; - public string Text { get; set; } = ""; + public string Text { get; set; } = string.Empty; - public string Face { get; set; } = ""; + public string Face { get; set; } = string.Empty; } public partial class ShowOptionsCommand : EventCommand @@ -54,20 +54,20 @@ public ShowOptionsCommand(Dictionary> commandLists) for (var i = 0; i < BranchIds.Length; i++) { BranchIds[i] = Guid.NewGuid(); - commandLists.Add(BranchIds[i], new List()); + commandLists.Add(BranchIds[i], []); } } public override EventCommandType Type { get; } = EventCommandType.ShowOptions; - public string Text { get; set; } = ""; + public string Text { get; set; } = string.Empty; public string[] Options { get; set; } = new string[4]; //Id of the command list(s) you follow when a particular option is selected public Guid[] BranchIds { get; set; } = new Guid[4]; - public string Face { get; set; } = ""; + public string Face { get; set; } = string.Empty; public override string GetCopyData( Dictionary> commandLists, @@ -113,7 +113,7 @@ public InputVariableCommand(Dictionary> commandLists) for (var i = 0; i < BranchIds.Length; i++) { BranchIds[i] = Guid.NewGuid(); - commandLists.Add(BranchIds[i], new List()); + commandLists.Add(BranchIds[i], []); } } @@ -121,11 +121,11 @@ public InputVariableCommand(Dictionary> commandLists) public string Title { get; set; } - public string Text { get; set; } = ""; + public string Text { get; set; } = string.Empty; public VariableType VariableType { get; set; } = VariableType.PlayerVariable; - public Guid VariableId { get; set; } = new Guid(); + public Guid VariableId { get; set; } = new(); public long Minimum { get; set; } = 0; @@ -170,12 +170,12 @@ public partial class AddChatboxTextCommand : EventCommand { public override EventCommandType Type { get; } = EventCommandType.AddChatboxText; - public string Text { get; set; } = ""; + public string Text { get; set; } = string.Empty; // TODO: Expose this option to the user? public ChatMessageType MessageType { get; set; } = ChatMessageType.Notice; - public string Color { get; set; } = ""; + public string Color { get; set; } = string.Empty; public ChatboxChannel Channel { get; set; } = ChatboxChannel.Player; @@ -216,7 +216,7 @@ public ConditionalBranchCommand(Dictionary> commandList for (var i = 0; i < BranchIds.Length; i++) { BranchIds[i] = Guid.NewGuid(); - commandLists.Add(BranchIds[i], new List()); + commandLists.Add(BranchIds[i], []); } } @@ -349,7 +349,7 @@ public ChangeSpellsCommand(Dictionary> commandLists) for (var i = 0; i < BranchIds.Length; i++) { BranchIds[i] = Guid.NewGuid(); - commandLists.Add(BranchIds[i], new List()); + commandLists.Add(BranchIds[i], []); } } @@ -408,7 +408,7 @@ public ChangeItemsCommand(Dictionary> commandLists) for (var i = 0; i < BranchIds.Length; i++) { BranchIds[i] = Guid.NewGuid(); - commandLists.Add(BranchIds[i], new List()); + commandLists.Add(BranchIds[i], []); } } @@ -495,7 +495,7 @@ public partial class ChangeSpriteCommand : EventCommand { public override EventCommandType Type { get; } = EventCommandType.ChangeSprite; - public string Sprite { get; set; } = ""; + public string Sprite { get; set; } = string.Empty; } public partial class ChangeNameColorCommand : EventCommand @@ -526,7 +526,7 @@ public partial class ChangeFaceCommand : EventCommand { public override EventCommandType Type { get; } = EventCommandType.ChangeFace; - public string Face { get; set; } = ""; + public string Face { get; set; } = string.Empty; } public partial class ChangeGenderCommand : EventCommand @@ -570,7 +570,7 @@ public partial class SetMoveRouteCommand : EventCommand { public override EventCommandType Type { get; } = EventCommandType.SetMoveRoute; - public EventMoveRoute Route { get; set; } = new EventMoveRoute(); + public EventMoveRoute Route { get; set; } = new(); } public partial class WaitForRouteCommand : EventCommand @@ -653,7 +653,7 @@ public partial class PlayBgmCommand : EventCommand { public override EventCommandType Type { get; } = EventCommandType.PlayBgm; - public string File { get; set; } = ""; + public string File { get; set; } = string.Empty; } public partial class FadeoutBgmCommand : EventCommand @@ -665,7 +665,7 @@ public partial class PlaySoundCommand : EventCommand { public override EventCommandType Type { get; } = EventCommandType.PlaySound; - public string File { get; set; } = ""; + public string File { get; set; } = string.Empty; } public partial class StopSoundsCommand : EventCommand @@ -680,7 +680,7 @@ public partial class ShowPictureCommand : EventCommand /// /// Picture filename to show. /// - public string File { get; set; } = ""; + public string File { get; set; } = string.Empty; /// /// How the picture is rendered on the screen. @@ -758,7 +758,7 @@ public StartQuestCommand(Dictionary> commandLists) for (var i = 0; i < BranchIds.Length; i++) { BranchIds[i] = Guid.NewGuid(); - commandLists.Add(BranchIds[i], new List()); + commandLists.Add(BranchIds[i], []); } } @@ -834,7 +834,7 @@ public partial class ChangePlayerColorCommand : EventCommand /// /// The to apply to the player. /// - public Color Color { get; set; } = new Color(255, 255, 255, 255); + public Color Color { get; set; } = new(255, 255, 255, 255); } public partial class ChangeNameCommand : EventCommand @@ -849,7 +849,7 @@ public ChangeNameCommand(Dictionary> commandLists) for (var i = 0; i < BranchIds.Length; i++) { BranchIds[i] = Guid.NewGuid(); - commandLists.Add(BranchIds[i], new List()); + commandLists.Add(BranchIds[i], []); } } @@ -904,7 +904,7 @@ public CreateGuildCommand(Dictionary> commandLists) for (var i = 0; i < BranchIds.Length; i++) { BranchIds[i] = Guid.NewGuid(); - commandLists.Add(BranchIds[i], new List()); + commandLists.Add(BranchIds[i], []); } } @@ -948,7 +948,7 @@ public DisbandGuildCommand(Dictionary> commandLists) for (var i = 0; i < BranchIds.Length; i++) { BranchIds[i] = Guid.NewGuid(); - commandLists.Add(BranchIds[i], new List()); + commandLists.Add(BranchIds[i], []); } } diff --git a/Framework/Intersect.Framework.Core/GameObjects/Events/Condition.cs b/Framework/Intersect.Framework.Core/GameObjects/Events/Condition.cs index 0cba1cf0a7..7fae2c2e36 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Events/Condition.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Events/Condition.cs @@ -22,7 +22,7 @@ public partial class VariableIsCondition : Condition public Guid VariableId { get; set; } - public VariableComparison Comparison { get; set; } = new VariableComparison(); + public VariableComparison Comparison { get; set; } = new(); } public partial class HasItemCondition : Condition diff --git a/Framework/Intersect.Framework.Core/GameObjects/Events/EventBase.cs b/Framework/Intersect.Framework.Core/GameObjects/Events/EventBase.cs index 95a1045b93..90ad312f93 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Events/EventBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Events/EventBase.cs @@ -12,7 +12,7 @@ public partial class EventBase : DatabaseObject, IFolderable private string mCachedPagesData = null; //Event Pages - private List mPages = new List(); + private List mPages = []; //EF Parameterless Constructor public EventBase() @@ -33,20 +33,20 @@ public EventBase(Guid id, Guid mapId, int x, int y, bool isCommon = false, bool SpawnY = y; CommonEvent = isCommon; Global = isGlobal; - Pages = new List { new EventPage() }; + Pages = [new()]; } public EventBase(Guid id, bool isCommon = false) : base(id) { Name = "New Event"; - Pages = new List(); + Pages = []; CommonEvent = isCommon; } public EventBase(Guid id, EventBase copy) : base(id) { Name = "New Event"; - Pages = new List(); + Pages = []; Load(copy.JsonData); CommonEvent = copy.CommonEvent; } @@ -55,7 +55,7 @@ public EventBase(Guid id, string json, bool isCommon = false) : base(id) { Name = "New Event"; CommonEvent = isCommon; - Pages = new List(); + Pages = []; Load(json); } @@ -135,7 +135,7 @@ public List Pages ); /// - public string Folder { get; set; } = ""; + public string Folder { get; set; } = string.Empty; public static new Guid IdFromList(int listIndex) { diff --git a/Framework/Intersect.Framework.Core/GameObjects/Events/EventGraphic.cs b/Framework/Intersect.Framework.Core/GameObjects/Events/EventGraphic.cs index fa3e91b41c..15f121e4ac 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Events/EventGraphic.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Events/EventGraphic.cs @@ -27,7 +27,7 @@ public partial class EventGraphic public EventGraphic() { Type = EventGraphicType.None; - Filename = ""; + Filename = string.Empty; X = -1; Y = -1; Width = -1; diff --git a/Framework/Intersect.Framework.Core/GameObjects/Events/EventMoveRoute.cs b/Framework/Intersect.Framework.Core/GameObjects/Events/EventMoveRoute.cs index 0b0fda6cc4..5e8d584911 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Events/EventMoveRoute.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Events/EventMoveRoute.cs @@ -4,7 +4,7 @@ namespace Intersect.GameObjects.Events; public partial class EventMoveRoute { - public List Actions { get; set; } = new List(); + public List Actions { get; set; } = []; public bool IgnoreIfBlocked { get; set; } diff --git a/Framework/Intersect.Framework.Core/GameObjects/Events/EventMovement.cs b/Framework/Intersect.Framework.Core/GameObjects/Events/EventMovement.cs index b49a085724..7911275ced 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Events/EventMovement.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Events/EventMovement.cs @@ -10,5 +10,5 @@ public partial class EventMovement public EventMovementSpeed Speed { get; set; } = EventMovementSpeed.Normal; - public EventMoveRoute Route { get; set; } = new EventMoveRoute(); + public EventMoveRoute Route { get; set; } = new(); } diff --git a/Framework/Intersect.Framework.Core/GameObjects/Events/EventPage.cs b/Framework/Intersect.Framework.Core/GameObjects/Events/EventPage.cs index 8ba1a53742..7b573d4cd1 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Events/EventPage.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Events/EventPage.cs @@ -10,7 +10,7 @@ public partial class EventPage { public EventPage() { - CommandLists.Add(Guid.NewGuid(), new List()); + CommandLists.Add(Guid.NewGuid(), []); } [JsonConstructor] @@ -20,20 +20,19 @@ public EventPage(int ignoreThis) public Guid AnimationId { get; set; } - public Dictionary> CommandLists { get; set; } = - new Dictionary>(); + public Dictionary> CommandLists { get; set; } = new(); - public ConditionLists ConditionLists { get; set; } = new ConditionLists(); + public ConditionLists ConditionLists { get; set; } = new(); - public string Description { get; set; } = ""; + public string Description { get; set; } = string.Empty; public bool DirectionFix { get; set; } public bool DisablePreview { get; set; } = true; - public string FaceGraphic { get; set; } = ""; + public string FaceGraphic { get; set; } = string.Empty; - public EventGraphic Graphic { get; set; } = new EventGraphic(); + public EventGraphic Graphic { get; set; } = new(); public bool HideName { get; set; } @@ -41,7 +40,7 @@ public EventPage(int ignoreThis) public EventRenderLayer Layer { get; set; } = EventRenderLayer.SameAsPlayer; - public EventMovement Movement { get; set; } = new EventMovement(); + public EventMovement Movement { get; set; } = new(); public bool Passable { get; set; } diff --git a/Framework/Intersect.Framework.Core/GameObjects/GuildVariableBase.cs b/Framework/Intersect.Framework.Core/GameObjects/GuildVariableBase.cs index 31f8fc8820..a8615c0cd9 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/GuildVariableBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/GuildVariableBase.cs @@ -23,7 +23,7 @@ public GuildVariableBase() public string TextId { get; set; } // TODO(0.8): Rename this to DataType - public VariableDataType Type { get; set; } = VariableDataType.Boolean; + public new VariableDataType Type { get; set; } = VariableDataType.Boolean; /// public string Folder { get; set; } = string.Empty; diff --git a/Framework/Intersect.Framework.Core/GameObjects/ItemBase.cs b/Framework/Intersect.Framework.Core/GameObjects/ItemBase.cs index 024e58df14..74f5474f72 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/ItemBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/ItemBase.cs @@ -16,9 +16,9 @@ namespace Intersect.GameObjects; public partial class ItemBase : DatabaseObject, IFolderable { [NotMapped] - public ConditionLists UsageRequirements = new ConditionLists(); + public ConditionLists UsageRequirements { get; set; } = new(); - public string CannotUseMessage { get; set; } = ""; + public string CannotUseMessage { get; set; } = string.Empty; public ItemBase() { @@ -170,15 +170,15 @@ public EventBase Event set => EventId = value?.Id ?? Guid.Empty; } - public string Description { get; set; } = ""; + public string Description { get; set; } = string.Empty; - public string FemalePaperdoll { get; set; } = ""; + public string FemalePaperdoll { get; set; } = string.Empty; public ItemType ItemType { get; set; } - public string MalePaperdoll { get; set; } = ""; + public string MalePaperdoll { get; set; } = string.Empty; - public string Icon { get; set; } = ""; + public string Icon { get; set; } = string.Empty; /// /// The database compatible version of @@ -330,7 +330,7 @@ public string JsonUsageRequirements public string EffectsJson { get => JsonConvert.SerializeObject(Effects); - set => Effects = JsonConvert.DeserializeObject>(value ?? "") ?? new List(); + set => Effects = JsonConvert.DeserializeObject>(value ?? "") ?? []; } public EquipmentProperties? EquipmentProperties { get; set; } @@ -353,7 +353,7 @@ public string EventTriggersJson } [NotMapped, JsonIgnore] - public Dictionary EventTriggers { get; set; } = new Dictionary(); + public Dictionary EventTriggers { get; set; } = new(); public bool TryGetRangeFor(Stat stat, [NotNullWhen(true)] out ItemRange? range) { @@ -428,7 +428,7 @@ public void SetEffectOfType(ItemEffect type, int value) } /// - public string Folder { get; set; } = ""; + public string Folder { get; set; } = string.Empty; /// /// Gets an array of all items sharing the provided cooldown group. @@ -442,7 +442,7 @@ public static ItemBase[] GetCooldownGroup(string cooldownGroup) // No point looking for nothing. if (string.IsNullOrWhiteSpace(cooldownGroup)) { - return Array.Empty(); + return []; } return Lookup.Values.OfType() @@ -482,7 +482,7 @@ private void Initialize() VitalsRegen = new long[Enum.GetValues().Length]; PercentageVitalsGiven = new int[Enum.GetValues().Length]; Consumable = new ConsumableData(); - Effects = new List(); + Effects = []; Color = new Color(255, 255, 255, 255); } } diff --git a/Framework/Intersect.Framework.Core/GameObjects/Maps/MapBase.cs b/Framework/Intersect.Framework.Core/GameObjects/Maps/MapBase.cs index 838ca9c2d9..54668be3bc 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Maps/MapBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Maps/MapBase.cs @@ -24,7 +24,7 @@ public partial class MapBase : DatabaseObject [NotMapped] [JsonIgnore] - public readonly Dictionary LocalEvents = new Dictionary(); + public readonly Dictionary LocalEvents = new(); //Client/Editor Only [JsonIgnore] @@ -32,12 +32,12 @@ public partial class MapBase : DatabaseObject public MapAutotiles Autotiles; [NotMapped] - public List EventIds = new List(); + public List EventIds { get; set; } = []; //Core Data [JsonIgnore] [NotMapped] - public Dictionary Layers = new Dictionary(); + public Dictionary Layers = new(); //Map Attributes private MapAttribute[,] mAttributes = new MapAttribute[Options.MapWidth, Options.MapHeight]; @@ -48,7 +48,7 @@ public partial class MapBase : DatabaseObject //SyncLock [JsonIgnore] [NotMapped] - protected object mMapLock = new object(); + protected object mMapLock = new(); [JsonConstructor] public MapBase(Guid id) : base(id) @@ -151,7 +151,7 @@ public MapBase(MapBase mapBase) : base(mapBase?.Id ?? Guid.Empty) } EventIds?.Clear(); - EventIds?.AddRange(mapBase.EventIds?.ToArray() ?? new Guid[] { }); + EventIds?.AddRange(mapBase.EventIds?.ToArray() ?? []); } } } @@ -223,7 +223,7 @@ public string LightsJson [NotMapped] [JsonProperty] - public List Lights { get; private set; } = new List(); + public List Lights { get; private set; } = []; [Column("Events")] [JsonIgnore] @@ -277,7 +277,7 @@ public string NpcSpawnsJson [NotMapped] [JsonProperty] - public List Spawns { get; private set; } = new List(); + public List Spawns { get; private set; } = []; //Properties public string Music { get; set; } = null; diff --git a/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapList.cs b/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapList.cs index 06cec31c6a..510b29bf33 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapList.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapList.cs @@ -12,11 +12,11 @@ public partial class MapList public Guid Id { get; protected set; } = Guid.NewGuid(); [NotMapped] - public List Items { get; set; } = new List(); + public List Items { get; set; } = []; - public static MapList List { get; set; } = new MapList(); + public static MapList List { get; set; } = new(); - public static List OrderedMaps { get; } = new List(); + public static List OrderedMaps { get; } = []; [JsonIgnore] [Column("JsonData")] diff --git a/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapListFolder.cs b/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapListFolder.cs index 05be7c6e82..162f1d08ac 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapListFolder.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapListFolder.cs @@ -4,7 +4,7 @@ namespace Intersect.GameObjects.Maps.MapList; public partial class MapListFolder : MapListItem { - public MapList Children = new MapList(); + public MapList Children = new(); public Guid FolderId = Guid.Empty; diff --git a/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapListItem.cs b/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapListItem.cs index 352d4052e8..1c5745b54d 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapListItem.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Maps/MapList/MapListItem.cs @@ -2,7 +2,7 @@ public partial class MapListItem { - public string Name = ""; + public string Name = string.Empty; public int Type = -1; //0 for directory, 1 for map } diff --git a/Framework/Intersect.Framework.Core/GameObjects/NpcBase.cs b/Framework/Intersect.Framework.Core/GameObjects/NpcBase.cs index 4853c716dc..b38666dfb7 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/NpcBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/NpcBase.cs @@ -12,29 +12,57 @@ namespace Intersect.GameObjects; public partial class NpcBase : DatabaseObject, IFolderable { - [NotMapped] - public ConditionLists AttackOnSightConditions = new ConditionLists(); + private long[] _maxVitals = new long[Enum.GetValues().Length]; + private int[] _stats = new int[Enum.GetValues().Length]; + private long[] _vitalRegen = new long[Enum.GetValues().Length]; [NotMapped] - public List Drops = new List(); + public ConditionLists AttackOnSightConditions { get; set; } = new(); [NotMapped] - public long[] MaxVital = new long[Enum.GetValues().Length]; + public List Drops { get; set; }= []; - [NotMapped] - public ConditionLists PlayerCanAttackConditions = new ConditionLists(); + [NotMapped, JsonIgnore] + public long[] MaxVitals + { + get => _maxVitals; + set => _maxVitals = value; + } - [NotMapped] - public ConditionLists PlayerFriendConditions = new ConditionLists(); + [JsonProperty(nameof(MaxVitals)), NotMapped] + public IReadOnlyDictionary MaxVitalsLookup => MaxVitals.Select((value, index) => (value, index)) + .ToDictionary(t => (Vital)t.index, t => t.value).AsReadOnly(); + + [JsonProperty(nameof(VitalRegen)), NotMapped] + public IReadOnlyDictionary VitalRegenLookup => VitalRegen.Select((value, index) => (value, index)) + .ToDictionary(t => (Vital)t.index, t => t.value).AsReadOnly(); + + [JsonProperty(nameof(Stats)), NotMapped] + public IReadOnlyDictionary StatsLookup => Stats.Select((statValue, index) => (statValue, index)) + .ToDictionary(t => (Stat)t.index, t => t.statValue).AsReadOnly(); [NotMapped] - public int[] Stats = new int[Enum.GetValues().Length]; + public ConditionLists PlayerCanAttackConditions { get; set; } = new(); [NotMapped] - public long[] VitalRegen = new long[Enum.GetValues().Length]; + public ConditionLists PlayerFriendConditions { get; set; } = new(); + + [NotMapped, JsonIgnore] + public int[] Stats + { + get => _stats; + set => _stats = value; + } + + [NotMapped, JsonIgnore] + public long[] VitalRegen + { + get => _vitalRegen; + set => _vitalRegen = value; + } [NotMapped] - public List Immunities = new List(); + public List Immunities { get; set; } = []; [JsonIgnore] [Column("Immunities")] @@ -43,7 +71,7 @@ public string ImmunitiesJson get => JsonConvert.SerializeObject(Immunities); set { - Immunities = JsonConvert.DeserializeObject>(value ?? "") ?? new List(); + Immunities = JsonConvert.DeserializeObject>(value ?? "") ?? []; } } @@ -68,7 +96,7 @@ public string JsonAggroList } [NotMapped] - public List AggroList { get; set; } = new List(); + public List AggroList { get; set; } = []; public bool AttackAllies { get; set; } @@ -182,8 +210,8 @@ public string JsonDrops [JsonIgnore] public string JsonMaxVital { - get => DatabaseUtils.SaveLongArray(MaxVital, Enum.GetValues().Length); - set => DatabaseUtils.LoadLongArray(ref MaxVital, value, Enum.GetValues().Length); + get => DatabaseUtils.SaveLongArray(_maxVitals, Enum.GetValues().Length); + set => DatabaseUtils.LoadLongArray(ref _maxVitals, value, Enum.GetValues().Length); } //NPC vs NPC Combat @@ -210,9 +238,9 @@ public string CraftsJson } [NotMapped] - public DbList Spells { get; set; } = new DbList(); + public DbList Spells { get; set; } = []; - public string Sprite { get; set; } = ""; + public string Sprite { get; set; } = string.Empty; /// /// The database compatible version of @@ -229,14 +257,14 @@ public string JsonColor /// Defines the ARGB color settings for this Npc. /// [NotMapped] - public Color Color { get; set; } = new Color(255, 255, 255, 255); + public Color Color { get; set; } = new(255, 255, 255, 255); [Column("Stats")] [JsonIgnore] public string JsonStat { - get => DatabaseUtils.SaveIntArray(Stats, Enum.GetValues().Length); - set => DatabaseUtils.LoadIntArray(ref Stats, value, Enum.GetValues().Length); + get => DatabaseUtils.SaveIntArray(_stats, Enum.GetValues().Length); + set => DatabaseUtils.LoadIntArray(ref _stats, value, Enum.GetValues().Length); } //Vital Regen % @@ -244,12 +272,12 @@ public string JsonStat [Column("VitalRegen")] public string RegenJson { - get => DatabaseUtils.SaveLongArray(VitalRegen, Enum.GetValues().Length); - set => VitalRegen = DatabaseUtils.LoadLongArray(value, Enum.GetValues().Length); + get => DatabaseUtils.SaveLongArray(_vitalRegen, Enum.GetValues().Length); + set => DatabaseUtils.LoadLongArray(ref _vitalRegen, value, Enum.GetValues().Length); } /// - public string Folder { get; set; } = ""; + public string Folder { get; set; } = string.Empty; public SpellBase GetRandomSpell(Random random) { diff --git a/Framework/Intersect.Framework.Core/GameObjects/ProjectileBase.cs b/Framework/Intersect.Framework.Core/GameObjects/ProjectileBase.cs index 979b4ddda9..173fba5f4e 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/ProjectileBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/ProjectileBase.cs @@ -11,7 +11,7 @@ public partial class ProjectileBase : DatabaseObject, IFolderabl public const int MAX_PROJECTILE_DIRECTIONS = 8; public static readonly int[] ProjectileRotationDir = - { + [ 0, 1, 2, 3, 4, 5, 7, 6, // Up 1, 0, 3, 2, 6, 7, 5, 4, // Down 2, 3, 1, 0, 7, 4, 6, 5, // Left @@ -19,18 +19,18 @@ public partial class ProjectileBase : DatabaseObject, IFolderabl 4, 6, 7, 5, 2, 0, 1, 3, // UpLeft 5, 7, 4, 6, 0, 3, 2, 1, // UpRight 6, 4, 5, 7, 3, 1, 0, 2, // DownRight - 7, 5, 6, 4, 1, 2, 3, 0 // DownLeft - }; + 7, 5, 6, 4, 1, 2, 3, 0, // DownLeft + ]; public const int SPAWN_LOCATIONS_HEIGHT = 5; public const int SPAWN_LOCATIONS_WIDTH = 5; [NotMapped] - public List Animations = new List(); + public List Animations { get; set; } = []; [NotMapped] - public Location[,] SpawnLocations = new Location[ + public Location[,] SpawnLocations { get; set; } = new Location[ SPAWN_LOCATIONS_WIDTH, SPAWN_LOCATIONS_HEIGHT ]; @@ -117,7 +117,7 @@ public string SpawnsJson } [NotMapped] - public List GrappleHookOptions = new List(); + public List GrappleHookOptions { get; set; } = []; [JsonIgnore] [Column("GrappleHookOptions")] @@ -126,7 +126,7 @@ public string GrappleHookOptionsJson get => JsonConvert.SerializeObject(GrappleHookOptions); set { - GrappleHookOptions = JsonConvert.DeserializeObject>(value ?? "") ?? new List(); + GrappleHookOptions = JsonConvert.DeserializeObject>(value ?? "") ?? []; } } @@ -144,7 +144,7 @@ public SpellBase Spell } /// - public string Folder { get; set; } = ""; + public string Folder { get; set; } = string.Empty; } public partial class Location diff --git a/Framework/Intersect.Framework.Core/GameObjects/QuestBase.cs b/Framework/Intersect.Framework.Core/GameObjects/QuestBase.cs index 7d067b61b9..05042747ea 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/QuestBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/QuestBase.cs @@ -41,14 +41,14 @@ public partial class QuestBase : DatabaseObject, IFolderable [NotMapped] [JsonIgnore] //Events that need to be added for the quest, int is task id - public Dictionary AddEvents = new Dictionary(); + public Dictionary AddEvents { get; set; } = []; [NotMapped] //Events that need to be removed for the quest - public List RemoveEvents = new List(); + public List RemoveEvents { get; set; } = []; [NotMapped] - public List Tasks = new List(); + public List Tasks { get; set; } = []; [JsonConstructor] public QuestBase(Guid Id) : base(Id) @@ -63,13 +63,13 @@ public QuestBase() } //Basic Quest Properties - public string StartDescription { get; set; } = ""; + public string StartDescription { get; set; } = string.Empty; - public string BeforeDescription { get; set; } = ""; + public string BeforeDescription { get; set; } = string.Empty; - public string EndDescription { get; set; } = ""; + public string EndDescription { get; set; } = string.Empty; - public string InProgressDescription { get; set; } = ""; + public string InProgressDescription { get; set; } = string.Empty; public bool LogAfterComplete { get; set; } @@ -89,7 +89,7 @@ public string JsonRequirements } [NotMapped] - public ConditionLists Requirements { get; set; } = new ConditionLists(); + public ConditionLists Requirements { get; set; } = new(); [Column("StartEvent")] public Guid StartEventId { get; set; } @@ -149,10 +149,10 @@ public string LocalEventsJson //Editor Only [NotMapped] [JsonIgnore] - public Dictionary OriginalTaskEventIds { get; set; } = new Dictionary(); + public Dictionary OriginalTaskEventIds { get; set; } = new(); /// - public string Folder { get; set; } = ""; + public string Folder { get; set; } = string.Empty; /// /// Hides this quest from the quest log if it has not been started and cannot be started due to the requiremetns/conditions @@ -162,17 +162,17 @@ public string LocalEventsJson /// /// Quest category in the quest log when this quest hasn't been started yet /// - public string UnstartedCategory { get; set; } = ""; + public string UnstartedCategory { get; set; } = string.Empty; /// /// Quest category in the quest log when this quest is in progress /// - public string InProgressCategory { get; set; } = ""; + public string InProgressCategory { get; set; } = string.Empty; /// /// Quest category in the quest log when this quest has been completed /// - public string CompletedCategory { get; set; } = ""; + public string CompletedCategory { get; set; } = string.Empty; /// /// Order priority of this quest within the quest log @@ -234,11 +234,11 @@ public EventBase CompletionEvent public int Quantity { get; set; } - public string Description { get; set; } = ""; + public string Description { get; set; } = string.Empty; public string GetTaskString(Dictionary descriptions) { - var taskString = ""; + var taskString = string.Empty; switch (Objective) { case QuestObjective.EventDriven: //Event Driven diff --git a/Framework/Intersect.Framework.Core/GameObjects/ResourceBase.cs b/Framework/Intersect.Framework.Core/GameObjects/ResourceBase.cs index 25349de54e..ec180e351d 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/ResourceBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/ResourceBase.cs @@ -31,12 +31,12 @@ public partial class ResourceState public partial class ResourceBase : DatabaseObject, IFolderable { [NotMapped] - public List Drops = new List(); + public List Drops { get; set; } = []; [NotMapped] - public ConditionLists HarvestingRequirements = new ConditionLists(); + public ConditionLists HarvestingRequirements { get; set; } = new(); - public string CannotHarvestMessage { get; set; } = ""; + public string CannotHarvestMessage { get; set; } = string.Empty; [JsonConstructor] public ResourceBase(Guid id) : base(id) diff --git a/Framework/Intersect.Framework.Core/GameObjects/ShopBase.cs b/Framework/Intersect.Framework.Core/GameObjects/ShopBase.cs index b80217db25..61386a7432 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/ShopBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/ShopBase.cs @@ -9,10 +9,10 @@ namespace Intersect.GameObjects; public partial class ShopBase : DatabaseObject, IFolderable { [NotMapped] - public List BuyingItems = new List(); + public List BuyingItems { get; set; } = []; [NotMapped] - public List SellingItems = new List(); + public List SellingItems { get; set; } = []; [JsonConstructor] public ShopBase(Guid id) : base(id) @@ -62,7 +62,7 @@ public string JsonSellingItems public string SellSound { get; set; } = null; /// - public string Folder { get; set; } = ""; + public string Folder { get; set; } = string.Empty; } public partial class ShopItem diff --git a/Framework/Intersect.Framework.Core/GameObjects/SpellBase.cs b/Framework/Intersect.Framework.Core/GameObjects/SpellBase.cs index 8937639c9b..eab96cabe3 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/SpellBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/SpellBase.cs @@ -13,8 +13,14 @@ namespace Intersect.GameObjects; public partial class SpellBase : DatabaseObject, IFolderable { + private long[] _vitalCost = new long[Enum.GetValues().Length]; + [NotMapped] - public long[] VitalCost = new long[Enum.GetValues().Length]; + public long[] VitalCost + { + get => _vitalCost; + set => _vitalCost = value; + } [JsonConstructor] public SpellBase(Guid id) : base(id) @@ -29,9 +35,9 @@ public SpellBase() public SpellType SpellType { get; set; } - public string Description { get; set; } = ""; + public string Description { get; set; } = string.Empty; - public string Icon { get; set; } = ""; + public string Icon { get; set; } = string.Empty; //Animations [Column("CastAnimation")] @@ -100,20 +106,20 @@ public string JsonCastRequirements } [NotMapped] - public ConditionLists CastingRequirements { get; set; } = new ConditionLists(); + public ConditionLists CastingRequirements { get; set; } = new(); - public string CannotCastMessage { get; set; } = ""; + public string CannotCastMessage { get; set; } = string.Empty; public string CastSpriteOverride { get; set; } //Combat Info - public SpellCombatData Combat { get; set; } = new SpellCombatData(); + public SpellCombatData Combat { get; set; } = new(); //Warp Info - public SpellWarpData Warp { get; set; } = new SpellWarpData(); + public SpellWarpData Warp { get; set; } = new(); //Dash Info - public SpellDashOpts Dash { get; set; } = new SpellDashOpts(); + public SpellDashOpts Dash { get; set; } = new(); //Event Info [Column("Event")] @@ -132,12 +138,12 @@ public EventBase Event [JsonIgnore] public string VitalCostJson { - get => DatabaseUtils.SaveLongArray(VitalCost, Enum.GetValues().Length); - set => VitalCost = DatabaseUtils.LoadLongArray(value, Enum.GetValues().Length); + get => DatabaseUtils.SaveLongArray(_vitalCost, Enum.GetValues().Length); + set => DatabaseUtils.LoadLongArray(ref _vitalCost, value, Enum.GetValues().Length); } /// - public string Folder { get; set; } = ""; + public string Folder { get; set; } = string.Empty; /// /// Gets an array of all items sharing the provided cooldown group. @@ -151,7 +157,7 @@ public static SpellBase[] GetCooldownGroup(string cooldownGroup) // No point looking for nothing. if (string.IsNullOrWhiteSpace(cooldownGroup)) { - return Array.Empty(); + return []; } return Lookup diff --git a/Framework/Intersect.Framework.Core/GameObjects/Switches and Variables/IVariableBase.cs b/Framework/Intersect.Framework.Core/GameObjects/Switches and Variables/IVariableBase.cs index 87bbddbb2e..9e5eb65073 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Switches and Variables/IVariableBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Switches and Variables/IVariableBase.cs @@ -4,7 +4,7 @@ namespace Intersect.GameObjects.Switches_and_Variables; public interface IVariableBase : IDatabaseObject, IFolderable { - public VariableDataType Type { get; set; } + new VariableDataType Type { get; set; } - public string TextId { get; set; } + string TextId { get; set; } } diff --git a/Framework/Intersect.Framework.Core/GameObjects/Switches and Variables/PlayerVariableBase.cs b/Framework/Intersect.Framework.Core/GameObjects/Switches and Variables/PlayerVariableBase.cs index 4e474251d8..7a4ba52b4c 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Switches and Variables/PlayerVariableBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Switches and Variables/PlayerVariableBase.cs @@ -23,7 +23,7 @@ public PlayerVariableBase() public string TextId { get; set; } // TODO(0.8): Rename this to DataType - public VariableDataType Type { get; set; } = VariableDataType.Boolean; + public new VariableDataType Type { get; set; } = VariableDataType.Boolean; /// public string Folder { get; set; } = string.Empty; diff --git a/Framework/Intersect.Framework.Core/GameObjects/Switches and Variables/ServerVariableBase.cs b/Framework/Intersect.Framework.Core/GameObjects/Switches and Variables/ServerVariableBase.cs index 26ec530921..8941142aa0 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Switches and Variables/ServerVariableBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Switches and Variables/ServerVariableBase.cs @@ -25,14 +25,14 @@ public ServerVariableBase() public string TextId { get; set; } // TODO(0.8): Rename this to DataType - public VariableDataType Type { get; set; } = VariableDataType.Boolean; + public new VariableDataType Type { get; set; } = VariableDataType.Boolean; [NotMapped] [JsonIgnore] - public VariableValue Value { get; set; } = new VariableValue(); + public VariableValue Value { get; set; } = new(); [NotMapped] - [JsonProperty("Value")] + [JsonProperty(nameof(Value))] public dynamic ValueData { get => Value.Value; diff --git a/Framework/Intersect.Framework.Core/GameObjects/Switches and Variables/VariableValue.cs b/Framework/Intersect.Framework.Core/GameObjects/Switches and Variables/VariableValue.cs index 04133af2b5..312289ebde 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/Switches and Variables/VariableValue.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/Switches and Variables/VariableValue.cs @@ -24,7 +24,7 @@ public dynamic Value [JsonIgnore] public JObject Json { - get => new JObject + get => new() { {nameof(Type), (byte) Type}, {nameof(Value), Value} diff --git a/Framework/Intersect.Framework.Core/GameObjects/TilesetBase.cs b/Framework/Intersect.Framework.Core/GameObjects/TilesetBase.cs index b35beceeb0..c552580143 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/TilesetBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/TilesetBase.cs @@ -9,13 +9,13 @@ public partial class TilesetBase : DatabaseObject [JsonConstructor] public TilesetBase(Guid id) : base(id) { - Name = ""; + Name = string.Empty; } //Ef Parameterless Constructor public TilesetBase() { - Name = ""; + Name = string.Empty; } public new string Name diff --git a/Framework/Intersect.Framework.Core/GameObjects/TimeBase.cs b/Framework/Intersect.Framework.Core/GameObjects/TimeBase.cs index 0c1cbe8671..7a251ed30d 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/TimeBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/TimeBase.cs @@ -6,10 +6,10 @@ namespace Intersect.GameObjects; public partial class TimeBase { - private static TimeBase sTimeBase = new TimeBase(); + private static TimeBase sTimeBase = new(); [NotMapped] - public Color[] DaylightHues; + public Color[]? DaylightHues { get; set; } public TimeBase() { diff --git a/Framework/Intersect.Framework.Core/GameObjects/UserVariableBase.cs b/Framework/Intersect.Framework.Core/GameObjects/UserVariableBase.cs index 9ca4e41303..aa6b274720 100644 --- a/Framework/Intersect.Framework.Core/GameObjects/UserVariableBase.cs +++ b/Framework/Intersect.Framework.Core/GameObjects/UserVariableBase.cs @@ -25,8 +25,8 @@ public UserVariableBase() // TODO rename this [Column("DataType")] - public VariableDataType Type { get; set; } = VariableDataType.Boolean; + public new VariableDataType Type { get; set; } = VariableDataType.Boolean; /// - public string Folder { get; set; } = ""; + public string Folder { get; set; } = string.Empty; } diff --git a/Framework/Intersect.Framework.Core/Models/DatabaseObject.cs b/Framework/Intersect.Framework.Core/Models/DatabaseObject.cs index ab76f1b3fe..41cad133a0 100644 --- a/Framework/Intersect.Framework.Core/Models/DatabaseObject.cs +++ b/Framework/Intersect.Framework.Core/Models/DatabaseObject.cs @@ -43,7 +43,8 @@ protected DatabaseObject(Guid guid) ? gameObjectType : throw new InvalidOperationException($"{typeof(TObject).Name} not set up correctly"); - [JsonIgnore] [NotMapped] public GameObjectType Type + [NotMapped] + public GameObjectType Type { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ObjectType; diff --git a/Framework/Intersect.Framework.Core/Models/IDatabaseObject.cs b/Framework/Intersect.Framework.Core/Models/IDatabaseObject.cs index ab8fffe90e..4db21ab758 100644 --- a/Framework/Intersect.Framework.Core/Models/IDatabaseObject.cs +++ b/Framework/Intersect.Framework.Core/Models/IDatabaseObject.cs @@ -1,28 +1,31 @@ using Intersect.Collections; using Intersect.Enums; +using Newtonsoft.Json; namespace Intersect.Models; public interface IDatabaseObject : INamedObject { - public GameObjectType Type { get; } + GameObjectType Type { get; } - public string DatabaseTable { get; } + [JsonIgnore] + string DatabaseTable { get; } - public long TimeCreated { get; set; } + long TimeCreated { get; set; } - public string JsonData { get; } + [JsonIgnore] + string JsonData { get; } - public void Load(string json, bool keepCreationTime = false); + void Load(string json, bool keepCreationTime = false); - public void MakeBackup(); + void MakeBackup(); - public void RestoreBackup(); + void RestoreBackup(); - public void DeleteBackup(); + void DeleteBackup(); - public void Delete(); + void Delete(); } diff --git a/Framework/Intersect.Framework.Core/Models/INamedObject.cs b/Framework/Intersect.Framework.Core/Models/INamedObject.cs index 715dfc7534..22d28cda17 100644 --- a/Framework/Intersect.Framework.Core/Models/INamedObject.cs +++ b/Framework/Intersect.Framework.Core/Models/INamedObject.cs @@ -3,6 +3,6 @@ public interface INamedObject : IObject { - public string Name { get; set; } + string Name { get; set; } } diff --git a/Framework/Intersect.Framework.Core/Models/IObject.cs b/Framework/Intersect.Framework.Core/Models/IObject.cs index 04b771db87..11774d9b04 100644 --- a/Framework/Intersect.Framework.Core/Models/IObject.cs +++ b/Framework/Intersect.Framework.Core/Models/IObject.cs @@ -3,6 +3,6 @@ public interface IObject { - public Guid Id { get; } + Guid Id { get; } } diff --git a/Framework/Intersect.Framework.Core/Serialization/AggregateContractResolver.cs b/Framework/Intersect.Framework.Core/Serialization/AggregateContractResolver.cs new file mode 100644 index 0000000000..e952932253 --- /dev/null +++ b/Framework/Intersect.Framework.Core/Serialization/AggregateContractResolver.cs @@ -0,0 +1,28 @@ +using Newtonsoft.Json.Serialization; + +namespace Intersect.Framework.Core.Serialization; + +public sealed class AggregateContractResolver( + IContractResolver fallbackContractResolver, + params IRestrictedContractResolver[] contractResolvers +) + : IContractResolver +{ + private readonly IContractResolver _fallbackContractResolver = fallbackContractResolver; + private readonly IRestrictedContractResolver[] _contractResolvers = contractResolvers; + + public JsonContract ResolveContract(Type type) + { + foreach (var contractResolver in _contractResolvers) + { + if (!contractResolver.SupportsType(type)) + { + continue; + } + + return contractResolver.ResolveContract(type); + } + + return _fallbackContractResolver.ResolveContract(type); + } +} \ No newline at end of file diff --git a/Framework/Intersect.Framework.Core/Serialization/IRestrictedContractResolver.cs b/Framework/Intersect.Framework.Core/Serialization/IRestrictedContractResolver.cs new file mode 100644 index 0000000000..b619711b1b --- /dev/null +++ b/Framework/Intersect.Framework.Core/Serialization/IRestrictedContractResolver.cs @@ -0,0 +1,8 @@ +using Newtonsoft.Json.Serialization; + +namespace Intersect.Framework.Core.Serialization; + +public interface IRestrictedContractResolver : IContractResolver +{ + bool SupportsType(Type type); +} \ No newline at end of file diff --git a/Framework/Intersect.Framework/Reflection/PropertyInfoExtensions.cs b/Framework/Intersect.Framework/Reflection/PropertyInfoExtensions.cs new file mode 100644 index 0000000000..f8880fda4a --- /dev/null +++ b/Framework/Intersect.Framework/Reflection/PropertyInfoExtensions.cs @@ -0,0 +1,81 @@ +using System.Reflection; + +namespace Intersect.Framework.Reflection; + +public static class PropertyInfoExtensions +{ + public static bool IsPropertyDeclaredInBaseTypeOrInterface( + this PropertyInfo propertyInfo, + Type type, + bool checkSetter = false + ) => propertyInfo.DeclaringType != type || propertyInfo.IsPropertyDeclaredByInterface(type, checkSetter); + + public static bool IsPropertyDeclaredByInterface(this PropertyInfo propertyInfo, bool checkSetter = false) => + IsPropertyDeclaredByInterface( + propertyInfo, + propertyInfo.DeclaringType ?? throw new ArgumentException( + $"{nameof(propertyInfo)} has a null {nameof(PropertyInfo.DeclaringType)}", + nameof(propertyInfo) + ), + checkSetter + ); + + public static bool IsPropertyDeclaredByInterface(this PropertyInfo propertyInfo, + bool checkSetter = false) => IsPropertyDeclaredByInterface(propertyInfo, typeof(TSearchType), checkSetter); + + public static bool IsPropertyDeclaredByInterface(this PropertyInfo propertyInfo, Type searchType, bool checkSetter = false) + { + var declaringType = propertyInfo.DeclaringType; + if (declaringType == default) + { + return false; + } + + MethodInfo searchMethodInfo; + if (checkSetter) + { + searchMethodInfo = propertyInfo.SetMethod ?? throw new InvalidOperationException( + $"Tried to check setter of {declaringType.GetName(qualified: true)}.{propertyInfo.Name} which does not exist" + ); + } + else + { + searchMethodInfo = propertyInfo.GetMethod ?? throw new InvalidOperationException( + $"Tried to check getter of {declaringType.GetName(qualified: true)}.{propertyInfo.Name} which does not exist" + ); + } + + var interfaceTypes = searchType.GetInterfaces(); + if (searchType.IsInterface) + { + interfaceTypes = + [ + searchType, + ..interfaceTypes, + ]; + } + + if (searchType.IsInterface) + { + return false; + } + + foreach (var interfaceType in interfaceTypes) + { + var interfaceMap = declaringType.GetInterfaceMap(interfaceType); + if (interfaceMap.TargetMethods.Contains(searchMethodInfo)) + { + return true; + } + + if (interfaceMap.TargetMethods.Any( + targetMethodInfo => targetMethodInfo.MethodHandle == searchMethodInfo.MethodHandle + )) + { + return true; + } + } + + return false; + } +} \ No newline at end of file diff --git a/Framework/Intersect.Framework/Reflection/TypeExtensions.cs b/Framework/Intersect.Framework/Reflection/TypeExtensions.cs index 3c07e949b4..dd87cd0000 100755 --- a/Framework/Intersect.Framework/Reflection/TypeExtensions.cs +++ b/Framework/Intersect.Framework/Reflection/TypeExtensions.cs @@ -55,6 +55,63 @@ public static string[] GetMappedColumnNames(this Type type) .ToArray(); } + public static bool IsOwnProperty(this Type type, PropertyInfo propertyInfo) => + !propertyInfo.IsPropertyDeclaredInBaseTypeOrInterface(type); + + public static bool TryFindProperty( + this Type type, + string propertyName, + [NotNullWhen(true)] out PropertyInfo? propertyInfo + ) => TryFindProperty( + type, + propertyName, + BindingFlags.Instance | BindingFlags.Public, + out propertyInfo + ); + + public static bool TryFindProperty( + this Type type, + string propertyName, + BindingFlags bindingFlags, + [NotNullWhen(true)] out PropertyInfo? propertyInfo + ) + { + try + { + propertyInfo = type.GetProperty(propertyName, bindingFlags); + } + catch (AmbiguousMatchException) + { + var matchingProperties = type.GetProperties(bindingFlags).Where( + propertyInfo => string.Equals(propertyName, propertyInfo.Name, StringComparison.Ordinal) + ).ToArray(); + propertyInfo = matchingProperties.FirstOrDefault(propertyInfo => propertyInfo.DeclaringType == type) ?? + matchingProperties.First(); + } + + if (!type.IsInterface || propertyInfo != default) + { + return propertyInfo != default; + } + + if (!bindingFlags.HasFlag(BindingFlags.Public) || !bindingFlags.HasFlag(BindingFlags.Instance)) + { + return propertyInfo != default; + } + + var baseInterfaceTypes = type.GetInterfaces(); + foreach (var baseInterfaceType in baseInterfaceTypes) + { + propertyInfo = baseInterfaceType.GetProperty(propertyName); + if (propertyInfo != default) + { + return true; + } + } + + return false; + } + public static string QualifiedGenericName(this Type type) => $"{type.Name}<{string.Join(", ", type.GenericTypeArguments.Select(parameterType => parameterType.Name))}>"; @@ -294,6 +351,16 @@ public static Type[] FindDerivedTypes(this Type type, params Assembly[] assembli ); } + public static Type[] GetUniqueInterfaces(this Type type) + { + var interfaceTypes = type.GetInterfaces().ToHashSet(); + var duplicateInterfaces = + interfaceTypes.SelectMany(interfaceType => interfaceType.GetInterfaces()).Distinct().ToArray(); + var uniqueInterfaces = interfaceTypes.Except(duplicateInterfaces) + .Except(type.BaseType?.GetUniqueInterfaces() ?? []).ToArray(); + return uniqueInterfaces; + } + public static Type[] FindGenericTypeParameters(this Type type) => type.FindGenericTypeParameters(true); public static Type[] FindGenericTypeParameters(this Type type, bool throwOnNonGeneric) => diff --git a/Intersect (Core)/IO/ConsoleContext.cs b/Intersect (Core)/IO/ConsoleContext.cs index fd75e10dc4..2bf00fba35 100644 --- a/Intersect (Core)/IO/ConsoleContext.cs +++ b/Intersect (Core)/IO/ConsoleContext.cs @@ -216,7 +216,7 @@ Func readKey { InputBuffer.Clear(); - var line = ""; + var line = string.Empty; var finished = false; while (!finished) { diff --git a/Intersect (Core)/Memory/MemoryBuffer.cs b/Intersect (Core)/Memory/MemoryBuffer.cs index 8890a6495e..549728c76d 100644 --- a/Intersect (Core)/Memory/MemoryBuffer.cs +++ b/Intersect (Core)/Memory/MemoryBuffer.cs @@ -280,7 +280,7 @@ public bool Read(out string value, Encoding encoding, bool nullTerminated = fals switch (length) { case 0: - value = ""; + value = string.Empty; break; diff --git a/Intersect (Core)/Memory/StreamWrapper.cs b/Intersect (Core)/Memory/StreamWrapper.cs index 017036d52a..a53dcca5a5 100644 --- a/Intersect (Core)/Memory/StreamWrapper.cs +++ b/Intersect (Core)/Memory/StreamWrapper.cs @@ -303,7 +303,7 @@ public bool Read(out string value, Encoding encoding, bool nullTerminated = fals switch (length) { case 0: - value = ""; + value = string.Empty; break; diff --git a/Intersect (Core)/Updater/Updater.cs b/Intersect (Core)/Updater/Updater.cs index fa1e71092e..d481850b66 100644 --- a/Intersect (Core)/Updater/Updater.cs +++ b/Intersect (Core)/Updater/Updater.cs @@ -181,7 +181,7 @@ private async void RunUpdates() } //Otherwise let's compare hashes and potentially add it to the update list - var md5Hash = ""; + var md5Hash = string.Empty; using (var md5 = MD5.Create()) { using (var fs = File.OpenRead(file.Path)) @@ -563,7 +563,7 @@ private void CheckFileData(UpdateFile file, byte[] fileData) } //Check MD5 - var md5Hash = ""; + var md5Hash = string.Empty; using (var md5 = MD5.Create()) { using (var stream = new MemoryStream(fileData)) diff --git a/Intersect.Client.Core/Core/Audio.cs b/Intersect.Client.Core/Core/Audio.cs index 0f6e61f175..f4cb8f16d0 100644 --- a/Intersect.Client.Core/Core/Audio.cs +++ b/Intersect.Client.Core/Core/Audio.cs @@ -11,7 +11,7 @@ namespace Intersect.Client.Core; public static partial class Audio { - private static string sCurrentSong = ""; + private static string sCurrentSong = string.Empty; private static int sFadeRate; @@ -29,7 +29,7 @@ public static partial class Audio private static bool sQueuedLoop; //Music - private static string? sQueuedMusic = ""; + private static string? sQueuedMusic = string.Empty; private static GameAudioInstance? sMyMusic { get; set; } @@ -211,7 +211,7 @@ public static void StopMusic(int fadeout = 0) sMyMusic.State == GameAudioInstance.AudioInstanceState.Paused || sMyMusic.GetVolume() == 0) { - sCurrentSong = ""; + sCurrentSong = string.Empty; sMyMusic.Stop(); sMyMusic.Dispose(); sMyMusic = null; diff --git a/Intersect.Client.Core/Entities/Entity.cs b/Intersect.Client.Core/Entities/Entity.cs index 75788bdb9d..c0b2b99d61 100644 --- a/Intersect.Client.Core/Entities/Entity.cs +++ b/Intersect.Client.Core/Entities/Entity.cs @@ -81,7 +81,7 @@ public Guid[] Equipment public Animation?[] EquipmentAnimations { get; set; } = new Animation[Options.EquipmentSlots.Count]; //Extras - public string Face { get; set; } = ""; + public string Face { get; set; } = string.Empty; public Label FooterLabel { get; set; } = new(string.Empty, Color.White); @@ -126,7 +126,7 @@ public Guid[] Equipment private long mLastUpdate; - protected string mMySprite = ""; + protected string mMySprite = string.Empty; public Color Color { get; set; } = new Color(255, 255, 255, 255); @@ -136,13 +136,13 @@ public Guid[] Equipment protected byte mRenderPriority = 1; - protected string mTransformedSprite = ""; + protected string mTransformedSprite = string.Empty; private long mWalkTimer; public int[] MyEquipment { get; set; } = new int[Options.EquipmentSlots.Count]; - public string Name { get; set; } = ""; + public string Name { get; set; } = string.Empty; public Color? NameColor { get; set; } = null; @@ -1056,11 +1056,11 @@ public virtual void Draw() return; } - var sprite = ""; + var sprite = string.Empty; // Copy the actual render color, because we'll be editing it later and don't want to overwrite it. var renderColor = new Color(Color.A, Color.R, Color.G, Color.B); - string transformedSprite = ""; + string transformedSprite = string.Empty; // Loop through the entity status list. for (var n = 0; n < Status.Count; n++) diff --git a/Intersect.Client.Core/Entities/Status.cs b/Intersect.Client.Core/Entities/Status.cs index 705d7ff263..70c9659c17 100644 --- a/Intersect.Client.Core/Entities/Status.cs +++ b/Intersect.Client.Core/Entities/Status.cs @@ -7,7 +7,7 @@ namespace Intersect.Client.Entities; public partial class Status : IStatus { - public string Data { get; set; } = ""; + public string Data { get; set; } = string.Empty; public long[] Shield { get; set; } = new long[Enum.GetValues().Length]; diff --git a/Intersect.Client.Core/Interface/Game/Bag/BagWindow.cs b/Intersect.Client.Core/Interface/Game/Bag/BagWindow.cs index 5e8173aa18..6b40e8dbc7 100644 --- a/Intersect.Client.Core/Interface/Game/Bag/BagWindow.cs +++ b/Intersect.Client.Core/Interface/Game/Bag/BagWindow.cs @@ -157,7 +157,7 @@ private void InitItemContainer() Items[i].Setup(); mValues.Add(new Label(Items[i].Container, "BagItemValue")); - mValues[i].Text = ""; + mValues[i].Text = string.Empty; Items[i].Container.LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer.GetResolutionString()); var xPadding = Items[i].Container.Margin.Left + Items[i].Container.Margin.Right; diff --git a/Intersect.Client.Core/Interface/Game/Bank/BankWindow.cs b/Intersect.Client.Core/Interface/Game/Bank/BankWindow.cs index 6a62b4f59e..5b3866917c 100644 --- a/Intersect.Client.Core/Interface/Game/Bank/BankWindow.cs +++ b/Intersect.Client.Core/Interface/Game/Bank/BankWindow.cs @@ -197,7 +197,7 @@ private void InitItemContainer() bankItem.Setup(); var bankLabel = new Label(bankItem.Container, "BankItemValue"); - bankLabel.Text = ""; + bankLabel.Text = string.Empty; bankItem.Container.LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer.GetResolutionString()); diff --git a/Intersect.Client.Core/Interface/Game/Character/CharacterWindow.cs b/Intersect.Client.Core/Interface/Game/Character/CharacterWindow.cs index d22d9e9073..efdc92f473 100644 --- a/Intersect.Client.Core/Interface/Game/Character/CharacterWindow.cs +++ b/Intersect.Client.Core/Interface/Game/Character/CharacterWindow.cs @@ -42,12 +42,12 @@ public partial class CharacterWindow private ImagePanel mCharacterPortrait; - private string mCharacterPortraitImg = ""; + private string mCharacterPortraitImg = string.Empty; //Controls private WindowControl mCharacterWindow; - private string mCurrentSprite = ""; + private string mCurrentSprite = string.Empty; Label mDefenseLabel; @@ -126,7 +126,7 @@ public CharacterWindow(Canvas gameCanvas) for (var i = 0; i <= Options.EquipmentSlots.Count; i++) { PaperdollPanels[i] = new ImagePanel(mCharacterContainer); - PaperdollTextures[i] = ""; + PaperdollTextures[i] = string.Empty; PaperdollPanels[i].Hide(); } @@ -244,7 +244,7 @@ public void Update() { for (var z = 0; z < Options.PaperdollOrder[1].Count; z++) { - var paperdoll = ""; + var paperdoll = string.Empty; if (Options.EquipmentSlots.IndexOf(Options.PaperdollOrder[1][z]) > -1) { var equipment = Globals.Me.MyEquipment; @@ -278,7 +278,7 @@ public void Update() { PaperdollPanels[z].Texture = null; PaperdollPanels[z].Hide(); - PaperdollTextures[z] = ""; + PaperdollTextures[z] = string.Empty; } else if (paperdoll != "" && paperdoll != PaperdollTextures[z]) { diff --git a/Intersect.Client.Core/Interface/Game/Chat/Chatbox.cs b/Intersect.Client.Core/Interface/Game/Chat/Chatbox.cs index 53b0ed6e45..7b3c0f7064 100644 --- a/Intersect.Client.Core/Interface/Game/Chat/Chatbox.cs +++ b/Intersect.Client.Core/Interface/Game/Chat/Chatbox.cs @@ -483,7 +483,7 @@ void ChatboxInput_Clicked(Base sender, ClickedEventArgs arguments) { if (mChatboxInput.Text == GetDefaultInputText()) { - mChatboxInput.Text = ""; + mChatboxInput.Text = string.Empty; } } diff --git a/Intersect.Client.Core/Interface/Game/Chat/ChatboxMsg.cs b/Intersect.Client.Core/Interface/Game/Chat/ChatboxMsg.cs index aa73e50454..96c16a6015 100644 --- a/Intersect.Client.Core/Interface/Game/Chat/ChatboxMsg.cs +++ b/Intersect.Client.Core/Interface/Game/Chat/ChatboxMsg.cs @@ -25,11 +25,11 @@ public partial class ChatboxMsg ChatMessageType.Admin } }, }; - private string mMsg = ""; + private string mMsg = string.Empty; private Color mMsgColor; - private string mTarget = ""; + private string mTarget = string.Empty; private ChatMessageType mType; diff --git a/Intersect.Client.Core/Interface/Game/EntityPanel/EntityBox.cs b/Intersect.Client.Core/Interface/Game/EntityPanel/EntityBox.cs index 56fd9f9511..d93448c670 100644 --- a/Intersect.Client.Core/Interface/Game/EntityPanel/EntityBox.cs +++ b/Intersect.Client.Core/Interface/Game/EntityPanel/EntityBox.cs @@ -72,7 +72,7 @@ public partial class EntityBox private Dictionary mActiveStatuses = new Dictionary(); - private string mCurrentSprite = ""; + private string mCurrentSprite = string.Empty; private long mLastUpdateTime; @@ -147,7 +147,7 @@ public EntityBox(Canvas gameCanvas, EntityType entityType, Entity? myEntity, boo else { PaperdollPanels[i] = new ImagePanel(EntityFaceContainer); - PaperdollTextures[i] = ""; + PaperdollTextures[i] = string.Empty; PaperdollPanels[i].Hide(); i++; } diff --git a/Intersect.Client.Core/Interface/Game/EntityPanel/SpellStatus.cs b/Intersect.Client.Core/Interface/Game/EntityPanel/SpellStatus.cs index ff3d2b6224..76c7cb618e 100644 --- a/Intersect.Client.Core/Interface/Game/EntityPanel/SpellStatus.cs +++ b/Intersect.Client.Core/Interface/Game/EntityPanel/SpellStatus.cs @@ -187,7 +187,7 @@ public void Update() _statusIcon.Texture = null; } - _textureLoaded = ""; + _textureLoaded = string.Empty; } } else if (remaining <= 0) @@ -198,7 +198,7 @@ public void Update() } _spellStatusContainer.Hide(); - _textureLoaded = ""; + _textureLoaded = string.Empty; } } } diff --git a/Intersect.Client.Core/Interface/Game/Inventory/InventoryItem.cs b/Intersect.Client.Core/Interface/Game/Inventory/InventoryItem.cs index 3cfe2c07a0..03756630f2 100644 --- a/Intersect.Client.Core/Interface/Game/Inventory/InventoryItem.cs +++ b/Intersect.Client.Core/Interface/Game/Inventory/InventoryItem.cs @@ -58,7 +58,7 @@ public partial class InventoryItem //Slot info private int mMySlot; - private string mTexLoaded = ""; + private string mTexLoaded = string.Empty; public ImagePanel Pnl; @@ -340,7 +340,7 @@ public void Update() Pnl.Texture = null; } - mTexLoaded = ""; + mTexLoaded = string.Empty; } if (mDescWindow != null) diff --git a/Intersect.Client.Core/Interface/Game/Inventory/InventoryWindow.cs b/Intersect.Client.Core/Interface/Game/Inventory/InventoryWindow.cs index 764dd3c097..a1719fc45a 100644 --- a/Intersect.Client.Core/Interface/Game/Inventory/InventoryWindow.cs +++ b/Intersect.Client.Core/Interface/Game/Inventory/InventoryWindow.cs @@ -248,7 +248,7 @@ private void InitItemContainer() Items[i].Setup(); mValues.Add(new Label(Items[i].Container, "InventoryItemValue")); - mValues[i].Text = ""; + mValues[i].Text = string.Empty; Items[i].Container.LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer.GetResolutionString()); diff --git a/Intersect.Client.Core/Interface/Game/MapItem/MapItemWindow.cs b/Intersect.Client.Core/Interface/Game/MapItem/MapItemWindow.cs index dcd58178da..68723546cd 100644 --- a/Intersect.Client.Core/Interface/Game/MapItem/MapItemWindow.cs +++ b/Intersect.Client.Core/Interface/Game/MapItem/MapItemWindow.cs @@ -167,7 +167,7 @@ private void CreateItemContainer() Items[i].Setup(); mValues.Add(new Label(Items[i].Container, "MapItemValue")); - mValues[i].Text = ""; + mValues[i].Text = string.Empty; Items[i].Container.LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer.GetResolutionString()); diff --git a/Intersect.Client.Core/Interface/Game/PartyWindow.cs b/Intersect.Client.Core/Interface/Game/PartyWindow.cs index a581b0bf2d..d306af12ad 100644 --- a/Intersect.Client.Core/Interface/Game/PartyWindow.cs +++ b/Intersect.Client.Core/Interface/Game/PartyWindow.cs @@ -81,7 +81,7 @@ public PartyWindow(Canvas gameCanvas) } else { - mLblnames[i].Text = ""; + mLblnames[i].Text = string.Empty; } //Health bars @@ -173,7 +173,7 @@ public void Update() mHpBarContainer[i].Hide(); mHpLabel[i].Hide(); mHpValue[i].Hide(); - mLblnames[i].Text = ""; + mLblnames[i].Text = string.Empty; mMpBarContainer[i].Hide(); mMpLabel[i].Hide(); mMpValue[i].Hide(); @@ -285,7 +285,7 @@ public void Update() mKickButtons[i].SetToolTipText(""); } - mLblnames[i].Text = ""; + mLblnames[i].Text = string.Empty; mHpBar[i].SetSize(0, mHpBarContainer[i].Height); mHpBarContainer[i].Hide(); } diff --git a/Intersect.Client.Core/Interface/Game/QuestOfferWindow.cs b/Intersect.Client.Core/Interface/Game/QuestOfferWindow.cs index 9546633be6..c0449fe545 100644 --- a/Intersect.Client.Core/Interface/Game/QuestOfferWindow.cs +++ b/Intersect.Client.Core/Interface/Game/QuestOfferWindow.cs @@ -17,7 +17,7 @@ public partial class QuestOfferWindow private Button mDeclineButton; - private string mQuestOfferText = ""; + private string mQuestOfferText = string.Empty; //Controls private WindowControl mQuestOfferWindow; diff --git a/Intersect.Client.Core/Interface/Game/QuestsWindow.cs b/Intersect.Client.Core/Interface/Game/QuestsWindow.cs index f97ecad8bd..f90a4df63a 100644 --- a/Intersect.Client.Core/Interface/Game/QuestsWindow.cs +++ b/Intersect.Client.Core/Interface/Game/QuestsWindow.cs @@ -199,7 +199,7 @@ private void UpdateQuestList() private void AddQuestToDict(Dictionary>> dict, QuestBase quest) { - var category = ""; + var category = string.Empty; var add = false; var color = Color.White; var orderVal = -1; diff --git a/Intersect.Client.Core/Interface/Game/Spells/SpellItem.cs b/Intersect.Client.Core/Interface/Game/Spells/SpellItem.cs index a56507332d..29e54a43e4 100644 --- a/Intersect.Client.Core/Interface/Game/Spells/SpellItem.cs +++ b/Intersect.Client.Core/Interface/Game/Spells/SpellItem.cs @@ -42,7 +42,7 @@ public partial class SpellItem //Drag/Drop References private SpellsWindow mSpellWindow; - private string mTexLoaded = ""; + private string mTexLoaded = string.Empty; private int mYindex; @@ -193,7 +193,7 @@ public void Update() Pnl.Texture = null; } - mTexLoaded = ""; + mTexLoaded = string.Empty; } } @@ -236,7 +236,7 @@ public void Update() Pnl.LocalPosToCanvas(new Point(0, 0)).X + mMouseY, Pnl.Texture, Pnl.RenderColor ); - mTexLoaded = ""; + mTexLoaded = string.Empty; } } } diff --git a/Intersect.Client.Core/Interface/Game/Trades/TradeSegment.cs b/Intersect.Client.Core/Interface/Game/Trades/TradeSegment.cs index 9cd7b394f8..708fd54311 100644 --- a/Intersect.Client.Core/Interface/Game/Trades/TradeSegment.cs +++ b/Intersect.Client.Core/Interface/Game/Trades/TradeSegment.cs @@ -62,7 +62,7 @@ public void InitItemContainer(int index) Items[i].Setup(); Values.Add(new Label(Items[i].Container, "TradeValue")); - Values[i].Text = ""; + Values[i].Text = string.Empty; Items[i].Container.LoadJsonUi(GameContentManager.UI.InGame, Graphics.Renderer.GetResolutionString()); diff --git a/Intersect.Client.Core/Interface/Menu/ForgotPasswordWindow.cs b/Intersect.Client.Core/Interface/Menu/ForgotPasswordWindow.cs index c4424eb27f..347e522a86 100644 --- a/Intersect.Client.Core/Interface/Menu/ForgotPasswordWindow.cs +++ b/Intersect.Client.Core/Interface/Menu/ForgotPasswordWindow.cs @@ -114,7 +114,7 @@ public void Hide() public void Show() { mResetWindow.IsHidden = false; - mInputTextbox.Text = ""; + mInputTextbox.Text = string.Empty; } void BackBtn_Clicked(Base sender, ClickedEventArgs arguments) diff --git a/Intersect.Client.Core/Interface/Menu/ResetPasswordWindow.cs b/Intersect.Client.Core/Interface/Menu/ResetPasswordWindow.cs index fb16ea30a1..41dedfab49 100644 --- a/Intersect.Client.Core/Interface/Menu/ResetPasswordWindow.cs +++ b/Intersect.Client.Core/Interface/Menu/ResetPasswordWindow.cs @@ -124,7 +124,7 @@ public ResetPasswordWindow(Canvas parent, MainMenu mainMenu) public bool IsHidden => mResetWindow.IsHidden; //The username or email of the acc we are resetting the pass for - public string Target { set; get; } = ""; + public string Target { set; get; } = string.Empty; private void Textbox_Clicked(Base sender, ClickedEventArgs arguments) { @@ -152,9 +152,9 @@ public void Hide() public void Show() { mResetWindow.IsHidden = false; - mCodeInputTextbox.Text = ""; - mPasswordTextbox.Text = ""; - mPasswordTextbox2.Text = ""; + mCodeInputTextbox.Text = string.Empty; + mPasswordTextbox.Text = string.Empty; + mPasswordTextbox2.Text = string.Empty; } void BackBtn_Clicked(Base sender, ClickedEventArgs arguments) diff --git a/Intersect.Client.Core/Interface/Shared/InputBox.cs b/Intersect.Client.Core/Interface/Shared/InputBox.cs index 948d31e0d3..ac75f4953f 100644 --- a/Intersect.Client.Core/Interface/Shared/InputBox.cs +++ b/Intersect.Client.Core/Interface/Shared/InputBox.cs @@ -48,7 +48,7 @@ public enum InputType private readonly Button _btnOk; private readonly Label _promptLabel; - private readonly string _prompt = ""; + private readonly string _prompt = string.Empty; private bool _initialized = false; public InputBox( diff --git a/Intersect.Client.Core/Localization/Strings.cs b/Intersect.Client.Core/Localization/Strings.cs index 2dffe7b85f..eac7c18ee9 100644 --- a/Intersect.Client.Core/Localization/Strings.cs +++ b/Intersect.Client.Core/Localization/Strings.cs @@ -24,7 +24,7 @@ public static string FormatQuantityAbbreviated(long value) else { double returnVal = 0; - var postfix = ""; + var postfix = string.Empty; // hundreds if (value <= 999) diff --git a/Intersect.Client.Core/MonoGame/IntersectGame.cs b/Intersect.Client.Core/MonoGame/IntersectGame.cs index ffbc5282c9..f66d09de53 100644 --- a/Intersect.Client.Core/MonoGame/IntersectGame.cs +++ b/Intersect.Client.Core/MonoGame/IntersectGame.cs @@ -97,7 +97,7 @@ private IntersectGame(IClientContext context, Action postStartupAction) ClientConfiguration.LoadAndSave(ClientConfiguration.DefaultPath); - Content.RootDirectory = ""; + Content.RootDirectory = string.Empty; IsMouseVisible = true; Globals.ContentManager = new MonoContentManager(Log.Default); Globals.Database = new JsonDatabase(Log.Default); @@ -417,11 +417,11 @@ private void DrawUpdater() ); } - var status = ""; + var status = string.Empty; var progressPercent = 0f; - var progress = ""; - var filesRemaining = ""; - var sizeRemaining = ""; + var progress = string.Empty; + var filesRemaining = string.Empty; + var sizeRemaining = string.Empty; switch (mUpdater.Status) { diff --git a/Intersect.Client.Core/Networking/PacketHandler.cs b/Intersect.Client.Core/Networking/PacketHandler.cs index 1f93f4d2d9..952ba7c5c5 100644 --- a/Intersect.Client.Core/Networking/PacketHandler.cs +++ b/Intersect.Client.Core/Networking/PacketHandler.cs @@ -1684,7 +1684,7 @@ public void HandlePacket(IPacketSender packetSender, GameObjectPacket packet) var id = packet.Id; var another = packet.AnotherFollowing; var deleted = packet.Deleted; - var json = ""; + var json = string.Empty; if (!deleted) { json = packet.Data; diff --git a/Intersect.Client.Framework/Gwen/Control/Button.cs b/Intersect.Client.Framework/Gwen/Control/Button.cs index 15d074016d..c589c97d45 100644 --- a/Intersect.Client.Framework/Gwen/Control/Button.cs +++ b/Intersect.Client.Framework/Gwen/Control/Button.cs @@ -287,10 +287,10 @@ public void PlayClickSound() public void ClearSounds() { - mMouseUpSound = ""; - mMouseDownSound = ""; - mHoverSound = ""; - mClickSound = ""; + mMouseUpSound = string.Empty; + mMouseDownSound = string.Empty; + mHoverSound = string.Empty; + mClickSound = string.Empty; } /// diff --git a/Intersect.Client.Framework/Gwen/Control/MultilineTextBox.cs b/Intersect.Client.Framework/Gwen/Control/MultilineTextBox.cs index 6ee3048633..f3c98e5e64 100644 --- a/Intersect.Client.Framework/Gwen/Control/MultilineTextBox.cs +++ b/Intersect.Client.Framework/Gwen/Control/MultilineTextBox.cs @@ -190,7 +190,7 @@ public override string Text { get { - var ret = ""; + var ret = string.Empty; for (var i = 0; i < TotalLines; i++) { ret += mTextLines[i]; @@ -1142,7 +1142,7 @@ private Point GetCharacterPosition(Point cursorPosition) var currLine = mTextLines[cursorPosition.Y] .Substring(0, Math.Min(cursorPosition.X, mTextLines[cursorPosition.Y].Length)); - var sub = ""; + var sub = string.Empty; for (var i = 0; i < cursorPosition.Y; i++) { sub += mTextLines[i] + "\n"; diff --git a/Intersect.Client.Framework/Gwen/ControlInternal/Dragger.cs b/Intersect.Client.Framework/Gwen/ControlInternal/Dragger.cs index ef945afac3..70585e3792 100644 --- a/Intersect.Client.Framework/Gwen/ControlInternal/Dragger.cs +++ b/Intersect.Client.Framework/Gwen/ControlInternal/Dragger.cs @@ -315,9 +315,9 @@ protected override void OnMouseEntered() public void ClearSounds() { - mHoverSound = ""; - mMouseDownSound = ""; - mMouseUpSound = ""; + mHoverSound = string.Empty; + mMouseDownSound = string.Empty; + mMouseUpSound = string.Empty; } } diff --git a/Intersect.Client.Framework/Gwen/Input/IntersectInput.cs b/Intersect.Client.Framework/Gwen/Input/IntersectInput.cs index 2a32b794a0..7deb191d35 100644 --- a/Intersect.Client.Framework/Gwen/Input/IntersectInput.cs +++ b/Intersect.Client.Framework/Gwen/Input/IntersectInput.cs @@ -158,7 +158,7 @@ public partial class GwenInputMessage public IntersectInput.InputEvent Type; - public string Unicode = ""; + public string Unicode = string.Empty; public GwenInputMessage( IntersectInput.InputEvent type, diff --git a/Intersect.Editor/Content/ContentManager.cs b/Intersect.Editor/Content/ContentManager.cs index 49e2339e25..0a1d4bbac0 100644 --- a/Intersect.Editor/Content/ContentManager.cs +++ b/Intersect.Editor/Content/ContentManager.cs @@ -57,7 +57,7 @@ public enum TextureType static IDictionary sEntityDict = new Dictionary(); - private static string sErrorString = ""; + private static string sErrorString = string.Empty; static IDictionary sFaceDict = new Dictionary(); diff --git a/Intersect.Editor/Forms/Editors/Events/CommandPrinter.cs b/Intersect.Editor/Forms/Editors/Events/CommandPrinter.cs index 7cbd7133cf..d5ec2350bb 100644 --- a/Intersect.Editor/Forms/Editors/Events/CommandPrinter.cs +++ b/Intersect.Editor/Forms/Editors/Events/CommandPrinter.cs @@ -689,7 +689,7 @@ private static string GetCommandText(InputVariableCommand command, MapInstance m private static string GetCommandText(AddChatboxTextCommand command, MapInstance map) { - var channel = ""; + var channel = string.Empty; switch (command.Channel) { case ChatboxChannel.Player: @@ -716,7 +716,7 @@ private static string GetCommandText(SetVariableCommand command, MapInstance map private static string GetCommandText(SetSelfSwitchCommand command, MapInstance map) { - var selfvalue = ""; + var selfvalue = string.Empty; selfvalue = Strings.EventCommandList.False; if (command.Value) { @@ -1114,7 +1114,7 @@ private static string GetCommandText(PlayAnimationCommand command, MapInstance m } else { - var spawnOpt = ""; + var spawnOpt = string.Empty; switch (command.Dir) { //0 does not adhere to direction, 1 is Spawning Relative to Direction, 2 is Rotating Relative to Direction, and 3 is both. @@ -1335,7 +1335,7 @@ private static string GetVariableModText(SetVariableCommand command, VariableMod private static string GetVariableModText(SetVariableCommand command, BooleanVariableMod mod) { - var varvalue = ""; + var varvalue = string.Empty; if (mod.DuplicateVariableId != Guid.Empty) { if (mod.DupVariableType == VariableType.PlayerVariable) @@ -1411,7 +1411,7 @@ private static string GetVariableModText(SetVariableCommand command, BooleanVari private static string GetVariableModText(SetVariableCommand command, IntegerVariableMod mod) { - var varvalue = ""; + var varvalue = string.Empty; switch (mod.ModType) { case VariableModType.Set: @@ -1674,7 +1674,7 @@ private static string GetVariableModText(SetVariableCommand command, IntegerVari private static string GetVariableModText(SetVariableCommand command, StringVariableMod mod) { - var varvalue = ""; + var varvalue = string.Empty; switch (mod.ModType) { case VariableModType.Set: diff --git a/Intersect.Editor/Forms/Editors/Events/Event Commands/EventCommand_ChangeItems.cs b/Intersect.Editor/Forms/Editors/Events/Event Commands/EventCommand_ChangeItems.cs index 8b5616543d..75df5717f7 100644 --- a/Intersect.Editor/Forms/Editors/Events/Event Commands/EventCommand_ChangeItems.cs +++ b/Intersect.Editor/Forms/Editors/Events/Event Commands/EventCommand_ChangeItems.cs @@ -142,7 +142,7 @@ private void VariableBlank() else { cmbVariable.SelectedIndex = -1; - cmbVariable.Text = ""; + cmbVariable.Text = string.Empty; } } diff --git a/Intersect.Editor/Forms/Editors/Events/Event Commands/EventCommand_ConditionalBranch.cs b/Intersect.Editor/Forms/Editors/Events/Event Commands/EventCommand_ConditionalBranch.cs index 18b7c59e35..4e7704c842 100644 --- a/Intersect.Editor/Forms/Editors/Events/Event Commands/EventCommand_ConditionalBranch.cs +++ b/Intersect.Editor/Forms/Editors/Events/Event Commands/EventCommand_ConditionalBranch.cs @@ -1225,7 +1225,7 @@ private void VariableBlank() else { cmbInvVariable.SelectedIndex = -1; - cmbInvVariable.Text = ""; + cmbInvVariable.Text = string.Empty; } } diff --git a/Intersect.Editor/Forms/Editors/Events/Event Commands/EventCommand_GiveExperience.cs b/Intersect.Editor/Forms/Editors/Events/Event Commands/EventCommand_GiveExperience.cs index 7f835280bd..6f42db8bd2 100644 --- a/Intersect.Editor/Forms/Editors/Events/Event Commands/EventCommand_GiveExperience.cs +++ b/Intersect.Editor/Forms/Editors/Events/Event Commands/EventCommand_GiveExperience.cs @@ -110,7 +110,7 @@ private void VariableBlank() else { cmbVariable.SelectedIndex = -1; - cmbVariable.Text = ""; + cmbVariable.Text = string.Empty; } } diff --git a/Intersect.Editor/Forms/Editors/Events/Event Commands/EventCommand_SetGuildBankSlots.cs b/Intersect.Editor/Forms/Editors/Events/Event Commands/EventCommand_SetGuildBankSlots.cs index 8f262a3d53..e24e3113ec 100644 --- a/Intersect.Editor/Forms/Editors/Events/Event Commands/EventCommand_SetGuildBankSlots.cs +++ b/Intersect.Editor/Forms/Editors/Events/Event Commands/EventCommand_SetGuildBankSlots.cs @@ -78,7 +78,7 @@ private void VariableBlank() else { cmbVariable.SelectedIndex = -1; - cmbVariable.Text = ""; + cmbVariable.Text = string.Empty; } } diff --git a/Intersect.Editor/Forms/Editors/Events/Event Commands/Event_GraphicSelector.cs b/Intersect.Editor/Forms/Editors/Events/Event Commands/Event_GraphicSelector.cs index faee5b2db1..378926531b 100644 --- a/Intersect.Editor/Forms/Editors/Events/Event Commands/Event_GraphicSelector.cs +++ b/Intersect.Editor/Forms/Editors/Events/Event Commands/Event_GraphicSelector.cs @@ -70,7 +70,7 @@ private void InitLocalization() private void GraphicTypeUpdated() { - mTmpGraphic.Filename = ""; + mTmpGraphic.Filename = string.Empty; mTmpGraphic.Type = EventGraphicType.None; mTmpGraphic.X = 0; mTmpGraphic.Y = 0; diff --git a/Intersect.Editor/Forms/Editors/Quest/frmQuest.cs b/Intersect.Editor/Forms/Editors/Quest/frmQuest.cs index 5a3271f3b6..674c8edf3f 100644 --- a/Intersect.Editor/Forms/Editors/Quest/frmQuest.cs +++ b/Intersect.Editor/Forms/Editors/Quest/frmQuest.cs @@ -611,7 +611,7 @@ public void InitEditor() private void btnAddFolder_Click(object sender, EventArgs e) { - var folderName = ""; + var folderName = string.Empty; var result = DarkInputBox.ShowInformation( Strings.QuestEditor.folderprompt, Strings.QuestEditor.foldertitle, ref folderName, DarkDialogButton.OkCancel diff --git a/Intersect.Editor/Forms/Editors/frmAnimation.cs b/Intersect.Editor/Forms/Editors/frmAnimation.cs index 0ca6285b1a..daf85d84cc 100644 --- a/Intersect.Editor/Forms/Editors/frmAnimation.cs +++ b/Intersect.Editor/Forms/Editors/frmAnimation.cs @@ -849,7 +849,7 @@ public void InitEditor() private void btnAddFolder_Click(object sender, EventArgs e) { - var folderName = ""; + var folderName = string.Empty; var result = DarkInputBox.ShowInformation( Strings.AnimationEditor.folderprompt, Strings.AnimationEditor.foldertitle, ref folderName, DarkDialogButton.OkCancel diff --git a/Intersect.Editor/Forms/Editors/frmClass.cs b/Intersect.Editor/Forms/Editors/frmClass.cs index 6efec03596..d0405f121b 100644 --- a/Intersect.Editor/Forms/Editors/frmClass.cs +++ b/Intersect.Editor/Forms/Editors/frmClass.cs @@ -1465,7 +1465,7 @@ private void expGrid_KeyDown(object sender, KeyEventArgs e) private void btnAddFolder_Click(object sender, EventArgs e) { - var folderName = ""; + var folderName = string.Empty; var result = DarkInputBox.ShowInformation( Strings.ClassEditor.folderprompt, Strings.ClassEditor.foldertitle, ref folderName, DarkDialogButton.OkCancel diff --git a/Intersect.Editor/Forms/Editors/frmCommonEvent.cs b/Intersect.Editor/Forms/Editors/frmCommonEvent.cs index 7518dfa846..1c76dc57f5 100644 --- a/Intersect.Editor/Forms/Editors/frmCommonEvent.cs +++ b/Intersect.Editor/Forms/Editors/frmCommonEvent.cs @@ -151,7 +151,7 @@ private EventBase GetSelectedEvent() private void btnAddFolder_Click(object sender, EventArgs e) { - var folderName = ""; + var folderName = string.Empty; var result = DarkInputBox.ShowInformation( Strings.CommonEventEditor.folderprompt, Strings.CommonEventEditor.foldertitle, ref folderName, DarkDialogButton.OkCancel diff --git a/Intersect.Editor/Forms/Editors/frmCraftingTables.cs b/Intersect.Editor/Forms/Editors/frmCraftingTables.cs index 731d2a0ac0..3e8f2a7da6 100644 --- a/Intersect.Editor/Forms/Editors/frmCraftingTables.cs +++ b/Intersect.Editor/Forms/Editors/frmCraftingTables.cs @@ -309,7 +309,7 @@ public void InitEditor() private void btnAddFolder_Click(object sender, EventArgs e) { - var folderName = ""; + var folderName = string.Empty; var result = DarkInputBox.ShowInformation( Strings.CraftingTableEditor.folderprompt, Strings.CraftingTableEditor.foldertitle, ref folderName, DarkDialogButton.OkCancel diff --git a/Intersect.Editor/Forms/Editors/frmCrafts.cs b/Intersect.Editor/Forms/Editors/frmCrafts.cs index 15b6fbf231..63833e4806 100644 --- a/Intersect.Editor/Forms/Editors/frmCrafts.cs +++ b/Intersect.Editor/Forms/Editors/frmCrafts.cs @@ -509,7 +509,7 @@ public void InitEditor() private void btnAddFolder_Click(object sender, EventArgs e) { - var folderName = ""; + var folderName = string.Empty; var result = DarkInputBox.ShowInformation( Strings.CraftsEditor.folderprompt, Strings.CraftsEditor.foldertitle, ref folderName, DarkDialogButton.OkCancel diff --git a/Intersect.Editor/Forms/Editors/frmItem.cs b/Intersect.Editor/Forms/Editors/frmItem.cs index 711f2eeef1..0558e99a9e 100644 --- a/Intersect.Editor/Forms/Editors/frmItem.cs +++ b/Intersect.Editor/Forms/Editors/frmItem.cs @@ -1053,7 +1053,7 @@ private void cmbCooldownGroup_SelectedIndexChanged(object sender, EventArgs e) private void btnAddCooldownGroup_Click(object sender, EventArgs e) { - var cdGroupName = ""; + var cdGroupName = string.Empty; var result = DarkInputBox.ShowInformation( Strings.ItemEditor.CooldownGroupPrompt, Strings.ItemEditor.CooldownGroupTitle, ref cdGroupName, DarkDialogButton.OkCancel @@ -1295,7 +1295,7 @@ public void InitEditor() private void btnAddFolder_Click(object sender, EventArgs e) { - var folderName = ""; + var folderName = string.Empty; var result = DarkInputBox.ShowInformation( Strings.ItemEditor.folderprompt, Strings.ItemEditor.foldertitle, ref folderName, DarkDialogButton.OkCancel diff --git a/Intersect.Editor/Forms/Editors/frmNpc.cs b/Intersect.Editor/Forms/Editors/frmNpc.cs index e9ec773a94..01888a029f 100644 --- a/Intersect.Editor/Forms/Editors/frmNpc.cs +++ b/Intersect.Editor/Forms/Editors/frmNpc.cs @@ -298,8 +298,8 @@ private void UpdateEditor() nudDef.Value = mEditorItem.Stats[(int)Stat.Defense]; nudMR.Value = mEditorItem.Stats[(int)Stat.MagicResist]; nudSpd.Value = mEditorItem.Stats[(int)Stat.Speed]; - nudHp.Value = mEditorItem.MaxVital[(int)Vital.Health]; - nudMana.Value = mEditorItem.MaxVital[(int)Vital.Mana]; + nudHp.Value = mEditorItem.MaxVitals[(int)Vital.Health]; + nudMana.Value = mEditorItem.MaxVitals[(int)Vital.Mana]; nudExp.Value = mEditorItem.Experience; chkAttackAllies.Checked = mEditorItem.AttackAllies; chkEnabled.Checked = mEditorItem.NpcVsNpcEnabled; @@ -713,12 +713,12 @@ private void nudCritChance_ValueChanged(object sender, EventArgs e) private void nudHp_ValueChanged(object sender, EventArgs e) { - mEditorItem.MaxVital[(int)Vital.Health] = (int)nudHp.Value; + mEditorItem.MaxVitals[(int)Vital.Health] = (int)nudHp.Value; } private void nudMana_ValueChanged(object sender, EventArgs e) { - mEditorItem.MaxVital[(int)Vital.Mana] = (int)nudMana.Value; + mEditorItem.MaxVitals[(int)Vital.Mana] = (int)nudMana.Value; } private void nudExp_ValueChanged(object sender, EventArgs e) @@ -978,7 +978,7 @@ public void InitEditor() private void btnAddFolder_Click(object sender, EventArgs e) { - var folderName = ""; + var folderName = string.Empty; var result = DarkInputBox.ShowInformation( Strings.NpcEditor.folderprompt, Strings.NpcEditor.foldertitle, ref folderName, DarkDialogButton.OkCancel ); diff --git a/Intersect.Editor/Forms/Editors/frmProjectile.cs b/Intersect.Editor/Forms/Editors/frmProjectile.cs index 2409ead000..7c2b68edf0 100644 --- a/Intersect.Editor/Forms/Editors/frmProjectile.cs +++ b/Intersect.Editor/Forms/Editors/frmProjectile.cs @@ -746,7 +746,7 @@ public void InitEditor() private void btnAddFolder_Click(object sender, EventArgs e) { - var folderName = ""; + var folderName = string.Empty; var result = DarkInputBox.ShowInformation( Strings.ProjectileEditor.folderprompt, Strings.ProjectileEditor.foldertitle, ref folderName, DarkDialogButton.OkCancel diff --git a/Intersect.Editor/Forms/Editors/frmResource.cs b/Intersect.Editor/Forms/Editors/frmResource.cs index 50631b1b77..739cc70ebb 100644 --- a/Intersect.Editor/Forms/Editors/frmResource.cs +++ b/Intersect.Editor/Forms/Editors/frmResource.cs @@ -906,7 +906,7 @@ public void InitEditor() private void btnAddFolder_Click(object sender, EventArgs e) { - var folderName = ""; + var folderName = string.Empty; var result = DarkInputBox.ShowInformation( Strings.ResourceEditor.folderprompt, Strings.ResourceEditor.foldertitle, ref folderName, DarkDialogButton.OkCancel diff --git a/Intersect.Editor/Forms/Editors/frmShop.cs b/Intersect.Editor/Forms/Editors/frmShop.cs index c6da2c311f..31a4e7acdb 100644 --- a/Intersect.Editor/Forms/Editors/frmShop.cs +++ b/Intersect.Editor/Forms/Editors/frmShop.cs @@ -493,7 +493,7 @@ public void InitEditor() private void btnAddFolder_Click(object sender, EventArgs e) { - var folderName = ""; + var folderName = string.Empty; var result = DarkInputBox.ShowInformation( Strings.ShopEditor.folderprompt, Strings.ShopEditor.foldertitle, ref folderName, DarkDialogButton.OkCancel diff --git a/Intersect.Editor/Forms/Editors/frmSpell.cs b/Intersect.Editor/Forms/Editors/frmSpell.cs index f8b24d10ef..e9568008c4 100644 --- a/Intersect.Editor/Forms/Editors/frmSpell.cs +++ b/Intersect.Editor/Forms/Editors/frmSpell.cs @@ -926,7 +926,7 @@ private void chkBound_CheckedChanged(object sender, EventArgs e) private void btnAddCooldownGroup_Click(object sender, EventArgs e) { - var cdGroupName = ""; + var cdGroupName = string.Empty; var result = DarkInputBox.ShowInformation( Strings.SpellEditor.CooldownGroupPrompt, Strings.SpellEditor.CooldownGroupTitle, ref cdGroupName, DarkDialogButton.OkCancel @@ -1020,7 +1020,7 @@ public void InitEditor() private void btnAddFolder_Click(object sender, EventArgs e) { - var folderName = ""; + var folderName = string.Empty; var result = DarkInputBox.ShowInformation( Strings.SpellEditor.folderprompt, Strings.SpellEditor.foldertitle, ref folderName, DarkDialogButton.OkCancel diff --git a/Intersect.Editor/Forms/Editors/frmVariable.cs b/Intersect.Editor/Forms/Editors/frmVariable.cs index 6aff7eeae2..9654211a54 100644 --- a/Intersect.Editor/Forms/Editors/frmVariable.cs +++ b/Intersect.Editor/Forms/Editors/frmVariable.cs @@ -672,7 +672,7 @@ public void InitEditor() private void btnAddFolder_Click(object sender, EventArgs e) { - var folderName = ""; + var folderName = string.Empty; var result = DarkInputBox.ShowInformation( Strings.VariableEditor.folderprompt, Strings.VariableEditor.foldertitle, ref folderName, DarkDialogButton.OkCancel diff --git a/Intersect.Editor/Forms/frmLogin.cs b/Intersect.Editor/Forms/frmLogin.cs index 23dc4de450..d03f95dd1c 100644 --- a/Intersect.Editor/Forms/frmLogin.cs +++ b/Intersect.Editor/Forms/frmLogin.cs @@ -25,7 +25,7 @@ public partial class FrmLogin : Form private bool mOptionsLoaded = false; - private string mSavedPassword = ""; + private string mSavedPassword = string.Empty; public FrmLogin() { @@ -212,8 +212,8 @@ private void txtPassword_KeyDown(object sender, KeyEventArgs e) if (mSavedPassword != "") { - mSavedPassword = ""; - txtPassword.Text = ""; + mSavedPassword = string.Empty; + txtPassword.Text = string.Empty; chkRemember.Checked = false; } } @@ -227,9 +227,9 @@ private void txtUsername_KeyDown(object sender, KeyEventArgs e) if (mSavedPassword != "") { - mSavedPassword = ""; - txtUsername.Text = ""; - txtPassword.Text = ""; + mSavedPassword = string.Empty; + txtUsername.Text = string.Empty; + txtPassword.Text = string.Empty; chkRemember.Checked = false; } } diff --git a/Intersect.Editor/Localization/Strings.cs b/Intersect.Editor/Localization/Strings.cs index af2765dcc5..e10b17a800 100644 --- a/Intersect.Editor/Localization/Strings.cs +++ b/Intersect.Editor/Localization/Strings.cs @@ -137,7 +137,7 @@ public static string GetEventConditionalDesc(KnowsSpellCondition condition) public static string GetEventConditionalDesc(LevelOrStatCondition condition) { - var pLvl = ""; + var pLvl = string.Empty; switch (condition.Comparator) { case VariableComparator.Equal: @@ -166,7 +166,7 @@ public static string GetEventConditionalDesc(LevelOrStatCondition condition) break; } - var lvlorstat = ""; + var lvlorstat = string.Empty; if (condition.ComparingLevel) { lvlorstat = EventConditionDesc.level; @@ -216,8 +216,8 @@ public static string GetEventConditionalDesc(TimeBetweenCondition condition) timeRanges.Add(addRange); } - var time1 = ""; - var time2 = ""; + var time1 = string.Empty; + var time2 = string.Empty; if (condition.Ranges[0] > -1 && condition.Ranges[0] < timeRanges.Count) { time1 = timeRanges[condition.Ranges[0]]; @@ -366,8 +366,8 @@ public static string GetVariableComparisonString(VariableComparison comparison) public static string GetVariableComparisonString(BooleanVariableComparison comparison) { - var value = ""; - var pVar = ""; + var value = string.Empty; + var pVar = string.Empty; if (comparison.CompareVariableId == Guid.Empty) { @@ -409,8 +409,8 @@ public static string GetVariableComparisonString(BooleanVariableComparison compa public static string GetVariableComparisonString(IntegerVariableComparison comparison) { - var value = ""; - var pVar = ""; + var value = string.Empty; + var pVar = string.Empty; if (comparison.CompareVariableId == Guid.Empty) { diff --git a/Intersect.Editor/Networking/PacketHandler.cs b/Intersect.Editor/Networking/PacketHandler.cs index 4166e0d457..cf20a7676d 100644 --- a/Intersect.Editor/Networking/PacketHandler.cs +++ b/Intersect.Editor/Networking/PacketHandler.cs @@ -389,7 +389,7 @@ public void HandlePacket(IPacketSender packetSender, GameObjectPacket packet) { var id = packet.Id; var deleted = packet.Deleted; - var json = ""; + var json = string.Empty; if (!packet.Deleted) { json = packet.Data; diff --git a/Intersect.Server.Core/Collections/Indexing/LookupKey.cs b/Intersect.Server.Core/Collections/Indexing/LookupKey.cs index 7f1a41a8ec..e81a94a398 100644 --- a/Intersect.Server.Core/Collections/Indexing/LookupKey.cs +++ b/Intersect.Server.Core/Collections/Indexing/LookupKey.cs @@ -1,10 +1,16 @@ using System.ComponentModel; using System.Globalization; +using Intersect.Server.Localization; +using Intersect.Utilities; namespace Intersect.Server.Web.RestApi.Payloads; - -[TypeConverter(typeof(Converter))] +// TODO: Figure out how to get LookupKey to show up in swagger.json components/schemas despite being a "string", one or more of the following commented out attributes may help +// [SwaggerSubType(typeof(Guid))] +// [SwaggerSubType(typeof(string))] +// [KnownType(typeof(LookupKey))] +// [SwaggerSchema] +// [TypeConverter(typeof(Converter))] public partial struct LookupKey { @@ -27,6 +33,36 @@ public override string ToString() return HasId ? Id.ToString() : Name; } + public static bool TryParse(string input, out LookupKey lookupKey) + { + if (Guid.TryParse(input, out var guid)) + { + lookupKey = new LookupKey + { + Id = guid, + }; + return true; + } + + if (string.IsNullOrWhiteSpace(input)) + { + lookupKey = default; + return false; + } + + if (!FieldChecking.IsValidUsername(input, Strings.Regex.Username)) + { + lookupKey = default; + return false; + } + + lookupKey = new LookupKey + { + Name = input, + }; + return true; + } + public partial class Converter : TypeConverter { @@ -41,13 +77,13 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c { return new LookupKey { - Id = guid + Id = guid, }; } return new LookupKey { - Name = value as string + Name = value as string, }; } } diff --git a/Intersect.Server.Core/Collections/Sorting/Sort.cs b/Intersect.Server.Core/Collections/Sorting/Sort.cs index fd6856e9d0..a4149316d1 100644 --- a/Intersect.Server.Core/Collections/Sorting/Sort.cs +++ b/Intersect.Server.Core/Collections/Sorting/Sort.cs @@ -4,7 +4,7 @@ public partial struct Sort { - public string By { get; set; } + public string[] By { get; set; } public SortDirection Direction { get; set; } @@ -12,7 +12,7 @@ public static Sort From(string sortBy, SortDirection sortDirection) { return new Sort { - By = sortBy, + By = [sortBy], Direction = sortDirection }; } diff --git a/Intersect.Server.Core/Database/DbInterface.cs b/Intersect.Server.Core/Database/DbInterface.cs index 8c8493f57e..d0079606da 100644 --- a/Intersect.Server.Core/Database/DbInterface.cs +++ b/Intersect.Server.Core/Database/DbInterface.cs @@ -2158,7 +2158,7 @@ private static void MigrateDbSet(DbSet oldDbSet, DbSet newDbSet) where //https://stackoverflow.com/questions/3404421/password-masking-console-application public static string GetPassword() { - var pwd = ""; + var pwd = string.Empty; while (true) { var i = Console.ReadKey(true); diff --git a/Intersect.Server.Core/Database/GameData/Migrations/Beta6Migration.cs b/Intersect.Server.Core/Database/GameData/Migrations/Beta6Migration.cs index 538f1ad257..ddbaa0d5b7 100644 --- a/Intersect.Server.Core/Database/GameData/Migrations/Beta6Migration.cs +++ b/Intersect.Server.Core/Database/GameData/Migrations/Beta6Migration.cs @@ -82,7 +82,7 @@ private static void FixVariableCommandsAndConditions(GameContext context, string { var i = 0; var currentCount = 0; - updateCmd.CommandText = ""; + updateCmd.CommandText = string.Empty; updateCmd.Transaction = trans; foreach (var update in updates) { @@ -106,7 +106,7 @@ private static void FixVariableCommandsAndConditions(GameContext context, string if (currentCount > 256) { updateCmd.ExecuteNonQuery(); - updateCmd.CommandText = ""; + updateCmd.CommandText = string.Empty; updateCmd.Parameters.Clear(); currentCount = 0; } @@ -593,7 +593,7 @@ private static void RemoveByteBufferUsageFromMaps(GameContext context) using (var updateCmd = connection.CreateCommand()) { - updateCmd.CommandText = ""; + updateCmd.CommandText = string.Empty; updateCmd.Transaction = trans; var i = 0; var currentCount = 0; @@ -626,7 +626,7 @@ private static void RemoveByteBufferUsageFromMaps(GameContext context) if (currentCount > 256) { updateCmd.ExecuteNonQuery(); - updateCmd.CommandText = ""; + updateCmd.CommandText = string.Empty; updateCmd.Parameters.Clear(); currentCount = 0; } diff --git a/Intersect.Server.Core/Database/GameData/Migrations/CerasVersionToleranceMigration.cs b/Intersect.Server.Core/Database/GameData/Migrations/CerasVersionToleranceMigration.cs index c9c1831b1f..39e2b3a86c 100644 --- a/Intersect.Server.Core/Database/GameData/Migrations/CerasVersionToleranceMigration.cs +++ b/Intersect.Server.Core/Database/GameData/Migrations/CerasVersionToleranceMigration.cs @@ -153,7 +153,7 @@ private static void UpdateMapTilesAttributesWithCerasVersionTolerance(GameContex if (currentCount > 256) { updateCmd.ExecuteNonQuery(); - updateCmd.CommandText = ""; + updateCmd.CommandText = string.Empty; updateCmd.Parameters.Clear(); currentCount = 0; } diff --git a/Intersect.Server.Core/Database/Item.cs b/Intersect.Server.Core/Database/Item.cs index daabf3a830..f3bf08b95b 100644 --- a/Intersect.Server.Core/Database/Item.cs +++ b/Intersect.Server.Core/Database/Item.cs @@ -82,7 +82,7 @@ public Item(Item item) : this(item.ItemId, item.Quantity, item.BagId, item.Bag) [NotMapped] public ItemProperties Properties { get; set; } - [Column("ItemProperties")] + [Column(nameof(ItemProperties))] [JsonIgnore] public string ItemPropertiesJson { @@ -91,7 +91,7 @@ public string ItemPropertiesJson Properties = JsonConvert.DeserializeObject(value ?? string.Empty) ?? new ItemProperties(); } - [JsonIgnore][NotMapped] public ItemBase Descriptor => ItemBase.Get(ItemId); + [JsonIgnore, NotMapped] public ItemBase Descriptor => ItemBase.Get(ItemId); public static Item None => new(); @@ -397,23 +397,29 @@ public bool TryGetBag(out Bag bag) var descriptor = Descriptor; if (descriptor?.ItemType == ItemType.Bag) { - bag = Bag.GetBag(BagId ?? Guid.Empty); - bag?.ValidateSlots(); + if (!Bag.TryGetBag(BagId ?? default, out bag)) + { + return false; + } + Bag = bag; + return true; } + + return false; } - else + + // Remove any items from this bag that have been removed from the game + foreach (var slot in bag.Slots) { - // Remove any items from this bag that have been removed from the game - foreach (var slot in bag.Slots) + if (ItemBase.TryGet(slot.ItemId, out _)) { - if (ItemBase.Get(slot.ItemId) == default) - { - slot.Set(None); - } + continue; } + + slot.Set(None); } - return default != bag; + return true; } } \ No newline at end of file diff --git a/Intersect.Server.Core/Database/Logging/Entities/ChatHistory.cs b/Intersect.Server.Core/Database/Logging/Entities/ChatHistory.cs index bac3d47dfb..14caf45b59 100644 --- a/Intersect.Server.Core/Database/Logging/Entities/ChatHistory.cs +++ b/Intersect.Server.Core/Database/Logging/Entities/ChatHistory.cs @@ -18,12 +18,8 @@ public partial class ChatHistory public DateTime TimeStamp { get; set; } - [JsonIgnore] public ChatMessageType MessageType { get; set; } - [JsonProperty("MessageType")] - public string MessageTypeName => Enum.GetName(typeof(ChatMessageType), MessageType); - public string MessageText { get; set; } public Guid TargetId { get; set; } diff --git a/Intersect.Server.Core/Database/Logging/Entities/GuildHistory.cs b/Intersect.Server.Core/Database/Logging/Entities/GuildHistory.cs index 3ce1f77960..f06a4397d3 100644 --- a/Intersect.Server.Core/Database/Logging/Entities/GuildHistory.cs +++ b/Intersect.Server.Core/Database/Logging/Entities/GuildHistory.cs @@ -19,12 +19,8 @@ public partial class GuildHistory public DateTime TimeStamp { get; set; } - [JsonIgnore] public GuildActivityType Type { get; set; } - [JsonProperty("ActivityType")] - public string ActivityTypeName => Enum.GetName(typeof(GuildActivityType), Type); - public string Meta { get; set; } public Guid InitiatorId { get; set; } diff --git a/Intersect.Server.Core/Database/Logging/Entities/UserActivityHistory.cs b/Intersect.Server.Core/Database/Logging/Entities/UserActivityHistory.cs index af9c8f26f4..b16910b313 100644 --- a/Intersect.Server.Core/Database/Logging/Entities/UserActivityHistory.cs +++ b/Intersect.Server.Core/Database/Logging/Entities/UserActivityHistory.cs @@ -14,18 +14,10 @@ public partial class UserActivityHistory public Guid? PlayerId { get; set; } - [JsonIgnore] public UserAction Action { get; set; } - [JsonProperty("Action")] - public string ActionString => Enum.GetName(typeof(UserAction), Action); - - [JsonIgnore] public PeerType Peer { get; set; } - [JsonProperty("Peer")] - public string PeerString => Enum.GetName(typeof(PeerType), Peer); - public string Ip { get; set; } public string Meta { get; set; } @@ -75,19 +67,19 @@ public enum UserAction DisconnectTimeout, DisconnectBan, - + DisconnectBanFail, DisconnectKick, - + DisconnectKickFail, - + Kill, - + KillFail, - + Mute, - + MuteFail, CreatePlayer, diff --git a/Intersect.Server.Core/Database/PlayerData/Players/Bag.cs b/Intersect.Server.Core/Database/PlayerData/Players/Bag.cs index 401c48a264..b94133e493 100644 --- a/Intersect.Server.Core/Database/PlayerData/Players/Bag.cs +++ b/Intersect.Server.Core/Database/PlayerData/Players/Bag.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations.Schema; +using System.Diagnostics.CodeAnalysis; using Intersect.GameObjects; using Intersect.Logging; using Microsoft.EntityFrameworkCore; @@ -84,38 +85,6 @@ public void ValidateSlots(bool checkItemExistence = true) Slots = slots; } - public static Bag GetBag(Guid id) - { - try - { - using (var context = DbInterface.CreatePlayerContext()) - { - var bag = context.Bags.Where(p => p.Id == id).Include(p => p.Slots).SingleOrDefault(); - if (bag != null) - { - bag.Slots = bag.Slots.OrderBy(p => p.Slot).ToList(); - bag.ValidateSlots(); - - //Remove any items from this bag that have been removed from the game - foreach (var itm in bag.Slots) - { - if (itm.ItemId != Guid.Empty && ItemBase.Get(itm.ItemId) == null) - { - itm.Set(Item.None); - } - } - } - - return bag; - } - } - catch (Exception ex) - { - Log.Error(ex); - return null; - } - } - public void Save (bool create = false) { try @@ -197,4 +166,40 @@ public int FindSlotIndex(BagSlot slot) return Slots.FindIndex(sl => sl.Id == slot.Id); } + public static bool TryGetBag(Guid bagId, [NotNullWhen(true)] out Bag? bag) + { + try + { + using var context = DbInterface.CreatePlayerContext(); + bag = context.Bags.Where(bag => bag.Id == bagId).Include(bag => bag.Slots).SingleOrDefault(); + if (bag == null) + { + return false; + } + + bag.Slots = bag.Slots.OrderBy(p => p.Slot).ToList(); + bag.ValidateSlots(); + + // Remove any items from this bag that have been removed from the game + // ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator + foreach (var bagSlot in bag.Slots) + { + if (ItemBase.TryGet(bagSlot.ItemId, out _)) + { + continue; + } + + bagSlot.Set(Item.None); + } + + return true; + + } + catch (Exception ex) + { + Log.Error(ex); + bag = null; + return false; + } + } } diff --git a/Intersect.Server.Core/Database/PlayerData/Players/BagSlot.cs b/Intersect.Server.Core/Database/PlayerData/Players/BagSlot.cs index 3c89555710..9fcf93a239 100644 --- a/Intersect.Server.Core/Database/PlayerData/Players/BagSlot.cs +++ b/Intersect.Server.Core/Database/PlayerData/Players/BagSlot.cs @@ -23,6 +23,7 @@ public BagSlot(int slot) [DatabaseGenerated(DatabaseGeneratedOption.Identity), JsonIgnore] public Guid Id { get; private set; } + [JsonIgnore] public bool IsEmpty => ItemId == default; [JsonIgnore] diff --git a/Intersect.Server.Core/Database/PlayerData/Players/BankSlot.cs b/Intersect.Server.Core/Database/PlayerData/Players/BankSlot.cs index f60d9bab40..75e3ccd520 100644 --- a/Intersect.Server.Core/Database/PlayerData/Players/BankSlot.cs +++ b/Intersect.Server.Core/Database/PlayerData/Players/BankSlot.cs @@ -26,6 +26,7 @@ public BankSlot(int slot) [DatabaseGenerated(DatabaseGeneratedOption.Identity), JsonIgnore] public Guid Id { get; private set; } + [JsonIgnore] public bool IsEmpty => ItemId == default; [JsonIgnore] diff --git a/Intersect.Server.Core/Database/PlayerData/Players/Guild.cs b/Intersect.Server.Core/Database/PlayerData/Players/Guild.cs index 3aa9fa11fb..2e91be27c1 100644 --- a/Intersect.Server.Core/Database/PlayerData/Players/Guild.cs +++ b/Intersect.Server.Core/Database/PlayerData/Players/Guild.cs @@ -5,6 +5,7 @@ using Intersect.Server.Entities; using Intersect.Server.Networking; using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; using Intersect.Collections.Slotting; using Microsoft.EntityFrameworkCore; using Intersect.Network.Packets.Server; @@ -88,6 +89,7 @@ public Guild() [JsonIgnore] public virtual List Variables { get; set; } = new List(); + [JsonIgnore] /// /// Variables that have been updated for this guild which need to be saved to the db /// @@ -156,6 +158,12 @@ public static Guild CreateGuild(Player creator, string name) } + public static bool TryGet(Guid guildId, [NotNullWhen(true)] out Guild? guild) + { + guild = LoadGuild(guildId); + return guild != default; + } + /// /// Loads a guild and it's members from the database /// diff --git a/Intersect.Server.Core/Database/PlayerData/Players/GuildBankSlot.cs b/Intersect.Server.Core/Database/PlayerData/Players/GuildBankSlot.cs index 0ee625f58f..9e18bcfe67 100644 --- a/Intersect.Server.Core/Database/PlayerData/Players/GuildBankSlot.cs +++ b/Intersect.Server.Core/Database/PlayerData/Players/GuildBankSlot.cs @@ -24,6 +24,7 @@ public GuildBankSlot(int slot) [DatabaseGenerated(DatabaseGeneratedOption.Identity), JsonIgnore] public Guid Id { get; private set; } + [JsonIgnore] public bool IsEmpty => ItemId == default; [JsonIgnore] diff --git a/Intersect.Server.Core/Database/PlayerData/Players/HotbarSlot.cs b/Intersect.Server.Core/Database/PlayerData/Players/HotbarSlot.cs index 5c41e4c6ec..25303e23a3 100644 --- a/Intersect.Server.Core/Database/PlayerData/Players/HotbarSlot.cs +++ b/Intersect.Server.Core/Database/PlayerData/Players/HotbarSlot.cs @@ -28,6 +28,7 @@ public HotbarSlot(int slot) [DatabaseGenerated(DatabaseGeneratedOption.Identity), JsonIgnore] public Guid Id { get; private set; } + [JsonIgnore] public bool IsEmpty => ItemOrSpellId == default; public Guid ItemOrSpellId { get; set; } = Guid.Empty; diff --git a/Intersect.Server.Core/Database/PlayerData/Players/InventorySlot.cs b/Intersect.Server.Core/Database/PlayerData/Players/InventorySlot.cs index ba14461241..e95997c61d 100644 --- a/Intersect.Server.Core/Database/PlayerData/Players/InventorySlot.cs +++ b/Intersect.Server.Core/Database/PlayerData/Players/InventorySlot.cs @@ -26,6 +26,7 @@ public InventorySlot(int slot) [DatabaseGenerated(DatabaseGeneratedOption.Identity), JsonIgnore] public Guid Id { get; private set; } + [JsonIgnore] public bool IsEmpty => ItemId == default; [JsonIgnore] diff --git a/Intersect.Server.Core/Database/PlayerData/Players/SpellSlot.cs b/Intersect.Server.Core/Database/PlayerData/Players/SpellSlot.cs index ce10ef2f3b..99754b6253 100644 --- a/Intersect.Server.Core/Database/PlayerData/Players/SpellSlot.cs +++ b/Intersect.Server.Core/Database/PlayerData/Players/SpellSlot.cs @@ -26,6 +26,7 @@ public SpellSlot(int slot) [DatabaseGenerated(DatabaseGeneratedOption.Identity), JsonIgnore] public Guid Id { get; private set; } + [JsonIgnore] public bool IsEmpty => SpellId == default; [JsonIgnore] diff --git a/Intersect.Server.Core/Database/PlayerData/Players/Variable.cs b/Intersect.Server.Core/Database/PlayerData/Players/Variable.cs index ca969d0d99..89bccbeded 100644 --- a/Intersect.Server.Core/Database/PlayerData/Players/Variable.cs +++ b/Intersect.Server.Core/Database/PlayerData/Players/Variable.cs @@ -18,7 +18,7 @@ public partial class Variable public VariableValue Value { get; set; } = new(); [NotMapped] - [JsonProperty("Value")] + [JsonProperty(nameof(Value))] public dynamic ValueData => Value.Value; [Column(nameof(Value))] diff --git a/Intersect.Server.Core/Database/PlayerData/User.cs b/Intersect.Server.Core/Database/PlayerData/User.cs index 98a0605254..56a1162381 100644 --- a/Intersect.Server.Core/Database/PlayerData/User.cs +++ b/Intersect.Server.Core/Database/PlayerData/User.cs @@ -592,9 +592,17 @@ out LoginFailureReason failureReason var salt = GetUserSalt(username); if (string.IsNullOrWhiteSpace(salt)) { - Log.Error($"Login to {username} failed because the salt is empty."); + if (UserExists(username)) + { + Log.Error($"Login to {username} failed because the salt is empty."); + failureReason = new LoginFailureReason(LoginFailureType.ServerError); + } + else + { + failureReason = new LoginFailureReason(LoginFailureType.InvalidCredentials); + } + user = default; - failureReason = new LoginFailureReason(LoginFailureType.ServerError); return false; } @@ -612,38 +620,117 @@ out LoginFailureReason failureReason } } - public static User FindById(Guid userId) + public static bool TryFetch(LookupKey lookupKey, [NotNullWhen(true)] out User? user) => + TryFetch(lookupKey, out user, out _); + + public static bool TryFetch(LookupKey lookupKey, [NotNullWhen(true)] out User? user, out Client? client) + { + if (lookupKey is { HasName: false, HasId: false }) + { + user = default; + client = default; + return false; + } + + (client, user) = lookupKey.HasId ? Fetch(lookupKey.Id) : Fetch(lookupKey.Name); + return user != default; + } + + public static bool TryFind(LookupKey lookupKey, [NotNullWhen(true)] out User? user) { using var playerContext = DbInterface.CreatePlayerContext(); - return FindById(userId, playerContext); + return TryFind(lookupKey, playerContext, out user); } - public static User FindById(Guid userId, PlayerContext playerContext) + public static bool TryFind(LookupKey lookupKey, PlayerContext playerContext, [NotNullWhen(true)] out User? user) { - if (userId == Guid.Empty) + if (lookupKey.HasId) { - return null; + return TryFindById(lookupKey.Id, playerContext, out user); + } + + if (lookupKey.HasName) + { + return TryFindByName(lookupKey.Name, playerContext, out user); } - var user = FindOnline(userId); + throw new InvalidOperationException($"Lookup key has neither an id nor a name: '{lookupKey}'"); + } + + public static bool TryFindByName(string username, [NotNullWhen(true)] out User? user) + { + using var playerContext = DbInterface.CreatePlayerContext(); + return TryFindByName(username, playerContext, out user); + } + + public static bool TryFindByName(string username, PlayerContext playerContext, [NotNullWhen(true)] out User? user) + { + if (string.IsNullOrWhiteSpace(username)) + { + user = default; + return false; + } + + user = FindOnline(username); if (user != null) { - return user; + return true; } try { using var context = DbInterface.CreatePlayerContext(); - return QueryUserByIdShallow(playerContext, userId); + var queriedUser = QueryUserByNameShallow(context, username); + if (queriedUser != default) + { + user = queriedUser; + return true; + } } - catch (Exception ex) + catch (Exception exception) { - Log.Error(ex); - return null; + Log.Error(exception, $"Failed to find user by name '{username}'"); } + + return false; } + public static bool TryFindById(Guid userId, [NotNullWhen(true)] out User? user) + { + using var playerContext = DbInterface.CreatePlayerContext(); + return TryFindById(userId, playerContext, out user); + } + + public static bool TryFindById(Guid userId, PlayerContext playerContext, [NotNullWhen(true)] out User? user) + { + if (userId == default) + { + user = default; + return false; + } + + user = FindOnline(userId); + + if (user != null) + { + return true; + } + + try + { + user = QueryUserByIdShallow(playerContext, userId); + } + catch (Exception exception) + { + Log.Error(exception, $"Failed to find user by id '{userId}'"); + } + + return user != default; + } + + public static User? FindById(Guid userId) => TryFindById(userId, out var user) ? user : default; + public static User Find(string username) { if (string.IsNullOrWhiteSpace(username)) @@ -754,10 +841,8 @@ public static string GetUserSalt(string userName) try { - using (var context = DbInterface.CreatePlayerContext()) - { - return SaltByName(context, userName); - } + using var context = DbInterface.CreatePlayerContext(); + return SaltByName(context, userName); } catch (Exception ex) { diff --git a/Intersect.Server.Core/Entities/Combat/Status.cs b/Intersect.Server.Core/Entities/Combat/Status.cs index a8f660a655..932d4942c8 100644 --- a/Intersect.Server.Core/Entities/Combat/Status.cs +++ b/Intersect.Server.Core/Entities/Combat/Status.cs @@ -10,7 +10,7 @@ namespace Intersect.Server.Entities.Combat; public partial class Status { - public string Data = ""; + public string Data = string.Empty; public long Duration; diff --git a/Intersect.Server.Core/Entities/Entity.cs b/Intersect.Server.Core/Entities/Entity.cs index 51b1a948a5..97608a3cb7 100644 --- a/Intersect.Server.Core/Entities/Entity.cs +++ b/Intersect.Server.Core/Entities/Entity.cs @@ -32,10 +32,22 @@ public abstract partial class Entity : IEntity [NotMapped] public Guid MapInstanceId { get; set; } = Guid.Empty; - [JsonProperty("MaxVitals"), NotMapped] private long[] _maxVital = new long[Enum.GetValues().Length]; + [NotMapped] private long[] _maxVital = new long[Enum.GetValues().Length]; + + [JsonProperty("MaxVitals"), NotMapped] + public IReadOnlyDictionary MaxVitalsLookup => GetMaxVitals().Select((value, index) => (value, index)) + .ToDictionary(t => (Vital)t.index, t => t.value).AsReadOnly(); [NotMapped, JsonIgnore] public Combat.Stat[] Stat = new Combat.Stat[Enum.GetValues().Length]; + [JsonProperty("Stats"), NotMapped] + public IReadOnlyDictionary StatsLookup => Stat.Select((computedStat, index) => (computedStat, index)) + .ToDictionary(t => (Stat)t.index, t => t.computedStat.Value()).AsReadOnly(); + + [JsonProperty("Vitals"), NotMapped] + public IReadOnlyDictionary VitalsLookup => _vitals.Select((value, index) => (value, index)) + .ToDictionary(t => (Vital)t.index, t => t.value).AsReadOnly(); + [NotMapped, JsonIgnore] public Entity Target { get; set; } = null; public Entity() : this(Guid.NewGuid(), Guid.Empty) @@ -102,12 +114,12 @@ public string JsonColor [JsonIgnore, Column("Vitals")] public string VitalsJson { - get => DatabaseUtils.SaveLongArray(mVitals, Enum.GetValues().Length); - set => mVitals = DatabaseUtils.LoadLongArray(value, Enum.GetValues().Length); + get => DatabaseUtils.SaveLongArray(_vitals, Enum.GetValues().Length); + set => _vitals = DatabaseUtils.LoadLongArray(value, Enum.GetValues().Length); } - [JsonProperty("Vitals"), NotMapped] - private long[] mVitals { get; set; } = new long[Enum.GetValues().Length]; + [NotMapped] + private long[] _vitals { get; set; } = new long[Enum.GetValues().Length]; [JsonIgnore, NotMapped] private long[] mOldVitals { get; set; } = new long[Enum.GetValues().Length]; @@ -138,7 +150,6 @@ public string StatPointsJson public int[] StatPointAllocations { get; set; } = new int[Enum.GetValues().Length]; //Inventory - [JsonIgnore] public virtual SlotList Items { get; set; } = new( Options.Instance.PlayerOpts.MaxInventory, InventorySlot.Create @@ -225,11 +236,10 @@ public string FooterLabelJson public bool HideEntity { get; set; } = false; [NotMapped, JsonIgnore] - public List Animations { get; set; } = new List(); + public List Animations { get; set; } = []; //DoT/HoT Spells - [NotMapped, JsonIgnore] - public ConcurrentDictionary DoT { get; set; } = new ConcurrentDictionary(); + [NotMapped, JsonIgnore] public ConcurrentDictionary DoT { get; } = []; [NotMapped, JsonIgnore] public DoT[] CachedDots { get; set; } = new DoT[0]; @@ -1404,13 +1414,13 @@ public virtual void ProcessRegen() public long GetVital(int vital) { - return mVitals[vital]; + return _vitals[vital]; } public long[] GetVitals() { var vitals = new long[Enum.GetValues().Length]; - Array.Copy(mVitals, 0, vitals, 0, Enum.GetValues().Length); + Array.Copy(_vitals, 0, vitals, 0, Enum.GetValues().Length); return vitals; } @@ -1432,7 +1442,7 @@ public void SetVital(int vital, long value) value = GetMaxVital(vital); } - mVitals[vital] = value; + _vitals[vital] = value; } public void SetVital(Vital vital, long value) diff --git a/Intersect.Server.Core/Entities/Events/CommandProcessing.cs b/Intersect.Server.Core/Entities/Events/CommandProcessing.cs index fb94b4d4b6..be5caec129 100644 --- a/Intersect.Server.Core/Entities/Events/CommandProcessing.cs +++ b/Intersect.Server.Core/Entities/Events/CommandProcessing.cs @@ -1748,7 +1748,7 @@ public static string ParseEventText(string input, Player player, Event instance) { if (input == null) { - input = ""; + input = string.Empty; } if (player != null && input.Contains("\\")) diff --git a/Intersect.Server.Core/Entities/Label.cs b/Intersect.Server.Core/Entities/Label.cs index 9182f9aa75..d28f8d6d3a 100644 --- a/Intersect.Server.Core/Entities/Label.cs +++ b/Intersect.Server.Core/Entities/Label.cs @@ -3,17 +3,10 @@ namespace Intersect.Server.Entities; -public partial struct Label +public partial struct Label(string label, Color color) { + [JsonProperty(nameof(Label))] + public readonly string Text = label; - [JsonProperty("Label")] public string Text; - - public Color Color; - - public Label(string label, Color color) - { - Text = label; - Color = color; - } - + public readonly Color Color = color; } diff --git a/Intersect.Server.Core/Entities/Npc.cs b/Intersect.Server.Core/Entities/Npc.cs index d006d333db..ff05842868 100644 --- a/Intersect.Server.Core/Entities/Npc.cs +++ b/Intersect.Server.Core/Entities/Npc.cs @@ -139,8 +139,8 @@ public Npc(NpcBase myBase, bool despawnable = false) : base() for (var i = 0; i < Enum.GetValues().Length; i++) { - SetMaxVital(i, myBase.MaxVital[i]); - SetVital(i, myBase.MaxVital[i]); + SetMaxVital(i, myBase.MaxVitals[i]); + SetVital(i, myBase.MaxVitals[i]); } Range = (byte)myBase.SightRange; diff --git a/Intersect.Server.Core/Entities/Player.Database.cs b/Intersect.Server.Core/Entities/Player.Database.cs index 2ada42b872..c2939ed0df 100644 --- a/Intersect.Server.Core/Entities/Player.Database.cs +++ b/Intersect.Server.Core/Entities/Player.Database.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations.Schema; +using System.Diagnostics.CodeAnalysis; using Intersect.Logging; using Intersect.Server.Database; using Intersect.Server.Database.PlayerData; @@ -37,23 +38,35 @@ public partial class Player #region Lookup - public static Tuple Fetch(LookupKey lookupKey) + public static bool TryFetch(LookupKey lookupKey, [NotNullWhen(true)] out Tuple? tuple) { - if (!lookupKey.HasName && !lookupKey.HasId) + tuple = Fetch(lookupKey); + return tuple != default; + } + + public static Tuple Fetch(LookupKey lookupKey, bool loadRelationships = false, + bool loadBags = false) + { + if (lookupKey is { HasName: false, HasId: false }) { return new Tuple(null, null); } // HasName checks if null or empty // ReSharper disable once AssignNullToNotNullAttribute - return lookupKey.HasId ? Fetch(lookupKey.Id) : Fetch(lookupKey.Name); + return lookupKey.HasId + ? Fetch(lookupKey.Id) + : Fetch(lookupKey.Name, loadRelationships: loadRelationships, loadBags: loadBags); } - public static Tuple Fetch(string playerName) + public static Tuple Fetch(string playerName, bool loadRelationships = false, bool loadBags = false) { var client = Globals.Clients.Find(queryClient => Entity.CompareName(playerName, queryClient?.Entity?.Name)); - return new Tuple(client, client?.Entity ?? Player.Find(playerName)); + return new Tuple( + client, + client?.Entity ?? Find(playerName, loadRelationships: loadRelationships, loadBags: loadBags) + ); } public static Tuple Fetch(Guid playerId) @@ -90,7 +103,7 @@ public static Player Find(Guid playerId) } } - public static Player Find(string playerName) + public static Player Find(string playerName, bool loadRelationships = false, bool loadBags = false) { if (string.IsNullOrWhiteSpace(playerName)) { @@ -107,6 +120,10 @@ public static Player Find(string playerName) { using var context = DbInterface.CreatePlayerContext(); player = QueryPlayerByName(context, playerName); + if (loadRelationships) + { + player.LoadRelationships(context, loadBags); + } _ = Validate(player); return player; } @@ -148,7 +165,7 @@ public static bool PlayerExists(string name) #region Loading - public bool LoadRelationships(PlayerContext playerContext) + public bool LoadRelationships(PlayerContext playerContext, bool loadBags = false) { lock (_savingLock) { @@ -166,6 +183,31 @@ public bool LoadRelationships(PlayerContext playerContext) entityEntry.Collection(p => p.Quests).Load(); entityEntry.Collection(p => p.Spells).Load(); entityEntry.Collection(p => p.Variables).Load(); + + if (loadBags) + { + foreach (var item in Items) + { + if (item.BagId == default) + { + continue; + } + + var navigationEntry = playerContext.Entry(item).Navigation(nameof(item.Bag)); + if (navigationEntry.IsLoaded) + { + continue; + } + + navigationEntry.Load(); + if (item.Bag != default) + { + item.Bag.ValidateSlots(); + playerContext.Bags.Entry(item.Bag).Collection(b => b.Slots).Load(); + } + } + } + return Validate(this, playerContext); } diff --git a/Intersect.Server.Core/Entities/Player.cs b/Intersect.Server.Core/Entities/Player.cs index bb80d5e6d0..851a46f37d 100644 --- a/Intersect.Server.Core/Entities/Player.cs +++ b/Intersect.Server.Core/Entities/Player.cs @@ -1,4 +1,5 @@ using System.Collections.Concurrent; +using System.Collections.Immutable; using System.ComponentModel.DataAnnotations.Schema; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -68,8 +69,8 @@ public partial class Player : Entity public static int OnlineCount => OnlinePlayers.Count; - [JsonProperty("MaxVitals"), NotMapped] - public new long[] MaxVitals => GetMaxVitals(); + [JsonIgnore, NotMapped] + public long[] MaxVitals => GetMaxVitals(); //Name, X, Y, Dir, Etc all in the base Entity Class public Guid ClassId { get; set; } @@ -90,13 +91,21 @@ public string EquipmentJson set => Equipment = DatabaseUtils.LoadIntArray(value, Options.EquipmentSlots.Count); } - [NotMapped] + [NotMapped, JsonProperty("EquipmentSlots")] public int[] Equipment { get; set; } = new int[Options.EquipmentSlots.Count]; + [NotMapped] + public virtual ImmutableArray Bags => + [..Items.Where(item => item.BagId.HasValue && item.BagId.Value != default).Select(item => item.Bag)]; + + [NotMapped] + public virtual ImmutableArray BagIds => + [..Items.Where(item => item.BagId.HasValue && item.BagId.Value != default).Select(item => item.BagId ?? default)]; + /// /// Returns a list of all equipped s /// - [NotMapped, JsonIgnore] + [NotMapped, JsonProperty(nameof(Equipment))] public List EquippedItems { get @@ -141,7 +150,6 @@ public ulong PlayTimeSeconds public DateTime? LoginTime { get; set; } //Bank - [JsonIgnore] public virtual SlotList Bank { get; set; } = new( Options.Instance.PlayerOpts.InitialBankslots, BankSlot.Create @@ -149,26 +157,23 @@ public ulong PlayTimeSeconds //Friends -- Not used outside of EF [JsonIgnore] - public virtual List Friends { get; set; } = new List(); + public virtual List Friends { get; set; } = []; //Local Friends - [NotMapped, JsonProperty("Friends")] - public virtual Dictionary CachedFriends { get; set; } = new Dictionary(); + [NotMapped, JsonProperty(nameof(Friends))] + public virtual Dictionary CachedFriends { get; set; } = []; - //HotBar - [JsonIgnore] + // HotBar public virtual SlotList Hotbar { get; set; } = new( Options.Instance.PlayerOpts.HotbarSlotCount, HotbarSlot.Create ); //Quests - [JsonIgnore] - public virtual List Quests { get; set; } = new List(); + public virtual List Quests { get; set; } = []; //Variables - [JsonIgnore] - public virtual List Variables { get; set; } = new List(); + public virtual List Variables { get; set; } = []; [JsonIgnore, NotMapped] public bool IsValidPlayer => !IsDisposed && Client?.Entity == this; @@ -7578,7 +7583,7 @@ public bool TryAddToInstanceController() #region Crafting - [NotMapped, JsonIgnore] public CraftingState CraftingState { get; set; } + [NotMapped] public CraftingState? CraftingState { get; set; } [NotMapped, JsonIgnore] public Guid OpenCraftingTableId { get; set; } diff --git a/Intersect.Server.Core/Extensions/EnumerableExtensions.cs b/Intersect.Server.Core/Extensions/EnumerableExtensions.cs index 23c08d86d0..621549bd10 100644 --- a/Intersect.Server.Core/Extensions/EnumerableExtensions.cs +++ b/Intersect.Server.Core/Extensions/EnumerableExtensions.cs @@ -10,42 +10,40 @@ public static partial class EnumerableExtensions public static IEnumerable Sort( this IEnumerable queryable, - IReadOnlyCollection sort + IReadOnlyCollection sorts ) { - if (sort == null || sort.Count < 1) + if (sorts == null || sorts.Count < 1) { return queryable; } var sorted = queryable; var orderedOnce = false; - foreach (var sortPair in sort) + foreach (var (by, direction) in sorts.SelectMany(sort => sort.By.Select(by => (by, sort.Direction)))) { - if (string.IsNullOrWhiteSpace(sortPair.By)) + if (string.IsNullOrWhiteSpace(by)) { continue; } - object OrderLambda(TValue entity) - { - return EF.Property(entity, sortPair.By); - } - - if (sortPair.Direction == SortDirection.Ascending) + if (direction == SortDirection.Ascending) { sorted = orderedOnce - ? ((IOrderedEnumerable) sorted).ThenBy(OrderLambda) + ? ((IOrderedEnumerable)sorted).ThenBy(OrderLambda) : sorted.OrderBy(OrderLambda); } else { sorted = orderedOnce - ? ((IOrderedEnumerable) sorted).ThenByDescending(OrderLambda) + ? ((IOrderedEnumerable)sorted).ThenByDescending(OrderLambda) : sorted.OrderByDescending(OrderLambda); } orderedOnce = true; + continue; + + object OrderLambda(TValue entity) => EF.Property(entity, by); } return sorted; diff --git a/Intersect.Server.Core/Extensions/QueryableExtensions.cs b/Intersect.Server.Core/Extensions/QueryableExtensions.cs index fc1ed2b528..0f409bb6a8 100644 --- a/Intersect.Server.Core/Extensions/QueryableExtensions.cs +++ b/Intersect.Server.Core/Extensions/QueryableExtensions.cs @@ -10,39 +10,11 @@ namespace Intersect.Server.Extensions; public static partial class QueryableExtensions { - public static IQueryable Sort( - this IQueryable queryable, - IReadOnlyCollection sort - ) - { - return DoSort(queryable, sort); - } - public static bool IsOrdered(this IQueryable queryable) { return queryable.Expression.Type == typeof(IOrderedQueryable); } - public static IOrderedQueryable SmartOrderBy( - this IQueryable queryable, - Sort sort - ) - { - return sort.Direction == SortDirection.Ascending - ? queryable.OrderBy(entity => EF.Property(entity, sort.By)) - : queryable.OrderByDescending(entity => EF.Property(entity, sort.By)); - } - - public static IOrderedQueryable SmartThenBy( - this IOrderedQueryable queryable, - Sort sort - ) - { - return sort.Direction == SortDirection.Ascending - ? queryable.ThenBy(entity => EF.Property(entity, sort.By)) - : queryable.ThenByDescending(entity => EF.Property(entity, sort.By)); - } - public static IQueryable SmartSort(this IQueryable queryable, Sort sort) { return sort.Direction == SortDirection.Ascending @@ -55,9 +27,9 @@ public static IQueryable SmartSortAscending( Sort sort ) { - return queryable.IsOrdered() - ? (queryable as IOrderedQueryable)?.ThenBy(entity => EF.Property(entity, sort.By)) - : queryable.OrderBy(entity => EF.Property(entity, sort.By)); + return queryable is IOrderedQueryable orderedQueryable + ? orderedQueryable.ThenBy(entity => EF.Property(entity, sort.By.First())) + : queryable.OrderBy(entity => EF.Property(entity, sort.By.First())); } public static IQueryable SmartSortDescending( @@ -65,51 +37,10 @@ public static IQueryable SmartSortDescending( Sort sort ) { - return queryable.IsOrdered() - ? (queryable as IOrderedQueryable)?.ThenByDescending( - entity => EF.Property(entity, sort.By) - ) - : queryable.OrderByDescending(entity => EF.Property(entity, sort.By)); - } - - public static IQueryable DoSort( - IQueryable queryable, - IReadOnlyCollection sort - ) - { - if (sort == null || sort.Count < 1) - { - return queryable; - } - - var sorted = queryable; - var orderedOnce = false; - foreach (var sortPair in sort) - { - if (string.IsNullOrWhiteSpace(sortPair.By)) - { - continue; - } - - Expression> orderLambda = entity => EF.Property(entity, sortPair.By); - - if (sortPair.Direction == SortDirection.Ascending) - { - sorted = orderedOnce - ? ((IOrderedQueryable) sorted).ThenBy(orderLambda) - : sorted.OrderBy(orderLambda); - } - else - { - sorted = orderedOnce - ? ((IOrderedQueryable) sorted).ThenByDescending(orderLambda) - : sorted.OrderByDescending(orderLambda); - } - - orderedOnce = true; - } - return sorted; + return queryable is IOrderedQueryable orderedQueryable + ? orderedQueryable.ThenByDescending(entity => EF.Property(entity, sort.By.First())) + : queryable.OrderByDescending(entity => EF.Property(entity, sort.By.First())); } } diff --git a/Intersect.Server.Core/Intersect.Server.Core.csproj b/Intersect.Server.Core/Intersect.Server.Core.csproj index 787bb0b860..5871a5fa37 100644 --- a/Intersect.Server.Core/Intersect.Server.Core.csproj +++ b/Intersect.Server.Core/Intersect.Server.Core.csproj @@ -19,7 +19,7 @@ - + Resources\runtimes\linux-arm64\libe_sqlite3.so @@ -40,7 +40,7 @@ Resources\runtimes\win-x64\e_sqlite3.dll - + @@ -55,6 +55,7 @@ + diff --git a/Intersect.Server.Core/Networking/PacketHandler.cs b/Intersect.Server.Core/Networking/PacketHandler.cs index e3876d778b..e41bdb0db4 100644 --- a/Intersect.Server.Core/Networking/PacketHandler.cs +++ b/Intersect.Server.Core/Networking/PacketHandler.cs @@ -825,7 +825,7 @@ public void HandlePacket(Client client, ChatMsgPacket packet) } //If no /command, then use the designated channel. - var cmd = ""; + var cmd = string.Empty; if (!msg.StartsWith("/")) { switch (channel) @@ -2602,7 +2602,7 @@ public void HandlePacket(Client client, ResetPasswordPacket packet) if (user.PasswordResetCode.ToLower().Trim() == packet.ResetCode.ToLower().Trim() && user.PasswordResetTime > DateTime.UtcNow) { - user.PasswordResetCode = ""; + user.PasswordResetCode = string.Empty; user.PasswordResetTime = DateTime.MinValue; DbInterface.ResetPass(user, packet.NewPassword); success = true; diff --git a/Intersect.Server/Intersect.Server.csproj b/Intersect.Server/Intersect.Server.csproj index 8ecb3e2ae8..97fc999e13 100644 --- a/Intersect.Server/Intersect.Server.csproj +++ b/Intersect.Server/Intersect.Server.csproj @@ -102,6 +102,7 @@ + diff --git a/Intersect.Server/Networking/Helpers/NetDebug.cs b/Intersect.Server/Networking/Helpers/NetDebug.cs index 2b22abf69b..8b8f57a03e 100644 --- a/Intersect.Server/Networking/Helpers/NetDebug.cs +++ b/Intersect.Server/Networking/Helpers/NetDebug.cs @@ -20,7 +20,7 @@ public static void GenerateDebugFile() var sb = new StringBuilder(); sb.AppendLine("Intersect Network Diagnostics"); sb.AppendLine(); - var externalIp = ""; + var externalIp = string.Empty; var serverAccessible = PortChecker.CanYouSeeMe(Options.ServerPort, out externalIp); string localIP; using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0)) diff --git a/Intersect.Server/Notifications/Notification.cs b/Intersect.Server/Notifications/Notification.cs index 79cd6409dc..293da178f1 100644 --- a/Intersect.Server/Notifications/Notification.cs +++ b/Intersect.Server/Notifications/Notification.cs @@ -20,11 +20,11 @@ public Notification(string to, string subject = "", bool html = false) IsHtml = html; } - public string ToAddress { get; set; } = ""; + public string ToAddress { get; set; } = string.Empty; - public string Subject { get; set; } = ""; + public string Subject { get; set; } = string.Empty; - public string Body { get; set; } = ""; + public string Body { get; set; } = string.Empty; public bool IsHtml { get; set; } = false; diff --git a/Intersect.Server/Web/Net7/ApiService.cs b/Intersect.Server/Web/Net7/ApiService.cs index 6150ebb3dc..91a34593b0 100644 --- a/Intersect.Server/Web/Net7/ApiService.cs +++ b/Intersect.Server/Web/Net7/ApiService.cs @@ -1,15 +1,20 @@ using System.Net; using System.Reflection; +using System.Text.Json.Serialization; using System.Threading.RateLimiting; using Intersect.Core; using Intersect.Enums; using Intersect.Logging; +using Intersect.Models; using Intersect.Server.Core; +using Intersect.Server.Localization; using Intersect.Server.Web.Configuration; using Intersect.Server.Web.Constraints; using Intersect.Server.Web.Middleware; using Intersect.Server.Web.RestApi.Payloads; +using Intersect.Server.Web.RestApi.Routes; using Intersect.Server.Web.Serialization; +using Intersect.Server.Web.Swagger.Filters; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; @@ -25,6 +30,9 @@ using Microsoft.Extensions.Hosting; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Newtonsoft.Json.Converters; namespace Intersect.Server.Web; @@ -74,10 +82,10 @@ internal partial class ApiService : ApplicationService { - routeOptions.ConstraintMap.Add(nameof(AdminAction), typeof(AdminActionsConstraint)); + routeOptions.ConstraintMap.Add(nameof(AdminAction), typeof(EnumConstraint)); + routeOptions.ConstraintMap.Add(nameof(GameObjectType), typeof(EnumConstraint)); routeOptions.ConstraintMap.Add(nameof(ChatMessage), typeof(ChatMessage.RouteConstraint)); - routeOptions.ConstraintMap.Add(nameof(GameObjectType), typeof(GameObjectTypeConstraint)); - routeOptions.ConstraintMap.Add(nameof(LookupKey), typeof(LookupKeyConstraint)); + routeOptions.ConstraintMap.Add(nameof(LookupKey), typeof(NonNullConstraint)); } ); @@ -127,6 +135,7 @@ internal partial class ApiService : ApplicationService jsonOptions.SerializerOptions.Converters.Add(new JsonStringEnumConverter()) + // ); + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddHealthChecks(); - builder.Services.AddSwaggerGen(); + + builder.Services.AddSwaggerGen( + sgo => + { + var documentFilterTokenRequest = + new PolymorphicDocumentFilter("grant_type") + .WithSubtype(OAuthController.GrantType.Password) + .WithSubtype( + OAuthController.GrantType.RefreshToken + ); + sgo.AddDocumentFilterInstance(documentFilterTokenRequest); + sgo.AddSchemaFilterInstance(documentFilterTokenRequest.CreateSchemaFilter()); + sgo.SchemaFilter(); + sgo.SchemaFilter(); + sgo.SchemaFilter(); + sgo.EnableAnnotations(enableAnnotationsForInheritance: true, enableAnnotationsForPolymorphism: true); + sgo.UseOneOfForPolymorphism(); + sgo.UseAllOfForInheritance(); + }); + builder.Services.AddSwaggerGenNewtonsoftSupport(); + var tokenGenerationOptionsSection = apiConfigurationSection.GetRequiredSection(nameof(TokenGenerationOptions)); var tokenGenerationOptions = tokenGenerationOptionsSection.Get(); @@ -269,6 +304,11 @@ internal partial class ApiService : ApplicationService? Cors )] public List StaticFilePaths { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)] + [DefaultValue(false)] + public bool EnableSwaggerUI { get; set; } + #endregion } \ No newline at end of file diff --git a/Intersect.Server/Web/Net7/Constraints/AdminActionsConstraint.cs b/Intersect.Server/Web/Net7/Constraints/AdminActionsConstraint.cs deleted file mode 100644 index 0dc4917a0d..0000000000 --- a/Intersect.Server/Web/Net7/Constraints/AdminActionsConstraint.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Intersect.Enums; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace Intersect.Server.Web.Constraints; - -internal sealed partial class AdminActionsConstraint : IRouteConstraint -{ - /// - public bool Match( - HttpContext httpContext, - IRouter route, - string routeKey, - RouteValueDictionary values, - RouteDirection routeDirection - ) - { - if (!values.TryGetValue(routeKey, out var value) || value == null) - { - return false; - } - - var stringValue = value as string ?? Convert.ToString(value); - - return Enum.TryParse(stringValue, out _); - } -} \ No newline at end of file diff --git a/Intersect.Server/Web/Net7/Constraints/EnumConstraint.cs b/Intersect.Server/Web/Net7/Constraints/EnumConstraint.cs new file mode 100644 index 0000000000..808a8e25be --- /dev/null +++ b/Intersect.Server/Web/Net7/Constraints/EnumConstraint.cs @@ -0,0 +1,20 @@ +using Intersect.Enums; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; + +namespace Intersect.Server.Web.Constraints; + +public class EnumConstraint : IRouteConstraint where TEnum : struct, Enum +{ + public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, + RouteDirection routeDirection) + { + if (!values.TryGetValue(routeKey, out var value) || value == null) + { + return false; + } + + var stringValue = value as string ?? Convert.ToString(value); + return stringValue != null && Enum.TryParse(stringValue, out _); + } +} diff --git a/Intersect.Server/Web/Net7/Constraints/GameObjectTypeConstraint.cs b/Intersect.Server/Web/Net7/Constraints/GameObjectTypeConstraint.cs deleted file mode 100644 index 97138027ea..0000000000 --- a/Intersect.Server/Web/Net7/Constraints/GameObjectTypeConstraint.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Intersect.Enums; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace Intersect.Server.Web.Constraints; - -internal sealed partial class GameObjectTypeConstraint : IRouteConstraint -{ - /// - public bool Match( - HttpContext httpContext, - IRouter route, - string routeKey, - RouteValueDictionary values, - RouteDirection routeDirection - ) - { - if (!values.TryGetValue(routeKey, out var value) || value == null) - { - return false; - } - - var stringValue = value as string ?? Convert.ToString(value); - - return Enum.TryParse(stringValue, true, out _); - } -} \ No newline at end of file diff --git a/Intersect.Server/Web/Net7/Constraints/LookupKeyConstraint.cs b/Intersect.Server/Web/Net7/Constraints/NonNullConstraint.cs similarity index 88% rename from Intersect.Server/Web/Net7/Constraints/LookupKeyConstraint.cs rename to Intersect.Server/Web/Net7/Constraints/NonNullConstraint.cs index 729d6dbb95..eed1907854 100644 --- a/Intersect.Server/Web/Net7/Constraints/LookupKeyConstraint.cs +++ b/Intersect.Server/Web/Net7/Constraints/NonNullConstraint.cs @@ -3,7 +3,7 @@ namespace Intersect.Server.Web.Constraints; -public class LookupKeyConstraint : IRouteConstraint +public class NonNullConstraint : IRouteConstraint { public bool Match( HttpContext httpContext, diff --git a/Intersect.Server/Web/Net7/IntersectController.cs b/Intersect.Server/Web/Net7/IntersectController.cs index 3cd9d5c134..3ed946c60e 100644 --- a/Intersect.Server/Web/Net7/IntersectController.cs +++ b/Intersect.Server/Web/Net7/IntersectController.cs @@ -1,6 +1,7 @@ using System.Net; using System.Security.Claims; using Intersect.Security.Claims; +using Intersect.Server.Web.RestApi.Types; using Microsoft.AspNetCore.Mvc; using IntersectUser = Intersect.Server.Database.PlayerData.User; @@ -8,13 +9,9 @@ namespace Intersect.Server.Web; public abstract class IntersectController : Controller { - public const int PAGE_SIZE_MAX = 100; - - public const int PAGE_SIZE_MIN = 5; - private IntersectUser? _intersectUser; - public IntersectUser? IntersectUser + protected IntersectUser? IntersectUser { get { @@ -37,32 +34,43 @@ public IntersectUser? IntersectUser } [NonAction] - public virtual ObjectResult StatusCode(HttpStatusCode statusCode, object? data = default) - { - return StatusCode((int)statusCode, data); - } + protected virtual IActionResult StatusCodeMessage(HttpStatusCode statusCode, string message) => StatusCode( + (int)statusCode, + new StatusMessageResponseBody(message) + ); [NonAction] - public virtual ObjectResult Forbidden(object? data = default) - { - return StatusCode(HttpStatusCode.Forbidden, data); - } + protected virtual IActionResult Forbidden(string message) => StatusCodeMessage(HttpStatusCode.Forbidden, message); [NonAction] - public virtual ObjectResult Gone() - { - return StatusCode(HttpStatusCode.Gone); - } + protected virtual IActionResult InternalServerError(string message) => + StatusCodeMessage(HttpStatusCode.InternalServerError, message); [NonAction] - public virtual ObjectResult InternalServerError(object? data = default) - { - return StatusCode(HttpStatusCode.InternalServerError, data); - } + protected virtual IActionResult NotImplemented(string message) => + StatusCodeMessage(HttpStatusCode.NotImplemented, message); [NonAction] - public virtual ObjectResult NotImplemented(object? data = default) - { - return StatusCode(HttpStatusCode.NotImplemented, data); - } + protected virtual IActionResult BadRequest(string message) => StatusCodeMessage(HttpStatusCode.BadRequest, message); + + [NonAction] + protected virtual IActionResult NotFound(string message) => StatusCodeMessage(HttpStatusCode.NotFound, message); + + [NonAction] + protected virtual ObjectResult StatusCode(HttpStatusCode statusCode, object? data = default) => + StatusCode((int)statusCode, data); + + [NonAction] + protected virtual ObjectResult Forbidden(object? data = default) => StatusCode(HttpStatusCode.Forbidden, data); + + [NonAction] + protected virtual ObjectResult Gone() => StatusCode(HttpStatusCode.Gone); + + [NonAction] + protected virtual ObjectResult InternalServerError(object? data = default) => + StatusCode(HttpStatusCode.InternalServerError, data); + + [NonAction] + protected virtual ObjectResult NotImplemented(object? data = default) => + StatusCode(HttpStatusCode.NotImplemented, data); } \ No newline at end of file diff --git a/Intersect.Server/Web/Net7/Swagger/Filters/DictionarySchemaFilter.cs b/Intersect.Server/Web/Net7/Swagger/Filters/DictionarySchemaFilter.cs new file mode 100644 index 0000000000..63b8a13517 --- /dev/null +++ b/Intersect.Server/Web/Net7/Swagger/Filters/DictionarySchemaFilter.cs @@ -0,0 +1,59 @@ +using Intersect.Reflection; +using Microsoft.OpenApi; +using Microsoft.OpenApi.Extensions; +using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Writers; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Intersect.Server.Web.Swagger.Filters; + +public sealed class OpenApiKeyTypeExtension : IOpenApiExtension +{ + public const string Name = "x-key-type"; + + public required OpenApiSchema KeyType { get; init; } + + public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion) + { + ArgumentNullException.ThrowIfNull(writer, nameof(writer)); + + KeyType.Reference.Serialize(writer, specVersion); + } + + public static bool Add(IDictionary extensions, OpenApiSchema keyType) => + extensions.TryAdd( + Name, + new OpenApiKeyTypeExtension + { + KeyType = keyType, + } + ); +} + +public sealed class DictionarySchemaFilter : ISchemaFilter +{ + public void Apply(OpenApiSchema schema, SchemaFilterContext context) + { + if (!typeof(IReadOnlyDictionary<,>).ExtendedBy(context.Type)) + { + return; + } + + var generator = context.SchemaGenerator; + var repository = context.SchemaRepository; + + var keyType = context.Type.GetGenericArguments().First(); + if (!keyType.IsEnum) + { + return; + } + + if (!repository.TryLookupByType(keyType, out var keyTypeSchema)) + { + keyTypeSchema = generator.GenerateSchema(keyType, repository); + } + + _ = OpenApiKeyTypeExtension.Add(schema.Extensions, keyTypeSchema); + } +} \ No newline at end of file diff --git a/Intersect.Server/Web/Net7/Swagger/Filters/GameObjectTypeSchemaFilter.cs b/Intersect.Server/Web/Net7/Swagger/Filters/GameObjectTypeSchemaFilter.cs new file mode 100644 index 0000000000..c85caf73ac --- /dev/null +++ b/Intersect.Server/Web/Net7/Swagger/Filters/GameObjectTypeSchemaFilter.cs @@ -0,0 +1,126 @@ +using System.Reflection; +using Intersect.Enums; +using Intersect.Framework.Reflection; +using Intersect.Models; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Intersect.Server.Web.Swagger.Filters; + +public sealed class GameObjectTypeSchemaFilter : ISchemaFilter +{ + public void Apply(OpenApiSchema schema, SchemaFilterContext context) + { + var generator = context.SchemaGenerator; + var repository = context.SchemaRepository; + + var contextType = context.Type; + if (contextType == typeof(IDatabaseObject)) + { + schema.Discriminator = new OpenApiDiscriminator + { + PropertyName = nameof(IDatabaseObject.Type), + Mapping = Enum.GetValues().ToDictionary( + gameObjectType => gameObjectType.ToString(), + gameObjectType => gameObjectType.GetObjectType().Name + ), + }; + + foreach (var gameObjectType in Enum.GetValues()) + { + var subtype = gameObjectType.GetObjectType(); + schema.Discriminator.Mapping[gameObjectType.ToString()] = subtype.Name; + if (!repository.TryLookupByType(subtype, out _)) + { + _ = generator.GenerateSchema(subtype, repository); + } + } + } + + if (!contextType.Extends() && !contextType.ExtendedBy() && + contextType != typeof(IDatabaseObject) && contextType != typeof(IFolderable)) + { + + return; + } + + List references = []; + var baseType = contextType.BaseType; + if (baseType != default) + { + if (contextType.IsGenericType) + { + var genericTypeDefinition = contextType.GetGenericTypeDefinition(); + if (genericTypeDefinition != contextType) + { + baseType = genericTypeDefinition; + } + } + + if (baseType is { IsGenericType: true, IsGenericTypeDefinition: false }) + { + baseType = baseType.GetGenericTypeDefinition(); + } + + if (baseType != typeof(object)) + { + if (!repository.TryLookupByType(baseType, out var baseTypeSchema)) + { + baseTypeSchema = generator.GenerateSchema(baseType, repository); + } + + references.Add(baseTypeSchema.Reference); + } + } + + foreach (var interfaceType in contextType.GetUniqueInterfaces()) + { + if (!repository.TryLookupByType(interfaceType, out _)) + { + _ = generator.GenerateSchema(interfaceType, repository); + } + + references.Add(new OpenApiReference + { + Id = interfaceType.Name, + Type = ReferenceType.Schema, + }); + } + + var nonInheritedProperties = schema.Properties.Where( + kvp => + { + var (propertyName, _) = kvp; + if (!contextType.TryFindProperty(propertyName, out var propertyInfo)) + { + throw new InvalidOperationException( + $"Missing {nameof(PropertyInfo)} for {contextType.GetName(qualified: true)}.{propertyName}" + ); + } + return contextType.IsOwnProperty(propertyInfo); + } + ).ToDictionary(); + + if (references.Count <= 0) + { + return; + } + + schema.AllOf = + [ + ..references.Select(reference => new OpenApiSchema { Reference = reference, }), + ]; + + if (nonInheritedProperties.Count > 0) + { + schema.AllOf.Add( + new OpenApiSchema + { + Type = "object", Properties = nonInheritedProperties, + } + ); + } + + schema.Properties = new Dictionary(); + } +} \ No newline at end of file diff --git a/Intersect.Server/Web/Net7/Swagger/Filters/LookupKeySchemaFilter.cs b/Intersect.Server/Web/Net7/Swagger/Filters/LookupKeySchemaFilter.cs new file mode 100644 index 0000000000..ac675d91b9 --- /dev/null +++ b/Intersect.Server/Web/Net7/Swagger/Filters/LookupKeySchemaFilter.cs @@ -0,0 +1,68 @@ +using Intersect.Server.Localization; +using Intersect.Server.Web.RestApi.Payloads; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Intersect.Server.Web.Swagger.Filters; + +public sealed class LookupKeySchemaFilter : ISchemaFilter +{ + public void Apply(OpenApiSchema schema, SchemaFilterContext context) + { + if (context.Type != typeof(LookupKey)) + { + return; + } + + schema.Type = "string"; + schema.Description = "An id or a name"; + + // TODO: Multiple examples? It doesn't look like the C# API exposes this but it exists according to the Swagger documentation + // Example = new OpenApiString($"test or {new Guid("01234567-89ab-cdef-0123-456789abcdef")}"), + schema.Example = new OpenApiString("test"); + + schema.OneOf = + [ + new OpenApiSchema + { + Type = "string", Format = "uuid", + }, + new OpenApiSchema + { + Type = "string", Format = "username", Pattern = Strings.Regex.Username, + }, + ]; + + schema.Properties = new Dictionary(); + + // var foundId = false; + // var foundName = false; + // foreach (var oneOfSchema in schema.OneOf) + // { + // if (oneOfSchema.Type != "string") + // { + // continue; + // } + // + // if (oneOfSchema.Format == "uuid") + // { + // foundId = true; + // } + // else if (string.IsNullOrWhiteSpace(oneOfSchema.Format)) + // { + // foundName = true; + // } + // } + // + // if (!foundId) + // { + // schema.OneOf.Add(new OpenApiSchema { Type = "string", Format = "uuid" }); + // } + // + // if (!foundName) + // { + // schema.OneOf.Add(new OpenApiSchema { Type = "string", Format = "username"}); + // } + } +} \ No newline at end of file diff --git a/Intersect.Server/Web/Net7/Swagger/Filters/PolymorphicDocumentFilter.cs b/Intersect.Server/Web/Net7/Swagger/Filters/PolymorphicDocumentFilter.cs new file mode 100644 index 0000000000..fb36a6e9b4 --- /dev/null +++ b/Intersect.Server/Web/Net7/Swagger/Filters/PolymorphicDocumentFilter.cs @@ -0,0 +1,89 @@ +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Intersect.Server.Web.Swagger.Filters; + +public sealed class PolymorphicDocumentFilter : PolymorphicDocumentFilter where TBase : class +{ + public PolymorphicDocumentFilter(string discriminatorPropertyName) : base(discriminatorPropertyName) + { + } +} + +public class PolymorphicDocumentFilter : IDocumentFilter where TBase : class +{ + private readonly string _discriminatorPropertyName; + private readonly Dictionary _subtypes = []; + + public PolymorphicDocumentFilter(string discriminatorPropertyName) + { + _discriminatorPropertyName = discriminatorPropertyName; + } + + public PolymorphicDocumentFilter WithSubtype(TDiscriminator discriminatorValue) + where TSubtype : TBase + { + if (typeof(TSubtype).IsAbstract || typeof(TSubtype).IsGenericTypeDefinition) + { + throw new ArgumentException( + $"Invalid subtype '{typeof(TSubtype).FullName ?? typeof(TSubtype).Name}'", + nameof(TSubtype) + ); + } + + var discriminatorValueString = discriminatorValue?.ToString(); + if (discriminatorValueString == null) + { + throw new ArgumentException("Discriminator value resolved to null string", nameof(discriminatorValue)); + } + + _subtypes.Add(discriminatorValueString, typeof(TSubtype)); + return this; + } + + public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) + { + var schemaGenerator = context.SchemaGenerator; + var schemaRepository = context.SchemaRepository; + + var baseType = typeof(TBase); + if (!schemaRepository.TryLookupByType(baseType, out var schema)) + { + schema = schemaGenerator.GenerateSchema(baseType, schemaRepository); + } + + schema.Discriminator ??= new OpenApiDiscriminator + { + PropertyName = _discriminatorPropertyName, + }; + + schema.Required.Add(schema.Discriminator.PropertyName); + if (!schema.Properties.ContainsKey(_discriminatorPropertyName)) + { + schema.Properties.Add( + _discriminatorPropertyName, + new OpenApiSchema + { + Type = "string", + } + ); + } + + foreach (var (subtypeDiscriminatorValue, subtype) in _subtypes) + { + if (!schemaRepository.TryLookupByType(subtype, out var subtypeSchema)) + { + subtypeSchema = schemaGenerator.GenerateSchema(subtype, schemaRepository); + } + + if (!schema.Discriminator.Mapping.TryAdd(subtypeDiscriminatorValue, subtypeSchema.Reference.ReferenceV3)) + { + throw new InvalidOperationException( + $"Duplicate subtype discriminator value: '{subtypeDiscriminatorValue}'" + ); + } + } + } + + public PolymorphicSchemaFilter CreateSchemaFilter() => new(_discriminatorPropertyName, _subtypes.Values); +} \ No newline at end of file diff --git a/Intersect.Server/Web/Net7/Swagger/Filters/PolymorphicSchemaFilter.cs b/Intersect.Server/Web/Net7/Swagger/Filters/PolymorphicSchemaFilter.cs new file mode 100644 index 0000000000..a02527558c --- /dev/null +++ b/Intersect.Server/Web/Net7/Swagger/Filters/PolymorphicSchemaFilter.cs @@ -0,0 +1,69 @@ +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Intersect.Server.Web.Swagger.Filters; + +public class PolymorphicSchemaFilter : ISchemaFilter where TBase : class +{ + private readonly string _discriminatorPropertyName; + private readonly HashSet _subtypes = []; + + public PolymorphicSchemaFilter(string discriminatorPropertyName, IEnumerable subtypes) + { + _discriminatorPropertyName = discriminatorPropertyName; + foreach (var subtype in subtypes) + { + if (subtype.IsAbstract || subtype.IsGenericTypeDefinition || !subtype.IsAssignableTo(typeof(TBase))) + { + throw new ArgumentException( + $"Tried to create schema filter for {typeof(TBase).FullName} with {subtype.FullName ?? subtype.Name} which is not a valid subtype." + ); + } + + _subtypes.Add(subtype); + } + } + + public void Apply(OpenApiSchema schema, SchemaFilterContext context) + { + if (!_subtypes.Contains(context.Type)) + { + return; + } + + if (schema.AllOf.Count > 0) + { + var foundDiscriminator = schema.AllOf.Any( + aos => aos.Reference == default && aos.Properties.Remove(_discriminatorPropertyName) + ); + + if (foundDiscriminator) + { + return; + } + + throw new InvalidOperationException("No discriminator found?"); + } + + var clonedSchema = new OpenApiSchema + { + Properties = schema.Properties.Where(kvp => kvp.Key != _discriminatorPropertyName).ToDictionary(), + Required = schema.Required, + Type = schema.Type, + }; + + if (context.SchemaRepository.TryLookupByType(typeof(TBase), out var baseSchema)) + { + schema.AllOf = new List + { + new() + { + Reference = new OpenApiReference(baseSchema.Reference), + }, + clonedSchema, + }; + } + + schema.Properties = new Dictionary(); + } +} \ No newline at end of file diff --git a/Intersect.Server/Web/RestApi/Payloads/PagingInfo.cs b/Intersect.Server/Web/RestApi/Payloads/PagingInfo.cs index 82bfa46807..11cd12668f 100644 --- a/Intersect.Server/Web/RestApi/Payloads/PagingInfo.cs +++ b/Intersect.Server/Web/RestApi/Payloads/PagingInfo.cs @@ -1,9 +1,13 @@ -namespace Intersect.Server.Web.RestApi.Payloads +namespace Intersect.Server.Web.RestApi.Payloads; + +// TODO: Struct, right now there's an exception when it's a struct and I'm not sure how to fix it +public class PagingInfo { - public partial class PagingInfo - { - public int Page { get; set; } + public const int MaxPageSize = 100; + public const int MinPageSize = 5; + public const int DefaultPageSize = 10; + + public int Page { get; set; } = 0; - public int Count { get; set; } = 10; - } -} + public int PageSize { get; set; } = DefaultPageSize; +} \ No newline at end of file diff --git a/Intersect.Server/Web/RestApi/Payloads/VariableValueAPI.cs b/Intersect.Server/Web/RestApi/Payloads/VariableValueBody.cs similarity index 72% rename from Intersect.Server/Web/RestApi/Payloads/VariableValueAPI.cs rename to Intersect.Server/Web/RestApi/Payloads/VariableValueBody.cs index d659409be7..e90c201524 100644 --- a/Intersect.Server/Web/RestApi/Payloads/VariableValueAPI.cs +++ b/Intersect.Server/Web/RestApi/Payloads/VariableValueBody.cs @@ -1,7 +1,7 @@ namespace Intersect.Server.Web.RestApi.Payloads { - public partial struct VariableValueAPI + public partial struct VariableValueBody { public dynamic Value { get; set; } diff --git a/Intersect.Server/Web/RestApi/Routes/OAuthController.cs b/Intersect.Server/Web/RestApi/Routes/OAuthController.cs index 85c9fdb749..54bc3ba0b0 100644 --- a/Intersect.Server/Web/RestApi/Routes/OAuthController.cs +++ b/Intersect.Server/Web/RestApi/Routes/OAuthController.cs @@ -13,6 +13,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; +using Swashbuckle.AspNetCore.Annotations; using JsonConverter = Newtonsoft.Json.JsonConverter; namespace Intersect.Server.Web.RestApi.Routes @@ -99,6 +100,7 @@ JsonSerializer serializer } [Newtonsoft.Json.JsonConverter(typeof(GrantTypeConverter))] + [System.Text.Json.Serialization.JsonConverter(typeof(JsonStringEnumConverter))] public enum GrantType { Password, @@ -161,16 +163,18 @@ protected override JsonConverter ResolveContractConverter(Type objectType) } } + [SwaggerDiscriminator("grant_type")] + [SwaggerSubType(typeof(TokenRequestPasswordGrant), DiscriminatorValue = "password")] + [SwaggerSubType(typeof(TokenRequestRefreshTokenGrant), DiscriminatorValue = "refresh_token")] [Newtonsoft.Json.JsonConverter(typeof(TokenRequestConverter))] public abstract class TokenRequest { - [Newtonsoft.Json.JsonIgnore] + [JsonProperty("grant_type")] public abstract GrantType GrantType { get; } } public class TokenRequestPasswordGrant : TokenRequest { - [Newtonsoft.Json.JsonIgnore] public override GrantType GrantType => GrantType.Password; [JsonProperty("username")] @@ -189,13 +193,14 @@ public class TokenRequestRefreshTokenGrant : TokenRequest } [HttpPost("token")] + [Consumes(typeof(TokenRequest), "application/json")] public async Task RequestToken([FromBody] TokenRequest tokenRequest) { return tokenRequest switch { TokenRequestPasswordGrant passwordGrant => await RequestTokenFrom(passwordGrant), TokenRequestRefreshTokenGrant refreshTokenGrant => await RequestTokenFrom(refreshTokenGrant), - _ => BadRequest() + _ => BadRequest(), }; } diff --git a/Intersect.Server/Web/RestApi/Routes/V1/GameObjectController.cs b/Intersect.Server/Web/RestApi/Routes/V1/GameObjectController.cs index e3968d42ad..13ae372504 100644 --- a/Intersect.Server/Web/RestApi/Routes/V1/GameObjectController.cs +++ b/Intersect.Server/Web/RestApi/Routes/V1/GameObjectController.cs @@ -1,7 +1,10 @@ -using Intersect.Enums; +using System.Net; +using Intersect.Enums; using Intersect.GameObjects; using Intersect.GameObjects.Events; +using Intersect.Models; using Intersect.Server.Web.RestApi.Payloads; +using Intersect.Server.Web.RestApi.Types; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -12,77 +15,88 @@ namespace Intersect.Server.Web.RestApi.Routes.V1 public sealed partial class GameObjectController : IntersectController { [HttpGet("{gameObjectType}")] - public object List(GameObjectType gameObjectType, [FromQuery] PagingInfo pageInfo) + [ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest, "application/json")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound, "application/json")] + [ProducesResponseType(typeof(DataPage), (int)HttpStatusCode.OK, "application/json")] + public IActionResult List(GameObjectType gameObjectType, [FromQuery] PagingInfo pageInfo) { pageInfo.Page = Math.Max(pageInfo.Page, 0); - pageInfo.Count = Math.Max(Math.Min(pageInfo.Count, 100), 5); + pageInfo.PageSize = Math.Max(Math.Min(pageInfo.PageSize, 100), 5); - var lookup = GameObjectTypeExtensions.GetLookup(gameObjectType); - - if (lookup != null) + if (!gameObjectType.TryGetLookup(out var lookup)) { - var entries = gameObjectType == GameObjectType.Event - ? lookup.Where(obj => ((EventBase) obj.Value).CommonEvent) - .OrderBy(obj => obj.Value.Name) - .Skip(pageInfo.Page * pageInfo.Count) - .Take(pageInfo.Count) - : lookup.OrderBy(obj => obj.Value.Name) - .Skip(pageInfo.Page * pageInfo.Count) - .Take(pageInfo.Count); - - return new - { - total = gameObjectType == GameObjectType.Event - ? lookup.Where(obj => ((EventBase) obj.Value).CommonEvent).Count() - : lookup.Count(), - pageInfo.Page, - count = entries.Count(), - entries - }; + return BadRequest($"Invalid {nameof(GameObjectType)} '{gameObjectType}'"); } - return BadRequest(); + var baseEnumerable = gameObjectType == GameObjectType.Event + ? lookup.Where(obj => ((EventBase)obj.Value).CommonEvent).Select(pair => pair.Value) + : lookup.Values; + + var entries = baseEnumerable.OrderBy(obj => obj.Name) + .Skip(pageInfo.Page * pageInfo.PageSize) + .Take(pageInfo.PageSize) + .ToArray(); + + var total = gameObjectType == GameObjectType.Event + ? lookup.Count(obj => ((EventBase)obj.Value).CommonEvent) + : lookup.Count; + return Ok( + new DataPage( + Total: total, + Page: pageInfo.Page, + PageSize: pageInfo.PageSize, + Count: entries.Length, + Values: entries + ) + ); } [HttpGet("{gameObjectType}/names")] - public object Names(GameObjectType gameObjectType) + public IActionResult Names(GameObjectType gameObjectType) { - var lookup = GameObjectTypeExtensions.GetLookup(gameObjectType); - - if (lookup != null) + if (!gameObjectType.TryGetLookup(out var lookup)) { - var entries = gameObjectType == GameObjectType.Event - ? lookup.Where(obj => ((EventBase)obj.Value).CommonEvent).Select(t => new { t.Key, t.Value.Name }).ToDictionary(t => t.Key, t => t.Name) - : lookup.Select(t => new { t.Key, t.Value.Name }).ToDictionary(t => t.Key, t => t.Name); + return BadRequest($"Invalid {nameof(GameObjectType)} '{gameObjectType}'"); + } - return entries; + var descriptors = lookup.Values.AsEnumerable(); + if (gameObjectType == GameObjectType.Event) + { + descriptors = descriptors.OfType().Where(descriptor => descriptor.CommonEvent); } - return BadRequest(); + var descriptorNames = descriptors.Select(descriptor => descriptor.Name).ToArray(); + + return Ok( + new DataPage( + Total: descriptorNames.Length, + Page: 0, + PageSize: descriptorNames.Length, + Count: descriptorNames.Length, + descriptorNames + ) + ); } - [HttpGet("{gameObjectType}/{objId:guid}")] - public object GameObjectById(GameObjectType gameObjectType, Guid objId) + [HttpGet("{gameObjectType}/{objectId:guid}")] + public object GameObjectById(GameObjectType gameObjectType, Guid objectId) { - if (objId == Guid.Empty) + if (objectId == default) { - return BadRequest(@"Object not found!"); + return BadRequest($@"Invalid id '{objectId}'"); } - var obj = GameObjectTypeExtensions.GetLookup(gameObjectType)?.Get(objId); - - if (obj != null) + if (!gameObjectType.TryGetLookup(out var lookup)) { - return obj; + return BadRequest($"Invalid {nameof(GameObjectType)} '{gameObjectType}'"); } - return NotFound(@"Object not found!"); + return !lookup.TryGetValue(objectId, out var gameObject) + ? NotFound($"No {gameObjectType} with id '{objectId}'") + : Ok(gameObject); } [HttpGet("time")] - public object Time() - { - return TimeBase.GetTimeBase(); - } + public IActionResult Time() => Ok(TimeBase.GetTimeBase()); } } diff --git a/Intersect.Server/Web/RestApi/Routes/V1/GuildController.cs b/Intersect.Server/Web/RestApi/Routes/V1/GuildController.cs index ea4d48b6c2..9f4f5ac5d1 100644 --- a/Intersect.Server/Web/RestApi/Routes/V1/GuildController.cs +++ b/Intersect.Server/Web/RestApi/Routes/V1/GuildController.cs @@ -1,4 +1,6 @@ -using Intersect.GameObjects; +using System.Net; +using Intersect.Enums; +using Intersect.GameObjects; using Intersect.Server.Database.PlayerData.Players; using Intersect.Server.Entities; using Intersect.Server.Localization; @@ -18,31 +20,31 @@ public sealed partial class GuildController : IntersectController public object ListPost([FromBody] PagingInfo pageInfo) { pageInfo.Page = Math.Max(pageInfo.Page, 0); - pageInfo.Count = Math.Max(Math.Min(pageInfo.Count, 100), 5); + pageInfo.PageSize = Math.Max(Math.Min(pageInfo.PageSize, 100), 5); - var entries = Guild.List(null, null, SortDirection.Ascending, pageInfo.Page * pageInfo.Count, pageInfo.Count, out int entryTotal); + var entries = Guild.List(null, null, SortDirection.Ascending, pageInfo.Page * pageInfo.PageSize, pageInfo.PageSize, out int entryTotal); - return new - { - total = entryTotal, - pageInfo.Page, - count = entries.Count, - entries - }; + return Ok(new DataPage>( + Total: entryTotal, + Page: pageInfo.Page, + PageSize: pageInfo.PageSize, + Count: entries.Count, + Values: entries + )); } [HttpGet] public DataPage> List( [FromQuery] int page = 0, [FromQuery] int pageSize = 0, - [FromQuery] int limit = PAGE_SIZE_MAX, + [FromQuery] int limit = PagingInfo.MaxPageSize, [FromQuery] string sortBy = null, [FromQuery] SortDirection sortDirection = SortDirection.Ascending, [FromQuery] string search = null ) { page = Math.Max(page, 0); - pageSize = Math.Max(Math.Min(pageSize, 100), 5); + pageSize = Math.Max(Math.Min(pageSize, PagingInfo.MaxPageSize), PagingInfo.MinPageSize); limit = Math.Max(Math.Min(limit, pageSize), 1); var values = Guild.List(search?.Length > 2 ? search : null, sortBy, sortDirection, page * pageSize, pageSize, out int total); @@ -52,14 +54,13 @@ public DataPage> List( values = values.Take(limit).ToList(); } - return new DataPage> - { - Total = total, - Page = page, - PageSize = pageSize, - Count = values.Count, - Values = values - }; + return new DataPage>( + Total: total, + Page: page, + PageSize: pageSize, + Count: values.Count, + Values: values + ); } [HttpGet("{guildId:guid}")] @@ -117,14 +118,14 @@ public DataPage Members( Guid guildId, [FromQuery] int page = 0, [FromQuery] int pageSize = 0, - [FromQuery] int limit = PAGE_SIZE_MAX, + [FromQuery] int limit = PagingInfo.MaxPageSize, [FromQuery] string sortBy = null, [FromQuery] SortDirection sortDirection = SortDirection.Ascending, [FromQuery] string search = null ) { page = Math.Max(page, 0); - pageSize = Math.Max(Math.Min(pageSize, 100), 5); + pageSize = Math.Max(Math.Min(pageSize, PagingInfo.MaxPageSize), PagingInfo.MinPageSize); limit = Math.Max(Math.Min(limit, pageSize), 1); var values = Player.List(search?.Length > 2 ? search : null, sortBy, sortDirection, page * pageSize, pageSize, out int total, guildId); @@ -134,26 +135,13 @@ public DataPage Members( values = values.Take(limit).ToList(); } - return new DataPage - { - Total = total, - Page = page, - PageSize = pageSize, - Count = values.Count, - Values = values - }; - } - - [HttpGet("{guildId:guid}/bank")] - public object ItemsListBank(Guid guildId) - { - var guild = Guild.LoadGuild(guildId); - if (guild == null) - { - return NotFound($@"No guild with id '{guildId}'."); - } - - return guild.Bank; + return new DataPage( + Total: total, + Page: page, + PageSize: pageSize, + Count: values.Count, + Values: values + ); } [HttpPost("{guildId:guid}/kick/{lookupKey:LookupKey}")] @@ -166,7 +154,7 @@ public object Kick(Guid guildId, LookupKey lookupKey) var guild = Guild.LoadGuild(guildId); - + if (guild == null) { return BadRequest($@"Guild does not exist."); @@ -185,7 +173,7 @@ public object Kick(Guid guildId, LookupKey lookupKey) { return BadRequest($@"{player.Name} is not a member of {guild.Name}."); } - + //Cannot kick the owner! if (player.GuildRank == 0) { @@ -252,7 +240,7 @@ public object Transfer(Guid guildId, LookupKey lookupKey) if (guild == null) { - return BadRequest($@"Guild does not exist."); + return NotFound($@"No guild found with id {guildId}"); } var (_, player) = Player.Fetch(lookupKey); @@ -278,92 +266,131 @@ public object Transfer(Guid guildId, LookupKey lookupKey) return player; } - [HttpGet("{guildId:guid}/variables")] - public object GuildVariablesGet(Guid guildId) + [HttpGet("{guildId:guid}/bank")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound, "application/json")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK, "application/json")] + public IActionResult ItemsListBank(Guid guildId) { var guild = Guild.LoadGuild(guildId); - if (guild == null) { - return BadRequest($@"Guild does not exist."); + return NotFound($@"No guild found with id {guildId}"); + } + + return Ok(guild.Bank.Where(slot => !slot.IsEmpty)); + } + + [HttpGet("{guildId:guid}/variables")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound, "application/json")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK, "application/json")] + public IActionResult GuildVariablesList(Guid guildId) + { + if (!Guild.TryGet(guildId, out var guild)) + { + return NotFound($@"No guild found with id {guildId}"); } - return guild.Variables; + return Ok(guild.Variables); } [HttpGet("{guildId:guid}/variables/{variableId:guid}")] - public object GuildVariableGet(Guid guildId, Guid variableId) + [ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest, "application/json")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound, "application/json")] + [ProducesResponseType(typeof(GuildVariable), (int)HttpStatusCode.OK, "application/json")] + public IActionResult GuildVariableGet(Guid guildId, Guid variableId) { - var guild = Guild.LoadGuild(guildId); + if (!Guild.TryGet(guildId, out var guild)) + { + return NotFound($@"No guild found with id {guildId}"); + } - if (guild == null) + if (variableId == Guid.Empty) { - return BadRequest($@"Guild does not exist."); + return BadRequest($@"Variable id cannot be {variableId}"); } - if (variableId == Guid.Empty || GuildVariableBase.Get(variableId) == null) + if (!GuildVariableBase.TryGet(variableId, out var variableDescriptor)) { - return BadRequest($@"Invalid variable id ${variableId}."); + return NotFound($@"Variable not found for id {variableId}"); } - return guild.GetVariable(variableId, true); + var variable = guild.GetVariable(variableDescriptor.Id, true); + return Ok(variable); } [HttpGet("{guildId:guid}/variables/{variableId:guid}/value")] - public object GuildVariableGetValue(Guid guildId, Guid variableId) + [ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest, "application/json")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound, "application/json")] + [ProducesResponseType(typeof(VariableValueBody), (int)HttpStatusCode.OK, "application/json")] + public IActionResult GuildVariableGetValue(Guid guildId, Guid variableId) { - var guild = Guild.LoadGuild(guildId); + if (!Guild.TryGet(guildId, out var guild)) + { + return NotFound($@"No guild found with id {guildId}"); + } - if (guild == null) + if (variableId == Guid.Empty) { - return BadRequest($@"Guild does not exist."); + return BadRequest($@"Variable id cannot be {variableId}"); } - if (variableId == Guid.Empty || GuildVariableBase.Get(variableId) == null) + if (!GuildVariableBase.TryGet(variableId, out var variableDescriptor)) { - return BadRequest($@"Invalid variable id ${variableId}."); + return NotFound($@"Variable not found for id {variableId}"); } - return new + var variable = guild.GetVariable(variableDescriptor.Id, true); + return Ok(new VariableValueBody { - value = guild.GetVariable(variableId, true).Value.Value, - }; + Value = variable.Value.Value, + }); } [HttpPost("{guildId:guid}/variables/{variableId:guid}")] - public object GuildVariableSet(Guid guildId, Guid variableId, [FromBody] VariableValueAPI valueApi) + [ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest, "application/json")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound, "application/json")] + [ProducesResponseType(typeof(GuildVariable), (int)HttpStatusCode.OK, "application/json")] + public IActionResult GuildVariableSet(Guid guildId, Guid variableId, [FromBody] VariableValueBody valueBody) { - var guild = Guild.LoadGuild(guildId); + if (!Guild.TryGet(guildId, out var guild)) + { + return NotFound($@"No guild found with id {guildId}"); + } - if (guild == null) + if (variableId == Guid.Empty) { - return BadRequest($@"Guild does not exist."); + return BadRequest($@"Variable id cannot be {variableId}"); } - if (variableId == Guid.Empty || GuildVariableBase.Get(variableId) == null) + if (!GuildVariableBase.TryGet(variableId, out var variableDescriptor)) { - return BadRequest($@"Invalid variable id ${variableId}."); + return NotFound($@"Variable not found for id {variableId}"); } - var variable = guild.GetVariable(variableId, true); + var variable = guild.GetVariable(variableDescriptor.Id, true); - var changed = true; + var changed = false; if (variable?.Value != null) { - if (variable?.Value?.Value != valueApi.Value) + if (variable.Value.Value != valueBody.Value) { - changed = false; + variable.Value.Value = valueBody.Value; + changed = true; } - variable.Value.Value = valueApi.Value; } + // ReSharper disable once InvertIf if (changed) { - guild.StartCommonEventsWithTriggerForAll(Enums.CommonEventTrigger.GuildVariableChange, "", variableId.ToString()); - guild.UpdatedVariables.AddOrUpdate(variableId, GuildVariableBase.Get(variableId), (key, oldValue) => GuildVariableBase.Get(variableId)); + guild.StartCommonEventsWithTriggerForAll(CommonEventTrigger.GuildVariableChange, string.Empty, variableId.ToString()); + guild.UpdatedVariables.AddOrUpdate( + variableId, + variableDescriptor, + (_, _) => variableDescriptor + ); } - return variable; + return Ok(variable); } } } diff --git a/Intersect.Server/Web/RestApi/Routes/V1/LogsController.cs b/Intersect.Server/Web/RestApi/Routes/V1/LogsController.cs index 80cc0d9290..a4e0f46939 100644 --- a/Intersect.Server/Web/RestApi/Routes/V1/LogsController.cs +++ b/Intersect.Server/Web/RestApi/Routes/V1/LogsController.cs @@ -18,7 +18,7 @@ public sealed partial class LogsController : IntersectController public DataPage ListChat( [FromQuery] int page = 0, [FromQuery] int pageSize = 0, - [FromQuery] int limit = PAGE_SIZE_MAX, + [FromQuery] int limit = PagingInfo.MaxPageSize, [FromQuery] int messageType = -1, [FromQuery] Guid userId = default, [FromQuery] Guid playerId = default, @@ -28,7 +28,7 @@ public DataPage ListChat( ) { page = Math.Max(page, 0); - pageSize = Math.Max(Math.Min(pageSize, 100), 5); + pageSize = Math.Max(Math.Min(pageSize, PagingInfo.MaxPageSize), PagingInfo.MinPageSize); limit = Math.Max(Math.Min(limit, pageSize), 1); using (var context = DbInterface.CreateLoggingContext()) @@ -85,14 +85,13 @@ public DataPage ListChat( values = values.Take(limit).ToList(); } - return new DataPage - { - Total = messages.Count(), - Page = page, - PageSize = pageSize, - Count = values.Count, - Values = values - }; + return new DataPage( + Total: messages.Count(), + Page: page, + PageSize: pageSize, + Count: values.Count, + Values: values + ); } } @@ -102,13 +101,13 @@ public DataPage ListPMs( [FromQuery] Guid player2Id, [FromQuery] int page = 0, [FromQuery] int pageSize = 0, - [FromQuery] int limit = PAGE_SIZE_MAX, + [FromQuery] int limit = PagingInfo.MaxPageSize, [FromQuery] string search = null, [FromQuery] SortDirection sortDirection = SortDirection.Ascending ) { page = Math.Max(page, 0); - pageSize = Math.Max(Math.Min(pageSize, 100), 5); + pageSize = Math.Max(Math.Min(pageSize, PagingInfo.MaxPageSize), PagingInfo.MinPageSize); limit = Math.Max(Math.Min(limit, pageSize), 1); using (var context = DbInterface.CreateLoggingContext()) @@ -142,14 +141,13 @@ public DataPage ListPMs( values = values.Take(limit).ToList(); } - return new DataPage - { - Total = messages.Count(), - Page = page, - PageSize = pageSize, - Count = values.Count, - Values = values - }; + return new DataPage( + Total: messages.Count(), + Page: page, + PageSize: pageSize, + Count: values.Count, + Values: values + ); } } @@ -193,11 +191,11 @@ public DataPage ListIpHistory( Guid userId, [FromQuery] int page = 0, [FromQuery] int pageSize = 0, - [FromQuery] int limit = PAGE_SIZE_MAX + [FromQuery] int limit = PagingInfo.MaxPageSize ) { page = Math.Max(page, 0); - pageSize = Math.Max(Math.Min(pageSize, 100), 5); + pageSize = Math.Max(Math.Min(pageSize, PagingInfo.MaxPageSize), PagingInfo.MinPageSize); limit = Math.Max(Math.Min(limit, pageSize), 1); using (var context = DbInterface.CreateLoggingContext()) @@ -223,14 +221,13 @@ public DataPage ListIpHistory( } } - return new DataPage() - { - Total = ipAddresses.Count(), - Page = page, - PageSize = pageSize, - Count = addresses.Count, - Values = addresses - }; + return new DataPage( + Total: ipAddresses.Count(), + Page: page, + PageSize: pageSize, + Count: addresses.Count, + Values: addresses + ); } } @@ -241,12 +238,12 @@ public DataPage ListUserActivity( Guid userId, [FromQuery] int page = 0, [FromQuery] int pageSize = 0, - [FromQuery] int limit = PAGE_SIZE_MAX, + [FromQuery] int limit = PagingInfo.MaxPageSize, [FromQuery] SortDirection sortDirection = SortDirection.Ascending ) { page = Math.Max(page, 0); - pageSize = Math.Max(Math.Min(pageSize, 100), 5); + pageSize = Math.Max(Math.Min(pageSize, PagingInfo.MaxPageSize), PagingInfo.MinPageSize); limit = Math.Max(Math.Min(limit, pageSize), 1); using (var context = DbInterface.CreateLoggingContext()) @@ -264,14 +261,13 @@ public DataPage ListUserActivity( var values = activity.Skip(page * pageSize).Take(pageSize).ToList(); - return new DataPage() - { - Total = activity.Count(), - Page = page, - PageSize = pageSize, - Count = values.Count, - Values = values - }; + return new DataPage( + Total: activity.Count(), + Page: page, + PageSize: pageSize, + Count: values.Count, + Values: values + ); } @@ -282,12 +278,12 @@ public DataPage ListPlayerActivity( Guid playerId, [FromQuery] int page = 0, [FromQuery] int pageSize = 0, - [FromQuery] int limit = PAGE_SIZE_MAX, + [FromQuery] int limit = PagingInfo.MaxPageSize, [FromQuery] SortDirection sortDirection = SortDirection.Ascending ) { page = Math.Max(page, 0); - pageSize = Math.Max(Math.Min(pageSize, 100), 5); + pageSize = Math.Max(Math.Min(pageSize, PagingInfo.MaxPageSize), PagingInfo.MinPageSize); limit = Math.Max(Math.Min(limit, pageSize), 1); using (var context = DbInterface.CreateLoggingContext()) @@ -305,14 +301,13 @@ public DataPage ListPlayerActivity( var values = activity.Skip(page * pageSize).Take(pageSize).ToList(); - return new DataPage() - { - Total = activity.Count(), - Page = page, - PageSize = pageSize, - Count = values.Count, - Values = values - }; + return new DataPage( + Total: activity.Count(), + Page: page, + PageSize: pageSize, + Count: values.Count, + Values: values + ); } @@ -322,14 +317,14 @@ public DataPage ListPlayerActivity( public DataPage ListTrades( [FromQuery] int page = 0, [FromQuery] int pageSize = 0, - [FromQuery] int limit = PAGE_SIZE_MAX, + [FromQuery] int limit = PagingInfo.MaxPageSize, [FromQuery] Guid userId = default, [FromQuery] Guid playerId = default, [FromQuery] SortDirection sortDirection = SortDirection.Ascending ) { page = Math.Max(page, 0); - pageSize = Math.Max(Math.Min(pageSize, 100), 5); + pageSize = Math.Max(Math.Min(pageSize, PagingInfo.MaxPageSize), PagingInfo.MinPageSize); limit = Math.Max(Math.Min(limit, pageSize), 1); var start = DateTime.UtcNow; @@ -381,10 +376,10 @@ public DataPage ListTrades( Page = page, PageSize = pageSize, Count = values.Count, - Sort = new[] { Sort.From(nameof(TradeHistory.TimeStamp), sortDirection) }, Values = values, + Sort = [Sort.From(nameof(TradeHistory.TimeStamp), sortDirection)], #if DEBUG - Extra = delta + Extra = delta, #endif }; } @@ -431,12 +426,12 @@ public DataPage ListGuildActivity( Guid guildId, [FromQuery] int page = 0, [FromQuery] int pageSize = 0, - [FromQuery] int limit = PAGE_SIZE_MAX, + [FromQuery] int limit = PagingInfo.MaxPageSize, [FromQuery] SortDirection sortDirection = SortDirection.Ascending ) { page = Math.Max(page, 0); - pageSize = Math.Max(Math.Min(pageSize, 100), 5); + pageSize = Math.Max(Math.Min(pageSize, PagingInfo.MaxPageSize), PagingInfo.MinPageSize); limit = Math.Max(Math.Min(limit, pageSize), 1); using (var context = DbInterface.CreateLoggingContext()) @@ -466,14 +461,13 @@ public DataPage ListGuildActivity( values = values.Take(limit).ToList(); } - return new DataPage - { - Total = guildActivity.Count(), - Page = page, - PageSize = pageSize, - Count = values.Count, - Values = values - }; + return new DataPage( + Total: guildActivity.Count(), + Page: page, + PageSize: pageSize, + Count: values.Count, + Values: values + ); } } diff --git a/Intersect.Server/Web/RestApi/Routes/V1/PlayerController.cs b/Intersect.Server/Web/RestApi/Routes/V1/PlayerController.cs index 4988056f18..eb822c057a 100644 --- a/Intersect.Server/Web/RestApi/Routes/V1/PlayerController.cs +++ b/Intersect.Server/Web/RestApi/Routes/V1/PlayerController.cs @@ -1,3 +1,4 @@ +using System.Net; using Intersect.Enums; using Intersect.GameObjects; using Intersect.Server.Database; @@ -38,65 +39,77 @@ public sealed partial class PlayerController : IntersectController { [HttpPost] - public object ListPost([FromBody] PagingInfo pageInfo) + public IActionResult ListPost([FromBody] PagingInfo pageInfo) { pageInfo.Page = Math.Max(pageInfo.Page, 0); - pageInfo.Count = Math.Max(Math.Min(pageInfo.Count, 100), 5); - - int entryTotal = 0; - var entries = Player.List(null, null, SortDirection.Ascending, pageInfo.Page * pageInfo.Count, pageInfo.Count, out entryTotal); + pageInfo.PageSize = Math.Max(Math.Min(pageInfo.PageSize, 100), 5); + + var values = Player.List( + null, + null, + SortDirection.Ascending, + pageInfo.Page * pageInfo.PageSize, + pageInfo.PageSize, + out var entryTotal + ); - return new - { - total = entryTotal, - pageInfo.Page, - count = entries.Count, - entries - }; + return Ok( + new DataPage + { + Total = entryTotal, + Page = pageInfo.Page, + PageSize = pageInfo.PageSize, + Count = values.Count, + Values = values, + } + ); } [HttpGet] - public DataPage List( + public IActionResult List( [FromQuery] int page = 0, [FromQuery] int pageSize = 0, - [FromQuery] int limit = PAGE_SIZE_MAX, + [FromQuery] int limit = PagingInfo.MaxPageSize, [FromQuery] string sortBy = null, [FromQuery] SortDirection sortDirection = SortDirection.Ascending, [FromQuery] string search = null ) { page = Math.Max(page, 0); - pageSize = Math.Max(Math.Min(pageSize, 100), 5); - limit = Math.Max(Math.Min(limit, pageSize), 1); - - int total = 0; - var values = Player.List(search?.Length > 2 ? search : null, sortBy, sortDirection, page * pageSize, pageSize, out total); - - if (limit != pageSize) - { - values = values.Take(limit).ToList(); - } + limit = Math.Max(limit, 1); + pageSize = Math.Clamp(pageSize, PagingInfo.MinPageSize, PagingInfo.MaxPageSize); + + var take = Math.Min(limit, pageSize); + var values = Player.List( + search?.Length > 2 ? search : null, + sortBy, + sortDirection, + page * pageSize, + take, + out var total + ); - return new DataPage - { - Total = total, - Page = page, - PageSize = pageSize, - Count = values.Count, - Values = values - }; + return Ok( + new DataPage( + Total: total, + Page: page, + PageSize: pageSize, + Count: values.Count, + Values: values + ) + ); } [HttpGet("rank")] public DataPage Rank( [FromQuery] int page = 0, [FromQuery] int pageSize = 0, - [FromQuery] int limit = PAGE_SIZE_MAX, + [FromQuery] int limit = PagingInfo.MaxPageSize, [FromQuery] SortDirection sortDirection = SortDirection.Descending ) { page = Math.Max(page, 0); - pageSize = Math.Max(Math.Min(pageSize, 100), 5); + pageSize = Math.Max(Math.Min(pageSize, PagingInfo.MaxPageSize), PagingInfo.MinPageSize); limit = Math.Max(Math.Min(limit, pageSize), 1); var values = Player.Rank(page, pageSize, sortDirection).ToList(); @@ -112,10 +125,14 @@ public DataPage Rank( PageSize = pageSize, Count = values.Count, Values = values, - Extra = new + Sort = [new Sort { - sortDirection - } + By = [ + nameof(Player.Level), + nameof(Player.Exp), + ], + Direction = sortDirection, + }], }; } @@ -123,16 +140,17 @@ public DataPage Rank( public object OnlinePost([FromBody] PagingInfo pageInfo) { pageInfo.Page = Math.Max(pageInfo.Page, 0); - pageInfo.Count = Math.Max(Math.Min(pageInfo.Count, 100), 5); + pageInfo.PageSize = Math.Max(Math.Min(pageInfo.PageSize, 100), 5); - var entries = Globals.OnlineList?.Skip(pageInfo.Page * pageInfo.Count).Take(pageInfo.Count).ToList(); + var entries = Globals.OnlineList?.Skip(pageInfo.Page * pageInfo.PageSize).Take(pageInfo.PageSize).ToList(); - return new + return new DataPage { - total = Globals.OnlineList?.Count ?? 0, - pageInfo.Page, - count = entries?.Count ?? 0, - entries + Total = Globals.OnlineList?.Count ?? 0, + Page = pageInfo.Page, + PageSize = pageInfo.PageSize, + Count = entries?.Count ?? 0, + Values = entries, }; } @@ -140,17 +158,17 @@ public object OnlinePost([FromBody] PagingInfo pageInfo) public DataPage Online( [FromQuery] int page = 0, [FromQuery] int pageSize = 0, - [FromQuery] int limit = PAGE_SIZE_MAX, + [FromQuery] int limit = PagingInfo.MaxPageSize, [FromQuery] string sortBy = null, [FromQuery] SortDirection sortDirection = SortDirection.Ascending, [FromQuery] string search = null ) { page = Math.Max(page, 0); - pageSize = Math.Max(Math.Min(pageSize, 100), 5); + pageSize = Math.Max(Math.Min(pageSize, PagingInfo.MaxPageSize), PagingInfo.MinPageSize); limit = Math.Max(Math.Min(limit, pageSize), 1); - var sort = Sort.From(sortBy, sortDirection); + Sort.From(sortBy, sortDirection); IEnumerable enumerable = Globals.OnlineList ?? new List(); if (!string.IsNullOrWhiteSpace(search)) @@ -181,14 +199,13 @@ public DataPage Online( values = values.Take(limit).ToList(); } - return new DataPage - { - Total = total, - Page = page, - PageSize = pageSize, - Count = values.Count, - Values = values - }; + return new DataPage( + Total: total, + Page: page, + PageSize: pageSize, + Count: values.Count, + Values: values + ); } [HttpGet("online/count")] @@ -201,17 +218,17 @@ public object OnlineCount() } [HttpGet("{lookupKey:LookupKey}")] - public object LookupPlayer(LookupKey lookupKey) + public IActionResult LookupPlayer(LookupKey lookupKey) { if (lookupKey.IsInvalid) { return BadRequest(lookupKey.IsIdInvalid ? @"Invalid player id." : @"Invalid player name."); } - var (client, player) = Player.Fetch(lookupKey); + var (_, player) = Player.Fetch(lookupKey, loadRelationships: true, loadBags: true); if (player != null) { - return player; + return Ok(player); } return NotFound(lookupKey.HasId ? $@"No player with id '{lookupKey.Id}'." : $@"No player with name '{lookupKey.Name}'."); @@ -275,7 +292,7 @@ public object ChangeName(LookupKey lookupKey, [FromBody] NameChange change) return BadRequest($@"Name already taken."); } - var (client, player) = Player.Fetch(lookupKey); + var (_, player) = Player.Fetch(lookupKey); if (player != null) { player.Name = change.Name; @@ -297,15 +314,17 @@ public object ChangeName(LookupKey lookupKey, [FromBody] NameChange change) } [HttpGet("{lookupKey:LookupKey}/variables")] - public object PlayerVariableGet(LookupKey lookupKey) + [ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest, "application/json")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound, "application/json")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK, "application/json")] + public IActionResult PlayerVariablesList(LookupKey lookupKey) { if (lookupKey.IsInvalid) { - return BadRequest(lookupKey.IsIdInvalid ? @"Invalid player id." : @"Invalid player name."); + return BadRequest(lookupKey.IsIdInvalid ? @"Invalid id." : @"Invalid name."); } - var (client, player) = Player.Fetch(lookupKey); - if (player == null) + if (!Player.TryFetch(lookupKey, out var fetchResult)) { return NotFound( lookupKey.HasId @@ -314,24 +333,23 @@ public object PlayerVariableGet(LookupKey lookupKey) ); } - return player.Variables; + var (_, player) = fetchResult; + + return Ok(player.Variables); } [HttpGet("{lookupKey:LookupKey}/variables/{variableId:guid}")] - public object PlayerVariableGet(LookupKey lookupKey, Guid variableId) + [ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest, "application/json")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound, "application/json")] + [ProducesResponseType(typeof(PlayerVariable), (int)HttpStatusCode.OK, "application/json")] + public IActionResult PlayerVariableGet(LookupKey lookupKey, Guid variableId) { if (lookupKey.IsInvalid) { - return BadRequest(lookupKey.IsIdInvalid ? @"Invalid player id." : @"Invalid player name."); - } - - if (variableId == Guid.Empty || PlayerVariableBase.Get(variableId) == null) - { - return BadRequest($@"Invalid variable id ${variableId}."); + return BadRequest(lookupKey.IsIdInvalid ? @"Invalid id." : @"Invalid name."); } - var (client, player) = Player.Fetch(lookupKey); - if (player == null) + if (!Player.TryFetch(lookupKey, out var fetchResult)) { return NotFound( lookupKey.HasId @@ -340,24 +358,29 @@ public object PlayerVariableGet(LookupKey lookupKey, Guid variableId) ); } - return player.GetVariable(variableId, true); + var (_, player) = fetchResult; + + if (!PlayerVariableBase.TryGet(variableId, out var variableDescriptor)) + { + return NotFound($@"Variable not found for id {variableId}"); + } + + var variable = player.GetVariable(variableDescriptor.Id, true); + return Ok(variable); } [HttpGet("{lookupKey:LookupKey}/variables/{variableId:guid}/value")] - public object PlayerVariableGetValue(LookupKey lookupKey, Guid variableId) + [ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest, "application/json")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound, "application/json")] + [ProducesResponseType(typeof(VariableValueBody), (int)HttpStatusCode.OK, "application/json")] + public IActionResult PlayerVariableValueGet(LookupKey lookupKey, Guid variableId) { if (lookupKey.IsInvalid) { - return BadRequest(lookupKey.IsIdInvalid ? @"Invalid player id." : @"Invalid player name."); + return BadRequest(lookupKey.IsIdInvalid ? @"Invalid id." : @"Invalid name."); } - if (variableId == Guid.Empty || PlayerVariableBase.Get(variableId) == null) - { - return BadRequest($@"Invalid variable id ${variableId}."); - } - - var (client, player) = Player.Fetch(lookupKey); - if (player == null) + if (!Player.TryFetch(lookupKey, out var fetchResult)) { return NotFound( lookupKey.HasId @@ -366,27 +389,32 @@ public object PlayerVariableGetValue(LookupKey lookupKey, Guid variableId) ); } - return new + var (_, player) = fetchResult; + + if (!PlayerVariableBase.TryGet(variableId, out var variableDescriptor)) { - value = player.GetVariable(variableId, true).Value.Value, - }; + return NotFound($@"Variable not found for id {variableId}"); + } + + var variable = player.GetVariable(variableDescriptor.Id, true); + return Ok(new VariableValueBody + { + Value = variable.Value.Value, + }); } [HttpPost("{lookupKey:LookupKey}/variables/{variableId:guid}")] - public object PlayerVariableSet(LookupKey lookupKey, Guid variableId, [FromBody] VariableValueAPI valueApi) + [ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest, "application/json")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound, "application/json")] + [ProducesResponseType(typeof(PlayerVariable), (int)HttpStatusCode.OK, "application/json")] + public IActionResult PlayerVariableSet(LookupKey lookupKey, Guid variableId, [FromBody] VariableValueBody valueBody) { if (lookupKey.IsInvalid) { - return BadRequest(lookupKey.IsIdInvalid ? @"Invalid player id." : @"Invalid player name."); - } - - if (variableId == Guid.Empty || PlayerVariableBase.Get(variableId) == null) - { - return BadRequest($@"Invalid variable id ${variableId}."); + return BadRequest(lookupKey.IsIdInvalid ? @"Invalid id." : @"Invalid name."); } - var (client, player) = Player.Fetch(lookupKey); - if (player == null) + if (!Player.TryFetch(lookupKey, out var fetchResult)) { return NotFound( lookupKey.HasId @@ -395,34 +423,35 @@ public object PlayerVariableSet(LookupKey lookupKey, Guid variableId, [FromBody] ); } - var variable = player.GetVariable(variableId, true); + var (_, player) = fetchResult; - var changed = true; - if (variable?.Value != null) + if (!PlayerVariableBase.TryGet(variableId, out var variableDescriptor)) { - if (variable?.Value?.Value != valueApi.Value) - { - changed = false; - } - variable.Value.Value = valueApi.Value; + return NotFound($@"Variable not found for id {variableId}"); } - if (changed) + var variable = player.GetVariable(variableDescriptor.Id, true); + + var changed = false; + if (variable?.Value != null) { - var plyr = Player.FindOnline(player.Id); - if (plyr != null) + if (variable.Value.Value != valueBody.Value) { - player.StartCommonEventsWithTrigger(CommonEventTrigger.PlayerVariableChange, "", variableId.ToString()); + variable.Value.Value = valueBody.Value; + changed = true; } } - using (var context = DbInterface.CreatePlayerContext(false)) + // ReSharper disable once InvertIf + if (changed) { + player.StartCommonEventsWithTrigger(CommonEventTrigger.PlayerVariableChange, string.Empty, variableId.ToString()); + using var context = DbInterface.CreatePlayerContext(false); context.Update(player); context.SaveChanges(); } - return variable; + return Ok(variable); } [HttpPost("{lookupKey:LookupKey}/class")] @@ -438,7 +467,7 @@ public object PlayerClassSet(LookupKey lookupKey, [FromBody] ClassChange change) return BadRequest($@"Invalid class id ${change.ClassId}."); } - var (client, player) = Player.Fetch(lookupKey); + var (_, player) = Player.Fetch(lookupKey); if (player != null) { player.ClassId = change.ClassId; @@ -469,7 +498,7 @@ public object PlayerLevelSet(LookupKey lookupKey, [FromBody] LevelChange change) return BadRequest(lookupKey.IsIdInvalid ? @"Invalid player id." : @"Invalid player name."); } - var (client, player) = Player.Fetch(lookupKey); + var (_, player) = Player.Fetch(lookupKey); if (player != null) { player.SetLevel(change.Level, true); @@ -490,25 +519,44 @@ public object PlayerLevelSet(LookupKey lookupKey, [FromBody] LevelChange change) return NotFound(lookupKey.HasId ? $@"No player with id '{lookupKey.Id}'." : $@"No player with name '{lookupKey.Name}'."); } + public struct ItemListResponse + { + public IEnumerable Bank { get; init; } + + public IEnumerable Inventory { get; init; } + } + [HttpGet("{lookupKey:LookupKey}/items")] - public object ItemsList(LookupKey lookupKey) + public IActionResult ItemsList(LookupKey lookupKey) { - return new + var (_, player) = Player.Fetch(lookupKey); + if (player == null) { - inventory = ItemsListInventory(lookupKey), - bank = ItemsListBank(lookupKey) - }; + return NotFound( + lookupKey.HasId + ? $@"No player with id '{lookupKey.Id}'." + : $@"No player with name '{lookupKey.Name}'." + ); + } + + return Ok( + new ItemListResponse + { + Bank = player.Bank.Where(slot => !slot.IsEmpty), + Inventory = player.Items.Where(slot => !slot.IsEmpty), + } + ); } [HttpGet("{lookupKey:LookupKey}/items/bank")] - public object ItemsListBank(LookupKey lookupKey) + public IActionResult ItemsListBank(LookupKey lookupKey) { if (lookupKey.IsInvalid) { return BadRequest(lookupKey.IsIdInvalid ? @"Invalid player id." : @"Invalid player name."); } - var (client, player) = Player.Fetch(lookupKey); + var (_, player) = Player.Fetch(lookupKey); if (player == null) { return NotFound( @@ -518,38 +566,34 @@ public object ItemsListBank(LookupKey lookupKey) ); } - return player.Bank; + return Ok(player.Bank.Where(slot => !slot.IsEmpty)); } [HttpGet("bag/{bagId:guid}")] - public object ItemsListBag(Guid bagId) + public IActionResult GetPlayerBag(Guid bagId) { if (bagId == Guid.Empty) { return BadRequest(@"Invalid bag id."); } - var bag = Bag.GetBag(bagId); - - if (bag == null) + if (Bag.TryGetBag(bagId, out var bag)) { - return NotFound(@"Bag does not exist."); - } - else - { - return bag; + return Ok(bag); } + + return NotFound(@"Bag does not exist."); } [HttpGet("{lookupKey:LookupKey}/items/inventory")] - public object ItemsListInventory(LookupKey lookupKey) + public IActionResult ItemsListInventory(LookupKey lookupKey) { if (lookupKey.IsInvalid) { return BadRequest(lookupKey.IsIdInvalid ? @"Invalid player id." : @"Invalid player name."); } - var (client, player) = Player.Fetch(lookupKey); + var (_, player) = Player.Fetch(lookupKey); if (player == null) { return NotFound( @@ -559,7 +603,7 @@ public object ItemsListInventory(LookupKey lookupKey) ); } - return player.Items; + return Ok(player.Items.Where(slot => !slot.IsEmpty)); } [HttpPost("{lookupKey:LookupKey}/items/give")] @@ -580,7 +624,7 @@ public object ItemsGive(LookupKey lookupKey, [FromBody] ItemInfo itemInfo) return BadRequest("Cannot give 0, or a negative amount of an item."); } - var (client, player) = Player.Fetch(lookupKey); + var (_, player) = Player.Fetch(lookupKey); if (player == null) { return NotFound( @@ -629,7 +673,7 @@ public object ItemsTake(LookupKey lookupKey, [FromBody] ItemInfo itemInfo) return BadRequest("Cannot take 0, or a negative amount of an item."); } - var (client, player) = Player.Fetch(lookupKey); + var (_, player) = Player.Fetch(lookupKey); if (player == null) { return NotFound( @@ -658,14 +702,14 @@ public object ItemsTake(LookupKey lookupKey, [FromBody] ItemInfo itemInfo) } [HttpGet("{lookupKey:LookupKey}/spells")] - public object SpellsList(LookupKey lookupKey) + public IActionResult SpellsList(LookupKey lookupKey) { if (lookupKey.IsInvalid) { return BadRequest(lookupKey.IsIdInvalid ? @"Invalid player id." : @"Invalid player name."); } - var (client, player) = Player.Fetch(lookupKey); + var (_, player) = Player.Fetch(lookupKey); if (player == null) { return NotFound( @@ -675,7 +719,7 @@ public object SpellsList(LookupKey lookupKey) ); } - return player.Spells; + return Ok(player.Spells.Where(s => !s.IsEmpty)); } [HttpPost("{lookupKey:LookupKey}/spells/teach")] @@ -691,7 +735,7 @@ public object SpellsTeach(LookupKey lookupKey, [FromBody] SpellInfo spell) return BadRequest(@"Invalid spell id."); } - var (client, player) = Player.Fetch(lookupKey); + var (_, player) = Player.Fetch(lookupKey); if (player == null) { return NotFound( @@ -728,7 +772,7 @@ public object SpellsTake(LookupKey lookupKey, [FromBody] SpellInfo spell) return BadRequest(@"Invalid spell id."); } - var (client, player) = Player.Fetch(lookupKey); + var (_, player) = Player.Fetch(lookupKey); if (player == null) { return NotFound( @@ -752,21 +796,16 @@ public object SpellsTake(LookupKey lookupKey, [FromBody] SpellInfo spell) return InternalServerError($@"Failed to remove player spell with id '{spell.SpellId}'."); } - [HttpPost("{lookupKey:LookupKey}/admin/{act}")] - public object DoAdminActionOnPlayerByName( + [HttpPost("{lookupKey:LookupKey}/admin/{adminAction:AdminAction}")] + public object DoAdminActionOnPlayerByLookupKey( LookupKey lookupKey, - string act, + AdminAction adminAction, [FromBody] AdminActionParameters actionParameters ) { - if (!Enum.TryParse(act, true, out var adminAction)) - { - return BadRequest(@"Invalid action."); - } - if (lookupKey.IsInvalid) { - return BadRequest(lookupKey.IsIdInvalid ? @"Invalid player id." : @"Invalid player name."); + return BadRequest(lookupKey.IsIdInvalid ? @"Invalid id." : @"Invalid name."); } Tuple fetchResult; diff --git a/Intersect.Server/Web/RestApi/Routes/V1/UserController.cs b/Intersect.Server/Web/RestApi/Routes/V1/UserController.cs index e64f0449f6..131d300162 100644 --- a/Intersect.Server/Web/RestApi/Routes/V1/UserController.cs +++ b/Intersect.Server/Web/RestApi/Routes/V1/UserController.cs @@ -1,8 +1,11 @@ +using System.Net; using System.Text.RegularExpressions; using Intersect.Enums; +using Intersect.GameObjects; using Intersect.Server.Database; using Intersect.Server.Database.PlayerData; +using Intersect.Server.Database.PlayerData.Players; using Intersect.Server.Database.PlayerData.Security; using Intersect.Server.Entities; using Intersect.Server.General; @@ -27,32 +30,31 @@ public sealed partial class UserController : IntersectController public DataPage ListPost([FromBody] PagingInfo pageInfo) { var page = Math.Max(pageInfo.Page, 0); - var pageSize = Math.Max(Math.Min(pageInfo.Count, PAGE_SIZE_MAX), PAGE_SIZE_MIN); + var pageSize = Math.Max(Math.Min(pageInfo.PageSize, PagingInfo.MaxPageSize), PagingInfo.MinPageSize); - var values = Database.PlayerData.User.List(null, null, SortDirection.Ascending, pageInfo.Page * pageInfo.Count, pageInfo.Count, out var total); + var values = Database.PlayerData.User.List(null, null, SortDirection.Ascending, pageInfo.Page * pageInfo.PageSize, pageInfo.PageSize, out var total); - return new DataPage - { - Total = total, - Page = page, - PageSize = pageSize, - Count = values.Count, - Values = values - }; + return new DataPage( + Total: total, + Page: page, + PageSize: pageSize, + Count: values.Count, + Values: values + ); } [HttpGet] public DataPage List( [FromQuery] int page = 0, [FromQuery] int pageSize = 0, - [FromQuery] int limit = PAGE_SIZE_MAX, + [FromQuery] int limit = PagingInfo.MaxPageSize, [FromQuery] string sortBy = null, [FromQuery] SortDirection sortDirection = SortDirection.Ascending, [FromQuery] string search = null ) { page = Math.Max(page, 0); - pageSize = Math.Max(Math.Min(pageSize, PAGE_SIZE_MAX), PAGE_SIZE_MIN); + pageSize = Math.Max(Math.Min(pageSize, PagingInfo.MaxPageSize), PagingInfo.MinPageSize); limit = Math.Max(Math.Min(limit, pageSize), 1); var values = Database.PlayerData.User.List(search?.Length > 2 ? search : null, sortBy, sortDirection, page * pageSize, pageSize, out var total); @@ -62,14 +64,13 @@ public DataPage List( values = values.Take(limit).ToList(); } - return new DataPage - { - Total = total, - Page = page, - PageSize = pageSize, - Count = values.Count, - Values = values - }; + return new DataPage( + Total: total, + Page: page, + PageSize: pageSize, + Count: values.Count, + Values: values + ); } [HttpGet("{userId:guid}")] @@ -109,7 +110,7 @@ public object UserByName(string userName) } [HttpPost("register")] - public object RegisterUser([FromBody] UserInfo user) + public IActionResult RegisterUser([FromBody] UserInfo user) { if (string.IsNullOrEmpty(user.Username) || string.IsNullOrEmpty(user.Email) || @@ -132,23 +133,20 @@ public object RegisterUser([FromBody] UserInfo user) { return BadRequest($@"Account already exists with username '{user.Username}'."); } - else + + if (Database.PlayerData.User.UserExists(user.Email)) { - if (Database.PlayerData.User.UserExists(user.Email)) - { - return BadRequest($@"Account already with email '{user.Email}'."); - } - else - { - DbInterface.CreateAccount(null, user.Username, user.Password?.ToUpperInvariant()?.Trim(), user.Email); + return BadRequest($@"Account already with email '{user.Email}'."); + } - return new - { - Username = user.Username, - Email = user.Email, - }; + DbInterface.CreateAccount(null, user.Username, user.Password?.ToUpperInvariant()?.Trim(), user.Email); + + return Ok( + new + { + user.Username, user.Email, } - } + ); } [HttpDelete("{username}")] @@ -862,64 +860,172 @@ public object UserSendPasswordResetEmailById(Guid userId) #endregion - #region "Admin Action" + #region Variables - [HttpPost("{userId:guid}/admin/{act}")] - public object DoAdminActionOnPlayerById( - Guid userId, - string act, - [FromBody] AdminActionParameters actionParameters - ) + [HttpGet("{lookupKey:LookupKey}/variables")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest, "application/json")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound, "application/json")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK, "application/json")] + public IActionResult UserVariablesList(LookupKey lookupKey) { - if (!Enum.TryParse(act, true, out var adminAction)) + if (lookupKey.IsInvalid) { - return BadRequest(@"Invalid action."); + return BadRequest(lookupKey.IsIdInvalid ? @"Invalid id." : @"Invalid name."); } - if (Guid.Empty == userId) + if (!Database.PlayerData.User.TryFind(lookupKey, out var user)) { - return BadRequest($@"Invalid user id '{userId}'."); + return NotFound($@"No user found for {lookupKey}"); + } + + return Ok(user.Variables); + } + + [HttpGet("{lookupKey:LookupKey}/variables/{variableId:guid}")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest, "application/json")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound, "application/json")] + [ProducesResponseType(typeof(UserVariable), (int)HttpStatusCode.OK, "application/json")] + public IActionResult UserVariableGet(LookupKey lookupKey, Guid variableId) + { + if (lookupKey.IsInvalid) + { + return BadRequest(lookupKey.IsIdInvalid ? @"Invalid id." : @"Invalid name."); } - Tuple fetchResult; - fetchResult = Database.PlayerData.User.Fetch(userId); + if (!Database.PlayerData.User.TryFind(lookupKey, out var user)) + { + return NotFound($@"No user found for {lookupKey}"); + } - return DoAdminActionOnUser( - () => fetchResult, - () => NotFound($@"No user with id '{userId}'."), - adminAction, actionParameters - ); + if (variableId == Guid.Empty) + { + return BadRequest($@"Variable id cannot be {variableId}"); + } + + if (!UserVariableBase.TryGet(variableId, out var variableDescriptor)) + { + return NotFound($@"Variable not found for id {variableId}"); + } + + var variable = user.GetVariable(variableDescriptor.Id, true); + return Ok(variable); + } + + [HttpGet("{lookupKey:LookupKey}/variables/{variableId:guid}/value")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest, "application/json")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound, "application/json")] + [ProducesResponseType(typeof(VariableValueBody), (int)HttpStatusCode.OK, "application/json")] + public IActionResult UserVariableValueGet(LookupKey lookupKey, Guid variableId) + { + if (lookupKey.IsInvalid) + { + return BadRequest(lookupKey.IsIdInvalid ? @"Invalid id." : @"Invalid name."); + } + + if (!Database.PlayerData.User.TryFind(lookupKey, out var user)) + { + return NotFound($@"No user found for {lookupKey}"); + } + + if (variableId == Guid.Empty) + { + return BadRequest($@"Variable id cannot be {variableId}"); + } + + if (!UserVariableBase.TryGet(variableId, out var variableDescriptor)) + { + return NotFound($@"Variable not found for id {variableId}"); + } + + var variable = user.GetVariable(variableDescriptor.Id, true); + return Ok(new VariableValueBody + { + Value = variable.Value.Value, + }); + } + + [HttpPost("{lookupKey:LookupKey}/variables/{variableId:guid}")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest, "application/json")] + [ProducesResponseType(typeof(string), (int)HttpStatusCode.NotFound, "application/json")] + [ProducesResponseType(typeof(UserVariable), (int)HttpStatusCode.OK, "application/json")] + public IActionResult UserVariableSet(LookupKey lookupKey, Guid variableId, [FromBody] VariableValueBody valueBody) + { + if (lookupKey.IsInvalid) + { + return BadRequest(lookupKey.IsIdInvalid ? @"Invalid id." : @"Invalid name."); + } + + if (!Database.PlayerData.User.TryFind(lookupKey, out var user)) + { + return NotFound($@"No user found for {lookupKey}"); + } + + if (variableId == Guid.Empty) + { + return BadRequest($@"Variable id cannot be {variableId}"); + } + + if (!UserVariableBase.TryGet(variableId, out var variableDescriptor)) + { + return NotFound($@"Variable not found for id {variableId}"); + } + + var variable = user.GetVariable(variableDescriptor.Id, true); + + var changed = false; + if (variable?.Value != null) + { + if (variable.Value.Value != valueBody.Value) + { + variable.Value.Value = valueBody.Value; + changed = true; + } + } + + // ReSharper disable once InvertIf + if (changed) + { + user.StartCommonEventsWithTriggerForAll(CommonEventTrigger.UserVariableChange, string.Empty, variableId.ToString()); + user.UpdatedVariables.AddOrUpdate( + variableId, + variableDescriptor, + (_, _) => variableDescriptor + ); + } + + return Ok(variable); } - [HttpPost("{userName}/admin/{act}")] - public object DoAdminActionOnPlayerByName( - string userName, - string act, + #endregion Variables + + #region "Admin Action" + + [HttpPost("{lookupKey:LookupKey}/admin/{adminAction:AdminAction}")] + public object DoAdminActionOnUserByLookupKey( + LookupKey lookupKey, + AdminAction adminAction, [FromBody] AdminActionParameters actionParameters ) { - if (string.IsNullOrWhiteSpace(userName)) + if (lookupKey.IsInvalid) { - return BadRequest("Invalid user name."); + return BadRequest(lookupKey.IsIdInvalid ? @"Invalid id." : @"Invalid name."); } - if (!Enum.TryParse(act, true, out var adminAction)) + if (!Database.PlayerData.User.TryFetch(lookupKey, out var user, out var client)) { - return BadRequest(@"Invalid action."); + return NotFound($"No user found for lookup key '{lookupKey}'"); } - Tuple fetchResult; - fetchResult = Database.PlayerData.User.Fetch(userName); - return DoAdminActionOnUser( - () => fetchResult, - () => NotFound($@"No user with name '{userName}'."), + () => (client, user), + () => NotFound($@"No user found for lookup key '{lookupKey}'."), adminAction, actionParameters ); } private IActionResult DoAdminActionOnUser( - Func> fetch, + Func> fetch, Func onError, AdminAction adminAction, AdminActionParameters actionParameters diff --git a/Intersect.Server/Web/RestApi/Routes/V1/VariablesController.cs b/Intersect.Server/Web/RestApi/Routes/V1/VariablesController.cs index e109241f53..8d593e53ea 100644 --- a/Intersect.Server/Web/RestApi/Routes/V1/VariablesController.cs +++ b/Intersect.Server/Web/RestApi/Routes/V1/VariablesController.cs @@ -3,6 +3,7 @@ using Intersect.Server.Database.GameData; using Intersect.Server.Entities; using Intersect.Server.Web.RestApi.Payloads; +using Intersect.Server.Web.RestApi.Types; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -16,16 +17,17 @@ public sealed partial class VariablesController : IntersectController public object GlobalVariablesGet([FromQuery] PagingInfo pageInfo) { pageInfo.Page = Math.Max(pageInfo.Page, 0); - pageInfo.Count = Math.Max(Math.Min(pageInfo.Count, 100), 5); + pageInfo.PageSize = Math.Max(Math.Min(pageInfo.PageSize, 100), 5); - var entries = GameContext.Queries.ServerVariables(pageInfo.Page, pageInfo.Count)?.ToList(); + var entries = GameContext.Queries.ServerVariables(pageInfo.Page, pageInfo.PageSize)?.ToList(); - return new + return new DataPage { - total = ServerVariableBase.Lookup.Count(), - pageInfo.Page, - count = entries?.Count ?? 0, - entries + Total = ServerVariableBase.Lookup.Count(), + Page = pageInfo.Page, + PageSize = pageInfo.PageSize, + Count = entries?.Count ?? 0, + Values = entries, }; } @@ -64,12 +66,12 @@ public object GlobalVariableGetValue(Guid guid) return new { - value = variable?.Value.Value, + value = variable.Value.Value, }; } [HttpPost("global/{guid:guid}")] - public object GlobalVariableSet(Guid guid, [FromBody] VariableValueAPI valueApi) + public object GlobalVariableSet(Guid guid, [FromBody] VariableValueBody valueBody) { if (Guid.Empty == guid) { @@ -84,11 +86,11 @@ public object GlobalVariableSet(Guid guid, [FromBody] VariableValueAPI valueApi) } var changed = true; - if (variable.Value?.Value == valueApi.Value) + if (variable.Value?.Value == valueBody.Value) { changed = false; } - variable.Value.Value = valueApi.Value; + variable.Value.Value = valueBody.Value; if (changed) { diff --git a/Intersect.Server/Web/RestApi/Types/DataPage.cs b/Intersect.Server/Web/RestApi/Types/DataPage.cs index 3ab4a3cb52..4a07057671 100644 --- a/Intersect.Server/Web/RestApi/Types/DataPage.cs +++ b/Intersect.Server/Web/RestApi/Types/DataPage.cs @@ -1,27 +1,44 @@ -using System.Collections.Generic; - +using Intersect.Framework.Core.Serialization; +using Intersect.Framework.Reflection; using Intersect.Server.Web.RestApi.Payloads; - -namespace Intersect.Server.Web.RestApi.Types +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Intersect.Server.Web.RestApi.Types; + +public struct DataPage( + int Total, + int Page, + int PageSize, + int Count, + IEnumerable Values, + IEnumerable? Sort = null, + dynamic? Extra = null +) : IDataPage { - - public partial struct DataPage + public DataPage() : this( + 0, + 0, + 0, + 0, + [] + ) { + } - public int Total { get; set; } - - public int Page { get; set; } - - public int PageSize { get; set; } + public int Total { get; init; } = Total; - public int Count { get; set; } + public int Page { get; init; } = Page; - public IEnumerable Values { get; set; } + public int PageSize { get; init; } = PageSize; - public IEnumerable Sort { get; set; } + public int Count { get; init; } = Count; - public dynamic Extra { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public IEnumerable? Sort { get; init; } = Sort; - } + public IEnumerable Values { get; init; } = Values; -} + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public dynamic? Extra { get; init; } = Extra; +} \ No newline at end of file diff --git a/Intersect.Server/Web/RestApi/Types/IDataPage.cs b/Intersect.Server/Web/RestApi/Types/IDataPage.cs new file mode 100644 index 0000000000..893830137b --- /dev/null +++ b/Intersect.Server/Web/RestApi/Types/IDataPage.cs @@ -0,0 +1,14 @@ +using Intersect.Server.Web.RestApi.Payloads; + +namespace Intersect.Server.Web.RestApi.Types; + +public interface IDataPage +{ + int Total { get; init; } + int Page { get; init; } + int PageSize { get; init; } + int Count { get; init; } + IEnumerable Sort { get; init; } + IEnumerable Values { get; init; } + dynamic Extra { get; init; } +} \ No newline at end of file diff --git a/Intersect.Server/Web/RestApi/Types/IpRange.cs b/Intersect.Server/Web/RestApi/Types/IpRange.cs index aba8e486fe..95acd6b5bf 100644 --- a/Intersect.Server/Web/RestApi/Types/IpRange.cs +++ b/Intersect.Server/Web/RestApi/Types/IpRange.cs @@ -1,81 +1,79 @@ -using System; using System.Net; using System.Net.Sockets; -namespace Intersect.Server.Web.RestApi.Types +namespace Intersect.Server.Web.RestApi.Types; + +internal class IpRange { - internal class IpRange + public IpRange(IPAddress address) { - public IpRange(IPAddress address) + if (address == null) { - if (address == null) - { - throw new ArgumentNullException(nameof(address)); - } + throw new ArgumentNullException(nameof(address)); + } - AddressFamily = address.AddressFamily; - Start = address.GetAddressBytes(); - End = address.GetAddressBytes(); + AddressFamily = address.AddressFamily; + Start = address.GetAddressBytes(); + End = address.GetAddressBytes(); + } + + public IpRange(IPAddress start, IPAddress end) + { + if (start == default) + { + throw new ArgumentNullException(nameof(start)); } - public IpRange(IPAddress start, IPAddress end) + if (end == default) { - if (start == default) - { - throw new ArgumentNullException(nameof(start)); - } + throw new ArgumentNullException(nameof(end)); + } - if (end == default) - { - throw new ArgumentNullException(nameof(end)); - } + if (start.AddressFamily != end.AddressFamily) + { + throw new ArgumentException("AddressFamily mismatch"); + } - if (start.AddressFamily != end.AddressFamily) - { - throw new ArgumentException("AddressFamily mismatch"); - } + AddressFamily = start.AddressFamily; + Start = start.GetAddressBytes(); + End = end.GetAddressBytes(); + } - AddressFamily = start.AddressFamily; - Start = start.GetAddressBytes(); - End = end.GetAddressBytes(); - } + public AddressFamily AddressFamily { get; } - public AddressFamily AddressFamily { get; } + public byte[] Start { get; } - public byte[] Start { get; } + public byte[] End { get; } - public byte[] End { get; } + public bool IsInRange(IPAddress address) + { + if (Start == default || End == default || address == default) { + return false; + } - public bool IsInRange(IPAddress address) + if (address.AddressFamily != AddressFamily) { - if (Start == default || End == default || address == default) { - return false; - } + return false; + } - if (address.AddressFamily != AddressFamily) - { - return false; - } + var octets = address.GetAddressBytes(); + if (Start.Length != End.Length || End.Length != octets.Length) + { + return false; + } - var octets = address.GetAddressBytes(); - if (Start.Length != End.Length || End.Length != octets.Length) - { - return false; - } + for (var index = 0; index < octets.Length; index++) + { + var start = Start[index]; + var end = End[index]; + var octet = octets[index]; - for (var index = 0; index < octets.Length; index++) + if (octet < start || end < octet) { - var start = Start[index]; - var end = End[index]; - var octet = octets[index]; - - if (octet < start || end < octet) - { - return false; - } + return false; } - - return true; } + + return true; } -} +} \ No newline at end of file diff --git a/Intersect.Server/Web/RestApi/Types/StatusMessageResponseBody.cs b/Intersect.Server/Web/RestApi/Types/StatusMessageResponseBody.cs new file mode 100644 index 0000000000..522448f7ee --- /dev/null +++ b/Intersect.Server/Web/RestApi/Types/StatusMessageResponseBody.cs @@ -0,0 +1,3 @@ +namespace Intersect.Server.Web.RestApi.Types; + +public record struct StatusMessageResponseBody(string Message); \ No newline at end of file diff --git a/Intersect.sln.DotSettings b/Intersect.sln.DotSettings index 3a51fda9a4..82879ffcb5 100644 --- a/Intersect.sln.DotSettings +++ b/Intersect.sln.DotSettings @@ -1,4 +1,5 @@  + UI True True True