parent
fb0df46572
commit
b21efc63d0
13 changed files with 1405 additions and 108 deletions
|
|
@ -139,6 +139,14 @@ namespace LANCommander.Launcher.Data
|
|||
gr => gr.HasOne<Game>().WithMany().HasForeignKey("GameId")
|
||||
);
|
||||
|
||||
builder.Entity<Game>()
|
||||
.HasMany(g => g.Tools)
|
||||
.WithMany(t => t.Games)
|
||||
.UsingEntity<GameTool>(
|
||||
gt => gt.HasOne(x => x.Tool).WithMany(t => t.GameTools).HasForeignKey(x => x.ToolId).OnDelete(DeleteBehavior.Cascade),
|
||||
gt => gt.HasOne(x => x.Game).WithMany(g => g.GameTools).HasForeignKey(x => x.GameId).OnDelete(DeleteBehavior.Cascade),
|
||||
gt => gt.HasKey(x => new { x.GameId, x.ToolId }));
|
||||
|
||||
builder.Entity<Game>()
|
||||
.HasMany(g => g.DependentGames)
|
||||
.WithOne(g => g.BaseGame)
|
||||
|
|
|
|||
985
LANCommander.Launcher.Data/Migrations/20260627164452_AddPerGameToolInstallState.Designer.cs
generated
Normal file
985
LANCommander.Launcher.Data/Migrations/20260627164452_AddPerGameToolInstallState.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,985 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using LANCommander.Launcher.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LANCommander.Launcher.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
[Migration("20260627164452_AddPerGameToolInstallState")]
|
||||
partial class AddPerGameToolInstallState
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "9.0.9");
|
||||
|
||||
modelBuilder.Entity("CategoryGame", b =>
|
||||
{
|
||||
b.Property<Guid>("CategoriesId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("GamesId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("CategoriesId", "GamesId");
|
||||
|
||||
b.HasIndex("GamesId");
|
||||
|
||||
b.ToTable("CategoryGame");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CollectionGame", b =>
|
||||
{
|
||||
b.Property<Guid>("CollectionId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("GameId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("CollectionId", "GameId");
|
||||
|
||||
b.HasIndex("GameId");
|
||||
|
||||
b.ToTable("CollectionGame");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameDeveloper", b =>
|
||||
{
|
||||
b.Property<Guid>("DeveloperId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("GameId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("DeveloperId", "GameId");
|
||||
|
||||
b.HasIndex("GameId");
|
||||
|
||||
b.ToTable("GameDeveloper");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameGenre", b =>
|
||||
{
|
||||
b.Property<Guid>("GamesId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("GenresId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("GamesId", "GenresId");
|
||||
|
||||
b.HasIndex("GenresId");
|
||||
|
||||
b.ToTable("GameGenre");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GamePlatform", b =>
|
||||
{
|
||||
b.Property<Guid>("GamesId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("PlatformsId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("GamesId", "PlatformsId");
|
||||
|
||||
b.HasIndex("PlatformsId");
|
||||
|
||||
b.ToTable("GamePlatform");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GamePublisher", b =>
|
||||
{
|
||||
b.Property<Guid>("GameId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("PublisherId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("GameId", "PublisherId");
|
||||
|
||||
b.HasIndex("PublisherId");
|
||||
|
||||
b.ToTable("GamePublisher");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameRedistributable", b =>
|
||||
{
|
||||
b.Property<Guid>("GameId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("RedistributableId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("GameId", "RedistributableId");
|
||||
|
||||
b.HasIndex("RedistributableId");
|
||||
|
||||
b.ToTable("GameRedistributable");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameTag", b =>
|
||||
{
|
||||
b.Property<Guid>("GamesId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("TagsId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("GamesId", "TagsId");
|
||||
|
||||
b.HasIndex("TagsId");
|
||||
|
||||
b.ToTable("GameTag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Category", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ImportedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ParentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.ToTable("Categories");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Collection", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ImportedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Collections");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Company", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ImportedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Companies");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Engine", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ImportedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Engines");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Game", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid?>("BaseGameId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid?>("EngineId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ImportedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("InstallDirectory")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Installed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("InstalledOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("InstalledVersion")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LatestVersion")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("ReleasedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Singleplayer")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SortTitle")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("BaseGameId");
|
||||
|
||||
b.HasIndex("EngineId");
|
||||
|
||||
b.ToTable("Games");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.GameExternalId", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ExternalId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid?>("GameId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ImportedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Provider")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("GameId");
|
||||
|
||||
b.ToTable("GameExternalIds");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.GameTool", b =>
|
||||
{
|
||||
b.Property<Guid>("GameId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ToolId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("InstallDirectory")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Installed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("InstalledOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("InstalledVersion")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("GameId", "ToolId");
|
||||
|
||||
b.HasIndex("ToolId");
|
||||
|
||||
b.ToTable("GameTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Genre", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ImportedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Genres");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Library", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ImportedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Libraries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Media", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Crc32")
|
||||
.IsRequired()
|
||||
.HasMaxLength(8)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("FileId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid?>("GameId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ImportedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("MimeType")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SourceUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("GameId");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Media");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.MultiplayerMode", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid?>("GameId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ImportedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("MaxPlayers")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MinPlayers")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("NetworkProtocol")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Spectators")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("GameId");
|
||||
|
||||
b.ToTable("MultiplayerModes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Platform", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ImportedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Platforms");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.PlaySession", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("End")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid?>("GameId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ImportedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("Start")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("GameId");
|
||||
|
||||
b.ToTable("PlaySessions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Redistributable", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ImportedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Redistributables");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Tag", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ImportedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Tags");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Tool", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ImportedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LatestVersion")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Tools");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.User", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Alias")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ImportedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LibraryGame", b =>
|
||||
{
|
||||
b.Property<Guid>("GameId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("LibraryId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("GameId", "LibraryId");
|
||||
|
||||
b.HasIndex("LibraryId");
|
||||
|
||||
b.ToTable("LibraryGame");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CategoryGame", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Category", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("CategoriesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Game", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("GamesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CollectionGame", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Collection", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("CollectionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Game", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("GameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameDeveloper", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Company", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("DeveloperId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Game", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("GameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameGenre", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Game", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("GamesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Genre", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("GenresId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GamePlatform", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Game", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("GamesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Platform", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("PlatformsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GamePublisher", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Game", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("GameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Company", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("PublisherId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameRedistributable", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Game", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("GameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Redistributable", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RedistributableId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameTag", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Game", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("GamesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Tag", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TagsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Category", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Category", "Parent")
|
||||
.WithMany("Children")
|
||||
.HasForeignKey("ParentId");
|
||||
|
||||
b.Navigation("Parent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Game", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Game", "BaseGame")
|
||||
.WithMany("DependentGames")
|
||||
.HasForeignKey("BaseGameId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Engine", "Engine")
|
||||
.WithMany("Games")
|
||||
.HasForeignKey("EngineId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("BaseGame");
|
||||
|
||||
b.Navigation("Engine");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.GameExternalId", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Game", "Game")
|
||||
.WithMany("ExternalIds")
|
||||
.HasForeignKey("GameId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.Navigation("Game");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.GameTool", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Game", "Game")
|
||||
.WithMany("GameTools")
|
||||
.HasForeignKey("GameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Tool", "Tool")
|
||||
.WithMany("GameTools")
|
||||
.HasForeignKey("ToolId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Game");
|
||||
|
||||
b.Navigation("Tool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Library", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.User", "User")
|
||||
.WithOne("Library")
|
||||
.HasForeignKey("LANCommander.Launcher.Data.Models.Library", "UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Media", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Game", "Game")
|
||||
.WithMany("Media")
|
||||
.HasForeignKey("GameId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.User", "User")
|
||||
.WithOne("Avatar")
|
||||
.HasForeignKey("LANCommander.Launcher.Data.Models.Media", "UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.Navigation("Game");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.MultiplayerMode", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Game", "Game")
|
||||
.WithMany("MultiplayerModes")
|
||||
.HasForeignKey("GameId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.Navigation("Game");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.PlaySession", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Game", "Game")
|
||||
.WithMany("PlaySessions")
|
||||
.HasForeignKey("GameId");
|
||||
|
||||
b.Navigation("Game");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LibraryGame", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Game", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("GameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Library", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("LibraryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Category", b =>
|
||||
{
|
||||
b.Navigation("Children");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Engine", b =>
|
||||
{
|
||||
b.Navigation("Games");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Game", b =>
|
||||
{
|
||||
b.Navigation("DependentGames");
|
||||
|
||||
b.Navigation("ExternalIds");
|
||||
|
||||
b.Navigation("GameTools");
|
||||
|
||||
b.Navigation("Media");
|
||||
|
||||
b.Navigation("MultiplayerModes");
|
||||
|
||||
b.Navigation("PlaySessions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Tool", b =>
|
||||
{
|
||||
b.Navigation("GameTools");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.User", b =>
|
||||
{
|
||||
b.Navigation("Avatar");
|
||||
|
||||
b.Navigation("Library");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LANCommander.Launcher.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddPerGameToolInstallState : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_GameTool_Games_GamesId",
|
||||
table: "GameTool");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_GameTool_Tools_ToolsId",
|
||||
table: "GameTool");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "ToolsId",
|
||||
table: "GameTool",
|
||||
newName: "ToolId");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "GamesId",
|
||||
table: "GameTool",
|
||||
newName: "GameId");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_GameTool_ToolsId",
|
||||
table: "GameTool",
|
||||
newName: "IX_GameTool_ToolId");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "InstallDirectory",
|
||||
table: "GameTool",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "Installed",
|
||||
table: "GameTool",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "InstalledOn",
|
||||
table: "GameTool",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "InstalledVersion",
|
||||
table: "GameTool",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
// Tool install state was previously tracked globally on the Tools table. Migrate it onto
|
||||
// the join row for the game the tool was actually installed under (the game whose install
|
||||
// directory matches the tool's recorded directory) before the old columns are dropped.
|
||||
migrationBuilder.Sql(@"
|
||||
UPDATE GameTool
|
||||
SET
|
||||
Installed = 1,
|
||||
InstallDirectory = (SELECT t.InstallDirectory FROM Tools t WHERE t.Id = GameTool.ToolId),
|
||||
InstalledVersion = (SELECT t.InstalledVersion FROM Tools t WHERE t.Id = GameTool.ToolId),
|
||||
InstalledOn = (SELECT t.InstalledOn FROM Tools t WHERE t.Id = GameTool.ToolId)
|
||||
WHERE EXISTS (
|
||||
SELECT 1
|
||||
FROM Tools t
|
||||
JOIN Games g ON g.Id = GameTool.GameId
|
||||
WHERE t.Id = GameTool.ToolId
|
||||
AND t.Installed = 1
|
||||
AND t.InstallDirectory IS NOT NULL
|
||||
AND t.InstallDirectory = g.InstallDirectory
|
||||
);");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "InstallDirectory",
|
||||
table: "Tools");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Installed",
|
||||
table: "Tools");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "InstalledOn",
|
||||
table: "Tools");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "InstalledVersion",
|
||||
table: "Tools");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_GameTool_Games_GameId",
|
||||
table: "GameTool",
|
||||
column: "GameId",
|
||||
principalTable: "Games",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_GameTool_Tools_ToolId",
|
||||
table: "GameTool",
|
||||
column: "ToolId",
|
||||
principalTable: "Tools",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_GameTool_Games_GameId",
|
||||
table: "GameTool");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_GameTool_Tools_ToolId",
|
||||
table: "GameTool");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "InstallDirectory",
|
||||
table: "GameTool");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Installed",
|
||||
table: "GameTool");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "InstalledOn",
|
||||
table: "GameTool");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "InstalledVersion",
|
||||
table: "GameTool");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "ToolId",
|
||||
table: "GameTool",
|
||||
newName: "ToolsId");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "GameId",
|
||||
table: "GameTool",
|
||||
newName: "GamesId");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_GameTool_ToolId",
|
||||
table: "GameTool",
|
||||
newName: "IX_GameTool_ToolsId");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "InstallDirectory",
|
||||
table: "Tools",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "Installed",
|
||||
table: "Tools",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "InstalledOn",
|
||||
table: "Tools",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "InstalledVersion",
|
||||
table: "Tools",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_GameTool_Games_GamesId",
|
||||
table: "GameTool",
|
||||
column: "GamesId",
|
||||
principalTable: "Games",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_GameTool_Tools_ToolsId",
|
||||
table: "GameTool",
|
||||
column: "ToolsId",
|
||||
principalTable: "Tools",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -137,21 +137,6 @@ namespace LANCommander.Launcher.Data.Migrations
|
|||
b.ToTable("GameTag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameTool", b =>
|
||||
{
|
||||
b.Property<Guid>("GamesId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ToolsId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("GamesId", "ToolsId");
|
||||
|
||||
b.HasIndex("ToolsId");
|
||||
|
||||
b.ToTable("GameTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Category", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
|
|
@ -355,6 +340,33 @@ namespace LANCommander.Launcher.Data.Migrations
|
|||
b.ToTable("GameExternalIds");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.GameTool", b =>
|
||||
{
|
||||
b.Property<Guid>("GameId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ToolId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("InstallDirectory")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Installed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("InstalledOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("InstalledVersion")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("GameId", "ToolId");
|
||||
|
||||
b.HasIndex("ToolId");
|
||||
|
||||
b.ToTable("GameTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Genre", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
|
|
@ -632,18 +644,6 @@ namespace LANCommander.Launcher.Data.Migrations
|
|||
b.Property<DateTime>("ImportedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("InstallDirectory")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Installed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("InstalledOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("InstalledVersion")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LatestVersion")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
|
@ -823,21 +823,6 @@ namespace LANCommander.Launcher.Data.Migrations
|
|||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameTool", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Game", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("GamesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Tool", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("ToolsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Category", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Category", "Parent")
|
||||
|
|
@ -874,6 +859,25 @@ namespace LANCommander.Launcher.Data.Migrations
|
|||
b.Navigation("Game");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.GameTool", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Game", "Game")
|
||||
.WithMany("GameTools")
|
||||
.HasForeignKey("GameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Tool", "Tool")
|
||||
.WithMany("GameTools")
|
||||
.HasForeignKey("ToolId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Game");
|
||||
|
||||
b.Navigation("Tool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Library", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.User", "User")
|
||||
|
|
@ -952,6 +956,8 @@ namespace LANCommander.Launcher.Data.Migrations
|
|||
|
||||
b.Navigation("ExternalIds");
|
||||
|
||||
b.Navigation("GameTools");
|
||||
|
||||
b.Navigation("Media");
|
||||
|
||||
b.Navigation("MultiplayerModes");
|
||||
|
|
@ -959,6 +965,11 @@ namespace LANCommander.Launcher.Data.Migrations
|
|||
b.Navigation("PlaySessions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.Tool", b =>
|
||||
{
|
||||
b.Navigation("GameTools");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.User", b =>
|
||||
{
|
||||
b.Navigation("Avatar");
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ namespace LANCommander.Launcher.Data.Models
|
|||
public virtual ICollection<Platform>? Platforms { get; set; } = new List<Platform>();
|
||||
public virtual ICollection<Redistributable>? Redistributables { get; set; } = new List<Redistributable>();
|
||||
public virtual ICollection<Tool>? Tools { get; set; } = new List<Tool>();
|
||||
public virtual ICollection<GameTool> GameTools { get; set; } = new List<GameTool>();
|
||||
public virtual ICollection<Media>? Media { get; set; } = new List<Media>();
|
||||
public virtual ICollection<Collection> Collections { get; set; } = new List<Collection>();
|
||||
public virtual ICollection<Game> DependentGames { get; set; } = new List<Game>();
|
||||
|
|
|
|||
25
LANCommander.Launcher.Data/Models/GameTool.cs
Normal file
25
LANCommander.Launcher.Data/Models/GameTool.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace LANCommander.Launcher.Data.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Join entity between <see cref="Game"/> and <see cref="Tool"/> that tracks the install
|
||||
/// state of a tool for a specific game. Because a tool can be shared by multiple games and is
|
||||
/// installed into each game's own directory, the install state must be tracked per game rather
|
||||
/// than on the tool itself.
|
||||
/// </summary>
|
||||
[Table("GameTool")]
|
||||
public class GameTool
|
||||
{
|
||||
public Guid GameId { get; set; }
|
||||
public virtual Game Game { get; set; }
|
||||
|
||||
public Guid ToolId { get; set; }
|
||||
public virtual Tool Tool { get; set; }
|
||||
|
||||
public bool Installed { get; set; }
|
||||
public string? InstallDirectory { get; set; }
|
||||
public string? InstalledVersion { get; set; }
|
||||
public DateTime? InstalledOn { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -8,13 +8,10 @@ namespace LANCommander.Launcher.Data.Models
|
|||
public string Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
|
||||
public bool Installed { get; set; }
|
||||
public string? InstallDirectory { get; set; }
|
||||
public string? InstalledVersion { get; set; }
|
||||
public DateTime? InstalledOn { get; set; }
|
||||
|
||||
public string? LatestVersion { get; set; }
|
||||
|
||||
|
||||
public virtual ICollection<Game>? Games { get; set; } = new List<Game>();
|
||||
public virtual ICollection<GameTool> GameTools { get; set; } = new List<GameTool>();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,29 +70,23 @@ namespace LANCommander.Launcher.Services
|
|||
}
|
||||
}
|
||||
|
||||
// Uninstall any tools that were installed alongside this game. Tools are
|
||||
// installed into the game's directory, so this must happen before the
|
||||
// install directory is cleared in ClearGameState.
|
||||
var installedTools = await toolService
|
||||
.Query(t => t.Installed && t.Games.Any(g => g.Id == game.Id))
|
||||
.ToListAsync();
|
||||
// Uninstall any tools that were installed for this game. Tools are installed
|
||||
// into the game's own directory and tracked per game, so uninstalling this
|
||||
// game only removes its copy and leaves the tool intact for other games that
|
||||
// share it. This must run before ClearGameState clears the install directory.
|
||||
var installedTools = await toolService.GetInstalledToolsForGameAsync(game.Id);
|
||||
|
||||
foreach (var tool in installedTools)
|
||||
foreach (var gameTool in installedTools)
|
||||
{
|
||||
try
|
||||
{
|
||||
await toolClient.UninstallAsync(game.InstallDirectory, tool.Id);
|
||||
await toolClient.UninstallAsync(game.InstallDirectory, gameTool.ToolId);
|
||||
|
||||
tool.Installed = false;
|
||||
tool.InstallDirectory = null;
|
||||
tool.InstalledVersion = null;
|
||||
tool.InstalledOn = null;
|
||||
|
||||
await toolService.UpdateAsync(tool);
|
||||
await toolService.SetToolUninstalledAsync(game.Id, gameTool.ToolId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger?.LogError(ex, "Could not uninstall tool {ToolId} from game {GameId}", tool.Id, game.Id);
|
||||
Logger?.LogError(ex, "Could not uninstall tool {ToolId} from game {GameId}", gameTool.ToolId, game.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ namespace LANCommander.Launcher.Services.Import.Importers;
|
|||
|
||||
public class ToolImporter(
|
||||
ToolService toolService,
|
||||
LibraryService libraryService,
|
||||
ILogger<ToolImporter> logger) : BaseImporter<Tool>
|
||||
{
|
||||
public override async Task<ImportItemInfo<Tool>> GetImportInfoAsync(Tool record, BaseManifest manifest) =>
|
||||
|
|
@ -77,9 +76,14 @@ public class ToolImporter(
|
|||
|
||||
await toolService.UpdateAsync(existing);
|
||||
await UpdateRelationships(importItemInfo.Record);
|
||||
|
||||
if (await libraryService.IsInstalledAsync(existing.Id) && existing.LatestVersion == existing.InstalledVersion)
|
||||
await ManifestHelper.WriteAsync(importItemInfo.Record, existing.InstallDirectory);
|
||||
|
||||
// Refresh the on-disk manifest for every game this tool is installed for and up to date
|
||||
// on. Install state is tracked per game because a tool can be shared by several games.
|
||||
foreach (var gameTool in await toolService.GetInstalledGameToolsAsync(existing.Id))
|
||||
{
|
||||
if (existing.LatestVersion == gameTool.InstalledVersion && !string.IsNullOrEmpty(gameTool.InstallDirectory))
|
||||
await ManifestHelper.WriteAsync(importItemInfo.Record, gameTool.InstallDirectory);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -600,28 +600,22 @@ namespace LANCommander.Launcher.Services
|
|||
await _gameClient.RestoreFilesAsync(localGame.InstallDirectory, localGame.Id, uninstallResult.FileList, installResult.FileList);
|
||||
|
||||
// Uninstall any tools that were deselected. Selected tools are installed
|
||||
// via their own queue items, so we only handle removal here.
|
||||
// via their own queue items, so we only handle removal here. Tool install
|
||||
// state is tracked per game, so this only affects this game's copy.
|
||||
var selectedToolIds = queueItem.ToolIds ?? [];
|
||||
var toolsToRemove = await _toolService
|
||||
.Query(t => t.Installed && t.Games.Any(g => g.Id == localGame.Id))
|
||||
.ToListAsync();
|
||||
var installedTools = await _toolService.GetInstalledToolsForGameAsync(localGame.Id);
|
||||
|
||||
foreach (var localTool in toolsToRemove.Where(t => !selectedToolIds.Contains(t.Id)))
|
||||
foreach (var gameTool in installedTools.Where(gt => !selectedToolIds.Contains(gt.ToolId)))
|
||||
{
|
||||
try
|
||||
{
|
||||
await _toolClient.UninstallAsync(localGame.InstallDirectory, localTool.Id);
|
||||
await _toolClient.UninstallAsync(localGame.InstallDirectory, gameTool.ToolId);
|
||||
|
||||
localTool.Installed = false;
|
||||
localTool.InstallDirectory = null;
|
||||
localTool.InstalledVersion = null;
|
||||
localTool.InstalledOn = null;
|
||||
|
||||
await _toolService.UpdateAsync(localTool);
|
||||
await _toolService.SetToolUninstalledAsync(localGame.Id, gameTool.ToolId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger?.LogError(ex, "Could not uninstall tool {ToolId} from game {GameId}", localTool.Id, localGame.Id);
|
||||
Logger?.LogError(ex, "Could not uninstall tool {ToolId} from game {GameId}", gameTool.ToolId, localGame.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -687,7 +681,10 @@ namespace LANCommander.Launcher.Services
|
|||
return;
|
||||
}
|
||||
|
||||
if (localTool.Installed)
|
||||
var alreadyInstalled = queueItem.DependsOnId.HasValue
|
||||
&& await _toolService.IsToolInstalledForGameAsync(queueItem.DependsOnId.Value, localTool.Id);
|
||||
|
||||
if (alreadyInstalled)
|
||||
{
|
||||
// Modify — currently no-op
|
||||
}
|
||||
|
|
@ -871,6 +868,19 @@ namespace LANCommander.Launcher.Services
|
|||
|
||||
// Update manifest and scripts on disk
|
||||
await _gameClient.UpdateGameInstallationAsync(localGame.InstallDirectory, remoteGame);
|
||||
|
||||
// Bug #1 convergence: after applying all updates and re-importing, the installed
|
||||
// version may still trail the server's resolved latest version (the last applied
|
||||
// archive's version string is not guaranteed to equal the canonical latest version).
|
||||
// Converge explicitly so the game leaves the "update available" state.
|
||||
if (!string.IsNullOrWhiteSpace(localGame.LatestVersion))
|
||||
{
|
||||
localGame.InstalledVersion = localGame.LatestVersion;
|
||||
await _gameService.UpdateAsync(localGame);
|
||||
|
||||
Logger?.LogInformation("[InstallQueue] Update(Game): Converged InstalledVersion to LatestVersion {LatestVersion} for {Title}",
|
||||
localGame.LatestVersion, currentItem.Title);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -937,6 +947,8 @@ namespace LANCommander.Launcher.Services
|
|||
currentItem.Status = InstallStatus.Downloading;
|
||||
OnQueueChanged?.Invoke();
|
||||
|
||||
string toolInstallDirectory = null;
|
||||
|
||||
try
|
||||
{
|
||||
var planItem = new InstallPlanItem
|
||||
|
|
@ -950,7 +962,7 @@ namespace LANCommander.Launcher.Services
|
|||
|
||||
var result = await _toolClient.ExecuteInstallPlanItemAsync(planItem, currentItem.CancellationToken.Token);
|
||||
|
||||
UpdateToolState(currentItem, localTool, result.InstallDirectory);
|
||||
toolInstallDirectory = result.InstallDirectory;
|
||||
}
|
||||
catch (InstallCanceledException ex)
|
||||
{
|
||||
|
|
@ -978,7 +990,12 @@ namespace LANCommander.Launcher.Services
|
|||
|
||||
try
|
||||
{
|
||||
await _toolService.UpdateAsync(localTool);
|
||||
// Install state is tracked per game because a tool can be shared by several
|
||||
// games and is installed into each game's own directory.
|
||||
if (currentItem.DependsOnId.HasValue)
|
||||
await _toolService.SetToolInstalledAsync(currentItem.DependsOnId.Value, localTool.Id, toolInstallDirectory, currentItem.Version);
|
||||
else
|
||||
Logger?.LogWarning("Tool {ToolName} ({ToolId}) was installed without an associated game; install state not recorded", localTool.Name, localTool.Id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -1200,14 +1217,6 @@ namespace LANCommander.Launcher.Services
|
|||
}
|
||||
}
|
||||
|
||||
private static void UpdateToolState(InstallQueueTool currentItem, Tool localTool, string installDirectory)
|
||||
{
|
||||
localTool.InstallDirectory = installDirectory;
|
||||
localTool.Installed = true;
|
||||
localTool.InstalledVersion = currentItem.Version;
|
||||
localTool.InstalledOn ??= DateTime.Now;
|
||||
}
|
||||
|
||||
public async Task Move(IInstallQueueItem currentItem, Game localGame, SDK.Models.Game remoteGame)
|
||||
{
|
||||
using (var operation = Logger.BeginOperation("Moving game {GameTitle} ({GameId}) to {Destination}", localGame.Title, localGame.Id, currentItem.InstallDirectory))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using LANCommander.Launcher.Data;
|
||||
using LANCommander.Launcher.Data;
|
||||
using LANCommander.Launcher.Data.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace LANCommander.Launcher.Services
|
||||
|
|
@ -8,5 +9,75 @@ namespace LANCommander.Launcher.Services
|
|||
ILogger<ToolService> logger,
|
||||
DatabaseContext dbContext) : BaseDatabaseService<Tool>(dbContext, logger)
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the install-state join rows for every tool currently installed for the given game.
|
||||
/// The related <see cref="Tool"/> is eagerly loaded.
|
||||
/// </summary>
|
||||
public async Task<List<GameTool>> GetInstalledToolsForGameAsync(Guid gameId)
|
||||
{
|
||||
return await Context.Set<GameTool>()
|
||||
.Include(gt => gt.Tool)
|
||||
.Where(gt => gt.GameId == gameId && gt.Installed)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the install-state join rows for every game the given tool is installed for.
|
||||
/// </summary>
|
||||
public async Task<List<GameTool>> GetInstalledGameToolsAsync(Guid toolId)
|
||||
{
|
||||
return await Context.Set<GameTool>()
|
||||
.Where(gt => gt.ToolId == toolId && gt.Installed)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> IsToolInstalledForGameAsync(Guid gameId, Guid toolId)
|
||||
{
|
||||
return await Context.Set<GameTool>()
|
||||
.AnyAsync(gt => gt.GameId == gameId && gt.ToolId == toolId && gt.Installed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Records that a tool has been installed for a specific game. Creates the join row if the
|
||||
/// game/tool association does not yet exist locally.
|
||||
/// </summary>
|
||||
public async Task SetToolInstalledAsync(Guid gameId, Guid toolId, string installDirectory, string version)
|
||||
{
|
||||
var gameTool = await Context.Set<GameTool>()
|
||||
.FirstOrDefaultAsync(gt => gt.GameId == gameId && gt.ToolId == toolId);
|
||||
|
||||
if (gameTool == null)
|
||||
{
|
||||
gameTool = new GameTool { GameId = gameId, ToolId = toolId };
|
||||
await Context.Set<GameTool>().AddAsync(gameTool);
|
||||
}
|
||||
|
||||
gameTool.Installed = true;
|
||||
gameTool.InstallDirectory = installDirectory;
|
||||
gameTool.InstalledVersion = version;
|
||||
gameTool.InstalledOn ??= DateTime.Now;
|
||||
|
||||
await Context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the install state for a tool on a specific game without removing the game/tool
|
||||
/// association. Other games keep their own install state.
|
||||
/// </summary>
|
||||
public async Task SetToolUninstalledAsync(Guid gameId, Guid toolId)
|
||||
{
|
||||
var gameTool = await Context.Set<GameTool>()
|
||||
.FirstOrDefaultAsync(gt => gt.GameId == gameId && gt.ToolId == toolId);
|
||||
|
||||
if (gameTool == null)
|
||||
return;
|
||||
|
||||
gameTool.Installed = false;
|
||||
gameTool.InstallDirectory = null;
|
||||
gameTool.InstalledVersion = null;
|
||||
gameTool.InstalledOn = null;
|
||||
|
||||
await Context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1062,7 +1062,7 @@ public partial class GameActionBarViewModel : ViewModelBase, IDisposable
|
|||
|
||||
var localGame = await dbContext.Set<Data.Models.Game>()
|
||||
.Include(g => g.DependentGames)
|
||||
.Include(g => g.Tools)
|
||||
.Include(g => g.GameTools)
|
||||
.FirstOrDefaultAsync(g => g.Id == GameId);
|
||||
|
||||
if (localGame == null)
|
||||
|
|
@ -1099,11 +1099,11 @@ public partial class GameActionBarViewModel : ViewModelBase, IDisposable
|
|||
.Where(a => a.Installed)
|
||||
.Select(a => a.Id));
|
||||
|
||||
// Build set of currently installed tool IDs
|
||||
// Build set of currently installed tool IDs (tracked per game)
|
||||
var installedToolIds = new HashSet<Guid>(
|
||||
(localGame.Tools ?? [])
|
||||
.Where(t => t.Installed)
|
||||
.Select(t => t.Id));
|
||||
(localGame.GameTools ?? [])
|
||||
.Where(gt => gt.Installed)
|
||||
.Select(gt => gt.ToolId));
|
||||
|
||||
// ── Build options VM ───────────────────────────────────────────────
|
||||
var optionsVm = new InstallOptionsViewModel();
|
||||
|
|
|
|||
|
|
@ -523,8 +523,8 @@ public partial class GameDetailViewModel : ViewModelBase
|
|||
{
|
||||
foreach (var tool in tools)
|
||||
{
|
||||
var localTool = await toolService.GetAsync(tool.Id);
|
||||
Tools.Add(new ToolItemViewModel(tool, localTool));
|
||||
var isInstalled = await toolService.IsToolInstalledForGameAsync(game.Id, tool.Id);
|
||||
Tools.Add(new ToolItemViewModel(tool, isInstalled));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -559,17 +559,10 @@ public partial class ToolItemViewModel : ViewModelBase
|
|||
public string Name { get; }
|
||||
public bool IsInstalled { get; }
|
||||
|
||||
public ToolItemViewModel(SDK.Models.Tool tool, Data.Models.Tool? localTool)
|
||||
public ToolItemViewModel(SDK.Models.Tool tool, bool isInstalled)
|
||||
{
|
||||
Id = tool.Id;
|
||||
Name = tool.Name ?? "Unknown Tool";
|
||||
IsInstalled = localTool?.Installed ?? false;
|
||||
}
|
||||
|
||||
public ToolItemViewModel(Data.Models.Tool tool)
|
||||
{
|
||||
Id = tool.Id;
|
||||
Name = tool.Name ?? "Unknown Tool";
|
||||
IsInstalled = tool.Installed;
|
||||
IsInstalled = isInstalled;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue