using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; 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 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(); } } [Table("preference")] 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. [Column("preference_id")] public int Id { get; set; } [Column("user_id")] public Guid UserId { get; set; } [Column("selected_character_slot")] public int SelectedCharacterSlot { get; set; } [Column("admin_ooc_color")] public string AdminOOCColor { get; set; } = null!; public List Profiles { get; } = new(); } [Table("profile")] public class Profile { [Column("profile_id")] public int Id { get; set; } [Column("slot")] public int Slot { get; set; } [Column("char_name")] public string CharacterName { get; set; } = null!; [Column("age")] public int Age { get; set; } [Column("sex")] public string Sex { get; set; } = null!; [Column("gender")] public string Gender { get; set; } = null!; [Column("hair_name")] public string HairName { get; set; } = null!; [Column("hair_color")] public string HairColor { get; set; } = null!; [Column("facial_hair_name")] public string FacialHairName { get; set; } = null!; [Column("facial_hair_color")] public string FacialHairColor { get; set; } = null!; [Column("eye_color")] public string EyeColor { get; set; } = null!; [Column("skin_color")] public string SkinColor { get; set; } = null!; [Column("clothing")] public string Clothing { get; set; } = null!; [Column("backpack")] public string Backpack { get; set; } = null!; public List Jobs { get; } = new(); public List Antags { get; } = new(); [Column("pref_unavailable")] public DbPreferenceUnavailableMode PreferenceUnavailable { get; set; } [Column("preference_id")] public int PreferenceId { get; set; } public Preference Preference { get; set; } = null!; } [Table("job")] public class Job { [Column("job_id")] public int Id { get; set; } public Profile Profile { get; set; } = null!; [Column("profile_id")] public int ProfileId { get; set; } [Column("job_name")] public string JobName { get; set; } = null!; [Column("priority")] 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 } [Table("antag")] public class Antag { [Column("antag_id")] public int Id { get; set; } public Profile Profile { get; set; } = null!; [Column("profile_id")] public int ProfileId { get; set; } [Column("antag_name")] public string AntagName { get; set; } = null!; } public enum DbPreferenceUnavailableMode { // These enum values HAVE to match the ones in PreferenceUnavailableMode in Shared. StayInLobby = 0, SpawnAsOverflow, } [Table("assigned_user_id")] public class AssignedUserId { [Column("assigned_user_id_id")] public int Id { get; set; } [Column("user_name")] public string UserName { get; set; } = null!; [Column("user_id")] public Guid UserId { get; set; } } [Table("admin")] public class Admin { [Column("user_id"), Key] public Guid UserId { get; set; } [Column("title")] public string? Title { get; set; } [Column("admin_rank_id")] public int? AdminRankId { get; set; } public AdminRank? AdminRank { get; set; } public List Flags { get; set; } = default!; } [Table("admin_flag")] public class AdminFlag { [Column("admin_flag_id")] public int Id { get; set; } [Column("flag")] public string Flag { get; set; } = default!; [Column("negative")] public bool Negative { get; set; } [Column("admin_id")] public Guid AdminId { get; set; } public Admin Admin { get; set; } = default!; } [Table("admin_rank")] public class AdminRank { [Column("admin_rank_id")] public int Id { get; set; } [Column("name")] public string Name { get; set; } = default!; public List Admins { get; set; } = default!; public List Flags { get; set; } = default!; } [Table("admin_rank_flag")] public class AdminRankFlag { [Column("admin_rank_flag_id")] public int Id { get; set; } [Column("flag")] public string Flag { get; set; } = default!; [Column("admin_rank_id")] public int AdminRankId { get; set; } public AdminRank Rank { get; set; } = default!; } }