using Cysharp.Threading.Tasks; using ShrinkNetwork; namespace ShrinkNetwork.ServerHost.Framework; public sealed class ShrinkDedicatedServerApp { private readonly List _modules; private readonly ServerModuleContext _context; public ShrinkDedicatedServerApp(ServerHostOptions options, params IShrinkServerModule[] modules) { _modules = modules.ToList(); _context = new ServerModuleContext(options, new ServerAuthStore()); } public ServerModuleContext Context => _context; public async UniTask StartAsync(CancellationToken cancellationToken = default) { if (_context.Options.EnableUnityCodeScan) { var manifest = UnityNetworkCodeScanner.Scan(_context.Options.UnityAssetsPath); UnityNetworkCodeScanner.WriteOutputs(manifest, _context.Options.ScanOutputDirectory); _context.ScanManifest = manifest; } var tcpService = CreateService("TCP"); var kcpService = CreateService("KCP"); _context.RegisterService("TCP", tcpService); _context.RegisterService("KCP", kcpService); foreach (var module in _modules) { module.ConfigureService(_context, tcpService, "TCP"); module.ConfigureService(_context, kcpService, "KCP"); } tcpService.BindTransport(new TcpServerTransport(System.Net.IPAddress.Any, _context.Options.Port, tlsOptions: BuildTcpTlsOptions())); kcpService.BindTransport(new KcpServerTransport(System.Net.IPAddress.Any, _context.Options.Port, new ShrinkKcpTransportOptions { Interval = 10, UpdateIntervalMs = 10, IdleTimeoutMs = 15000, HandshakeRetryMs = 250, ConnectTimeoutMs = 4000 })); foreach (var module in _modules) module.StartAsync(_context, cancellationToken).Forget(); await UniTask.CompletedTask; } private ShrinkNetworkService CreateService(string transportName) { var service = new ShrinkNetworkService(new ShrinkJsonNetworkSerializer(), new ShrinkNetworkMessageRegistry(), new ShrinkNetworkRouter()); service.MinProtocolVersion = _context.Options.MinProtocolVersion; service.MaxProtocolVersion = _context.Options.MaxProtocolVersion; service.MinSchemaVersion = _context.Options.MinSchemaVersion; service.MaxSchemaVersion = _context.Options.MaxSchemaVersion; service.DisconnectOnProtocolViolation = _context.Options.DisconnectOnProtocolViolation; service.IncomingPacketValidator = (session, packet) => ValidateIncomingPacket(service, session, packet); service.OnSessionConnected += session => { session.SetPeerKind(ShrinkNetworkPeerKind.Client); ShrinkNetworkLogger.Info($"[{transportName}] Client connected: {session.SessionId} {session.RemoteAddress}"); }; service.OnSessionDisconnected += session => { _context.AuthStore.Remove(service, session.SessionId); ShrinkNetworkLogger.Info($"[{transportName}] Client disconnected: {session.SessionId}"); }; return service; } private ShrinkIncomingPacketValidationResult ValidateIncomingPacket( ShrinkNetworkService service, ShrinkNetworkSession session, ShrinkNetworkPacket packet) { if (!_context.Options.EnableSessionTokens) return ShrinkIncomingPacketValidationResult.Allow; if (packet.Kind == ShrinkNetworkPacketKind.Response) return ShrinkIncomingPacketValidationResult.Allow; if (string.Equals(packet.Route, AuthServerModule.LoginRoute, StringComparison.Ordinal) || string.Equals(packet.Route, AuthServerModule.RefreshRoute, StringComparison.Ordinal)) return ShrinkIncomingPacketValidationResult.Allow; if (!_context.AuthStore.TryGetState(service, session.SessionId, out _)) return ShrinkIncomingPacketValidationResult.Allow; if (_context.AuthStore.TryValidateSessionToken(service, session.SessionId, packet.SessionToken, out var authState, out var failureReason)) { session.SetSessionToken(authState.SessionToken, authState.SessionTokenExpiresAtUtc); return ShrinkIncomingPacketValidationResult.Allow; } return ShrinkIncomingPacketValidationResult.Reject(failureReason, _context.Options.DisconnectOnInvalidSessionToken); } private ShrinkTcpTlsOptions? BuildTcpTlsOptions() { if (!_context.Options.EnableTcpTls) return null; return new ShrinkTcpTlsOptions { Enabled = true, TargetHost = _context.Options.TcpTlsTargetHost, ServerCertificatePath = _context.Options.TcpTlsCertificatePath, ServerCertificatePassword = _context.Options.TcpTlsCertificatePassword, CheckCertificateRevocation = _context.Options.TcpTlsCheckCertificateRevocation }; } }