Add per-profile "Lock to exact peer addresses" setting (#17, held for next release)

Feature request from the same singer: a way to pin a profile to exact peer addresses
so RemSound never matches the other computer by its advertised name and never switches
to a different address it discovers — for a machine reachable at two addresses at once
(VPN + LAN), they want only the one they chose, and would rather the connection die than
wander. The logical end of the v4.7/v4.8 direction.

New per-profile Profile.LockPeerAddresses (default false), round-tripped through
RemSoundSettingsStore (ApplyProfile/CopyTo + Load/SaveLockPeerAddresses), mirroring the
PriorityMode pattern. New AccessibleCheckBox on the Connectivity tab ("Lock to these exact
peer addresses, no matter what — never follow names or switch", Alt+L), saved with the
profile, marks the profile dirty on change. When set, RefreshKnownPeers early-returns
before the discovered-peer merge and the address-follow, so the profile's peers stay
exactly as set (the allow-list is still pushed). Off = unchanged behaviour.

Manual: rewrote the "Connecting to one specific IP" section (which over-promised that
add-by-IP "can never drift" — the merge/follow could) into an unambiguous "Locking a
profile to one exact address" section, plus a Connectivity-tab control-table row.

Held for the next release.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ednunp
2026-06-28 15:31:50 +01:00
co-authored by Claude Opus 4.8
parent 0df2a576fc
commit c7d422e3ba
5 changed files with 86 additions and 17 deletions
@@ -282,6 +282,19 @@ public sealed class RemSoundSettingsStore
Save(s);
}
/// <summary>When true, this profile is locked to the exact peer addresses the user set — RemSound
/// uses only those, never follows the other computer by its advertised name and never switches to a
/// discovered address. Off by default; opt in per profile. (#17)</summary>
public bool LoadLockPeerAddresses(bool defaultValue = false) =>
Try(() => Load()?.LockPeerAddresses) ?? defaultValue;
public void SaveLockPeerAddresses(bool value)
{
var s = Load() ?? new Settings();
s.LockPeerAddresses = value;
Save(s);
}
/// <summary>Suppresses the connect/disconnect sound cues that play when a peer's health
/// transitions to/from Healthy. Off by default — cues are on. Saved per-profile so users
/// who don't want them in a given setup don't have to remember to mute every session.
@@ -537,6 +550,7 @@ public sealed class RemSoundSettingsStore
SendRate = profile.SendRate,
TightLatencyMode = profile.TightLatencyMode,
PriorityMode = profile.PriorityMode,
LockPeerAddresses = profile.LockPeerAddresses,
Smoothness = profile.Smoothness,
ConcealmentArtifact = (ConcealmentArtifact)profile.ConcealmentArtifactRaw,
MuteConnectionCues = profile.MuteConnectionCues,
@@ -581,6 +595,7 @@ public sealed class RemSoundSettingsStore
if (s.SendRate is SendRate sr) profile.SendRate = sr;
if (s.TightLatencyMode is bool tl) profile.TightLatencyMode = tl;
if (s.PriorityMode is bool pm) profile.PriorityMode = pm;
if (s.LockPeerAddresses is bool lpa) profile.LockPeerAddresses = lpa;
if (s.Smoothness is int sm) profile.Smoothness = sm;
if (s.ConcealmentArtifact is ConcealmentArtifact ca) profile.ConcealmentArtifactRaw = (int)ca;
if (s.MuteConnectionCues is bool mc) profile.MuteConnectionCues = mc;
@@ -626,6 +641,7 @@ public sealed class RemSoundSettingsStore
public SendRate? SendRate { get; set; }
public bool? TightLatencyMode { get; set; }
public bool? PriorityMode { get; set; }
public bool? LockPeerAddresses { get; set; }
public int? Smoothness { get; set; }
public ConcealmentArtifact? ConcealmentArtifact { get; set; }
public bool? MuteConnectionCues { get; set; }