diff --git a/client/config_ignorer.go b/client/config_ignorer.go index 4b87a22..c144b5e 100644 --- a/client/config_ignorer.go +++ b/client/config_ignorer.go @@ -16,7 +16,7 @@ type ConfigIgnorer struct { } func (c *ConfigIgnorer) OnStart() (err error) { - c.Register(c.Default) + c.RegisterCritical(c.Default) c.RegisterUntilLatest(c.OnKnownPacks) c.RegisterUntilLatest(c.OnFinish) c.RegisterUntilLatest(c.OnPing) diff --git a/client/login.go b/client/login.go index fad5200..761cdb4 100644 --- a/client/login.go +++ b/client/login.go @@ -124,9 +124,9 @@ func (s *LoginClient) OnSuccess(success *v1_21_8.LoginToClientPacketSuccess) (er } func (s *LoginClient) OnStart() (err error) { - s.Register(s.OnDefault) - s.Register(s.OnCycle) - s.Register(s.OnClose) + s.RegisterCritical(s.OnDefault) + s.RegisterCritical(s.OnCycle) + s.RegisterCritical(s.OnClose) s.RegisterUntilLatest(s.OnCompress) s.RegisterUntilLatest(s.OnEncrypt) s.RegisterUntilLatest(s.OnDisconnect) diff --git a/event/event_bus.go b/event/event_bus.go index 9e50f1c..e855825 100644 --- a/event/event_bus.go +++ b/event/event_bus.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "log/slog" "reflect" "time" ) @@ -60,7 +61,7 @@ func (e HandlerDoneErr) Unwrap() error { var ErrDontForward = errors.New("dont forward") var WhileClosingErr = errors.New("error while closing") -func ValidateHandler(h any) (hv reflect.Value, eventType reflect.Type) { +func ValidateHandler(h any) (eventType reflect.Type, hv reflect.Value) { hv = reflect.ValueOf(h) if hv.Kind() != reflect.Func { panic("expected method") @@ -75,8 +76,31 @@ func ValidateHandler(h any) (hv reflect.Value, eventType reflect.Type) { return } +// Register registers a handler that will not close the loop if it returns an error. +// Instead, it will log it to slog.Error +// Warning: If a bad handler is passed in the program will panic. func (l *Loop) Register(h any) { - hv, eventType := ValidateHandler(h) + l.RegisterCustomType(ValidateHandler(h)) +} + +// RegisterCustomType registers a handler that will not close the loop if it returns an error. +// Instead, it will log it to slog.Error +// Warning: If a bad handler is passed in the program will panic. +func (l *Loop) RegisterCustomType(eventType reflect.Type, hv reflect.Value) { + handleFunc := func(packet any) error { + v := hv.Call([]reflect.Value{reflect.ValueOf(packet)})[0] + if !v.IsNil() { + slog.Error("handler failed", v.Interface().(error)) + } + return nil + } + l.RegisterDirect(eventType, reflect.ValueOf(handleFunc)) +} + +// RegisterCritical registers a handler that will close the loop if it returns an error. +// Warning: If a bad handler is passed in the program will panic. +func (l *Loop) RegisterCritical(h any) { + eventType, hv := ValidateHandler(h) l.RegisterDirect(eventType, hv) } diff --git a/event/stdin.go b/event/stdin.go index 2f9ad88..2123faf 100644 --- a/event/stdin.go +++ b/event/stdin.go @@ -38,7 +38,7 @@ func (s *StdinHandler) ScanJob() { } } func (s *StdinHandler) OnStart() (err error) { - s.Register(s.OnTick) + s.RegisterCritical(s.OnTick) s.stdInChan = make(chan Stdin) go s.ScanJob() return diff --git a/proto/connection.go b/proto/connection.go index 615ddee..5477fd8 100644 --- a/proto/connection.go +++ b/proto/connection.go @@ -7,7 +7,6 @@ import ( "io" "log/slog" "reflect" - "slices" "sync" "github.com/admin-else/strom/data" @@ -60,54 +59,8 @@ type Conn struct { packetCh chan proto_base.EncodeDecodeAble } -func (c *Conn) RegisterUntilLatest(h any) { - c.RegisterUntil(h, LatestVersion) -} - -func (c *Conn) RegisterUntil(h any, until string) { - hv, eventType := event.ValidateHandler(h) - c.RegisterDirect(eventType, hv) - if !eventType.Implements(reflect.TypeFor[proto_base.EncodeDecodeAble]()) { - panic("expected method with argument that implements proto_base.EncodeDecodeAble") - } - packetInfo, found := LookupPacketInfoByType(reflect.Zero(eventType).Interface().(proto_base.EncodeDecodeAble)) - if !found { - panic("packet not found") - } - versionInfo, err := data.LookUpVersionByProtocolVersion(packetInfo.ProtocolVersion) - if err != nil { - panic("protocol version not found") - } - startIndex := slices.Index(proto_generated.SupportedVersions, versionInfo.MinecraftVersion) - endIndex := slices.Index(proto_generated.SupportedVersions, until) - for i := startIndex + 1; i <= endIndex; i++ { - versionName := proto_generated.SupportedVersions[i] - versionInfo, err = data.LookUpVersionByName(versionName) - if err != nil { - panic("protocol version not found") - } - var newPacketInfo proto_base.PacketInfo - newPacketInfo, found = LookupPacketInfoByNameProtocolVersionAndState(packetInfo.Name, versionInfo.Version, packetInfo.State) - if !found { - panic("packet not found") - } - newT := reflect.TypeOf(newPacketInfo.Type) - if !SmartConvertibleTo(newT, reflect.TypeOf(packetInfo.Type)) { - panic(fmt.Sprintf("packet %v is not convertible to %v", newT, reflect.TypeOf(packetInfo.Type))) - } - handleFunc := func(packet any) error { - v := hv.Call([]reflect.Value{SmartConvert(reflect.ValueOf(packet), reflect.TypeOf(packetInfo.Type))})[0] - if v.IsNil() { - return nil - } - return v.Interface().(error) - } - c.RegisterDirect(newT, reflect.ValueOf(handleFunc)) - } -} - func (c *Conn) OnStart() (err error) { - c.Register(c.OnTick) + c.RegisterCritical(c.OnTick) return c.handler.OnStart() } diff --git a/proto/smart_convert.go b/proto/smart_convert.go index 8433ad6..5ed4956 100644 --- a/proto/smart_convert.go +++ b/proto/smart_convert.go @@ -1,7 +1,14 @@ package proto import ( + "fmt" "reflect" + "slices" + + "github.com/admin-else/strom/data" + "github.com/admin-else/strom/event" + "github.com/admin-else/strom/proto_base" + "github.com/admin-else/strom/proto_generated" ) func SmartConvertibleTo(from, to reflect.Type) bool { @@ -40,3 +47,75 @@ func SmartConvert(from reflect.Value, to reflect.Type) (ret reflect.Value) { return from.Convert(to) } } + +// RegisterUntilLatest registers a handler for all versions between the passed in packet version and the latest supported version. +// Warning: If a bad handler is passed in, the program will panic. +func (c *Conn) RegisterUntilLatest(h any) { + c.registerUntil(h, LatestVersion, false) +} + +// RegisterUntil registers a handler for all versions between the passed in packet version and the specified version. +// Warning: If a bad handler is passed in, the program will panic. +func (c *Conn) RegisterUntil(h any, until string) { + c.registerUntil(h, until, false) +} + +// RegisterCriticalUntilLatest registers a handler for all versions between the passed in packet version and the latest supported version. +// If this handler returns an error, the connection will be closed. +// Warning: If a bad handler is passed in, the program will panic. +func (c *Conn) RegisterCriticalUntilLatest(h any) { + c.registerUntil(h, LatestVersion, true) +} + +// RegisterCriticalUntil registers a handler for all versions between the passed in packet version and the specified version. +// If this handler returns an error, the connection will be closed. +// Warning: If a bad handler is passed in, the program will panic. +func (c *Conn) RegisterCriticalUntil(h any, until string) { + c.registerUntil(h, until, true) +} + +func (c *Conn) registerUntil(h any, until string, critical bool) { + eventType, hv := event.ValidateHandler(h) + c.RegisterDirect(eventType, hv) + if !eventType.Implements(reflect.TypeFor[proto_base.EncodeDecodeAble]()) { + panic("expected method with argument that implements proto_base.EncodeDecodeAble") + } + packetInfo, found := LookupPacketInfoByType(reflect.Zero(eventType).Interface().(proto_base.EncodeDecodeAble)) + if !found { + panic("packet not found") + } + versionInfo, err := data.LookUpVersionByProtocolVersion(packetInfo.ProtocolVersion) + if err != nil { + panic("protocol version not found") + } + startIndex := slices.Index(proto_generated.SupportedVersions, versionInfo.MinecraftVersion) + endIndex := slices.Index(proto_generated.SupportedVersions, until) + for i := startIndex + 1; i <= endIndex; i++ { + versionName := proto_generated.SupportedVersions[i] + versionInfo, err = data.LookUpVersionByName(versionName) + if err != nil { + panic("protocol version not found") + } + var newPacketInfo proto_base.PacketInfo + newPacketInfo, found = LookupPacketInfoByNameProtocolVersionAndState(packetInfo.Name, versionInfo.Version, packetInfo.State) + if !found { + panic("packet not found") + } + newT := reflect.TypeOf(newPacketInfo.Type) + if !SmartConvertibleTo(newT, reflect.TypeOf(packetInfo.Type)) { + panic(fmt.Sprintf("packet %v is not convertible to %v", newT, reflect.TypeOf(packetInfo.Type))) + } + handleFunc := func(packet any) error { + v := hv.Call([]reflect.Value{SmartConvert(reflect.ValueOf(packet), reflect.TypeOf(packetInfo.Type))})[0] + if v.IsNil() { + return nil + } + return v.Interface().(error) + } + if critical { + c.RegisterCustomType(newT, reflect.ValueOf(handleFunc)) + } else { + c.RegisterDirect(newT, reflect.ValueOf(handleFunc)) + } + } +} diff --git a/tools/uncodeable_packet_hunter_proxy/main.go b/tools/uncodeable_packet_hunter_proxy/main.go index ba30408..5c63431 100644 --- a/tools/uncodeable_packet_hunter_proxy/main.go +++ b/tools/uncodeable_packet_hunter_proxy/main.go @@ -34,7 +34,7 @@ func (p *ProxyClient) OnCycle(_ event.Tick) (err error) { return } -func (p *ProxyClient) OnStart(_ event.Start) (err error) { +func (p *ProxyClient) OnStart() (err error) { return } @@ -65,7 +65,7 @@ func (p *Proxy) OnCycle(_ event.Tick) (err error) { return } -func (p *Proxy) OnStart(_ event.Start) (err error) { +func (p *Proxy) OnStart() (err error) { return }