Release combat sharing leases without interrupting dashboard vitals #3

Merged
erik merged 3 commits from codex/release-0.1.1 into main 2026-09-27 21:17:11 +00:00
7 changed files with 40 additions and 4 deletions
Showing only changes of commit 55dc989f85 - Show all commits

View file

@ -11,8 +11,8 @@ env:
# The OpenAC release whose AcDream.Plugin.Abstractions package this plugin # The OpenAC release whose AcDream.Plugin.Abstractions package this plugin
# builds against. PluginApiPackageVersion in Directory.Build.props has to # builds against. PluginApiPackageVersion in Directory.Build.props has to
# name the same version. # name the same version.
OPENAC_API_RELEASE_TAG: v0.1.19 OPENAC_API_RELEASE_TAG: v0.1.21
OPENAC_API_VERSION: 0.1.19 OPENAC_API_VERSION: 0.1.21
jobs: jobs:
build-and-test: build-and-test:

View file

@ -15,7 +15,7 @@
<!-- The plugin contract, compile-only, from the .nupkg in ./packages-local <!-- The plugin contract, compile-only, from the .nupkg in ./packages-local
(downloaded from the OpenAC release of the same version). The host (downloaded from the OpenAC release of the same version). The host
supplies the contract assembly at runtime. --> supplies the contract assembly at runtime. -->
<PluginApiPackageVersion>0.1.19</PluginApiPackageVersion> <PluginApiPackageVersion>0.1.21</PluginApiPackageVersion>
<RestorePackagesWithLockFile>false</RestorePackagesWithLockFile> <RestorePackagesWithLockFile>false</RestorePackagesWithLockFile>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View file

@ -56,3 +56,9 @@ dotnet run --project tools/FrameRecorder -- record 18765 frames.jsonl
/mm ws url ws://localhost:18765/websocket/ /mm ws url ws://localhost:18765/websocket/
dotnet run --project tools/FrameRecorder -- compare decal.jsonl port.jsonl dotnet run --project tools/FrameRecorder -- compare decal.jsonl port.jsonl
``` ```
## Local peer subscriptions
Requires OpenAC 0.1.21. Combat vital sharing acquires a local state/cast subscription only while enabled, and releases it on disable, logout or unload. Dashboard vitals still stream every five seconds directly from character state without enabling local peer communication. Existing remote WebSocket frames and timing are unchanged.
Before the new contract release exists, pack AcDream.Plugin.Abstractions 0.1.21 from the matching OpenAC checkout and copy its nupkg into packages-local before restore/build. CI downloads that release, so publish the OpenAC contract before dependent plugin releases.

View file

@ -56,6 +56,7 @@ internal sealed class VitalSharing : IMmFeature, IBackendConnectionListener, ISh
private readonly MmContext _context; private readonly MmContext _context;
private readonly PeerTable _peers = new(); private readonly PeerTable _peers = new();
private IDisposable? _timer; private IDisposable? _timer;
private IDisposable? _peerSubscription;
private int _lastHealth = -1, _lastMaxHealth = -1; private int _lastHealth = -1, _lastMaxHealth = -1;
private int _lastStamina = -1, _lastMaxStamina = -1; private int _lastStamina = -1, _lastMaxStamina = -1;
@ -103,6 +104,8 @@ internal sealed class VitalSharing : IMmFeature, IBackendConnectionListener, ISh
if (IsActive) if (IsActive)
return; return;
IsActive = true; IsActive = true;
_peerSubscription = _context.Automation.Network.Subscribe(
PluginPeerCapabilities.ClientState | PluginPeerCapabilities.Casts);
ResetOutgoingState(); ResetOutgoingState();
_timer = _context.Scheduler.Every(TickInterval, OnTick); _timer = _context.Scheduler.Every(TickInterval, OnTick);
_context.Send(new _context.Send(new
@ -121,6 +124,8 @@ internal sealed class VitalSharing : IMmFeature, IBackendConnectionListener, ISh
if (!IsActive) if (!IsActive)
return; return;
IsActive = false; IsActive = false;
_peerSubscription?.Dispose();
_peerSubscription = null;
_timer?.Dispose(); _timer?.Dispose();
_timer = null; _timer = null;
_context.Send(new _context.Send(new

View file

@ -6,7 +6,7 @@
"apiVersion": 1, "apiVersion": 1,
"kinds": ["Gameplay"], "kinds": ["Gameplay"],
"hosts": ["Graphical", "Headless"], "hosts": ["Graphical", "Headless"],
"minHostVersion": "0.1.19", "minHostVersion": "0.1.21",
"capabilitiesVersion": 1, "capabilitiesVersion": 1,
"capabilities": [ "capabilities": [
{ "name": "network", "note": "Streams stats, vitals, chat and inventory to Mosswart Overlord, and receives shared vitals and commands from it." }, { "name": "network", "note": "Streams stats, vitals, chat and inventory to Mosswart Overlord, and receives shared vitals and commands from it." },

View file

@ -8,6 +8,16 @@ namespace OpenAC.MosswartMassacre.Tests.Fakes;
/// </summary> /// </summary>
public sealed class FakeNetwork : INetworkAutomation public sealed class FakeNetwork : INetworkAutomation
{ {
public bool SupportsSubscriptions => true;
public List<PluginPeerCapabilities> Requests { get; } = [];
public int ActiveLeases { get; private set; }
public IDisposable Subscribe(PluginPeerCapabilities capabilities)
{ Requests.Add(capabilities); ActiveLeases++; return new Lease(this); }
private sealed class Lease(FakeNetwork owner) : IDisposable
{
private bool _disposed;
public void Dispose() { if (_disposed) return; _disposed = true; owner.ActiveLeases--; }
}
public bool IsAvailable { get; set; } = true; public bool IsAvailable { get; set; } = true;
public List<PluginNetworkClient> ImportedClients { get; } = []; public List<PluginNetworkClient> ImportedClients { get; } = [];

View file

@ -42,6 +42,21 @@ public sealed class VitalSharingTests
private static VitalSharing Feature(PluginHarness harness) => harness.Feature<VitalSharing>(); private static VitalSharing Feature(PluginHarness harness) => harness.Feature<VitalSharing>();
[Fact]
public void Local_peer_subscription_exists_only_while_combat_sharing_is_enabled()
{
using var harness = Sharing();
var network = harness.Host.Automation.Network;
Assert.Empty(network.Requests);
Feature(harness).Start();
Feature(harness).Start();
Assert.Equal(1, network.ActiveLeases);
Assert.Equal(PluginPeerCapabilities.ClientState | PluginPeerCapabilities.Casts, Assert.Single(network.Requests));
Feature(harness).OnLogoff();
Feature(harness).Dispose();
Assert.Equal(0, network.ActiveLeases);
}
[Fact] [Fact]
public void Subscribe_on_start_carries_the_player_id_and_tags() public void Subscribe_on_start_carries_the_player_id_and_tags()
{ {