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 @@
-
-
+