From 2ac804ed6c57f00bcd066bd3945e43d0d1180eda Mon Sep 17 00:00:00 2001 From: VinPropane Date: Mon, 15 Jun 2026 20:32:53 -0400 Subject: [PATCH] NEO-135: validate faction before player gate in in-memory store Match PostgresFactionStandingStore check order so unknown faction ids return unknown_faction even when the player is not writable. --- .../InMemoryFactionStandingStoreTests.cs | 15 ++++++++++++++ .../Factions/InMemoryFactionStandingStore.cs | 20 +++++++++---------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/server/NeonSprawl.Server.Tests/Game/Factions/InMemoryFactionStandingStoreTests.cs b/server/NeonSprawl.Server.Tests/Game/Factions/InMemoryFactionStandingStoreTests.cs index b5dd1f3..bf11902 100644 --- a/server/NeonSprawl.Server.Tests/Game/Factions/InMemoryFactionStandingStoreTests.cs +++ b/server/NeonSprawl.Server.Tests/Game/Factions/InMemoryFactionStandingStoreTests.cs @@ -118,6 +118,21 @@ public sealed class InMemoryFactionStandingStoreTests Assert.Equal(0, store.TryGetStanding(PlayerId, GridFactionId).Standing); } + [Fact] + public void TryGetStanding_ShouldPreferUnknownFaction_WhenPlayerNotWritableAndFactionUnknown() + { + // Arrange + var store = CreateStore(); + // Act + var read = store.TryGetStanding(UnknownPlayerId, "not_a_real_faction"); + var write = store.TryApplyStandingDelta(UnknownPlayerId, "not_a_real_faction", 5); + // Assert + Assert.False(read.Success); + Assert.Equal(FactionStandingReasonCodes.UnknownFaction, read.ReasonCode); + Assert.False(write.Success); + Assert.Equal(FactionStandingReasonCodes.UnknownFaction, write.ReasonCode); + } + [Fact] public void TryApplyStandingDelta_ShouldDenyNonWritablePlayer() { diff --git a/server/NeonSprawl.Server/Game/Factions/InMemoryFactionStandingStore.cs b/server/NeonSprawl.Server/Game/Factions/InMemoryFactionStandingStore.cs index 3a5eb6c..9da80e8 100644 --- a/server/NeonSprawl.Server/Game/Factions/InMemoryFactionStandingStore.cs +++ b/server/NeonSprawl.Server/Game/Factions/InMemoryFactionStandingStore.cs @@ -48,16 +48,16 @@ public sealed class InMemoryFactionStandingStore( return DenyRead(FactionStandingReasonCodes.UnknownFaction); } - if (!CanWritePlayer(player)) - { - return DenyRead(FactionStandingReasonCodes.PlayerNotWritable); - } - if (!factionRegistry.TryGetDefinition(faction, out var definition) || definition is null) { return DenyRead(FactionStandingReasonCodes.UnknownFaction); } + if (!CanWritePlayer(player)) + { + return DenyRead(FactionStandingReasonCodes.PlayerNotWritable); + } + var key = FactionStandingIds.MakeStandingKey(player, faction); var raw = standingByKey.GetValueOrDefault(key, 0); return new FactionStandingReadOutcome(true, null, FactionStandingIds.ClampStanding(raw, definition)); @@ -78,16 +78,16 @@ public sealed class InMemoryFactionStandingStore( return DenyMutation(FactionStandingReasonCodes.UnknownFaction); } - if (!CanWritePlayer(player)) - { - return DenyMutation(FactionStandingReasonCodes.PlayerNotWritable); - } - if (!factionRegistry.TryGetDefinition(faction, out var definition) || definition is null) { return DenyMutation(FactionStandingReasonCodes.UnknownFaction); } + if (!CanWritePlayer(player)) + { + return DenyMutation(FactionStandingReasonCodes.PlayerNotWritable); + } + var key = FactionStandingIds.MakeStandingKey(player, faction); lock (keyLocks.GetOrAdd(key, _ => new object())) {