From ae44cf4ac84a3433f3a5a9625354db1c29132448 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Sep 2025 07:19:43 +0000 Subject: [PATCH 1/6] Initial plan From 9e8c307b87557b8db6c33f044109ee55e2437320 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Sep 2025 07:23:23 +0000 Subject: [PATCH 2/6] Initial analysis - database system already partially implemented Co-authored-by: Foxlider <19773387+Foxlider@users.noreply.github.com> --- KAST/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KAST/Program.cs b/KAST/Program.cs index c576d64..e8a419d 100644 --- a/KAST/Program.cs +++ b/KAST/Program.cs @@ -2,7 +2,7 @@ using KAST.Core.Helpers; using KAST.Core.Services; using KAST.Data; -using KAST.Data.Interfaces; +using KAST.Core.Interfaces; using Microsoft.EntityFrameworkCore; using MudBlazor.Services; using System.Diagnostics; From 58ea154821b23b05119e14a06fe9ab67dc00fc93 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Sep 2025 07:32:51 +0000 Subject: [PATCH 3/6] Complete database system implementation with SQLite and Entity Framework Core Co-authored-by: Foxlider <19773387+Foxlider@users.noreply.github.com> --- KAST.Core/Services/ModService.cs | 102 ++++++ KAST.Core/Services/ProfileService.cs | 292 +++++++++++++++++ KAST.Data/ApplicationDbContext.cs | 60 +++- ...ddModsProfilesAndRelationships.Designer.cs | 301 ++++++++++++++++++ ...6072553_AddModsProfilesAndRelationships.cs | 254 +++++++++++++++ .../ApplicationDbContextModelSnapshot.cs | 255 ++++++++++++++- KAST.Data/Models/Mod.cs | 65 ++++ KAST.Data/Models/ModProfile.cs | 44 +++ KAST.Data/Models/Profile.cs | 67 ++++ KAST.Data/Models/ProfileHistory.cs | 67 ++++ KAST.Data/Models/Server.cs | 36 ++- .../Arma3Profile | 33 ++ .../perf.cfg | 8 + .../server.cfg | 7 + KAST/Components/Pages/Mods.razor | 291 ++++++++--------- KAST/KAST.csproj | 4 + KAST/Program.cs | 12 +- .../Arma3Profile | 33 ++ .../perf.cfg | 8 + .../server.cfg | 7 + 20 files changed, 1778 insertions(+), 168 deletions(-) create mode 100644 KAST.Core/Services/ModService.cs create mode 100644 KAST.Core/Services/ProfileService.cs create mode 100644 KAST.Data/Migrations/20250926072553_AddModsProfilesAndRelationships.Designer.cs create mode 100644 KAST.Data/Migrations/20250926072553_AddModsProfilesAndRelationships.cs create mode 100644 KAST.Data/Models/Mod.cs create mode 100644 KAST.Data/Models/ModProfile.cs create mode 100644 KAST.Data/Models/Profile.cs create mode 100644 KAST.Data/Models/ProfileHistory.cs create mode 100644 KAST/3b7d876b-a563-6e50-befb-76657ecf4671/Arma3Profile create mode 100644 KAST/3b7d876b-a563-6e50-befb-76657ecf4671/perf.cfg create mode 100644 KAST/3b7d876b-a563-6e50-befb-76657ecf4671/server.cfg create mode 100644 KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/Arma3Profile create mode 100644 KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/perf.cfg create mode 100644 KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/server.cfg diff --git a/KAST.Core/Services/ModService.cs b/KAST.Core/Services/ModService.cs new file mode 100644 index 0000000..e4fb993 --- /dev/null +++ b/KAST.Core/Services/ModService.cs @@ -0,0 +1,102 @@ +using KAST.Data; +using KAST.Data.Models; +using Microsoft.EntityFrameworkCore; + +namespace KAST.Core.Services +{ + public class ModService + { + private readonly ApplicationDbContext _context; + + public ModService(ApplicationDbContext context) + { + _context = context; + } + + /// + /// Get all mods from the database + /// + public async Task> GetAllModsAsync() + { + return await _context.Mods + .OrderBy(m => m.Name) + .ToListAsync(); + } + + /// + /// Get a specific mod by ID + /// + public async Task GetModByIdAsync(Guid id) + { + return await _context.Mods + .Include(m => m.ModProfiles) + .FirstOrDefaultAsync(m => m.Id == id); + } + + /// + /// Get a mod by Steam ID + /// + public async Task GetModBySteamIdAsync(string steamId) + { + return await _context.Mods + .FirstOrDefaultAsync(m => m.SteamId == steamId); + } + + /// + /// Add or update a mod + /// + public async Task SaveModAsync(Mod mod) + { + var existingMod = await _context.Mods + .FirstOrDefaultAsync(m => m.Id == mod.Id); + + if (existingMod == null) + { + _context.Mods.Add(mod); + } + else + { + _context.Entry(existingMod).CurrentValues.SetValues(mod); + existingMod.LastUpdated = DateTime.UtcNow; + } + + await _context.SaveChangesAsync(); + return mod; + } + + /// + /// Delete a mod + /// + public async Task DeleteModAsync(Guid id) + { + var mod = await _context.Mods.FindAsync(id); + if (mod == null) return false; + + _context.Mods.Remove(mod); + await _context.SaveChangesAsync(); + return true; + } + + /// + /// Get mods for a specific profile + /// + public async Task> GetModsForProfileAsync(Guid profileId) + { + return await _context.ModProfiles + .Where(mp => mp.ProfileId == profileId && mp.IsEnabled) + .OrderBy(mp => mp.Order) + .Select(mp => mp.Mod) + .ToListAsync(); + } + + /// + /// Update mod versions based on file system scan + /// + public async Task UpdateModFromFileSystemAsync(string modPath) + { + // This would integrate with existing file system scanning logic + // For now, this is a placeholder for future implementation + await Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/KAST.Core/Services/ProfileService.cs b/KAST.Core/Services/ProfileService.cs new file mode 100644 index 0000000..c12544a --- /dev/null +++ b/KAST.Core/Services/ProfileService.cs @@ -0,0 +1,292 @@ +using KAST.Data; +using KAST.Data.Models; +using Microsoft.EntityFrameworkCore; +using System.Text.Json; + +namespace KAST.Core.Services +{ + public class ProfileService + { + private readonly ApplicationDbContext _context; + + public ProfileService(ApplicationDbContext context) + { + _context = context; + } + + /// + /// Get all profiles + /// + public async Task> GetAllProfilesAsync() + { + return await _context.Profiles + .Include(p => p.Server) + .Include(p => p.ModProfiles) + .ThenInclude(mp => mp.Mod) + .OrderBy(p => p.Name) + .ToListAsync(); + } + + /// + /// Get a specific profile by ID + /// + public async Task GetProfileByIdAsync(Guid id) + { + return await _context.Profiles + .Include(p => p.Server) + .Include(p => p.ModProfiles) + .ThenInclude(mp => mp.Mod) + .Include(p => p.ProfileHistories) + .FirstOrDefaultAsync(p => p.Id == id); + } + + /// + /// Get the currently active profile + /// + public async Task GetActiveProfileAsync() + { + return await _context.Profiles + .Include(p => p.Server) + .Include(p => p.ModProfiles) + .ThenInclude(mp => mp.Mod) + .FirstOrDefaultAsync(p => p.IsActive); + } + + /// + /// Create a new profile + /// + public async Task CreateProfileAsync(Profile profile) + { + profile.Id = Guid.NewGuid(); + profile.CreatedAt = DateTime.UtcNow; + profile.LastModified = DateTime.UtcNow; + + _context.Profiles.Add(profile); + await _context.SaveChangesAsync(); + + // Create initial history entry + await CreateHistoryEntryAsync(profile, "Profile created"); + + return profile; + } + + /// + /// Update an existing profile + /// + public async Task UpdateProfileAsync(Profile profile, string? changeDescription = null) + { + var existingProfile = await _context.Profiles + .Include(p => p.ProfileHistories) + .FirstOrDefaultAsync(p => p.Id == profile.Id); + + if (existingProfile == null) + throw new ArgumentException("Profile not found", nameof(profile)); + + // Store old values for history + var oldProfile = new Profile + { + ServerConfig = existingProfile.ServerConfig, + ServerProfile = existingProfile.ServerProfile, + PerformanceConfig = existingProfile.PerformanceConfig, + CommandLineArgs = existingProfile.CommandLineArgs + }; + + // Update profile + _context.Entry(existingProfile).CurrentValues.SetValues(profile); + existingProfile.LastModified = DateTime.UtcNow; + + await _context.SaveChangesAsync(); + + // Create history entry if there were significant changes + if (HasSignificantChanges(oldProfile, existingProfile)) + { + await CreateHistoryEntryAsync(existingProfile, changeDescription ?? "Profile updated"); + } + + return existingProfile; + } + + /// + /// Set a profile as active (and deactivate others) + /// + public async Task SetActiveProfileAsync(Guid profileId) + { + // Deactivate all profiles + await _context.Profiles + .Where(p => p.IsActive) + .ExecuteUpdateAsync(p => p.SetProperty(x => x.IsActive, false)); + + // Activate the specified profile + var profile = await _context.Profiles.FindAsync(profileId); + if (profile == null) + throw new ArgumentException("Profile not found", nameof(profileId)); + + profile.IsActive = true; + await _context.SaveChangesAsync(); + + return profile; + } + + /// + /// Delete a profile + /// + public async Task DeleteProfileAsync(Guid id) + { + var profile = await _context.Profiles.FindAsync(id); + if (profile == null) return false; + + _context.Profiles.Remove(profile); + await _context.SaveChangesAsync(); + return true; + } + + /// + /// Add a mod to a profile + /// + public async Task AddModToProfileAsync(Guid profileId, Guid modId, int order = 0) + { + // Check if relationship already exists + var existing = await _context.ModProfiles + .FirstOrDefaultAsync(mp => mp.ProfileId == profileId && mp.ModId == modId); + + if (existing != null) + { + existing.IsEnabled = true; + existing.Order = order; + } + else + { + var modProfile = new ModProfile + { + Id = Guid.NewGuid(), + ProfileId = profileId, + ModId = modId, + Order = order, + IsEnabled = true, + AddedAt = DateTime.UtcNow + }; + + _context.ModProfiles.Add(modProfile); + } + + await _context.SaveChangesAsync(); + } + + /// + /// Remove a mod from a profile + /// + public async Task RemoveModFromProfileAsync(Guid profileId, Guid modId) + { + var modProfile = await _context.ModProfiles + .FirstOrDefaultAsync(mp => mp.ProfileId == profileId && mp.ModId == modId); + + if (modProfile != null) + { + _context.ModProfiles.Remove(modProfile); + await _context.SaveChangesAsync(); + } + } + + /// + /// Update mod order in a profile + /// + public async Task UpdateModOrderInProfileAsync(Guid profileId, Dictionary modOrders) + { + var modProfiles = await _context.ModProfiles + .Where(mp => mp.ProfileId == profileId) + .ToListAsync(); + + foreach (var modProfile in modProfiles) + { + if (modOrders.TryGetValue(modProfile.ModId, out int newOrder)) + { + modProfile.Order = newOrder; + } + } + + await _context.SaveChangesAsync(); + } + + /// + /// Create a history entry for a profile + /// + private async Task CreateHistoryEntryAsync(Profile profile, string changeDescription) + { + var lastVersion = await _context.ProfileHistories + .Where(ph => ph.ProfileId == profile.Id) + .MaxAsync(ph => (int?)ph.Version) ?? 0; + + var modsSnapshot = await _context.ModProfiles + .Where(mp => mp.ProfileId == profile.Id) + .Select(mp => new { mp.ModId, mp.Order, mp.IsEnabled }) + .ToListAsync(); + + var history = new ProfileHistory + { + Id = Guid.NewGuid(), + ProfileId = profile.Id, + Version = lastVersion + 1, + ChangeDescription = changeDescription, + ServerConfigSnapshot = profile.ServerConfig, + ServerProfileSnapshot = profile.ServerProfile, + PerformanceConfigSnapshot = profile.PerformanceConfig, + CommandLineArgsSnapshot = profile.CommandLineArgs, + ModsSnapshot = JsonSerializer.Serialize(modsSnapshot), + CreatedAt = DateTime.UtcNow + }; + + _context.ProfileHistories.Add(history); + await _context.SaveChangesAsync(); + } + + /// + /// Check if there are significant changes between two profiles + /// + private static bool HasSignificantChanges(Profile oldProfile, Profile newProfile) + { + return oldProfile.ServerConfig != newProfile.ServerConfig || + oldProfile.ServerProfile != newProfile.ServerProfile || + oldProfile.PerformanceConfig != newProfile.PerformanceConfig || + oldProfile.CommandLineArgs != newProfile.CommandLineArgs; + } + + /// + /// Get profile history + /// + public async Task> GetProfileHistoryAsync(Guid profileId) + { + return await _context.ProfileHistories + .Where(ph => ph.ProfileId == profileId) + .OrderByDescending(ph => ph.Version) + .ToListAsync(); + } + + /// + /// Restore profile from a specific version + /// + public async Task RestoreProfileFromHistoryAsync(Guid profileId, int version) + { + var profile = await GetProfileByIdAsync(profileId); + var historyEntry = await _context.ProfileHistories + .FirstOrDefaultAsync(ph => ph.ProfileId == profileId && ph.Version == version); + + if (profile == null || historyEntry == null) + throw new ArgumentException("Profile or history entry not found"); + + // Restore configuration + profile.ServerConfig = historyEntry.ServerConfigSnapshot; + profile.ServerProfile = historyEntry.ServerProfileSnapshot; + profile.PerformanceConfig = historyEntry.PerformanceConfigSnapshot; + profile.CommandLineArgs = historyEntry.CommandLineArgsSnapshot; + + // Restore mods if snapshot exists + if (!string.IsNullOrEmpty(historyEntry.ModsSnapshot)) + { + var modsSnapshot = JsonSerializer.Deserialize>(historyEntry.ModsSnapshot); + // Implementation would restore mod associations here + } + + return await UpdateProfileAsync(profile, $"Restored from version {version}"); + } + } +} \ No newline at end of file diff --git a/KAST.Data/ApplicationDbContext.cs b/KAST.Data/ApplicationDbContext.cs index 07af8d1..beeaa10 100644 --- a/KAST.Data/ApplicationDbContext.cs +++ b/KAST.Data/ApplicationDbContext.cs @@ -12,6 +12,10 @@ public class ApplicationDbContext : DbContext #region DbSets public DbSet Servers { get; set; } public DbSet Settings { get; set; } + public DbSet Mods { get; set; } + public DbSet Profiles { get; set; } + public DbSet ModProfiles { get; set; } + public DbSet ProfileHistories { get; set; } #endregion @@ -50,7 +54,61 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) } protected override void OnModelCreating(ModelBuilder modelBuilder) - { /* So far we have nothing to do here */ } + { + // Configure Mod entity + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.SteamId).IsUnique(); + entity.Property(e => e.Name).IsRequired(); + entity.Property(e => e.Path).IsRequired(); + }); + + // Configure Profile entity + modelBuilder.Entity(entity => + { + entity.Property(e => e.Name).IsRequired().HasMaxLength(255); + entity.HasOne(e => e.Server) + .WithMany(s => s.Profiles) + .HasForeignKey(e => e.ServerId) + .OnDelete(DeleteBehavior.SetNull); + }); + + // Configure ModProfile many-to-many relationship + modelBuilder.Entity(entity => + { + entity.HasOne(mp => mp.Mod) + .WithMany(m => m.ModProfiles) + .HasForeignKey(mp => mp.ModId) + .OnDelete(DeleteBehavior.Cascade); + + entity.HasOne(mp => mp.Profile) + .WithMany(p => p.ModProfiles) + .HasForeignKey(mp => mp.ProfileId) + .OnDelete(DeleteBehavior.Cascade); + + // Ensure unique combination of mod and profile + entity.HasIndex(mp => new { mp.ModId, mp.ProfileId }).IsUnique(); + }); + + // Configure ProfileHistory + modelBuilder.Entity(entity => + { + entity.HasOne(ph => ph.Profile) + .WithMany(p => p.ProfileHistories) + .HasForeignKey(ph => ph.ProfileId) + .OnDelete(DeleteBehavior.Cascade); + + // Ensure unique combination of profile and version + entity.HasIndex(ph => new { ph.ProfileId, ph.Version }).IsUnique(); + }); + + // Configure Server entity + modelBuilder.Entity(entity => + { + entity.Property(e => e.Name).IsRequired().HasMaxLength(255); + entity.Property(e => e.InstallPath).IsRequired(); + }); + } public void EnsureSeedData() { /* So far we have no need to seed the DB as the models are not ready for production yet */ } diff --git a/KAST.Data/Migrations/20250926072553_AddModsProfilesAndRelationships.Designer.cs b/KAST.Data/Migrations/20250926072553_AddModsProfilesAndRelationships.Designer.cs new file mode 100644 index 0000000..f571082 --- /dev/null +++ b/KAST.Data/Migrations/20250926072553_AddModsProfilesAndRelationships.Designer.cs @@ -0,0 +1,301 @@ +// +using System; +using KAST.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace KAST.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250926072553_AddModsProfilesAndRelationships")] + partial class AddModsProfilesAndRelationships + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.9"); + + modelBuilder.Entity("KAST.Data.Models.KastSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApiKey") + .HasColumnType("TEXT"); + + b.Property("ModFolderPath") + .HasColumnType("TEXT"); + + b.Property("ServerDefaultPath") + .HasColumnType("TEXT"); + + b.Property("ThemeAccent") + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("KAST.Data.Models.Mod", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Author") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("IsLocal") + .HasColumnType("INTEGER"); + + b.Property("LastUpdated") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SizeBytes") + .HasColumnType("INTEGER"); + + b.Property("SteamId") + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("SteamId") + .IsUnique(); + + b.ToTable("Mods"); + }); + + modelBuilder.Entity("KAST.Data.Models.ModProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("ModId") + .HasColumnType("TEXT"); + + b.Property("Order") + .HasColumnType("INTEGER"); + + b.Property("ProfileId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ProfileId"); + + b.HasIndex("ModId", "ProfileId") + .IsUnique(); + + b.ToTable("ModProfiles"); + }); + + modelBuilder.Entity("KAST.Data.Models.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CommandLineArgs") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("PerformanceConfig") + .HasColumnType("TEXT"); + + b.Property("ServerConfig") + .HasColumnType("TEXT"); + + b.Property("ServerId") + .HasColumnType("TEXT"); + + b.Property("ServerProfile") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("Profiles"); + }); + + modelBuilder.Entity("KAST.Data.Models.ProfileHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ChangeDescription") + .HasColumnType("TEXT"); + + b.Property("ChangedBy") + .HasColumnType("TEXT"); + + b.Property("CommandLineArgsSnapshot") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("ModsSnapshot") + .HasColumnType("TEXT"); + + b.Property("PerformanceConfigSnapshot") + .HasColumnType("TEXT"); + + b.Property("ProfileId") + .HasColumnType("TEXT"); + + b.Property("ServerConfigSnapshot") + .HasColumnType("TEXT"); + + b.Property("ServerProfileSnapshot") + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProfileId", "Version") + .IsUnique(); + + b.ToTable("ProfileHistories"); + }); + + modelBuilder.Entity("KAST.Data.Models.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("InstallPath") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastUpdated") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Servers"); + }); + + modelBuilder.Entity("KAST.Data.Models.ModProfile", b => + { + b.HasOne("KAST.Data.Models.Mod", "Mod") + .WithMany("ModProfiles") + .HasForeignKey("ModId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("KAST.Data.Models.Profile", "Profile") + .WithMany("ModProfiles") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Mod"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("KAST.Data.Models.Profile", b => + { + b.HasOne("KAST.Data.Models.Server", "Server") + .WithMany("Profiles") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("KAST.Data.Models.ProfileHistory", b => + { + b.HasOne("KAST.Data.Models.Profile", "Profile") + .WithMany("ProfileHistories") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("KAST.Data.Models.Mod", b => + { + b.Navigation("ModProfiles"); + }); + + modelBuilder.Entity("KAST.Data.Models.Profile", b => + { + b.Navigation("ModProfiles"); + + b.Navigation("ProfileHistories"); + }); + + modelBuilder.Entity("KAST.Data.Models.Server", b => + { + b.Navigation("Profiles"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/KAST.Data/Migrations/20250926072553_AddModsProfilesAndRelationships.cs b/KAST.Data/Migrations/20250926072553_AddModsProfilesAndRelationships.cs new file mode 100644 index 0000000..e6afbc7 --- /dev/null +++ b/KAST.Data/Migrations/20250926072553_AddModsProfilesAndRelationships.cs @@ -0,0 +1,254 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace KAST.Data.Migrations +{ + /// + public partial class AddModsProfilesAndRelationships : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "ThemeAccent", + table: "Settings", + type: "TEXT", + maxLength: 10, + nullable: true, + oldClrType: typeof(string), + oldType: "TEXT", + oldMaxLength: 10); + + migrationBuilder.AlterColumn( + name: "ModFolderPath", + table: "Settings", + type: "TEXT", + nullable: true, + oldClrType: typeof(string), + oldType: "TEXT"); + + migrationBuilder.AddColumn( + name: "ApiKey", + table: "Settings", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "ServerDefaultPath", + table: "Settings", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedAt", + table: "Servers", + type: "TEXT", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "LastUpdated", + table: "Servers", + type: "TEXT", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "Version", + table: "Servers", + type: "TEXT", + nullable: true); + + migrationBuilder.CreateTable( + name: "Mods", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + SteamId = table.Column(type: "TEXT", nullable: true), + Name = table.Column(type: "TEXT", nullable: false), + Author = table.Column(type: "TEXT", nullable: true), + Path = table.Column(type: "TEXT", nullable: false), + SizeBytes = table.Column(type: "INTEGER", nullable: false), + IsLocal = table.Column(type: "INTEGER", nullable: false), + LastUpdated = table.Column(type: "TEXT", nullable: true), + Version = table.Column(type: "TEXT", nullable: true), + IsEnabled = table.Column(type: "INTEGER", nullable: false), + CreatedAt = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Mods", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Profiles", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", maxLength: 255, nullable: false), + Description = table.Column(type: "TEXT", nullable: true), + ServerConfig = table.Column(type: "TEXT", nullable: true), + ServerProfile = table.Column(type: "TEXT", nullable: true), + PerformanceConfig = table.Column(type: "TEXT", nullable: true), + CommandLineArgs = table.Column(type: "TEXT", nullable: true), + IsActive = table.Column(type: "INTEGER", nullable: false), + CreatedAt = table.Column(type: "TEXT", nullable: false), + LastModified = table.Column(type: "TEXT", nullable: false), + ServerId = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Profiles", x => x.Id); + table.ForeignKey( + name: "FK_Profiles_Servers_ServerId", + column: x => x.ServerId, + principalTable: "Servers", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + }); + + migrationBuilder.CreateTable( + name: "ModProfiles", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + ModId = table.Column(type: "TEXT", nullable: false), + ProfileId = table.Column(type: "TEXT", nullable: false), + Order = table.Column(type: "INTEGER", nullable: false), + IsEnabled = table.Column(type: "INTEGER", nullable: false), + AddedAt = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ModProfiles", x => x.Id); + table.ForeignKey( + name: "FK_ModProfiles_Mods_ModId", + column: x => x.ModId, + principalTable: "Mods", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_ModProfiles_Profiles_ProfileId", + column: x => x.ProfileId, + principalTable: "Profiles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ProfileHistories", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + ProfileId = table.Column(type: "TEXT", nullable: false), + Version = table.Column(type: "INTEGER", nullable: false), + ChangeDescription = table.Column(type: "TEXT", nullable: true), + ServerConfigSnapshot = table.Column(type: "TEXT", nullable: true), + ServerProfileSnapshot = table.Column(type: "TEXT", nullable: true), + PerformanceConfigSnapshot = table.Column(type: "TEXT", nullable: true), + CommandLineArgsSnapshot = table.Column(type: "TEXT", nullable: true), + ModsSnapshot = table.Column(type: "TEXT", nullable: true), + CreatedAt = table.Column(type: "TEXT", nullable: false), + ChangedBy = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ProfileHistories", x => x.Id); + table.ForeignKey( + name: "FK_ProfileHistories_Profiles_ProfileId", + column: x => x.ProfileId, + principalTable: "Profiles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_ModProfiles_ModId_ProfileId", + table: "ModProfiles", + columns: new[] { "ModId", "ProfileId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_ModProfiles_ProfileId", + table: "ModProfiles", + column: "ProfileId"); + + migrationBuilder.CreateIndex( + name: "IX_Mods_SteamId", + table: "Mods", + column: "SteamId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_ProfileHistories_ProfileId_Version", + table: "ProfileHistories", + columns: new[] { "ProfileId", "Version" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Profiles_ServerId", + table: "Profiles", + column: "ServerId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ModProfiles"); + + migrationBuilder.DropTable( + name: "ProfileHistories"); + + migrationBuilder.DropTable( + name: "Mods"); + + migrationBuilder.DropTable( + name: "Profiles"); + + migrationBuilder.DropColumn( + name: "ApiKey", + table: "Settings"); + + migrationBuilder.DropColumn( + name: "ServerDefaultPath", + table: "Settings"); + + migrationBuilder.DropColumn( + name: "CreatedAt", + table: "Servers"); + + migrationBuilder.DropColumn( + name: "LastUpdated", + table: "Servers"); + + migrationBuilder.DropColumn( + name: "Version", + table: "Servers"); + + migrationBuilder.AlterColumn( + name: "ThemeAccent", + table: "Settings", + type: "TEXT", + maxLength: 10, + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "TEXT", + oldMaxLength: 10, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ModFolderPath", + table: "Settings", + type: "TEXT", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "TEXT", + oldNullable: true); + } + } +} diff --git a/KAST.Data/Migrations/ApplicationDbContextModelSnapshot.cs b/KAST.Data/Migrations/ApplicationDbContextModelSnapshot.cs index b6d9818..b706b17 100644 --- a/KAST.Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/KAST.Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -15,45 +15,282 @@ partial class ApplicationDbContextModelSnapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "7.0.12"); + modelBuilder.HasAnnotation("ProductVersion", "9.0.9"); - modelBuilder.Entity("KAST.Data.Models.Server", b => + modelBuilder.Entity("KAST.Data.Models.KastSettings", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("TEXT"); - b.Property("InstallPath") + b.Property("ApiKey") + .HasColumnType("TEXT"); + + b.Property("ModFolderPath") + .HasColumnType("TEXT"); + + b.Property("ServerDefaultPath") + .HasColumnType("TEXT"); + + b.Property("ThemeAccent") + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("KAST.Data.Models.Mod", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Author") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("IsLocal") + .HasColumnType("INTEGER"); + + b.Property("LastUpdated") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Path") .IsRequired() .HasColumnType("TEXT"); + b.Property("SizeBytes") + .HasColumnType("INTEGER"); + + b.Property("SteamId") + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("SteamId") + .IsUnique(); + + b.ToTable("Mods"); + }); + + modelBuilder.Entity("KAST.Data.Models.ModProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("ModId") + .HasColumnType("TEXT"); + + b.Property("Order") + .HasColumnType("INTEGER"); + + b.Property("ProfileId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ProfileId"); + + b.HasIndex("ModId", "ProfileId") + .IsUnique(); + + b.ToTable("ModProfiles"); + }); + + modelBuilder.Entity("KAST.Data.Models.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CommandLineArgs") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .HasColumnType("TEXT"); + b.Property("Name") .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("PerformanceConfig") + .HasColumnType("TEXT"); + + b.Property("ServerConfig") + .HasColumnType("TEXT"); + + b.Property("ServerId") + .HasColumnType("TEXT"); + + b.Property("ServerProfile") .HasColumnType("TEXT"); b.HasKey("Id"); - b.ToTable("Servers"); + b.HasIndex("ServerId"); + + b.ToTable("Profiles"); }); - modelBuilder.Entity("KAST.Data.Models.Settings", b => + modelBuilder.Entity("KAST.Data.Models.ProfileHistory", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("TEXT"); - b.Property("ModFolderPath") + b.Property("ChangeDescription") + .HasColumnType("TEXT"); + + b.Property("ChangedBy") + .HasColumnType("TEXT"); + + b.Property("CommandLineArgsSnapshot") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("ModsSnapshot") + .HasColumnType("TEXT"); + + b.Property("PerformanceConfigSnapshot") + .HasColumnType("TEXT"); + + b.Property("ProfileId") + .HasColumnType("TEXT"); + + b.Property("ServerConfigSnapshot") + .HasColumnType("TEXT"); + + b.Property("ServerProfileSnapshot") + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProfileId", "Version") + .IsUnique(); + + b.ToTable("ProfileHistories"); + }); + + modelBuilder.Entity("KAST.Data.Models.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("InstallPath") .IsRequired() .HasColumnType("TEXT"); - b.Property("ThemeAccent") + b.Property("LastUpdated") + .HasColumnType("TEXT"); + + b.Property("Name") .IsRequired() - .HasMaxLength(10) + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("Version") .HasColumnType("TEXT"); b.HasKey("Id"); - b.ToTable("Settings"); + b.ToTable("Servers"); + }); + + modelBuilder.Entity("KAST.Data.Models.ModProfile", b => + { + b.HasOne("KAST.Data.Models.Mod", "Mod") + .WithMany("ModProfiles") + .HasForeignKey("ModId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("KAST.Data.Models.Profile", "Profile") + .WithMany("ModProfiles") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Mod"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("KAST.Data.Models.Profile", b => + { + b.HasOne("KAST.Data.Models.Server", "Server") + .WithMany("Profiles") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("KAST.Data.Models.ProfileHistory", b => + { + b.HasOne("KAST.Data.Models.Profile", "Profile") + .WithMany("ProfileHistories") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("KAST.Data.Models.Mod", b => + { + b.Navigation("ModProfiles"); + }); + + modelBuilder.Entity("KAST.Data.Models.Profile", b => + { + b.Navigation("ModProfiles"); + + b.Navigation("ProfileHistories"); + }); + + modelBuilder.Entity("KAST.Data.Models.Server", b => + { + b.Navigation("Profiles"); }); #pragma warning restore 612, 618 } diff --git a/KAST.Data/Models/Mod.cs b/KAST.Data/Models/Mod.cs new file mode 100644 index 0000000..1e66358 --- /dev/null +++ b/KAST.Data/Models/Mod.cs @@ -0,0 +1,65 @@ +using System.ComponentModel.DataAnnotations; + +namespace KAST.Data.Models +{ + public class Mod + { + [Key] + public Guid Id { get; set; } + + /// + /// Steam Workshop ID for steam mods, null for local mods + /// + public string? SteamId { get; set; } + + /// + /// Display name of the mod + /// + [Required] + public string Name { get; set; } = string.Empty; + + /// + /// Author of the mod + /// + public string? Author { get; set; } + + /// + /// Local file path to the mod + /// + [Required] + public string Path { get; set; } = string.Empty; + + /// + /// Size of the mod in bytes + /// + public long SizeBytes { get; set; } + + /// + /// Whether this is a local mod or from Steam Workshop + /// + public bool IsLocal { get; set; } + + /// + /// Last time the mod was updated + /// + public DateTime? LastUpdated { get; set; } + + /// + /// Current version/revision of the mod + /// + public string? Version { get; set; } + + /// + /// Whether the mod is currently enabled + /// + public bool IsEnabled { get; set; } = true; + + /// + /// Creation timestamp + /// + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + // Navigation properties + public ICollection ModProfiles { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/KAST.Data/Models/ModProfile.cs b/KAST.Data/Models/ModProfile.cs new file mode 100644 index 0000000..c37a547 --- /dev/null +++ b/KAST.Data/Models/ModProfile.cs @@ -0,0 +1,44 @@ +using System.ComponentModel.DataAnnotations; + +namespace KAST.Data.Models +{ + /// + /// Junction table for many-to-many relationship between Mods and Profiles + /// + public class ModProfile + { + [Key] + public Guid Id { get; set; } + + /// + /// Foreign key to Mod + /// + [Required] + public Guid ModId { get; set; } + + /// + /// Foreign key to Profile + /// + [Required] + public Guid ProfileId { get; set; } + + /// + /// Order of the mod in the profile (for launch order) + /// + public int Order { get; set; } + + /// + /// Whether this mod is enabled for this specific profile + /// + public bool IsEnabled { get; set; } = true; + + /// + /// When this mod was added to the profile + /// + public DateTime AddedAt { get; set; } = DateTime.UtcNow; + + // Navigation properties + public Mod Mod { get; set; } = null!; + public Profile Profile { get; set; } = null!; + } +} \ No newline at end of file diff --git a/KAST.Data/Models/Profile.cs b/KAST.Data/Models/Profile.cs new file mode 100644 index 0000000..0af59e7 --- /dev/null +++ b/KAST.Data/Models/Profile.cs @@ -0,0 +1,67 @@ +using System.ComponentModel.DataAnnotations; + +namespace KAST.Data.Models +{ + public class Profile + { + [Key] + public Guid Id { get; set; } + + /// + /// Display name for the profile + /// + [Required] + [MaxLength(255)] + public string Name { get; set; } = string.Empty; + + /// + /// Optional description of the profile + /// + public string? Description { get; set; } + + /// + /// Server configuration (serialized JSON) + /// + public string? ServerConfig { get; set; } + + /// + /// Server profile configuration (serialized JSON) + /// + public string? ServerProfile { get; set; } + + /// + /// Performance configuration (serialized JSON) + /// + public string? PerformanceConfig { get; set; } + + /// + /// Command line arguments for the server + /// + public string? CommandLineArgs { get; set; } + + /// + /// Whether this profile is currently active + /// + public bool IsActive { get; set; } + + /// + /// Creation timestamp + /// + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + /// + /// Last modified timestamp + /// + public DateTime LastModified { get; set; } = DateTime.UtcNow; + + /// + /// Associated server ID + /// + public Guid? ServerId { get; set; } + + // Navigation properties + public Server? Server { get; set; } + public ICollection ModProfiles { get; set; } = new List(); + public ICollection ProfileHistories { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/KAST.Data/Models/ProfileHistory.cs b/KAST.Data/Models/ProfileHistory.cs new file mode 100644 index 0000000..07d4e47 --- /dev/null +++ b/KAST.Data/Models/ProfileHistory.cs @@ -0,0 +1,67 @@ +using System.ComponentModel.DataAnnotations; + +namespace KAST.Data.Models +{ + /// + /// Stores historical versions of profile configurations for versioning support + /// + public class ProfileHistory + { + [Key] + public Guid Id { get; set; } + + /// + /// Foreign key to the profile this history entry belongs to + /// + [Required] + public Guid ProfileId { get; set; } + + /// + /// Version number for this configuration snapshot + /// + public int Version { get; set; } + + /// + /// Optional comment or description of changes made + /// + public string? ChangeDescription { get; set; } + + /// + /// Snapshot of server configuration at this point in time + /// + public string? ServerConfigSnapshot { get; set; } + + /// + /// Snapshot of server profile configuration at this point in time + /// + public string? ServerProfileSnapshot { get; set; } + + /// + /// Snapshot of performance configuration at this point in time + /// + public string? PerformanceConfigSnapshot { get; set; } + + /// + /// Snapshot of command line arguments at this point in time + /// + public string? CommandLineArgsSnapshot { get; set; } + + /// + /// JSON array of mod IDs and their order at this point in time + /// + public string? ModsSnapshot { get; set; } + + /// + /// When this snapshot was created + /// + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + /// + /// Who made the changes (for future use) + /// + public string? ChangedBy { get; set; } + + // Navigation properties + public Profile Profile { get; set; } = null!; + } +} \ No newline at end of file diff --git a/KAST.Data/Models/Server.cs b/KAST.Data/Models/Server.cs index 261e6bd..52eb466 100644 --- a/KAST.Data/Models/Server.cs +++ b/KAST.Data/Models/Server.cs @@ -1,11 +1,41 @@ -namespace KAST.Data.Models +using System.ComponentModel.DataAnnotations; + +namespace KAST.Data.Models { public class Server { + [Key] public Guid Id { get; set; } - public string Name { get; set; } + /// + /// Display name for the server + /// + [Required] + [MaxLength(255)] + public string Name { get; set; } = string.Empty; + + /// + /// Installation path for the Arma 3 server + /// + [Required] + public string InstallPath { get; set; } = string.Empty; + + /// + /// Version of the server installation + /// + public string? Version { get; set; } + + /// + /// When the server was created/added + /// + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + /// + /// Last time the server was updated + /// + public DateTime LastUpdated { get; set; } = DateTime.UtcNow; - public string InstallPath { get; set; } + // Navigation properties + public ICollection Profiles { get; set; } = new List(); } } diff --git a/KAST/3b7d876b-a563-6e50-befb-76657ecf4671/Arma3Profile b/KAST/3b7d876b-a563-6e50-befb-76657ecf4671/Arma3Profile new file mode 100644 index 0000000..a1f41c4 --- /dev/null +++ b/KAST/3b7d876b-a563-6e50-befb-76657ecf4671/Arma3Profile @@ -0,0 +1,33 @@ +class CustomDifficulty +{ + class Options + { + reducedDamage = 0; + groupIndicators = 0; + friendlyTags = 0; + enemyTags = 0; + detectedMines = 0; + commands = 0; + waypoints = 0; + tacticalPing = 0; + weaponInfo = 0; + stanceIndicator = 0; + staminaBar = 0; + weaponCrosshair = 0; + visionAid = 0; + thirdPersonView = 0; + cameraShake = 0; + scoreTable = 0; + deathMessages = 0; + vonID = 0; + mapContent = 0; + autoReport = 0; + multipleSaves = 0; + }; + aiLevelPreset = 3; +}; +class CustomAILevel +{ + skillAI = 0; + precisionAI = 0; +}; \ No newline at end of file diff --git a/KAST/3b7d876b-a563-6e50-befb-76657ecf4671/perf.cfg b/KAST/3b7d876b-a563-6e50-befb-76657ecf4671/perf.cfg new file mode 100644 index 0000000..47db563 --- /dev/null +++ b/KAST/3b7d876b-a563-6e50-befb-76657ecf4671/perf.cfg @@ -0,0 +1,8 @@ +MaxMsgSend = 128; +MaxSizeGuaranteed = 512; +MaxSizeNonguaranteed = 256; +MinBandwidth = 131072; +MaxBandwidth = 4294967295; +MinErrorToSend = 0.001; +MinErrorToSendNear = 0.01; +MaxCustomFileSize = 0; \ No newline at end of file diff --git a/KAST/3b7d876b-a563-6e50-befb-76657ecf4671/server.cfg b/KAST/3b7d876b-a563-6e50-befb-76657ecf4671/server.cfg new file mode 100644 index 0000000..7db2e4c --- /dev/null +++ b/KAST/3b7d876b-a563-6e50-befb-76657ecf4671/server.cfg @@ -0,0 +1,7 @@ +Hostname = My Arma 3 Server; +Password = ; +PasswordAdmin = adminpass; +MaxPlayers = 40; +Persistent = True; +TimeStampFormat = short; +BattlEye = True; \ No newline at end of file diff --git a/KAST/Components/Pages/Mods.razor b/KAST/Components/Pages/Mods.razor index 234e8de..039c2f4 100644 --- a/KAST/Components/Pages/Mods.razor +++ b/KAST/Components/Pages/Mods.razor @@ -1,190 +1,181 @@ @page "/mods" -@* @using KAST.Data.Enums; -@using KAST.Data.Models; -@using KAST.Core.Services; -@using KAST.Server.Data; -@inject ModsService ModsService -@inject SteamService SteamService *@ +@using KAST.Data.Models +@using KAST.Core.Services +@inject ModService ModService @inject ISnackbar Snackbar Mods Mods -This component demonstrates fetching data from the server. -@* @if (localMods == null || steamMods == null) +Manage your Arma 3 mods from the database. + +@if (mods == null) { } +else if (!mods.Any()) +{ + + No mods found in the database. Add some mods to get started. + + + + Add Sample Mod + +} else { - + - - - + + + - - ID - Name - Author + Type + Steam ID + Name + Author + Enabled + Size Actions - Status - Size - - @context.SteamID + + @if (context.IsLocal) + { + + } + else + { + + } + + @(context.SteamId ?? "N/A") @context.Name - @context.Author?.Name + @(context.Author ?? "Unknown") + + @if (context.IsEnabled) + { + + } + else + { + + } + + @FormatBytes(context.SizeBytes) - - - - @switch (modContext[context.Id].GetStatus) - { - case QueryStatus.Progress: - - break; - case QueryStatus.Failed: - - break; - default: - - break; - } - - - - - @switch (modContext[context.Id].UpdateStatus) - { - case QueryStatus.Progress: - - break; - case QueryStatus.Failed: - - break; - default: - - break; - } - - + + + Edit + + + Delete + - - @context.Status - @context.ActualSize - - - - - - - - - - - - - - - - Name - Path - Size - - - - @context.Name - @context.Path - @context.ActualSize -} *@ + + + Add Sample Mod + +} @code { - // private SteamMod[]? steamMods; - // private LocalMod[]? localMods; - - // private Dictionary modContext = new(); - - // async Task GetModInfos(SteamMod mod) - // { - // modContext[mod.Id].GetStatus = QueryStatus.Progress; - // mod.Status = ModStatus.Progress; - // try - // { - // await SteamService.GetModInfo(mod.SteamID); - // } - // catch (Exception e) - // { - // modContext[mod.Id].GetStatus = QueryStatus.Failed; - // Snackbar.Add($"{e.GetType().Name} : {e.Message}", Severity.Error); - // StateHasChanged(); - // await Task.Delay(5000); - // } - // finally - // { - // modContext[mod.Id].GetStatus = QueryStatus.Success; - // StateHasChanged(); - // } - // } - + private List? mods; - // async Task DownloadMod(SteamMod mod, MouseEventArgs args) - // { - // modContext[mod.Id].UpdateStatus = QueryStatus.Progress; - // StateHasChanged(); - // try - // { - // modContext[mod.Id].UpdateProgress = 0; - // StateHasChanged(); + protected override async Task OnInitializedAsync() + { + await LoadMods(); + } - // await Task.Delay(2000); + private async Task LoadMods() + { + try + { + mods = await ModService.GetAllModsAsync(); + } + catch (Exception ex) + { + Snackbar.Add($"Error loading mods: {ex.Message}", Severity.Error); + } + } - // for (int i = 0; i < 100; i++) - // { - // await Task.Delay(100); - // modContext[mod.Id].UpdateProgress = i; - // StateHasChanged(); - // } + private async Task AddSampleMod() + { + try + { + var sampleMod = new Mod + { + Id = Guid.NewGuid(), + Name = $"Sample Mod {DateTime.Now:HH:mm:ss}", + Author = "KAST Team", + Path = "/sample/path", + SteamId = Random.Shared.Next(100000, 999999).ToString(), + IsLocal = Random.Shared.NextDouble() > 0.5, + SizeBytes = Random.Shared.NextInt64(1024 * 1024, 1024 * 1024 * 500), // 1MB to 500MB + IsEnabled = true, + Version = "1.0.0", + CreatedAt = DateTime.UtcNow + }; - // mod.Status = ModStatus.UpToDate; - // await ModsService.Save(); + await ModService.SaveModAsync(sampleMod); + await LoadMods(); + Snackbar.Add("Sample mod added successfully!", Severity.Success); + } + catch (Exception ex) + { + Snackbar.Add($"Error adding sample mod: {ex.Message}", Severity.Error); + } + } - // } - // catch (Exception e) - // { - // modContext[mod.Id].UpdateStatus = QueryStatus.Failed; - // mod.Status = ModStatus.Error; - // await ModsService.Save(); - // Snackbar.Add($"{e.GetType().Name} : {e.Message}", Severity.Error); - // StateHasChanged(); - // await Task.Delay(5000); - // } - // finally - // { - // modContext[mod.Id].UpdateStatus = QueryStatus.Success; - // StateHasChanged(); - // } - // } + private async Task DeleteMod(Guid modId) + { + try + { + if (await ModService.DeleteModAsync(modId)) + { + await LoadMods(); + Snackbar.Add("Mod deleted successfully!", Severity.Success); + } + else + { + Snackbar.Add("Failed to delete mod.", Severity.Error); + } + } + catch (Exception ex) + { + Snackbar.Add($"Error deleting mod: {ex.Message}", Severity.Error); + } + } - // protected override async Task OnInitializedAsync() - // { - // steamMods = await ModsService.GetSteamModsAsync(); - // localMods = await ModsService.GetLocalModsAsync(); - // modContext.Clear(); - // foreach (var mod in steamMods) - // { modContext.Add(mod.Id, new ModContext()); } - // } + private static string FormatBytes(long bytes) + { + if (bytes == 0) return "0 B"; + + string[] suffixes = { "B", "KB", "MB", "GB", "TB" }; + int counter = 0; + decimal number = bytes; + + while (Math.Round(number / 1024) >= 1) + { + number /= 1024; + counter++; + } + + return $"{number:n1} {suffixes[counter]}"; + } } \ No newline at end of file diff --git a/KAST/KAST.csproj b/KAST/KAST.csproj index 763155e..636b6b0 100644 --- a/KAST/KAST.csproj +++ b/KAST/KAST.csproj @@ -7,6 +7,10 @@ 930b9a04-f06a-4052-9bae-75bf7da13f28 + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/KAST/Program.cs b/KAST/Program.cs index e8a419d..5d4c760 100644 --- a/KAST/Program.cs +++ b/KAST/Program.cs @@ -30,11 +30,13 @@ public static async Task Main(string[] args) builder.Services.AddSingleton(new ActivitySource(builder.Environment.ApplicationName)); builder.Services.AddSingleton(); builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(sp => - { - var env = sp.GetRequiredService(); // Get environment - return new FileSystemService(env.ContentRootPath); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(sp => + { + var env = sp.GetRequiredService(); // Get environment + return new FileSystemService(env.ContentRootPath); }); var app = builder.Build(); diff --git a/KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/Arma3Profile b/KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/Arma3Profile new file mode 100644 index 0000000..a1f41c4 --- /dev/null +++ b/KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/Arma3Profile @@ -0,0 +1,33 @@ +class CustomDifficulty +{ + class Options + { + reducedDamage = 0; + groupIndicators = 0; + friendlyTags = 0; + enemyTags = 0; + detectedMines = 0; + commands = 0; + waypoints = 0; + tacticalPing = 0; + weaponInfo = 0; + stanceIndicator = 0; + staminaBar = 0; + weaponCrosshair = 0; + visionAid = 0; + thirdPersonView = 0; + cameraShake = 0; + scoreTable = 0; + deathMessages = 0; + vonID = 0; + mapContent = 0; + autoReport = 0; + multipleSaves = 0; + }; + aiLevelPreset = 3; +}; +class CustomAILevel +{ + skillAI = 0; + precisionAI = 0; +}; \ No newline at end of file diff --git a/KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/perf.cfg b/KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/perf.cfg new file mode 100644 index 0000000..47db563 --- /dev/null +++ b/KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/perf.cfg @@ -0,0 +1,8 @@ +MaxMsgSend = 128; +MaxSizeGuaranteed = 512; +MaxSizeNonguaranteed = 256; +MinBandwidth = 131072; +MaxBandwidth = 4294967295; +MinErrorToSend = 0.001; +MinErrorToSendNear = 0.01; +MaxCustomFileSize = 0; \ No newline at end of file diff --git a/KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/server.cfg b/KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/server.cfg new file mode 100644 index 0000000..7db2e4c --- /dev/null +++ b/KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/server.cfg @@ -0,0 +1,7 @@ +Hostname = My Arma 3 Server; +Password = ; +PasswordAdmin = adminpass; +MaxPlayers = 40; +Persistent = True; +TimeStampFormat = short; +BattlEye = True; \ No newline at end of file From 23a84a097afd207eab03263a4c267d047e6a3b3c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Sep 2025 08:18:37 +0000 Subject: [PATCH 4/6] Refactor database implementation to focus on settings system only Co-authored-by: Foxlider <19773387+Foxlider@users.noreply.github.com> --- KAST.Core/Services/ModService.cs | 102 ------ KAST.Core/Services/ProfileService.cs | 292 ----------------- KAST.Data/ApplicationDbContext.cs | 60 +--- ...ddModsProfilesAndRelationships.Designer.cs | 301 ------------------ ...926081639_EnhanceSettingsModel.Designer.cs | 82 +++++ ...=> 20250926081639_EnhanceSettingsModel.cs} | 176 +++++----- .../ApplicationDbContextModelSnapshot.cs | 241 +------------- KAST.Data/Models/KastSettings.cs | 30 +- KAST.Data/Models/Mod.cs | 65 ---- KAST.Data/Models/ModProfile.cs | 44 --- KAST.Data/Models/Profile.cs | 67 ---- KAST.Data/Models/ProfileHistory.cs | 67 ---- KAST.Data/Models/Server.cs | 36 +-- KAST/Components/Pages/Mods.razor | 291 +++++++++-------- KAST/Components/Pages/Settings.razor | 35 ++ KAST/Program.cs | 2 - 16 files changed, 389 insertions(+), 1502 deletions(-) delete mode 100644 KAST.Core/Services/ModService.cs delete mode 100644 KAST.Core/Services/ProfileService.cs delete mode 100644 KAST.Data/Migrations/20250926072553_AddModsProfilesAndRelationships.Designer.cs create mode 100644 KAST.Data/Migrations/20250926081639_EnhanceSettingsModel.Designer.cs rename KAST.Data/Migrations/{20250926072553_AddModsProfilesAndRelationships.cs => 20250926081639_EnhanceSettingsModel.cs} (85%) delete mode 100644 KAST.Data/Models/Mod.cs delete mode 100644 KAST.Data/Models/ModProfile.cs delete mode 100644 KAST.Data/Models/Profile.cs delete mode 100644 KAST.Data/Models/ProfileHistory.cs diff --git a/KAST.Core/Services/ModService.cs b/KAST.Core/Services/ModService.cs deleted file mode 100644 index e4fb993..0000000 --- a/KAST.Core/Services/ModService.cs +++ /dev/null @@ -1,102 +0,0 @@ -using KAST.Data; -using KAST.Data.Models; -using Microsoft.EntityFrameworkCore; - -namespace KAST.Core.Services -{ - public class ModService - { - private readonly ApplicationDbContext _context; - - public ModService(ApplicationDbContext context) - { - _context = context; - } - - /// - /// Get all mods from the database - /// - public async Task> GetAllModsAsync() - { - return await _context.Mods - .OrderBy(m => m.Name) - .ToListAsync(); - } - - /// - /// Get a specific mod by ID - /// - public async Task GetModByIdAsync(Guid id) - { - return await _context.Mods - .Include(m => m.ModProfiles) - .FirstOrDefaultAsync(m => m.Id == id); - } - - /// - /// Get a mod by Steam ID - /// - public async Task GetModBySteamIdAsync(string steamId) - { - return await _context.Mods - .FirstOrDefaultAsync(m => m.SteamId == steamId); - } - - /// - /// Add or update a mod - /// - public async Task SaveModAsync(Mod mod) - { - var existingMod = await _context.Mods - .FirstOrDefaultAsync(m => m.Id == mod.Id); - - if (existingMod == null) - { - _context.Mods.Add(mod); - } - else - { - _context.Entry(existingMod).CurrentValues.SetValues(mod); - existingMod.LastUpdated = DateTime.UtcNow; - } - - await _context.SaveChangesAsync(); - return mod; - } - - /// - /// Delete a mod - /// - public async Task DeleteModAsync(Guid id) - { - var mod = await _context.Mods.FindAsync(id); - if (mod == null) return false; - - _context.Mods.Remove(mod); - await _context.SaveChangesAsync(); - return true; - } - - /// - /// Get mods for a specific profile - /// - public async Task> GetModsForProfileAsync(Guid profileId) - { - return await _context.ModProfiles - .Where(mp => mp.ProfileId == profileId && mp.IsEnabled) - .OrderBy(mp => mp.Order) - .Select(mp => mp.Mod) - .ToListAsync(); - } - - /// - /// Update mod versions based on file system scan - /// - public async Task UpdateModFromFileSystemAsync(string modPath) - { - // This would integrate with existing file system scanning logic - // For now, this is a placeholder for future implementation - await Task.CompletedTask; - } - } -} \ No newline at end of file diff --git a/KAST.Core/Services/ProfileService.cs b/KAST.Core/Services/ProfileService.cs deleted file mode 100644 index c12544a..0000000 --- a/KAST.Core/Services/ProfileService.cs +++ /dev/null @@ -1,292 +0,0 @@ -using KAST.Data; -using KAST.Data.Models; -using Microsoft.EntityFrameworkCore; -using System.Text.Json; - -namespace KAST.Core.Services -{ - public class ProfileService - { - private readonly ApplicationDbContext _context; - - public ProfileService(ApplicationDbContext context) - { - _context = context; - } - - /// - /// Get all profiles - /// - public async Task> GetAllProfilesAsync() - { - return await _context.Profiles - .Include(p => p.Server) - .Include(p => p.ModProfiles) - .ThenInclude(mp => mp.Mod) - .OrderBy(p => p.Name) - .ToListAsync(); - } - - /// - /// Get a specific profile by ID - /// - public async Task GetProfileByIdAsync(Guid id) - { - return await _context.Profiles - .Include(p => p.Server) - .Include(p => p.ModProfiles) - .ThenInclude(mp => mp.Mod) - .Include(p => p.ProfileHistories) - .FirstOrDefaultAsync(p => p.Id == id); - } - - /// - /// Get the currently active profile - /// - public async Task GetActiveProfileAsync() - { - return await _context.Profiles - .Include(p => p.Server) - .Include(p => p.ModProfiles) - .ThenInclude(mp => mp.Mod) - .FirstOrDefaultAsync(p => p.IsActive); - } - - /// - /// Create a new profile - /// - public async Task CreateProfileAsync(Profile profile) - { - profile.Id = Guid.NewGuid(); - profile.CreatedAt = DateTime.UtcNow; - profile.LastModified = DateTime.UtcNow; - - _context.Profiles.Add(profile); - await _context.SaveChangesAsync(); - - // Create initial history entry - await CreateHistoryEntryAsync(profile, "Profile created"); - - return profile; - } - - /// - /// Update an existing profile - /// - public async Task UpdateProfileAsync(Profile profile, string? changeDescription = null) - { - var existingProfile = await _context.Profiles - .Include(p => p.ProfileHistories) - .FirstOrDefaultAsync(p => p.Id == profile.Id); - - if (existingProfile == null) - throw new ArgumentException("Profile not found", nameof(profile)); - - // Store old values for history - var oldProfile = new Profile - { - ServerConfig = existingProfile.ServerConfig, - ServerProfile = existingProfile.ServerProfile, - PerformanceConfig = existingProfile.PerformanceConfig, - CommandLineArgs = existingProfile.CommandLineArgs - }; - - // Update profile - _context.Entry(existingProfile).CurrentValues.SetValues(profile); - existingProfile.LastModified = DateTime.UtcNow; - - await _context.SaveChangesAsync(); - - // Create history entry if there were significant changes - if (HasSignificantChanges(oldProfile, existingProfile)) - { - await CreateHistoryEntryAsync(existingProfile, changeDescription ?? "Profile updated"); - } - - return existingProfile; - } - - /// - /// Set a profile as active (and deactivate others) - /// - public async Task SetActiveProfileAsync(Guid profileId) - { - // Deactivate all profiles - await _context.Profiles - .Where(p => p.IsActive) - .ExecuteUpdateAsync(p => p.SetProperty(x => x.IsActive, false)); - - // Activate the specified profile - var profile = await _context.Profiles.FindAsync(profileId); - if (profile == null) - throw new ArgumentException("Profile not found", nameof(profileId)); - - profile.IsActive = true; - await _context.SaveChangesAsync(); - - return profile; - } - - /// - /// Delete a profile - /// - public async Task DeleteProfileAsync(Guid id) - { - var profile = await _context.Profiles.FindAsync(id); - if (profile == null) return false; - - _context.Profiles.Remove(profile); - await _context.SaveChangesAsync(); - return true; - } - - /// - /// Add a mod to a profile - /// - public async Task AddModToProfileAsync(Guid profileId, Guid modId, int order = 0) - { - // Check if relationship already exists - var existing = await _context.ModProfiles - .FirstOrDefaultAsync(mp => mp.ProfileId == profileId && mp.ModId == modId); - - if (existing != null) - { - existing.IsEnabled = true; - existing.Order = order; - } - else - { - var modProfile = new ModProfile - { - Id = Guid.NewGuid(), - ProfileId = profileId, - ModId = modId, - Order = order, - IsEnabled = true, - AddedAt = DateTime.UtcNow - }; - - _context.ModProfiles.Add(modProfile); - } - - await _context.SaveChangesAsync(); - } - - /// - /// Remove a mod from a profile - /// - public async Task RemoveModFromProfileAsync(Guid profileId, Guid modId) - { - var modProfile = await _context.ModProfiles - .FirstOrDefaultAsync(mp => mp.ProfileId == profileId && mp.ModId == modId); - - if (modProfile != null) - { - _context.ModProfiles.Remove(modProfile); - await _context.SaveChangesAsync(); - } - } - - /// - /// Update mod order in a profile - /// - public async Task UpdateModOrderInProfileAsync(Guid profileId, Dictionary modOrders) - { - var modProfiles = await _context.ModProfiles - .Where(mp => mp.ProfileId == profileId) - .ToListAsync(); - - foreach (var modProfile in modProfiles) - { - if (modOrders.TryGetValue(modProfile.ModId, out int newOrder)) - { - modProfile.Order = newOrder; - } - } - - await _context.SaveChangesAsync(); - } - - /// - /// Create a history entry for a profile - /// - private async Task CreateHistoryEntryAsync(Profile profile, string changeDescription) - { - var lastVersion = await _context.ProfileHistories - .Where(ph => ph.ProfileId == profile.Id) - .MaxAsync(ph => (int?)ph.Version) ?? 0; - - var modsSnapshot = await _context.ModProfiles - .Where(mp => mp.ProfileId == profile.Id) - .Select(mp => new { mp.ModId, mp.Order, mp.IsEnabled }) - .ToListAsync(); - - var history = new ProfileHistory - { - Id = Guid.NewGuid(), - ProfileId = profile.Id, - Version = lastVersion + 1, - ChangeDescription = changeDescription, - ServerConfigSnapshot = profile.ServerConfig, - ServerProfileSnapshot = profile.ServerProfile, - PerformanceConfigSnapshot = profile.PerformanceConfig, - CommandLineArgsSnapshot = profile.CommandLineArgs, - ModsSnapshot = JsonSerializer.Serialize(modsSnapshot), - CreatedAt = DateTime.UtcNow - }; - - _context.ProfileHistories.Add(history); - await _context.SaveChangesAsync(); - } - - /// - /// Check if there are significant changes between two profiles - /// - private static bool HasSignificantChanges(Profile oldProfile, Profile newProfile) - { - return oldProfile.ServerConfig != newProfile.ServerConfig || - oldProfile.ServerProfile != newProfile.ServerProfile || - oldProfile.PerformanceConfig != newProfile.PerformanceConfig || - oldProfile.CommandLineArgs != newProfile.CommandLineArgs; - } - - /// - /// Get profile history - /// - public async Task> GetProfileHistoryAsync(Guid profileId) - { - return await _context.ProfileHistories - .Where(ph => ph.ProfileId == profileId) - .OrderByDescending(ph => ph.Version) - .ToListAsync(); - } - - /// - /// Restore profile from a specific version - /// - public async Task RestoreProfileFromHistoryAsync(Guid profileId, int version) - { - var profile = await GetProfileByIdAsync(profileId); - var historyEntry = await _context.ProfileHistories - .FirstOrDefaultAsync(ph => ph.ProfileId == profileId && ph.Version == version); - - if (profile == null || historyEntry == null) - throw new ArgumentException("Profile or history entry not found"); - - // Restore configuration - profile.ServerConfig = historyEntry.ServerConfigSnapshot; - profile.ServerProfile = historyEntry.ServerProfileSnapshot; - profile.PerformanceConfig = historyEntry.PerformanceConfigSnapshot; - profile.CommandLineArgs = historyEntry.CommandLineArgsSnapshot; - - // Restore mods if snapshot exists - if (!string.IsNullOrEmpty(historyEntry.ModsSnapshot)) - { - var modsSnapshot = JsonSerializer.Deserialize>(historyEntry.ModsSnapshot); - // Implementation would restore mod associations here - } - - return await UpdateProfileAsync(profile, $"Restored from version {version}"); - } - } -} \ No newline at end of file diff --git a/KAST.Data/ApplicationDbContext.cs b/KAST.Data/ApplicationDbContext.cs index beeaa10..07af8d1 100644 --- a/KAST.Data/ApplicationDbContext.cs +++ b/KAST.Data/ApplicationDbContext.cs @@ -12,10 +12,6 @@ public class ApplicationDbContext : DbContext #region DbSets public DbSet Servers { get; set; } public DbSet Settings { get; set; } - public DbSet Mods { get; set; } - public DbSet Profiles { get; set; } - public DbSet ModProfiles { get; set; } - public DbSet ProfileHistories { get; set; } #endregion @@ -54,61 +50,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) } protected override void OnModelCreating(ModelBuilder modelBuilder) - { - // Configure Mod entity - modelBuilder.Entity(entity => - { - entity.HasIndex(e => e.SteamId).IsUnique(); - entity.Property(e => e.Name).IsRequired(); - entity.Property(e => e.Path).IsRequired(); - }); - - // Configure Profile entity - modelBuilder.Entity(entity => - { - entity.Property(e => e.Name).IsRequired().HasMaxLength(255); - entity.HasOne(e => e.Server) - .WithMany(s => s.Profiles) - .HasForeignKey(e => e.ServerId) - .OnDelete(DeleteBehavior.SetNull); - }); - - // Configure ModProfile many-to-many relationship - modelBuilder.Entity(entity => - { - entity.HasOne(mp => mp.Mod) - .WithMany(m => m.ModProfiles) - .HasForeignKey(mp => mp.ModId) - .OnDelete(DeleteBehavior.Cascade); - - entity.HasOne(mp => mp.Profile) - .WithMany(p => p.ModProfiles) - .HasForeignKey(mp => mp.ProfileId) - .OnDelete(DeleteBehavior.Cascade); - - // Ensure unique combination of mod and profile - entity.HasIndex(mp => new { mp.ModId, mp.ProfileId }).IsUnique(); - }); - - // Configure ProfileHistory - modelBuilder.Entity(entity => - { - entity.HasOne(ph => ph.Profile) - .WithMany(p => p.ProfileHistories) - .HasForeignKey(ph => ph.ProfileId) - .OnDelete(DeleteBehavior.Cascade); - - // Ensure unique combination of profile and version - entity.HasIndex(ph => new { ph.ProfileId, ph.Version }).IsUnique(); - }); - - // Configure Server entity - modelBuilder.Entity(entity => - { - entity.Property(e => e.Name).IsRequired().HasMaxLength(255); - entity.Property(e => e.InstallPath).IsRequired(); - }); - } + { /* So far we have nothing to do here */ } public void EnsureSeedData() { /* So far we have no need to seed the DB as the models are not ready for production yet */ } diff --git a/KAST.Data/Migrations/20250926072553_AddModsProfilesAndRelationships.Designer.cs b/KAST.Data/Migrations/20250926072553_AddModsProfilesAndRelationships.Designer.cs deleted file mode 100644 index f571082..0000000 --- a/KAST.Data/Migrations/20250926072553_AddModsProfilesAndRelationships.Designer.cs +++ /dev/null @@ -1,301 +0,0 @@ -// -using System; -using KAST.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace KAST.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20250926072553_AddModsProfilesAndRelationships")] - partial class AddModsProfilesAndRelationships - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "9.0.9"); - - modelBuilder.Entity("KAST.Data.Models.KastSettings", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("ApiKey") - .HasColumnType("TEXT"); - - b.Property("ModFolderPath") - .HasColumnType("TEXT"); - - b.Property("ServerDefaultPath") - .HasColumnType("TEXT"); - - b.Property("ThemeAccent") - .HasMaxLength(10) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("KAST.Data.Models.Mod", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("Author") - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("IsEnabled") - .HasColumnType("INTEGER"); - - b.Property("IsLocal") - .HasColumnType("INTEGER"); - - b.Property("LastUpdated") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Path") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("SizeBytes") - .HasColumnType("INTEGER"); - - b.Property("SteamId") - .HasColumnType("TEXT"); - - b.Property("Version") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("SteamId") - .IsUnique(); - - b.ToTable("Mods"); - }); - - modelBuilder.Entity("KAST.Data.Models.ModProfile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AddedAt") - .HasColumnType("TEXT"); - - b.Property("IsEnabled") - .HasColumnType("INTEGER"); - - b.Property("ModId") - .HasColumnType("TEXT"); - - b.Property("Order") - .HasColumnType("INTEGER"); - - b.Property("ProfileId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ProfileId"); - - b.HasIndex("ModId", "ProfileId") - .IsUnique(); - - b.ToTable("ModProfiles"); - }); - - modelBuilder.Entity("KAST.Data.Models.Profile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CommandLineArgs") - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("Description") - .HasColumnType("TEXT"); - - b.Property("IsActive") - .HasColumnType("INTEGER"); - - b.Property("LastModified") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("TEXT"); - - b.Property("PerformanceConfig") - .HasColumnType("TEXT"); - - b.Property("ServerConfig") - .HasColumnType("TEXT"); - - b.Property("ServerId") - .HasColumnType("TEXT"); - - b.Property("ServerProfile") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ServerId"); - - b.ToTable("Profiles"); - }); - - modelBuilder.Entity("KAST.Data.Models.ProfileHistory", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("ChangeDescription") - .HasColumnType("TEXT"); - - b.Property("ChangedBy") - .HasColumnType("TEXT"); - - b.Property("CommandLineArgsSnapshot") - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("ModsSnapshot") - .HasColumnType("TEXT"); - - b.Property("PerformanceConfigSnapshot") - .HasColumnType("TEXT"); - - b.Property("ProfileId") - .HasColumnType("TEXT"); - - b.Property("ServerConfigSnapshot") - .HasColumnType("TEXT"); - - b.Property("ServerProfileSnapshot") - .HasColumnType("TEXT"); - - b.Property("Version") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("ProfileId", "Version") - .IsUnique(); - - b.ToTable("ProfileHistories"); - }); - - modelBuilder.Entity("KAST.Data.Models.Server", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("InstallPath") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("LastUpdated") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("TEXT"); - - b.Property("Version") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Servers"); - }); - - modelBuilder.Entity("KAST.Data.Models.ModProfile", b => - { - b.HasOne("KAST.Data.Models.Mod", "Mod") - .WithMany("ModProfiles") - .HasForeignKey("ModId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("KAST.Data.Models.Profile", "Profile") - .WithMany("ModProfiles") - .HasForeignKey("ProfileId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Mod"); - - b.Navigation("Profile"); - }); - - modelBuilder.Entity("KAST.Data.Models.Profile", b => - { - b.HasOne("KAST.Data.Models.Server", "Server") - .WithMany("Profiles") - .HasForeignKey("ServerId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("KAST.Data.Models.ProfileHistory", b => - { - b.HasOne("KAST.Data.Models.Profile", "Profile") - .WithMany("ProfileHistories") - .HasForeignKey("ProfileId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Profile"); - }); - - modelBuilder.Entity("KAST.Data.Models.Mod", b => - { - b.Navigation("ModProfiles"); - }); - - modelBuilder.Entity("KAST.Data.Models.Profile", b => - { - b.Navigation("ModProfiles"); - - b.Navigation("ProfileHistories"); - }); - - modelBuilder.Entity("KAST.Data.Models.Server", b => - { - b.Navigation("Profiles"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/KAST.Data/Migrations/20250926081639_EnhanceSettingsModel.Designer.cs b/KAST.Data/Migrations/20250926081639_EnhanceSettingsModel.Designer.cs new file mode 100644 index 0000000..943710e --- /dev/null +++ b/KAST.Data/Migrations/20250926081639_EnhanceSettingsModel.Designer.cs @@ -0,0 +1,82 @@ +// +using System; +using KAST.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace KAST.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250926081639_EnhanceSettingsModel")] + partial class EnhanceSettingsModel + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.9"); + + modelBuilder.Entity("KAST.Data.Models.KastSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApiKey") + .HasColumnType("TEXT"); + + b.Property("CheckForUpdates") + .HasColumnType("INTEGER"); + + b.Property("DebugLogging") + .HasColumnType("INTEGER"); + + b.Property("Language") + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.Property("ModFolderPath") + .HasColumnType("TEXT"); + + b.Property("ServerDefaultPath") + .HasColumnType("TEXT"); + + b.Property("ThemeAccent") + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.Property("ThemeMode") + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("KAST.Data.Models.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("InstallPath") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Servers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/KAST.Data/Migrations/20250926072553_AddModsProfilesAndRelationships.cs b/KAST.Data/Migrations/20250926081639_EnhanceSettingsModel.cs similarity index 85% rename from KAST.Data/Migrations/20250926072553_AddModsProfilesAndRelationships.cs rename to KAST.Data/Migrations/20250926081639_EnhanceSettingsModel.cs index e6afbc7..f134292 100644 --- a/KAST.Data/Migrations/20250926072553_AddModsProfilesAndRelationships.cs +++ b/KAST.Data/Migrations/20250926081639_EnhanceSettingsModel.cs @@ -6,40 +6,80 @@ namespace KAST.Data.Migrations { /// - public partial class AddModsProfilesAndRelationships : Migration + public partial class EnhanceSettingsModel : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { - migrationBuilder.AlterColumn( - name: "ThemeAccent", + migrationBuilder.DropTable( + name: "ModProfiles"); + + migrationBuilder.DropTable( + name: "ProfileHistories"); + + migrationBuilder.DropTable( + name: "Mods"); + + migrationBuilder.DropTable( + name: "Profiles"); + + migrationBuilder.DropColumn( + name: "CreatedAt", + table: "Servers"); + + migrationBuilder.DropColumn( + name: "LastUpdated", + table: "Servers"); + + migrationBuilder.DropColumn( + name: "Version", + table: "Servers"); + + migrationBuilder.AddColumn( + name: "CheckForUpdates", table: "Settings", - type: "TEXT", - maxLength: 10, - nullable: true, - oldClrType: typeof(string), - oldType: "TEXT", - oldMaxLength: 10); + type: "INTEGER", + nullable: true); - migrationBuilder.AlterColumn( - name: "ModFolderPath", + migrationBuilder.AddColumn( + name: "DebugLogging", table: "Settings", - type: "TEXT", - nullable: true, - oldClrType: typeof(string), - oldType: "TEXT"); + type: "INTEGER", + nullable: true); migrationBuilder.AddColumn( - name: "ApiKey", + name: "Language", table: "Settings", type: "TEXT", + maxLength: 10, nullable: true); migrationBuilder.AddColumn( - name: "ServerDefaultPath", + name: "ThemeMode", table: "Settings", type: "TEXT", + maxLength: 10, nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CheckForUpdates", + table: "Settings"); + + migrationBuilder.DropColumn( + name: "DebugLogging", + table: "Settings"); + + migrationBuilder.DropColumn( + name: "Language", + table: "Settings"); + + migrationBuilder.DropColumn( + name: "ThemeMode", + table: "Settings"); migrationBuilder.AddColumn( name: "CreatedAt", @@ -66,16 +106,16 @@ protected override void Up(MigrationBuilder migrationBuilder) columns: table => new { Id = table.Column(type: "TEXT", nullable: false), - SteamId = table.Column(type: "TEXT", nullable: true), - Name = table.Column(type: "TEXT", nullable: false), Author = table.Column(type: "TEXT", nullable: true), - Path = table.Column(type: "TEXT", nullable: false), - SizeBytes = table.Column(type: "INTEGER", nullable: false), + CreatedAt = table.Column(type: "TEXT", nullable: false), + IsEnabled = table.Column(type: "INTEGER", nullable: false), IsLocal = table.Column(type: "INTEGER", nullable: false), LastUpdated = table.Column(type: "TEXT", nullable: true), - Version = table.Column(type: "TEXT", nullable: true), - IsEnabled = table.Column(type: "INTEGER", nullable: false), - CreatedAt = table.Column(type: "TEXT", nullable: false) + Name = table.Column(type: "TEXT", nullable: false), + Path = table.Column(type: "TEXT", nullable: false), + SizeBytes = table.Column(type: "INTEGER", nullable: false), + SteamId = table.Column(type: "TEXT", nullable: true), + Version = table.Column(type: "TEXT", nullable: true) }, constraints: table => { @@ -87,16 +127,16 @@ protected override void Up(MigrationBuilder migrationBuilder) columns: table => new { Id = table.Column(type: "TEXT", nullable: false), - Name = table.Column(type: "TEXT", maxLength: 255, nullable: false), - Description = table.Column(type: "TEXT", nullable: true), - ServerConfig = table.Column(type: "TEXT", nullable: true), - ServerProfile = table.Column(type: "TEXT", nullable: true), - PerformanceConfig = table.Column(type: "TEXT", nullable: true), + ServerId = table.Column(type: "TEXT", nullable: true), CommandLineArgs = table.Column(type: "TEXT", nullable: true), - IsActive = table.Column(type: "INTEGER", nullable: false), CreatedAt = table.Column(type: "TEXT", nullable: false), + Description = table.Column(type: "TEXT", nullable: true), + IsActive = table.Column(type: "INTEGER", nullable: false), LastModified = table.Column(type: "TEXT", nullable: false), - ServerId = table.Column(type: "TEXT", nullable: true) + Name = table.Column(type: "TEXT", maxLength: 255, nullable: false), + PerformanceConfig = table.Column(type: "TEXT", nullable: true), + ServerConfig = table.Column(type: "TEXT", nullable: true), + ServerProfile = table.Column(type: "TEXT", nullable: true) }, constraints: table => { @@ -116,9 +156,9 @@ protected override void Up(MigrationBuilder migrationBuilder) Id = table.Column(type: "TEXT", nullable: false), ModId = table.Column(type: "TEXT", nullable: false), ProfileId = table.Column(type: "TEXT", nullable: false), - Order = table.Column(type: "INTEGER", nullable: false), + AddedAt = table.Column(type: "TEXT", nullable: false), IsEnabled = table.Column(type: "INTEGER", nullable: false), - AddedAt = table.Column(type: "TEXT", nullable: false) + Order = table.Column(type: "INTEGER", nullable: false) }, constraints: table => { @@ -143,15 +183,15 @@ protected override void Up(MigrationBuilder migrationBuilder) { Id = table.Column(type: "TEXT", nullable: false), ProfileId = table.Column(type: "TEXT", nullable: false), - Version = table.Column(type: "INTEGER", nullable: false), ChangeDescription = table.Column(type: "TEXT", nullable: true), - ServerConfigSnapshot = table.Column(type: "TEXT", nullable: true), - ServerProfileSnapshot = table.Column(type: "TEXT", nullable: true), - PerformanceConfigSnapshot = table.Column(type: "TEXT", nullable: true), + ChangedBy = table.Column(type: "TEXT", nullable: true), CommandLineArgsSnapshot = table.Column(type: "TEXT", nullable: true), - ModsSnapshot = table.Column(type: "TEXT", nullable: true), CreatedAt = table.Column(type: "TEXT", nullable: false), - ChangedBy = table.Column(type: "TEXT", nullable: true) + ModsSnapshot = table.Column(type: "TEXT", nullable: true), + PerformanceConfigSnapshot = table.Column(type: "TEXT", nullable: true), + ServerConfigSnapshot = table.Column(type: "TEXT", nullable: true), + ServerProfileSnapshot = table.Column(type: "TEXT", nullable: true), + Version = table.Column(type: "INTEGER", nullable: false) }, constraints: table => { @@ -192,63 +232,5 @@ protected override void Up(MigrationBuilder migrationBuilder) table: "Profiles", column: "ServerId"); } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "ModProfiles"); - - migrationBuilder.DropTable( - name: "ProfileHistories"); - - migrationBuilder.DropTable( - name: "Mods"); - - migrationBuilder.DropTable( - name: "Profiles"); - - migrationBuilder.DropColumn( - name: "ApiKey", - table: "Settings"); - - migrationBuilder.DropColumn( - name: "ServerDefaultPath", - table: "Settings"); - - migrationBuilder.DropColumn( - name: "CreatedAt", - table: "Servers"); - - migrationBuilder.DropColumn( - name: "LastUpdated", - table: "Servers"); - - migrationBuilder.DropColumn( - name: "Version", - table: "Servers"); - - migrationBuilder.AlterColumn( - name: "ThemeAccent", - table: "Settings", - type: "TEXT", - maxLength: 10, - nullable: false, - defaultValue: "", - oldClrType: typeof(string), - oldType: "TEXT", - oldMaxLength: 10, - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "ModFolderPath", - table: "Settings", - type: "TEXT", - nullable: false, - defaultValue: "", - oldClrType: typeof(string), - oldType: "TEXT", - oldNullable: true); - } } } diff --git a/KAST.Data/Migrations/ApplicationDbContextModelSnapshot.cs b/KAST.Data/Migrations/ApplicationDbContextModelSnapshot.cs index b706b17..829057f 100644 --- a/KAST.Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/KAST.Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -26,185 +26,33 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ApiKey") .HasColumnType("TEXT"); - b.Property("ModFolderPath") - .HasColumnType("TEXT"); - - b.Property("ServerDefaultPath") - .HasColumnType("TEXT"); - - b.Property("ThemeAccent") - .HasMaxLength(10) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("KAST.Data.Models.Mod", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("Author") - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("IsEnabled") - .HasColumnType("INTEGER"); - - b.Property("IsLocal") - .HasColumnType("INTEGER"); - - b.Property("LastUpdated") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Path") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("SizeBytes") - .HasColumnType("INTEGER"); - - b.Property("SteamId") - .HasColumnType("TEXT"); - - b.Property("Version") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("SteamId") - .IsUnique(); - - b.ToTable("Mods"); - }); - - modelBuilder.Entity("KAST.Data.Models.ModProfile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AddedAt") - .HasColumnType("TEXT"); - - b.Property("IsEnabled") + b.Property("CheckForUpdates") .HasColumnType("INTEGER"); - b.Property("ModId") - .HasColumnType("TEXT"); - - b.Property("Order") + b.Property("DebugLogging") .HasColumnType("INTEGER"); - b.Property("ProfileId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ProfileId"); - - b.HasIndex("ModId", "ProfileId") - .IsUnique(); - - b.ToTable("ModProfiles"); - }); - - modelBuilder.Entity("KAST.Data.Models.Profile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CommandLineArgs") - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("Description") - .HasColumnType("TEXT"); - - b.Property("IsActive") - .HasColumnType("INTEGER"); - - b.Property("LastModified") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("TEXT"); - - b.Property("PerformanceConfig") - .HasColumnType("TEXT"); - - b.Property("ServerConfig") - .HasColumnType("TEXT"); - - b.Property("ServerId") - .HasColumnType("TEXT"); - - b.Property("ServerProfile") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ServerId"); - - b.ToTable("Profiles"); - }); - - modelBuilder.Entity("KAST.Data.Models.ProfileHistory", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("ChangeDescription") - .HasColumnType("TEXT"); - - b.Property("ChangedBy") - .HasColumnType("TEXT"); - - b.Property("CommandLineArgsSnapshot") - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("ModsSnapshot") + b.Property("Language") + .HasMaxLength(10) .HasColumnType("TEXT"); - b.Property("PerformanceConfigSnapshot") + b.Property("ModFolderPath") .HasColumnType("TEXT"); - b.Property("ProfileId") + b.Property("ServerDefaultPath") .HasColumnType("TEXT"); - b.Property("ServerConfigSnapshot") + b.Property("ThemeAccent") + .HasMaxLength(10) .HasColumnType("TEXT"); - b.Property("ServerProfileSnapshot") + b.Property("ThemeMode") + .HasMaxLength(10) .HasColumnType("TEXT"); - b.Property("Version") - .HasColumnType("INTEGER"); - b.HasKey("Id"); - b.HasIndex("ProfileId", "Version") - .IsUnique(); - - b.ToTable("ProfileHistories"); + b.ToTable("Settings"); }); modelBuilder.Entity("KAST.Data.Models.Server", b => @@ -213,85 +61,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("TEXT"); - b.Property("CreatedAt") - .HasColumnType("TEXT"); - b.Property("InstallPath") .IsRequired() .HasColumnType("TEXT"); - b.Property("LastUpdated") - .HasColumnType("TEXT"); - b.Property("Name") .IsRequired() - .HasMaxLength(255) - .HasColumnType("TEXT"); - - b.Property("Version") .HasColumnType("TEXT"); b.HasKey("Id"); b.ToTable("Servers"); }); - - modelBuilder.Entity("KAST.Data.Models.ModProfile", b => - { - b.HasOne("KAST.Data.Models.Mod", "Mod") - .WithMany("ModProfiles") - .HasForeignKey("ModId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("KAST.Data.Models.Profile", "Profile") - .WithMany("ModProfiles") - .HasForeignKey("ProfileId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Mod"); - - b.Navigation("Profile"); - }); - - modelBuilder.Entity("KAST.Data.Models.Profile", b => - { - b.HasOne("KAST.Data.Models.Server", "Server") - .WithMany("Profiles") - .HasForeignKey("ServerId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("KAST.Data.Models.ProfileHistory", b => - { - b.HasOne("KAST.Data.Models.Profile", "Profile") - .WithMany("ProfileHistories") - .HasForeignKey("ProfileId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Profile"); - }); - - modelBuilder.Entity("KAST.Data.Models.Mod", b => - { - b.Navigation("ModProfiles"); - }); - - modelBuilder.Entity("KAST.Data.Models.Profile", b => - { - b.Navigation("ModProfiles"); - - b.Navigation("ProfileHistories"); - }); - - modelBuilder.Entity("KAST.Data.Models.Server", b => - { - b.Navigation("Profiles"); - }); #pragma warning restore 612, 618 } } diff --git a/KAST.Data/Models/KastSettings.cs b/KAST.Data/Models/KastSettings.cs index 64946a0..5076a29 100644 --- a/KAST.Data/Models/KastSettings.cs +++ b/KAST.Data/Models/KastSettings.cs @@ -27,10 +27,36 @@ public class KastSettings [EnvVariableAttribute("KAST_STEAM_API_KEY")] public string? ApiKey { get; set; } - /// - /// Gets or sets the default Arma server path. + /// + /// Gets or sets the default Arma server path. /// [EnvVariableAttribute("KAST_SERVER_DEFAULT_PATH")] public string? ServerDefaultPath { get; set; } + + /// + /// Gets or sets the theme mode (light, dark, auto). + /// + [MaxLength(10)] + [EnvVariableAttribute("KAST_THEME_MODE")] + public string? ThemeMode { get; set; } = "auto"; + + /// + /// Gets or sets whether to check for updates on startup. + /// + [EnvVariableAttribute("KAST_CHECK_UPDATES")] + public bool? CheckForUpdates { get; set; } = true; + + /// + /// Gets or sets the UI language/locale. + /// + [MaxLength(10)] + [EnvVariableAttribute("KAST_LANGUAGE")] + public string? Language { get; set; } = "en"; + + /// + /// Gets or sets whether to enable debug logging. + /// + [EnvVariableAttribute("KAST_DEBUG_LOGGING")] + public bool? DebugLogging { get; set; } = false; } } diff --git a/KAST.Data/Models/Mod.cs b/KAST.Data/Models/Mod.cs deleted file mode 100644 index 1e66358..0000000 --- a/KAST.Data/Models/Mod.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace KAST.Data.Models -{ - public class Mod - { - [Key] - public Guid Id { get; set; } - - /// - /// Steam Workshop ID for steam mods, null for local mods - /// - public string? SteamId { get; set; } - - /// - /// Display name of the mod - /// - [Required] - public string Name { get; set; } = string.Empty; - - /// - /// Author of the mod - /// - public string? Author { get; set; } - - /// - /// Local file path to the mod - /// - [Required] - public string Path { get; set; } = string.Empty; - - /// - /// Size of the mod in bytes - /// - public long SizeBytes { get; set; } - - /// - /// Whether this is a local mod or from Steam Workshop - /// - public bool IsLocal { get; set; } - - /// - /// Last time the mod was updated - /// - public DateTime? LastUpdated { get; set; } - - /// - /// Current version/revision of the mod - /// - public string? Version { get; set; } - - /// - /// Whether the mod is currently enabled - /// - public bool IsEnabled { get; set; } = true; - - /// - /// Creation timestamp - /// - public DateTime CreatedAt { get; set; } = DateTime.UtcNow; - - // Navigation properties - public ICollection ModProfiles { get; set; } = new List(); - } -} \ No newline at end of file diff --git a/KAST.Data/Models/ModProfile.cs b/KAST.Data/Models/ModProfile.cs deleted file mode 100644 index c37a547..0000000 --- a/KAST.Data/Models/ModProfile.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace KAST.Data.Models -{ - /// - /// Junction table for many-to-many relationship between Mods and Profiles - /// - public class ModProfile - { - [Key] - public Guid Id { get; set; } - - /// - /// Foreign key to Mod - /// - [Required] - public Guid ModId { get; set; } - - /// - /// Foreign key to Profile - /// - [Required] - public Guid ProfileId { get; set; } - - /// - /// Order of the mod in the profile (for launch order) - /// - public int Order { get; set; } - - /// - /// Whether this mod is enabled for this specific profile - /// - public bool IsEnabled { get; set; } = true; - - /// - /// When this mod was added to the profile - /// - public DateTime AddedAt { get; set; } = DateTime.UtcNow; - - // Navigation properties - public Mod Mod { get; set; } = null!; - public Profile Profile { get; set; } = null!; - } -} \ No newline at end of file diff --git a/KAST.Data/Models/Profile.cs b/KAST.Data/Models/Profile.cs deleted file mode 100644 index 0af59e7..0000000 --- a/KAST.Data/Models/Profile.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace KAST.Data.Models -{ - public class Profile - { - [Key] - public Guid Id { get; set; } - - /// - /// Display name for the profile - /// - [Required] - [MaxLength(255)] - public string Name { get; set; } = string.Empty; - - /// - /// Optional description of the profile - /// - public string? Description { get; set; } - - /// - /// Server configuration (serialized JSON) - /// - public string? ServerConfig { get; set; } - - /// - /// Server profile configuration (serialized JSON) - /// - public string? ServerProfile { get; set; } - - /// - /// Performance configuration (serialized JSON) - /// - public string? PerformanceConfig { get; set; } - - /// - /// Command line arguments for the server - /// - public string? CommandLineArgs { get; set; } - - /// - /// Whether this profile is currently active - /// - public bool IsActive { get; set; } - - /// - /// Creation timestamp - /// - public DateTime CreatedAt { get; set; } = DateTime.UtcNow; - - /// - /// Last modified timestamp - /// - public DateTime LastModified { get; set; } = DateTime.UtcNow; - - /// - /// Associated server ID - /// - public Guid? ServerId { get; set; } - - // Navigation properties - public Server? Server { get; set; } - public ICollection ModProfiles { get; set; } = new List(); - public ICollection ProfileHistories { get; set; } = new List(); - } -} \ No newline at end of file diff --git a/KAST.Data/Models/ProfileHistory.cs b/KAST.Data/Models/ProfileHistory.cs deleted file mode 100644 index 07d4e47..0000000 --- a/KAST.Data/Models/ProfileHistory.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace KAST.Data.Models -{ - /// - /// Stores historical versions of profile configurations for versioning support - /// - public class ProfileHistory - { - [Key] - public Guid Id { get; set; } - - /// - /// Foreign key to the profile this history entry belongs to - /// - [Required] - public Guid ProfileId { get; set; } - - /// - /// Version number for this configuration snapshot - /// - public int Version { get; set; } - - /// - /// Optional comment or description of changes made - /// - public string? ChangeDescription { get; set; } - - /// - /// Snapshot of server configuration at this point in time - /// - public string? ServerConfigSnapshot { get; set; } - - /// - /// Snapshot of server profile configuration at this point in time - /// - public string? ServerProfileSnapshot { get; set; } - - /// - /// Snapshot of performance configuration at this point in time - /// - public string? PerformanceConfigSnapshot { get; set; } - - /// - /// Snapshot of command line arguments at this point in time - /// - public string? CommandLineArgsSnapshot { get; set; } - - /// - /// JSON array of mod IDs and their order at this point in time - /// - public string? ModsSnapshot { get; set; } - - /// - /// When this snapshot was created - /// - public DateTime CreatedAt { get; set; } = DateTime.UtcNow; - - /// - /// Who made the changes (for future use) - /// - public string? ChangedBy { get; set; } - - // Navigation properties - public Profile Profile { get; set; } = null!; - } -} \ No newline at end of file diff --git a/KAST.Data/Models/Server.cs b/KAST.Data/Models/Server.cs index 52eb466..261e6bd 100644 --- a/KAST.Data/Models/Server.cs +++ b/KAST.Data/Models/Server.cs @@ -1,41 +1,11 @@ -using System.ComponentModel.DataAnnotations; - -namespace KAST.Data.Models +namespace KAST.Data.Models { public class Server { - [Key] public Guid Id { get; set; } - /// - /// Display name for the server - /// - [Required] - [MaxLength(255)] - public string Name { get; set; } = string.Empty; - - /// - /// Installation path for the Arma 3 server - /// - [Required] - public string InstallPath { get; set; } = string.Empty; - - /// - /// Version of the server installation - /// - public string? Version { get; set; } - - /// - /// When the server was created/added - /// - public DateTime CreatedAt { get; set; } = DateTime.UtcNow; - - /// - /// Last time the server was updated - /// - public DateTime LastUpdated { get; set; } = DateTime.UtcNow; + public string Name { get; set; } - // Navigation properties - public ICollection Profiles { get; set; } = new List(); + public string InstallPath { get; set; } } } diff --git a/KAST/Components/Pages/Mods.razor b/KAST/Components/Pages/Mods.razor index 039c2f4..234e8de 100644 --- a/KAST/Components/Pages/Mods.razor +++ b/KAST/Components/Pages/Mods.razor @@ -1,181 +1,190 @@ @page "/mods" -@using KAST.Data.Models -@using KAST.Core.Services -@inject ModService ModService +@* @using KAST.Data.Enums; +@using KAST.Data.Models; +@using KAST.Core.Services; +@using KAST.Server.Data; +@inject ModsService ModsService +@inject SteamService SteamService *@ @inject ISnackbar Snackbar Mods Mods -Manage your Arma 3 mods from the database. - -@if (mods == null) +This component demonstrates fetching data from the server. +@* @if (localMods == null || steamMods == null) { } -else if (!mods.Any()) -{ - - No mods found in the database. Add some mods to get started. - - - - Add Sample Mod - -} else { - + - - - + + + - Type - Steam ID - Name - Author - Enabled - Size + + ID + Name + Author Actions + Status + Size - - @if (context.IsLocal) - { - - } - else - { - - } - - @(context.SteamId ?? "N/A") + + @context.SteamID @context.Name - @(context.Author ?? "Unknown") - - @if (context.IsEnabled) - { - - } - else - { - - } - - @FormatBytes(context.SizeBytes) + @context.Author?.Name - - - Edit - - - Delete - + + + + @switch (modContext[context.Id].GetStatus) + { + case QueryStatus.Progress: + + break; + case QueryStatus.Failed: + + break; + default: + + break; + } + + + + + @switch (modContext[context.Id].UpdateStatus) + { + case QueryStatus.Progress: + + break; + case QueryStatus.Failed: + + break; + default: + + break; + } + + + + @context.Status + @context.ActualSize - - - Add Sample Mod - -} + + + + + + + + + + + Name + Path + Size + + + + @context.Name + @context.Path + @context.ActualSize + + + + + +} *@ @code { - private List? mods; + // private SteamMod[]? steamMods; + // private LocalMod[]? localMods; + + // private Dictionary modContext = new(); + + // async Task GetModInfos(SteamMod mod) + // { + // modContext[mod.Id].GetStatus = QueryStatus.Progress; + // mod.Status = ModStatus.Progress; + // try + // { + // await SteamService.GetModInfo(mod.SteamID); + // } + // catch (Exception e) + // { + // modContext[mod.Id].GetStatus = QueryStatus.Failed; + // Snackbar.Add($"{e.GetType().Name} : {e.Message}", Severity.Error); + // StateHasChanged(); + // await Task.Delay(5000); + // } + // finally + // { + // modContext[mod.Id].GetStatus = QueryStatus.Success; + // StateHasChanged(); + // } + // } + - protected override async Task OnInitializedAsync() - { - await LoadMods(); - } + // async Task DownloadMod(SteamMod mod, MouseEventArgs args) + // { + // modContext[mod.Id].UpdateStatus = QueryStatus.Progress; + // StateHasChanged(); + // try + // { + // modContext[mod.Id].UpdateProgress = 0; + // StateHasChanged(); - private async Task LoadMods() - { - try - { - mods = await ModService.GetAllModsAsync(); - } - catch (Exception ex) - { - Snackbar.Add($"Error loading mods: {ex.Message}", Severity.Error); - } - } + // await Task.Delay(2000); - private async Task AddSampleMod() - { - try - { - var sampleMod = new Mod - { - Id = Guid.NewGuid(), - Name = $"Sample Mod {DateTime.Now:HH:mm:ss}", - Author = "KAST Team", - Path = "/sample/path", - SteamId = Random.Shared.Next(100000, 999999).ToString(), - IsLocal = Random.Shared.NextDouble() > 0.5, - SizeBytes = Random.Shared.NextInt64(1024 * 1024, 1024 * 1024 * 500), // 1MB to 500MB - IsEnabled = true, - Version = "1.0.0", - CreatedAt = DateTime.UtcNow - }; + // for (int i = 0; i < 100; i++) + // { + // await Task.Delay(100); + // modContext[mod.Id].UpdateProgress = i; + // StateHasChanged(); + // } - await ModService.SaveModAsync(sampleMod); - await LoadMods(); - Snackbar.Add("Sample mod added successfully!", Severity.Success); - } - catch (Exception ex) - { - Snackbar.Add($"Error adding sample mod: {ex.Message}", Severity.Error); - } - } + // mod.Status = ModStatus.UpToDate; + // await ModsService.Save(); - private async Task DeleteMod(Guid modId) - { - try - { - if (await ModService.DeleteModAsync(modId)) - { - await LoadMods(); - Snackbar.Add("Mod deleted successfully!", Severity.Success); - } - else - { - Snackbar.Add("Failed to delete mod.", Severity.Error); - } - } - catch (Exception ex) - { - Snackbar.Add($"Error deleting mod: {ex.Message}", Severity.Error); - } - } + // } + // catch (Exception e) + // { + // modContext[mod.Id].UpdateStatus = QueryStatus.Failed; + // mod.Status = ModStatus.Error; + // await ModsService.Save(); + // Snackbar.Add($"{e.GetType().Name} : {e.Message}", Severity.Error); + // StateHasChanged(); + // await Task.Delay(5000); + // } + // finally + // { + // modContext[mod.Id].UpdateStatus = QueryStatus.Success; + // StateHasChanged(); + // } + // } - private static string FormatBytes(long bytes) - { - if (bytes == 0) return "0 B"; - - string[] suffixes = { "B", "KB", "MB", "GB", "TB" }; - int counter = 0; - decimal number = bytes; - - while (Math.Round(number / 1024) >= 1) - { - number /= 1024; - counter++; - } - - return $"{number:n1} {suffixes[counter]}"; - } + // protected override async Task OnInitializedAsync() + // { + // steamMods = await ModsService.GetSteamModsAsync(); + // localMods = await ModsService.GetLocalModsAsync(); + // modContext.Clear(); + // foreach (var mod in steamMods) + // { modContext.Add(mod.Id, new ModContext()); } + // } } \ No newline at end of file diff --git a/KAST/Components/Pages/Settings.razor b/KAST/Components/Pages/Settings.razor index 077e422..eac6ea1 100644 --- a/KAST/Components/Pages/Settings.razor +++ b/KAST/Components/Pages/Settings.razor @@ -45,6 +45,41 @@ disabled="@config.IsEnvLocked(nameof(KastSettings.ThemeAccent))" /> +
+ + + Auto (System) + Light + Dark + +
+ +
+ + + English + Français + Deutsch + Español + +
+ +
+ +
+ +
+ +
+ Save diff --git a/KAST/Program.cs b/KAST/Program.cs index 5d4c760..78bc912 100644 --- a/KAST/Program.cs +++ b/KAST/Program.cs @@ -31,8 +31,6 @@ public static async Task Main(string[] args) builder.Services.AddSingleton(); builder.Services.AddScoped(); builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); builder.Services.AddScoped(sp => { var env = sp.GetRequiredService(); // Get environment From 4005f82f49b20c35f7f663879dfa342d7960a64e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Sep 2025 09:01:19 +0000 Subject: [PATCH 5/6] Implement auto-save settings and sync theme mode with MainLayout Co-authored-by: Foxlider <19773387+Foxlider@users.noreply.github.com> --- KAST.Core/Services/ThemeService.cs | 64 +++++++++++++++++++++++++ KAST/Components/Layout/MainLayout.razor | 27 +++++++++-- KAST/Components/Pages/Settings.razor | 63 ++++++++++++++++++++---- KAST/Program.cs | 1 + 4 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 KAST.Core/Services/ThemeService.cs diff --git a/KAST.Core/Services/ThemeService.cs b/KAST.Core/Services/ThemeService.cs new file mode 100644 index 0000000..75a8d37 --- /dev/null +++ b/KAST.Core/Services/ThemeService.cs @@ -0,0 +1,64 @@ +using KAST.Data.Models; + +namespace KAST.Core.Services +{ + public class ThemeService + { + private readonly ConfigService _configService; + private bool _isDarkMode = false; + + public event Action? OnThemeChanged; + + public ThemeService(ConfigService configService) + { + _configService = configService; + } + + public bool IsDarkMode => _isDarkMode; + + public async Task InitializeAsync() + { + var config = await _configService.GetConfigAsync(); + await UpdateThemeFromSettings(config.ThemeMode); + } + + public async Task SetThemeModeAsync(string themeMode) + { + var config = await _configService.GetConfigAsync(); + config.ThemeMode = themeMode; + await _configService.UpdateConfigAsync(config); + await UpdateThemeFromSettings(themeMode); + } + + public async Task ToggleDarkModeAsync() + { + var newMode = _isDarkMode ? "light" : "dark"; + await SetThemeModeAsync(newMode); + } + + private async Task UpdateThemeFromSettings(string? themeMode) + { + bool newDarkMode = themeMode switch + { + "dark" => true, + "light" => false, + "auto" => await GetSystemPreferenceAsync(), + _ => false + }; + + if (_isDarkMode != newDarkMode) + { + _isDarkMode = newDarkMode; + OnThemeChanged?.Invoke(_isDarkMode); + } + } + + private async Task GetSystemPreferenceAsync() + { + // For now, default to light mode for "auto" + // In a real implementation, this could detect system preference + await Task.CompletedTask; + return false; + } + } +} \ No newline at end of file diff --git a/KAST/Components/Layout/MainLayout.razor b/KAST/Components/Layout/MainLayout.razor index ef85570..c81c9aa 100644 --- a/KAST/Components/Layout/MainLayout.razor +++ b/KAST/Components/Layout/MainLayout.razor @@ -1,4 +1,7 @@ @inherits LayoutComponentBase +@using KAST.Core.Services +@inject ThemeService ThemeService +@implements IDisposable @@ -36,7 +39,7 @@ private bool _isDarkMode = false; private MudTheme? _theme = null; - protected override void OnInitialized() + protected override async Task OnInitializedAsync() { _theme = new() { @@ -44,6 +47,13 @@ PaletteDark = _darkPalette, LayoutProperties = new LayoutProperties() { DefaultBorderRadius = "0" } }; + + // Initialize theme service and sync with its state + await ThemeService.InitializeAsync(); + _isDarkMode = ThemeService.IsDarkMode; + + // Subscribe to theme changes + ThemeService.OnThemeChanged += OnThemeChanged; } private void DrawerToggle() @@ -51,9 +61,20 @@ _drawerOpen = !_drawerOpen; } - private void DarkModeToggle() + private async Task DarkModeToggle() + { + await ThemeService.ToggleDarkModeAsync(); + } + + private void OnThemeChanged(bool isDarkMode) + { + _isDarkMode = isDarkMode; + InvokeAsync(StateHasChanged); + } + + public void Dispose() { - _isDarkMode = !_isDarkMode; + ThemeService.OnThemeChanged -= OnThemeChanged; } private readonly PaletteLight _lightPalette = new() {}; diff --git a/KAST/Components/Pages/Settings.razor b/KAST/Components/Pages/Settings.razor index eac6ea1..5b60793 100644 --- a/KAST/Components/Pages/Settings.razor +++ b/KAST/Components/Pages/Settings.razor @@ -4,16 +4,18 @@ @using KAST.Data.Models @using KAST.Core.Extensions @inject ConfigService ConfigService +@inject ThemeService ThemeService @inject ISnackbar Snackbar @inject IDialogService DialogService Settings Settings - +
@@ -42,12 +44,14 @@
Auto (System) @@ -59,6 +63,7 @@
English @@ -70,20 +75,18 @@
- - - Save - - +
@code { private KastSettings config = new(); @@ -93,10 +96,49 @@ config = await ConfigService.GetConfigAsync(); } - private async Task SaveConfig() + private async Task SaveConfigAsync() + { + try + { + await ConfigService.UpdateConfigAsync(config); + Snackbar.Add("Settings saved", Severity.Success); + } + catch (Exception ex) + { + Snackbar.Add($"Failed to save settings: {ex.Message}", Severity.Error); + } + } + + private async Task OnApiKeyChanged() + { + await SaveConfigAsync(); + } + + private async Task OnThemeAccentChanged() + { + await SaveConfigAsync(); + } + + private async Task OnThemeModeChanged() + { + // Update the theme service when theme mode changes + await ThemeService.SetThemeModeAsync(config.ThemeMode ?? "auto"); + Snackbar.Add("Theme updated", Severity.Success); + } + + private async Task OnLanguageChanged() + { + await SaveConfigAsync(); + } + + private async Task OnCheckForUpdatesChanged() + { + await SaveConfigAsync(); + } + + private async Task OnDebugLoggingChanged() { - await ConfigService.UpdateConfigAsync(config); - Snackbar.Add("Settings saved", Severity.Success); + await SaveConfigAsync(); } private async Task PickFolder(string propertyName) @@ -108,7 +150,10 @@ { var prop = typeof(KastSettings).GetProperty(propertyName); if (prop != null && prop.CanWrite) + { prop.SetValue(config, folderPath); + await SaveConfigAsync(); + } } } } diff --git a/KAST/Program.cs b/KAST/Program.cs index 78bc912..68e719c 100644 --- a/KAST/Program.cs +++ b/KAST/Program.cs @@ -31,6 +31,7 @@ public static async Task Main(string[] args) builder.Services.AddSingleton(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(sp => { var env = sp.GetRequiredService(); // Get environment From 0b3edf2b755f73df6b05d22a54264cb118966f56 Mon Sep 17 00:00:00 2001 From: Keelah Atom Date: Fri, 26 Sep 2025 11:17:16 +0200 Subject: [PATCH 6/6] Removed build artifact folders --- .../Arma3Profile | 33 ------------------- .../perf.cfg | 8 ----- .../server.cfg | 7 ---- .../Arma3Profile | 33 ------------------- .../perf.cfg | 8 ----- .../server.cfg | 7 ---- 6 files changed, 96 deletions(-) delete mode 100644 KAST/3b7d876b-a563-6e50-befb-76657ecf4671/Arma3Profile delete mode 100644 KAST/3b7d876b-a563-6e50-befb-76657ecf4671/perf.cfg delete mode 100644 KAST/3b7d876b-a563-6e50-befb-76657ecf4671/server.cfg delete mode 100644 KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/Arma3Profile delete mode 100644 KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/perf.cfg delete mode 100644 KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/server.cfg diff --git a/KAST/3b7d876b-a563-6e50-befb-76657ecf4671/Arma3Profile b/KAST/3b7d876b-a563-6e50-befb-76657ecf4671/Arma3Profile deleted file mode 100644 index a1f41c4..0000000 --- a/KAST/3b7d876b-a563-6e50-befb-76657ecf4671/Arma3Profile +++ /dev/null @@ -1,33 +0,0 @@ -class CustomDifficulty -{ - class Options - { - reducedDamage = 0; - groupIndicators = 0; - friendlyTags = 0; - enemyTags = 0; - detectedMines = 0; - commands = 0; - waypoints = 0; - tacticalPing = 0; - weaponInfo = 0; - stanceIndicator = 0; - staminaBar = 0; - weaponCrosshair = 0; - visionAid = 0; - thirdPersonView = 0; - cameraShake = 0; - scoreTable = 0; - deathMessages = 0; - vonID = 0; - mapContent = 0; - autoReport = 0; - multipleSaves = 0; - }; - aiLevelPreset = 3; -}; -class CustomAILevel -{ - skillAI = 0; - precisionAI = 0; -}; \ No newline at end of file diff --git a/KAST/3b7d876b-a563-6e50-befb-76657ecf4671/perf.cfg b/KAST/3b7d876b-a563-6e50-befb-76657ecf4671/perf.cfg deleted file mode 100644 index 47db563..0000000 --- a/KAST/3b7d876b-a563-6e50-befb-76657ecf4671/perf.cfg +++ /dev/null @@ -1,8 +0,0 @@ -MaxMsgSend = 128; -MaxSizeGuaranteed = 512; -MaxSizeNonguaranteed = 256; -MinBandwidth = 131072; -MaxBandwidth = 4294967295; -MinErrorToSend = 0.001; -MinErrorToSendNear = 0.01; -MaxCustomFileSize = 0; \ No newline at end of file diff --git a/KAST/3b7d876b-a563-6e50-befb-76657ecf4671/server.cfg b/KAST/3b7d876b-a563-6e50-befb-76657ecf4671/server.cfg deleted file mode 100644 index 7db2e4c..0000000 --- a/KAST/3b7d876b-a563-6e50-befb-76657ecf4671/server.cfg +++ /dev/null @@ -1,7 +0,0 @@ -Hostname = My Arma 3 Server; -Password = ; -PasswordAdmin = adminpass; -MaxPlayers = 40; -Persistent = True; -TimeStampFormat = short; -BattlEye = True; \ No newline at end of file diff --git a/KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/Arma3Profile b/KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/Arma3Profile deleted file mode 100644 index a1f41c4..0000000 --- a/KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/Arma3Profile +++ /dev/null @@ -1,33 +0,0 @@ -class CustomDifficulty -{ - class Options - { - reducedDamage = 0; - groupIndicators = 0; - friendlyTags = 0; - enemyTags = 0; - detectedMines = 0; - commands = 0; - waypoints = 0; - tacticalPing = 0; - weaponInfo = 0; - stanceIndicator = 0; - staminaBar = 0; - weaponCrosshair = 0; - visionAid = 0; - thirdPersonView = 0; - cameraShake = 0; - scoreTable = 0; - deathMessages = 0; - vonID = 0; - mapContent = 0; - autoReport = 0; - multipleSaves = 0; - }; - aiLevelPreset = 3; -}; -class CustomAILevel -{ - skillAI = 0; - precisionAI = 0; -}; \ No newline at end of file diff --git a/KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/perf.cfg b/KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/perf.cfg deleted file mode 100644 index 47db563..0000000 --- a/KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/perf.cfg +++ /dev/null @@ -1,8 +0,0 @@ -MaxMsgSend = 128; -MaxSizeGuaranteed = 512; -MaxSizeNonguaranteed = 256; -MinBandwidth = 131072; -MaxBandwidth = 4294967295; -MinErrorToSend = 0.001; -MinErrorToSendNear = 0.01; -MaxCustomFileSize = 0; \ No newline at end of file diff --git a/KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/server.cfg b/KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/server.cfg deleted file mode 100644 index 7db2e4c..0000000 --- a/KAST/c50e6d0f-cf1c-5357-ad9c-298407844f2d/server.cfg +++ /dev/null @@ -1,7 +0,0 @@ -Hostname = My Arma 3 Server; -Password = ; -PasswordAdmin = adminpass; -MaxPlayers = 40; -Persistent = True; -TimeStampFormat = short; -BattlEye = True; \ No newline at end of file