diff --git a/client/modules/config_ignorer.go b/client/modules/config_ignorer.go index 856e418..39f6bfe 100644 --- a/client/modules/config_ignorer.go +++ b/client/modules/config_ignorer.go @@ -19,8 +19,8 @@ func (c *ConfigIgnorer) Default(event any) (err error) { return } -func (c *ConfigIgnorer) OnKnownPacks(packet *v1_21_8.PacketCommonSelectKnownPacks) (err error) { - return c.Send(packet) +func (c *ConfigIgnorer) OnKnownPacks(packet *v1_21_8.ConfigurationToClientPacketCommonSelectKnownPacks) (err error) { + return c.Send(&v1_21_8.ConfigurationToServerPacketCommonSelectKnownPacks{Packs: packet.Packs}) } func (c *ConfigIgnorer) OnFinish(_ v1_21_8.ConfigurationToClientPacketFinishConfiguration) (err error) { diff --git a/event/event_bus.go b/event/event_bus.go index 62b9d51..425607f 100644 --- a/event/event_bus.go +++ b/event/event_bus.go @@ -45,6 +45,8 @@ func FireFound(event any, handlersMap map[reflect.Type][]reflect.Value) (found b errV := handler.Call([]reflect.Value{reflect.ValueOf(event)})[0] if !errV.IsNil() { err = errV.Interface().(error) + err = errors.Join(err, fmt.Errorf("event handler failed: at ")) + return } } } diff --git a/nbt/nbt.go b/nbt/nbt.go index b6e372a..626068f 100644 --- a/nbt/nbt.go +++ b/nbt/nbt.go @@ -25,6 +25,8 @@ const ( var Order binary.ByteOrder = binary.BigEndian +var BadStringLengthError = errors.New("bad string length") + type Tag struct { Name string Value any @@ -190,7 +192,7 @@ func readPayload(id int8, r io.Reader) (ret any, err error) { return } if len(s) != int(l) { - err = errors.New("bad string length") + err = BadStringLengthError return } ret = string(s) diff --git a/proto/connection.go b/proto/connection.go index 0819254..685b1b0 100644 --- a/proto/connection.go +++ b/proto/connection.go @@ -7,12 +7,14 @@ import ( "io" "net" + "github.com/admin-else/strom/data" "github.com/admin-else/strom/event" "github.com/admin-else/strom/proto_base" ) var BadPacketTypeError = errors.New("bad packet type") var BadPacketIdError = errors.New("bad packet id") +var PacketNotFullyDecodedError = errors.New("packet not fully decoded") // UnCodablePacket represents a packet that could not be decoded due to proto_generated not supporting all packets. type UnCodablePacket struct { @@ -33,14 +35,23 @@ func (u *UnCodablePacket) Decode(r io.Reader) (err error) { type Conn struct { net.Conn - R io.Reader - W io.Writer - CompressionThreshold int32 - State proto_base.State - Actor proto_base.Actor - Version string - ProtocolVersion int32 - DontDecodePacketsWithoutHandlers bool + R io.Reader + W io.Writer + CompressionThreshold int32 + State proto_base.State + Actor proto_base.Actor + Version string + ProtocolVersion int32 +} + +func (c *Conn) SetVersion(version string) (err error) { + c.Version = version + versionData, err := data.LookUpProtocolVersionByName(c.Version) + if err != nil { + return + } + c.ProtocolVersion = int32(versionData.Version) + return } func (c *Conn) Send(packet proto_base.EncodeDecodeAble) (err error) { @@ -48,7 +59,7 @@ func (c *Conn) Send(packet proto_base.EncodeDecodeAble) (err error) { case *UnCodablePacket: err = c.SendRaw(packet.Data) default: - i, ok := LookupPacketInfoByTypeAndMore(packet, c.State) + i, ok := LookupPacketInfoByTypeLookupPacketInfoByTypeAndMore(packet, c.State) if !ok { err = BadPacketTypeError } @@ -109,6 +120,10 @@ func (c *Conn) ReceiveRaw() (packetBytes []byte, err error) { if err != nil { return } + if len(rawPacketBytes) != int(rawPacketLen) { + err = errors.New("bad packet length") + return + } rawPacketBuffer := bytes.NewBuffer(rawPacketBytes) if c.CompressionThreshold > 0 { var packetLen int32 @@ -159,6 +174,11 @@ func (c *Conn) Receive() (packet proto_base.EncodeDecodeAble, err error) { packet = &UnCodablePacket{Err: err, Data: packetBytes, Direction: c.Actor.ReceiveDirection()} err = nil } + if b.Len() != 0 { + err = nil + packet = &UnCodablePacket{Err: PacketNotFullyDecodedError, Data: packetBytes, Direction: c.Actor.ReceiveDirection()} + return + } return } @@ -174,7 +194,7 @@ func (c *Conn) Start(insts []any) (err error) { var packet any packet, err = c.Receive() if err != nil { - return + break } err = event.Fire(packet, handlers) if err != nil { diff --git a/proto/connection_test.go b/proto/connection_test.go new file mode 100644 index 0000000..d626d2d --- /dev/null +++ b/proto/connection_test.go @@ -0,0 +1,59 @@ +package proto_test + +import ( + "bytes" + "math" + "testing" + + "github.com/admin-else/strom/proto" + "github.com/admin-else/strom/server" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" +) + +var nanIgnorer32 = cmp.Comparer(func(x, y float32) bool { + return (math.IsNaN(float64(x)) && math.IsNaN(float64(y))) || x == y +}) + +var nanIgnorer64 = cmp.Comparer(func(x, y float64) bool { + return (math.IsNaN(x) && math.IsNaN(y)) || x == y +}) + +func FuzzConn(f *testing.F) { + f.Fuzz(func(t *testing.T, inbytes []byte) { + b1 := bytes.NewBuffer(inbytes) + b2 := bytes.NewBuffer(nil) + conn := server.Servee(nil) + conn.R = b1 + conn.W = b2 + _ = conn.SetVersion("1.21.8") // cant fail + p, err := conn.Receive() + if err != nil { + t.Skip(err) + return + } + if b1.Len() != 0 { + t.Skip("not all bytes read") + return + } + err = conn.Send(p) + if err != nil { + t.Fatal(err) + return + } + b1.Reset() + conn.R = b2 + conn.W = b1 + p2, err := conn.Receive() + if err != nil { + t.Fatal(err) + return + } + if !cmp.Equal(p, p2, nanIgnorer32, nanIgnorer64, cmpopts.IgnoreFields(proto.UnCodablePacket{}, "Err")) { + t.Error("decoded and encoded decoded data are not equal", + cmp.Diff(p, p2, nanIgnorer32, nanIgnorer64, cmpopts.IgnoreFields(proto.UnCodablePacket{}, "Err")), + ) + } + return + }) +} diff --git a/proto/packet_lookup.go b/proto/packet_lookup.go index c9859d6..eb55c38 100644 --- a/proto/packet_lookup.go +++ b/proto/packet_lookup.go @@ -39,6 +39,7 @@ func LookupPacketInfoByType(packet proto_base.EncodeDecodeAble) (p proto_base.Pa PacketId: -1, ProtocolVersion: -1, } + ok = true default: for _, p := range proto_generated.Packets { if reflect.TypeOf(p.Type) == reflect.TypeOf(packet) { @@ -49,7 +50,7 @@ func LookupPacketInfoByType(packet proto_base.EncodeDecodeAble) (p proto_base.Pa return } -func LookupPacketInfoByTypeAndMore(packet proto_base.EncodeDecodeAble, state proto_base.State) (p proto_base.PacketInfo, ok bool) { +func LookupPacketInfoByTypeLookupPacketInfoByTypeAndMore(packet proto_base.EncodeDecodeAble, state proto_base.State) (p proto_base.PacketInfo, ok bool) { switch packet := packet.(type) { case *UnCodablePacket: p = proto_base.PacketInfo{ diff --git a/proto/packet_test.go b/proto/packet_test.go index 222f1f8..c57a67b 100644 --- a/proto/packet_test.go +++ b/proto/packet_test.go @@ -1,5 +1,6 @@ package proto_test +/* import ( "bytes" "math" @@ -51,3 +52,4 @@ func FuzzPlayPackets(f *testing.F) { } }) } +*/ diff --git a/proto_generator/helper_generator.go b/proto_generator/helper_generator.go index 7922c02..04d11c7 100644 --- a/proto_generator/helper_generator.go +++ b/proto_generator/helper_generator.go @@ -5,8 +5,6 @@ import ( "os" "strconv" "strings" - - "github.com/admin-else/strom/data" ) type TypesInfo struct { @@ -19,71 +17,7 @@ type PacketInfo struct { TName, Name, Direction, State, PacketId, ProtocolVersion, MinecraftVersion string } -func CollectPacketInfo(versions []string) (packetInfos []PacketInfo, err error) { - var versionData data.ProtocolVersion - for _, version := range versions { - versionData, err = data.LookUpProtocolVersionByName(version) - if err != nil { - return - } - protocol := Protocol{} - err = data.LoadVersionedJson(version, "protocol", &protocol) - if err != nil { - return - } - prefixTypeMap := map[string]TypesInfo{ - "HandshakingToServer": {protocol.Handshaking.ToServer, "ToServer", "Handshaking"}, - "HandshakingToClient": {protocol.Handshaking.ToClient, "ToClient", "Handshaking"}, - "StatusToServer": {protocol.Status.ToServer, "ToServer", "Status"}, - "StatusToClient": {protocol.Status.ToClient, "ToClient", "Status"}, - "LoginToServer": {protocol.Login.ToServer, "ToServer", "Login"}, - "LoginToClient": {protocol.Login.ToClient, "ToClient", "Login"}, - "ConfigurationToServer": {protocol.Configuration.ToServer, "ToServer", "Configuration"}, - "ConfigurationToClient": {protocol.Configuration.ToClient, "ToClient", "Configuration"}, - "PlayToServer": {protocol.Play.ToServer, "ToServer", "Play"}, - "PlayToClient": {protocol.Play.ToClient, "ToClient", "Play"}, - } - for prefix, types := range prefixTypeMap { - for k, v := range types.Types.Types { - if k != "packet" { - continue - } - packetIds := v.([]any)[1].([]any)[0].(map[string]any)["type"].([]any)[1].(map[string]any)["mappings"].(map[string]any) - packetIdsStrings := AssertAndConvertMapValues[string](packetIds) - packetIdsRev := ReverseMap(packetIdsStrings) - v := v.([]any)[1].([]any)[1].(map[string]any)["type"].([]any)[1].(map[string]any)["fields"].(map[string]any) - for k2, v2 := range v { - v2 := v2.(string) - var typeName string - if strings.HasPrefix(v2, "packet_common") { - typeName = CamelCase(v2) - } else if v2 == "void" { - typeName = "struct{}" - } else { - typeName = prefix + CamelCase(v2) - } - packetInfos = append(packetInfos, PacketInfo{ - TName: typeName, - Name: k2, - Direction: types.Direction, - State: types.State, - PacketId: packetIdsRev[k2], - ProtocolVersion: strconv.Itoa(versionData.Version), - MinecraftVersion: version, - }) - } - } - } - } - return -} - -func GeneratePacketInfoFile(versions []string) (err error) { - packetInfos, err := CollectPacketInfo(versions) - if err != nil { - return - } - +func GeneratePacketInfoFile(versions []string, packetInfos []PacketInfo) (err error) { f := NewFile("proto_generated") imports := []string{"github.com/admin-else/strom/proto_base"} for _, v := range versions { diff --git a/proto_generator/main.go b/proto_generator/main.go index 48dbed5..4880fe7 100644 --- a/proto_generator/main.go +++ b/proto_generator/main.go @@ -8,6 +8,7 @@ import ( "io" "os" "slices" + "strconv" "strings" "github.com/admin-else/strom/data" @@ -38,6 +39,8 @@ type ContainerStackEntry struct { } type Generator struct { + packetInfos []PacketInfo + // These persist the entire generate-call Natives map[string]ExprGeneratorFunc DecoderNatives map[string]FunctionGeneratorFunc @@ -243,41 +246,75 @@ func (g *Generator) GenerateTypes(prefix string, types Types) (err error) { return } -func (g *Generator) GenerateState(prefix string, state State) (err error) { - err = g.GenerateTypes(prefix+"ToServer", state.ToServer) +func (g *Generator) GenerateProtocol(protocol Protocol, version string) (err error) { + versionData, err := data.LookUpProtocolVersionByName(version) if err != nil { return } - err = g.GenerateTypes(prefix+"ToClient", state.ToClient) + + prefixTypeMap := map[string]TypesInfo{ + "": {protocol.Types, "", ""}, + "HandshakingToServer": {protocol.Handshaking.ToServer, "ToServer", "Handshaking"}, + "HandshakingToClient": {protocol.Handshaking.ToClient, "ToClient", "Handshaking"}, + "StatusToServer": {protocol.Status.ToServer, "ToServer", "Status"}, + "StatusToClient": {protocol.Status.ToClient, "ToClient", "Status"}, + "LoginToServer": {protocol.Login.ToServer, "ToServer", "Login"}, + "LoginToClient": {protocol.Login.ToClient, "ToClient", "Login"}, + "ConfigurationToServer": {protocol.Configuration.ToServer, "ToServer", "Configuration"}, + "ConfigurationToClient": {protocol.Configuration.ToClient, "ToClient", "Configuration"}, + "PlayToServer": {protocol.Play.ToServer, "ToServer", "Play"}, + "PlayToClient": {protocol.Play.ToClient, "ToClient", "Play"}, + } + + // Move common packet to their own areas to make packets more identifiable + for _, k := range OrderedKeys(protocol.Types.Types) { + v := protocol.Types.Types[k] + if strings.Contains(k, "packet") { + delete(protocol.Types.Types, k) + for prefix, types := range prefixTypeMap { + if prefix == "" { + continue + } + types.Types.Types[k] = v + } + } + } + + for prefix, types := range prefixTypeMap { + for k, v := range types.Types.Types { + if k != "packet" { + continue + } + packetIds := v.([]any)[1].([]any)[0].(map[string]any)["type"].([]any)[1].(map[string]any)["mappings"].(map[string]any) + packetIdsStrings := AssertAndConvertMapValues[string](packetIds) + packetIdsRev := ReverseMap(packetIdsStrings) + v := v.([]any)[1].([]any)[1].(map[string]any)["type"].([]any)[1].(map[string]any)["fields"].(map[string]any) + for k2, v2 := range v { + v2 := v2.(string) + typeName := prefix + CamelCase(v2) + g.packetInfos = append(g.packetInfos, PacketInfo{ + TName: typeName, + Name: k2, + Direction: types.Direction, + State: types.State, + PacketId: packetIdsRev[k2], + ProtocolVersion: strconv.Itoa(versionData.Version), + MinecraftVersion: version, + }) + } + } + } + + for prefix, types := range prefixTypeMap { + err = g.GenerateTypes(prefix, types.Types) + if err != nil { + return + } + } return } -func (g *Generator) GenerateProtocol(protocol Protocol) (err error) { - err = g.GenerateTypes("", protocol.Types) - if err != nil { - return - } - err = g.GenerateState("Handshaking", protocol.Handshaking) - if err != nil { - return - } - err = g.GenerateState("Status", protocol.Status) - if err != nil { - return - } - err = g.GenerateState("Login", protocol.Login) - if err != nil { - return - } - err = g.GenerateState("Configuration", protocol.Configuration) - if err != nil { - return - } - err = g.GenerateState("Play", protocol.Play) - return -} - -func Generate(version string, w io.Writer) (err error) { +func Generate(version string, w io.Writer) (packetInfos []PacketInfo, err error) { protocol := Protocol{} err = data.LoadVersionedJson(version, "protocol", &protocol) if err != nil { @@ -292,14 +329,16 @@ func Generate(version string, w io.Writer) (err error) { g.RegisterDecoderNatives() g.RegisterEncoderNatives() g.RegisterCompareToNatives() - err = g.GenerateProtocol(protocol) + err = g.GenerateProtocol(protocol, version) if err != nil { return } - return PrintToFile(g.File, w) + packetInfos = g.packetInfos + err = PrintToFile(g.File, w) + return } -func generateVersion(v string) (err error) { +func generateVersion(v string) (packetInfos []PacketInfo, err error) { vUnderscore := strings.ReplaceAll(v, ".", "_") err = os.MkdirAll("proto_generated/v"+vUnderscore, 0755) if err != nil { @@ -310,7 +349,7 @@ func generateVersion(v string) (err error) { return } defer f.Close() - err = Generate(v, f) + packetInfos, err = Generate(v, f) if err != nil { return } @@ -326,14 +365,16 @@ func GenerateVersions(versions []string) (err error) { if err != nil { return } + var packetInfos, newPacketInfos []PacketInfo for _, version := range versions { fmt.Println("generating", version) - err = generateVersion(version) + newPacketInfos, err = generateVersion(version) if err != nil { return } + packetInfos = append(packetInfos, newPacketInfos...) } - return GeneratePacketInfoFile(versions) + return GeneratePacketInfoFile(versions, packetInfos) } func main() {