diff --git a/cmd/strom/print_replay/failed_packet.go.tmpl b/cmd/strom/print_replay/failed_packet.go.tmpl index bd4e7bb..7ff9249 100644 --- a/cmd/strom/print_replay/failed_packet.go.tmpl +++ b/cmd/strom/print_replay/failed_packet.go.tmpl @@ -4,15 +4,15 @@ import ( "bytes" "testing" - "github.com/admin-else/strom/proto_generated/v1_21_8" + generated "github.com/admin-else/strom/proto_generated/%v" ) // Error: %v func TestFailedPacket%10X(t *testing.T) { - p := v1_21_8.Play%vPacket{} - b := bytes.NewBuffer(%#v) + p := &generated.%v{} + b := bytes.NewBuffer(%#v) err := p.Decode(b) if err != nil { t.Fatal(err) } -} \ No newline at end of file +} diff --git a/cmd/strom/print_replay/main.go b/cmd/strom/print_replay/main.go index 837fb27..39379bd 100644 --- a/cmd/strom/print_replay/main.go +++ b/cmd/strom/print_replay/main.go @@ -5,13 +5,16 @@ import ( "crypto/sha256" _ "embed" "encoding/binary" + "errors" "flag" "fmt" "io" "log/slog" "os" "reflect" + "strings" + "github.com/admin-else/strom/data" "github.com/admin-else/strom/proto" "github.com/admin-else/strom/proto_base" ) @@ -20,13 +23,13 @@ var ( cmd = flag.NewFlagSet("print-replay", flag.ContinueOnError) FileFlag = cmd.String("file", "", "The tmcpr file to print") ProtocolVersion = cmd.Int("protocol", -1, "The protocol version") - CreateTestsPath = cmd.String("create-tests", "", "The path to create tests from failed packets, if empty no tests will be created") + CreateTestsPath = cmd.String("create-tests", "", "") ) //go:embed failed_packet.go.tmpl var TestSrcF string -func SaveUnCodeAbleAsTest(d proto_base.Direction, packet *proto.UnCodablePacket) { +func SaveUnCodeAbleAsTest(packet *proto.UnCodablePacket) { hUntrimmed := sha256.Sum256(packet.Data) h := hUntrimmed[:8] f, err := os.Create(fmt.Sprintf(*CreateTestsPath+"/%v_%10x_test.go", len(packet.Data), h)) @@ -34,9 +37,15 @@ func SaveUnCodeAbleAsTest(d proto_base.Direction, packet *proto.UnCodablePacket) panic(err) } + ret, err := data.LookUpVersionByProtocolVersion(packet.Info.ProtocolVersion) + if err != nil { + panic(err) + } + goPackage := "v" + strings.ReplaceAll(ret.MinecraftVersion, ".", "_") + //goland:noinspection GoUnhandledErrorResult defer f.Close() - _, err = fmt.Fprintf(f, TestSrcF, packet.Err, h, d.Opposite(), packet.Data) + _, err = fmt.Fprintf(f, TestSrcF, goPackage, packet.Err, h, reflect.TypeOf(packet.Info.Type).Elem().Name(), packet.Data) if err != nil { panic(err) } @@ -55,26 +64,35 @@ func UnpackPacket(r io.Reader) (packet proto_base.EncodeDecodeAble, err error) { if err != nil { return } - b := bytes.NewBuffer(nil) - _, err = io.CopyN(b, r, int64(length)) + + // Read the packet data into a byte slice + packetData := make([]byte, length) + _, err = io.ReadFull(r, packetData) if err != nil { return } + // Use bytes.Reader instead of bytes.Buffer + b := bytes.NewReader(packetData) + packetId, err := proto_base.DecodeVarInt(b) if err != nil { return } + // Calculate position after reading the packet ID + // indexafterid represents how many bytes we've read so far + indexafterid := int(b.Size()) - int(b.Len()) + i, ok := proto.LookUpTypeByPacketInfo(proto_base.ToClient, state, packetId, int32(*ProtocolVersion)) if !ok { - packet = &proto.UnCodablePacket{Err: proto.BadPacketIdErr, Data: b.Bytes(), Direction: proto_base.ToClient} + packet = &proto.UnCodablePacket{Err: proto.BadPacketIdErr, Data: packetData, Info: i} return } packet = reflect.New(reflect.TypeOf(i.Type).Elem()).Interface().(proto_base.EncodeDecodeAble) err = packet.Decode(b) if err != nil { - packet = &proto.UnCodablePacket{Err: err, Data: b.Bytes(), Direction: proto_base.ToClient} + packet = &proto.UnCodablePacket{Err: err, Data: packetData[indexafterid:], Info: i} return } switch i.Name { @@ -85,7 +103,6 @@ func UnpackPacket(r io.Reader) (packet proto_base.EncodeDecodeAble, err error) { } return } - func Run(args []string) (err error) { err = cmd.Parse(args) if err != nil { @@ -103,17 +120,28 @@ func Run(args []string) (err error) { } defer f.Close() + var totalPackets, failedPackets int for { var packet proto_base.EncodeDecodeAble packet, err = UnpackPacket(f) unCodablePacket, ok := packet.(*proto.UnCodablePacket) - if ok && *CreateTestsPath != "" { - SaveUnCodeAbleAsTest(proto_base.ToClient, unCodablePacket) + if ok { + if *CreateTestsPath != "" { + SaveUnCodeAbleAsTest(unCodablePacket) + } + failedPackets++ } if err != nil { - return + fmt.Println(err) + } else { + fmt.Printf("Packet: %#v\n", packet) } - fmt.Printf("Packet: %#v\n", packet) + if errors.Is(err, io.EOF) { + break + } + totalPackets++ } + fmt.Printf("Total packets: %d, failed packets: %d, %%failed: %.2f%%\n", totalPackets, failedPackets, float64(failedPackets)/float64(totalPackets)*100) + return } diff --git a/proto/connection.go b/proto/connection.go index 312641a..99c20fd 100644 --- a/proto/connection.go +++ b/proto/connection.go @@ -41,9 +41,9 @@ var EarliestVersion = proto_generated.SupportedVersions[0] // UnCodablePacket represents a packet that could not be decoded. type UnCodablePacket struct { - Err error - Data []byte - Direction proto_base.Direction + Err error + Data []byte + Info proto_base.PacketInfo } func (u *UnCodablePacket) Encode(w io.Writer) (err error) { @@ -242,7 +242,7 @@ func (c *Conn) Receive() (packet proto_base.EncodeDecodeAble, err error) { i, ok := LookUpTypeByPacketInfo(c.Actor.ReceiveDirection(), c.State(), id, c.ProtocolVersion) if !ok { - packet = &UnCodablePacket{Err: BadPacketIdErr, Data: packetBytes, Direction: c.Actor.ReceiveDirection()} + packet = &UnCodablePacket{Err: BadPacketIdErr, Data: packetBytes, Info: i} return } packet = reflect.New(reflect.TypeOf(i.Type).Elem()).Interface().(proto_base.EncodeDecodeAble) @@ -250,18 +250,18 @@ func (c *Conn) Receive() (packet proto_base.EncodeDecodeAble, err error) { slices.ContainsFunc(c.DebugPrintPackets, func(s string) bool { return strings.Contains(i.Name, s) }) isListenerRegistered := c.IsTypeRegistered(reflect.TypeOf(packet)) if !isListenerRegistered && !debugPrint { - packet = &UnCodablePacket{Err: NoHandlerRegisteredErr, Data: packetBytes, Direction: c.Actor.ReceiveDirection()} + packet = &UnCodablePacket{Err: NoHandlerRegisteredErr, Data: packetBytes, Info: i} return } err = packet.Decode(b) if err != nil { - packet = &UnCodablePacket{Err: err, Data: packetBytes, Direction: c.Actor.ReceiveDirection()} + packet = &UnCodablePacket{Err: err, Data: packetBytes, Info: i} err = nil return } if b.Len() != 0 { err = nil - packet = &UnCodablePacket{Err: PacketNotFullyDecodedErr, Data: packetBytes, Direction: c.Actor.ReceiveDirection()} + packet = &UnCodablePacket{Err: PacketNotFullyDecodedErr, Data: packetBytes, Info: i} return } if debugPrint { diff --git a/proto/packet_lookup.go b/proto/packet_lookup.go index b4c8ade..0f14778 100644 --- a/proto/packet_lookup.go +++ b/proto/packet_lookup.go @@ -25,6 +25,14 @@ func init() { func LookUpTypeByPacketInfo(direction proto_base.Direction, state proto_base.State, pid, version int32) (p proto_base.PacketInfo, ok bool) { pP, ok := pidMap[PidEtc{direction, state, pid, version}] if !ok { + p = proto_base.PacketInfo{ + Type: nil, + Name: "", + Direction: direction, + State: state, + PacketId: pid, + ProtocolVersion: version, + } return } p = *pP @@ -43,7 +51,7 @@ func LookUpTypeByPacketInfoAndCopyType(direction proto_base.Direction, state pro func LookupPacketInfoByType(packet proto_base.EncodeDecodeAble) (p proto_base.PacketInfo, ok bool) { undecodedPacket, isUndecoable := packet.(*UnCodablePacket) if isUndecoable { - p = proto_base.PacketInfo{Type: &UnCodablePacket{}, Name: "un_codable", Direction: undecodedPacket.Direction, State: proto_base.Handshaking, PacketId: -1, ProtocolVersion: -1} + p = undecodedPacket.Info ok = true return } diff --git a/proto_base/base_types.go b/proto_base/base_types.go index c638cd0..098ae6a 100644 --- a/proto_base/base_types.go +++ b/proto_base/base_types.go @@ -42,6 +42,23 @@ const ( Play ) +func (s State) String() string { + switch s { + case Handshaking: + return "Handshaking" + case Status: + return "Status" + case Login: + return "Login" + case Configuration: + return "Configuration" + case Play: + return "Play" + default: + return "Unknown" + } +} + type Actor int const (