server-master/srcs/GameChannel/Program.cs

210 lines
No EOL
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using dotenv.net;
using GameChannel.Network;
using GameChannel.Services;
using GameChannel.Utils;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using PhoenixLib.Events;
using PhoenixLib.Logging;
using PhoenixLib.ServiceBus.MQTT;
using WingsAPI.Communication;
using WingsAPI.Communication.ServerApi;
using WingsAPI.Communication.ServerApi.Protocol;
using WingsAPI.Communication.Sessions;
using WingsAPI.Plugins;
using WingsEmu.Game.Commands;
using WingsEmu.Game.Managers;
using WingsEmu.Game.Miniland;
using WingsEmu.Packets;
namespace GameChannel
{
public class Program
{
public static async Task Main(string[] args)
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US");
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("en-US");
// workaround
// http2 needs SSL
// https://github.com/grpc/grpc-dotnet/issues/626
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
PrintHeader();
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
DotEnv.Load(new DotEnvOptions(true, new[] { "world.env" }, Encoding.UTF8));
using IHost httpServer = CreateHostBuilder(args).Build();
{
IServiceProvider services = httpServer.Services;
IServerApiService serverApiService = services.GetRequiredService<IServerApiService>();
BasicRpcResponse response = null;
while (response == null)
{
try
{
response = await serverApiService.IsMasterOnline(new EmptyRpcRequest());
}
catch (Exception e)
{
Log.Warn("Failed to contact with Master Server, retrying...");
await Task.Delay(TimeSpan.FromSeconds(2));
}
}
GameSession.Initialize(
services.GetService<IGlobalCommandExecutor>(),
services.GetService<IAsyncEventPipeline>(),
services.GetService<IServerManager>(),
services.GetService<ISessionManager>(),
services.GetService<ISessionService>(),
services.GetRequiredService<IMinilandManager>(),
services.GetRequiredService<IPacketDeserializer>()
);
SerializableGameServer gameServer = services.GetRequiredService<SerializableGameServer>();
IEnumerable<IGamePlugin> plugins = services.GetServices<IGamePlugin>();
foreach (IGamePlugin gamePlugin in plugins)
{
gamePlugin.OnLoad();
}
IServerManager serverManager = services.GetRequiredService<IServerManager>();
StaticServerManager.Initialize(serverManager);
serverManager.InitializeAsync();
using var stopService = new DockerGracefulStopService();
GameTcpServer server;
portLoop:
try
{
Log.Warn($"Binding TCP port {gameServer.EndPointPort}");
server = new GameTcpServer(
IPAddress.Any,
gameServer.EndPointPort,
new SmartSpamProtector(),
serverManager, httpServer.Services.GetRequiredService<GameSessionFactory>());
server.Start();
}
catch (SocketException ex)
{
if (ex.ErrorCode == 10048)
{
gameServer.EndPointPort++;
Log.Info("Port already in use! Incrementing...");
goto portLoop;
}
Log.Error("General Error", ex);
Environment.Exit(1);
return;
}
await httpServer.StartAsync();
IMessagingService messagingService = services.GetRequiredService<IMessagingService>();
await messagingService.StartAsync();
Log.Warn($"Registering GameChannel [{gameServer.WorldGroup}] {gameServer.EndPointIp}:{gameServer.EndPointPort}");
BasicRpcResponse response2 = null;
try
{
response2 = await serverApiService.RegisterWorldServer(new RegisterWorldServerRequest
{
GameServer = gameServer
});
}
catch (Exception e)
{
server.Stop();
Log.Error("Could not register channel. Already running?", e);
Console.ReadKey();
}
if (response2?.ResponseType != RpcResponseType.SUCCESS)
{
Log.Info($"New channel: {gameServer.ChannelId.ToString()} registered");
}
Log.Warn($"[GAME_CHANNEL] Started as : {gameServer.WorldGroup}:{gameServer.ChannelId}");
serverManager.PutIdle();
GameChannelStopService serverShutdown = services.GetRequiredService<GameChannelStopService>();
serverManager.ListenCancellation(stopService.TokenSource);
await httpServer.WaitForShutdownAsync(stopService.CancellationToken);
await serverShutdown.StopAsync();
Log.Warn("Properly shutting down server...");
server?.Stop();
await httpServer?.StopAsync();
await messagingService.DisposeAsync();
Serilog.Log.CloseAndFlush();
}
}
// For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682
// Additional configuration is required to successfully run gRPC on macOS.
private static IHostBuilder CreateHostBuilder(string[] args)
{
IHostBuilder hostBuilder = Host.CreateDefaultBuilder(args);
hostBuilder.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseSetting(WebHostDefaults.ServerUrlsKey, string.Empty);
webBuilder.ConfigureKestrel(s =>
{
s.ListenAnyIP(Convert.ToInt32(Environment.GetEnvironmentVariable("HTTP_LISTEN_PORT") ?? "17500"),
options => { options.Protocols = HttpProtocols.Http1AndHttp2; });
});
webBuilder.UseStartup<Startup>();
});
return hostBuilder;
}
private static void PrintHeader()
{
Console.Title = "WingsEmu - World";
const string text = @"
██╗ ██╗██╗███╗ ██╗ ██████╗ ███████╗███████╗███╗ ███╗██╗ ██╗
██║ ██║██║████╗ ██║██╔════╝ ██╔════╝██╔════╝████╗ ████║██║ ██║
██║ █╗ ██║██║██╔██╗ ██║██║ ███╗███████╗█████╗ ██╔████╔██║██║ ██║
██║███╗██║██║██║╚██╗██║██║ ██║╚════██║██╔══╝ ██║╚██╔╝██║██║ ██║
╚███╔███╔╝██║██║ ╚████║╚██████╔╝███████║███████╗██║ ╚═╝ ██║╚██████╔╝
╚══╝╚══╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝
██████╗ █████╗ ███╗ ███╗███████╗ ███████╗███████╗██████╗ ██╗ ██╗███████╗██████╗
██╔════╝ ██╔══██╗████╗ ████║██╔════╝ ██╔════╝██╔════╝██╔══██╗██║ ██║██╔════╝██╔══██╗
██║ ███╗███████║██╔████╔██║█████╗█████╗███████╗█████╗ ██████╔╝██║ ██║█████╗ ██████╔╝
██║ ██║██╔══██║██║╚██╔╝██║██╔══╝╚════╝╚════██║██╔══╝ ██╔══██╗╚██╗ ██╔╝██╔══╝ ██╔══██╗
╚██████╔╝██║ ██║██║ ╚═╝ ██║███████╗ ███████║███████╗██║ ██║ ╚████╔╝ ███████╗██║ ██║
╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝
";
string separator = new('=', Console.WindowWidth);
string logo = text.Split('\n')
.Select(s => string.Format("{0," + (Console.WindowWidth / 2 + s.Length / 2) + "}\n", s))
.Aggregate("", (current, i) => current + i);
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(separator + logo + $"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n" + separator);
Console.ForegroundColor = ConsoleColor.White;
}
}
}