acdream/src/AcDream.Core/Items/HouseRestrictions.cs

63 lines
2.8 KiB
C#

using System.Collections.Frozen;
using System.Collections.Generic;
namespace AcDream.Core.Items;
/// <summary>
/// AP-129 (Campaign P Slice P4 review fix, 2026-07-30). Retail
/// <c>RestrictionDB</c> — the access-control list carried on a house/dwelling
/// object's <c>PublicWeenieDesc</c> (<c>WeenieHeaderFlag.HouseRestrictions</c>,
/// 0x04000000) and refreshed live via <c>House_UpdateRestrictions (0x0248)</c>.
/// Wire shape confirmed verbatim against
/// <c>references/Chorizite.ACProtocol/Chorizite.ACProtocol/Types/RestrictionDB.generated.cs</c>
/// and <c>protocol.xml:6270-6275</c>: <c>uint Version, uint Flags (0=private,
/// 1=open), ObjectId MonarchId, PHashTable&lt;ObjectId,uint&gt; Permissions</c>.
/// </summary>
/// <param name="OpenToPublic">Wire <c>Flags != 0</c> — retail's <c>_bitmask
/// &amp; 1</c>. When true, every mover is admitted regardless of the guest
/// list.</param>
/// <param name="AllegianceMonarchId">Wire <c>MonarchId</c> — retail
/// <c>_monarch_iid</c>. A mover whose own monarch matches this id is admitted
/// (allegiance-wide access), independent of the guest list.</param>
/// <param name="Guests">Wire <c>Permissions</c> — guid to permission value
/// (0 = dwelling access only, 1 = storage access also). Retail's
/// <c>IsAllowedIn</c> only consults key membership for entry; the permission
/// value is preserved for wire fidelity but not consulted here.</param>
public sealed record HouseRestrictionRecord
{
public HouseRestrictionRecord(
bool OpenToPublic,
uint AllegianceMonarchId,
IReadOnlyDictionary<uint, uint> Guests)
{
ArgumentNullException.ThrowIfNull(Guests);
this.OpenToPublic = OpenToPublic;
this.AllegianceMonarchId = AllegianceMonarchId;
this.Guests = Guests.ToFrozenDictionary();
}
public bool OpenToPublic { get; }
public uint AllegianceMonarchId { get; }
/// <summary>
/// Immutable snapshot of the wire permission table. The parser's mutable
/// dictionary must never remain an untracked mutation path into live
/// collision-entry authority.
/// </summary>
public IReadOnlyDictionary<uint, uint> Guests { get; }
/// <summary>
/// Verbatim port of retail <c>RestrictionDB::IsAllowedIn</c>
/// (named-retail pc:444493-444516, 0x005ae8f0):
/// <c>if ((_bitmask &amp; 1) == 0) { if (_monarch_iid == 0 || arg3 !=
/// _monarch_iid) { if (arg2 == 0) return 0; if (!table.find(arg2)) return
/// 0; } } return 1;</c> — open bit set, OR mover shares the house's
/// allegiance monarch, OR mover's own guid is a table member.
/// </summary>
public bool IsAllowedIn(uint moverId, uint moverMonarchId)
{
if (OpenToPublic) return true;
if (AllegianceMonarchId != 0 && moverMonarchId == AllegianceMonarchId) return true;
return moverId != 0 && Guests.ContainsKey(moverId);
}
}