using System.Reflection; using ShrinkNetwork; using ShrinkNetwork.ServerHost; using ShrinkNetwork.ServerHost.Framework; ShrinkNetworkLogger.InfoHandler = msg => Console.WriteLine(msg); ShrinkNetworkLogger.WarningHandler = msg => Console.WriteLine("[Warn] " + msg); ShrinkNetworkLogger.ErrorHandler = msg => Console.Error.WriteLine(msg); ShrinkNetworkLogger.ExceptionHandler = ex => Console.Error.WriteLine(ex); var config = ServerHostProperties.LoadOrCreate(); var options = config.Options; var modules = DiscoverModules(); var app = new ShrinkDedicatedServerApp(options, modules); await app.StartAsync(); Console.WriteLine("ShrinkNetwork 独立服务器模板已启动。"); Console.WriteLine($"配置文件:{config.FilePath}"); Console.WriteLine($"首次生成默认配置:{config.CreatedDefaultFile}"); foreach (var warning in config.Warnings) Console.WriteLine($"[Warn] 配置文件:{warning}"); Console.WriteLine($"TCP: 0.0.0.0:{options.Port}"); Console.WriteLine($"KCP: 0.0.0.0:{options.Port}"); Console.WriteLine($"Unity 扫描输出目录:{options.ScanOutputDirectory}"); Console.WriteLine($"Unity 资产扫描目录:{options.UnityAssetsPath}"); Console.WriteLine($"共享口令鉴权已启用:{!string.IsNullOrWhiteSpace(options.SharedAuthToken)}"); Console.WriteLine($"缺少口令时允许匿名登录:{options.AllowAnonymousWhenAuthTokenMissing}"); Console.WriteLine($"会话令牌已启用:{options.EnableSessionTokens}"); Console.WriteLine($"会话令牌 TTL(秒):{options.SessionTokenTtlSeconds}"); Console.WriteLine($"会话令牌续期窗口(秒):{options.SessionTokenRefreshWindowSeconds}"); Console.WriteLine($"会话令牌校验失败时断开会话:{options.DisconnectOnInvalidSessionToken}"); Console.WriteLine($"协议版本范围:{options.MinProtocolVersion}-{options.MaxProtocolVersion}"); Console.WriteLine($"Schema 版本范围:{options.MinSchemaVersion}-{options.MaxSchemaVersion}"); Console.WriteLine($"TCP TLS 已启用:{options.EnableTcpTls}"); if (options.EnableTcpTls) { Console.WriteLine($"TCP TLS 证书路径:{options.TcpTlsCertificatePath}"); Console.WriteLine($"TCP TLS SNI/目标主机:{options.TcpTlsTargetHost}"); } if (string.IsNullOrWhiteSpace(options.SharedAuthToken) && !options.AllowAnonymousWhenAuthTokenMissing) { Console.WriteLine("[Warn] 当前未配置 SHRINK_SERVER_AUTH_TOKEN,登录请求默认会被拒绝。"); } Console.WriteLine("[Info] 配置优先级:代码默认值 < server.properties < 环境变量"); Console.WriteLine("已加载模块:"); foreach (var module in modules) Console.WriteLine($"- {module.Name}"); Console.WriteLine("按 Ctrl+C 退出。"); using var metricsCts = new CancellationTokenSource(); if (options.DiagnosticsLogIntervalSeconds > 0) _ = RunDiagnosticsLoopAsync(app, options.DiagnosticsLogIntervalSeconds, metricsCts.Token); Console.CancelKeyPress += (_, args) => { args.Cancel = true; metricsCts.Cancel(); DumpDiagnostics(app); Environment.Exit(0); }; try { await Task.Delay(Timeout.Infinite, metricsCts.Token); } catch (OperationCanceledException) { } static IShrinkServerModule[] DiscoverModules() { return Assembly.GetExecutingAssembly() .GetTypes() .Where(type => typeof(IShrinkServerModule).IsAssignableFrom(type)) .Where(type => !type.IsAbstract && !type.IsInterface) .Where(type => type.GetConstructor(Type.EmptyTypes) != null) .Select(type => (IShrinkServerModule)Activator.CreateInstance(type)!) .OrderBy(module => module.Name, StringComparer.Ordinal) .ToArray(); } static async Task RunDiagnosticsLoopAsync(ShrinkDedicatedServerApp app, int intervalSeconds, CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { await Task.Delay(TimeSpan.FromSeconds(intervalSeconds), cancellationToken); } catch (OperationCanceledException) { break; } DumpDiagnostics(app); } } static void DumpDiagnostics(ShrinkDedicatedServerApp app) { Console.WriteLine("[Metrics] ShrinkNetwork service snapshots:"); foreach (var pair in app.Context.Services.OrderBy(item => item.Key, StringComparer.OrdinalIgnoreCase)) { var metrics = pair.Value.GetDiagnosticsSnapshot(); Console.WriteLine( $"[Metrics][{pair.Key}] sessions={metrics.CurrentSessions} connected={metrics.SessionsConnected} disconnected={metrics.SessionsDisconnected} sent={metrics.PacketsSent}/{metrics.BytesSent}B recv={metrics.PacketsReceived}/{metrics.BytesReceived}B rpc={metrics.RpcStarted}/{metrics.RpcCompleted} timeout={metrics.RpcTimedOut} canceled={metrics.RpcCanceled} failed={metrics.RpcFailed} proto={metrics.ProtocolViolations} authRejected={metrics.AuthRejectedCount} denied={metrics.PermissionDeniedCount} handlerEx={metrics.HandlerExceptionCount} unknownOpcode={metrics.UnknownOpcodeCount} dispatchMiss={metrics.DispatchMissCount} serialization={metrics.SerializationErrorCount} queueReject={metrics.DispatchQueueRejectedCount}"); } }