Fields, order and types as the original sent them. Values follow the original's client library where it matters: race and gender are the player's string properties, birth the creation time as a US date (explicit pattern, so no newer-culture space quirk), burden the units over 150 times effective strength truncated to a percent, capacity with the carrying augmentation, and properties the player's integer properties minus the original's blacklist. Attributes, vitals and skills carry unbuffed values, creation being the value before experience was spent. Titles, the allegiance tree and luminance total come through seams that the plugin API additions fill; until then they are null, as the original sent them before the matching server message arrived. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
99 lines
4.7 KiB
C#
99 lines
4.7 KiB
C#
using AcDream.Plugin.Abstractions;
|
|
using OpenAC.MosswartMassacre.Backend;
|
|
using OpenAC.MosswartMassacre.Streams;
|
|
|
|
namespace OpenAC.MosswartMassacre.Tests.Streams;
|
|
|
|
/// <summary>The character_stats frame as the original's CharacterStats.CollectAndSend wrote it.</summary>
|
|
public sealed class CharacterStatsTests
|
|
{
|
|
private const string Stamp = "2026-09-25T08:30:15.1230000Z";
|
|
|
|
private static PluginHarness Arranged()
|
|
{
|
|
var harness = new PluginHarness("Mossy");
|
|
var c = harness.Host.Automation.Character;
|
|
c.Level = 126;
|
|
c.Attributes =
|
|
[
|
|
new PluginAttributeInfo(0, "Strength", 300u) { Base = 290u, Ranks = 190u },
|
|
new PluginAttributeInfo(1, "Endurance", 200u) { Base = 200u, Ranks = 100u },
|
|
];
|
|
c.Vitals = [new PluginVitalInfo(0, "Health", 300u, 341u) { Base = 331u }];
|
|
c.Skills.Add(new PluginSkillInfo(34u, "War Magic", PluginSkillTraining.Specialized, 540u) { Base = 533u });
|
|
c.Skills.Add(new PluginSkillInfo(6u, "Melee Defense", PluginSkillTraining.Trained, 400u) { Base = 388u });
|
|
harness.Host.Automation.Objects.Properties[0x50000001] = new PluginItemProperties(
|
|
new Dictionary<uint, int>
|
|
{
|
|
[5] = 9000, // burden units (blacklisted from properties)
|
|
[24] = 3, // skill credits (blacklisted)
|
|
[43] = 12, // deaths (blacklisted)
|
|
[98] = 1_000_000_000, // creation time (blacklisted)
|
|
[230] = 2, // carrying-capacity augmentations
|
|
[218] = 1, // an augmentation: kept
|
|
},
|
|
new Dictionary<uint, long> { [1] = 123_456_789_012L, [2] = 5_000L, [6] = 500_000L },
|
|
new Dictionary<uint, bool>(),
|
|
new Dictionary<uint, double>(),
|
|
new Dictionary<uint, string> { [3] = "Male", [4] = "Aluvian" },
|
|
new Dictionary<uint, uint>(),
|
|
new Dictionary<uint, uint>());
|
|
harness.Login();
|
|
harness.Frames.Clear();
|
|
return harness;
|
|
}
|
|
|
|
[Fact]
|
|
public void The_frame_keeps_the_originals_fields_order_and_types()
|
|
{
|
|
// CharacterStats.cs:252-383. burden = (int)((float)9000 / (300*150) * 100) = 20;
|
|
// capacity = 150*300 + 2*30*300 = 63000; birth = 1970 + 1e9 s in en-US.
|
|
using var harness = Arranged();
|
|
var stream = harness.Feature<CharacterStatsStream>();
|
|
stream.CurrentTitle = () => 42;
|
|
stream.Titles = () => ["Adventurer", "Sawato Resident"];
|
|
stream.Allegiance = () => new AllegianceSummary(
|
|
"Mossy Club", new AllegianceMember("King", 1, 8, 1), null, 3, 17);
|
|
|
|
string json = Wire.Serialize(stream.Build());
|
|
|
|
Assert.Equal(
|
|
"{\"type\":\"character_stats\",\"timestamp\":\"" + Stamp + "\",\"character_name\":\"Mossy\","
|
|
+ "\"level\":126,\"race\":\"Aluvian\",\"gender\":\"Male\",\"birth\":\"9/9/2001 1:46:40 AM\","
|
|
+ "\"total_xp\":123456789012,\"unassigned_xp\":5000,\"skill_credits\":3,\"deaths\":12,"
|
|
+ "\"burden\":20,\"burden_units\":9000,\"encumbrance_capacity\":63000,"
|
|
+ "\"luminance_earned\":500000,\"luminance_total\":null,\"current_title\":42,"
|
|
+ "\"attributes\":{\"strength\":{\"base\":290,\"creation\":100},\"endurance\":{\"base\":200,\"creation\":100}},"
|
|
+ "\"vitals\":{\"health\":{\"base\":331}},"
|
|
+ "\"skills\":{\"melee_defense\":{\"base\":388,\"training\":\"Trained\"},\"war_magic\":{\"base\":533,\"training\":\"Specialized\"}},"
|
|
+ "\"allegiance\":{\"name\":\"Mossy Club\",\"monarch\":{\"name\":\"King\",\"race\":1,\"rank\":8,\"gender\":1},\"patron\":null,\"rank\":3,\"followers\":17},"
|
|
+ "\"properties\":{\"230\":2,\"218\":1},"
|
|
+ "\"titles\":[\"Adventurer\",\"Sawato Resident\"]}",
|
|
json);
|
|
}
|
|
|
|
[Fact]
|
|
public void Unknown_title_and_allegiance_are_null()
|
|
{
|
|
using var harness = Arranged();
|
|
|
|
string json = Wire.Serialize(harness.Feature<CharacterStatsStream>().Build());
|
|
|
|
Assert.Contains("\"current_title\":null,", json, StringComparison.Ordinal);
|
|
Assert.Contains("\"allegiance\":null,", json, StringComparison.Ordinal);
|
|
Assert.EndsWith("\"titles\":null}", json, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Sent_five_seconds_after_login_then_every_ten_minutes()
|
|
{
|
|
using var harness = Arranged();
|
|
|
|
harness.Run(4.5, 0.5);
|
|
Assert.Empty(harness.FramesOfType("character_stats"));
|
|
harness.Run(0.6, 0.1);
|
|
Assert.Single(harness.FramesOfType("character_stats"));
|
|
harness.Run(600, 5);
|
|
Assert.Equal(2, harness.FramesOfType("character_stats").Count());
|
|
}
|
|
}
|