diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index 203fd44..cbef352 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -57,13 +57,13 @@ jobs: run: dotnet restore --locked-mode - name: pack - run: dotnet pack src/Launcher/CS2Launcher.AspNetCore.Launcher.csproj -c Release -o .package --no-restore + run: dotnet pack -c Release -o .packages --no-restore - name: upload artifact uses: actions/upload-artifact@v4 with: - name: cs2-launcher - path: .package + name: cs2-launcher-packages + path: .packages publish: needs: [pack] @@ -83,7 +83,7 @@ jobs: - name: download artifact uses: actions/download-artifact@v4 with: - name: cs2-launcher + name: cs2-launcher-packages - name: push run: dotnet nuget push "*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate diff --git a/.vscode/launch.json b/.vscode/launch.json index c914e23..18c1b04 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,14 +5,14 @@ "name": "Debug Sample (Core)", "type": "coreclr", "request": "launch", - "program": "${workspaceFolder}/sample/bin/Debug/net8.0/CS2Launcher.Sample.dll", + "program": "${workspaceFolder}/sample/Launcher/bin/Debug/net8.0/CS2Launcher.Sample.Launcher.dll", "args": [ "environment=Development" ], "env": { "DOTNET_ENVIRONMENT": "Development" }, - "cwd": "${workspaceFolder}/sample", + "cwd": "${workspaceFolder}/sample/Launcher", "justMyCode": true, "preLaunchTask": "${defaultBuildTask}", "requireExactSource": true, @@ -36,7 +36,7 @@ "type": "blazorwasm", "request": "launch", "browser": "chrome", - "cwd": "${workspaceFolder}/sample", + "cwd": "${workspaceFolder}/sample/Launcher", "dotNetConfig": { "justMyCode": true, "logging": { @@ -63,14 +63,14 @@ }, "hosted": true, "preLaunchTask": "${defaultBuildTask}", - "program": "${workspaceFolder}/sample/bin/Debug/net8.0/CS2Launcher.Sample.dll", + "program": "${workspaceFolder}/sample/Launcher/bin/Debug/net8.0/CS2Launcher.Sample.Launcher.dll", "serverReadyAction": { "action": "debugWithChrome", "killOnServerStop": true, "pattern": "\\bNow listening on:\\s+(https?://\\S+)", }, "url": "https://localhost:52750", - "webRoot": "${workspaceFolder}/sample", + "webRoot": "${workspaceFolder}/sample/Launcher", } ] } \ No newline at end of file diff --git a/cs2-launcher.sln b/cs2-launcher.sln index a79d6c3..84f894a 100644 --- a/cs2-launcher.sln +++ b/cs2-launcher.sln @@ -4,13 +4,21 @@ Microsoft Visual Studio Solution File, Format Version 12.00 VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5222FED7-6FE1-46CA-AA88-EA10FA14075C}" + ProjectSection(SolutionItems) = preProject + src\Directory.Build.props = src\Directory.Build.props + src\Directory.Build.targets = src\Directory.Build.targets + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CS2Launcher.AspNetCore.Launcher", "src\Launcher\CS2Launcher.AspNetCore.Launcher.csproj", "{115173BC-C8AD-4266-8759-3CA0A14374C2}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CS2Launcher.Sample", "sample\CS2Launcher.Sample.csproj", "{2ECAA280-96DA-4212-9857-6E3BAA4E2217}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CS2Launcher.AspNetCore.App", "src\App\CS2Launcher.AspNetCore.App.csproj", "{42BE4AA8-0386-474D-BFE2-0F4E68476B42}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{170E8297-96FC-4172-B45C-2FCBCEFB9F4D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CS2Launcher.Sample.Launcher", "sample\Launcher\CS2Launcher.Sample.Launcher.csproj", "{A415B696-7249-4A76-8423-6AC7163C8347}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CS2Launcher.Sample.App", "sample\App\CS2Launcher.Sample.App.csproj", "{55CFF728-1CC8-45A8-A4A8-1FC690065E3E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,14 +29,18 @@ Global {115173BC-C8AD-4266-8759-3CA0A14374C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {115173BC-C8AD-4266-8759-3CA0A14374C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {115173BC-C8AD-4266-8759-3CA0A14374C2}.Release|Any CPU.Build.0 = Release|Any CPU - {2ECAA280-96DA-4212-9857-6E3BAA4E2217}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2ECAA280-96DA-4212-9857-6E3BAA4E2217}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2ECAA280-96DA-4212-9857-6E3BAA4E2217}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2ECAA280-96DA-4212-9857-6E3BAA4E2217}.Release|Any CPU.Build.0 = Release|Any CPU {42BE4AA8-0386-474D-BFE2-0F4E68476B42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {42BE4AA8-0386-474D-BFE2-0F4E68476B42}.Debug|Any CPU.Build.0 = Debug|Any CPU {42BE4AA8-0386-474D-BFE2-0F4E68476B42}.Release|Any CPU.ActiveCfg = Release|Any CPU {42BE4AA8-0386-474D-BFE2-0F4E68476B42}.Release|Any CPU.Build.0 = Release|Any CPU + {A415B696-7249-4A76-8423-6AC7163C8347}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A415B696-7249-4A76-8423-6AC7163C8347}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A415B696-7249-4A76-8423-6AC7163C8347}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A415B696-7249-4A76-8423-6AC7163C8347}.Release|Any CPU.Build.0 = Release|Any CPU + {55CFF728-1CC8-45A8-A4A8-1FC690065E3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CFF728-1CC8-45A8-A4A8-1FC690065E3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CFF728-1CC8-45A8-A4A8-1FC690065E3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CFF728-1CC8-45A8-A4A8-1FC690065E3E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -36,6 +48,8 @@ Global GlobalSection(NestedProjects) = preSolution {115173BC-C8AD-4266-8759-3CA0A14374C2} = {5222FED7-6FE1-46CA-AA88-EA10FA14075C} {42BE4AA8-0386-474D-BFE2-0F4E68476B42} = {5222FED7-6FE1-46CA-AA88-EA10FA14075C} + {A415B696-7249-4A76-8423-6AC7163C8347} = {170E8297-96FC-4172-B45C-2FCBCEFB9F4D} + {55CFF728-1CC8-45A8-A4A8-1FC690065E3E} = {170E8297-96FC-4172-B45C-2FCBCEFB9F4D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {56EB9027-8ADE-4B88-BDE0-E9BC3DD1ACF4} diff --git a/sample/App/AppRoot.cs b/sample/App/AppRoot.cs new file mode 100644 index 0000000..cad74d2 --- /dev/null +++ b/sample/App/AppRoot.cs @@ -0,0 +1,5 @@ +using CS2Launcher.AspNetCore.App; + +namespace CS2Launcher.Sample.App; + +public sealed class AppRoot : RootComponent; \ No newline at end of file diff --git a/sample/App/CS2Launcher.Sample.App.csproj b/sample/App/CS2Launcher.Sample.App.csproj new file mode 100644 index 0000000..f61c6d5 --- /dev/null +++ b/sample/App/CS2Launcher.Sample.App.csproj @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/sample/App/Program.cs b/sample/App/Program.cs new file mode 100644 index 0000000..3819553 --- /dev/null +++ b/sample/App/Program.cs @@ -0,0 +1,9 @@ +using CS2Launcher.AspNetCore.App.Hosting; +using CS2Launcher.Sample.App; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; + +var builder = WebAssemblyHostBuilder.CreateDefault( args ); +builder.UseCS2Launcher(); + +await using var app = builder.Build(); +await app.RunAsync(); \ No newline at end of file diff --git a/sample/App/packages.lock.json b/sample/App/packages.lock.json new file mode 100644 index 0000000..172d2e4 --- /dev/null +++ b/sample/App/packages.lock.json @@ -0,0 +1,399 @@ +{ + "version": 1, + "dependencies": { + "net8.0": { + "Microsoft.AspNetCore.Components.WebAssembly": { + "type": "Direct", + "requested": "[8.0.4, )", + "resolved": "8.0.4", + "contentHash": "4mi1GyihE1R9BocAc5ZUeYZN/G2cFfoGibb6H+yQ7xXk3ec7cvqkjamdnXH2EwsikS+hX44pgirtiwUHDUw1uw==", + "dependencies": { + "Microsoft.AspNetCore.Components.Web": "8.0.4", + "Microsoft.Extensions.Configuration.Binder": "8.0.1", + "Microsoft.Extensions.Configuration.Json": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "Microsoft.JSInterop.WebAssembly": "8.0.4" + } + }, + "Microsoft.NET.ILLink.Tasks": { + "type": "Direct", + "requested": "[8.0.4, )", + "resolved": "8.0.4", + "contentHash": "PZb5nfQ+U19nhnmnR9T1jw+LTmozhuG2eeuzuW5A7DqxD/UXW2ucjmNJqnqOuh8rdPzM3MQXoF8AfFCedJdCUw==" + }, + "Microsoft.NET.Sdk.WebAssembly.Pack": { + "type": "Direct", + "requested": "[8.0.4, )", + "resolved": "8.0.4", + "contentHash": "D4kK4zXW43HvIX0lG9rBGGVzNtY6WwbCGAHJEi6iChU5Dgw8VenJS7Jc1g9Yy8pq4mEQA5PCcZicA16hGTjRpw==" + }, + "MessagePack": { + "type": "Transitive", + "resolved": "2.5.108", + "contentHash": "kcVRbdWP3xNWLZmmpm4DFO+kuXf6mUR2mHZ27WoZIEFIv9hazuUd80injXhNrZnlq/FklAdCsLOil5M76I4Ndg==", + "dependencies": { + "MessagePack.Annotations": "2.5.108", + "Microsoft.NET.StringTools": "17.4.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "MessagePack.Annotations": { + "type": "Transitive", + "resolved": "2.5.108", + "contentHash": "28aNCvfJClgwaKr26gf2S6LT+C1PNyPxiG+ihYpy8uCJsRLJEDoCt2I0Uk5hqOPQ8P8hI0ESy520oMkZkPmsOQ==" + }, + "Microsoft.AspNetCore.Authorization": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "dzYUHvqyavBiYVd5BZNAgV5rT+DC4HyIWPnbeYgxQNBYOFomvvwzPfidQf1Ld/HhkXhEj3tlzqNb4gy2P2ukUg==", + "dependencies": { + "Microsoft.AspNetCore.Metadata": "8.0.4", + "Microsoft.Extensions.Logging.Abstractions": "8.0.1", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.AspNetCore.Components": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "ExTJT1u3QCTKaq2LjazkoHMNVlz0Q93A4ei5Zqd38bj3hggpShi4A/U22zk1LOmT0eUE5WklOBMpKZMg+T6Veg==", + "dependencies": { + "Microsoft.AspNetCore.Authorization": "8.0.4", + "Microsoft.AspNetCore.Components.Analyzers": "8.0.4" + } + }, + "Microsoft.AspNetCore.Components.Analyzers": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "kEvEOOkgN8uLjP2PMR481yxSjUsyGLaehHSD3HWjboNsXA5++7Hxn83LRdVri9/0rlY2zS/dP9oGHZpgxYu2iQ==" + }, + "Microsoft.AspNetCore.Components.Forms": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "I/kttR/nmRZTc+bsetYnvbkVzHgsJXrWKxHo30vGvQV+dTeur9LV2g2TvyLerpWL9OFbNWgpTGBUwgX1IW/0Zw==", + "dependencies": { + "Microsoft.AspNetCore.Components": "8.0.4" + } + }, + "Microsoft.AspNetCore.Components.Web": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "K9zcUkciMHmPjYdaB417xlG9Srv2udLT5j7CIAN92H5KNLH1ItIGIEIIUMJT3r+2Ma3ZLZO0eddEbR+Z59MbOQ==", + "dependencies": { + "Microsoft.AspNetCore.Components": "8.0.4", + "Microsoft.AspNetCore.Components.Forms": "8.0.4", + "Microsoft.Extensions.DependencyInjection": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0", + "Microsoft.JSInterop": "8.0.4", + "System.IO.Pipelines": "8.0.0" + } + }, + "Microsoft.AspNetCore.Connections.Abstractions": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "pvO0JgjrNOas+WzmHxqawTv64lwTy1zbGm4WQY7OOd/OfKMw8LaoNEtn9jsAfiKUkt3lYK8kOL8Uy9XyyTCUwg==", + "dependencies": { + "Microsoft.Extensions.Features": "8.0.4", + "System.IO.Pipelines": "8.0.0" + } + }, + "Microsoft.AspNetCore.Http.Connections.Client": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "Tl9Hf+ftWZeKDGJpQOMwDgev7EXiiqk71NvoGDNhAYhYJMpoyf4591aINACAPSZ3zWNs4cqKIm8YQ5eG1slf5Q==", + "dependencies": { + "Microsoft.AspNetCore.Http.Connections.Common": "8.0.4", + "Microsoft.Extensions.Logging.Abstractions": "8.0.1", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.AspNetCore.Http.Connections.Common": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "SG02A6/ZET3sCW/PiJumg3q4yZdBZeMetdy/ebS4ZcmFPr+yI9sSmLAw9MHZVNNxxBWM5VuLg463YwlXoxhJHQ==", + "dependencies": { + "Microsoft.AspNetCore.Connections.Abstractions": "8.0.4" + } + }, + "Microsoft.AspNetCore.Metadata": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "TG3JhI7dkDCIoxZ3TL/5yptLYLg56w1f4jE8GSYnZXSso1xY5JKKCxObK+xKQJU5kZir30+QUGX1WpbC1kDcow==" + }, + "Microsoft.AspNetCore.SignalR.Client": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "HPM5/ifdz4PvK6W3t7CNHgV69MPPrS75tLD9HUYFRlwGmvbVPYbsgES2b7wmnvZEsaGlEBNOdw//xU1jK4FM0w==", + "dependencies": { + "Microsoft.AspNetCore.Http.Connections.Client": "8.0.4", + "Microsoft.AspNetCore.SignalR.Client.Core": "8.0.4" + } + }, + "Microsoft.AspNetCore.SignalR.Client.Core": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "w8eaEHS6Qn0VALv3G7EuLg/i5WInxt2Jps4IbvB0u61l9GaTAOGw0Xx1G+0UJgWwd2RZoaJXqMr748TuSpxm6g==", + "dependencies": { + "Microsoft.AspNetCore.SignalR.Common": "8.0.4", + "Microsoft.AspNetCore.SignalR.Protocols.Json": "8.0.4", + "Microsoft.Extensions.DependencyInjection": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "System.Threading.Channels": "8.0.0" + } + }, + "Microsoft.AspNetCore.SignalR.Common": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "vsoXzcAibrem3/qWSpZ9Q/Wc/4UpFdXtsNTBMAmltnQPGR07qa7Pzgrc7K9pT/gEkFR5DkFKkEOzgCkNBDUE+g==", + "dependencies": { + "Microsoft.AspNetCore.Connections.Abstractions": "8.0.4", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.AspNetCore.SignalR.Protocols.Json": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "9X4TnAZTGMVzH7DghV7AWXX5r8UO7RpSJAPxfd9lTu3GChEyUpppKIJMaOKpseplnlRxOedve8UPdjx5ks06vA==", + "dependencies": { + "Microsoft.AspNetCore.SignalR.Common": "8.0.4" + } + }, + "Microsoft.AspNetCore.SignalR.Protocols.MessagePack": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "Zr+ur9AHHLuKWbp1b2Tw/evvY41xt2Dar5zEmtfYg0vO/ztU4CBLCDdXvB01o9Dtb0bHoQ+25xwF11ur08/l1g==", + "dependencies": { + "MessagePack": "2.5.108", + "Microsoft.AspNetCore.SignalR.Common": "8.0.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "2UKFJnLiBt7Od6nCnTqP9rTIUNhzmn9Hv1l2FchyKbz8xieB9ULwZTbQZMw+M24Qw3F5dzzH1U9PPleN0LNLOQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "McP+Lz/EKwvtCv48z0YImw+L1gi1gy5rHhNaNIY2CrjloV+XY8gydT8DjMR6zWeL13AFK+DioVpppwAuO1Gi1w==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "C2wqUoh9OmRL1akaCcKSTmRU8z0kckfImG7zLNI8uyi47Lp+zd5LWAD17waPQEqCz3ioWOCrFUo+JJuoeZLOBw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "System.Text.Json": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "fGLiCRLMYd00JYpClraLjJTNKLmMJPnqxMaiRzEBIIvevlzxz33mXy39Lkd48hu1G+N21S7QpaO5ZzKsI6FRuA==" + }, + "Microsoft.Extensions.Features": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "ym56/tUtFHFHucqiyQq42Ha6n1CT3CkrSZmbb3Szfkl/KNEaNqotKXFQMWHlFk2WMvU4fepqUsW8VK5WEjMChA==" + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "RIFgaqoaINxkM2KTOw72dmilDmTrYA0ns2KW4lDz4gZ2+o6IQ894CzmdL3StM2oh7QQq44nCWiqKqc4qUI9Jmg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.1" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" + }, + "Microsoft.JSInterop": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "97XhCIlz57G74fuf9XsuItyeO/8e6etWYAPH8XuDmmIW7461Gu9WN3louVqjkh4BU0q3Nf5acvSIfkIuGzHVWw==" + }, + "Microsoft.JSInterop.WebAssembly": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "mG6XiYUmdLdrvowICG/nmGolx6oDZGt8xbkkI+7QvJL1aWuR/TTWPb6I6/N1ngQHMWzKpeRx1gWkmGV+dhJ58A==", + "dependencies": { + "Microsoft.JSInterop": "8.0.4" + } + }, + "Microsoft.NET.StringTools": { + "type": "Transitive", + "resolved": "17.4.0", + "contentHash": "06T6Hqfs3JDIaBvJaBRFFMIdU7oE0OMab5Xl8LKQjWPxBQr3BgVFKMQPTC+GsSEuYREWmK6g5eOd7Xqd9p1YCA==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Nito.AsyncEx.Coordination": { + "type": "Transitive", + "resolved": "5.1.2", + "contentHash": "QMyUfsaxov//0ZMbOHWr9hJaBFteZd66DV1ay4J5wRODDb8+K/uHC7+3VsOflo6SVw/29mu8OWZp8vMDSuzc0w==", + "dependencies": { + "Nito.AsyncEx.Tasks": "5.1.2", + "Nito.Collections.Deque": "1.1.1" + } + }, + "Nito.AsyncEx.Tasks": { + "type": "Transitive", + "resolved": "5.1.2", + "contentHash": "jEkCfR2/M26OK/U4G7SEN063EU/F4LiVA06TtpZILMdX/quIHCg+wn31Zerl2LC+u1cyFancjTY3cNAr2/89PA==", + "dependencies": { + "Nito.Disposables": "2.2.1" + } + }, + "Nito.Collections.Deque": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "CU0/Iuv5VDynK8I8pDLwkgF0rZhbQoZahtodfL0M3x2gFkpBRApKs8RyMyNlAi1mwExE4gsmqQXk4aFVvW9a4Q==" + }, + "Nito.Disposables": { + "type": "Transitive", + "resolved": "2.2.1", + "contentHash": "6sZ5uynQeAE9dPWBQGKebNmxbY4xsvcc5VplB5WkYEESUS7oy4AwnFp0FhqxTSKm/PaFrFqLrYr696CYN8cugg==", + "dependencies": { + "System.Collections.Immutable": "1.7.1" + } + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "1.7.1", + "contentHash": "B43Zsz5EfMwyEbnObwRxW5u85fzJma3lrDeGcSAV1qkhSRTNY5uXAByTn9h9ddNdhM+4/YoLc/CI43umjwIl9Q==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "FHNOatmUq0sqJOkTx+UF/9YK1f180cnW5FVqnQMvYUN0elp6wFzbtPSiqbo1/ru8ICp43JM1i7kKkk6GsNGHlA==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "OdrZO2WjkiEG6ajEFRABTRCi/wuXQPxeV6g8xvUJqdxMvvuCCEk86zPla8UiIQJz3durtUEbNyY/3lIhS0yZvQ==", + "dependencies": { + "System.Text.Encodings.Web": "8.0.0" + } + }, + "System.Threading.Channels": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA==" + }, + "cs2launcher.aspnetcore.app": { + "type": "Project", + "dependencies": { + "Microsoft.AspNetCore.Components.WebAssembly": "[8.0.4, )", + "Microsoft.AspNetCore.SignalR.Client": "[8.0.4, )", + "Microsoft.AspNetCore.SignalR.Protocols.MessagePack": "[8.0.4, )", + "Nito.AsyncEx.Coordination": "[5.1.2, )" + } + } + }, + "net8.0/browser-wasm": { + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" + } + } + } +} \ No newline at end of file diff --git a/sample/Directory.Build.props b/sample/Directory.Build.props new file mode 100644 index 0000000..cc07614 --- /dev/null +++ b/sample/Directory.Build.props @@ -0,0 +1,14 @@ + + + + true + $(DefaultItemExcludesInProjectFolder);packages.lock.json;package.json;package-lock.json + enable + false + latest + enable + true + net8.0 + + + \ No newline at end of file diff --git a/sample/CS2Launcher.Sample.csproj b/sample/Launcher/CS2Launcher.Sample.Launcher.csproj similarity index 51% rename from sample/CS2Launcher.Sample.csproj rename to sample/Launcher/CS2Launcher.Sample.Launcher.csproj index 5d42164..aa6f09b 100644 --- a/sample/CS2Launcher.Sample.csproj +++ b/sample/Launcher/CS2Launcher.Sample.Launcher.csproj @@ -1,21 +1,16 @@ - true - enable true - latest - enable Speed linux-x64;win-x64 - true - net8.0 partial CS2LAUNCHER-SAMPLE-17947b4b-b2a8-4c6d-a8ae-8eea3c24f3c0 - + + \ No newline at end of file diff --git a/sample/Program.cs b/sample/Launcher/Program.cs similarity index 65% rename from sample/Program.cs rename to sample/Launcher/Program.cs index 92e2898..380ef83 100644 --- a/sample/Program.cs +++ b/sample/Launcher/Program.cs @@ -1,7 +1,10 @@ using CS2Launcher.AspNetCore.Launcher; using CS2Launcher.AspNetCore.Launcher.Proc; +using CS2Launcher.Sample.App; + +var builder = CS2LauncherApplication.CreateBuilder( args ) + .WithLauncherApp(); -var builder = CS2LauncherApplication.CreateBuilder( args ); builder.Services.Configure( options => options.Enabled = false ); await using var app = builder.Build(); diff --git a/sample/Properties/launchSettings.json b/sample/Launcher/Properties/launchSettings.json similarity index 100% rename from sample/Properties/launchSettings.json rename to sample/Launcher/Properties/launchSettings.json diff --git a/sample/appsettings.Development.json b/sample/Launcher/appsettings.Development.json similarity index 100% rename from sample/appsettings.Development.json rename to sample/Launcher/appsettings.Development.json diff --git a/sample/appsettings.json b/sample/Launcher/appsettings.json similarity index 100% rename from sample/appsettings.json rename to sample/Launcher/appsettings.json diff --git a/sample/packages.lock.json b/sample/Launcher/packages.lock.json similarity index 98% rename from sample/packages.lock.json rename to sample/Launcher/packages.lock.json index 1f08737..d3b2b18 100644 --- a/sample/packages.lock.json +++ b/sample/Launcher/packages.lock.json @@ -472,6 +472,13 @@ "CoreRCON.Extensions.CounterStrike": "[1.0.0, )", "Microsoft.AspNetCore.Components.WebAssembly.Server": "[8.0.4, )" } + }, + "cs2launcher.sample.app": { + "type": "Project", + "dependencies": { + "CS2Launcher.AspNetCore.App": "[1.0.0, )", + "Microsoft.AspNetCore.Components.WebAssembly": "[8.0.4, )" + } } }, "net8.0/linux-x64": { diff --git a/src/App/Abstractions/ConsoleSignals.cs b/src/App/Abstractions/ConsoleSignals.cs index 5360e25..15485a5 100644 --- a/src/App/Abstractions/ConsoleSignals.cs +++ b/src/App/Abstractions/ConsoleSignals.cs @@ -2,11 +2,25 @@ namespace CS2Launcher.AspNetCore.App.Abstractions; +/// Signals used by the 'Console' feature. public static class ConsoleSignals { + /// Represents the 'Connected' signal. + /// The host that's been connected. public sealed record Connected( string Host ) : Signal; + /// Represents the 'ExecuteCommand' signal. + /// The command to be executed. + /// A unique identifier used for traking the signal. public sealed record ExecuteCommand( string Command, long Token ) : Signal; + + /// Represents the 'ExecutedCommand' signal. + /// The result of executing a command. + /// A unique identifier used for traking the signal. public sealed record ExecutedCommand( string Text, long Token ) : Signal; + + /// Represents the 'ExecuteCommandFailed' signal. + /// The result of executing a command. + /// A unique identifier used for traking the signal. public sealed record ExecuteCommandFailed( string Error, long Token ) : Signal; } \ No newline at end of file diff --git a/src/App/Abstractions/HostContext.cs b/src/App/Abstractions/HostContext.cs deleted file mode 100644 index bed315c..0000000 --- a/src/App/Abstractions/HostContext.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Reflection; - -namespace CS2Launcher.AspNetCore.App.Abstractions; - -public abstract class HostContext( Assembly host ) -{ - public string AssemblyName { get; } = host.GetName().Name!; -} \ No newline at end of file diff --git a/src/App/AppServiceExtensions.cs b/src/App/AppServiceExtensions.cs index fdcea57..c21a9b7 100644 --- a/src/App/AppServiceExtensions.cs +++ b/src/App/AppServiceExtensions.cs @@ -1,22 +1,20 @@ using System.Diagnostics.CodeAnalysis; +using CS2Launcher.AspNetCore.App.Hosting; using CS2Launcher.AspNetCore.App.Interop; -using CS2Launcher.AspNetCore.App.Shared; +using Microsoft.Extensions.DependencyInjection; namespace CS2Launcher.AspNetCore.App; +/// Extensions for registering services required by the CS2Launcher App. public static class AppServiceExtensions { - [DynamicDependency( DynamicallyAccessedMemberTypes.All, typeof( AppRoot ) )] - [DynamicDependency( DynamicallyAccessedMemberTypes.All, typeof( Console ) )] - [DynamicDependency( DynamicallyAccessedMemberTypes.All, typeof( Layout ) )] - public static IServiceCollection AddCS2LauncherApp( this IServiceCollection services ) + /// Add services required by the CS2Launcher App. + /// The type of of the app. + public static IServiceCollection AddCS2LauncherApp<[DynamicallyAccessedMembers( DynamicallyAccessedMemberTypes.All )] TRoot>( this IServiceCollection services ) + where TRoot : RootComponent { ArgumentNullException.ThrowIfNull( services ); - - // EnsureRuntimeRefs(); - return services.AddScoped(); + return services.AddSingleton( new RootComponentDescriptor( typeof( TRoot ) ) ) + .AddScoped(); } - - // [DynamicDependency( DynamicallyAccessedMemberTypes.All, typeof( AppRoot ) )] - // internal static void EnsureRuntimeRefs( ) => typeof( AppRoot ).GetTypeInfo(); } \ No newline at end of file diff --git a/src/App/CS2Launcher.AspNetCore.App.csproj b/src/App/CS2Launcher.AspNetCore.App.csproj index 0098db8..112d174 100644 --- a/src/App/CS2Launcher.AspNetCore.App.csproj +++ b/src/App/CS2Launcher.AspNetCore.App.csproj @@ -1,14 +1,11 @@ - + - true + Blazor Runtime Components for CS2 Launcher. true + true true - true - - Default - true - true + README.md @@ -19,6 +16,9 @@ + + + diff --git a/src/App/Hosting/RootComponentDescriptor.cs b/src/App/Hosting/RootComponentDescriptor.cs new file mode 100644 index 0000000..c4990dd --- /dev/null +++ b/src/App/Hosting/RootComponentDescriptor.cs @@ -0,0 +1,7 @@ +using System.Diagnostics.CodeAnalysis; + +namespace CS2Launcher.AspNetCore.App.Hosting; + +/// Represents metadata about the root component. +/// The type of . +public sealed record RootComponentDescriptor( [DynamicallyAccessedMembers( DynamicallyAccessedMemberTypes.All )] Type ComponentType ); \ No newline at end of file diff --git a/src/App/Hosting/WebAssemblyHostBuilderExtensions.cs b/src/App/Hosting/WebAssemblyHostBuilderExtensions.cs new file mode 100644 index 0000000..18e26bf --- /dev/null +++ b/src/App/Hosting/WebAssemblyHostBuilderExtensions.cs @@ -0,0 +1,24 @@ +using System.Diagnostics.CodeAnalysis; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Microsoft.Extensions.Logging; + +namespace CS2Launcher.AspNetCore.App.Hosting; + +/// Extensions for configuring a CS2Launcher App in a Web Assembly Host. +public static class WebAssemblyHostBuilderExtensions +{ + /// Configure the Web Assembly Host to use the CS2Launcher App with the root component of type . + /// The type of . + public static WebAssemblyHostBuilder UseCS2Launcher<[DynamicallyAccessedMembers( DynamicallyAccessedMemberTypes.All )] TRoot>( this WebAssemblyHostBuilder builder ) + where TRoot : RootComponent + { + ArgumentNullException.ThrowIfNull( builder ); + + builder.Logging.SetMinimumLevel( builder.HostEnvironment.IsDevelopment() + ? LogLevel.Information + : LogLevel.Error ); + + builder.Services.AddCS2LauncherApp(); + return builder; + } +} \ No newline at end of file diff --git a/src/App/Infrastructure/Signaler.cs b/src/App/Infrastructure/Signaler.cs index 77b3279..62e1924 100644 --- a/src/App/Infrastructure/Signaler.cs +++ b/src/App/Infrastructure/Signaler.cs @@ -3,6 +3,8 @@ using CS2Launcher.AspNetCore.App.Infrastructure; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.SignalR.Client; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; internal abstract class Signaler( Action configure ) : IAsyncDisposable { diff --git a/src/App/Interop/Interop.cs b/src/App/Interop/Interop.cs index 852273a..dd9b52c 100644 --- a/src/App/Interop/Interop.cs +++ b/src/App/Interop/Interop.cs @@ -6,7 +6,7 @@ namespace CS2Launcher.AspNetCore.App.Interop; internal abstract class Interop( IJSRuntime runtime, string moduleName ) : IAsyncDisposable { - public string ModulePath => $"/Interop/{moduleName}.module.js"; + public string ModulePath => $"/_content/CS2Launcher.AspNetCore.App/Interop/{moduleName}.module.js"; private readonly AsyncLock moduleLock = new(); diff --git a/src/App/Program.cs b/src/App/Program.cs deleted file mode 100644 index 18d6c78..0000000 --- a/src/App/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using CS2Launcher.AspNetCore.App; -using CS2Launcher.AspNetCore.App.Abstractions; -using Microsoft.AspNetCore.Components.WebAssembly.Hosting; - -var builder = WebAssemblyHostBuilder.CreateDefault( args ); -builder.Logging.SetMinimumLevel( builder.HostEnvironment.IsDevelopment() ? LogLevel.Information : LogLevel.Error ); - -builder.Services.AddCS2LauncherApp(); - -await using var app = builder.Build(); -Console.WriteLine( $"CS2Launcher v{AppVersion.Value}" ); - -await app.RunAsync(); diff --git a/src/App/AppRoot.razor b/src/App/RootComponent.razor similarity index 50% rename from src/App/AppRoot.razor rename to src/App/RootComponent.razor index 5c2fcfa..992b064 100644 --- a/src/App/AppRoot.razor +++ b/src/App/RootComponent.razor @@ -1,4 +1,4 @@ - + @@ -9,4 +9,17 @@

Nothing to see here...

-
\ No newline at end of file +
+ +@code { + + protected override void OnAfterRender(bool firstRender) + { + base.OnAfterRender(firstRender); + if (firstRender) + { + Console.WriteLine($"CS2Launcher App v{AppVersion.Value}"); + } + } + +} \ No newline at end of file diff --git a/src/App/RootComponent.razor.cs b/src/App/RootComponent.razor.cs new file mode 100644 index 0000000..43482a0 --- /dev/null +++ b/src/App/RootComponent.razor.cs @@ -0,0 +1,6 @@ +using Microsoft.AspNetCore.Components; + +namespace CS2Launcher.AspNetCore.App; + +/// Describes the root component of a CS2Launcher App. +public abstract partial class RootComponent : ComponentBase; \ No newline at end of file diff --git a/src/App/Shared/ClassNames.cs b/src/App/Shared/ClassNames.cs index 83901cc..7c77560 100644 --- a/src/App/Shared/ClassNames.cs +++ b/src/App/Shared/ClassNames.cs @@ -1,9 +1,12 @@ namespace CS2Launcher.AspNetCore.App.Shared; +/// Helpers for combining css class names. public static class ClassNames { + /// Combine the given . public static string Combine( params string[] classNames ) => string.Join( " ", classNames.Select( className => className.Trim() ) ).TrimEnd(); + /// Combine the given with the class value in the given . public static string Combine( IReadOnlyDictionary? attributes, params string[] classNames ) { if( attributes?.TryGetValue( "class", out var value ) is true && value is string @class ) diff --git a/src/App/Shared/Icon.razor.cs b/src/App/Shared/Icon.razor.cs index bc770bb..99d8b14 100644 --- a/src/App/Shared/Icon.razor.cs +++ b/src/App/Shared/Icon.razor.cs @@ -1,5 +1,7 @@ namespace CS2Launcher.AspNetCore.App.Shared; +/// Describes the name of icons. +/// The name of the icon. public readonly struct IconName( string name ) { public static readonly IconName ChevronRight = new( "chevron_right" ); @@ -13,6 +15,7 @@ public readonly struct IconName( string name ) public override string ToString( ) => name; } +/// Describes the desired size of an icon. public enum IconSize { ExtraSmall, diff --git a/src/App/build/CS2Launcher.AspNetCore.App.props b/src/App/build/CS2Launcher.AspNetCore.App.props new file mode 100644 index 0000000..679a09d --- /dev/null +++ b/src/App/build/CS2Launcher.AspNetCore.App.props @@ -0,0 +1,25 @@ + + + + true + true + false + true + true + true + Default + partial + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/src/App/packages.lock.json b/src/App/packages.lock.json index 07f5c43..984900b 100644 --- a/src/App/packages.lock.json +++ b/src/App/packages.lock.json @@ -47,12 +47,6 @@ "resolved": "8.0.4", "contentHash": "PZb5nfQ+U19nhnmnR9T1jw+LTmozhuG2eeuzuW5A7DqxD/UXW2ucjmNJqnqOuh8rdPzM3MQXoF8AfFCedJdCUw==" }, - "Microsoft.NET.Sdk.WebAssembly.Pack": { - "type": "Direct", - "requested": "[8.0.4, )", - "resolved": "8.0.4", - "contentHash": "D4kK4zXW43HvIX0lG9rBGGVzNtY6WwbCGAHJEi6iChU5Dgw8VenJS7Jc1g9Yy8pq4mEQA5PCcZicA16hGTjRpw==" - }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -413,13 +407,6 @@ "resolved": "8.0.0", "contentHash": "CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA==" } - }, - "net8.0/browser-wasm": { - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" - } } } } \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 7214464..cae7de7 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -7,7 +7,7 @@ true - $(DefaultItemExcludesInProjectFolder);packages.lock.json + $(DefaultItemExcludesInProjectFolder);packages.lock.json;package.json;package-lock.json true true false diff --git a/src/Launcher/Abstractions/LauncherAppOptions.cs b/src/Launcher/Abstractions/LauncherAppOptions.cs index 4f4f676..de96e38 100644 --- a/src/Launcher/Abstractions/LauncherAppOptions.cs +++ b/src/Launcher/Abstractions/LauncherAppOptions.cs @@ -1,6 +1,8 @@ namespace CS2Launcher.AspNetCore.Launcher.Abstractions; +/// Represents the options for configuring the CS2Launcher App. public sealed class LauncherAppOptions { + /// The Steam64 User IDs of users permitted access. public List Users { get; set; } = []; } \ No newline at end of file diff --git a/src/Launcher/Api/ConsoleHub.cs b/src/Launcher/Api/ConsoleHub.cs index d5dfbdb..2a4733c 100644 --- a/src/Launcher/Api/ConsoleHub.cs +++ b/src/Launcher/Api/ConsoleHub.cs @@ -10,11 +10,13 @@ namespace CS2Launcher.AspNetCore.Launcher.Api; +/// Hub for handling . [Authorize] public sealed class ConsoleHub( IOptions serverOptionsAccessor ) : Hub { private static readonly object ConsoleKey = new(); + /// Handle the . [HubMethodName( nameof( ConsoleSignals.ExecuteCommand ) )] public async Task ExecuteCommand( ConsoleSignals.ExecuteCommand command ) { @@ -46,9 +48,7 @@ private RCONClient GetOrCreateConsole( ) var options = serverOptionsAccessor.Value; client = new RCONClient( - new IPEndPoint( - IPAddress.Parse( options.Host ), - 27015 ), + new IPEndPoint( IPAddress.Parse( options.Host ), 27015 ), options.RconPassword!, new() { AutoConnect = true } ); diff --git a/src/Launcher/Authorization/AppUserRequirement.cs b/src/Launcher/Authorization/AppUserRequirement.cs index e4d5157..01456f1 100644 --- a/src/Launcher/Authorization/AppUserRequirement.cs +++ b/src/Launcher/Authorization/AppUserRequirement.cs @@ -24,7 +24,7 @@ protected override Task HandleRequirementAsync( AuthorizationHandlerContext cont } } -public sealed class AppUserRequirement : IAuthorizationRequirement; +internal sealed class AppUserRequirement : IAuthorizationRequirement; internal static class SteamClaimsExtensions { diff --git a/src/Launcher/CS2Launcher.AspNetCore.Launcher.csproj b/src/Launcher/CS2Launcher.AspNetCore.Launcher.csproj index 3050461..920c386 100644 --- a/src/Launcher/CS2Launcher.AspNetCore.Launcher.csproj +++ b/src/Launcher/CS2Launcher.AspNetCore.Launcher.csproj @@ -19,7 +19,6 @@ - diff --git a/src/Launcher/CS2LauncherApplication.cs b/src/Launcher/CS2LauncherApplication.cs index 73155c9..090d5dc 100644 --- a/src/Launcher/CS2LauncherApplication.cs +++ b/src/Launcher/CS2LauncherApplication.cs @@ -1,4 +1,15 @@ -using Microsoft.AspNetCore.Builder; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using CS2Launcher.AspNetCore.App; +using CS2Launcher.AspNetCore.App.Hosting; +using CS2Launcher.AspNetCore.Launcher.Abstractions; +using CS2Launcher.AspNetCore.Launcher.Authorization; +using CS2Launcher.AspNetCore.Launcher.Configuration; +using CS2Launcher.AspNetCore.Launcher.Hosting; +using CS2Launcher.AspNetCore.Launcher.Proc; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; @@ -15,22 +26,22 @@ namespace CS2Launcher.AspNetCore.Launcher; public sealed class CS2LauncherApplication : IApplicationBuilder, IAsyncDisposable, IEndpointRouteBuilder, IHost { /// The underlying host of the launcher. - internal readonly WebApplication app; + internal readonly WebApplication WebApp; - IServiceProvider IEndpointRouteBuilder.ServiceProvider => (( IEndpointRouteBuilder )app).ServiceProvider; - ICollection IEndpointRouteBuilder.DataSources => (( IEndpointRouteBuilder )app).DataSources; + IServiceProvider IEndpointRouteBuilder.ServiceProvider => (( IEndpointRouteBuilder )WebApp).ServiceProvider; + ICollection IEndpointRouteBuilder.DataSources => (( IEndpointRouteBuilder )WebApp).DataSources; - IServiceProvider IHost.Services => app.Services; + IServiceProvider IHost.Services => WebApp.Services; - IServiceProvider IApplicationBuilder.ApplicationServices { get => (( IApplicationBuilder )app).ApplicationServices; set => (( IApplicationBuilder )app).ApplicationServices = value; } - IFeatureCollection IApplicationBuilder.ServerFeatures => (( IApplicationBuilder )app).ServerFeatures; - IDictionary IApplicationBuilder.Properties => (( IApplicationBuilder )app).Properties; + IServiceProvider IApplicationBuilder.ApplicationServices { get => (( IApplicationBuilder )WebApp).ApplicationServices; set => (( IApplicationBuilder )WebApp).ApplicationServices = value; } + IFeatureCollection IApplicationBuilder.ServerFeatures => (( IApplicationBuilder )WebApp).ServerFeatures; + IDictionary IApplicationBuilder.Properties => (( IApplicationBuilder )WebApp).Properties; - internal CS2LauncherApplication( WebApplication app ) => this.app = app; + internal CS2LauncherApplication( WebApplication app ) => WebApp = app; - RequestDelegate IApplicationBuilder.Build( ) => (( IApplicationBuilder )app).Build(); + RequestDelegate IApplicationBuilder.Build( ) => (( IApplicationBuilder )WebApp).Build(); - IApplicationBuilder IEndpointRouteBuilder.CreateApplicationBuilder( ) => (( IEndpointRouteBuilder )app).CreateApplicationBuilder(); + IApplicationBuilder IEndpointRouteBuilder.CreateApplicationBuilder( ) => (( IEndpointRouteBuilder )WebApp).CreateApplicationBuilder(); /// Initialize a with pre-configured defaults. /// The command line arguments. @@ -38,28 +49,38 @@ public static CS2LauncherApplicationBuilder CreateBuilder( string[] args ) { var builder = WebApplication.CreateBuilder( args ); builder.Configuration.AddEnvironmentVariables( prefix: "CS2L_" ); - builder.Services.AddCS2Launcher(); + + builder.Services.AddCors() + .AddRequestDecompression() + .AddResponseCaching() + .AddResponseCompression() + .AddRouting( options => options.LowercaseUrls = true ); + + builder.Services.AddHostedService() + .AddOptions() + .BindConfiguration( "Server" ) + .ValidateDataAnnotations(); return new( builder ); } - void IDisposable.Dispose( ) => (app as IDisposable)?.Dispose(); + void IDisposable.Dispose( ) => (WebApp as IDisposable)?.Dispose(); /// - public ValueTask DisposeAsync( ) => app.DisposeAsync(); + public ValueTask DisposeAsync( ) => WebApp.DisposeAsync(); - IApplicationBuilder IApplicationBuilder.New( ) => (( IApplicationBuilder )app).New(); + IApplicationBuilder IApplicationBuilder.New( ) => (( IApplicationBuilder )WebApp).New(); /// Runs the applications. /// A task that completes when the application is shutdown. - /// If the crashes, the host application will be shutdown. - public Task RunAsync( ) => app.RunAsync(); + /// If the crashes, the host application will be shutdown. + public Task RunAsync( ) => WebApp.RunAsync(); - Task IHost.StartAsync( CancellationToken cancellation ) => app.StartAsync( cancellation ); + Task IHost.StartAsync( CancellationToken cancellation ) => WebApp.StartAsync( cancellation ); - Task IHost.StopAsync( CancellationToken cancellation ) => app.StopAsync( cancellation ); + Task IHost.StopAsync( CancellationToken cancellation ) => WebApp.StopAsync( cancellation ); - IApplicationBuilder IApplicationBuilder.Use( Func middleware ) => app.Use( middleware ); + IApplicationBuilder IApplicationBuilder.Use( Func middleware ) => WebApp.Use( middleware ); } /// A builder for a CS2 Launcher and its services. @@ -97,7 +118,34 @@ public sealed class CS2LauncherApplicationBuilder : IHostApplicationBuilder public CS2LauncherApplication Build( ) { var app = builder.Build(); - app.UseCS2Launcher(); + if( app.Environment.IsDevelopment() ) + { + app.UseDeveloperExceptionPage(); + } + else + { + app.UseHsts(); + } + + app.UseHttpsRedirection(); + app.UseCookiePolicy(); + app.UseCors(); + + app.UseRequestDecompression(); + app.UseResponseCaching(); + if( !app.Environment.IsDevelopment() ) + { + app.UseResponseCompression(); + } + + if( app.Services.GetService() is not null ) + { + app.UseLauncherApp(); + } + else + { + app.Map( "/", ( ) => Results.NoContent() ); + } return new( app ); } @@ -106,4 +154,36 @@ public CS2LauncherApplication Build( ) public void ConfigureContainer( IServiceProviderFactory factory, Action? configure = null ) where TContainerBuilder : notnull => (( IHostApplicationBuilder )builder).ConfigureContainer( factory, configure ); + + /// Host the Launcher App with the root component of type . + /// The root component of the Launcher App. + public CS2LauncherApplicationBuilder WithLauncherApp<[DynamicallyAccessedMembers( DynamicallyAccessedMemberTypes.All )] TRoot>( ) + where TRoot : RootComponent + { + Services.AddCS2LauncherApp() + .AddSingleton( LauncherHostContext.From( Assembly.GetEntryAssembly()! ) ) + .AddControllersWithViews(); + + Services.AddAuthorization() + .AddSingleton() + .AddAuthentication( CookieAuthenticationDefaults.AuthenticationScheme ) + .AddCookie() + .AddSteam(); + + Services.AddSignalR() +#if DEBUG + .AddJsonProtocol() +#endif + .AddMessagePackProtocol(); + + Services.AddOptions() + .BindConfiguration( "App" ) + .ValidateDataAnnotations(); + + Services.ConfigureOptions() + .ConfigureOptions() + .ConfigureOptions(); + + return this; + } } \ No newline at end of file diff --git a/src/Launcher/Controllers/AppController.cs b/src/Launcher/Controllers/AppController.cs index ab8a4ac..551b340 100644 --- a/src/Launcher/Controllers/AppController.cs +++ b/src/Launcher/Controllers/AppController.cs @@ -5,11 +5,14 @@ namespace CS2Launcher.AspNetCore.Launcher.Controllers; +/// Default controller for routing to the CS2Launcher App. public sealed class AppController : Controller { + /// Default route for the CS2Launcher App. [Authorize] public ViewResult Index( ) => View( "_App" ); + /// Challenges authentication via Steam. [AllowAnonymous] [HttpGet( "/login" )] public ChallengeResult Login( string returnUrl = "/" ) => Challenge( diff --git a/src/Launcher/Hosting/LauncherHostContext.cs b/src/Launcher/Hosting/LauncherHostContext.cs index 45ca114..364e083 100644 --- a/src/Launcher/Hosting/LauncherHostContext.cs +++ b/src/Launcher/Hosting/LauncherHostContext.cs @@ -1,6 +1,15 @@ using System.Reflection; -using CS2Launcher.AspNetCore.App.Abstractions; namespace CS2Launcher.AspNetCore.Launcher.Hosting; -public sealed class LauncherHostContext( Assembly assembly ) : HostContext( assembly ); \ No newline at end of file +/// Represents metadata about the launcher host. +/// The name of the launcher host's assembly. +public sealed record LauncherHostContext( string AssemblyName ) +{ + /// Create a from the given . + public static LauncherHostContext From( Assembly assembly ) + { + ArgumentNullException.ThrowIfNull( assembly ); + return new( assembly.GetName().Name! ); + } +} \ No newline at end of file diff --git a/src/Launcher/WebApplicationExtensions.cs b/src/Launcher/LauncherApplicationExtensions.cs similarity index 56% rename from src/Launcher/WebApplicationExtensions.cs rename to src/Launcher/LauncherApplicationExtensions.cs index 9f38ec4..1d308d2 100644 --- a/src/Launcher/WebApplicationExtensions.cs +++ b/src/Launcher/LauncherApplicationExtensions.cs @@ -4,36 +4,18 @@ namespace CS2Launcher.AspNetCore.Launcher; -internal static class WebApplicationExtensions +internal static class LauncherApplicationExtensions { - public static WebApplication UseCS2Launcher( this WebApplication app ) + internal static WebApplication UseLauncherApp( this WebApplication app ) { ArgumentNullException.ThrowIfNull( app ); - - // Configure the HTTP request pipeline. if( app.Environment.IsDevelopment() ) { app.UseWebAssemblyDebugging(); - app.UseDeveloperExceptionPage(); - } - else - { - app.UseHsts(); - } - - app.UseHttpsRedirection(); - app.UseCookiePolicy(); - - app.UseRequestDecompression(); - app.UseResponseCaching(); - if( !app.Environment.IsDevelopment() ) - { - app.UseResponseCompression(); } app.UseBlazorFrameworkFiles(); app.UseStaticFiles( new StaticFileOptions { ServeUnknownFileTypes = true } ); - app.UseCors(); app.UseAuthentication(); app.UseAuthorization(); diff --git a/src/Launcher/LauncherServiceExtensions.cs b/src/Launcher/LauncherServiceExtensions.cs deleted file mode 100644 index b1d3f9b..0000000 --- a/src/Launcher/LauncherServiceExtensions.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Reflection; -using AspNet.Security.OpenId.Steam; -using CS2Launcher.AspNetCore.App; -using CS2Launcher.AspNetCore.App.Abstractions; -using CS2Launcher.AspNetCore.Launcher.Abstractions; -using CS2Launcher.AspNetCore.Launcher.Authorization; -using CS2Launcher.AspNetCore.Launcher.Configuration; -using CS2Launcher.AspNetCore.Launcher.Hosting; -using CS2Launcher.AspNetCore.Launcher.Proc; -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Mvc.Authorization; -using Microsoft.Extensions.DependencyInjection; - -namespace CS2Launcher.AspNetCore.Launcher; - -/// Extensions for register services required by launchers. -public static class LauncherServiceExtensions -{ - /// Adds services for CS2 launcher applications. - /// - public static IServiceCollection AddCS2Launcher( this IServiceCollection services ) - { - ArgumentNullException.ThrowIfNull( services ); - - services.AddHostedService() - .AddOptions() - .BindConfiguration( "Server" ) - .ValidateDataAnnotations(); - - services.AddCS2LauncherApp() - .AddControllersWithViews(); - - services.AddAuthentication( CookieAuthenticationDefaults.AuthenticationScheme ) - .AddCookie() - .AddSteam(); - - services.AddAuthorization() - .AddSingleton() - .AddCors() - .AddRequestDecompression() - .AddResponseCaching() - .AddResponseCompression() - .AddRouting( options => options.LowercaseUrls = true ) - .AddSingleton( new LauncherHostContext( Assembly.GetEntryAssembly()! ) ) - .AddTransient( serviceProvider => serviceProvider.GetRequiredService() ); - - services.AddSignalR() -#if DEBUG - .AddJsonProtocol() -#endif - .AddMessagePackProtocol(); - - services.AddOptions() - .BindConfiguration( "App" ) - .ValidateDataAnnotations(); - - return services.ConfigureOptions() - .ConfigureOptions() - .ConfigureOptions(); - } -} \ No newline at end of file diff --git a/src/Launcher/Views/Shared/_App.cshtml b/src/Launcher/Views/Shared/_App.cshtml index 062cbb9..a7698e4 100644 --- a/src/Launcher/Views/Shared/_App.cshtml +++ b/src/Launcher/Views/Shared/_App.cshtml @@ -1,8 +1,10 @@ +@using CS2Launcher.AspNetCore.App.Hosting @using CS2Launcher.AspNetCore.Launcher.Hosting @using Microsoft.AspNetCore.Components.Web @using Microsoft.Extensions.Hosting @inject LauncherHostContext LauncherHost +@inject RootComponentDescriptor RootComponent @@ -15,11 +17,7 @@ - - - - @@ -28,14 +26,13 @@ - - +