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

View file

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

View file

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

View file

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