mirror of
https://gitee.com/xiarenalofs/squad-rain-ops-mini.git
synced 2026-08-06 05:16:26 +08:00
209 lines
6.6 KiB
C#
209 lines
6.6 KiB
C#
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
|
||
{
|
||
/// <summary>
|
||
/// 开启此插件
|
||
/// </summary>
|
||
public bool Enabled { get; set; } = false;
|
||
|
||
/// <summary>
|
||
/// 基础偏移量
|
||
/// </summary>
|
||
public int BaseOffset { get; set; } = 0x0BE67B48;
|
||
|
||
/// <summary>
|
||
/// 阵营1基址偏移 (十进制)
|
||
/// </summary>
|
||
public List<int> Team1Offsets { get; set; } = new List<int> { 0x158, 0x408, 0x8, 0x2C0, 0x2B0 };
|
||
|
||
/// <summary>
|
||
/// 阵营2基址偏移 (十进制)
|
||
/// </summary>
|
||
public List<int> Team2Offsets { get; set; } = new List<int> { 0x158, 0x408, 0x10, 0x2C0, 0x2B0 };
|
||
|
||
/// <summary>
|
||
/// 游戏主程序路径
|
||
/// </summary>
|
||
public string GameExePath { get; set; } = "./SquadGame/Binaries/Win64/SquadGameServer.exe";
|
||
|
||
/// <summary>
|
||
/// 自动发现的候选路径列表
|
||
/// </summary>
|
||
public List<string> CandidateGameExePaths { get; set; } = new List<string>();
|
||
}
|
||
|
||
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<TicketQueryConfig>(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<string> DetectGameProcesses()
|
||
{
|
||
var list = new List<string>();
|
||
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;
|
||
}
|
||
}
|
||
}
|