using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Diagnostics;
using System.Linq;
using RainOpsMini.Helpers;
namespace RainOpsMini.Plugins
{
public class TicketQueryConfig
{
///
/// 开启此插件
///
public bool Enabled { get; set; } = false;
///
/// 基础偏移量
///
public int BaseOffset { get; set; } = 0x0BE67B48;
///
/// 阵营1基址偏移 (十进制)
///
public List Team1Offsets { get; set; } = new List { 0x158, 0x408, 0x8, 0x2C0, 0x2B0 };
///
/// 阵营2基址偏移 (十进制)
///
public List Team2Offsets { get; set; } = new List { 0x158, 0x408, 0x10, 0x2C0, 0x2B0 };
///
/// 游戏主程序路径
///
public string GameExePath { get; set; } = "./SquadGame/Binaries/Win64/SquadGameServer.exe";
///
/// 自动发现的候选路径列表
///
public List CandidateGameExePaths { get; set; } = new List();
}
public class TicketQueryPlugin : IPlugin, IDisposable
{
public string Name => "阵营票数查询";
public string Description => "读取阵营票数,并支持动态修改内存偏移量配置";
public PluginCategory Category => PluginCategory.BasicFunction;
public bool IsEnabled => _config.Enabled;
private TicketQueryConfig _config = new TicketQueryConfig();
private Timer? _timer;
private int _cachedTeam1Tickets;
private int _cachedTeam2Tickets;
public TicketQueryPlugin()
{
ApplyConfig();
}
public object GetConfig() => _config;
public void UpdateConfig(JsonElement config)
{
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
_config = JsonSerializer.Deserialize(config.GetRawText(), options) ?? new TicketQueryConfig();
ApplyConfig();
}
public void ResetConfig()
{
_config = new TicketQueryConfig();
ApplyConfig();
}
public void Dispose()
{
_timer?.Dispose();
}
private void ApplyConfig()
{
// 自动发现路径逻辑
var candidates = DetectGameProcesses();
_config.CandidateGameExePaths = candidates;
// 如果当前配置为空,尝试自动填充
if (string.IsNullOrEmpty(_config.GameExePath))
{
if (candidates.Count == 1)
{
_config.GameExePath = candidates[0];
}
// 如果有多个,保留为空让用户选择(通过CandidateGameExePaths查看)
}
//检测是否相对路径
if (_config.GameExePath.StartsWith("."))
{
_config.GameExePath = Path.GetFullPath(_config.GameExePath, Environment.CurrentDirectory);
}
MemoryExtraction.path = _config.GameExePath;
if (_config.Team1Offsets != null && _config.Team2Offsets != null)
{
MemoryExtraction.UpdateOffsets(_config.BaseOffset, _config.Team1Offsets, _config.Team2Offsets);
}
// Manage timer based on Enabled state
if (_config.Enabled)
{
StartTimer();
}
else
{
StopTimer();
}
}
private void StartTimer()
{
if (_timer == null)
{
// Execute immediately, then every 10 seconds
_timer = new Timer(OnTimerTick, null, 0, 10000);
}
else
{
_timer.Change(0, 10000);
}
}
private void StopTimer()
{
_timer?.Change(Timeout.Infinite, Timeout.Infinite);
}
private void OnTimerTick(object? state)
{
try
{
if (!IsEnabled) return;
// Read tickets and cache
_cachedTeam1Tickets = MemoryExtraction.TickChange(1);
_cachedTeam2Tickets = MemoryExtraction.TickChange(2);
}
catch (Exception ex)
{
RainOpsLog.Log($"[TicketQuery] 自动读取票数失败: {ex.Message}");
}
}
private List DetectGameProcesses()
{
var list = new List();
try
{
var currentDir = Environment.CurrentDirectory;
// Normalize current directory path (ensure it ends with separator for proper check)
if (!currentDir.EndsWith(Path.DirectorySeparatorChar.ToString()) &&
!currentDir.EndsWith(Path.AltDirectorySeparatorChar.ToString()))
{
currentDir += Path.DirectorySeparatorChar;
}
var processes = Process.GetProcesses();
foreach (var p in processes)
{
try
{
if (p.MainModule != null &&
!string.IsNullOrEmpty(p.MainModule.FileName) &&
p.MainModule.FileName.StartsWith(currentDir, StringComparison.OrdinalIgnoreCase) &&
p.MainModule.FileName.IndexOf("SquadGame\\Binaries\\Win64", StringComparison.OrdinalIgnoreCase) >= 0)
{
list.Add(p.MainModule.FileName);
}
}
catch { }
}
}
catch { }
return list.Distinct().ToList();
}
public int GetTickets(int teamId)
{
if (!IsEnabled) return 0;
// Return cached value instead of reading from memory every time
return teamId == 1 ? _cachedTeam1Tickets : _cachedTeam2Tickets;
}
public int SetTickets(int teamId, int tickets)
{
if (!IsEnabled) return 0;
int result = MemoryExtraction.TickChange(teamId, tickets);
// Update cache immediately
if (teamId == 1) _cachedTeam1Tickets = result;
if (teamId == 2) _cachedTeam2Tickets = result;
return result;
}
}
}