feat: 增加 EnsureOpcodes 自动兜底装载与重载提示,消除手动装载时序依赖

This commit is contained in:
2026-10-14 00:35:47 +08:00
parent 2e1f00087a
commit 702d770d07
+15 -3
View File
@@ -86,8 +86,7 @@ public sealed class Plugin : IDalamudPlugin
adaptationReport=game.EvaluateAdaptation(modules);
AdaptationManifest.ApplyGate(modules,adaptationReport);
});
var opcodeFile=Path.Combine(directory,"opcodes.json");
if(File.Exists(opcodeFile)) Try(() => opcodes.Load(File.ReadAllText(opcodeFile),gameVersion,"本地缓存"));
Try(() => EnsureOpcodes());
serviceBar=dtrBar.Get("IC Rebuild · 高度与迷宫");
serviceBar.Shown=false;
serviceBar.OnClick=_=>Open();
@@ -339,7 +338,7 @@ public sealed class Plugin : IDalamudPlugin
ImGui.SameLine(); if(ImGui.Button("导出记录") && pending?.IsCompleted!=false)
{ var exportRecords=packets.Snapshot(); Try(()=>{ var path=DataPath(file); pending=Background(()=>PacketFiles.ExportAtomic(path,exportRecords)); }); }
ImGui.SameLine(); if(ImGui.Button("清空记录")) packets.Clear();
if(ImGui.Button("载入本地 opcodes.json")) Try(()=>opcodes.Load(File.ReadAllText(DataPath("opcodes.json")),gameVersion,"本地文件"));
if(ImGui.Button("载入本地 opcodes.json")) Try(()=>{ EnsureOpcodes(true); notice=$"已成功载入 {gameVersion} Opcode 映射({opcodes.Snapshot.Source})"; });
ImGui.SameLine(); if(ImGui.Button("更新在线 Opcode") && pending?.IsCompleted!=false) pending=UpdateOpcodes();
ImGui.TextWrapped($"映射版本:{opcodes.Snapshot.GameVersion},来源:{opcodes.Snapshot.Source}");
var url=config.OpcodeUrl;
@@ -372,10 +371,13 @@ public sealed class Plugin : IDalamudPlugin
var dispatcher=network.ActiveSender;
ImGui.TextWrapped($"原生发送通道:{(network.CanSendNative ? "已就绪 (ReceiverSendPacket + ZoneUp)" : "未就绪(需开启 36 或完成绑定)")} · 队列 {dispatcher.PendingQueueCount} · 等待响应 {dispatcher.InFlightCount}");
ImGui.TextWrapped($"发送会话:{(session.IsValid ? $"就绪 · 区域 {session.Territory} · Entity 0x{session.LocalEntityId:X8}" : "未就绪(需角色在场且非过图/占卜回放)")}");
if(opcodes.Snapshot.GameVersion!=gameVersion)
ImGui.TextColored(new Vector4(1,0.4f,0.4f,1),$"提示:Opcode 映射尚未就绪(当前为 {opcodes.Snapshot.GameVersion});发送时将自动载入");
ImGui.InputInt("测试技能 ID (UP_ActionSend)",ref manualSendActionId);
ImGui.InputInt("请求序号 RequestId",ref manualSendRequestId);
if(ImGui.Button("加入技能发送队列 (当前目标)")) Try(()=>
{
EnsureOpcodes();
if(manualSendActionId<=0) throw new ArgumentException("请输入有效技能 ID");
if(manualSendRequestId is <=0 or >ushort.MaxValue) manualSendRequestId=1;
var targetId=game.Targets.Target?.EntityId is >0 and var tid ? (ulong)tid : session.LocalEntityId;
@@ -387,6 +389,7 @@ public sealed class Plugin : IDalamudPlugin
ImGui.SameLine();
if(ImGui.Button("加入 ClientTrigger 队列")) Try(()=>
{
EnsureOpcodes();
if(manualTriggerCommand<=0 || manualTriggerCommand>ushort.MaxValue) throw new ArgumentException("指令编号超出范围");
var packet=AuditedPacketEncoder.EncodeClientTrigger(opcodes,gameVersion,(uint)manualTriggerCommand,(uint)Math.Max(0,manualTriggerParam1));
var tx=network.EnqueueSend(packet);
@@ -412,6 +415,15 @@ public sealed class Plugin : IDalamudPlugin
if(string.IsNullOrWhiteSpace(name) || Path.GetFileName(name)!=name || name.IndexOfAny(Path.GetInvalidFileNameChars())>=0) throw new ArgumentException("请输入当前记录目录中的文件名");
return Path.Combine(directory,name);
}
private void EnsureOpcodes(bool force=false)
{
if(!force && opcodes.Snapshot.GameVersion==gameVersion) return;
var path=DataPath("opcodes.json");
if(!File.Exists(path) && pi.AssemblyLocation.DirectoryName is { } devDir)
path=Path.Combine(devDir,"opcodes.json");
if(File.Exists(path))
opcodes.Load(File.ReadAllText(path),gameVersion,"本地文件");
}
private async Task Background(Action action)
{ try { await Task.Run(action,cancellation.Token); if(!disposed) notice="文件操作完成"; } catch(Exception ex) { if(!disposed) notice=ex.Message; } }
private async Task SaveCaptureAsync(string path,PacketRecord[] records)