diff --git a/.gitignore b/.gitignore index e9ffc91..3a9dc75 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .devres/ .idea/ .failed_packets/ +tools/.*/ \ No newline at end of file diff --git a/client/login.go b/client/login.go index e25389c..e9b1211 100644 --- a/client/login.go +++ b/client/login.go @@ -46,7 +46,11 @@ func (s *LoginClient) OnCycle(_ event.Tick) (err error) { return } -func (s *LoginClient) OnStart(_ event.OnStart) (err error) { +func (s *LoginClient) OnClose(_ event.Close) (err error) { + return +} + +func (s *LoginClient) OnStart(_ event.Start) (err error) { if s.ServerHost == "" && s.ServerPort == 0 { var portStr string s.ServerHost, portStr, err = net.SplitHostPort(s.RemoteAddr().String()) diff --git a/client/status.go b/client/status.go index 114e114..272e721 100644 --- a/client/status.go +++ b/client/status.go @@ -23,7 +23,7 @@ func (s *StatusClient) Default(event any) (err error) { return } -func (s *StatusClient) OnStart(_ event.OnStart) (err error) { +func (s *StatusClient) OnStart(_ event.Start) (err error) { parts := strings.Split(s.RemoteAddr().String(), ":") versionData, err := data.LookUpVersionByName(s.Version) if err != nil { diff --git a/event/event_bus.go b/event/event_bus.go index 94ee444..3025dcb 100644 --- a/event/event_bus.go +++ b/event/event_bus.go @@ -10,9 +10,8 @@ import ( ) type Loop struct { - HandlerFunctions map[reflect.Type][]reflect.Value + handlerFunctions map[reflect.Type][]reflect.Value Handlers []any - HandlerIndex int } type Unhandled struct { @@ -24,8 +23,8 @@ type Anything struct { } type ( - OnStart struct{} - Tick struct{} + Start struct{} + Tick struct{} ) // HandlerDoneErr will stop the event loop and return the error if provided. @@ -33,6 +32,11 @@ type HandlerDoneErr struct { Return error } +// Close is fired when the loop shuts down. +type Close struct { + Reason error +} + func (e HandlerDoneErr) Error() string { return "HandlerDoneErr" } @@ -43,10 +47,11 @@ func (e HandlerDoneErr) Unwrap() error { // ErrDontForward will stop the event from being forwarded to other handlers, including anything handlers. var ErrDontForward = errors.New("dont forward") +var WhileClosingErr = errors.New("error while closing") -// SetHandlerFunctions initializes and populates the HandlerFunctions map by reflecting over methods of registered handlers. -func (l *Loop) SetHandlerFunctions() { - l.HandlerFunctions = make(map[reflect.Type][]reflect.Value) +// UpdateHandlerFunctions initializes and populates the handlerFunctions map by reflecting over methods of registered handlers. +func (l *Loop) UpdateHandlerFunctions() { + l.handlerFunctions = make(map[reflect.Type][]reflect.Value) for _, inst := range l.Handlers { t := reflect.TypeOf(inst) v := reflect.ValueOf(inst) @@ -54,14 +59,14 @@ func (l *Loop) SetHandlerFunctions() { method := t.Method(i) if strings.HasPrefix(method.Name, "On") { if method.Type.NumIn() != 2 || method.Type.NumOut() != 1 || method.Type.Out(0) != reflect.TypeFor[error]() { - fmt.Println("Invalid event handler signature:", t.String(), ".", method.Name) + slog.Warn("Invalid event handler signature", "at", t.String()+"."+method.Name) continue } } else { continue } eventType := method.Type.In(1) - l.HandlerFunctions[eventType] = append(l.HandlerFunctions[eventType], v.Method(i)) + l.handlerFunctions[eventType] = append(l.handlerFunctions[eventType], v.Method(i)) slog.Debug("Registered Handler", "at", t.String()+"."+method.Name, "for", eventType.String()) } } @@ -70,7 +75,7 @@ func (l *Loop) SetHandlerFunctions() { // FireFound triggers all handlers registered for the specified event type and returns if handlers were found or an error occurred. func (l *Loop) FireFound(event any) (found bool, err error) { var handlers []reflect.Value - if handlers, found = l.HandlerFunctions[reflect.TypeOf(event)]; found { + if handlers, found = l.handlerFunctions[reflect.TypeOf(event)]; found { for _, handler := range handlers { errV := handler.Call([]reflect.Value{reflect.ValueOf(event)})[0] if !errV.IsNil() { @@ -103,9 +108,9 @@ func (l *Loop) Fire(event any) (err error) { } func (l *Loop) Start() (err error) { - _ = *l // exit early on nil connection - l.SetHandlerFunctions() - err = l.Fire(OnStart{}) + _ = *l // exit early on nil loop + l.UpdateHandlerFunctions() + err = l.Fire(Start{}) for err == nil { err = l.Fire(Tick{}) if err != nil { @@ -115,5 +120,9 @@ func (l *Loop) Start() (err error) { if errors.Is(err, HandlerDoneErr{}) { err = errors.Unwrap(err) } + closeErr := l.Fire(Close{err}) + if closeErr != nil { + err = errors.Join(err, errors.Join(WhileClosingErr, closeErr)) + } return } diff --git a/event/stdin.go b/event/stdin.go new file mode 100644 index 0000000..6929bf6 --- /dev/null +++ b/event/stdin.go @@ -0,0 +1,51 @@ +package event + +import ( + "bufio" + "context" + "os" +) + +type Stdin struct { + Val string +} + +type StdinHandler struct { + *Loop + stdInChan chan Stdin + ctx context.Context + cancel context.CancelFunc +} + +func (s *StdinHandler) OnTick(_ Tick) (err error) { + select { + case stdin := <-s.stdInChan: + err = s.Fire(Stdin{stdin.Val}) + default: + + } + return +} + +func (s *StdinHandler) ScanJob() { + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + select { + case <-s.ctx.Done(): + return + default: + s.stdInChan <- Stdin{scanner.Text()} + } + } +} +func (s *StdinHandler) OnStart(_ Start) (err error) { + s.stdInChan = make(chan Stdin) + s.ctx, s.cancel = context.WithCancel(context.Background()) + go s.ScanJob() + return +} + +func (s *StdinHandler) OnClose(_ Close) (err error) { + s.cancel() + return +} diff --git a/client/timer.go b/event/timer.go similarity index 98% rename from client/timer.go rename to event/timer.go index d31dc0a..aa6dc3e 100644 --- a/client/timer.go +++ b/event/timer.go @@ -1,4 +1,4 @@ -package client +package event import ( "slices" diff --git a/proto/connection.go b/proto/connection.go index cdbb664..7fbbf72 100644 --- a/proto/connection.go +++ b/proto/connection.go @@ -17,7 +17,7 @@ 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. +// UnCodablePacket represents a packet that could not be decoded. type UnCodablePacket struct { Err error Data []byte @@ -109,13 +109,13 @@ func (c *Conn) Receive() (packet proto_base.EncodeDecodeAble, err error) { if err != nil { packet = &UnCodablePacket{Err: err, Data: packetBytes, Direction: c.Actor.ReceiveDirection()} err = nil + return } if b.Len() != 0 { err = nil packet = &UnCodablePacket{Err: PacketNotFullyDecodedError, Data: packetBytes, Direction: c.Actor.ReceiveDirection()} return } - //fmt.Println("recv:", packet) return } @@ -130,11 +130,11 @@ func (c *Conn) OnTick(_ event.Tick) (err error) { } func (c *Conn) Start(handlers ...any) (err error) { - c.Loop = &event.Loop{} - c.Handlers = append(c.Handlers, c) + if c.Loop == nil { + c.Loop = &event.Loop{} + } + c.Handlers = []any{c} c.Handlers = append(c.Handlers, handlers...) err = c.Loop.Start() - c.Handlers = c.Handlers[:len(c.Handlers)-len(handlers)] - c.Loop.SetHandlerFunctions() return } diff --git a/proto/raw_conn.go b/proto/raw_conn.go index ce97921..638a66d 100644 --- a/proto/raw_conn.go +++ b/proto/raw_conn.go @@ -30,7 +30,7 @@ func (c *RawConn) SendRaw(rawPacketBytes []byte) (err error) { if err != nil { return } - zW := zlib.NewWriter(packetBuffer) + zW := zlib.NewWriter(packetBuffer) // ignore we don't care about the result if it errors _, err = zW.Write(rawPacketBytes) if err != nil { return @@ -106,7 +106,6 @@ func (c *RawConn) ReceiveRaw() (packetBytes []byte, err error) { } else { packetBytes = rawPacketBytes } - //fmt.Println("recv:", packetBytes) return } diff --git a/server/config_server.go b/server/config_server.go index bfdcbe7..99690f7 100644 --- a/server/config_server.go +++ b/server/config_server.go @@ -21,7 +21,7 @@ func (c *ConfigServer) Default(event event.Anything) (err error) { return } -func (c *ConfigServer) OnStart(_ event.OnStart) (err error) { +func (c *ConfigServer) OnStart(_ event.Start) (err error) { n, err := data.LoadRegistry(c.Version) if err != nil { return diff --git a/server/login_server.go b/server/login_server.go index 18a7825..d1e3f84 100644 --- a/server/login_server.go +++ b/server/login_server.go @@ -116,7 +116,7 @@ func (l *LoginServer) OnDefault(event event.Unhandled) (err error) { return } -func (l *LoginServer) OnStart(_ event.OnStart) (err error) { +func (l *LoginServer) OnStart(_ event.Start) (err error) { return } @@ -124,6 +124,10 @@ func (l *LoginServer) OnCycle(_ event.Tick) (err error) { return } +func (s *LoginServer) OnClose(_ event.Close) (err error) { + return +} + type LoginServerSetting func(*LoginServer) func WithOtherAccount(a *api.Account) LoginServerSetting { diff --git a/tools/schematic_server/main.go b/tools/schematic_server/main.go index ca98ab1..06b8867 100644 --- a/tools/schematic_server/main.go +++ b/tools/schematic_server/main.go @@ -5,7 +5,6 @@ import ( "math/rand/v2" "time" - "github.com/admin-else/strom/client" "github.com/admin-else/strom/data" "github.com/admin-else/strom/event" "github.com/admin-else/strom/proto" @@ -34,7 +33,7 @@ var StatusResponse = server.StatusResponse{ type Server struct { *proto.Conn - *client.Timer + *event.Timer MinCycle, MaxCycle, Current int32 } @@ -66,7 +65,7 @@ var voidChunk = &v1_21_8.PlayToClientPacketMapChunk{X: 3, Z: 3, Heightmaps: []st Data []int64 }{Type: "motion_blocking", Data: []int64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}}, ChunkData: v1_21_8.ByteArray{Val: []uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39}}, BlockEntities: []v1_21_8.ChunkBlockEntity{}, SkyLightMask: []int64{}, BlockLightMask: []int64{}, EmptySkyLightMask: []int64{}, EmptyBlockLightMask: []int64{}, SkyLight: [][]uint8{}, BlockLight: [][]uint8{}} -func (s *Server) OnStart(_ event.OnStart) (err error) { +func (s *Server) OnStart(_ event.Start) (err error) { //goland:noinspection GoResourceLeak captures the conn object which should NOT be closed playerData, err := server.ServeLogin(s.Conn, server.WithRawStatus(StatusResponse), server.WithCompatibleVersions(772)) if err != nil { @@ -170,7 +169,7 @@ func main() { slog.SetLogLoggerLevel(slog.LevelInfo) err := server.StartServerWithFactory(":25565", func(c *proto.Conn) (h any, err error) { s := &Server{Conn: c, MinCycle: 8297, MaxCycle: 8304, Current: 8297} - s.Timer = &client.Timer{} + s.Timer = &event.Timer{} h = s return }) diff --git a/tools/uncodeable_packet_hunter_proxy/main.go b/tools/uncodeable_packet_hunter_proxy/main.go index fa884f8..ba30408 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.OnStart) (err error) { +func (p *ProxyClient) OnStart(_ event.Start) (err error) { return } @@ -65,7 +65,7 @@ func (p *Proxy) OnCycle(_ event.Tick) (err error) { return } -func (p *Proxy) OnStart(_ event.OnStart) (err error) { +func (p *Proxy) OnStart(_ event.Start) (err error) { return } @@ -121,7 +121,7 @@ func SaveUnCodeAbleAsTest(d proto_base.Direction, packet *proto.UnCodablePacket) if err != nil { panic(err) } - fmt.Println("Error", packet.Err, "saved to", f.Name(), "data:", packet.Data) + slog.Info("Packet failed to parse", "error", packet.Err, "saved", f.Name(), "data", fmt.Sprintf("%q", packet.Data)) } var StatusResponse = server.StatusResponse{