using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Net; using Microsoft.EntityFrameworkCore; namespace Content.Server.Database { public abstract class ServerDbContext : DbContext { /// /// The "dotnet ef" CLI tool uses the parameter-less constructor. /// When that happens we want to supply the via . /// To use the context within the application, the options need to be passed the constructor instead. /// protected readonly bool InitializedWithOptions; public ServerDbContext() { } public ServerDbContext(DbContextOptions options) : base(options) { InitializedWithOptions = true; } public DbSet Preference { get; set; } = null!; public DbSet Profile { get; set; } = null!; public DbSet AssignedUserId { get; set; } = null!; public DbSet Player { get; set; } = default!; public DbSet Admin { get; set; } = null!; public DbSet AdminRank { get; set; } = null!; protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity() .HasIndex(p => p.UserId) .IsUnique(); modelBuilder.Entity() .HasIndex(p => new {p.Slot, PrefsId = p.PreferenceId}) .IsUnique(); modelBuilder.Entity() .HasIndex(p => new {HumanoidProfileId = p.ProfileId, p.AntagName}) .IsUnique(); modelBuilder.Entity() .HasIndex(j => j.ProfileId); modelBuilder.Entity() .HasIndex(j => j.ProfileId, "IX_job_one_high_priority") .IsUnique() .HasFilter("priority = 3"); modelBuilder.Entity() .HasIndex(j => new { j.ProfileId, j.JobName }) .IsUnique(); modelBuilder.Entity() .HasIndex(p => p.UserName) .IsUnique(); // Can't have two usernames with the same user ID. modelBuilder.Entity() .HasIndex(p => p.UserId) .IsUnique(); modelBuilder.Entity() .HasOne(p => p.AdminRank) .WithMany(p => p!.Admins) .OnDelete(DeleteBehavior.SetNull); modelBuilder.Entity() .HasIndex(f => new {f.Flag, f.AdminId}) .IsUnique(); modelBuilder.Entity() .HasIndex(f => new {f.Flag, f.AdminRankId}) .IsUnique(); } } public class Preference { // NOTE: on postgres there SHOULD be an FK ensuring that the selected character slot always exists. // I had to use a migration to implement it and as a result its creation is a finicky mess. // Because if I let EFCore know about it it would explode on a circular reference. // Also it has to be DEFERRABLE INITIALLY DEFERRED so that insertion of new preferences works. // Also I couldn't figure out how to create it on SQLite. public int Id { get; set; } public Guid UserId { get; set; } public int SelectedCharacterSlot { get; set; } public string AdminOOCColor { get; set; } = null!; public List Profiles { get; } = new(); } public class Profile { public int Id { get; set; } public int Slot { get; set; } [Column("char_name")] public string CharacterName { get; set; } = null!; public int Age { get; set; } public string Sex { get; set; } = null!; public string Gender { get; set; } = null!; public string HairName { get; set; } = null!; public string HairColor { get; set; } = null!; public string FacialHairName { get; set; } = null!; public string FacialHairColor { get; set; } = null!; public string EyeColor { get; set; } = null!; public string SkinColor { get; set; } = null!; public string Clothing { get; set; } = null!; public string Backpack { get; set; } = null!; public List Jobs { get; } = new(); public List Antags { get; } = new(); [Column("pref_unavailable")] public DbPreferenceUnavailableMode PreferenceUnavailable { get; set; } public int PreferenceId { get; set; } public Preference Preference { get; set; } = null!; } public class Job { public int Id { get; set; } public Profile Profile { get; set; } = null!; public int ProfileId { get; set; } public string JobName { get; set; } = null!; public DbJobPriority Priority { get; set; } } public enum DbJobPriority { // These enum values HAVE to match the ones in JobPriority in Content.Shared Never = 0, Low = 1, Medium = 2, High = 3 } public class Antag { public int Id { get; set; } public Profile Profile { get; set; } = null!; public int ProfileId { get; set; } public string AntagName { get; set; } = null!; } public enum DbPreferenceUnavailableMode { // These enum values HAVE to match the ones in PreferenceUnavailableMode in Shared. StayInLobby = 0, SpawnAsOverflow, } public class AssignedUserId { public int Id { get; set; } public string UserName { get; set; } = null!; public Guid UserId { get; set; } } [Table("player")] public class Player { public int Id { get; set; } // Permanent data public Guid UserId { get; set; } public DateTime FirstSeenTime { get; set; } // Data that gets updated on each join. public string LastSeenUserName { get; set; } = null!; public DateTime LastSeenTime { get; set; } public IPAddress LastSeenAddress { get; set; } = null!; public byte[]? LastSeenHWId { get; set; } } public class Admin { [Key] public Guid UserId { get; set; } public string? Title { get; set; } public int? AdminRankId { get; set; } public AdminRank? AdminRank { get; set; } public List Flags { get; set; } = default!; } public class AdminFlag { public int Id { get; set; } public string Flag { get; set; } = default!; public bool Negative { get; set; } public Guid AdminId { get; set; } public Admin Admin { get; set; } = default!; } public class AdminRank { public int Id { get; set; } public string Name { get; set; } = default!; public List Admins { get; set; } = default!; public List Flags { get; set; } = default!; } public class AdminRankFlag { public int Id { get; set; } public string Flag { get; set; } = default!; public int AdminRankId { get; set; } public AdminRank Rank { get; set; } = default!; } }