using System;
using AcDream.Core.Net.Messages;
using Xunit;
namespace AcDream.Core.Net.Tests.Messages;
///
/// Golden-byte conformance for the two inbound script-playback GameMessages:
/// PlayScriptId (0xF754) and PlayEffect (0xF755).
///
/// Oracle derivation. Bytes are generated by
/// (a faithful mirror of ACE's
/// Extensions.cs writers).
///
/// For 0xF755, ACE's
/// GameMessages/Messages/GameMessageScript.cs writes:
///
/// Writer.WriteGuid(guid); // u32
/// Writer.Write((uint)scriptId); // u32
/// Writer.Write(speed); // f32, default 1.0f
///
/// declaring a 16-byte message (4 opcode + 12 payload), which matches
/// .
///
/// For 0xF754, ACE names the opcode PlayScriptId
/// (GameMessageOpcode.cs:63) and the retail handler is
/// SmartBox::HandlePlayScriptID (0x00452020) — guid + script DID, no
/// intensity, giving the 12-byte body
/// asserts.
///
public class PlayScriptGoldenTests
{
// ---- 0xF754 PlayScriptId -------------------------------------------------
public static TheoryData ScriptIdCases() => new()
{
{ 0x50000001u, 0x0D000001u }, // player, a portal-space script DID
{ 0x7C95B01Au, 0x00000000u }, // zero DID must survive as zero, not null
{ 0xFFFFFFFFu, 0xFFFFFFFFu }, // full-width guid + DID
};
[Theory]
[MemberData(nameof(ScriptIdCases))]
public void PlayScriptId_AceGoldenBytes_DecodesGuidAndDid(uint guid, uint scriptDid)
{
byte[] body = AceWireWriter.GameMessage(PlayPhysicsScript.Opcode)
.WriteGuid(guid)
.Write(scriptDid)
.ToArray();
Assert.Equal(PlayPhysicsScript.WireSize, body.Length);
PlayPhysicsScript? parsed = PlayPhysicsScript.TryParse(body);
Assert.NotNull(parsed);
Assert.Equal(guid, parsed!.Value.Guid);
Assert.Equal(scriptDid, parsed.Value.ScriptDid);
}
[Fact]
public void PlayScriptId_WrongOpcode_ReturnsNull()
{
byte[] body = AceWireWriter.GameMessage(0xF755u)
.WriteGuid(1u).Write(2u).ToArray();
Assert.Null(PlayPhysicsScript.TryParse(body));
}
[Fact]
public void PlayScriptId_TrailingByte_ReturnsNull()
{
// The parser demands an exact length; a 13-byte body is not a
// truncated-but-usable 0xF754.
byte[] body = AceWireWriter.GameMessage(PlayPhysicsScript.Opcode)
.WriteGuid(1u).Write(2u).Pad(1).ToArray();
Assert.Null(PlayPhysicsScript.TryParse(body));
}
// ---- 0xF755 PlayEffect ---------------------------------------------------
public static TheoryData ScriptTypeCases() => new()
{
// ACE's default speed argument is 1.0f.
{ 0x50000001u, 0x00000021u, 1.0f },
// Intensity is a free float on the wire; fractional values must survive.
{ 0x7C95B01Au, 0x00000083u, 0.25f },
// Zero intensity is meaningful (script suppressed), not "absent".
{ 0x800114C0u, 0x00000001u, 0f },
// Unknown type values are retained losslessly for the resolver to reject.
{ 0xA9B40001u, 0xDEADBEEFu, -3.5f },
};
[Theory]
[MemberData(nameof(ScriptTypeCases))]
public void PlayEffect_AceGoldenBytes_DecodesGuidTypeAndIntensity(
uint guid, uint rawScriptType, float intensity)
{
byte[] body = AceWireWriter.GameMessage(PlayPhysicsScriptType.Opcode)
.WriteGuid(guid)
.Write(rawScriptType)
.Write(intensity)
.ToArray();
// ACE declares GameMessageScript's length as 16.
Assert.Equal(PlayPhysicsScriptType.WireSize, body.Length);
Assert.Equal(16, body.Length);
PlayPhysicsScriptType? parsed = PlayPhysicsScriptType.TryParse(body);
Assert.NotNull(parsed);
Assert.Equal(guid, parsed!.Value.Guid);
Assert.Equal(rawScriptType, parsed.Value.RawScriptType);
Assert.Equal(intensity, parsed.Value.Intensity);
}
[Fact]
public void PlayEffect_NonFiniteIntensity_IsRetainedLosslessly()
{
// The parser documents that non-finite floats are kept for the
// resolver to reject rather than being coerced at parse time.
byte[] body = AceWireWriter.GameMessage(PlayPhysicsScriptType.Opcode)
.WriteGuid(0x50000001u)
.Write(0x00000021u)
.Write(float.NaN)
.ToArray();
PlayPhysicsScriptType? parsed = PlayPhysicsScriptType.TryParse(body);
Assert.NotNull(parsed);
Assert.True(float.IsNaN(parsed!.Value.Intensity));
}
[Fact]
public void PlayEffect_WrongOpcode_ReturnsNull()
{
byte[] body = AceWireWriter.GameMessage(0xF754u)
.WriteGuid(1u).Write(2u).Write(1.0f).ToArray();
Assert.Null(PlayPhysicsScriptType.TryParse(body));
}
}