fix: Adds expansion specific mobile status version (#1145)

This commit is contained in:
Kamron Batman 2022-08-22 21:04:59 -07:00 committed by GitHub
parent 61f77df892
commit 2d95fb20a6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 363 additions and 324 deletions

View file

@ -60,7 +60,8 @@
"Shadowguard": false, "Shadowguard": false,
"TOL": false, "TOL": false,
"EJ": false "EJ": false
} },
"MobileStatusVersion": 3
}, },
{ {
"Name": "The Second Age", "Name": "The Second Age",
@ -123,7 +124,8 @@
"Shadowguard": false, "Shadowguard": false,
"TOL": false, "TOL": false,
"EJ": false "EJ": false
} },
"MobileStatusVersion": 3
}, },
{ {
"Name": "Renaissance", "Name": "Renaissance",
@ -186,7 +188,8 @@
"Shadowguard": false, "Shadowguard": false,
"TOL": false, "TOL": false,
"EJ": false "EJ": false
} },
"MobileStatusVersion": 3
}, },
{ {
"Name": "Third Dawn", "Name": "Third Dawn",
@ -249,7 +252,8 @@
"Shadowguard": false, "Shadowguard": false,
"TOL": false, "TOL": false,
"EJ": false "EJ": false
} },
"MobileStatusVersion": 3
}, },
{ {
"Name": "Blackthorn's Revenge", "Name": "Blackthorn's Revenge",
@ -312,7 +316,8 @@
"Shadowguard": false, "Shadowguard": false,
"TOL": false, "TOL": false,
"EJ": false "EJ": false
} },
"MobileStatusVersion": 3
}, },
{ {
"Name": "Age of Shadows", "Name": "Age of Shadows",
@ -375,7 +380,8 @@
"Shadowguard": false, "Shadowguard": false,
"TOL": false, "TOL": false,
"EJ": false "EJ": false
} },
"MobileStatusVersion": 6
}, },
{ {
"Name": "Samurai Empire", "Name": "Samurai Empire",
@ -438,7 +444,8 @@
"Shadowguard": false, "Shadowguard": false,
"TOL": false, "TOL": false,
"EJ": false "EJ": false
} },
"MobileStatusVersion": 6
}, },
{ {
"Name": "Mondain's Legacy", "Name": "Mondain's Legacy",
@ -501,7 +508,8 @@
"Shadowguard": false, "Shadowguard": false,
"TOL": false, "TOL": false,
"EJ": false "EJ": false
} },
"MobileStatusVersion": 6
}, },
{ {
"Name": "Stygian Abyss", "Name": "Stygian Abyss",
@ -564,7 +572,8 @@
"Shadowguard": false, "Shadowguard": false,
"TOL": false, "TOL": false,
"EJ": false "EJ": false
} },
"MobileStatusVersion": 6
}, },
{ {
"Name": "High Seas", "Name": "High Seas",
@ -627,7 +636,8 @@
"Shadowguard": false, "Shadowguard": false,
"TOL": false, "TOL": false,
"EJ": false "EJ": false
} },
"MobileStatusVersion": 6
}, },
{ {
"Name": "Time of Legends", "Name": "Time of Legends",
@ -690,7 +700,8 @@
"Shadowguard": true, "Shadowguard": true,
"TOL": true, "TOL": true,
"EJ": false "EJ": false
} },
"MobileStatusVersion": 6
}, },
{ {
"Name": "Endless Journey", "Name": "Endless Journey",
@ -753,6 +764,7 @@
"Shadowguard": true, "Shadowguard": true,
"TOL": true, "TOL": true,
"EJ": true "EJ": true
} },
"MobileStatusVersion": 6
} }
] ]

View file

@ -232,11 +232,23 @@ namespace Server.Tests.Network
} }
[Theory] [Theory]
[InlineData(ProtocolChanges.Version70610)] [InlineData(ProtocolChanges.Version70610, "7.0.61.0", ClientFlags.TerMur, Expansion.HS, 6)]
[InlineData(ProtocolChanges.Version400a)] [InlineData(ProtocolChanges.Version400a, "4.0.0a", ClientFlags.Malas, Expansion.AOS, 4)]
[InlineData(ProtocolChanges.Version502b)] [InlineData(ProtocolChanges.Version502b, "5.0.2b", ClientFlags.Malas, Expansion.ML, 5)]
public void TestMobileStatusExtendedSelf(ProtocolChanges changes) public void TestMobileStatusExtendedSelf(
ProtocolChanges changes,
string version,
ClientFlags clientFlags,
Expansion expansion,
int mobileStatusVersion
)
{ {
var expansionInfo = ExpansionInfo.GetInfo(Core.Expansion);
var oldExpansion = Core.Expansion;
var oldVersion = expansionInfo.MobileStatusVersion;
Core.Expansion = expansion;
ExpansionInfo.GetInfo(Core.Expansion).MobileStatusVersion = mobileStatusVersion;
var m = new Mobile((Serial)0x1) { Name = "Random Mobile 1" }; var m = new Mobile((Serial)0x1) { Name = "Random Mobile 1" };
m.DefaultMobileInit(); m.DefaultMobileInit();
m.Str = 50; m.Str = 50;
@ -248,12 +260,16 @@ namespace Server.Tests.Network
var ns = PacketTestUtilities.CreateTestNetState(); var ns = PacketTestUtilities.CreateTestNetState();
ns.ProtocolChanges = changes; ns.ProtocolChanges = changes;
ns.Version = new ClientVersion(version);
ns.Flags = clientFlags;
var expected = new MobileStatusExtended(m, ns).Compile(); var expected = new MobileStatusExtended(m, ns).Compile();
ns.SendMobileStatus(m, m); ns.SendMobileStatus(m, m);
var result = ns.SendPipe.Reader.TryRead(); var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected); AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
Core.Expansion = oldExpansion;
expansionInfo.MobileStatusVersion = oldVersion;
} }
[Theory] [Theory]

View file

@ -19,289 +19,298 @@ using System.IO;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Server.Json; using Server.Json;
namespace Server namespace Server;
public enum Expansion
{ {
public enum Expansion None,
{ T2A,
None, UOR,
T2A, UOTD,
UOR, LBR,
UOTD, AOS,
LBR, SE,
AOS, ML,
SE, SA,
ML, HS,
SA, TOL,
HS, EJ
TOL, }
EJ
} [Flags]
public enum ClientFlags
[Flags] {
public enum ClientFlags None = 0x00000000,
{ Felucca = 0x00000001,
None = 0x00000000, Trammel = 0x00000002,
Felucca = 0x00000001, Ilshenar = 0x00000004,
Trammel = 0x00000002, Malas = 0x00000008,
Ilshenar = 0x00000004, Tokuno = 0x00000010,
Malas = 0x00000008, TerMur = 0x00000020,
Tokuno = 0x00000010, Unk1 = 0x00000040,
TerMur = 0x00000020, Unk2 = 0x00000080,
Unk1 = 0x00000040, UOTD = 0x00000100
Unk2 = 0x00000080, }
UOTD = 0x00000100
} [Flags]
public enum FeatureFlags
[Flags] {
public enum FeatureFlags None = 0x00000000,
{ T2A = 0x00000001,
None = 0x00000000, UOR = 0x00000002, // In later clients, the T2A/UOR flags are negative feature flags to disable body replacement of Pre-AOS graphics.
T2A = 0x00000001, UOTD = 0x00000004,
UOR = 0x00000002, // In later clients, the T2A/UOR flags are negative feature flags to disable body replacement of Pre-AOS graphics. LBR = 0x00000008,
UOTD = 0x00000004, AOS = 0x00000010,
LBR = 0x00000008, SixthCharacterSlot = 0x00000020,
AOS = 0x00000010, SE = 0x00000040,
SixthCharacterSlot = 0x00000020, ML = 0x00000080,
SE = 0x00000040, EighthAge = 0x00000100,
ML = 0x00000080, NinthAge = 0x00000200, // Crystal/Shadow Custom House Tiles
EighthAge = 0x00000100, TenthAge = 0x00000400,
NinthAge = 0x00000200, // Crystal/Shadow Custom House Tiles IncreasedStorage = 0x00000800, // Increased Housing/Bank Storage
TenthAge = 0x00000400, SeventhCharacterSlot = 0x00001000,
IncreasedStorage = 0x00000800, // Increased Housing/Bank Storage RoleplayFaces = 0x00002000,
SeventhCharacterSlot = 0x00001000, TrialAccount = 0x00004000,
RoleplayFaces = 0x00002000, LiveAccount = 0x00008000,
TrialAccount = 0x00004000, SA = 0x00010000,
LiveAccount = 0x00008000, HS = 0x00020000,
SA = 0x00010000, Gothic = 0x00040000,
HS = 0x00020000, Rustic = 0x00080000,
Gothic = 0x00040000, Jungle = 0x00100000,
Rustic = 0x00080000, Shadowguard = 0x00200000,
Jungle = 0x00100000, TOL = 0x00400000,
Shadowguard = 0x00200000, EJ = 0x00800000,
TOL = 0x00400000,
EJ = 0x00800000, ExpansionNone = None,
ExpansionT2A = T2A,
ExpansionNone = None, ExpansionUOR = ExpansionT2A | UOR,
ExpansionT2A = T2A, ExpansionUOTD = ExpansionUOR | UOTD,
ExpansionUOR = ExpansionT2A | UOR, ExpansionLBR = ExpansionUOTD | LBR,
ExpansionUOTD = ExpansionUOR | UOTD, ExpansionAOS = LBR | AOS | LiveAccount,
ExpansionLBR = ExpansionUOTD | LBR, ExpansionSE = ExpansionAOS | SE,
ExpansionAOS = LBR | AOS | LiveAccount, ExpansionML = ExpansionSE | ML | NinthAge,
ExpansionSE = ExpansionAOS | SE, ExpansionSA = ExpansionML | SA | Gothic | Rustic,
ExpansionML = ExpansionSE | ML | NinthAge, ExpansionHS = ExpansionSA | HS,
ExpansionSA = ExpansionML | SA | Gothic | Rustic, ExpansionTOL = ExpansionHS | TOL | Jungle | Shadowguard,
ExpansionHS = ExpansionSA | HS, ExpansionEJ = ExpansionTOL | EJ
ExpansionTOL = ExpansionHS | TOL | Jungle | Shadowguard, }
ExpansionEJ = ExpansionTOL | EJ
} [Flags]
public enum CharacterListFlags
[Flags] {
public enum CharacterListFlags None = 0x00000000,
{ Unk1 = 0x00000001,
None = 0x00000000, OverwriteConfigButton = 0x00000002,
Unk1 = 0x00000001, OneCharacterSlot = 0x00000004,
OverwriteConfigButton = 0x00000002, ContextMenus = 0x00000008,
OneCharacterSlot = 0x00000004, SlotLimit = 0x00000010,
ContextMenus = 0x00000008, AOS = 0x00000020,
SlotLimit = 0x00000010, SixthCharacterSlot = 0x00000040,
AOS = 0x00000020, SE = 0x00000080,
SixthCharacterSlot = 0x00000040, ML = 0x00000100,
SE = 0x00000080, Unk2 = 0x00000200,
ML = 0x00000100, UO3DClientType = 0x00000400,
Unk2 = 0x00000200, Unk3 = 0x00000800,
UO3DClientType = 0x00000400, SeventhCharacterSlot = 0x00001000,
Unk3 = 0x00000800, Unk4 = 0x00002000,
SeventhCharacterSlot = 0x00001000, NewMovementSystem = 0x00004000, // Doesn't seem to be used on OSI
Unk4 = 0x00002000, NewFeluccaAreas = 0x00008000,
NewMovementSystem = 0x00004000, // Doesn't seem to be used on OSI
NewFeluccaAreas = 0x00008000, ExpansionNone = ContextMenus,
ExpansionT2A = ContextMenus,
ExpansionNone = ContextMenus, ExpansionUOR = ContextMenus,
ExpansionT2A = ContextMenus, ExpansionUOTD = ContextMenus,
ExpansionUOR = ContextMenus, ExpansionLBR = ContextMenus,
ExpansionUOTD = ContextMenus, ExpansionAOS = ContextMenus | AOS,
ExpansionLBR = ContextMenus, ExpansionSE = ExpansionAOS | SE,
ExpansionAOS = ContextMenus | AOS, ExpansionML = ExpansionSE | ML,
ExpansionSE = ExpansionAOS | SE, ExpansionSA = ExpansionML,
ExpansionML = ExpansionSE | ML, ExpansionHS = ExpansionSA,
ExpansionSA = ExpansionML, ExpansionTOL = ExpansionHS,
ExpansionHS = ExpansionSA, ExpansionEJ = ExpansionTOL
ExpansionTOL = ExpansionHS, }
ExpansionEJ = ExpansionTOL
} [Flags]
public enum HousingFlags
[Flags] {
public enum HousingFlags None = 0x0,
{ AOS = 0x10,
None = 0x0, SE = 0x40,
AOS = 0x10, ML = 0x80,
SE = 0x40, Crystal = 0x200,
ML = 0x80, SA = 0x10000,
Crystal = 0x200, HS = 0x20000,
SA = 0x10000, Gothic = 0x40000,
HS = 0x20000, Rustic = 0x80000,
Gothic = 0x40000, Jungle = 0x100000,
Rustic = 0x80000, Shadowguard = 0x200000,
Jungle = 0x100000, TOL = 0x400000,
Shadowguard = 0x200000, EJ = 0x800000,
TOL = 0x400000,
EJ = 0x800000, HousingAOS = AOS,
HousingSE = HousingAOS | SE,
HousingAOS = AOS, HousingML = HousingSE | ML | Crystal,
HousingSE = HousingAOS | SE, HousingSA = HousingML | SA | Gothic | Rustic,
HousingML = HousingSE | ML | Crystal, HousingHS = HousingSA | HS,
HousingSA = HousingML | SA | Gothic | Rustic, HousingTOL = HousingHS | TOL | Jungle | Shadowguard,
HousingHS = HousingSA | HS, HousingEJ = HousingTOL | EJ
HousingTOL = HousingHS | TOL | Jungle | Shadowguard, }
HousingEJ = HousingTOL | EJ
} public class ExpansionInfo
{
public class ExpansionInfo public static bool ForceOldAnimations { get; private set; }
{ public static void Configure()
public static bool ForceOldAnimations { get; private set; } {
public static void Configure() ForceOldAnimations = ServerConfiguration.GetSetting("expansion.forceOldAnimations", false);
{ }
ForceOldAnimations = ServerConfiguration.GetSetting("expansion.forceOldAnimations", false);
} public static string GetEraFolder(string parentDirectory)
{
public static string GetEraFolder(string parentDirectory) var expansion = Core.Expansion;
{ var folders = Directory.GetDirectories(
var expansion = Core.Expansion; parentDirectory,
var folders = Directory.GetDirectories( "*",
parentDirectory, new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive }
"*", );
new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive }
); while (expansion-- >= 0)
{
while (expansion-- >= 0) foreach (var folder in folders)
{ {
foreach (var folder in folders) var di = new DirectoryInfo(folder);
{ if (di.Name.InsensitiveEquals(expansion.ToString()))
var di = new DirectoryInfo(folder); {
if (di.Name.InsensitiveEquals(expansion.ToString())) return folder;
{ }
return folder; }
} }
}
} return null;
}
return null;
} static ExpansionInfo()
{
static ExpansionInfo() var path = Path.Combine(Core.BaseDirectory, "Data/expansion.json");
{ var expansions = JsonConfig.Deserialize<List<ExpansionConfig>>(path);
var path = Path.Combine(Core.BaseDirectory, "Data/expansion.json");
var expansions = JsonConfig.Deserialize<List<ExpansionConfig>>(path); Table = new ExpansionInfo[expansions.Count];
Table = new ExpansionInfo[expansions.Count]; for (var i = 0; i < expansions.Count; i++)
{
for (var i = 0; i < expansions.Count; i++) var expansion = expansions[i];
{ if (expansion.ClientVersion != null)
var expansion = expansions[i]; {
if (expansion.ClientVersion != null) Table[i] = new ExpansionInfo(
{ i,
Table[i] = new ExpansionInfo( expansion.Name,
i, expansion.ClientVersion,
expansion.Name, expansion.FeatureFlags,
expansion.ClientVersion, expansion.CharacterListFlags,
expansion.FeatureFlags, expansion.HousingFlags,
expansion.CharacterListFlags, expansion.MobileStatusVersion
expansion.HousingFlags );
); }
} else
else {
{ Table[i] = new ExpansionInfo(
Table[i] = new ExpansionInfo( i,
i, expansion.Name,
expansion.Name, expansion.ClientFlags ?? ClientFlags.None,
expansion.ClientFlags ?? ClientFlags.None, expansion.FeatureFlags,
expansion.FeatureFlags, expansion.CharacterListFlags,
expansion.CharacterListFlags, expansion.HousingFlags,
expansion.HousingFlags expansion.MobileStatusVersion
); );
} }
} }
} }
public ExpansionInfo( public ExpansionInfo(
int id, int id,
string name, string name,
ClientFlags clientFlags, ClientFlags clientFlags,
FeatureFlags supportedFeatures, FeatureFlags supportedFeatures,
CharacterListFlags charListFlags, CharacterListFlags charListFlags,
HousingFlags customHousingFlag HousingFlags customHousingFlag,
) : this(id, name, supportedFeatures, charListFlags, customHousingFlag) => ClientFlags = clientFlags; int mobileStatusVersion
) : this(id, name, supportedFeatures, charListFlags, customHousingFlag, mobileStatusVersion) =>
public ExpansionInfo( ClientFlags = clientFlags;
int id,
string name, public ExpansionInfo(
ClientVersion requiredClient, int id,
FeatureFlags supportedFeatures, string name,
CharacterListFlags charListFlags, ClientVersion requiredClient,
HousingFlags customHousingFlag FeatureFlags supportedFeatures,
) : this(id, name, supportedFeatures, charListFlags, customHousingFlag) => RequiredClient = requiredClient; CharacterListFlags charListFlags,
HousingFlags customHousingFlag,
private ExpansionInfo( int mobileStatusVersion
int id, ) : this(id, name, supportedFeatures, charListFlags, customHousingFlag, mobileStatusVersion) =>
string name, RequiredClient = requiredClient;
FeatureFlags supportedFeatures,
CharacterListFlags charListFlags, private ExpansionInfo(
HousingFlags customHousingFlag int id,
) string name,
{ FeatureFlags supportedFeatures,
ID = id; CharacterListFlags charListFlags,
Name = name; HousingFlags customHousingFlag,
int mobileStatusVersion
SupportedFeatures = supportedFeatures; )
CharacterListFlags = charListFlags; {
CustomHousingFlag = customHousingFlag; ID = id;
} Name = name;
public static ExpansionInfo CoreExpansion => GetInfo(Core.Expansion); SupportedFeatures = supportedFeatures;
CharacterListFlags = charListFlags;
public static ExpansionInfo[] Table { get; } CustomHousingFlag = customHousingFlag;
MobileStatusVersion = mobileStatusVersion;
public int ID { get; } }
public string Name { get; set; }
public static ExpansionInfo CoreExpansion => GetInfo(Core.Expansion);
public ClientFlags ClientFlags { get; set; }
public FeatureFlags SupportedFeatures { get; set; } public static ExpansionInfo[] Table { get; }
public CharacterListFlags CharacterListFlags { get; set; }
public ClientVersion RequiredClient { get; set; } public int ID { get; }
public HousingFlags CustomHousingFlag { get; set; } public string Name { get; set; }
public ClientFlags ClientFlags { get; set; }
public static ExpansionInfo GetInfo(Expansion ex) => GetInfo((int)ex); public FeatureFlags SupportedFeatures { get; set; }
public CharacterListFlags CharacterListFlags { get; set; }
public static ExpansionInfo GetInfo(int ex) public ClientVersion RequiredClient { get; set; }
{ public HousingFlags CustomHousingFlag { get; set; }
var v = ex; public int MobileStatusVersion { get; set; }
if (v < 0 || v >= Table.Length) public static ExpansionInfo GetInfo(Expansion ex) => GetInfo((int)ex);
{
v = 0; public static ExpansionInfo GetInfo(int ex)
} {
var v = ex;
return Table[v];
} if (v < 0 || v >= Table.Length)
{
public override string ToString() => Name; v = 0;
} }
public record ExpansionConfig return Table[v];
{ }
public string Name { get; init; }
public override string ToString() => Name;
public ClientVersion? ClientVersion { get; init; } }
public ClientFlags? ClientFlags { get; init; } public record ExpansionConfig
{
[JsonConverter(typeof(FlagsConverter<FeatureFlags>))] public string Name { get; init; }
public FeatureFlags FeatureFlags { get; init; }
public ClientVersion? ClientVersion { get; init; }
[JsonConverter(typeof(FlagsConverter<CharacterListFlags>))]
public CharacterListFlags CharacterListFlags { get; init; } public ClientFlags? ClientFlags { get; init; }
[JsonConverter(typeof(FlagsConverter<HousingFlags>))] [JsonConverter(typeof(FlagsConverter<FeatureFlags>))]
public HousingFlags HousingFlags { get; init; } public FeatureFlags FeatureFlags { get; init; }
}
[JsonConverter(typeof(FlagsConverter<CharacterListFlags>))]
public CharacterListFlags CharacterListFlags { get; init; }
[JsonConverter(typeof(FlagsConverter<HousingFlags>))]
public HousingFlags HousingFlags { get; init; }
public int MobileStatusVersion { get; set; }
} }

View file

@ -39,13 +39,6 @@ public static class OutgoingMobilePackets
public const int MobileStatusMLLength = 91; public const int MobileStatusMLLength = 91;
public const int MobileStatusHSLength = 121; public const int MobileStatusHSLength = 121;
public static bool ExtendedStatus { get; private set; } = true;
public static void Configure()
{
ExtendedStatus = ServerConfiguration.GetSetting("client.showExtendedStatus", true);
}
public static void CreateBondedStatus(Span<byte> buffer, Serial serial, bool bonded) public static void CreateBondedStatus(Span<byte> buffer, Serial serial, bool bonded)
{ {
if (buffer[0] != 0) if (buffer[0] != 0)
@ -467,25 +460,31 @@ public static class OutgoingMobilePackets
version = 0; version = 0;
length = MobileStatusCompactLength; length = MobileStatusCompactLength;
} }
else if (ExtendedStatus && ns.ExtendedStatus)
{
version = 6;
length = MobileStatusHSLength;
}
else if (Core.ML && ns.SupportsExpansion(Expansion.ML))
{
version = 5;
length = MobileStatusMLLength;
}
else if (Core.AOS)
{
version = 4;
length = MobileStatusAOSLength;
}
else else
{ {
version = 3; var maxVersion = ExpansionInfo.CoreExpansion.MobileStatusVersion;
length = MobileStatusLength; var nsExpansion = (Expansion)ns.ExpansionInfo.ID;
if (maxVersion >= 6 && nsExpansion >= Expansion.HS && ns.ExtendedStatus)
{
version = 6;
length = MobileStatusHSLength;
}
else if (maxVersion >= 5 && nsExpansion >= Expansion.ML)
{
version = 5;
length = MobileStatusMLLength;
}
else if (maxVersion >= 4 && nsExpansion >= Expansion.AOS)
{
version = 4;
length = MobileStatusAOSLength;
}
else
{
version = 3;
length = MobileStatusLength;
}
} }
Span<byte> buffer = stackalloc byte[length]; Span<byte> buffer = stackalloc byte[length];
@ -563,6 +562,8 @@ public static class OutgoingMobilePackets
if (version >= 6) if (version >= 6)
{ {
// TODO: Once the new statuses are added, the length should be capped by expansion.
// This will allow newer clients to see AOS stats, but not newer ones.
for (var i = 0; i < 15; ++i) for (var i = 0; i < 15; ++i)
{ {
writer.Write((short)beheld.GetAOSStatus(i)); writer.Write((short)beheld.GetAOSStatus(i));

View file

@ -4,6 +4,7 @@ public static class AssistantProtocol
{ {
private static PacketHandler[] _handlers; private static PacketHandler[] _handlers;
[CallPriority(10)]
public static void Configure() public static void Configure()
{ {
_handlers = ProtocolExtensions<AssistantsProtocolInfo>.Register(new AssistantsProtocolInfo()); _handlers = ProtocolExtensions<AssistantsProtocolInfo>.Register(new AssistantsProtocolInfo());