using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using RainOpsMini.Helpers;
using RainOpsMini.Services;
namespace RainOpsMini.Plugins
{
///
/// 战队跳边插件配置类
///
public class TeamSwitchConfig
{
///
/// 启用插件
///
public bool Enabled { get; set; } = false;
///
/// 触发关键字 (逗号分隔,支持拼音首字母)
///
public string LikeKey { get; set; } = "tb,stb,跳边";
///
/// 免费额度耗尽提示内容
///
public string LimitExhaustedMsg { get; set; } = "免费更换阵营额度已经耗尽,请使用STB命令更换阵营!";
///
/// 每张地图允许免费更换阵营的个数
///
public int LimitExhaustedCount { get; set; } = 10;
///
/// 使用积分跳边所需要的积分数量
///
public int STBIntegrate { get; set; } = 30;
///
/// 有权限协助他人进行跳边的SteamID列表 (包含SteamID即可)
///
public List STBSteamID { get; set; } = new List();
///
/// 开局多少秒内允许使用免费额度
///
public int STBSecond { get; set; } = 300;
///
/// 免费跳边时间已过提示语
///
public string STBSecondMsg { get; set; } = "免费跳边时间已过,请使用STB命令更换阵营";
///
/// 玩家求助广播内容模板 ({Name}为玩家名,{Code}为指令)
///
public string STBHelpMsg { get; set; } = "玩家【{Name}】想要更换阵营!快来帮帮Ta:{Code}";
///
/// 帮助指令前缀 (后面跟4位随机码)
///
public string STBHelpCMD { get; set; } = "hp";
}
///
/// 战队跳边插件
///
public class TeamSwitchPlugin : IPlugin, IDisposable
{
///
/// 插件名称
///
public string Name => "玩家跳边系统";
///
/// 插件描述
///
public string Description => "允许玩家通过聊天指令或积分更换阵营,支持免费额度和战队协助";
///
/// 插件分类
///
public PluginCategory Category => PluginCategory.PointsFunction;
///
/// 是否启用
///
public bool IsEnabled => _config.Enabled;
private TeamSwitchConfig _config = new TeamSwitchConfig();
// 状态
private string _currentMap = "";
private Dictionary _freeUsage = new Dictionary(); // SteamID -> 次数
private Dictionary _pendingHelp = new Dictionary(); // 验证码 -> SteamId
private HashSet _helperSteamIds = new HashSet();
private readonly object _lock = new object();
private readonly Random _random = new Random();
///
/// 构造函数,初始化插件并订阅RCON消息事件
///
public TeamSwitchPlugin()
{
ApplyConfig();
}
///
/// 获取当前配置
///
/// 配置对象
public object GetConfig() => _config;
///
/// 更新配置
///
/// JSON配置元素
public void UpdateConfig(JsonElement config)
{
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
try
{
_config = JsonSerializer.Deserialize(config.GetRawText(), options) ?? new TeamSwitchConfig();
ParseHelperSteamIds();
ApplyConfig();
}
catch (Exception ex)
{
RainOpsLog.Log($"[{Name}] UpdateConfig Error: {ex.Message}");
}
}
///
/// 应用配置,根据启用状态订阅或取消订阅事件
///
private void ApplyConfig()
{
Program.OnRconMessageReceived -= OnRconMessage;
if (_config.Enabled)
{
Program.OnRconMessageReceived += OnRconMessage;
}
}
///
/// 重置配置为默认值
///
public void ResetConfig()
{
_config = new TeamSwitchConfig();
ParseHelperSteamIds();
}
///
/// 解析协助者SteamID列表
///
private void ParseHelperSteamIds()
{
lock (_lock)
{
_helperSteamIds.Clear();
if (_config.STBSteamID == null || _config.STBSteamID.Count == 0) return;
foreach (var id in _config.STBSteamID)
{
if (!string.IsNullOrWhiteSpace(id))
{
_helperSteamIds.Add(id.Trim());
}
}
}
}
///
/// 释放资源,取消订阅事件
///
public void Dispose()
{
Program.OnRconMessageReceived -= OnRconMessage;
}
///
/// 处理RCON消息
///
/// 消息内容
private async void OnRconMessage(string msg)
{
if (!_config.Enabled) return;
try
{
// 检查地图状态
CheckMapStatus();
// 解析聊天
var (steamId, name, content) = ParseChat(msg);
if (string.IsNullOrEmpty(steamId)) return;
content = content.Trim();
// 1. 检查协助指令
if (content.StartsWith(_config.STBHelpCMD, StringComparison.OrdinalIgnoreCase))
{
await HandleHelpCommand(steamId, name, content);
return;
}
// 2. 检查跳边指令
if (IsSwitchCommand(content))
{
await HandleSwitchRequest(steamId, name, content);
}
}
catch (Exception ex)
{
RainOpsLog.Log($"[{Name}] Error: {ex.Message}");
}
}
///
/// 检查地图状态,如果地图更换则重置状态
///
private void CheckMapStatus()
{
var serverInfo = Program.RconCache?.ServerInfo;
if (serverInfo == null) return;
string map = serverInfo.MapName_s ?? "";
if (map != _currentMap)
{
lock (_lock)
{
_currentMap = map;
_freeUsage.Clear();
_pendingHelp.Clear();
}
RainOpsLog.Log($"[{Name}] Map changed to {map}. Reset limits.");
}
}
///
/// 解析聊天消息
///
/// 原始消息
/// 解析出的SteamID、名字和内容
private (string steamId, string name, string content) ParseChat(string msg)
{
// 格式 1
var match = Regex.Match(msg, @"ChatMessage: \[(?\d+)\] (?.+) : (?.+)");
if (!match.Success)
{
// 格式 2
match = Regex.Match(msg, @"\[Chat.+?\] \[Online IDs:EOS: .+? steam: (?\d+)\]\s+(?.+?) : (?.+)");
}
if (match.Success)
{
return (match.Groups["steamId"].Value, match.Groups["name"].Value, match.Groups["content"].Value);
}
return (null, null, null);
}
///
/// 检查是否为跳边指令
///
/// 聊天内容
/// 是否匹配
private bool IsSwitchCommand(string content)
{
if (string.IsNullOrWhiteSpace(_config.LikeKey)) return false;
var keys = _config.LikeKey.Split(new[] { ',', ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var key in keys)
{
if (string.IsNullOrWhiteSpace(key)) continue;
// 精确匹配
if (content.Equals(key, StringComparison.OrdinalIgnoreCase)) return true;
// 拼音首字母
string initials = PinyinHelper.GetInitials(key);
if (!string.IsNullOrEmpty(initials) && content.Equals(initials, StringComparison.OrdinalIgnoreCase)) return true;
// 全拼
string pinyin = PinyinHelper.GetPinyin(key);
if (!string.IsNullOrEmpty(pinyin) && content.Equals(pinyin, StringComparison.OrdinalIgnoreCase)) return true;
}
return false;
}
///
/// 处理跳边请求
///
/// 玩家SteamID
/// 玩家名字
/// 指令内容
private async Task HandleSwitchRequest(string steamId, string name, string content)
{
bool isFree = false;
string failReason = "";
lock (_lock)
{
// 检查时间
int matchTime = Program.RconCache?.ServerInfo?.PlayTime_I ?? 999999;
bool timeValid = matchTime <= _config.STBSecond;
if (!timeValid) failReason = _config.STBSecondMsg;
// 检查限制
if (!_freeUsage.TryGetValue(steamId, out int count)) count = 0;
bool limitValid = count < _config.LimitExhaustedCount;
if (!limitValid) failReason = _config.LimitExhaustedMsg;
if (timeValid && limitValid)
{
isFree = true;
}
}
if (isFree)
{
if (await ExecuteSwitch(steamId))
{
lock (_lock)
{
if (!_freeUsage.ContainsKey(steamId)) _freeUsage[steamId] = 0;
_freeUsage[steamId]++;
}
await SendWarn(steamId, $"已为您更换阵营 (免费额度: {_freeUsage[steamId]}/{_config.LimitExhaustedCount})");
}
}
else
{
// 如果不是使用STB指令,则提示错误并返回,不进行扣分尝试
if (!content.Equals("stb", StringComparison.OrdinalIgnoreCase))
{
await SendWarn(steamId, failReason);
return;
}
// 尝试扣分
await SendWarn(steamId, failReason); // 通知为什么免费失败
int points = await PointsService.GetPointsAsync(steamId);
if (points >= _config.STBIntegrate)
{
if (await PointsService.AdjustPointsAsync(steamId, -_config.STBIntegrate) != 0)
{
if (await ExecuteSwitch(steamId))
{
await SendWarn(steamId, $"已消耗 {_config.STBIntegrate} 积分更换阵营 (剩余: {points - _config.STBIntegrate})");
}
else
{
// 失败退款?通常比较复杂,假设切换有效或手动退款。
// 暂时只记录日志。
RainOpsLog.Log($"[{Name}] Switch failed for {steamId} but points deducted.");
}
}
}
else
{
// 积分不足 -> 生成求助码
string code = GenerateHelpCode();
lock (_lock)
{
_pendingHelp[code] = steamId;
}
string helpCmd = $"{_config.STBHelpCMD}{code}";
string broadcastMsg = _config.STBHelpMsg
.Replace("{Name}", name)
.Replace("{Code}", helpCmd);
await SendBroadcast(broadcastMsg);
await SendWarn(steamId, $"积分不足,已发送求助广播!");
}
}
}
///
/// 生成随机求助代码
///
/// 4位随机代码
private string GenerateHelpCode()
{
// 生成4位随机码
return _random.Next(1000, 9999).ToString();
}
///
/// 处理协助指令
///
/// 协助者SteamID
/// 协助者名字
/// 指令内容
private async Task HandleHelpCommand(string helperSteamId, string helperName, string content)
{
// content 类似 "hp1234"
string code = content.Substring(_config.STBHelpCMD.Length).Trim();
// 验证协助者权限
bool isHelper = false;
lock (_lock)
{
if (_helperSteamIds.Contains(helperSteamId)) isHelper = true;
}
if (!isHelper)
{
await SendWarn(helperSteamId, "您没有权限执行此操作!");
return;
}
string requesterSteamId = null;
lock (_lock)
{
if (_pendingHelp.TryGetValue(code, out var sid))
{
requesterSteamId = sid;
_pendingHelp.Remove(code);
}
}
if (requesterSteamId != null)
{
if (await ExecuteSwitch(requesterSteamId))
{
await SendBroadcast($"感谢 {helperName} 协助玩家更换阵营!");
}
else
{
await SendWarn(helperSteamId, "操作失败,玩家可能已不在服务器或无法更换。");
}
}
else
{
await SendWarn(helperSteamId, "无效的求助代码或请求已过期。");
}
}
///
/// 执行跳边操作
///
/// 玩家SteamID
/// 是否成功
private async Task ExecuteSwitch(string steamId)
{
if (Program.Client != null && Program.Client.IsConnected)
{
// Squad AdminForceTeamChange 逻辑
// 命令: AdminForceTeamChange
// 注意: 这会杀死玩家并切换队伍。
string cmd = $"AdminForceTeamChange \"{steamId}\"";
await Program.Client.SendCommandAsync(cmd);
return true;
}
return false;
}
///
/// 发送警告消息给玩家
///
/// 玩家SteamID
/// 消息内容
private async Task SendWarn(string steamId, string msg)
{
if (Program.Client != null && Program.Client.IsConnected)
{
await Program.Client.SendCommandAsync($"AdminWarn \"{steamId}\" \"{msg}\"");
}
}
///
/// 发送全服广播
///
/// 广播内容
private async Task SendBroadcast(string msg)
{
if (Program.Client != null && Program.Client.IsConnected)
{
await Program.Client.SendCommandAsync($"AdminBroadcast \"{msg}\"");
}
}
}
}