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
{
///
/// 个人BUFF插件配置
///
public class PersonalBuffConfig
{
///
/// 是否启用插件
///
public bool Enabled { get; set; } = false;
///
/// 触发关键字 (逗号分隔,支持拼音首字母)
///
public string LikeKey { get; set; } = "buff,grbuff,战斗模式";
///
/// 需要扣除的积分数量
///
public int Integral { get; set; } = 100;
///
/// 击杀提醒是否开启
///
public bool EnableDie { get; set; } = true;
///
/// 受伤提醒是否开启
///
public bool EnableWound { get; set; } = true;
///
/// 救治提醒是否开启
///
public bool EnableRevive { get; set; } = true;
///
/// 攻击伤害提醒是否开启
///
public bool EnableAttack { get; set; } = true;
///
/// 激活模式: Map (整张地图有效) 或 Time (时间有效)
///
public string Type { get; set; } = "Time";
///
/// 激活时长(分钟),仅当Type为Time时有效
///
public int Minute { get; set; } = 120;
///
/// 切换静默模式指令
///
public string ToggleSilentCommand { get; set; } = "静默BUFF";
///
/// 激活成功提示模板
///
public string MsgTemplate_ActiveSuccess { get; set; } = "BUFF激活成功!消耗{Points}积分,{ExpiryMsg}。";
///
/// 重复激活提示模板
///
public string MsgTemplate_AlreadyActive { get; set; } = "您的BUFF正在生效中,剩余时间:{TimeLeft}。";
///
/// 击伤攻击者提示模板
///
public string MsgTemplate_Wound_Attacker { get; set; } = "你击伤了 {Victim} (伤害: {Damage}, 武器: {Weapon})";
///
/// 击伤受害者提示模板
///
public string MsgTemplate_Wound_Victim { get; set; } = "{Attacker} 击伤了你 (伤害: {Damage}, 武器: {Weapon})";
///
/// 击杀攻击者提示模板
///
public string MsgTemplate_Die_Attacker { get; set; } = "你击杀了 {Victim} (武器: {Weapon})";
///
/// 击杀受害者提示模板
///
public string MsgTemplate_Die_Victim { get; set; } = "{Attacker} 击杀了你 (武器: {Weapon})";
///
/// 救人者提示模板
///
public string MsgTemplate_Revive_Reviver { get; set; } = "你救起了 {Revived}";
///
/// 被救者提示模板
///
public string MsgTemplate_Revive_Revived { get; set; } = "{Reviver} 救起了你";
///
/// 造成伤害提示模板
///
public string MsgTemplate_Attack { get; set; } = "造成伤害: {Damage}";
///
/// VIP豁免列表
///
public List VipList { get; set; } = new List();
}
///
/// VIP信息类
///
public class BUFFVipInfo
{
///
/// 玩家ID
///
public string SteamId { get; set; }
///
/// 昵称(可不填)
///
public string Name { get; set; }
///
/// 开通原因(可不填)
///
public string Reason { get; set; }
///
/// 过期时间(可不填)
///
public DateTime ExpireTime { get; set; }
}
///
/// BUFF状态类
///
public class BuffState
{
public DateTime ExpireTime { get; set; }
public bool IsSilent { get; set; }
public bool IsVip { get; set; }
}
///
/// 个人BUFF插件
///
public class PersonalBuffPlugin : IPlugin, IDisposable
{
public string Name => "个人BUFF系统";
public string Description => "允许玩家消耗积分开启战斗日志通知(BUFF),支持击杀、受伤、攻击、救治提示";
public PluginCategory Category => PluginCategory.PointsFunction;
public bool IsEnabled => _config.Enabled;
private PersonalBuffConfig _config = new PersonalBuffConfig();
// SteamID -> BuffState
private Dictionary _activeBuffs = new Dictionary();
private string _currentMap = "";
private readonly object _lock = new object();
private readonly string _storagePath;
private System.Timers.Timer _vipTimer;
///
/// 构造函数,初始化存储路径并加载数据,注册事件
///
public PersonalBuffPlugin()
{
_storagePath = Path.Combine(Environment.CurrentDirectory, "PluginData", "PersonalBuffData.json");
LoadBuffs();
// 初始化VIP检查定时器 (每60秒检查一次)
_vipTimer = new System.Timers.Timer(60000);
_vipTimer.Elapsed += (s, e) => CheckVips();
ApplyConfig();
}
///
/// 从本地JSON文件加载活跃的BUFF数据
///
private void LoadBuffs()
{
try
{
if (File.Exists(_storagePath))
{
string json = File.ReadAllText(_storagePath);
// 尝试先读取为 JsonDocument 以判断格式
using (JsonDocument doc = JsonDocument.Parse(json))
{
JsonElement root = doc.RootElement;
string map = "";
if (root.TryGetProperty("CurrentMap", out JsonElement mapEl))
{
map = mapEl.GetString() ?? "";
}
var newBuffs = new Dictionary();
if (root.TryGetProperty("ActiveBuffs", out JsonElement buffsEl))
{
foreach (JsonProperty property in buffsEl.EnumerateObject())
{
string steamId = property.Name;
JsonElement value = property.Value;
if (value.ValueKind == JsonValueKind.String)
{
// 旧格式: Value 是 DateTime 字符串
if (DateTime.TryParse(value.GetString(), out DateTime expiry))
{
newBuffs[steamId] = new BuffState { ExpireTime = expiry, IsSilent = false, IsVip = false };
}
}
else if (value.ValueKind == JsonValueKind.Object)
{
// 新格式: Value 是 BuffState 对象
var state = JsonSerializer.Deserialize(value.GetRawText());
if (state != null)
{
newBuffs[steamId] = state;
}
}
}
}
lock (_lock)
{
_activeBuffs = newBuffs;
_currentMap = map;
}
}
}
}
catch (Exception ex)
{
RainOpsLog.Log($"[{Name}] LoadBuffs Error: {ex.Message}");
}
}
///
/// 定期检查VIP玩家并自动激活BUFF
///
private async void CheckVips()
{
try
{
if (!_config.Enabled || _config.VipList == null || _config.VipList.Count == 0) return;
var players = Program.RconCache?.Players;
if (players == null || players.Count == 0) return;
foreach (var player in players)
{
var vip = _config.VipList.FirstOrDefault(v => v.SteamId == player.SteamId);
if (vip != null)
{
if (DateTime.Now < vip.ExpireTime)
{
// 检查是否已经激活BUFF
bool needActivate = false;
lock (_lock)
{
if (!_activeBuffs.ContainsKey(player.SteamId))
{
needActivate = true;
}
else
{
// 如果已激活但不是VIP模式,或者是VIP但时间快到了(小于5分钟),刷新时间
var state = _activeBuffs[player.SteamId];
if (!state.IsVip || state.ExpireTime < DateTime.Now.AddMinutes(5))
{
needActivate = true;
}
}
}
if (needActivate)
{
DateTime newExpiry = DateTime.Now.AddHours(1); // VIP自动续期,每次1小时
string expiryMsg = "VIP自动激活";
lock (_lock)
{
_activeBuffs[player.SteamId] = new BuffState
{
ExpireTime = newExpiry,
IsSilent = false, // 默认不静默,用户可自行切换
IsVip = true
};
}
SaveBuffs();
RainOpsLog.Log($"[BUFF] VIP Auto Activate: {player.Name} ({player.SteamId}), Reason: {vip.Reason}");
// 发送通知
string msg = FormatMessage(_config.MsgTemplate_ActiveSuccess, new Dictionary {
{ "Points", "0 (VIP)" }, { "ExpiryMsg", expiryMsg }
});
await SendWarn(player.SteamId, msg);
}
}
}
}
}
catch (Exception ex)
{
RainOpsLog.Log($"[{Name}] CheckVips Error: {ex.Message}");
}
}
///
/// 将当前活跃的BUFF数据保存到本地JSON文件
///
private void SaveBuffs()
{
try
{
string dir = Path.GetDirectoryName(_storagePath);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
BuffStorageData data;
lock (_lock)
{
data = new BuffStorageData
{
CurrentMap = _currentMap,
ActiveBuffs = _activeBuffs
};
}
string json = JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(_storagePath, json);
}
catch (Exception ex)
{
RainOpsLog.Log($"[{Name}] SaveBuffs Error: {ex.Message}");
}
}
///
/// BUFF数据存储结构类
///
private class BuffStorageData
{
public string CurrentMap { get; set; }
public Dictionary ActiveBuffs { get; set; }
}
///
/// 获取当前配置
///
///
public object GetConfig() => _config;
///
/// 更新配置,反序列化JSON并处理大小写
///
///
public void UpdateConfig(JsonElement config)
{
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
try
{
_config = JsonSerializer.Deserialize(config.GetRawText(), options) ?? new PersonalBuffConfig();
ApplyConfig();
}
catch (Exception ex)
{
RainOpsLog.Log($"[{Name}] UpdateConfig Error: {ex.Message}");
}
}
///
/// 应用配置,根据启用状态管理事件订阅和定时器
///
private void ApplyConfig()
{
Program.OnRconMessageReceived -= OnRconMessage;
SquadLogHelper.squadLogCleaning.OnLogReceived -= OnLogMessage;
_vipTimer.Stop();
if (_config.Enabled)
{
Program.OnRconMessageReceived += OnRconMessage;
SquadLogHelper.squadLogCleaning.OnLogReceived += OnLogMessage;
_vipTimer.Start();
}
}
///
/// 重置配置为默认值
///
public void ResetConfig()
{
_config = new PersonalBuffConfig();
}
///
/// 释放资源,取消事件订阅
///
public void Dispose()
{
Program.OnRconMessageReceived -= OnRconMessage;
// 取消订阅日志事件
SquadLogHelper.squadLogCleaning.OnLogReceived -= OnLogMessage;
_vipTimer?.Stop();
_vipTimer?.Dispose();
}
///
/// 处理游戏日志消息
///
///
private async void OnLogMessage(string msg)
{
if (!_config.Enabled) return;
try
{
// 解析战斗日志 (仅当有活跃BUFF时)
if (_activeBuffs.Count > 0 && (_config.EnableDie || _config.EnableWound || _config.EnableRevive || _config.EnableAttack))
{
await ProcessGameLog(msg);
}
}
catch (Exception ex)
{
RainOpsLog.Log($"[{Name}] Log Error: {ex.Message}");
}
}
///
/// 处理RCON消息的主入口
///
/// RCON消息内容
private async void OnRconMessage(string msg)
{
if (!_config.Enabled) return;
try
{
// 1. 检查地图变更
CheckMapStatus();
// 2. 解析聊天指令
await ProcessChatCommand(msg);
}
catch (Exception ex)
{
RainOpsLog.Log($"[{Name}] Error: {ex.Message}");
}
}
///
/// 检查地图状态,处理换图时的BUFF清理
///
private void CheckMapStatus()
{
var serverInfo = Program.RconCache?.ServerInfo;
if (serverInfo == null) return;
string map = serverInfo.MapName_s ?? "";
if (map != _currentMap)
{
lock (_lock)
{
_currentMap = map;
// 如果是Map模式,换图时清除所有BUFF
// 如果是Time模式,清理已过期的BUFF (虽然OnRconMessage也会用到,但换图清理一下也好)
if (_config.Type.Equals("Map", StringComparison.OrdinalIgnoreCase))
{
_activeBuffs.Clear();
SaveBuffs();
}
}
}
// 清理过期BUFF (通用)
CleanupExpiredBuffs();
}
///
/// 清理已过期的Time模式BUFF
///
private void CleanupExpiredBuffs()
{
bool changed = false;
lock (_lock)
{
var now = DateTime.Now;
var expired = _activeBuffs.Where(kv => kv.Value.ExpireTime < now).Select(kv => kv.Key).ToList();
if (expired.Count > 0)
{
foreach (var id in expired)
{
_activeBuffs.Remove(id);
}
changed = true;
}
}
if (changed)
{
SaveBuffs();
}
}
///
/// 切换静默模式
///
///
///
private async Task ToggleSilent(string steamId)
{
bool currentState = false;
bool hasBuff = false;
lock (_lock)
{
if (_activeBuffs.TryGetValue(steamId, out var state))
{
hasBuff = true;
currentState = state.IsSilent;
state.IsSilent = !currentState;
}
}
if (hasBuff)
{
SaveBuffs();
string status = !currentState ? "已开启" : "已关闭";
await SendWarn(steamId, $"静默模式{status}。");
}
else
{
await SendWarn(steamId, "您未激活BUFF,无法设置静默模式。");
}
}
///
/// 解析聊天消息,检测是否为触发指令
///
///
///
private async Task ProcessChatCommand(string msg)
{
// RainOpsLog.Log($"[BUFF] Debug Chat: {msg}");
// 简单解析聊天
var match = Regex.Match(msg, @"ChatMessage: \[(?\d+)\] (?.+) : (?.+)");
if (!match.Success)
{
match = Regex.Match(msg, @"\[Chat.+?\] \[Online IDs:EOS: .+? steam: (?\d+)\]\s+(?.+?) : (?.+)");
}
if (match.Success)
{
string steamId = match.Groups["steamId"].Value;
string content = match.Groups["content"].Value.Trim();
// RainOpsLog.Log($"[BUFF] Chat Match: {steamId} said '{content}'");
if (!string.IsNullOrEmpty(_config.ToggleSilentCommand) &&
content.Equals(_config.ToggleSilentCommand, StringComparison.OrdinalIgnoreCase))
{
await ToggleSilent(steamId);
return;
}
if (IsTriggerCommand(content))
{
RainOpsLog.Log($"[BUFF] Trigger Command Detected from {steamId}: {content}");
await ActivateBuff(steamId);
}
}
}
///
/// 判断内容是否匹配触发关键字(支持模糊匹配、拼音首字母)
///
///
///
private bool IsTriggerCommand(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;
}
///
/// 激活玩家BUFF,执行扣分并设置过期时间
///
///
///
private async Task ActivateBuff(string steamId)
{
// 检查是否已经激活
BuffState state = null;
lock (_lock)
{
if (_activeBuffs.TryGetValue(steamId, out var s))
{
if (s.ExpireTime > DateTime.Now)
{
state = s;
}
}
}
if (state != null)
{
// 计算剩余时间
TimeSpan left = state.ExpireTime - DateTime.Now;
string timeLeftStr;
if (state.ExpireTime == DateTime.MaxValue)
timeLeftStr = "本局结束前";
else if (left.TotalHours >= 1)
timeLeftStr = $"{(int)left.TotalHours}小时{left.Minutes}分";
else
timeLeftStr = $"{left.Minutes}分{left.Seconds}秒";
string msg = FormatMessage(_config.MsgTemplate_AlreadyActive, new Dictionary {
{ "TimeLeft", timeLeftStr }
});
await SendWarn(steamId, msg);
return;
}
// 扣分
int points = await PointsService.GetPointsAsync(steamId);
if (points >= _config.Integral)
{
if (await PointsService.AdjustPointsAsync(steamId, -_config.Integral) != 0)
{
// 激活成功
DateTime expiry;
string expiryMsg;
if (_config.Type.Equals("Map", StringComparison.OrdinalIgnoreCase))
{
expiry = DateTime.MaxValue; // Map模式,直到换图
expiryMsg = "本局地图结束前有效";
}
else
{
expiry = DateTime.Now.AddMinutes(_config.Minute);
expiryMsg = $"{_config.Minute}分钟内有效";
}
lock (_lock)
{
_activeBuffs[steamId] = new BuffState { ExpireTime = expiry, IsSilent = false, IsVip = false };
}
SaveBuffs();
string msg = FormatMessage(_config.MsgTemplate_ActiveSuccess, new Dictionary {
{ "Points", _config.Integral.ToString() },
{ "ExpiryMsg", expiryMsg }
});
await SendWarn(steamId, msg);
}
else
{
await SendWarn(steamId, "扣分失败,请联系管理员。");
}
}
else
{
await SendWarn(steamId, $"积分不足!需要{_config.Integral}积分,当前{points}积分。");
}
}
///
/// 解析游戏日志,分发各类战斗事件
///
///
///
private async Task ProcessGameLog(string msg)
{
// 过滤旧日志 (超过3秒)
// 格式: [2024.02.11-12.00.00:123]
var match = Regex.Match(msg, @"^\[(\d{4}\.\d{2}\.\d{2}-\d{2}\.\d{2}\.\d{2}:\d{3})\]");
if (match.Success)
{
// 尝试解析时间
if (DateTime.TryParseExact(match.Groups[1].Value, "yyyy.MM.dd-HH.mm.ss:fff", null, System.Globalization.DateTimeStyles.None, out DateTime logTime))
{
// 日志时间通常是UTC,转换为UTC+8北京时间以匹配系统时间
logTime = logTime.AddHours(8);
// 假设日志时间与系统时间一致(或时区匹配),若差异过大则忽略
if (Math.Abs((DateTime.Now - logTime).TotalSeconds) > 3)
{
// RainOpsLog.Log($"[BUFF] Log Time Ignored: LogTime(UTC+8)={logTime}, SysTime={DateTime.Now}");
return;
}
}
}
// Wound
if (_config.EnableWound && msg.Contains("Wound()"))
{
string cleanMsg = msg.Replace("ASQSoldier::", "");
RainOpsLog.Log($"[BUFF] Processing Wound: {cleanMsg}");
await HandleWound(cleanMsg);
}
// Die
else if (_config.EnableDie && msg.Contains("Die()"))
{
string cleanMsg = msg.Replace("ASQSoldier::", "");
RainOpsLog.Log($"[BUFF] Processing Die: {cleanMsg}");
await HandleDie(cleanMsg);
}
// Revived
else if (_config.EnableRevive && msg.Contains("has revived"))
{
RainOpsLog.Log($"[BUFF] Processing Revived: {msg}");
await HandleRevived(msg);
}
// Attack
else if (_config.EnableAttack && msg.Contains("ActualDamage="))
{
// RainOpsLog.Log($"[BUFF] Processing Attack: {msg}");
await HandleAttack(msg);
}
}
///
/// 处理受伤(Wound)日志,通知双方
///
///
///
private async Task HandleWound(string log)
{
// 优化正则:使用 \s* 允许空格波动,且忽略 Controller ID 具体文本
string pattern = @"\[(.*?)\]\[(.*?)\]LogSquadTrace: \[DedicatedServer\]Wound\(\): Player:\s*(.*?)\s*KillingDamage=(.*?) from (.*?) \(Online IDs: (.*?) steam:\s*(.*?)\s*\|.*?\) caused by (.*)";
if (!log.Contains("steam:")) log = log.Replace("INVALID", "EOS: 0000000000000000000000000000000 steam: 00000000000000000");
var match = Regex.Match(log, pattern);
if (match.Success)
{
string victimName = match.Groups[3].Value.Trim();
string damage = match.Groups[4].Value.Trim();
string attackerSteamId = match.Groups[7].Value.Trim();
string weapon = ExtractMiddlePart(match.Groups[8].Value.Trim());
// 查找受害者SteamID
string victimSteamId = FindSteamIdByName(victimName);
RainOpsLog.Log($"[BUFF] Wound Match: Victim={victimName}({victimSteamId}), Attacker={attackerSteamId}, Dmg={damage}");
if (HasBuff(attackerSteamId) || HasBuff(victimSteamId))
{
string attackerName = FindNameBySteamId(attackerSteamId) ?? "Unknown";
// 通知攻击者
if (!string.IsNullOrEmpty(attackerSteamId))
{
string msg = FormatMessage(_config.MsgTemplate_Wound_Attacker, new Dictionary {
{ "Victim", victimName }, { "Damage", damage }, { "Weapon", weapon }, { "Attacker", attackerName }
});
await SendWarn(attackerSteamId, msg, true);
}
// 通知受害者
if (!string.IsNullOrEmpty(victimSteamId))
{
string msg = FormatMessage(_config.MsgTemplate_Wound_Victim, new Dictionary {
{ "Victim", victimName }, { "Damage", damage }, { "Weapon", weapon }, { "Attacker", attackerName }
});
await SendWarn(victimSteamId, msg, true);
}
}
else
{
RainOpsLog.Log($"[BUFF] Wound No Buff Active: {attackerSteamId} or {victimSteamId}");
}
}
else
{
RainOpsLog.Log($"[BUFF] Wound Match Failed: {log}");
}
}
///
/// 处理死亡(Die)日志,通知双方
///
///
///
private async Task HandleDie(string log)
{
// 优化正则:忽略 Controller ID 具体文本
string pattern = @"\[(.*?)\]\[(.*?)\]LogSquadTrace: \[DedicatedServer\]Die\(\): Player:\s*(.*?)\s*KillingDamage=(.*?) from (.*?) \(Online IDs: EOS: (.*?) steam:\s*(.*?)\s*\|.*?\) caused by (.*)";
if (!log.Contains("steam:")) log = log.Replace("INVALID", "EOS: 0000000000000000000000000000000 steam: 00000000000000000");
var match = Regex.Match(log, pattern);
if (match.Success)
{
string victimName = match.Groups[3].Value.Trim();
string damage = match.Groups[4].Value.Trim();
string attackerSteamId = match.Groups[7].Value.Trim();
string weapon = ExtractMiddlePart(match.Groups[8].Value.Trim());
string victimSteamId = FindSteamIdByName(victimName);
RainOpsLog.Log($"[BUFF] Die Match: Victim={victimName}({victimSteamId}), Attacker={attackerSteamId}");
if (HasBuff(attackerSteamId) || HasBuff(victimSteamId))
{
string attackerName = FindNameBySteamId(attackerSteamId) ?? "Unknown";
// 通知攻击者
if (!string.IsNullOrEmpty(attackerSteamId))
{
string msg = FormatMessage(_config.MsgTemplate_Die_Attacker, new Dictionary {
{ "Victim", victimName }, { "Damage", damage }, { "Weapon", weapon }, { "Attacker", attackerName }
});
await SendWarn(attackerSteamId, msg, true);
}
// 通知受害者
if (!string.IsNullOrEmpty(victimSteamId))
{
string msg = FormatMessage(_config.MsgTemplate_Die_Victim, new Dictionary {
{ "Victim", victimName }, { "Damage", damage }, { "Weapon", weapon }, { "Attacker", attackerName }
});
await SendWarn(victimSteamId, msg, true);
}
}
else
{
RainOpsLog.Log($"[BUFF] Die No Buff Active: {attackerSteamId} or {victimSteamId}");
}
}
else
{
RainOpsLog.Log($"[BUFF] Die Match Failed: {log}");
}
}
///
/// 处理救治(Revived)日志,通知双方
///
///
///
private async Task HandleRevived(string log)
{
string pattern = @"\[.*?\]\[.*?\]LogSquad:(.*?)\s*\(Online IDs: EOS: (.*?) steam:\s*(.*?)\) has revived (.*?)\s*\(Online IDs: EOS: (.*?) steam:\s*(.*?)\)\.";
var match = Regex.Match(log, pattern);
if (match.Success)
{
string reviverName = match.Groups[1].Value.Trim();
string reviverSteamId = match.Groups[3].Value.Trim();
string revivedName = match.Groups[4].Value.Trim();
string revivedSteamId = match.Groups[6].Value.Trim();
RainOpsLog.Log($"[BUFF] Revive Match: Reviver={reviverName}({reviverSteamId}), Revived={revivedName}({revivedSteamId})");
if (HasBuff(reviverSteamId) || HasBuff(revivedSteamId))
{
if (!string.IsNullOrEmpty(reviverSteamId))
{
string msg = FormatMessage(_config.MsgTemplate_Revive_Reviver, new Dictionary {
{ "Revived", revivedName }, { "Reviver", reviverName }
});
await SendWarn(reviverSteamId, msg, true);
}
if (!string.IsNullOrEmpty(revivedSteamId))
{
string msg = FormatMessage(_config.MsgTemplate_Revive_Revived, new Dictionary {
{ "Revived", revivedName }, { "Reviver", reviverName }
});
await SendWarn(revivedSteamId, msg, true);
}
}
else
{
RainOpsLog.Log($"[BUFF] Revive No Buff Active: {reviverSteamId} or {revivedSteamId}");
}
}
else
{
RainOpsLog.Log($"[BUFF] Revive Match Failed: {log}");
}
}
///
/// 处理攻击(Attack)日志,通知双方
///
///
///
private async Task HandleAttack(string log)
{
// 优化正则:忽略 Controller ID 具体文本
string pattern = @"\[.*?\]\[.*?\]LogSquad: Player:\s*(.*?)\s*ActualDamage=(.*?) from (.*?) \(Online IDs: EOS: (.*?) steam:\s*(.*?)\s*\|.*?\)caused by (.*)";
if (!log.Contains("steam:")) log = log.Replace("INVALID", "EOS: 0000000000000000000000000000000 steam: 00000000000000000");
var match = Regex.Match(log, pattern);
if (match.Success)
{
string victimName = match.Groups[1].Value.Trim();
string damage = match.Groups[2].Value.Trim();
string attackerSteamId = match.Groups[5].Value.Trim();
string weapon = ExtractMiddlePart(match.Groups[6].Value.Trim());
// 查找受害者SteamID
string victimSteamId = FindSteamIdByName(victimName);
RainOpsLog.Log($"[BUFF] Attack Match: Victim={victimName}({victimSteamId}), Attacker={attackerSteamId}, Dmg={damage}");
if (HasBuff(attackerSteamId) || HasBuff(victimSteamId))
{
string attackerName = FindNameBySteamId(attackerSteamId) ?? "Unknown";
// 通知攻击者
if (!string.IsNullOrEmpty(attackerSteamId))
{
string msg = FormatMessage(_config.MsgTemplate_Attack, new Dictionary {
{ "Victim", victimName }, { "Damage", damage }, { "Weapon", weapon }, { "Attacker", attackerName }
});
await SendWarn(attackerSteamId, msg, true);
}
// 通知受害者
if (!string.IsNullOrEmpty(victimSteamId))
{
string msg = FormatMessage(_config.MsgTemplate_Attack, new Dictionary {
{ "Victim", victimName }, { "Damage", damage }, { "Weapon", weapon }, { "Attacker", attackerName }
});
await SendWarn(victimSteamId, msg, true);
}
}
else
{
RainOpsLog.Log($"[BUFF] Attack No Buff Active: {attackerSteamId} or {victimSteamId}");
}
}
else
{
RainOpsLog.Log($"[BUFF] Attack Match Failed: {log}");
}
}
///
/// 检查玩家是否拥有活跃BUFF
///
///
///
private bool HasBuff(string steamId)
{
if (string.IsNullOrEmpty(steamId)) return false;
lock (_lock)
{
if (_activeBuffs.TryGetValue(steamId, out var state))
{
return state.ExpireTime > DateTime.Now;
}
}
return false;
}
///
/// 根据玩家名称查找SteamID
///
///
///
private string FindSteamIdByName(string name)
{
var players = Program.RconCache?.Players;
if (players == null) return null;
return players.FirstOrDefault(p => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase))?.SteamId;
}
///
/// 根据SteamID查找玩家名称
///
///
///
private string FindNameBySteamId(string steamId)
{
var players = Program.RconCache?.Players;
if (players == null) return null;
return players.FirstOrDefault(p => p.SteamId == steamId)?.Name;
}
///
/// 提取字符串中间部分(通常用于简化武器名称)
///
///
///
private string ExtractMiddlePart(string input)
{
if (string.IsNullOrEmpty(input))
return input;
// 移除末尾的 _C_xxxxx 部分
string cleaned = Regex.Replace(input, @"_C_\d+$", "");
// 分割字符串
string[] parts = cleaned.Split('_');
// 从数组中间提取关键部分
// BP_Soldier_PLA_SquadLeader_Woodland 的关键是 PLA 和 SquadLeader
if (parts.Length >= 4)
{
// 提取阵营(通常是第3部分)
string faction = parts[2];
// 提取兵种(通常是第4部分)
string role = parts[3];
return $"{faction}_{role}";
}
else if (parts.Length >= 2)
{
// 如果格式不标准,至少返回后两个部分
return $"{parts[parts.Length - 2]}_{parts[parts.Length - 1]}";
}
return input;
}
///
/// 格式化消息模板
///
private string FormatMessage(string template, Dictionary args)
{
if (string.IsNullOrEmpty(template)) return "";
string result = template;
foreach (var kv in args)
{
result = result.Replace("{" + kv.Key + "}", kv.Value);
}
return result;
}
///
/// 发送AdminWarn警告通知给玩家
///
///
///
/// 是否检查静默模式
///
private async Task SendWarn(string steamId, string msg, bool checkSilent = false)
{
if (checkSilent)
{
lock (_lock)
{
if (_activeBuffs.TryGetValue(steamId, out var state) && state.IsSilent)
{
return;
}
}
}
if (Program.Client != null && Program.Client.IsConnected && !string.IsNullOrEmpty(steamId))
{
RainOpsLog.Log($"[BUFF] Sending AdminWarn to {steamId}: {msg}");
// 防止消息中的双引号破坏RCON命令结构
msg = msg.Replace("\"", "'");
// 移除换行符
msg = msg.Replace("\r", "").Replace("\n", " ");
// AdminWarn "SteamId" "Message"
await Program.Client.SendCommandAsync($"AdminWarn \"{steamId}\" \"{msg}\"");
}
else
{
RainOpsLog.Log($"[BUFF] SendWarn Failed: Client Connected={Program.Client?.IsConnected}, SteamId={steamId}");
}
}
}
}