diff --git a/A2SService.sln b/A2SService.sln
index 1feeba9..4ca4cef 100644
--- a/A2SService.sln
+++ b/A2SService.sln
@@ -1,31 +1,47 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.8.34525.116
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "A2SService", "A2SService\A2SService.csproj", "{64F34E17-19DD-47B0-BF4B-5C7BD34B99E5}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{2C00A9D7-DC9F-4A9F-8A34-E5AC09604B4E}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {64F34E17-19DD-47B0-BF4B-5C7BD34B99E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {64F34E17-19DD-47B0-BF4B-5C7BD34B99E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {64F34E17-19DD-47B0-BF4B-5C7BD34B99E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {64F34E17-19DD-47B0-BF4B-5C7BD34B99E5}.Release|Any CPU.Build.0 = Release|Any CPU
- {2C00A9D7-DC9F-4A9F-8A34-E5AC09604B4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2C00A9D7-DC9F-4A9F-8A34-E5AC09604B4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2C00A9D7-DC9F-4A9F-8A34-E5AC09604B4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2C00A9D7-DC9F-4A9F-8A34-E5AC09604B4E}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {73DD4E4E-B4EB-472F-93D9-1AE45DFF4741}
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.8.34525.116
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "A2SService", "A2SService\A2SService.csproj", "{64F34E17-19DD-47B0-BF4B-5C7BD34B99E5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests", "UnitTests\UnitTests.csproj", "{2C00A9D7-DC9F-4A9F-8A34-E5AC09604B4E}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{41CD9BD9-5437-40D2-A8F1-D3CCB5AA8AA9}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ .gitignore = .gitignore
+ common.props = common.props
+ LICENSE = LICENSE
+ NuGet.Config = NuGet.Config
+ README.md = README.md
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FakeA2SServer", "FakeA2SServer\FakeA2SServer.csproj", "{7402E4BF-BEEE-4EBA-B016-B8CBFE62862F}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {64F34E17-19DD-47B0-BF4B-5C7BD34B99E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {64F34E17-19DD-47B0-BF4B-5C7BD34B99E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {64F34E17-19DD-47B0-BF4B-5C7BD34B99E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {64F34E17-19DD-47B0-BF4B-5C7BD34B99E5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2C00A9D7-DC9F-4A9F-8A34-E5AC09604B4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2C00A9D7-DC9F-4A9F-8A34-E5AC09604B4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2C00A9D7-DC9F-4A9F-8A34-E5AC09604B4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2C00A9D7-DC9F-4A9F-8A34-E5AC09604B4E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7402E4BF-BEEE-4EBA-B016-B8CBFE62862F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7402E4BF-BEEE-4EBA-B016-B8CBFE62862F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7402E4BF-BEEE-4EBA-B016-B8CBFE62862F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7402E4BF-BEEE-4EBA-B016-B8CBFE62862F}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {73DD4E4E-B4EB-472F-93D9-1AE45DFF4741}
+ EndGlobalSection
+EndGlobal
diff --git a/A2SService/A2SServer.cs b/A2SService/A2SServer.cs
index f548dc9..22266af 100644
--- a/A2SService/A2SServer.cs
+++ b/A2SService/A2SServer.cs
@@ -60,11 +60,16 @@ public async ValueTask StartAsync(CancellationToken cancellationToken)
while (true)
{
- UdpReceiveResult message = await Server.ReceiveAsync(cancellationToken);
+ try
+ {
+ UdpReceiveResult message = await Server.ReceiveAsync(cancellationToken);
-#pragma warning disable CA2012
- _ = HandleAsync(message, cancellationToken);
-#pragma warning restore CA2012
+ ValueTask _ = HandleAsync(message, cancellationToken);
+ }
+ catch (Exception) when (!cancellationToken.IsCancellationRequested)
+ {
+
+ }
}
// ReSharper disable once FunctionNeverReturns
}
diff --git a/A2SService/A2SService.csproj b/A2SService/A2SService.csproj
index f6986ab..9a64922 100644
--- a/A2SService/A2SService.csproj
+++ b/A2SService/A2SService.csproj
@@ -15,7 +15,7 @@
true
snupkg
Steam game server impl
- README.md
+ README.md
diff --git a/FakeA2SServer/A2SServerService.cs b/FakeA2SServer/A2SServerService.cs
new file mode 100644
index 0000000..2febb59
--- /dev/null
+++ b/FakeA2SServer/A2SServerService.cs
@@ -0,0 +1,65 @@
+namespace FakeA2SServer;
+
+[UsedImplicitly]
+public class A2SServerService : ITransientDependency
+{
+ public required IAbpLazyServiceProvider LazyServiceProvider { get; [UsedImplicitly] init; }
+
+ private ILogger Logger => LazyServiceProvider.LazyGetRequiredService>();
+
+ private IConfiguration Configuration => LazyServiceProvider.LazyGetRequiredService();
+
+ private readonly CancellationTokenSource _cts = new();
+
+ private A2SServer? _server;
+
+ public async ValueTask StartAsync()
+ {
+ IPEndPoint serverAddress = IPEndPoint.Parse(Configuration.GetValue(@"A2SListenEndpoint", @"[::]:27015")!);
+
+ _server = new A2SServer(serverAddress);
+ if (Equals(serverAddress.Address, IPAddress.IPv6Any))
+ {
+ _server.Server.Client.DualMode = true;
+ }
+
+ const string prefix = @"A2S";
+
+ _server.A2SInfo = new A2SInfo
+ {
+ Protocol = Configuration.GetValue(prefix + nameof(A2SInfo.Protocol)),
+ Name = Configuration.GetValue(prefix + nameof(A2SInfo.Name)),
+ Map = Configuration.GetValue(prefix + nameof(A2SInfo.Map)),
+ Folder = Configuration.GetValue(prefix + nameof(A2SInfo.Folder)),
+ Game = Configuration.GetValue(prefix + nameof(A2SInfo.Game)),
+ ID = (short)Configuration.GetValue(prefix + nameof(A2SInfo.ID)),
+ Players = Configuration.GetValue(prefix + nameof(A2SInfo.Players)),
+ MaxPlayers = Configuration.GetValue(prefix + nameof(A2SInfo.MaxPlayers)),
+ Bots = Configuration.GetValue(prefix + nameof(A2SInfo.Bots)),
+ ServerType = Configuration.GetValue(prefix + nameof(A2SInfo.ServerType), A2SType.Dedicated),
+ Environment = Configuration.GetValue(prefix + nameof(A2SInfo.Environment), _server.A2SInfo.Environment),
+ Visibility = Configuration.GetValue(prefix + nameof(A2SInfo.Visibility)),
+ Vac = Configuration.GetValue(prefix + nameof(A2SInfo.Vac)),
+ Version = Configuration.GetValue(prefix + nameof(A2SInfo.Version)),
+ Port = (short)Configuration.GetValue(prefix + nameof(A2SInfo.Port), (ushort)serverAddress.Port),
+ SteamID = (long)Configuration.GetValue(prefix + nameof(A2SInfo.SteamID)),
+ SourceTvPort = (short)Configuration.GetValue(prefix + nameof(A2SInfo.SourceTvPort)),
+ SourceTvName = Configuration.GetValue(prefix + nameof(A2SInfo.SourceTvName)),
+ Keywords = Configuration.GetValue(prefix + nameof(A2SInfo.Keywords)),
+ GameID = (long)Configuration.GetValue(prefix + nameof(A2SInfo.GameID))
+ };
+
+ _cts.Token.Register(() => _server.Dispose());
+
+ ValueTask _ = _server.StartAsync(_cts.Token);
+
+ Logger.LogInformation(@"A2S Server listen on {endpoint}: {info}", serverAddress, _server.A2SInfo);
+
+ await ValueTask.CompletedTask;
+ }
+
+ public async ValueTask StopAsync()
+ {
+ await _cts.CancelAsync();
+ }
+}
diff --git a/FakeA2SServer/FakeA2SServer.csproj b/FakeA2SServer/FakeA2SServer.csproj
new file mode 100644
index 0000000..71c746e
--- /dev/null
+++ b/FakeA2SServer/FakeA2SServer.csproj
@@ -0,0 +1,21 @@
+
+
+
+
+
+ Exe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FakeA2SServer/FakeA2SServerHostedService.cs b/FakeA2SServer/FakeA2SServerHostedService.cs
new file mode 100644
index 0000000..a3561a9
--- /dev/null
+++ b/FakeA2SServer/FakeA2SServerHostedService.cs
@@ -0,0 +1,18 @@
+namespace FakeA2SServer;
+
+public class FakeA2SServerHostedService : IHostedService
+{
+ public required IAbpLazyServiceProvider LazyServiceProvider { get; [UsedImplicitly] init; }
+
+ private A2SServerService Service => LazyServiceProvider.LazyGetRequiredService();
+
+ public async Task StartAsync(CancellationToken cancellationToken)
+ {
+ await Service.StartAsync();
+ }
+
+ public async Task StopAsync(CancellationToken cancellationToken)
+ {
+ await Service.StopAsync();
+ }
+}
diff --git a/FakeA2SServer/FakeA2SServerModule.cs b/FakeA2SServer/FakeA2SServerModule.cs
new file mode 100644
index 0000000..ee068d8
--- /dev/null
+++ b/FakeA2SServer/FakeA2SServerModule.cs
@@ -0,0 +1,22 @@
+global using A2SService;
+global using FakeA2SServer;
+global using JetBrains.Annotations;
+global using Microsoft.Extensions.Configuration;
+global using Microsoft.Extensions.DependencyInjection;
+global using Microsoft.Extensions.Hosting;
+global using Microsoft.Extensions.Logging;
+global using Serilog;
+global using Serilog.Events;
+global using System.Net;
+global using Volo.Abp;
+global using Volo.Abp.Autofac;
+global using Volo.Abp.DependencyInjection;
+global using Volo.Abp.Modularity;
+
+namespace FakeA2SServer;
+
+[DependsOn(
+ typeof(AbpAutofacModule)
+)]
+[UsedImplicitly]
+internal class FakeA2SServerModule : AbpModule;
diff --git a/FakeA2SServer/Program.cs b/FakeA2SServer/Program.cs
new file mode 100644
index 0000000..b5d58ea
--- /dev/null
+++ b/FakeA2SServer/Program.cs
@@ -0,0 +1,45 @@
+Log.Logger = new LoggerConfiguration()
+#if DEBUG
+ .MinimumLevel.Debug()
+#else
+ .MinimumLevel.Information()
+#endif
+ .MinimumLevel.Override(@"Microsoft", LogEventLevel.Information)
+ .MinimumLevel.Override(@"Volo.Abp", LogEventLevel.Warning)
+ .Enrich.FromLogContext()
+ .WriteTo.Async(c => c.Console(outputTemplate: @"[{Timestamp:O}] [{Level}] {Message:lj}{NewLine}{Exception}"))
+ .CreateLogger();
+
+try
+{
+ HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
+
+ builder.Logging.ClearProviders().AddSerilog();
+
+ builder.ConfigureContainer(builder.Services.AddAutofacServiceProviderFactory());
+
+ builder.Services.AddHostedService();
+
+ await builder.Services.AddApplicationAsync();
+
+ using IHost host = builder.Build();
+
+ await host.InitializeAsync();
+
+ await host.RunAsync();
+
+ return 0;
+}
+catch (HostAbortedException)
+{
+ throw;
+}
+catch (Exception ex)
+{
+ Log.Fatal(ex, @"Host terminated unexpectedly!");
+ return 1;
+}
+finally
+{
+ Log.CloseAndFlush();
+}
diff --git a/common.props b/common.props
index 4e80d47..250bbf1 100644
--- a/common.props
+++ b/common.props
@@ -5,5 +5,6 @@
latest
enable
HMBSbige
+ 1.0.0