diff --git a/LibreMetaverse.Tests/AppearanceManagerTests.cs b/LibreMetaverse.Tests/AppearanceManagerTests.cs index fb20697d..da77593e 100644 --- a/LibreMetaverse.Tests/AppearanceManagerTests.cs +++ b/LibreMetaverse.Tests/AppearanceManagerTests.cs @@ -1,3 +1,4 @@ +using System.Linq; using NUnit.Framework; namespace LibreMetaverse.Tests @@ -5,6 +6,30 @@ namespace LibreMetaverse.Tests [TestFixture] public class AppearanceManagerTests { + [Test] + public void MakeAppearancePacket_UsesCompleteVisualParamWireSequence() + { + var client = new GridClient(); + var packet = client.Appearance.MakeAppearancePacket(); + var wireParamIds = VisualParams.Group0ParamIds; + var expectedValues = wireParamIds + .Select(id => VisualParams.Params[id]) + .Select(param => Utils.FloatToByte(param.DefaultValue, param.MinValue, param.MaxValue)) + .ToArray(); + + Assert.Multiple(() => + { + Assert.That(wireParamIds, Has.Length.EqualTo(253)); + Assert.That(wireParamIds, Is.Ordered); + Assert.That(wireParamIds.Distinct().Count(), Is.EqualTo(wireParamIds.Length)); + Assert.That(wireParamIds.All(id => + VisualParams.Params.TryGetValue(id, out var param) && + (param.Group == 0 || param.Group == 3)), Is.True); + Assert.That(packet.VisualParam.Select(block => block.ParamValue), + Is.EqualTo(expectedValues)); + }); + } + [Test] public void WearableTypeToAssetType_BodypartsAndClothing_ReturnsExpected() { diff --git a/LibreMetaverse.Tests/AppearanceTests.cs b/LibreMetaverse.Tests/AppearanceTests.cs index 10a640a3..c025693a 100644 --- a/LibreMetaverse.Tests/AppearanceTests.cs +++ b/LibreMetaverse.Tests/AppearanceTests.cs @@ -70,8 +70,8 @@ public void DecodeVisualParams_CountMatchesGroup0ParamCount() [Test] public void DecodeVisualParams_RoundTrip_DefaultValues() { - // Build the byte array in Group0ParamIds document order — this is the order - // DecodeVisualParams consumes, which differs from the SortedList key order. + // Build the byte array in Group0ParamIds wire order — this is the order + // DecodeVisualParams consumes. var bytes = VisualParams.Group0ParamIds .Select(id => VisualParams.Params.TryGetValue(id, out var vp) ? Utils.FloatToByte(vp.DefaultValue, vp.MinValue, vp.MaxValue) @@ -176,7 +176,7 @@ public void AvatarAppearanceEventArgs_DecodeVisualParams_EmptyList_ReturnsEmptyD [Test] public void AvatarAppearanceEventArgs_DecodeVisualParams_RoundTrip_DefaultValues() { - // Build bytes in Group0ParamIds document order — this is the order DecodeVisualParams consumes. + // Build bytes in Group0ParamIds wire order — this is the order DecodeVisualParams consumes. var bytes = VisualParams.Group0ParamIds .Select(id => VisualParams.Params.TryGetValue(id, out var vp) ? Utils.FloatToByte(vp.DefaultValue, vp.MinValue, vp.MaxValue) diff --git a/LibreMetaverse/Appearance/AppearanceManager.cs b/LibreMetaverse/Appearance/AppearanceManager.cs index 34c535ef..ddecc9d9 100644 --- a/LibreMetaverse/Appearance/AppearanceManager.cs +++ b/LibreMetaverse/Appearance/AppearanceManager.cs @@ -2484,16 +2484,12 @@ public AgentSetAppearancePacket MakeAppearancePacket() { #region VisualParam - var vpIndex = 0; - var wearingPhysics = Wearables.ContainsKey(WearableType.Physics); + var wireParamIds = VisualParams.Group0ParamIds; + set.VisualParam = new AgentSetAppearancePacket.VisualParamBlock[wireParamIds.Length]; - var nrParams = wearingPhysics ? 251 : 218; - set.VisualParam = new AgentSetAppearancePacket.VisualParamBlock[nrParams]; - - foreach (var kvp in VisualParams.Params) + for (var vpIndex = 0; vpIndex < wireParamIds.Length; ++vpIndex) { - if (vpIndex >= nrParams) break; - var vp = kvp.Value; + var vp = VisualParams.Params[wireParamIds[vpIndex]]; var paramValue = 0f; var found = Wearables.Any(wearableList => wearableList.Value.Any(wearable => wearable.Asset != null && wearable.Asset.Params.TryGetValue(vp.ParamID, out paramValue))); @@ -2505,7 +2501,6 @@ public AgentSetAppearancePacket MakeAppearancePacket() { ParamValue = Utils.FloatToByte(paramValue, vp.MinValue, vp.MaxValue) }; - ++vpIndex; // Check if this is one of the visual params used in the agent height calculation switch (vp.ParamID) diff --git a/LibreMetaverse/Avatar.cs b/LibreMetaverse/Avatar.cs index 217afcef..6944f474 100644 --- a/LibreMetaverse/Avatar.cs +++ b/LibreMetaverse/Avatar.cs @@ -498,7 +498,7 @@ public string GroupName /// /// /// The AvatarAppearance packet transmits both TWEAKABLE (group-0) and - /// TRANSMIT_NOT_TWEAKABLE (group-3) params interleaved in avatar_lad.xml document order. + /// TRANSMIT_NOT_TWEAKABLE (group-3) params interleaved in ascending numeric ID order. /// includes both groups in that order so each /// byte maps to the correct parameter. /// @@ -662,4 +662,3 @@ public override OSD GetOSD() } } - diff --git a/SourceGenerators/VisualParamGenerator/VisualParamGenerator.cs b/SourceGenerators/VisualParamGenerator/VisualParamGenerator.cs index 97d698b4..667535f4 100644 --- a/SourceGenerators/VisualParamGenerator/VisualParamGenerator.cs +++ b/SourceGenerators/VisualParamGenerator/VisualParamGenerator.cs @@ -294,7 +294,7 @@ public static class VisualParams public static SortedList Params = new SortedList(); /// - /// Group-0 parameter IDs in the exact order they appear in avatar_lad.xml. + /// Group-0 and group-3 parameter IDs in ascending numeric ID order. /// This order matches the byte sequence in the AvatarAppearance packet visual_param block. /// public static int[] Group0ParamIds = Array.Empty(); @@ -382,7 +382,7 @@ private static string GenerateFromTemplateAndXml(string templateText, string xml var nodes = doc.GetElementsByTagName("param"); var ids = new SortedList(); - var group0IdsInOrder = new List(); // group-0 and group-3 (TRANSMIT_NOT_TWEAKABLE) IDs in avatar_lad.xml document order + var group0IdsInOrder = new List(); // group-0 and group-3 (TRANSMIT_NOT_TWEAKABLE) wire IDs var alphas = new Dictionary(); var colors = new Dictionary(); var drivenParamInfoMap = new Dictionary(); @@ -640,7 +640,7 @@ private static string GenerateFromTemplateAndXml(string templateText, string xml { // group-3 (VISUAL_PARAM_GROUP_TRANSMIT_NOT_TWEAKABLE) params are also // transmitted in the AvatarAppearance packet interleaved with group-0 params - // in avatar_lad.xml document order. They must be included here so that + // in ascending numeric ID order. They must be included here so that // DecodeVisualParams() reads each byte from the correct offset. group0IdsInOrder.Add(id); }