Files
ArachnaeSwarm/Source/ArachnaeSwarm/Pawn_Comps/ARA_UniquePawn/UniquePawnManager.cs
2026-02-05 12:01:03 +08:00

572 lines
19 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
/// <summary>
/// 唯一Pawn管理器
/// 管理所有具有唯一标识的Pawn确保同一标识只有一个存活
/// 注意数据完全绑定到当前Game不会跨存档生效
/// </summary>
public class UniquePawnManager : GameComponent
{
#region
/// <summary>
/// Pawn注册信息
/// </summary>
public class UniquePawnInfo : IExposable
{
public string globalVariable;
public int thingID;
public int spawnTick;
public Map map;
public string pawnName;
public bool isValid = true;
public int lastVerificationTick = -1;
public string gameId; // 添加游戏ID确保只对当前游戏有效
public void ExposeData()
{
Scribe_Values.Look(ref globalVariable, "globalVariable");
Scribe_Values.Look(ref thingID, "thingID");
Scribe_Values.Look(ref spawnTick, "spawnTick");
Scribe_References.Look(ref map, "map");
Scribe_Values.Look(ref pawnName, "pawnName");
Scribe_Values.Look(ref isValid, "isValid", true);
Scribe_Values.Look(ref lastVerificationTick, "lastVerificationTick", -1);
Scribe_Values.Look(ref gameId, "gameId");
}
}
#endregion
#region
private List<UniquePawnInfo> registeredPawns = new List<UniquePawnInfo>();
private int lastVerificationTick = -1;
private const int VERIFICATION_INTERVAL = 600; // 每10秒验证一次
private const int VERIFICATION_TIMEOUT = 1200; // 20秒无响应视为失效
private string currentGameId;
private bool initialized = false;
#endregion
#region
public UniquePawnManager(Game game) : base()
{
if (game == null)
return;
Initialize();
}
private void Initialize()
{
if (initialized)
return;
try
{
// 生成当前游戏的唯一ID基于游戏开始时间和随机数
GenerateGameId();
// 清理不属于当前游戏的注册信息
CleanupForeignRegistrations();
initialized = true;
}
catch (Exception ex)
{
Log.Error($"[唯一Pawn系统] 初始化失败: {ex.Message}");
}
}
/// <summary>
/// 生成当前游戏的唯一ID
/// </summary>
private void GenerateGameId()
{
if (Current.Game == null)
{
currentGameId = "NoGame";
return;
}
try
{
// 获取游戏开始时间使用tickManager的gameStartAbsTick
int gameStartTick = Find.TickManager.gameStartAbsTick;
int randomSeed = UnityEngine.Random.Range(1000, 9999);
// 获取游戏信息
string gameName = GetGameName();
// 创建唯一ID
currentGameId = $"Game_{gameStartTick}_{randomSeed}_{gameName.GetHashCode():X8}";
}
catch (Exception ex)
{
Log.Error($"[唯一Pawn系统] 生成游戏ID失败: {ex.Message}");
// 使用备用ID
currentGameId = $"Game_{Find.TickManager.TicksGame}_{UnityEngine.Random.Range(1000, 9999)}";
}
}
// <summary>
/// 获取游戏名称
/// </summary>
private string GetGameName()
{
try
{
// 方法1尝试从存档文件获取
if (Current.Game.Info != null)
{
// GameInfo中没有name属性但可能有其他标识
if (!string.IsNullOrEmpty(Current.Game.Info.permadeathModeUniqueName))
{
return Current.Game.Info.permadeathModeUniqueName;
}
}
// 方法2使用世界种子
if (Current.Game.World != null && Current.Game.World.info != null)
{
return Current.Game.World.info.seedString ?? "UnknownSeed";
}
// 方法3使用当前时间
return DateTime.Now.ToString("yyyyMMdd_HHmmss");
}
catch (Exception)
{
return "UnknownGame";
}
}
#endregion
#region
/// <summary>
/// 注册唯一Pawn
/// </summary>
public bool RegisterPawn(Pawn pawn, string globalVariable)
{
if (pawn == null || string.IsNullOrEmpty(globalVariable))
{
Log.Warning("[唯一Pawn系统] 尝试注册空Pawn或空全局变量");
return false;
}
if (!initialized)
Initialize();
// 清理无效注册
CleanupInvalidRegistrations();
// 获取当前tick
int currentTick = Find.TickManager.TicksGame;
// 检查是否已存在相同全局变量的Pawn仅限当前游戏
var existingInfos = GetInfosForVariable(globalVariable, true);
var validExistingInfos = existingInfos.Where(info => info.isValid).ToList();
// 如果有有效的已注册Pawn
if (validExistingInfos.Count > 0)
{
// 按生成时间排序,保留最早的
var earliestInfo = validExistingInfos.OrderBy(info => info.spawnTick).First();
// 如果当前Pawn不是最早的则杀死
if (earliestInfo.thingID != pawn.thingIDNumber)
{
// 杀死当前Pawn
KillDuplicatePawn(pawn, globalVariable, earliestInfo.pawnName);
return false;
}
else
{
// 当前Pawn就是最早的更新验证时间
var info = registeredPawns.FirstOrDefault(i =>
i.globalVariable == globalVariable &&
i.thingID == pawn.thingIDNumber &&
i.gameId == currentGameId);
if (info != null)
{
info.lastVerificationTick = currentTick;
info.isValid = true;
info.map = pawn.Map;
}
return true;
}
}
else
{
// 创建新注册信息(绑定到当前游戏)
var newInfo = new UniquePawnInfo
{
globalVariable = globalVariable,
thingID = pawn.thingIDNumber,
spawnTick = currentTick,
map = pawn.Map,
pawnName = pawn.Label,
isValid = true,
lastVerificationTick = currentTick,
gameId = currentGameId
};
registeredPawns.Add(newInfo);
return true;
}
}
/// <summary>
/// 注销Pawn
/// </summary>
public void UnregisterPawn(Pawn pawn, string globalVariable)
{
if (pawn == null || string.IsNullOrEmpty(globalVariable))
return;
if (!initialized)
Initialize();
var info = registeredPawns.FirstOrDefault(i =>
i.globalVariable == globalVariable &&
i.thingID == pawn.thingIDNumber &&
i.gameId == currentGameId);
if (info != null)
{
info.isValid = false;
}
}
/// <summary>
/// 发送验证信号
/// </summary>
public void SendVerificationSignal(Pawn pawn, string globalVariable)
{
if (pawn == null || string.IsNullOrEmpty(globalVariable) || pawn.Destroyed || !pawn.Spawned)
return;
if (!initialized)
Initialize();
var info = registeredPawns.FirstOrDefault(i =>
i.globalVariable == globalVariable &&
i.thingID == pawn.thingIDNumber &&
i.gameId == currentGameId);
if (info != null)
{
info.lastVerificationTick = Find.TickManager.TicksGame;
info.isValid = true;
info.map = pawn.Map;
}
else
{
// Pawn未注册尝试重新注册只注册到当前游戏
RegisterPawn(pawn, globalVariable);
}
}
/// <summary>
/// 检查Pawn是否是最早的仅限当前游戏
/// </summary>
public bool IsEarliestPawn(Pawn pawn, string globalVariable)
{
if (pawn == null || string.IsNullOrEmpty(globalVariable))
return false;
if (!initialized)
Initialize();
var existingInfos = GetInfosForVariable(globalVariable, true);
var validExistingInfos = existingInfos.Where(info => info.isValid).ToList();
if (validExistingInfos.Count == 0)
return true;
var earliestInfo = validExistingInfos.OrderBy(info => info.spawnTick).First();
return earliestInfo.thingID == pawn.thingIDNumber;
}
/// <summary>
/// 获取指定变量的所有Pawn信息
/// </summary>
public List<UniquePawnInfo> GetInfosForVariable(string globalVariable, bool onlyCurrentGame = true)
{
if (!initialized)
Initialize();
if (onlyCurrentGame)
{
return registeredPawns.Where(info =>
info.globalVariable == globalVariable &&
info.gameId == currentGameId).ToList();
}
else
{
return registeredPawns.Where(info => info.globalVariable == globalVariable).ToList();
}
}
/// <summary>
/// 获取调试信息
/// </summary>
public string GetDebugInfo()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.AppendLine("=== 唯一Pawn管理器调试信息 ===");
sb.AppendLine($"当前游戏ID: {currentGameId}");
sb.AppendLine($"已注册Pawn总数: {registeredPawns.Count}");
sb.AppendLine($"当前游戏Pawn数: {registeredPawns.Count(info => info.gameId == currentGameId)}");
var grouped = registeredPawns
.Where(info => info.gameId == currentGameId) // 只显示当前游戏的
.GroupBy(info => info.globalVariable)
.OrderBy(g => g.Key);
foreach (var group in grouped)
{
sb.AppendLine($"\n变量: {group.Key}");
var ordered = group.OrderBy(info => info.spawnTick).ToList();
for (int i = 0; i < ordered.Count; i++)
{
var info = ordered[i];
string status = i == 0 ? "[最早]" : "[重复]";
int ageTicks = Find.TickManager.TicksGame - info.spawnTick;
int lastVerifyAgo = Find.TickManager.TicksGame - info.lastVerificationTick;
sb.AppendLine($" {status} {info.pawnName} (ID: {info.thingID})");
sb.AppendLine($" 生成时间: {info.spawnTick} ({ageTicks} ticks前)");
sb.AppendLine($" 最后验证: {info.lastVerificationTick} ({lastVerifyAgo} ticks前)");
sb.AppendLine($" 地图: {info.map?.Index ?? -1}");
sb.AppendLine($" 有效: {info.isValid}");
}
}
return sb.ToString();
}
#endregion
#region
/// <summary>
/// 清理无效注册
/// </summary>
private void CleanupInvalidRegistrations()
{
int currentTick = Find.TickManager.TicksGame;
int removedCount = 0;
for (int i = registeredPawns.Count - 1; i >= 0; i--)
{
var info = registeredPawns[i];
// 移除无效的
if (!info.isValid)
{
registeredPawns.RemoveAt(i);
removedCount++;
continue;
}
// 检查超时(只检查当前游戏的)
if (info.gameId == currentGameId &&
info.lastVerificationTick > 0 &&
currentTick - info.lastVerificationTick > VERIFICATION_TIMEOUT)
{
// 尝试查找Pawn
Pawn pawn = FindPawnByID(info.thingID, info.map);
if (pawn == null || pawn.Destroyed || !pawn.Spawned)
{
info.isValid = false;
registeredPawns.RemoveAt(i);
removedCount++;
}
else
{
// Pawn仍然存在更新验证时间
info.lastVerificationTick = currentTick;
}
}
}
}
/// <summary>
/// 清理不属于当前游戏的注册信息
/// </summary>
private void CleanupForeignRegistrations()
{
if (string.IsNullOrEmpty(currentGameId))
return;
int foreignCount = 0;
for (int i = registeredPawns.Count - 1; i >= 0; i--)
{
var info = registeredPawns[i];
if (info.gameId != currentGameId)
{
registeredPawns.RemoveAt(i);
foreignCount++;
}
}
}
/// <summary>
/// 通过ID查找Pawn
/// </summary>
private Pawn FindPawnByID(int thingID, Map map)
{
if (map == null || thingID <= 0)
return null;
try
{
return map.listerThings.ThingsInGroup(ThingRequestGroup.Pawn)
.OfType<Pawn>()
.FirstOrDefault(p => p.thingIDNumber == thingID);
}
catch (Exception ex)
{
Log.Error($"[唯一Pawn系统] 查找Pawn失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 杀死重复的Pawn
/// </summary>
private void KillDuplicatePawn(Pawn pawn, string globalVariable, string earliestPawnName)
{
try
{
if (pawn == null || pawn.Destroyed || !pawn.Spawned)
return;
// 显示死亡消息
string deathMessage = $"{pawn.Label} 被移除,因为 {earliestPawnName} 是更早生成的唯一Pawn ({globalVariable})";
Messages.Message(deathMessage, pawn, MessageTypeDefOf.NegativeEvent);
// 使用安全的杀死方法
pawn.Kill(null);
}
catch (Exception ex)
{
Log.Error($"[唯一Pawn系统] 杀死重复Pawn失败: {ex.Message}");
}
}
/// <summary>
/// 定期验证所有Pawn只验证当前游戏的
/// </summary>
private void VerifyAllPawns()
{
int currentTick = Find.TickManager.TicksGame;
int verifiedCount = 0;
int timeoutCount = 0;
foreach (var info in registeredPawns.Where(i => i.isValid && i.gameId == currentGameId))
{
// 查找Pawn
Pawn pawn = FindPawnByID(info.thingID, info.map);
if (pawn == null || pawn.Destroyed || !pawn.Spawned)
{
// Pawn已不存在
info.isValid = false;
timeoutCount++;
}
else if (currentTick - info.lastVerificationTick > VERIFICATION_INTERVAL)
{
// 需要发送验证请求
SendVerificationRequest(pawn, info.globalVariable);
}
else
{
verifiedCount++;
}
}
// 清理无效的
CleanupInvalidRegistrations();
}
/// <summary>
/// 发送验证请求给Pawn
/// </summary>
private void SendVerificationRequest(Pawn pawn, string globalVariable)
{
// 发送验证信号
var comp = pawn.TryGetComp<CompUniquePawn>();
if (comp != null)
{
comp.SendVerificationSignal();
}
}
#endregion
#region GameComponent实现
public override void GameComponentTick()
{
base.GameComponentTick();
if (!initialized)
Initialize();
if (Find.TickManager.TicksGame - lastVerificationTick > VERIFICATION_INTERVAL)
{
VerifyAllPawns();
lastVerificationTick = Find.TickManager.TicksGame;
}
}
public override void ExposeData()
{
base.ExposeData();
Scribe_Collections.Look(ref registeredPawns, "registeredPawns", LookMode.Deep);
Scribe_Values.Look(ref lastVerificationTick, "lastVerificationTick", -1);
Scribe_Values.Look(ref currentGameId, "currentGameId");
Scribe_Values.Look(ref initialized, "initialized", false);
if (Scribe.mode == LoadSaveMode.LoadingVars)
{
// 加载后重新初始化
initialized = false;
}
else if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
// 加载后初始化
Initialize();
// 清理无效数据
CleanupInvalidRegistrations();
}
}
public override void StartedNewGame()
{
base.StartedNewGame();
// 开始新游戏时重新初始化
initialized = false;
Initialize();
}
public override void LoadedGame()
{
base.LoadedGame();
// 加载游戏时确保初始化
if (!initialized)
Initialize();
}
#endregion
}
}