From 47076668904f67994c88c7d7d8a39613e03c7e03 Mon Sep 17 00:00:00 2001
From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com>
Date: Mon, 23 Jan 2023 21:51:05 -0800
Subject: [PATCH] feat: Moves antimacro to a configuration (#1323)
### Summary
- [X] Moves antimacro to `Distribution/Configuration/antimacro.json`.
- [X] Disables antimacro by default.
---
.gitignore | 1 +
.../Converters/BitArrayEnumIndexConverter.cs | 88 +++++++++++++++++++
.../Server/Json/Converters/FlagsConverter.cs | 18 +---
Projects/Server/Utilities/Utility.cs | 16 ++++
.../Assistants/AssistantConfiguration.cs | 7 +-
Projects/UOContent/Misc/SkillCheck.cs | 72 +++++++++++----
Projects/UOContent/Mobiles/PlayerMobile.cs | 6 +-
7 files changed, 169 insertions(+), 39 deletions(-)
create mode 100644 Projects/Server/Json/Converters/BitArrayEnumIndexConverter.cs
diff --git a/.gitignore b/.gitignore
index 7022fc9b2..13a182b47 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
/Distribution/ModernUO.*
/Distribution/Assemblies
/Distribution/bsdtar
+/Distribution/Configuration/antimacro.json
/Distribution/Configuration/assistants.json
/Distribution/Configuration/modernuo.json
/Distribution/Configuration/email-settings.json
diff --git a/Projects/Server/Json/Converters/BitArrayEnumIndexConverter.cs b/Projects/Server/Json/Converters/BitArrayEnumIndexConverter.cs
new file mode 100644
index 000000000..0f5d5f89e
--- /dev/null
+++ b/Projects/Server/Json/Converters/BitArrayEnumIndexConverter.cs
@@ -0,0 +1,88 @@
+/*************************************************************************
+ * ModernUO *
+ * Copyright 2019-2023 - ModernUO Development Team *
+ * Email: hi@modernuo.com *
+ * File: BitArrayEnumIndexConverter.cs *
+ * *
+ * This program is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation, either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see . *
+ *************************************************************************/
+
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Server.Collections;
+
+namespace Server.Json;
+
+public class BitArrayEnumIndexConverter : JsonConverter where T : struct, Enum
+{
+ // ReSharper disable once StaticMemberInGenericType
+ private static int _maxValue = int.MinValue;
+
+ public override BitArray Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (_maxValue < 0)
+ {
+ // Get the max value and cache it.
+ var values = Enum.GetValues();
+ for (var i = 0; i < values.Length; i++)
+ {
+ var v = (int)(object)values[i];
+ if (v > _maxValue)
+ {
+ _maxValue = v;
+ }
+ }
+ }
+
+ // If the value is very large, we will have a major problem.
+ // We always assume zero-offset
+ var bitArray = new BitArray(_maxValue + 1);
+
+ while (true)
+ {
+ reader.Read();
+ if (reader.TokenType == JsonTokenType.EndObject)
+ {
+ break;
+ }
+
+ if (reader.TokenType != JsonTokenType.PropertyName)
+ {
+ throw new JsonException("Invalid Json structure for Flag object");
+ }
+
+ var key = reader.GetString();
+
+ reader.Read();
+
+ if (!reader.GetBoolean() || !Enum.TryParse(key, out var val))
+ {
+ continue;
+ }
+
+ bitArray[(int)(object)val] = true;
+ }
+
+ return bitArray;
+ }
+
+ public override void Write(Utf8JsonWriter writer, BitArray bitArray, JsonSerializerOptions options)
+ {
+ writer.WriteStartObject();
+
+ foreach (var flagName in Enum.GetNames(typeof(T)))
+ {
+ var value = (int)(object)Enum.Parse(flagName, false);
+ writer.WriteBoolean(flagName, bitArray[value]);
+ }
+
+ writer.WriteEndObject();
+ }
+}
diff --git a/Projects/Server/Json/Converters/FlagsConverter.cs b/Projects/Server/Json/Converters/FlagsConverter.cs
index 0eea803b9..7b2d1c738 100644
--- a/Projects/Server/Json/Converters/FlagsConverter.cs
+++ b/Projects/Server/Json/Converters/FlagsConverter.cs
@@ -130,20 +130,4 @@ public class FlagsConverter : JsonConverter where T : struct, Enum
TypeCode.UInt64 => (ulong)value,
_ => throw new InvalidOperationException()
};
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static ulong GetUnderlyingTypeLength(TypeCode typeCode) =>
- typeCode switch
- {
- TypeCode.Byte => 8,
- TypeCode.SByte => 8,
- TypeCode.Int16 => 16,
- TypeCode.UInt16 => 16,
- TypeCode.Char => 16,
- TypeCode.Int32 => 32,
- TypeCode.UInt32 => 32,
- TypeCode.Int64 => 64,
- TypeCode.UInt64 => 64,
- _ => 64
- };
-}
\ No newline at end of file
+}
diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs
index 9effa3b73..a7d5210b9 100644
--- a/Projects/Server/Utilities/Utility.cs
+++ b/Projects/Server/Utilities/Utility.cs
@@ -1703,4 +1703,20 @@ public static class Utility
return center;
}
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int GetUnderlyingNumericBitLength(this TypeCode typeCode) =>
+ typeCode switch
+ {
+ TypeCode.Byte => 8,
+ TypeCode.SByte => 8,
+ TypeCode.Int16 => 16,
+ TypeCode.UInt16 => 16,
+ TypeCode.Char => 16,
+ TypeCode.Int32 => 32,
+ TypeCode.UInt32 => 32,
+ TypeCode.Int64 => 64,
+ TypeCode.UInt64 => 64,
+ _ => 64
+ };
}
diff --git a/Projects/UOContent/Assistants/AssistantConfiguration.cs b/Projects/UOContent/Assistants/AssistantConfiguration.cs
index 1c29e4bb0..c4d3de460 100644
--- a/Projects/UOContent/Assistants/AssistantConfiguration.cs
+++ b/Projects/UOContent/Assistants/AssistantConfiguration.cs
@@ -38,7 +38,7 @@ public static class AssistantConfiguration
WarningMessage = _defaultWarningMessage
};
- Save(path);
+ Save();
}
}
@@ -62,10 +62,9 @@ public static class AssistantConfiguration
Save();
}
- private static void Save(string path = null)
+ private static void Save()
{
- path ??= Path.Join(Core.BaseDirectory, _path);
- JsonConfig.Serialize(path, Settings);
+ JsonConfig.Serialize(Path.Join(Core.BaseDirectory, _path), Settings);
}
}
diff --git a/Projects/UOContent/Misc/SkillCheck.cs b/Projects/UOContent/Misc/SkillCheck.cs
index 60b7cb120..5105e53f2 100644
--- a/Projects/UOContent/Misc/SkillCheck.cs
+++ b/Projects/UOContent/Misc/SkillCheck.cs
@@ -1,7 +1,10 @@
using System;
+using System.IO;
+using System.Text.Json.Serialization;
+using Server.Collections;
using Server.Factions;
+using Server.Json;
using Server.Mobiles;
-using Server.Regions;
namespace Server.Misc;
@@ -18,18 +21,9 @@ public static class SkillCheck
// Publish 16 changed max stats from 100 to 125
private static int StatMax = Core.LBR ? 125 : 100;
- public const int Allowance = 3; // How many times may we use the same location/target for gain
-
- private const int
- LocationSize = 5; // The size of eeach location, make this smaller so players dont have to move as far
-
- private static readonly bool AntiMacroCode = !Core.ML; // Change this to false to disable anti-macro code
-
- public static TimeSpan AntiMacroExpire = TimeSpan.FromMinutes(5.0); // How long do we remember targets/locations?
-
- private static readonly bool[] UseAntiMacro =
+ // *** NOTE ***: Modifying these values will not change an already created antimacro.json file!
+ private static readonly bool[] _skillThatUseAntiMacro =
{
- // true if this skill uses the anti-macro code, false if it does not
false, // Alchemy = 0,
true, // Anatomy = 1,
true, // AnimalLore = 2,
@@ -93,6 +87,32 @@ public static class SkillCheck
private static readonly TimeSpan m_StatGainDelay = TimeSpan.FromMinutes(Core.ML ? 0.05 : 15);
private static readonly TimeSpan m_PetStatGainDelay = TimeSpan.FromMinutes(5.0);
+ private const string _antiMacroPath = "Configuration/antimacro.json";
+ public static AntiMacroSettings AntiMacro { get; private set; }
+
+ public static void Configure()
+ {
+ var path = Path.Combine(Core.BaseDirectory, _antiMacroPath);
+
+ if (File.Exists(path))
+ {
+ AntiMacro = JsonConfig.Deserialize(path);
+ }
+ else
+ {
+ AntiMacro = new AntiMacroSettings
+ {
+ Enabled = false,
+ Allowance = 3,
+ LocationSize = 5,
+ Expire = TimeSpan.FromMinutes(5.0),
+ SkillsThatUseAntiMacro = new BitArray(_skillThatUseAntiMacro)
+ };
+
+ JsonConfig.Serialize(Path.Join(Core.BaseDirectory, _antiMacroPath), AntiMacro);
+ }
+ }
+
public static void Initialize()
{
Mobile.SkillCheckLocationHandler = Mobile_SkillCheckLocation;
@@ -125,7 +145,8 @@ public static class SkillCheck
var chance = (value - minSkill) / (maxSkill - minSkill);
- var loc = new Point2D(from.Location.X / LocationSize, from.Location.Y / LocationSize);
+ var size = AntiMacro.LocationSize;
+ var loc = new Point2D(from.Location.X / size, from.Location.Y / size);
return CheckSkill(from, skill, loc, chance);
}
@@ -148,7 +169,8 @@ public static class SkillCheck
return true; // No challenge
}
- var loc = new Point2D(from.Location.X / LocationSize, from.Location.Y / LocationSize);
+ var size = AntiMacro.LocationSize;
+ var loc = new Point2D(from.Location.X / size, from.Location.Y / size);
return CheckSkill(from, skill, loc, chance);
}
@@ -257,7 +279,7 @@ public static class SkillCheck
return false;
}
- if (AntiMacroCode && from is PlayerMobile mobile && UseAntiMacro[skill.Info.SkillID])
+ if (AntiMacro.Enabled && from is PlayerMobile mobile && AntiMacro.UseAntiMacro(skill.Info.SkillID))
{
return mobile.AntiMacroCheck(skill, obj);
}
@@ -496,4 +518,24 @@ public static class SkillCheck
IncreaseStat(from, stat, atrophy);
}
+
+ public record AntiMacroSettings
+ {
+ // How many times may we use the same location/target for gain
+ public int Allowance { get; init; }
+
+ // The size of each location, make this smaller so players dont have to move as far
+ public int LocationSize { get; init; }
+
+ public bool Enabled { get; init; }
+
+ // How long do we remember targets/locations?
+ public TimeSpan Expire { get; init; }
+
+ [JsonConverter(typeof(BitArrayEnumIndexConverter))]
+ public BitArray SkillsThatUseAntiMacro { get; init; }
+
+ public bool UseAntiMacro(int skillId) =>
+ skillId < SkillsThatUseAntiMacro.Length && SkillsThatUseAntiMacro[skillId];
+ }
}
diff --git a/Projects/UOContent/Mobiles/PlayerMobile.cs b/Projects/UOContent/Mobiles/PlayerMobile.cs
index 5c44a75d2..874b7f5f5 100644
--- a/Projects/UOContent/Mobiles/PlayerMobile.cs
+++ b/Projects/UOContent/Mobiles/PlayerMobile.cs
@@ -2886,14 +2886,14 @@ namespace Server.Mobiles
if (tbl.TryGetValue(obj, out var count))
{
- if (count.TimeStamp + SkillCheck.AntiMacroExpire <= Core.Now)
+ if (count.TimeStamp + SkillCheck.AntiMacro.Expire <= Core.Now)
{
count.Count = 1;
return true;
}
++count.Count;
- return count.Count <= SkillCheck.Allowance;
+ return count.Count <= SkillCheck.AntiMacro.Allowance;
}
tbl[obj] = count = new CountAndTimeStamp();
@@ -3229,7 +3229,7 @@ namespace Server.Mobiles
foreach (var (k, v) in t)
{
- if (v.TimeStamp + SkillCheck.AntiMacroExpire <= Core.Now)
+ if (v.TimeStamp + SkillCheck.AntiMacro.Expire <= Core.Now)
{
toRemove.Add(k);
}