From abecd9f2f25a6670c372b9f2e23e0250b37bb0e8 Mon Sep 17 00:00:00 2001 From: Pat Hartl Date: Mon, 16 Jan 2023 23:45:46 -0600 Subject: [PATCH] Added sort ordering to actions and fixed install process not updating game from live manifest --- .../InstallController.cs | 14 +- .../LANCommanderLibraryPlugin.cs | 185 ++- LANCommander.SDK/Models/Action.cs | 1 + LANCommander.SDK/Models/GameManifest.cs | 1 + LANCommander/Components/ActionEditor.razor | 60 +- LANCommander/Data/Models/Action.cs | 1 + LANCommander/Extensions/ListExtensions.cs | 36 + ...30117002451_AddActionSortOrder.Designer.cs | 1102 +++++++++++++++++ .../20230117002451_AddActionSortOrder.cs | 26 + .../DatabaseContextModelSnapshot.cs | 3 + LANCommander/Services/GameService.cs | 3 +- LANCommander/Views/Games/Add.cshtml | 2 +- LANCommander/Views/Games/Edit.cshtml | 2 +- 13 files changed, 1325 insertions(+), 111 deletions(-) create mode 100644 LANCommander/Extensions/ListExtensions.cs create mode 100644 LANCommander/Migrations/20230117002451_AddActionSortOrder.Designer.cs create mode 100644 LANCommander/Migrations/20230117002451_AddActionSortOrder.cs diff --git a/LANCommander.Playnite.Extension/InstallController.cs b/LANCommander.Playnite.Extension/InstallController.cs index da2b5ea..6841b3d 100644 --- a/LANCommander.Playnite.Extension/InstallController.cs +++ b/LANCommander.Playnite.Extension/InstallController.cs @@ -15,6 +15,7 @@ using ICSharpCode.SharpZipLib.Core; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; using LANCommander.SDK.Models; +using System.Collections.ObjectModel; namespace LANCommander.PlaynitePlugin { @@ -37,6 +38,7 @@ namespace LANCommander.PlaynitePlugin var gameId = Guid.Parse(Game.GameId); var game = Plugin.LANCommander.GetGame(gameId); + var manifest = Plugin.LANCommander.GetGameManifest(gameId); var tempFile = Download(game); @@ -49,7 +51,7 @@ namespace LANCommander.PlaynitePlugin PlayniteGame.InstallDirectory = installDirectory; - File.WriteAllText(Path.Combine(installDirectory, "_manifest.yml"), GetManifest(gameId)); + WriteManifest(manifest, installDirectory); SaveScript(game, installDirectory, ScriptType.Install); SaveScript(game, installDirectory, ScriptType.Uninstall); @@ -62,9 +64,9 @@ namespace LANCommander.PlaynitePlugin } catch { } - InvokeOnInstalled(new GameInstalledEventArgs(installInfo)); + Plugin.UpdateGame(manifest, gameId); - Plugin.UpdateGamesFromManifest(); + InvokeOnInstalled(new GameInstalledEventArgs(installInfo)); } private string Download(LANCommander.SDK.Models.Game game) @@ -164,17 +166,15 @@ namespace LANCommander.PlaynitePlugin return destination; } - private string GetManifest(Guid gameId) + private void WriteManifest(SDK.GameManifest manifest, string installDirectory) { - var manifest = Plugin.LANCommander.GetGameManifest(gameId); - var serializer = new SerializerBuilder() .WithNamingConvention(PascalCaseNamingConvention.Instance) .Build(); var yaml = serializer.Serialize(manifest); - return yaml; + File.WriteAllText(Path.Combine(installDirectory, "_manifest.yml"), yaml); } private void SaveScript(LANCommander.SDK.Models.Game game, string installationDirectory, ScriptType type) diff --git a/LANCommander.Playnite.Extension/LANCommanderLibraryPlugin.cs b/LANCommander.Playnite.Extension/LANCommanderLibraryPlugin.cs index 0d682a7..5fed962 100644 --- a/LANCommander.Playnite.Extension/LANCommanderLibraryPlugin.cs +++ b/LANCommander.Playnite.Extension/LANCommanderLibraryPlugin.cs @@ -5,6 +5,7 @@ using Playnite.SDK.Models; using Playnite.SDK.Plugins; using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Net.NetworkInformation; @@ -94,7 +95,7 @@ namespace LANCommander.PlaynitePlugin ReleaseDate = new ReleaseDate(manifest.ReleasedOn), //Version = game.Archives.OrderByDescending(a => a.CreatedOn).FirstOrDefault().Version, Icon = new MetadataFile(iconUri.ToString()), - GameActions = game.Actions.Select(a => new PN.SDK.Models.GameAction() + GameActions = game.Actions.OrderBy(a => a.SortOrder).Select(a => new PN.SDK.Models.GameAction() { Name = a.Name, Arguments = a.Arguments, @@ -139,7 +140,25 @@ namespace LANCommander.PlaynitePlugin metadata.Features.Add(new MetadataNameProperty($"Online Multiplayer {manifest.OnlineMultiplayer.GetPlayerCount()}".Trim())); gameMetadata.Add(metadata); + + if (existingGame != null) + { + existingGame.GameActions.Clear(); + + foreach (var action in game.Actions) + { + existingGame.GameActions.Add(new PN.SDK.Models.GameAction() + { + Name = action.Name, + Arguments = action.Arguments, + Path = action.Path, + WorkingDir = action.WorkingDirectory, + IsPlayAction = action.PrimaryAction + }); + } + } }; + } catch (Exception ex) { @@ -272,113 +291,83 @@ namespace LANCommander.PlaynitePlugin return window; } - public void UpdateGamesFromManifest() + public void UpdateGame(SDK.GameManifest manifest, Guid gameId) { - var games = PlayniteApi.Database.Games; + var game = PlayniteApi.Database.Games.First(g => g.GameId == gameId.ToString()); - foreach (var game in games.Where(g => g.PluginId == Id && g.IsInstalled)) + if (game.GameActions == null) + game.GameActions = new ObservableCollection(); + else + game.GameActions.Clear(); + + foreach (var action in manifest.Actions.OrderBy(a => a.SortOrder)) { - if (!Directory.Exists(game.InstallDirectory)) - continue; + bool isFirstAction = !manifest.Actions.Any(a => a.IsPrimaryAction) && manifest.Actions.First().Name == action.Name; - var manifestPath = Path.Combine(game.InstallDirectory, "_manifest.yml"); - - if (File.Exists(manifestPath)) + game.GameActions.Add(new PN.SDK.Models.GameAction() { - try + Name = action.Name, + Arguments = action.Arguments, + Path = PlayniteApi.ExpandGameVariables(game, action.Path?.Replace('/', Path.DirectorySeparatorChar)), + WorkingDir = action.WorkingDirectory?.Replace('/', Path.DirectorySeparatorChar) ?? game.InstallDirectory, + IsPlayAction = action.IsPrimaryAction || isFirstAction + }); + } + + #region Features + var singlePlayerFeature = PlayniteApi.Database.Features.FirstOrDefault(f => f.Name == "Single Player"); + + if (manifest.LanMultiplayer != null) + { + var multiplayerInfo = manifest.LanMultiplayer; + + string playerCount = multiplayerInfo.MinPlayers == multiplayerInfo.MaxPlayers ? $"({multiplayerInfo.MinPlayers} players)" : $"({multiplayerInfo.MinPlayers} - {multiplayerInfo.MaxPlayers} players)"; + string featureName = $"LAN Multiplayer {playerCount}"; + + if (PlayniteApi.Database.Features.Any(f => f.Name == featureName)) + { + game.Features.Add(PlayniteApi.Database.Features.FirstOrDefault(f => f.Name == featureName)); + } + else + { + PlayniteApi.Database.Features.Add(new GameFeature() { - var manifestContents = File.ReadAllText(manifestPath); - var deserializer = new DeserializerBuilder() - .IgnoreUnmatchedProperties() - .WithNamingConvention(PascalCaseNamingConvention.Instance) - .Build(); + Name = featureName + }); - var manifest = deserializer.Deserialize(manifestContents); - - #region Actions - if (game.GameActions == null) - game.GameActions = new System.Collections.ObjectModel.ObservableCollection(); - - foreach (var action in manifest.Actions) - { - bool isFirstAction = !manifest.Actions.Any(a => a.IsPrimaryAction) && manifest.Actions.First().Name == action.Name; - - foreach (var existingAction in game.GameActions) - if (action.Name == existingAction.Name) - game.GameActions.Remove(existingAction); - - game.GameActions.AddMissing(new PN.SDK.Models.GameAction() - { - Name = action.Name, - Arguments = action.Arguments, - Path = PlayniteApi.ExpandGameVariables(game, action.Path?.Replace('/', Path.DirectorySeparatorChar)), - WorkingDir = action.WorkingDirectory?.Replace('/', Path.DirectorySeparatorChar) ?? game.InstallDirectory, - IsPlayAction = action.IsPrimaryAction || isFirstAction - }); - } - #endregion - - #region Features - var singlePlayerFeature = PlayniteApi.Database.Features.FirstOrDefault(f => f.Name == "Single Player"); - - if (manifest.LanMultiplayer != null) - { - var multiplayerInfo = manifest.LanMultiplayer; - - string playerCount = multiplayerInfo.MinPlayers == multiplayerInfo.MaxPlayers ? $"({multiplayerInfo.MinPlayers} players)" : $"({multiplayerInfo.MinPlayers} - {multiplayerInfo.MaxPlayers} players)"; - string featureName = $"LAN Multiplayer {playerCount}"; - - if (PlayniteApi.Database.Features.Any(f => f.Name == featureName)) - { - game.Features.Add(PlayniteApi.Database.Features.FirstOrDefault(f => f.Name == featureName)); - } - else - { - PlayniteApi.Database.Features.Add(new PN.SDK.Models.GameFeature() - { - Name = featureName - }); - - game.Features.Add(new PN.SDK.Models.GameFeature() - { - Name = $"LAN Multiplayer {playerCount}" - }); - } - } - - if (manifest.LocalMultiplayer != null) - { - var multiplayerInfo = manifest.LocalMultiplayer; - - string playerCount = multiplayerInfo.MinPlayers == multiplayerInfo.MaxPlayers ? $"({multiplayerInfo.MinPlayers} players)" : $"({multiplayerInfo.MinPlayers} - {multiplayerInfo.MaxPlayers} players)"; - - game.Features.Add(new PN.SDK.Models.GameFeature() - { - Name = $"Local Multiplayer {playerCount}" - }); - } - - if (manifest.OnlineMultiplayer != null) - { - var multiplayerInfo = manifest.OnlineMultiplayer; - - string playerCount = multiplayerInfo.MinPlayers == multiplayerInfo.MaxPlayers ? $"({multiplayerInfo.MinPlayers} players)" : $"({multiplayerInfo.MinPlayers} - {multiplayerInfo.MaxPlayers} players)"; - - game.Features.Add(new PN.SDK.Models.GameFeature() - { - Name = $"Online Multiplayer {playerCount}" - }); - } - #endregion - - PlayniteApi.Database.Games.Update(game); - } - catch (Exception ex) + game.Features.Add(new GameFeature() { - - } + Name = $"LAN Multiplayer {playerCount}" + }); } } + + if (manifest.LocalMultiplayer != null) + { + var multiplayerInfo = manifest.LocalMultiplayer; + + string playerCount = multiplayerInfo.MinPlayers == multiplayerInfo.MaxPlayers ? $"({multiplayerInfo.MinPlayers} players)" : $"({multiplayerInfo.MinPlayers} - {multiplayerInfo.MaxPlayers} players)"; + + game.Features.Add(new GameFeature() + { + Name = $"Local Multiplayer {playerCount}" + }); + } + + if (manifest.OnlineMultiplayer != null) + { + var multiplayerInfo = manifest.OnlineMultiplayer; + + string playerCount = multiplayerInfo.MinPlayers == multiplayerInfo.MaxPlayers ? $"({multiplayerInfo.MinPlayers} players)" : $"({multiplayerInfo.MinPlayers} - {multiplayerInfo.MaxPlayers} players)"; + + game.Features.Add(new GameFeature() + { + Name = $"Online Multiplayer {playerCount}" + }); + } + #endregion + + PlayniteApi.Database.Games.Update(game); } } } diff --git a/LANCommander.SDK/Models/Action.cs b/LANCommander.SDK/Models/Action.cs index e7880e8..f53bac1 100644 --- a/LANCommander.SDK/Models/Action.cs +++ b/LANCommander.SDK/Models/Action.cs @@ -11,5 +11,6 @@ namespace LANCommander.SDK.Models public string Path { get; set; } public string WorkingDirectory { get; set; } public bool PrimaryAction { get; set; } + public int SortOrder { get; set; } } } diff --git a/LANCommander.SDK/Models/GameManifest.cs b/LANCommander.SDK/Models/GameManifest.cs index 6f705fc..ab214bc 100644 --- a/LANCommander.SDK/Models/GameManifest.cs +++ b/LANCommander.SDK/Models/GameManifest.cs @@ -31,6 +31,7 @@ namespace LANCommander.SDK public string Path { get; set; } public string WorkingDirectory { get; set; } public bool IsPrimaryAction { get; set; } + public int SortOrder { get; set; } } public class MultiplayerInfo diff --git a/LANCommander/Components/ActionEditor.razor b/LANCommander/Components/ActionEditor.razor index 5dbeeb5..1c5c67e 100644 --- a/LANCommander/Components/ActionEditor.razor +++ b/LANCommander/Components/ActionEditor.razor @@ -1,4 +1,5 @@ @using LANCommander.Data.Models +@using LANCommander.Extensions @{ int i = 0; @@ -21,7 +22,7 @@ Actions are used to start the game or launch other executables. It is recommended to have at least one action to launch the game. } - @foreach (var action in Actions) + @foreach (var action in Actions.OrderBy(a => a.SortOrder)) { var index = i; @@ -37,8 +38,23 @@ +
+ + + +
@@ -64,9 +81,18 @@ @code { - [Parameter] public ICollection Actions { get; set; } + [Parameter] public List Actions { get; set; } [Parameter] public Guid GameId { get; set; } + protected override void OnInitialized() + { + Actions = Actions.OrderBy(a => a.SortOrder).ToList(); + + FixSortOrders(); + + base.OnInitialized(); + } + private void AddAction() { if (Actions == null) @@ -74,12 +100,40 @@ Actions.Add(new Data.Models.Action() { - PrimaryAction = Actions.Count == 0 + PrimaryAction = Actions.Count == 0, + SortOrder = Actions.Count }); } private void RemoveAction(int index) { Actions.Remove(Actions.ElementAt(index)); + + FixSortOrders(); + } + + private void MoveUp(int index) + { + if (index == 0) + return; + + Actions.Move(Actions.ElementAt(index), index - 1); + FixSortOrders(); + } + + private void MoveDown(int index) + { + if (index == Actions.Count - 1) + return; + + Actions.Move(Actions.ElementAt(index), index + 1); + FixSortOrders(); + } + + private void FixSortOrders() { + for (int i = 0; i < Actions.Count; i++) + { + Actions.ElementAt(i).SortOrder = i; + } } } diff --git a/LANCommander/Data/Models/Action.cs b/LANCommander/Data/Models/Action.cs index 2b5f630..0150251 100644 --- a/LANCommander/Data/Models/Action.cs +++ b/LANCommander/Data/Models/Action.cs @@ -11,6 +11,7 @@ namespace LANCommander.Data.Models public string? Path { get; set; } public string? WorkingDirectory { get; set; } public bool PrimaryAction { get; set; } + public int SortOrder { get; set; } public Guid GameId { get; set; } [JsonIgnore] diff --git a/LANCommander/Extensions/ListExtensions.cs b/LANCommander/Extensions/ListExtensions.cs new file mode 100644 index 0000000..79a9efe --- /dev/null +++ b/LANCommander/Extensions/ListExtensions.cs @@ -0,0 +1,36 @@ +namespace LANCommander.Extensions +{ + public static class ListExtensions + { + public static void Move(this List list, int oldIndex, int newIndex) + { + var item = list[oldIndex]; + + list.RemoveAt(oldIndex); + + if (newIndex > oldIndex) newIndex--; + // the actual index could have shifted due to the removal + + list.Insert(newIndex, item); + } + + public static void Move(this List list, T item, int newIndex) + { + if (item != null) + { + var oldIndex = list.IndexOf(item); + + if (oldIndex > -1) + { + list.RemoveAt(oldIndex); + + if (newIndex > oldIndex) newIndex--; + // the actual index could have shifted due to the removal + + list.Insert(newIndex, item); + } + } + + } + } +} diff --git a/LANCommander/Migrations/20230117002451_AddActionSortOrder.Designer.cs b/LANCommander/Migrations/20230117002451_AddActionSortOrder.Designer.cs new file mode 100644 index 0000000..80a0a2d --- /dev/null +++ b/LANCommander/Migrations/20230117002451_AddActionSortOrder.Designer.cs @@ -0,0 +1,1102 @@ +// +using System; +using LANCommander.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace LANCommander.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20230117002451_AddActionSortOrder")] + partial class AddActionSortOrder + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.8"); + + modelBuilder.Entity("CategoryGame", b => + { + b.Property("CategoriesId") + .HasColumnType("TEXT"); + + b.Property("GamesId") + .HasColumnType("TEXT"); + + b.HasKey("CategoriesId", "GamesId"); + + b.HasIndex("GamesId"); + + b.ToTable("CategoryGame"); + }); + + modelBuilder.Entity("GameDeveloper", b => + { + b.Property("DeveloperId") + .HasColumnType("TEXT"); + + b.Property("GameId") + .HasColumnType("TEXT"); + + b.HasKey("DeveloperId", "GameId"); + + b.HasIndex("GameId"); + + b.ToTable("GameDeveloper"); + }); + + modelBuilder.Entity("GameGenre", b => + { + b.Property("GamesId") + .HasColumnType("TEXT"); + + b.Property("GenresId") + .HasColumnType("TEXT"); + + b.HasKey("GamesId", "GenresId"); + + b.HasIndex("GenresId"); + + b.ToTable("GameGenre"); + }); + + modelBuilder.Entity("GamePublisher", b => + { + b.Property("GameId") + .HasColumnType("TEXT"); + + b.Property("PublisherId") + .HasColumnType("TEXT"); + + b.HasKey("GameId", "PublisherId"); + + b.HasIndex("PublisherId"); + + b.ToTable("GamePublisher"); + }); + + modelBuilder.Entity("GameTag", b => + { + b.Property("GamesId") + .HasColumnType("TEXT"); + + b.Property("TagsId") + .HasColumnType("TEXT"); + + b.HasKey("GamesId", "TagsId"); + + b.HasIndex("TagsId"); + + b.ToTable("GameTag"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Action", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Arguments") + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("GameId") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("PrimaryAction") + .HasColumnType("INTEGER"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.Property("WorkingDirectory") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("GameId"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Actions"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Archive", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Changelog") + .HasColumnType("TEXT"); + + b.Property("CompressedSize") + .HasColumnType("INTEGER"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("GameId") + .HasColumnType("TEXT"); + + b.Property("LastVersionId") + .HasColumnType("TEXT"); + + b.Property("ObjectKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UncompressedSize") + .HasColumnType("INTEGER"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.Property("Version") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("GameId"); + + b.HasIndex("LastVersionId"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Archive"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("ParentId"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Categories"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Company", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Companies"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Game", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("DirectoryName") + .HasColumnType("TEXT"); + + b.Property("IGDBId") + .HasColumnType("INTEGER"); + + b.Property("Icon") + .HasColumnType("TEXT"); + + b.Property("ReleasedOn") + .HasColumnType("TEXT"); + + b.Property("Singleplayer") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.Property("ValidKeyRegex") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Games"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Genre", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Genres"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Key", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AllocationMethod") + .HasColumnType("INTEGER"); + + b.Property("ClaimedByComputerName") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("ClaimedByIpv4Address") + .HasMaxLength(15) + .HasColumnType("TEXT"); + + b.Property("ClaimedByMacAddress") + .HasMaxLength(17) + .HasColumnType("TEXT"); + + b.Property("ClaimedByUserId") + .HasColumnType("TEXT"); + + b.Property("ClaimedOn") + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("GameId") + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ClaimedByUserId"); + + b.HasIndex("CreatedById"); + + b.HasIndex("GameId"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Keys"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.MultiplayerMode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("GameId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MaxPlayers") + .HasColumnType("INTEGER"); + + b.Property("MinPlayers") + .HasColumnType("INTEGER"); + + b.Property("NetworkProtocol") + .HasColumnType("INTEGER"); + + b.Property("Spectators") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("GameId"); + + b.HasIndex("UpdatedById"); + + b.ToTable("MultiplayerModes"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Script", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Contents") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("GameId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RequiresAdmin") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("GameId"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Scripts"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Tags"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("RefreshToken") + .HasColumnType("TEXT"); + + b.Property("RefreshTokenExpiration") + .HasColumnType("TEXT"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("CategoryGame", b => + { + b.HasOne("LANCommander.Data.Models.Category", null) + .WithMany() + .HasForeignKey("CategoriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Data.Models.Game", null) + .WithMany() + .HasForeignKey("GamesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("GameDeveloper", b => + { + b.HasOne("LANCommander.Data.Models.Company", null) + .WithMany() + .HasForeignKey("DeveloperId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Data.Models.Game", null) + .WithMany() + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("GameGenre", b => + { + b.HasOne("LANCommander.Data.Models.Game", null) + .WithMany() + .HasForeignKey("GamesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Data.Models.Genre", null) + .WithMany() + .HasForeignKey("GenresId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("GamePublisher", b => + { + b.HasOne("LANCommander.Data.Models.Game", null) + .WithMany() + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Data.Models.Company", null) + .WithMany() + .HasForeignKey("PublisherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("GameTag", b => + { + b.HasOne("LANCommander.Data.Models.Game", null) + .WithMany() + .HasForeignKey("GamesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Data.Models.Tag", null) + .WithMany() + .HasForeignKey("TagsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Action", b => + { + b.HasOne("LANCommander.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById"); + + b.HasOne("LANCommander.Data.Models.Game", "Game") + .WithMany("Actions") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById"); + + b.Navigation("CreatedBy"); + + b.Navigation("Game"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Archive", b => + { + b.HasOne("LANCommander.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById"); + + b.HasOne("LANCommander.Data.Models.Game", "Game") + .WithMany("Archives") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Data.Models.Archive", "LastVersion") + .WithMany() + .HasForeignKey("LastVersionId"); + + b.HasOne("LANCommander.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById"); + + b.Navigation("CreatedBy"); + + b.Navigation("Game"); + + b.Navigation("LastVersion"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Category", b => + { + b.HasOne("LANCommander.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById"); + + b.HasOne("LANCommander.Data.Models.Category", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("LANCommander.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById"); + + b.Navigation("CreatedBy"); + + b.Navigation("Parent"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Company", b => + { + b.HasOne("LANCommander.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById"); + + b.HasOne("LANCommander.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById"); + + b.Navigation("CreatedBy"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Game", b => + { + b.HasOne("LANCommander.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById"); + + b.HasOne("LANCommander.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById"); + + b.Navigation("CreatedBy"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Genre", b => + { + b.HasOne("LANCommander.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById"); + + b.HasOne("LANCommander.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById"); + + b.Navigation("CreatedBy"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Key", b => + { + b.HasOne("LANCommander.Data.Models.User", "ClaimedByUser") + .WithMany() + .HasForeignKey("ClaimedByUserId"); + + b.HasOne("LANCommander.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById"); + + b.HasOne("LANCommander.Data.Models.Game", "Game") + .WithMany("Keys") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById"); + + b.Navigation("ClaimedByUser"); + + b.Navigation("CreatedBy"); + + b.Navigation("Game"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.MultiplayerMode", b => + { + b.HasOne("LANCommander.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById"); + + b.HasOne("LANCommander.Data.Models.Game", "Game") + .WithMany("MultiplayerModes") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById"); + + b.Navigation("CreatedBy"); + + b.Navigation("Game"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Script", b => + { + b.HasOne("LANCommander.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById"); + + b.HasOne("LANCommander.Data.Models.Game", "Game") + .WithMany("Scripts") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById"); + + b.Navigation("CreatedBy"); + + b.Navigation("Game"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Tag", b => + { + b.HasOne("LANCommander.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById"); + + b.HasOne("LANCommander.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById"); + + b.Navigation("CreatedBy"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("LANCommander.Data.Models.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("LANCommander.Data.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("LANCommander.Data.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("LANCommander.Data.Models.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Data.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("LANCommander.Data.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Category", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("LANCommander.Data.Models.Game", b => + { + b.Navigation("Actions"); + + b.Navigation("Archives"); + + b.Navigation("Keys"); + + b.Navigation("MultiplayerModes"); + + b.Navigation("Scripts"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/LANCommander/Migrations/20230117002451_AddActionSortOrder.cs b/LANCommander/Migrations/20230117002451_AddActionSortOrder.cs new file mode 100644 index 0000000..8978cc2 --- /dev/null +++ b/LANCommander/Migrations/20230117002451_AddActionSortOrder.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LANCommander.Migrations +{ + public partial class AddActionSortOrder : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SortOrder", + table: "Actions", + type: "INTEGER", + nullable: false, + defaultValue: 0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "SortOrder", + table: "Actions"); + } + } +} diff --git a/LANCommander/Migrations/DatabaseContextModelSnapshot.cs b/LANCommander/Migrations/DatabaseContextModelSnapshot.cs index b2f5ca1..ce4c96d 100644 --- a/LANCommander/Migrations/DatabaseContextModelSnapshot.cs +++ b/LANCommander/Migrations/DatabaseContextModelSnapshot.cs @@ -120,6 +120,9 @@ namespace LANCommander.Migrations b.Property("PrimaryAction") .HasColumnType("INTEGER"); + b.Property("SortOrder") + .HasColumnType("INTEGER"); + b.Property("UpdatedById") .HasColumnType("TEXT"); diff --git a/LANCommander/Services/GameService.cs b/LANCommander/Services/GameService.cs index 6ca24b2..eb45d39 100644 --- a/LANCommander/Services/GameService.cs +++ b/LANCommander/Services/GameService.cs @@ -71,7 +71,8 @@ namespace LANCommander.Services Arguments = a.Arguments, Path = a.Path, WorkingDirectory = a.WorkingDirectory, - IsPrimaryAction = a.PrimaryAction + IsPrimaryAction = a.PrimaryAction, + SortOrder = a.SortOrder, }).ToArray(); } diff --git a/LANCommander/Views/Games/Add.cshtml b/LANCommander/Views/Games/Add.cshtml index b992877..ef0490c 100644 --- a/LANCommander/Views/Games/Add.cshtml +++ b/LANCommander/Views/Games/Add.cshtml @@ -93,7 +93,7 @@

Actions

- +

Multiplayer Modes

diff --git a/LANCommander/Views/Games/Edit.cshtml b/LANCommander/Views/Games/Edit.cshtml index 1978beb..b3c4d16 100644 --- a/LANCommander/Views/Games/Edit.cshtml +++ b/LANCommander/Views/Games/Edit.cshtml @@ -96,7 +96,7 @@

Actions

- +

Multiplayer Modes