feat(ui): share indicator detail panels

Port the authored Link Status, Vitae, and Mini Game detail roots and register every indicator page with retail's one-active gmPanelUI owner. Helpful/Harmful and the new pages now replace Inventory, Character, or Magic at one canonical window position while preserving the DAT restore-previous flag.

Correct the retail ping wire to its payload-free request/response, publish measured RTT, and port Vitae recovery XP from the live modifier and player properties. Keep transport packet-loss averaging and mini-game gameplay explicitly tracked under AP-110.

Release build and all 5,814 tests pass with five intentional skips. Connected visual gate pending.

Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-17 10:27:41 +02:00
parent 52c529be86
commit a96767ba6d
28 changed files with 6539 additions and 32 deletions

View file

@ -1,4 +1,6 @@
using AcDream.Core.Net;
using AcDream.Core.Net.Messages;
using System.Net;
using Xunit;
namespace AcDream.Core.Net.Tests;
@ -43,4 +45,40 @@ public sealed class WorldSessionLinkStatusTests
Assert.True(snapshot.Connected);
Assert.Equal(0d, snapshot.SecondsSinceLastPacket);
}
[Fact]
public void ConnectedSnapshot_CarriesPacketLossAndRoundTripTelemetry()
{
LinkStatusSnapshot snapshot = WorldSession.BuildLinkStatus(
WorldSession.State.InWorld,
lastInboundPacketTicks: 100,
nowTicks: 200,
frequency: 100,
roundTripSeconds: 0.125d,
packetLossPercentage: 2.5d);
Assert.Equal(2.5d, snapshot.PacketLossPercentage);
Assert.Equal(0.125d, snapshot.RoundTripSeconds);
}
[Fact]
public void RequestAndEmptyPingResponse_MeasureMonotonicRoundTrip()
{
using var session = new WorldSession(
new IPEndPoint(IPAddress.Loopback, 65000));
byte[]? action = null;
session.GameActionCapture = bytes => action = bytes;
session.RequestLinkStatusPing();
session.GameEvents.Dispatch(new GameEventEnvelope(
PlayerGuid: 0u,
Sequence: 1u,
EventType: GameEventType.PingResponse,
Payload: ReadOnlyMemory<byte>.Empty));
Assert.NotNull(action);
Assert.Equal(12, action.Length);
Assert.NotNull(session.PingRoundTripSeconds);
Assert.True(session.PingRoundTripSeconds >= 0d);
}
}