diff --git a/Plugin.cs b/Plugin.cs index 7e6af37..bb16f92 100644 --- a/Plugin.cs +++ b/Plugin.cs @@ -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)