Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f771166a6 | ||
|
|
5d6a1a25ea | ||
|
|
ca33185f20 | ||
|
|
e921e9e9d2 | ||
|
|
7337068d86 | ||
|
|
9983062522 | ||
|
|
4fd5324064 |
@@ -20,9 +20,6 @@ func (c *ConfigIgnorer) OnStart() (err error) {
|
||||
}
|
||||
|
||||
func (c *ConfigIgnorer) Default(e event.Anything) (err error) {
|
||||
if e.Val == (event.Tick{}) {
|
||||
return
|
||||
}
|
||||
slog.Debug("Config ignorer", "packet", fmt.Sprintf("%#v", e))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -46,10 +46,6 @@ func (s *LoginClient) OnDefault(event event.Unhandled) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LoginClient) OnCycle(_ event.Tick) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LoginClient) OnClose(_ event.Close) (err error) {
|
||||
return
|
||||
}
|
||||
@@ -133,7 +129,6 @@ func LoginRaw(c *proto.Conn, account *api.Account, addr string) (err error) {
|
||||
Account: account,
|
||||
}
|
||||
lc.RegisterCritical(lc.OnDefault)
|
||||
lc.RegisterCritical(lc.OnCycle)
|
||||
lc.RegisterCritical(lc.OnClose)
|
||||
lc.RegisterUntilLatest(lc.OnCompress)
|
||||
lc.RegisterUntilLatest(lc.OnEncrypt)
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
var ExpectedASubcommandErr = errors.New("expected a subcommand")
|
||||
var UnknownSubcommandErr = errors.New("unknown subcommand")
|
||||
var UnknownSubcommandErr = errors.New("unknown subcommand try 'strom help' for a list of subcommands")
|
||||
|
||||
var subcommands = map[string]func(args []string) error{
|
||||
"offline-uuid": offline_uuid.Run,
|
||||
|
||||
@@ -93,6 +93,7 @@ func Run(args []string) (err error) {
|
||||
slog.SetLogLoggerLevel(slog.LevelDebug)
|
||||
slog.Info("Starting packet inspector", "listen", *ListenAddr)
|
||||
err = server.StartServerWithOnConn(*ListenAddr, func(serveeConn *proto.Conn) (err error) {
|
||||
serveeConn.DecodeEvenIfUnused = true
|
||||
p := &Proxy{Servee: serveeConn, Client: nil}
|
||||
var acc = api.NewOfflineAccount(*OfflineNameFlag)
|
||||
if *Token != "" {
|
||||
@@ -105,7 +106,17 @@ func Run(args []string) (err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c, err := client.Login(*TargetAddr, acc)
|
||||
|
||||
*TargetAddr, err = client.DoDnsSimple(*TargetAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c, err := client.Connect(*TargetAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.DecodeEvenIfUnused = true
|
||||
err = client.LoginRaw(c, acc, *TargetAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
+68
-37
@@ -7,20 +7,14 @@ import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
LoopsPerSecond = 10000 // this is just so the cpu doesn't burn itself in an unlimited infinite loop
|
||||
TimePerLoop = time.Second / LoopsPerSecond
|
||||
)
|
||||
|
||||
type Loop struct {
|
||||
HandlerFunctions map[reflect.Type][]reflect.Value
|
||||
|
||||
Ctx context.Context
|
||||
ErrChan chan error
|
||||
cancel context.CancelFunc
|
||||
eventSources []reflect.SelectCase
|
||||
}
|
||||
|
||||
type Unhandled struct {
|
||||
@@ -31,10 +25,6 @@ type Anything struct {
|
||||
Val any
|
||||
}
|
||||
|
||||
type (
|
||||
Tick struct{}
|
||||
)
|
||||
|
||||
// Close is fired when the loop shuts down.
|
||||
type Close struct {
|
||||
Reason error
|
||||
@@ -45,6 +35,14 @@ type HandlerDoneErr struct {
|
||||
Return error
|
||||
}
|
||||
|
||||
// CallFunc is a special value that when fired with Loop.Fire it calls a function on the main loop
|
||||
// Its usefull for event event sources to call functions on the main loop
|
||||
// because event sources dont run on the main loop
|
||||
type CallFunc struct {
|
||||
F reflect.Value
|
||||
Args []reflect.Value
|
||||
}
|
||||
|
||||
func (e HandlerDoneErr) Error() string {
|
||||
return "HandlerDoneErr"
|
||||
}
|
||||
@@ -73,6 +71,7 @@ var WhileClosingErr = errors.New("error while closing")
|
||||
// CloseNonCriticalErr will close even when the event is non-critical
|
||||
var CloseNonCriticalErr = errors.New("close non-critical")
|
||||
var ContextDoneErr = errors.New("context done")
|
||||
var RecvNotOkErr = errors.New("recv not ok")
|
||||
|
||||
func ValidateHandler(h any) (eventType reflect.Type, hv reflect.Value) {
|
||||
hv = reflect.ValueOf(h)
|
||||
@@ -89,6 +88,29 @@ func ValidateHandler(h any) (eventType reflect.Type, hv reflect.Value) {
|
||||
return
|
||||
}
|
||||
|
||||
// RegisterEventSource registers an event source an event source is a channel this channel along with all other event sources will be reflect.Select 'ed until the loop closes
|
||||
// here is an example of an event source function
|
||||
//
|
||||
// func() (chSending <-chan any) {
|
||||
// ch := make(chan any)
|
||||
// go func() {
|
||||
// ctx := conn.Ctx
|
||||
// for {
|
||||
// select {
|
||||
// case <-ctx.Done():
|
||||
// return
|
||||
// case eventt := <-otherSource:
|
||||
// err = c.DoCommand(eventt)
|
||||
// }
|
||||
// }
|
||||
// }()
|
||||
// chSending = ch
|
||||
// return
|
||||
// }
|
||||
func (l *Loop) RegisterEventSource(ch <-chan any) {
|
||||
l.eventSources = append(l.eventSources, reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ch)})
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -137,7 +159,7 @@ func (l *Loop) FireFound(event any) (found bool, err error) {
|
||||
errV := handler.Call([]reflect.Value{reflect.ValueOf(event)})[0]
|
||||
if !errV.IsNil() {
|
||||
err = errV.Interface().(error)
|
||||
err = errors.Join(fmt.Errorf("event handler failed at: %s(%T)", handler.Type().In(0), event), err)
|
||||
err = errors.Join(fmt.Errorf("event handler failed at: %T", event), err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -147,6 +169,21 @@ func (l *Loop) FireFound(event any) (found bool, err error) {
|
||||
|
||||
// Fire dispatches the provided event to all appropriate handlers and invokes fallback handlers if the event is unhandled.
|
||||
func (l *Loop) Fire(event any) (err error) {
|
||||
err, isErr := event.(error)
|
||||
if isErr {
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
|
||||
f, isCallFunc := event.(CallFunc)
|
||||
if isCallFunc {
|
||||
errV := f.F.Call(f.Args)[0].Interface()
|
||||
if errV != nil {
|
||||
err = errV.(error)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
found, err := l.FireFound(event)
|
||||
if err != nil {
|
||||
if errors.Is(err, DontForwardErr) {
|
||||
@@ -164,24 +201,35 @@ func (l *Loop) Fire(event any) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (l *Loop) OnTick(_ Tick) (err error) {
|
||||
func (l *Loop) ContextDoneListener() (chSending <-chan any) {
|
||||
ch := make(chan any)
|
||||
go func() {
|
||||
select {
|
||||
case <-l.Ctx.Done():
|
||||
err = ContextDoneErr
|
||||
default:
|
||||
ch <- ContextDoneErr
|
||||
}
|
||||
return
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func (l *Loop) startLoop() (err error) {
|
||||
func (l *Loop) IsTypeRegistered(t reflect.Type) bool {
|
||||
_, ok := l.HandlerFunctions[t]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (l *Loop) StartLoop() (err error) {
|
||||
_ = *l // exit early on nil loop
|
||||
l.RegisterEventSource(l.ContextDoneListener())
|
||||
for {
|
||||
tickStartTime := time.Now()
|
||||
err = l.Fire(Tick{})
|
||||
_, v, ok := reflect.Select(l.eventSources)
|
||||
if !ok {
|
||||
err = RecvNotOkErr
|
||||
break
|
||||
}
|
||||
err = l.Fire(v.Interface())
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(TimePerLoop - time.Since(tickStartTime))
|
||||
}
|
||||
if errors.Is(err, HandlerDoneErr{}) {
|
||||
err = errors.Unwrap(err)
|
||||
@@ -193,25 +241,8 @@ func (l *Loop) startLoop() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (l *Loop) StartLoop() (err error) {
|
||||
go func() {
|
||||
l.ErrChan <- MainHandlerErr{l.startLoop()}
|
||||
}()
|
||||
err = <-l.ErrChan
|
||||
l.cancel()
|
||||
errMaybe := MainHandlerErr{}
|
||||
if errors.As(err, &errMaybe) {
|
||||
err = errors.Unwrap(err)
|
||||
} else {
|
||||
for !errors.Is(<-l.ErrChan, MainHandlerErr{}) {
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func NewLoop() (l *Loop) {
|
||||
l = &Loop{}
|
||||
l.ErrChan = make(chan error)
|
||||
l.Ctx, l.cancel = context.WithCancel(context.Background())
|
||||
l.HandlerFunctions = make(map[reflect.Type][]reflect.Value)
|
||||
return
|
||||
|
||||
+13
-24
@@ -2,7 +2,6 @@ package event
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"os"
|
||||
)
|
||||
|
||||
@@ -10,36 +9,26 @@ type Stdin struct {
|
||||
Val string
|
||||
}
|
||||
|
||||
type StdinHandler struct {
|
||||
*Loop
|
||||
stdInChan chan Stdin
|
||||
Ctx context.Context
|
||||
}
|
||||
|
||||
func (s *StdinHandler) OnTick(_ Tick) (err error) {
|
||||
select {
|
||||
case stdin := <-s.stdInChan:
|
||||
err = s.Fire(Stdin{stdin.Val})
|
||||
default:
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *StdinHandler) ScanJob() {
|
||||
func StartListingStdin(l *Loop) {
|
||||
l.RegisterEventSource(func() (chSending <-chan any) {
|
||||
ch := make(chan any)
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
ctx := l.Ctx
|
||||
for scanner.Scan() {
|
||||
select {
|
||||
case <-s.Ctx.Done():
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
s.stdInChan <- Stdin{scanner.Text()}
|
||||
ch <- Stdin{scanner.Text()}
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
ch <- err
|
||||
}
|
||||
func (s *StdinHandler) OnStart() (err error) {
|
||||
s.RegisterCritical(s.OnTick)
|
||||
s.stdInChan = make(chan Stdin)
|
||||
go s.ScanJob()
|
||||
}()
|
||||
chSending = ch
|
||||
return
|
||||
}())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"slices"
|
||||
"time"
|
||||
)
|
||||
@@ -48,6 +50,45 @@ func (t *Timer) Tick() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Timer) Trigger(ch chan any) {
|
||||
triggerAt := time.Time{}
|
||||
found := false
|
||||
iOnce, iInterval := -1, -1
|
||||
|
||||
for i, oTask := range t.Once {
|
||||
if oTask.Trigger.Before(triggerAt) || triggerAt.IsZero() {
|
||||
found = true
|
||||
triggerAt = oTask.Trigger
|
||||
iOnce = i
|
||||
}
|
||||
}
|
||||
|
||||
for i, iTask := range t.Interval {
|
||||
if iTask.LastTrigger.Add(iTask.Interval).Before(triggerAt) || triggerAt.IsZero() {
|
||||
found = true
|
||||
triggerAt = iTask.LastTrigger.Add(iTask.Interval)
|
||||
iOnce = -1
|
||||
iInterval = i
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
now := time.Now()
|
||||
time.Sleep(triggerAt.Sub(now))
|
||||
if iOnce >= 0 {
|
||||
ch <- CallFunc{F: reflect.ValueOf(t.Once[iOnce].F), Args: []reflect.Value{}}
|
||||
t.Once = slices.Delete(t.Once, iOnce, iOnce+1)
|
||||
return
|
||||
}
|
||||
if iInterval >= 0 {
|
||||
ch <- CallFunc{F: reflect.ValueOf(t.Interval[iInterval].F), Args: []reflect.Value{}}
|
||||
t.Interval[iInterval].LastTrigger = now
|
||||
return
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
func (t *Timer) Every(interval time.Duration, f func() error) {
|
||||
t.Interval = append(t.Interval, IntervalTask{
|
||||
F: f,
|
||||
@@ -61,3 +102,23 @@ func (t *Timer) In(interval time.Duration, f func() error) {
|
||||
Trigger: time.Now().Add(interval),
|
||||
})
|
||||
}
|
||||
|
||||
func (t *Timer) MakeEventSource(ctx context.Context) (chSending <-chan any) {
|
||||
ch := make(chan any)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
t.Trigger(ch)
|
||||
}
|
||||
}
|
||||
}()
|
||||
chSending = ch
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Timer) Start(c *Loop) {
|
||||
c.RegisterEventSource(t.MakeEventSource(c.Ctx))
|
||||
}
|
||||
|
||||
+45
-45
@@ -2,7 +2,6 @@ package proto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -16,6 +15,10 @@ import (
|
||||
"github.com/admin-else/strom/proto_generated"
|
||||
)
|
||||
|
||||
const (
|
||||
BufferNPackets = 100
|
||||
)
|
||||
|
||||
var (
|
||||
BadPacketTypeErr = errors.New("bad packet type")
|
||||
BadPacketIdErr = errors.New("bad packet id")
|
||||
@@ -24,8 +27,12 @@ var (
|
||||
WrongDirectionErr = errors.New("wrong direction")
|
||||
CantTranslateErr = errors.New("cant translate packet")
|
||||
PacketDoesntExistInFutureVersionErr = errors.New("packet doesnt exist in future version")
|
||||
NoHandlerRegisteredErr = errors.New("no handler registered")
|
||||
)
|
||||
|
||||
// LoginTick is used as a subsite for the old event system because it is very useful in login contexts
|
||||
type LoginTick struct{}
|
||||
|
||||
var LatestVersion = proto_generated.SupportedVersions[len(proto_generated.SupportedVersions)-1]
|
||||
|
||||
// UnCodablePacket represents a packet that could not be decoded.
|
||||
@@ -53,36 +60,29 @@ type Conn struct {
|
||||
Actor proto_base.Actor
|
||||
Version string
|
||||
ProtocolVersion int32
|
||||
DecodeEvenIfUnused bool
|
||||
|
||||
receiveRoutine bool
|
||||
packetCh chan proto_base.EncodeDecodeAble
|
||||
sendTickAlways bool
|
||||
}
|
||||
|
||||
func (c *Conn) StartConn() (err error) {
|
||||
c.RegisterCritical(c.OnTick)
|
||||
if c.state == proto_base.Play {
|
||||
c.ActivateReceiveRoutine()
|
||||
}
|
||||
c.sendTickAlways = c.State() != proto_base.Play
|
||||
|
||||
c.RegisterEventSource(c.ActivateReceiveRoutine())
|
||||
c.RegisterCritical(c.OnLoginTick)
|
||||
err = c.Loop.StartLoop()
|
||||
c.Loop = event.NewLoop()
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Conn) ReceiveJob() {
|
||||
ctx := c.Ctx
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
func (c *Conn) OnLoginTick(_ LoginTick) (err error) {
|
||||
packet, err := c.Receive()
|
||||
if err != nil {
|
||||
c.ErrChan <- err
|
||||
return
|
||||
}
|
||||
c.packetCh <- packet
|
||||
}
|
||||
}
|
||||
slog.Debug("logintick recv", "actor", c.Actor, "packet", fmt.Sprintf("%#v", packet))
|
||||
err = c.Fire(packet)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Conn) SetState(state proto_base.State) {
|
||||
@@ -119,13 +119,31 @@ func (c *Conn) SetProtocolVersion(version int32) (err error) {
|
||||
|
||||
// ActivateReceiveRoutine activates the receive-routine.
|
||||
// It ensures that the receive-routine is only started once and creates a buffered channel for packet handling.
|
||||
func (c *Conn) ActivateReceiveRoutine() {
|
||||
if c.receiveRoutine {
|
||||
func (c *Conn) ActivateReceiveRoutine() (chSending <-chan any) {
|
||||
ch := make(chan any, BufferNPackets)
|
||||
go func() {
|
||||
ctx := c.Ctx // this prevents c.Ctx being reassigned
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
if c.sendTickAlways {
|
||||
ch <- LoginTick{}
|
||||
} else {
|
||||
packet, err := c.Receive()
|
||||
if err != nil {
|
||||
ch <- err
|
||||
return
|
||||
}
|
||||
c.packetCh = make(chan proto_base.EncodeDecodeAble, 100) // i dunno how many to buffer 100 seems reasonable
|
||||
go c.ReceiveJob()
|
||||
c.receiveRoutine = true
|
||||
slog.Debug("recv", "actor", c.Actor, "packet", fmt.Sprintf("%#v", packet))
|
||||
ch <- packet
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
chSending = ch
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Conn) translatePacketVersion(packet proto_base.EncodeDecodeAble, packetInfo proto_base.PacketInfo) (convertedPacket proto_base.EncodeDecodeAble, err error) {
|
||||
@@ -203,6 +221,10 @@ func (c *Conn) Receive() (packet proto_base.EncodeDecodeAble, err error) {
|
||||
packet = &UnCodablePacket{Err: BadPacketIdErr, Data: packetBytes, Direction: c.Actor.ReceiveDirection()}
|
||||
return
|
||||
}
|
||||
if !c.DecodeEvenIfUnused && !c.IsTypeRegistered(reflect.TypeOf(packet)) {
|
||||
packet = &UnCodablePacket{Err: NoHandlerRegisteredErr, Data: packetBytes, Direction: c.Actor.ReceiveDirection()}
|
||||
return
|
||||
}
|
||||
err = packet.Decode(b)
|
||||
if err != nil {
|
||||
packet = &UnCodablePacket{Err: err, Data: packetBytes, Direction: c.Actor.ReceiveDirection()}
|
||||
@@ -216,25 +238,3 @@ func (c *Conn) Receive() (packet proto_base.EncodeDecodeAble, err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Conn) OnTick(_ event.Tick) (err error) {
|
||||
var packet proto_base.EncodeDecodeAble
|
||||
if c.receiveRoutine {
|
||||
select {
|
||||
case packet = <-c.packetCh:
|
||||
if slog.Default().Enabled(context.Background(), slog.LevelDebug) {
|
||||
slog.Debug("receive", "actor", c.Actor, "packet", fmt.Sprintf("%#v", packet))
|
||||
}
|
||||
err = c.Loop.Fire(packet)
|
||||
default:
|
||||
}
|
||||
} else {
|
||||
packet, err = c.Receive()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
slog.Debug("receive", "actor", c.Actor, "packet", fmt.Sprintf("%#v", packet))
|
||||
err = c.Loop.Fire(packet)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+25
-6
@@ -7,15 +7,27 @@ import (
|
||||
"github.com/admin-else/strom/proto_generated"
|
||||
)
|
||||
|
||||
// I know this is in-performant and i dont care until it becomes a problem for somebody it will stay like this and it is pretty easy to make caches or similar to speed this up so yeah
|
||||
type PidEtc struct {
|
||||
Direction proto_base.Direction
|
||||
State proto_base.State
|
||||
PacketId int32
|
||||
ProtocolVersion int32
|
||||
}
|
||||
|
||||
var pidMap = make(map[PidEtc]*proto_base.PacketInfo)
|
||||
|
||||
func init() {
|
||||
for _, pin := range proto_generated.Packets {
|
||||
pidMap[PidEtc{pin.Direction, pin.State, pin.PacketId, pin.ProtocolVersion}] = &pin
|
||||
}
|
||||
}
|
||||
|
||||
func LookUpTypeByPacketInfo(direction proto_base.Direction, state proto_base.State, pid, version int32) (p proto_base.PacketInfo, ok bool) {
|
||||
for _, pin := range proto_generated.Packets {
|
||||
if pin.Direction != direction || pin.State != state || pin.ProtocolVersion != version || pin.PacketId != pid {
|
||||
continue
|
||||
}
|
||||
return pin, true
|
||||
pP, ok := pidMap[PidEtc{direction, state, pid, version}]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
p = *pP
|
||||
return
|
||||
}
|
||||
|
||||
@@ -29,6 +41,13 @@ 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}
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
for _, p := range proto_generated.Packets {
|
||||
if reflect.TypeOf(p.Type) == reflect.TypeOf(packet) {
|
||||
return p, true
|
||||
|
||||
Reference in New Issue
Block a user