From 4afafacab01b640e49d9032489777edc1ecd0c8a Mon Sep 17 00:00:00 2001 From: Pat Hartl <pat@pathar.tl> Date: Thu, 12 Jan 2023 01:27:32 -0600 Subject: [PATCH] Added Blazor support with two components for managing actions and multiplayer modes on a game --- LANCommander/Components/ActionEditor.razor | 77 ++++++++++++++++++ .../Components/MultiplayerModeEditor.razor | 78 +++++++++++++++++++ LANCommander/Components/_Imports.razor | 7 ++ LANCommander/Program.cs | 13 +++- LANCommander/Views/Games/Edit.cshtml | 15 +++- LANCommander/Views/Shared/_Layout.cshtml | 9 ++- 6 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 LANCommander/Components/ActionEditor.razor create mode 100644 LANCommander/Components/MultiplayerModeEditor.razor create mode 100644 LANCommander/Components/_Imports.razor diff --git a/LANCommander/Components/ActionEditor.razor b/LANCommander/Components/ActionEditor.razor new file mode 100644 index 0000000..f7b292a --- /dev/null +++ b/LANCommander/Components/ActionEditor.razor @@ -0,0 +1,77 @@ +@using LANCommander.Data.Models + +@{ + int i = 0; +} +<div class="table-responsive"> + <table class="table mb-0"> + <thead> + <tr> + <th>Name</th> + <th>Path</th> + <th>Arguments</th> + <th>Primary</th> + <th></th> + </tr> + </thead> + + <tbody> + @if (Actions == null || Actions.Count == 0) + { + <tr><td colspan="5">Actions are used to start the game or launch other executables. It is recommended to have at least one action to launch the game.</td></tr> + } + + @foreach (var action in Actions) + { + <tr> + <td><input @bind="action.Name" name="Game.Actions[@i].Name" class="form-control" placeholder="Play" /></td> + <td><input @bind="action.Path" name="Game.Actions[@i].Path" class="form-control" placeholder="Game.exe" /></td> + <td><input @bind="action.Arguments" name="Game.Actions[@i].Arguments" class="form-control" placeholder="Launch Arguments" /></td> + <td class="align-middle"> + <div class="form-check form-switch form-check-inline mb-0"> + <input @bind="action.PrimaryAction" name="Game.Actions[@i].PrimaryAction" class="form-check-input" type="checkbox" /> + </div> + </td> + <td> + <div class="btn-list flex-nowrap justify-content-end"> + <button class="btn btn-ghost-danger btn-icon" @onclick="() => RemoveAction(i)" type="button"> + <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-x" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> + <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> + <line x1="18" y1="6" x2="6" y2="18"></line> + <line x1="6" y1="6" x2="18" y2="18"></line> + </svg> + </button> + </div> + </td> + </tr> + } + <tr> + <td colspan="5"> + <div class="btn-list flex-nowrap justify-content-end"> + <button class="btn btn-ghost-primary" @onclick="AddAction" type="button">Add Action</button> + </div> + </td> + </tr> + </tbody> + </table> +</div> + +@code { + [Parameter] public ICollection<Data.Models.Action> Actions { get; set; } + + private void AddAction() + { + if (Actions == null) + Actions = new List<Data.Models.Action>(); + + Actions.Add(new Data.Models.Action() + { + PrimaryAction = Actions.Count == 0 + }); + } + + private void RemoveAction(int index) + { + Actions.Remove(Actions.ElementAt(index)); + } +} diff --git a/LANCommander/Components/MultiplayerModeEditor.razor b/LANCommander/Components/MultiplayerModeEditor.razor new file mode 100644 index 0000000..c7f11a4 --- /dev/null +++ b/LANCommander/Components/MultiplayerModeEditor.razor @@ -0,0 +1,78 @@ +@using LANCommander.Data.Enums +@using LANCommander.Data.Models + +@{ + int i = 0; +} +<div class="table-responsive"> + <table class="table mb-0"> + <thead> + <tr> + <th>Type</th> + <th>Min Players</th> + <th>Max Players</th> + <th>Description</th> + <th></th> + </tr> + </thead> + + <tbody> + @if (MultiplayerModes.Count == 0) + { + <tr><td colspan="5">If the game has any multiplayer modes you can add them here to provide metadata to clients.</td></tr> + } + + @foreach (var multiplayerMode in MultiplayerModes) + { + <tr> + <td> + <select @bind="multiplayerMode.Type" name="Game.MultiplayerModes[@i].Type" class="form-control"> + @foreach (var type in Enum.GetValues(typeof(MultiplayerType))) + { + <option value="@type">@type</option> + } + </select> + </td> + <td><input @bind="multiplayerMode.MinPlayers" name="Game.MultiplayerModes[@i].MinPlayers" class="form-control" /></td> + <td><input @bind="multiplayerMode.MaxPlayers" name="Game.MultiplayerModes[@i].MaxPlayers" class="form-control" /></td> + <td><input @bind="multiplayerMode.Description" name="Game.MultiplayerModes[@i].Description" class="form-control" /></td> + <td> + <div class="btn-list flex-nowrap justify-content-end"> + <button class="btn btn-ghost-danger btn-icon" @onclick="() => RemoveMode(i)" type="button"> + <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-x" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> + <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> + <line x1="18" y1="6" x2="6" y2="18"></line> + <line x1="6" y1="6" x2="18" y2="18"></line> + </svg> + </button> + </div> + </td> + </tr> + } + <tr> + <td colspan="5"> + <div class="btn-list flex-nowrap justify-content-end"> + <button class="btn btn-ghost-primary" @onclick="AddMode" type="button">Add Mode</button> + </div> + </td> + </tr> + </tbody> + </table> +</div> + +@code { + [Parameter] public ICollection<MultiplayerMode> MultiplayerModes { get; set; } + + private void AddMode() + { + if (MultiplayerModes == null) + MultiplayerModes = new List<MultiplayerMode>(); + + MultiplayerModes.Add(new MultiplayerMode()); + } + + private void RemoveMode(int index) + { + MultiplayerModes.Remove(MultiplayerModes.ElementAt(index)); + } +} diff --git a/LANCommander/Components/_Imports.razor b/LANCommander/Components/_Imports.razor new file mode 100644 index 0000000..bdfbec1 --- /dev/null +++ b/LANCommander/Components/_Imports.razor @@ -0,0 +1,7 @@ +@using System.Net.Http +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Components.Authorization +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.JSInterop \ No newline at end of file diff --git a/LANCommander/Program.cs b/LANCommander/Program.cs index a3f3d96..450b7dc 100644 --- a/LANCommander/Program.cs +++ b/LANCommander/Program.cs @@ -56,7 +56,9 @@ builder.Services.AddAuthentication(options => builder.Services.AddControllersWithViews().AddJsonOptions(x => { x.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles; + x.JsonSerializerOptions.MaxDepth = 3; }); +builder.Services.AddServerSideBlazor(); builder.Services.AddScoped<SettingService>(); builder.Services.AddScoped<ArchiveService>(); @@ -92,9 +94,14 @@ app.UseAuthorization(); app.MapControllers(); -app.MapControllerRoute( - name: "default", - pattern: "{controller=Home}/{action=Index}/{id?}"); +app.UseEndpoints(endpoints => +{ + endpoints.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); + endpoints.MapBlazorHub(); +}); + app.MapRazorPages(); if (!Directory.Exists("Upload")) diff --git a/LANCommander/Views/Games/Edit.cshtml b/LANCommander/Views/Games/Edit.cshtml index 22c5653..31e71fc 100644 --- a/LANCommander/Views/Games/Edit.cshtml +++ b/LANCommander/Views/Games/Edit.cshtml @@ -1,4 +1,5 @@ -@using LANCommander.Data.Models +@using LANCommander.Components +@using LANCommander.Data.Models @model LANCommander.Models.GameViewModel @{ @@ -79,6 +80,18 @@ </div> </div> + <div class="card-header"> + <h3 class="card-title">Actions</h3> + </div> + + <component type="typeof(ActionEditor)" render-mode="Server" param-Actions="Model.Game.Actions" /> + + <div class="card-header"> + <h3 class="card-title">Multiplayer Modes</h3> + </div> + + <component type="typeof(MultiplayerModeEditor)" render-mode="Server" param-MultiplayerModes="Model.Game.MultiplayerModes" /> + <div class="card-footer"> <div class="d-flex"> <a asp-action="Index" class="btn btn-ghost-primary">Cancel</a> diff --git a/LANCommander/Views/Shared/_Layout.cshtml b/LANCommander/Views/Shared/_Layout.cshtml index 6da2064..7bcd38f 100644 --- a/LANCommander/Views/Shared/_Layout.cshtml +++ b/LANCommander/Views/Shared/_Layout.cshtml @@ -1,4 +1,9 @@ -<!DOCTYPE html> +@inject Microsoft.AspNetCore.Http.IHttpContextAccessor _HttpContext +@{ + _HttpContext.HttpContext.Response.Headers["Cache-Control"] = "no-store"; +} + +<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> @@ -6,6 +11,7 @@ <title>@ViewData["Title"] - LANCommander</title> <link href="~/css/tabler.min.css" rel="stylesheet" /> <link href="~/lib/selectize.js/css/selectize.bootstrap5.min.css" rel="stylesheet" /> + <base href="~/"/> </head> <body> <header class="navbar navbar-expand-md navbar-light"> @@ -92,6 +98,7 @@ <script src="~/lib/tabler/core/dist/js/tabler.min.js"></script> <script src="~/js/Modal.js"></script> <script src="~/js/Select.js"></script> + <script src="~/_framework/blazor.server.js"></script> @await RenderSectionAsync("Scripts", required: false) </body>