mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 15:43:49 +01:00
Wire ExcludeOpcodes config into RecordingConn so configured opcodes (e.g. ping, nop, position) are filtered at record time. Add padded metadata with in-place PatchMetadata to populate CharID/UserID after login. Implement --mode replay using protbot's encrypted connection with timing-aware packet sending, auto-ping response, concurrent S→C collection, and byte-level payload diff reporting.
136 lines
3.8 KiB
Go
136 lines
3.8 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"erupe-ce/network"
|
|
"erupe-ce/network/pcap"
|
|
)
|
|
|
|
// maxPayloadDiffs is the maximum number of byte-level diffs to report per packet.
|
|
const maxPayloadDiffs = 16
|
|
|
|
// ByteDiff describes a single byte difference between expected and actual payloads.
|
|
type ByteDiff struct {
|
|
Offset int
|
|
Expected byte
|
|
Actual byte
|
|
}
|
|
|
|
// PacketDiff describes a difference between an expected and actual packet.
|
|
type PacketDiff struct {
|
|
Index int
|
|
Expected pcap.PacketRecord
|
|
Actual *pcap.PacketRecord // nil if no response received
|
|
OpcodeMismatch bool
|
|
SizeDelta int
|
|
PayloadDiffs []ByteDiff // byte-level diffs (when opcodes match and sizes match)
|
|
}
|
|
|
|
func (d PacketDiff) String() string {
|
|
if d.Actual == nil {
|
|
if d.Expected.Opcode == 0 {
|
|
return fmt.Sprintf("#%d: unexpected extra response 0x%04X (%s)",
|
|
d.Index, d.Expected.Opcode, network.PacketID(d.Expected.Opcode))
|
|
}
|
|
return fmt.Sprintf("#%d: expected 0x%04X (%s), got no response",
|
|
d.Index, d.Expected.Opcode, network.PacketID(d.Expected.Opcode))
|
|
}
|
|
if d.OpcodeMismatch {
|
|
return fmt.Sprintf("#%d: opcode mismatch: expected 0x%04X (%s), got 0x%04X (%s)",
|
|
d.Index,
|
|
d.Expected.Opcode, network.PacketID(d.Expected.Opcode),
|
|
d.Actual.Opcode, network.PacketID(d.Actual.Opcode))
|
|
}
|
|
if d.SizeDelta != 0 {
|
|
return fmt.Sprintf("#%d: 0x%04X (%s) size delta %+d bytes",
|
|
d.Index, d.Expected.Opcode, network.PacketID(d.Expected.Opcode), d.SizeDelta)
|
|
}
|
|
if len(d.PayloadDiffs) > 0 {
|
|
var sb strings.Builder
|
|
fmt.Fprintf(&sb, "#%d: 0x%04X (%s) %d byte diff(s):",
|
|
d.Index, d.Expected.Opcode, network.PacketID(d.Expected.Opcode), len(d.PayloadDiffs))
|
|
for _, bd := range d.PayloadDiffs {
|
|
fmt.Fprintf(&sb, " [0x%04X: %02X→%02X]", bd.Offset, bd.Expected, bd.Actual)
|
|
}
|
|
return sb.String()
|
|
}
|
|
return fmt.Sprintf("#%d: 0x%04X (%s) unknown diff",
|
|
d.Index, d.Expected.Opcode, network.PacketID(d.Expected.Opcode))
|
|
}
|
|
|
|
// ComparePackets compares expected server responses against actual responses.
|
|
// Only compares S→C packets (server responses).
|
|
func ComparePackets(expected, actual []pcap.PacketRecord) []PacketDiff {
|
|
expectedS2C := pcap.FilterByDirection(expected, pcap.DirServerToClient)
|
|
actualS2C := pcap.FilterByDirection(actual, pcap.DirServerToClient)
|
|
|
|
var diffs []PacketDiff
|
|
for i, exp := range expectedS2C {
|
|
if i >= len(actualS2C) {
|
|
diffs = append(diffs, PacketDiff{
|
|
Index: i,
|
|
Expected: exp,
|
|
Actual: nil,
|
|
})
|
|
continue
|
|
}
|
|
act := actualS2C[i]
|
|
if exp.Opcode != act.Opcode {
|
|
diffs = append(diffs, PacketDiff{
|
|
Index: i,
|
|
Expected: exp,
|
|
Actual: &act,
|
|
OpcodeMismatch: true,
|
|
})
|
|
} else if len(exp.Payload) != len(act.Payload) {
|
|
diffs = append(diffs, PacketDiff{
|
|
Index: i,
|
|
Expected: exp,
|
|
Actual: &act,
|
|
SizeDelta: len(act.Payload) - len(exp.Payload),
|
|
})
|
|
} else {
|
|
// Same opcode and size — check for byte-level diffs.
|
|
byteDiffs := comparePayloads(exp.Payload, act.Payload)
|
|
if len(byteDiffs) > 0 {
|
|
diffs = append(diffs, PacketDiff{
|
|
Index: i,
|
|
Expected: exp,
|
|
Actual: &act,
|
|
PayloadDiffs: byteDiffs,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
// Extra actual packets beyond expected.
|
|
for i := len(expectedS2C); i < len(actualS2C); i++ {
|
|
act := actualS2C[i]
|
|
diffs = append(diffs, PacketDiff{
|
|
Index: i,
|
|
Expected: pcap.PacketRecord{},
|
|
Actual: &act,
|
|
})
|
|
}
|
|
|
|
return diffs
|
|
}
|
|
|
|
// comparePayloads returns byte-level diffs between two equal-length payloads.
|
|
// Returns at most maxPayloadDiffs entries.
|
|
func comparePayloads(expected, actual []byte) []ByteDiff {
|
|
var diffs []ByteDiff
|
|
for i := 0; i < len(expected) && len(diffs) < maxPayloadDiffs; i++ {
|
|
if expected[i] != actual[i] {
|
|
diffs = append(diffs, ByteDiff{
|
|
Offset: i,
|
|
Expected: expected[i],
|
|
Actual: actual[i],
|
|
})
|
|
}
|
|
}
|
|
return diffs
|
|
}
|