Initial import of IC.Rebuild
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using ICRebuild.Core;
|
||||
|
||||
if(args.Length!=3) throw new ArgumentException("CaptureAudit capture.jsonl opcodes.json report.json");
|
||||
var packets=PacketFiles.Import(args[0]);
|
||||
var versions=packets.Select(p=>p.GameVersion).Distinct().ToArray();
|
||||
if(versions.Length!=1 || packets.Length==0) throw new InvalidDataException("Expected one nonempty capture version");
|
||||
var map=new OpcodeMap(); map.Load(File.ReadAllText(args[1]),versions[0],"local cache");
|
||||
string? Name(PacketRecord p)=>map.Resolve(p.GameVersion,p.Direction,p.Opcode);
|
||||
var supported=new HashSet<string>{
|
||||
"UP_PingReq","DOWN_PingRsp","UP_UpdatePositionHandler","UP_UpdatePositionInstance","UP_EventStart",
|
||||
"UP_ActionSend","UP_ActionSendPos","UP_ClientTrigger",
|
||||
"UP_EventFinish","UP_EventFinish4","UP_EventFinish8","UP_EventFinish16","UP_EventFinish32","UP_EventFinish64","UP_EventFinish128","UP_EventFinish255",
|
||||
"UP_EventAction","UP_EventAction4","UP_EventAction8","UP_EventAction16","UP_EventAction32","UP_EventAction64","UP_EventAction128","UP_EventAction255",
|
||||
"DOWN_EffectResult","DOWN_UpdateHate","DOWN_UpdateHater","DOWN_EffectResultBasic","DOWN_JobGauge","DOWN_UpdateClassInfo","DOWN_FirstAttack",
|
||||
"DOWN_ActorControl","DOWN_ActorControlSelf","DOWN_ActorControlTarget","DOWN_Effect","DOWN_StatusEffectList","DOWN_ActorDelete","DOWN_ObjectSpawn","DOWN_NpcSpawn"
|
||||
};
|
||||
var parsed=packets.Select(p=>(Packet:p,Name:Name(p),Result:p.PayloadComplete ? PacketParser.Parse(Name(p),p.Payload,p.GameVersion) : new ParseResult(false,"incomplete"))).ToArray();
|
||||
var correlations=PacketCorrelator.Ping(packets,map);
|
||||
var actionCorrelations=PacketCorrelator.Actions(packets,map);
|
||||
// Replay owned sample data through the manager and recorder; no native calls or sockets.
|
||||
using var manager=new NetworkPipeline(versions[0]);
|
||||
var replayBuffer=new PacketBuffer { Paused=false };
|
||||
var recorder=new PacketRecorder(replayBuffer);
|
||||
using var up=manager.Subscribe(NetworkPhase.BeforeSend,recorder.Record);
|
||||
using var down=manager.Subscribe(NetworkPhase.Received,recorder.Record);
|
||||
long returned=0;
|
||||
using var post=manager.Subscribe(NetworkPhase.AfterSend,_=>returned++);
|
||||
foreach(var p in packets)
|
||||
{
|
||||
CaptureRead Read(Func<int,bool> accepts)=>accepts(p.Opcode) ? new(new(p.Opcode,p.Target,p.Payload,p.PayloadComplete,p.CaptureIssue)) : new(null);
|
||||
if(p.Direction==PacketDirection.Up) manager.Send(()=>1,Read); else manager.Receive(()=>1,Read);
|
||||
}
|
||||
var replayed=replayBuffer.Snapshot();
|
||||
var replayMatches=replayed.Length==packets.Length && replayed.Zip(packets).All(pair=>
|
||||
pair.First.Direction==pair.Second.Direction && pair.First.Opcode==pair.Second.Opcode &&
|
||||
pair.First.GameVersion==pair.Second.GameVersion && pair.First.Target==pair.Second.Target &&
|
||||
pair.First.PayloadComplete==pair.Second.PayloadComplete && pair.First.CaptureIssue==pair.Second.CaptureIssue &&
|
||||
pair.First.Payload.SequenceEqual(pair.Second.Payload));
|
||||
if(!replayMatches || manager.ObserverErrors!=0 || returned!=packets.Count(p=>p.Direction==PacketDirection.Up))
|
||||
throw new InvalidDataException("Manager replay changed sample data or missed send completion observations");
|
||||
var report=new {
|
||||
Source=Path.GetFileName(args[0]),SHA256=Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(args[0]))),
|
||||
MapSHA256=Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(args[1]))),MapSource=map.Snapshot.Source,Version=versions[0],
|
||||
Records=packets.Length,Up=packets.Count(p=>p.Direction==PacketDirection.Up),Down=packets.Count(p=>p.Direction==PacketDirection.Down),
|
||||
ManagerReplay=new {Matches=replayMatches,Records=replayed.Length,SendReturns=returned,manager.ObserverErrors,manager.ReadErrors},
|
||||
FirstTime=packets.Min(p=>p.Time),LastTime=packets.Max(p=>p.Time),FirstSequence=packets.First().Sequence,LastSequence=packets.Last().Sequence,
|
||||
SequenceContinuous=packets.Zip(packets.Skip(1)).All(p=>p.Second.Sequence==p.First.Sequence+1),
|
||||
MonotonicOrder=packets.Zip(packets.Skip(1)).All(p=>p.Second.MonotonicTicks>=p.First.MonotonicTicks),
|
||||
Complete=packets.Count(p=>p.PayloadComplete),Incomplete=packets.Count(p=>!p.PayloadComplete),CaptureIssues=packets.Count(p=>p.CaptureIssue!=null),
|
||||
PayloadBytes=packets.Sum(p=>p.Payload.Length),MinPayload=packets.Min(p=>p.Payload.Length),MaxPayload=packets.Max(p=>p.Payload.Length),
|
||||
EmptyPayloads=packets.Count(p=>p.Payload.Length==0),Known=packets.Count(p=>Name(p)!=null),Unknown=packets.Count(p=>Name(p)==null),
|
||||
Parsed=parsed.Count(p=>p.Result.Parsed),SupportedButRejected=parsed.Count(p=>p.Name!=null && supported.Contains(p.Name) && !p.Result.Parsed),
|
||||
Groups=parsed.GroupBy(p=>new{p.Packet.Direction,p.Packet.Opcode,p.Name,Length=p.Packet.Payload.Length,p.Packet.PayloadComplete,p.Result.Parsed})
|
||||
.OrderByDescending(g=>g.Count()).Select(g=>new{g.Key,Count=g.Count(),Rejection=g.Key.Name!=null && supported.Contains(g.Key.Name) && !g.Key.Parsed ? g.First().Result.Summary : null}),
|
||||
PingPairs=correlations.Select(p=>new{p.Request,p.Response,Milliseconds=p.Elapsed.TotalMilliseconds}),
|
||||
ActionPairs=actionCorrelations.Select(p=>new{p.Request,p.Response,Milliseconds=p.Elapsed.TotalMilliseconds}),
|
||||
Limitations="No raw payload, coordinates or actor IDs included. Successful parsing does not verify all fields. Header-read failures absent from export require UI counters."
|
||||
};
|
||||
File.WriteAllText(args[2],JsonSerializer.Serialize(report,new JsonSerializerOptions{WriteIndented=true}));
|
||||
Console.WriteLine($"Records={report.Records}; up={report.Up}; down={report.Down}; complete={report.Complete}; known={report.Known}; parsed={report.Parsed}; rejected={report.SupportedButRejected}; pingPairs={correlations.Length}; actionPairs={actionCorrelations.Length}");
|
||||
Reference in New Issue
Block a user