using Microsoft.Extensions.Hosting; using RainOpsMini.Helpers; using RainOpsMini.Helpers.RCON; using RainOpsMini.Services; using RainOpsMini; using RainOpsMini.Models; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; namespace RainOpsMini.Plugins { public class RedPacketConfig { /// /// 是否启用插件 /// public bool Enabled { get; set; } = false; /// /// 红包过期时间(秒),默认300秒(5分钟) /// public int TimeoutSeconds { get; set; } = 300; /// /// 发红包触发关键字 /// public string SendKeyword { get; set; } = "发红包"; /// /// 抢红包触发关键字 /// public List GrabKeywords { get; set; } = new List { "抢红包", "抢", "qhb", "QHB" }; } public class RedPacketSession { /// /// 发送者SteamID /// public string SenderSteamId { get; set; } = ""; /// /// 发送者名称 /// public string SenderName { get; set; } = ""; /// /// 红包总积分 /// public int TotalPoints { get; set; } /// /// 红包总个数 /// public int TotalCount { get; set; } /// /// 红包开始时间 /// public DateTime StartTime { get; set; } /// /// 待领取的红包金额栈 /// public ConcurrentStack Packets { get; set; } = new ConcurrentStack(); /// /// 已领取红包的玩家记录 (SteamId -> Points) /// public ConcurrentDictionary GrabbedPlayers { get; set; } = new ConcurrentDictionary(); /// /// 剩余红包个数 /// public int RemainingCount => Packets.Count; /// /// 剩余积分总额 /// public int RemainingPoints => Packets.Sum(); } public class RedPacketPlugin : BackgroundService, IPlugin { public string Name => "红包插件"; public string Description => "玩家可以发送拼手气红包,其他玩家抢红包"; public PluginCategory Category => PluginCategory.PointsFunction; public bool IsEnabled => _config.Enabled; private RedPacketConfig _config = new RedPacketConfig(); private RedPacketSession? _currentSession; private readonly object _sessionLock = new object(); private readonly string _configPath; /// /// 构造函数,初始化配置路径并加载配置 /// public RedPacketPlugin() { _configPath = Path.Combine(Environment.CurrentDirectory, "PluginConfig", "RedPacketConfig.json"); LoadConfig(); Program.OnRconMessageReceived += OnRconMessage; } /// /// 获取当前配置 /// /// 配置对象 public object GetConfig() => _config; /// /// 更新配置 /// /// JSON配置元素 public void UpdateConfig(JsonElement config) { try { var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; _config = JsonSerializer.Deserialize(config.GetRawText(), options) ?? new RedPacketConfig(); SaveConfig(); } catch (Exception ex) { RainOpsLog.Log($"[{Name}] UpdateConfig Error: {ex.Message}"); } } /// /// 重置配置为默认值 /// public void ResetConfig() { _config = new RedPacketConfig(); SaveConfig(); } /// /// 释放资源 /// public override void Dispose() { Program.OnRconMessageReceived -= OnRconMessage; base.Dispose(); } /// /// 后台任务执行入口,定期检查红包过期 /// /// 取消令牌 protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { try { if (_config.Enabled) { await CheckTimeout(); } } catch (Exception ex) { RainOpsLog.Log($"[{Name}] Timeout Check Error: {ex.Message}"); } await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken); } } /// /// 检查红包是否过期 /// private async Task CheckTimeout() { RedPacketSession? expiredSession = null; lock (_sessionLock) { if (_currentSession != null) { if ((DateTime.Now - _currentSession.StartTime).TotalSeconds >= _config.TimeoutSeconds) { expiredSession = _currentSession; _currentSession = null; } } } if (expiredSession != null) { int refundAmount = expiredSession.RemainingPoints; if (refundAmount > 0) { await PointsService.AdjustPointsAsync(expiredSession.SenderSteamId, refundAmount); string msg = $"红包已过期,剩余 {refundAmount} 积分已退回给发起者 {expiredSession.SenderName}。"; await Broadcast(msg); RainOpsLog.Log($"[{Name}] Red packet expired. Refunded {refundAmount} to {expiredSession.SenderName} ({expiredSession.SenderSteamId})."); } else { await Broadcast("红包已过期,已被抢完。"); } } } /// /// 处理RCON消息 /// /// RCON消息内容 private async void OnRconMessage(string msg) { if (!_config.Enabled) return; try { var chatMsg = RconParser.ParseChatMessage(msg); if (chatMsg == null) return; string content = chatMsg.Message.Trim(); // 检查发红包命令 if (content.StartsWith(_config.SendKeyword, StringComparison.OrdinalIgnoreCase)) { await HandleSendCommand(chatMsg, content); return; } // 检查抢红包命令 if (_config.GrabKeywords.Any(k => string.Equals(content, k, StringComparison.OrdinalIgnoreCase))) { await HandleGrabCommand(chatMsg); return; } } catch (Exception ex) { RainOpsLog.Log($"[{Name}] OnRconMessage Error: {ex.Message}"); } } /// /// 处理发红包命令 /// /// 聊天消息 /// 消息内容 private async Task HandleSendCommand(ChatMessage chatMsg, string content) { // 格式: 发红包 数量 总积分 var parts = content.Split(' ', StringSplitOptions.RemoveEmptyEntries); if (parts.Length != 3) { // 可选:提示格式错误?为了不刷屏,用户只输入“发红包”时不提示 return; } if (!int.TryParse(parts[1], out int count) || !int.TryParse(parts[2], out int totalPoints)) { await SendWarn(chatMsg.SteamId, "格式错误!正确格式:发红包 数量 总积分 (例如:发红包 10 100)"); return; } if (count <= 0 || totalPoints <= 0) { await SendWarn(chatMsg.SteamId, "数量和积分必须大于0!"); return; } if (totalPoints < count) { await SendWarn(chatMsg.SteamId, "总积分不能小于红包数量(每人至少1积分)!"); return; } lock (_sessionLock) { if (_currentSession != null) { SendWarn(chatMsg.SteamId, "当前已有正在进行的红包活动,请稍后再试!").GetAwaiter(); return; } } // 检查余额 int currentPoints = await PointsService.GetPointsAsync(chatMsg.SteamId); if (currentPoints < totalPoints) { await SendWarn(chatMsg.SteamId, $"积分不足!当前积分:{currentPoints}"); return; } // 扣除积分 await PointsService.AdjustPointsAsync(chatMsg.SteamId, -totalPoints); // 生成红包 var packets = GenerateRandomPackets(totalPoints, count); var session = new RedPacketSession { SenderSteamId = chatMsg.SteamId, SenderName = chatMsg.Name, TotalPoints = totalPoints, TotalCount = count, StartTime = DateTime.Now, Packets = new ConcurrentStack(packets) }; lock (_sessionLock) { _currentSession = session; } RainOpsLog.Log($"[{Name}] {chatMsg.Name} sent red packet: {count} packets, {totalPoints} points."); await Broadcast($"土豪 {chatMsg.Name} 发出了 {count} 个拼手气红包,总积分 {totalPoints}!输入 '{_config.GrabKeywords[0]}' 立即开抢!"); } /// /// 处理抢红包命令 /// /// 聊天消息 private async Task HandleGrabCommand(ChatMessage chatMsg) { RedPacketSession? session; lock (_sessionLock) { session = _currentSession; } if (session == null) { await SendWarn(chatMsg.SteamId, "当前没有可抢的红包!"); return; } if (session.GrabbedPlayers.ContainsKey(chatMsg.SteamId)) { await SendWarn(chatMsg.SteamId, "你已经抢过这个红包了!"); return; } if (session.Packets.TryPop(out int points)) { if (session.GrabbedPlayers.TryAdd(chatMsg.SteamId, points)) { // 增加积分 int newTotal = await PointsService.AdjustPointsAsync(chatMsg.SteamId, points); await SendWarn(chatMsg.SteamId, $"抢到了 {points} 积分!当前总积分:{newTotal}"); RainOpsLog.Log($"[{Name}] {chatMsg.Name} grabbed {points} points."); // 检查是否抢完 if (session.Packets.IsEmpty) { FinishSession(session); } } } else { await SendWarn(chatMsg.SteamId, "手慢了,红包已被抢完!"); } } /// /// 结束红包会话并结算 /// /// 红包会话对象 private void FinishSession(RedPacketSession session) { lock (_sessionLock) { if (_currentSession == session) { _currentSession = null; } } // 寻找运气王 var luckiest = session.GrabbedPlayers.OrderByDescending(x => x.Value).FirstOrDefault(); // 从缓存获取玩家名称以优化显示 string luckiestName = "未知玩家"; var player = Program.RconCache.Players.FirstOrDefault(p => p.SteamId == luckiest.Key); if (player != null) luckiestName = player.Name; Broadcast($"红包已被抢完!运气王是 {luckiestName},抢到了 {luckiest.Value} 积分!").GetAwaiter(); } /// /// 生成随机红包金额 /// /// 总积分 /// 红包数量 /// 红包金额列表 private List GenerateRandomPackets(int totalPoints, int count) { // 简单逻辑: // 1. 先给每个红包分配1积分 // 2. 将剩余积分随机分配 int[] result = new int[count]; for (int i = 0; i < count; i++) result[i] = 1; int remaining = totalPoints - count; Random rnd = new Random(); for (int i = 0; i < remaining; i++) { int index = rnd.Next(count); result[index]++; } // 打乱顺序 return result.OrderBy(x => rnd.Next()).ToList(); } /// /// 发送全服广播 /// /// 消息内容 private async Task Broadcast(string message) { if (Program.Client != null && Program.Client.IsConnected) { await Program.Client.SendCommandAsync($"AdminBroadcast \"{message}\""); } } /// /// 发送警告消息给特定玩家 /// /// 玩家SteamID /// 消息内容 private async Task SendWarn(string steamId, string message) { if (Program.Client != null && Program.Client.IsConnected) { await Program.Client.SendCommandAsync($"AdminWarn \"{steamId}\" \"{message}\""); } } /// /// 从文件加载配置 /// private void LoadConfig() { if (File.Exists(_configPath)) { try { string json = File.ReadAllText(_configPath); var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; _config = JsonSerializer.Deserialize(json, options) ?? new RedPacketConfig(); } catch (Exception ex) { RainOpsLog.Log($"[{Name}] LoadConfig Error: {ex.Message}"); } } else { SaveConfig(); } } /// /// 保存配置到文件 /// private void SaveConfig() { try { string dir = Path.GetDirectoryName(_configPath); if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); string json = JsonSerializer.Serialize(_config, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(_configPath, json); } catch (Exception ex) { RainOpsLog.Log($"[{Name}] SaveConfig Error: {ex.Message}"); } } } }