using System.ComponentModel.DataAnnotations; namespace DorkNet.Server.Data.Entities; /// /// Durable record of a private (invite-only) Photon match created via /// POST /goto/room/{name}?CreatePrivateInstance=True. Replaces /// the in-process ConcurrentDictionary in /// so /// invitations land on any replica. /// /// Without this row in shared storage, an invite issued by Player A /// (replica 1) wouldn't be visible to Player B's POST /goto/instance/{id} /// when their request lands on replica 2 — they'd be routed to a fresh /// public lobby instead of the inviter's match. /// /// The associated invite list lives in ; /// we keep it as a join table rather than a JSON column so the per-row /// "is X invited to Y?" check stays a fast indexed lookup. /// public class PrivateInstanceEntity { /// The instance id used as the wire roomInstanceId /// in matchmaking responses. Generated server-side at creation /// time; stable for the lifetime of the match. NOT autoincrement — /// the value is computed from owner/room/sub plus a nonce so the /// id is itself unforgeable without the nonce. public long Id { get; set; } public long RoomId { get; set; } public long SubRoomId { get; set; } public long OwnerPlayerId { get; set; } [MaxLength(256)] public string PhotonRoomId { get; set; } = string.Empty; [MaxLength(64)] public string Location { get; set; } = string.Empty; [MaxLength(256)] public string DataBlob { get; set; } = string.Empty; [MaxLength(16)] public string PhotonRegion { get; set; } = "us"; public int MaxCapacity { get; set; } = 8; [MaxLength(128)] public string Name { get; set; } = string.Empty; /// Host-chosen room code, set via PUT /// /roominstance/{id}/roomcode. Empty means "no custom code", in /// which case the deterministic RoomCodeService.Generate value is /// served instead. [MaxLength(16)] public string RoomCode { get; set; } = string.Empty; public DateTime CreatedAt { get; set; } = DateTime.UtcNow; }