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