Files
squad-rain-ops-mini/Program.cs

945 lines
37 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Microsoft.AspNetCore.Builder;
using SqlSugar;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.FileProviders;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication;
using System.Security.Claims;
using System.Reflection;
using RainOpsMini.Models;
using System.Text.Json;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;
using System;
using System.IO;
using RainOpsMini.Helpers.SquadGameLog;
using RainOpsMini.Helpers;
using RainOpsMini.Helpers.RCON;
using RainOpsMini.Services;
using RainOpsMini.Plugins;
using RainOpsMini.Hubs;
using Microsoft.AspNetCore.SignalR;
using RainOpsMini.Apis;
namespace RainOpsMini
{
using RainOpsMini.Models;
using RainOpsMini;
public class Program
{
/// <summary>
/// RCON配置类用于存储连接信息和Web服务配置
/// </summary>
public class RconConfig
{
/// <summary>
/// RCON服务器IP地址
/// </summary>
public string Ip { get; set; } = "";
/// <summary>
/// RCON端口
/// </summary>
public int Port { get; set; }
/// <summary>
/// RCON密码
/// </summary>
public string Password { get; set; } = "";
/// <summary>
/// Web管理界面端口默认5000
/// </summary>
public int WebPort { get; set; } = 5000;
/// <summary>
/// Web管理界面用户名
/// </summary>
public string WebUser { get; set; } = "";
/// <summary>
/// Web管理界面密码
/// </summary>
public string WebPassword { get; set; } = "";
}
// 全局配置实例
public static RconConfig Config = new RconConfig();
// 配置文件路径
public static string ConfigPath = "rcon_Config.json";
// RCON客户端实例
static RconClient? client;
// 公开访问的RCON客户端属性
public static RconClient? Client => client;
// SignalR Hub上下文用于发送实时消息
public static IHubContext<ChatHub>? ChatHubContext { get; set; }
// 静态缓存实例在静态上下文和DI容器之间共享
public static RconDataCache RconCache = new RconDataCache();
// 插件监听原始RCON消息的事件
public static event Action<string>? OnRconMessageReceived;
// 系统是否已配置标记
public static bool isConfigured = false;
static async Task Main(string[] args)
{
// 1. 初始化数据库
// 确保数据库表结构存在,这是程序运行的基础
try
{
Console.WriteLine("====== STARTING DATABASE INITIALIZATION ======");
DbHelper.InitDatabase();
Console.WriteLine("====== DATABASE INITIALIZATION COMPLETED ======");
}
catch (Exception ex)
{
// 数据库初始化失败,打印严重错误并退出程序
Console.WriteLine($"\n\n[FATAL ERROR] Database initialization failed. The program cannot start.");
Console.WriteLine($"Error: {ex.Message}");
Console.WriteLine("Please check 'db_init_error.log' for details.");
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
return;
}
// 2. 启动 Squad 游戏日志监听(后台任务)
// 负责实时解析游戏日志文件,捕捉击杀、聊天等关键事件
Task.Run(() =>
{
try
{
SquadLogHelper squadLogHelper = new SquadLogHelper();
// 确定日志文件路径,当前默认为程序运行目录下的 SquadGame/Saved/Logs/SquadGame.log
// 建议后续改为可配置路径
string logpath = Environment.CurrentDirectory + "/SquadGame/Saved/Logs/SquadGame.log";
squadLogHelper.Run(logpath);
}
catch (Exception ex)
{
Console.WriteLine($"日志监听启动失败: {ex.Message}");
}
});
// 3. 确定 Web 服务端口
// 默认端口 23368
int webPort = 23368;
// 尝试从命令行参数获取端口,优先级最高
if (args.Length > 0 && int.TryParse(args[0], out int p) && p > 0 && p < 65536)
{
webPort = p;
}
// 加载配置文件 rcon_Config.json
LoadConfig();
// 端口优先级逻辑:命令行参数 > 配置文件 > 默认值
// 如果指定了命令行参数,则覆盖配置中的端口
if (Config.WebPort == 0) Config.WebPort = webPort;
else if (args.Length > 0) Config.WebPort = webPort;
// 自动检测端口占用,如果被占用则自动+1查找
Config.WebPort = GetAvailablePort(Config.WebPort);
// 4. 初始化 RCON 连接
// 如果系统已配置(非首次运行),则尝试连接 RCON
if (isConfigured)
{
await InitializeRcon();
}
// 启动后台任务:保持 RCON 连接活跃并自动重连
_ = KeepAliveAndReconnectTask();
// 5. 配置 Web 应用程序构建器
var builder = WebApplication.CreateBuilder(args);
// 优化控制台体验:设置日志级别为 Warning减少常规日志干扰控制台交互
builder.Logging.SetMinimumLevel(LogLevel.Warning);
// 注册 RCON 客户端(注意:这里可能为空或占位符,后续会更新)
builder.Services.AddSingleton(client ?? new RconClient("", 0, "")); // 占位,避免 DI 报错
// 注册缓存和 RCON 轮询服务
builder.Services.AddMemoryCache();
builder.Services.AddSingleton(RconCache);
builder.Services.AddHostedService<RconPollingService>(); // RCON 状态轮询
builder.Services.AddHostedService<VipExpirationCheckService>(); // VIP 过期检查
builder.Services.AddHostedService<ServerInfoStorageService>(); // 服务器历史信息存储
builder.Services.AddHostedService<DatabaseCleanupService>(); // 数据库清理任务
builder.Services.AddSingleton<OnlineUserService>(); // 在线玩家服务
builder.Services.AddSingleton<BanService>(); // 封禁管理服务
builder.Services.AddSingleton<BanSyncState>(); // 封禁同步状态
builder.Services.AddHostedService<BanSyncService>(); // 封禁同步后台服务
// 注册玩家数据维护服务
builder.Services.AddHostedService<PlayerMaintenanceService>();
// 注册 FRP 内网穿透管理服务
builder.Services.AddSingleton<FrpManager>();
// 注册插件系统 (使用扩展方法自动扫描注册)
builder.Services.AddAllPlugins();
// 注册 SignalR (实时通信)
builder.Services.AddSignalR();
// 配置 Cookie 认证服务
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/login.html";
options.ExpireTimeSpan = TimeSpan.FromDays(1);
});
builder.Services.AddAuthorization();
// 配置 JSON 序列化选项 (忽略大小写)
builder.Services.Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(options =>
{
options.SerializerOptions.PropertyNameCaseInsensitive = true;
});
// 6. 构建并配置 Web 应用管道
var app = builder.Build();
// 获取 ChatHub 上下文,供非 Hub 类使用
ChatHubContext = app.Services.GetRequiredService<IHubContext<ChatHub>>();
// 开启认证中间件
app.UseAuthentication();
app.UseAuthorization();
// 7. 配置静态文件服务
// 优先使用物理目录下的 wwwroot便于开发调试如果不存在则使用嵌入式资源
IFileProvider fileProvider = new ManifestEmbeddedFileProvider(Assembly.GetExecutingAssembly(), "wwwroot");
string physicalWwwroot = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
if (Directory.Exists(physicalWwwroot))
{
fileProvider = new CompositeFileProvider(
new PhysicalFileProvider(physicalWwwroot),
fileProvider
);
}
// 启用默认文件映射(如 index.html
app.UseDefaultFiles(new DefaultFilesOptions
{
FileProvider = fileProvider
});
// 8. 自定义请求拦截中间件
// 处理系统初始化检查、页面重定向和全局权限验证
app.Use(async (context, next) =>
{
var path = context.Request.Path.Value?.ToLower();
// 场景A: 系统未初始化
// 强制重定向到安装向导 (setup.html),除非正在访问安装相关接口
if (!isConfigured)
{
if (path == "/setup.html" || path == "/api/setup" || path == "/api/setup/detect-Config")
{
await next();
return;
}
context.Response.Redirect("/setup.html");
return;
}
// 场景B: 系统已初始化
// 禁止再次访问安装向导,重定向回首页
if (path == "/setup.html" || path == "/api/setup" || path == "/api/setup/detect-Config")
{
context.Response.Redirect("/");
return;
}
// 场景C: 公开资源访问
// 放行登录页面、登录接口和验证码接口
if (path == "/login.html" || path == "/api/login" || path == "/api/captcha")
{
await next();
return;
}
// 场景D: 需要认证的资源
// 检查用户是否已登录
if (!context.User.Identity?.IsAuthenticated ?? true)
{
// 特殊处理 SignalR 连接 (chatHub)
// 允许协商请求通过SignalR 内部会处理或依赖 Cookie
if (path?.StartsWith("/chatHub") == true || path?.StartsWith("/presenceHub") == true)
{
await next();
return;
}
// 如果是 API 请求,返回 401 未授权状态码
if (path?.StartsWith("/api/") == true)
{
context.Response.StatusCode = 401;
return;
}
// 如果是普通页面访问,重定向到登录页
context.Response.Redirect("/login.html");
return;
}
// 已认证,放行请求
await next();
});
// 启用静态文件中间件
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = fileProvider
});
// API 路由
// Register Modular APIs
app.MapSystemApis();
app.MapAuthApis();
app.MapUserApis();
app.MapVipApis();
app.MapPlayerApis();
app.MapServerApis();
app.MapLogApis();
app.MapFrpApis();
app.MapBanApis();
// Ensure PluginManager is initialized and Configs are loaded
try
{
app.Services.GetRequiredService<PluginManager>();
}
catch (Exception ex)
{
Console.WriteLine($"[Warning] PluginManager initialization failed: {ex.Message}");
}
Console.WriteLine($"Web 服务启动中: http://localhost:{Config.WebPort}");
// FRP no longer auto-starts at application startup.
// Use console command "frp start" or /api/frp/start when needed.
// 9. 启动 Web 服务
// 使用异步非阻塞方式启动,以便主线程可以继续运行控制台交互循环
var webTask = app.RunAsync($"http://*:{Config.WebPort}");
// 10. 启动控制台交互循环
// 允许用户在控制台直接输入命令进行管理
await RunConsoleLoop(app);
// 当控制台退出时,优雅停止 Web 服务
await app.StopAsync();
await webTask;
}
// 控制台交互主循环
// 允许用户在控制台直接输入命令进行管理
static async Task RunConsoleLoop(WebApplication app)
{
var version = Assembly.GetExecutingAssembly().GetName().Version;
Console.WriteLine("--------------------------------------------------");
Console.WriteLine($" RainOPS Mini 控制台已就绪 v{version}");
Console.WriteLine(" 输入 'help' 查看可用命令");
Console.WriteLine("--------------------------------------------------");
while (true)
{
string statusIndicator = (client != null && client.IsConnected) ? "Online" : "Offline";
Console.Write($"RainOPS [{statusIndicator}]> ");
string? input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input)) continue;
string cmd = input.Trim();
switch (cmd.ToLower())
{
case "help":
PrintHelp();
break;
case "1":
case "Config":
ShowConfig();
break;
case "2":
case "setrcon":
await UpdateRconConfig();
break;
case "3":
case "setwebport":
UpdateWebPort();
break;
case "4":
case "showlogin":
ShowLoginInfo();
break;
case "5":
case "resetlogin":
ResetLoginInfo();
break;
case "setinterval":
Console.WriteLine("请输入刷新间隔 (10-60 秒):");
string? intStr = Console.ReadLine();
if (int.TryParse(intStr, out int interval))
{
var cache = app.Services.GetService<RconDataCache>();
if (cache != null)
{
cache.RefreshIntervalSeconds = interval;
Console.WriteLine($"刷新频率已设置为 {cache.RefreshIntervalSeconds} 秒");
}
}
else
{
Console.WriteLine("输入无效");
}
break;
case "status":
ShowStatus(app);
break;
case "frp":
Console.WriteLine("用法: frp start | frp stop | frp restart");
break;
case "frp start":
var frpStart = app.Services.GetService<FrpManager>();
if (frpStart != null)
{
try {
frpStart.Start();
Console.WriteLine("FRP 任务已启动,日志将显示在下方。");
} catch (Exception ex) {
Console.WriteLine($"启动失败: {ex.Message}");
}
}
break;
case "frp stop":
var frpStop = app.Services.GetService<FrpManager>();
if (frpStop != null)
{
frpStop.Stop();
Console.WriteLine("FRP 任务已停止。");
}
break;
case "frp restart":
var frpRestart = app.Services.GetService<FrpManager>();
if (frpRestart != null)
{
try {
frpRestart.Restart();
Console.WriteLine("FRP 任务已重启。");
} catch (Exception ex) {
Console.WriteLine($"重启失败: {ex.Message}");
}
}
break;
case "connect":
await ManualConnect();
break;
case "exit":
case "quit":
Console.WriteLine("正在退出程序...");
return;
default:
// 尝试作为 RCON 命令发送到游戏服务器
if (client != null && client.IsConnected)
{
try
{
// Console.WriteLine($"[发送] {cmd}");
string response = await client.SendCommandAsync(cmd);
if (!string.IsNullOrWhiteSpace(response))
{
Console.WriteLine(response);
}
else
{
Console.WriteLine("(无返回数据)");
}
}
catch (Exception ex)
{
Console.WriteLine($"[错误] 命令执行失败: {ex.Message}");
}
}
else
{
Console.WriteLine("未知命令,且 RCON 未连接。输入 'help' 查看帮助。");
}
break;
}
}
}
// 显示系统状态
// 打印 RCON 连接、Web 端口、FRP 状态等信息
static void ShowStatus(WebApplication app)
{
Console.WriteLine("------------- 系统状态 -------------");
Console.WriteLine($" RCON 连接状态 : {(client?.IsConnected == true ? "" : "")}");
if (client?.IsConnected == true)
{
Console.WriteLine($" RCON 目标 : {Config.Ip}:{Config.Port}");
}
Console.WriteLine($" Web 服务端口 : {Config.WebPort}");
var frp = app.Services.GetService<FrpManager>();
if (frp != null)
{
Console.WriteLine($" FRP 运行状态 : {(frp.IsRunning ? "" : "")}");
}
Console.WriteLine("------------------------------------");
}
// 手动连接 RCON
// 尝试根据当前配置重新连接游戏服务器
static async Task ManualConnect()
{
Console.WriteLine("正在尝试连接 RCON...");
if (!isConfigured) LoadConfig();
if (string.IsNullOrEmpty(Config.Ip))
{
Console.WriteLine("错误: 未配置 RCON IP。请先使用 'Config' 或 'setrcon' 进行配置。");
return;
}
await InitializeRcon();
}
// 打印帮助信息
// 显示控制台支持的所有命令
static void PrintHelp()
{
Console.WriteLine("================ 功能菜单 ================");
Console.WriteLine(" [直接输入 RCON 命令即可发送到服务器]");
Console.WriteLine(" status - 查看连接状态");
Console.WriteLine(" connect - 手动尝试连接 RCON");
Console.WriteLine(" 1. 查看配置文件全部信息 (或输入 Config)");
Console.WriteLine(" - 显示服务器IP、端口、RCON密码、登录名、登录密码等");
Console.WriteLine(" 2. 修改 RCON 信息 (或输入 setrcon)");
Console.WriteLine(" 3. 修改 WEB 端口 (或输入 setwebport)");
Console.WriteLine(" 4. 查看登录名和密码 (或输入 showlogin)");
Console.WriteLine(" 5. 重置登录名和密码 (或输入 resetlogin)");
Console.WriteLine(" setinterval <seconds> - 设置 RCON 数据刷新频率 (10-60秒)");
Console.WriteLine(" frp start - 启动 FRP 任务");
Console.WriteLine(" frp stop - 停止 FRP 任务");
Console.WriteLine(" frp restart - 重启 FRP 任务");
Console.WriteLine(" help - 显示此帮助信息");
Console.WriteLine(" exit - 退出程序");
Console.WriteLine("==========================================");
}
// 显示配置信息
// 打印当前加载的所有配置项
static void ShowConfig()
{
Console.WriteLine("------------- 当前配置信息 -------------");
Console.WriteLine($" RCON 服务器IP : {Config.Ip}");
Console.WriteLine($" RCON 端口 : {Config.Port}");
Console.WriteLine($" RCON 密码 : {Config.Password}");
Console.WriteLine($" WEB 端口 : {Config.WebPort}");
Console.WriteLine($" WEB 登录名 : {Config.WebUser}");
Console.WriteLine($" WEB 登录密码 : {Config.WebPassword}");
Console.WriteLine("----------------------------------------");
}
// 显示登录凭据
// 打印当前的 Web 登录用户名和密码
static void ShowLoginInfo()
{
Console.WriteLine("------------- 登录凭据 -------------");
Console.WriteLine($" 登录名 : {Config.WebUser}");
Console.WriteLine($" 登录密码 : {Config.WebPassword}");
Console.WriteLine("------------------------------------");
}
// 修改 RCON 配置
// 交互式修改 IP、端口和密码
static async Task UpdateRconConfig()
{
Console.WriteLine(">>> 修改 RCON 配置 (直接回车保持原值) <<<");
Console.Write($"输入新IP [{Config.Ip}]: ");
string? ip = Console.ReadLine();
if (!string.IsNullOrWhiteSpace(ip)) Config.Ip = ip.Trim();
Console.Write($"输入新端口 [{Config.Port}]: ");
string? portStr = Console.ReadLine();
if (!string.IsNullOrWhiteSpace(portStr) && int.TryParse(portStr, out int p)) Config.Port = p;
Console.Write($"输入新密码 [{Config.Password}]: ");
string? pass = Console.ReadLine();
if (!string.IsNullOrWhiteSpace(pass)) Config.Password = pass.Trim();
SaveConfigToFile();
Console.WriteLine("配置已保存。正在重新初始化 RCON 连接...");
isConfigured = true;
await InitializeRcon();
}
// 修改 Web 端口
// 修改 Web 服务监听端口(需重启生效)
static void UpdateWebPort()
{
Console.WriteLine(">>> 修改 WEB 端口 <<<");
Console.Write($"输入新端口 [{Config.WebPort}]: ");
string? portStr = Console.ReadLine();
if (!string.IsNullOrWhiteSpace(portStr) && int.TryParse(portStr, out int p) && p > 0 && p < 65536)
{
Config.WebPort = p;
SaveConfigToFile();
Console.WriteLine($"端口已修改为 {p}。");
Console.WriteLine("注意Web 端口修改需要重启程序才能生效!");
}
else
{
Console.WriteLine("端口无效或未修改。");
}
}
// 重置 Web 登录信息
// 修改或生成新的 Web 登录凭据
static void ResetLoginInfo()
{
Console.WriteLine(">>> 重置 Web 登录信息 <<<");
Console.Write($"输入新用户名 (回车保持 '{Config.WebUser}'): ");
string? user = Console.ReadLine();
if (!string.IsNullOrWhiteSpace(user)) Config.WebUser = user.Trim();
Console.Write("输入新密码 (回车自动生成随机密码): ");
string? pass = Console.ReadLine();
if (string.IsNullOrWhiteSpace(pass))
{
pass = GenerateRandomPassword();
Console.WriteLine($"已生成随机密码: {pass}");
}
else
{
if (!ValidatePassword(pass))
{
Console.WriteLine("密码强度不足(需至少包含字母、数字、符号中的两种,且长度>=6。是否强制使用(y/n)");
string? confirm = Console.ReadLine();
if (confirm?.Trim().ToLower() != "y")
{
Console.WriteLine("取消修改。");
return;
}
}
}
Config.WebPassword = pass;
SaveConfigToFile();
Console.WriteLine("登录信息已更新并保存。");
}
// 保存配置到文件
// 将内存中的 Config 对象序列化并写入 JSON 文件
public static void SaveConfigToFile()
{
try
{
string json = JsonSerializer.Serialize(Config, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(ConfigPath, json);
Console.WriteLine("[系统] 配置文件已更新。");
}
catch (Exception ex)
{
Console.WriteLine($"[错误] 保存配置文件失败: {ex.Message}");
}
}
// 加载配置文件
// 从 JSON 文件读取配置,如果不存在则提示
static void LoadConfig()
{
if (!File.Exists(ConfigPath))
{
Console.WriteLine("未检测到配置文件,请通过 Web 页面进行初始化配置,或在控制台使用命令配置。");
isConfigured = false;
}
else
{
try
{
string json = File.ReadAllText(ConfigPath);
var loadedConfig = JsonSerializer.Deserialize<RconConfig>(json);
if (loadedConfig != null)
{
Config = loadedConfig;
isConfigured = true;
Console.WriteLine($"已加载配置: RCON IP={Config.Ip}, Port={Config.Port}");
}
else
{
Console.WriteLine("配置文件格式错误,需重新配置。");
isConfigured = false;
}
}
catch (Exception ex)
{
Console.WriteLine($"读取配置文件出错: {ex.Message}");
isConfigured = false;
}
}
}
// 初始化 RCON 连接
// 建立连接并设置事件监听器
public static async Task InitializeRcon()
{
if (!string.IsNullOrEmpty(Config.Ip) && Config.Port > 0)
{
// 如果已有连接,尝试断开并清理资源
client?.Dispose();
client = new RconClient(Config.Ip, Config.Port, Config.Password);
// 监听 RCON 服务端主动推送的消息 (如聊天、击杀日志)
client.OnMessageReceived += (msg) =>
{
// 1. 通知插件系统
try
{
OnRconMessageReceived?.Invoke(msg);
}
catch (Exception ex)
{
Console.WriteLine($"[Error] Plugin RCON message handler failed: {ex.Message}");
}
// 2. 解析聊天消息
var chatMsg = RconParser.ParseChatMessage(msg);
if (chatMsg != null)
{
// 尝试从缓存中补充玩家的队伍和分队信息
var player = RconCache.GetPlayer(chatMsg.SteamId, chatMsg.EosId);
if (player != null)
{
chatMsg.TeamId = player.TeamId;
chatMsg.SquadId = player.SquadId;
}
// 添加到内存缓存
RconCache.AddChatMessage(chatMsg);
// 3. 保存到数据库 (SQLite)
try
{
var chatLog = new ChatLog
{
Type = chatMsg.Type,
EosId = chatMsg.EosId,
SteamId = chatMsg.SteamId,
Name = chatMsg.Name,
Message = chatMsg.Message,
Timestamp = chatMsg.Timestamp,
TeamId = chatMsg.TeamId,
SquadId = chatMsg.SquadId
};
using (var db = DbHelper.GetInstance())
{
db.Insertable(chatLog).ExecuteCommand();
}
}
catch (Exception ex)
{
Console.WriteLine($"[Error] Failed to save chat to DB: {ex.Message}");
}
Console.WriteLine($"[{chatMsg.Type}] {chatMsg.Name}: {chatMsg.Message}");
// 4. 通过 SignalR 广播给前端
if (ChatHubContext != null)
{
_ = ChatHubContext.Clients.All.SendAsync("ReceiveChat", chatMsg);
}
}
else
{
// 非聊天消息,直接打印
Console.WriteLine($"[RCON Push] {msg}");
}
};
//client.OnLog += (log) => Console.WriteLine($"[RCON Log] {log}");
try
{
await client.ConnectAsync();
Console.WriteLine($"[系统] RCON 客户端已连接 ({Config.Ip}:{Config.Port})");
}
catch (Exception ex)
{
Console.WriteLine($"[警告] RCON 连接失败: {ex.Message}");
}
}
}
static int GetAvailablePort(int startPort)
{
int port = startPort;
while (IsPortInUse(port))
{
Console.WriteLine($"[系统] 端口 {port} 已被占用,尝试使用 {port + 10}...");
port += 10;
if (port > 65535)
{
throw new Exception("无法找到可用端口");
}
}
if (port != startPort)
{
Console.WriteLine($"[系统] 最终使用端口: {port}");
}
return port;
}
static bool IsPortInUse(int port)
{
bool inUse = false;
System.Net.NetworkInformation.IPGlobalProperties ipProperties = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties();
System.Net.IPEndPoint[] ipEndPoints = ipProperties.GetActiveTcpListeners();
foreach (System.Net.IPEndPoint endPoint in ipEndPoints)
{
if (endPoint.Port == port)
{
inUse = true;
break;
}
}
return inUse;
}
static async Task KeepAliveAndReconnectTask()
{
while (true)
{
if (client != null && !client.IsConnected)
{
// Console.WriteLine("[系统] 检测到 RCON 未连接,正在尝试连接...");
try
{
await client.ConnectAsync();
// Console.WriteLine("[系统] RCON 重连成功");
}
catch
{
// 静默失败,等待下次重试
}
}
await Task.Delay(5000);
}
}
public static bool ValidatePassword(string pwd)
{
if (string.IsNullOrEmpty(pwd) || pwd.Length < 6) return false;
int types = 0;
if (pwd.Any(char.IsDigit)) types++;
if (pwd.Any(char.IsLetter)) types++;
if (pwd.Any(c => !char.IsLetterOrDigit(c))) types++;
return types >= 2;
}
public static string GenerateRandomPassword()
{
const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const string digits = "0123456789";
const string symbols = "!@#$%^&*()_+-=[]{}|;:,.<>?";
var random = new Random();
var password = new char[12];
// 确保至少包含各类字符以满足要求
password[0] = chars[random.Next(chars.Length)];
password[1] = digits[random.Next(digits.Length)];
password[2] = symbols[random.Next(symbols.Length)];
string allChars = chars + digits + symbols;
for (int i = 3; i < 12; i++)
{
password[i] = allChars[random.Next(allChars.Length)];
}
// 打乱顺序
return new string(password.OrderBy(x => random.Next()).ToArray());
}
}
public class CommandRequest
{
public string Command { get; set; } = string.Empty;
}
public class LoginRequest
{
public string Username { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
public string CaptchaId { get; set; } = string.Empty;
public string CaptchaCode { get; set; } = string.Empty;
}
public class SetupRequest
{
public string Ip { get; set; } = "";
public int Port { get; set; }
public string Password { get; set; } = "";
public string WebUser { get; set; } = "";
public string WebPassword { get; set; } = "";
}
public class ConfigSaveRequest
{
public string Filename { get; set; } = "";
public string Content { get; set; } = "";
}
public class PasswordResetRequest
{
public string NewPassword { get; set; } = string.Empty;
}
public class BanRequest
{
public string SteamId { get; set; } = "";
public string Reason { get; set; } = "";
public string Duration { get; set; } = "perm";
}
public class KickRequest
{
public string SteamId { get; set; } = "";
public string Reason { get; set; } = "";
}
public class IntervalRequest
{
public int Seconds { get; set; }
}
public class TicketRequest
{
public int TeamId { get; set; }
public int Tickets { get; set; }
}
public class GameTimeUpdateModel
{
public int Hours { get; set; }
}
}