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}");
}
}
}
}