version compat system
This commit is contained in:
@@ -15,6 +15,15 @@ type ConfigIgnorer struct {
|
|||||||
*proto.Conn
|
*proto.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ConfigIgnorer) OnStart() (err error) {
|
||||||
|
c.Register(c.Default)
|
||||||
|
c.RegisterUntilLatest(c.OnKnownPacks)
|
||||||
|
c.RegisterUntilLatest(c.OnFinish)
|
||||||
|
c.RegisterUntilLatest(c.OnPing)
|
||||||
|
c.RegisterUntilLatest(c.OnKeepAlive)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ConfigIgnorer) Default(event event.Anything) (err error) {
|
func (c *ConfigIgnorer) Default(event event.Anything) (err error) {
|
||||||
slog.Debug("Config ignorer", "packet", fmt.Sprintf("%#v", event))
|
slog.Debug("Config ignorer", "packet", fmt.Sprintf("%#v", event))
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,3 +21,20 @@ func LookupMinecraftSRV(host string, port uint16) (hostRet string, portRet uint1
|
|||||||
portRet = srvs[0].Port
|
portRet = srvs[0].Port
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DoDNS(in string) (out string, err error) { // Bad code should match minecraft
|
||||||
|
// https://mcsrc.dev/1/26.1-pre-1/net/minecraft/client/multiplayer/resolver/ServerAddress#L34
|
||||||
|
host, port, _ := net.SplitHostPort(in)
|
||||||
|
var portN uint16 = 25565
|
||||||
|
if port != "" {
|
||||||
|
var porti int
|
||||||
|
porti, err = strconv.Atoi(port)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
portN = uint16(porti)
|
||||||
|
}
|
||||||
|
host, portN = LookupMinecraftSRV(host, portN)
|
||||||
|
out = net.JoinHostPort(host, strconv.Itoa(int(portN)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
+38
-24
@@ -27,6 +27,12 @@ type LoginClient struct {
|
|||||||
GivenAccount *v1_21_8.LoginToClientPacketSuccess
|
GivenAccount *v1_21_8.LoginToClientPacketSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
NoTokenErr = errors.New("the account has no token so we cant join online servers")
|
||||||
|
BadPublicKeyTypeErr = errors.New("the public key is not rsa")
|
||||||
|
FailedToLoginErr = errors.New("failed to login")
|
||||||
|
)
|
||||||
|
|
||||||
type KickedDuringLoginErr struct {
|
type KickedDuringLoginErr struct {
|
||||||
text.Component
|
text.Component
|
||||||
}
|
}
|
||||||
@@ -35,7 +41,7 @@ func (k KickedDuringLoginErr) Error() string {
|
|||||||
return k.String()
|
return k.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LoginClient) Default(event event.Unhandled) (err error) {
|
func (s *LoginClient) OnDefault(event event.Unhandled) (err error) {
|
||||||
err = fmt.Errorf("unexpected event: %#v", event)
|
err = fmt.Errorf("unexpected event: %#v", event)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -48,25 +54,6 @@ func (s *LoginClient) OnClose(_ event.Close) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LoginClient) OnStart(_ event.Start) (err error) {
|
|
||||||
p, err := MakeHandshakePacket(s.Conn, proto_base.Login)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if s.ServerHost == "" && s.ServerPort == 0 {
|
|
||||||
p.ServerHost = s.ServerHost
|
|
||||||
p.ServerPort = s.ServerPort
|
|
||||||
}
|
|
||||||
|
|
||||||
err = s.Send(p)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.SetState(proto_base.Login)
|
|
||||||
err = s.Send(&v1_21_8.LoginToServerPacketLoginStart{Username: s.Account.Name, PlayerUUID: s.Account.Uuid})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *LoginClient) OnCompress(compress *v1_21_8.LoginToClientPacketCompress) (err error) {
|
func (s *LoginClient) OnCompress(compress *v1_21_8.LoginToClientPacketCompress) (err error) {
|
||||||
s.SetCompressionThreshold(compress.Threshold)
|
s.SetCompressionThreshold(compress.Threshold)
|
||||||
return
|
return
|
||||||
@@ -78,7 +65,7 @@ func (s *LoginClient) OnEncrypt(packet *v1_21_8.LoginToClientPacketEncryptionBeg
|
|||||||
|
|
||||||
if packet.ShouldAuthenticate {
|
if packet.ShouldAuthenticate {
|
||||||
if s.Account.Ygg == "" {
|
if s.Account.Ygg == "" {
|
||||||
err = fmt.Errorf("the account has no token so we cant join online servers")
|
err = NoTokenErr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
serverId := crypto.AuthDigest([]byte(packet.ServerId), sharedSecret, packet.PublicKey)
|
serverId := crypto.AuthDigest([]byte(packet.ServerId), sharedSecret, packet.PublicKey)
|
||||||
@@ -94,7 +81,7 @@ func (s *LoginClient) OnEncrypt(packet *v1_21_8.LoginToClientPacketEncryptionBeg
|
|||||||
}
|
}
|
||||||
pub, ok := pubAny.(*rsa.PublicKey)
|
pub, ok := pubAny.(*rsa.PublicKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
err = fmt.Errorf("public key is not rsa")
|
err = BadPublicKeyTypeErr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
verifyTokenEnc, err := rsa.EncryptPKCS1v15(rand.Reader, pub, packet.VerifyToken)
|
verifyTokenEnc, err := rsa.EncryptPKCS1v15(rand.Reader, pub, packet.VerifyToken)
|
||||||
@@ -136,13 +123,40 @@ func (s *LoginClient) OnSuccess(success *v1_21_8.LoginToClientPacketSuccess) (er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *LoginClient) OnStart() (err error) {
|
||||||
|
s.Register(s.OnDefault)
|
||||||
|
s.Register(s.OnCycle)
|
||||||
|
s.Register(s.OnClose)
|
||||||
|
s.RegisterUntilLatest(s.OnCompress)
|
||||||
|
s.RegisterUntilLatest(s.OnEncrypt)
|
||||||
|
s.RegisterUntilLatest(s.OnDisconnect)
|
||||||
|
s.RegisterUntilLatest(s.OnSuccess)
|
||||||
|
|
||||||
|
p, err := MakeHandshakePacket(s.Conn, proto_base.Login)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if s.ServerHost == "" && s.ServerPort == 0 {
|
||||||
|
p.ServerHost = s.ServerHost
|
||||||
|
p.ServerPort = s.ServerPort
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.Send(p)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.SetState(proto_base.Login)
|
||||||
|
err = s.Send(&v1_21_8.LoginToServerPacketLoginStart{Username: s.Account.Name, PlayerUUID: s.Account.Uuid})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func Login(c *proto.Conn, account *api.Account) (err error) {
|
func Login(c *proto.Conn, account *api.Account) (err error) {
|
||||||
err = c.Start(&LoginClient{
|
err = c.Start(&LoginClient{
|
||||||
Conn: c,
|
Conn: c,
|
||||||
Account: account,
|
Account: account,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.Join(err, errors.New("failed to login"))
|
err = errors.Join(FailedToLoginErr, err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -157,7 +171,7 @@ func ConnectAndLogin(connectTo string, account *api.Account) (c *proto.Conn, err
|
|||||||
Account: account,
|
Account: account,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.Join(errors.New("failed to login"), err)
|
err = errors.Join(FailedToLoginErr, err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-6
@@ -2,7 +2,6 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/admin-else/strom/event"
|
"github.com/admin-else/strom/event"
|
||||||
@@ -19,12 +18,10 @@ type StatusClient struct {
|
|||||||
PingSendTime, PingReceiveTime time.Time
|
PingSendTime, PingReceiveTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StatusClient) Default(event any) (err error) {
|
func (s *StatusClient) OnStart() (err error) {
|
||||||
err = fmt.Errorf("unexpected event: %v", event)
|
s.RegisterUntilLatest(s.OnStatus)
|
||||||
return
|
s.RegisterUntilLatest(s.OnPong)
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StatusClient) OnStart(_ event.Start) (err error) {
|
|
||||||
p, err := MakeHandshakePacket(s.Conn, proto_base.Status)
|
p, err := MakeHandshakePacket(s.Conn, proto_base.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|||||||
+31
-31
@@ -5,9 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -16,9 +14,12 @@ const (
|
|||||||
TimePerLoop = time.Second / LoopsPerSecond
|
TimePerLoop = time.Second / LoopsPerSecond
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Handler interface {
|
||||||
|
OnStart() (err error)
|
||||||
|
}
|
||||||
|
|
||||||
type Loop struct {
|
type Loop struct {
|
||||||
handlerFunctions map[reflect.Type][]reflect.Value
|
handlerFunctions map[reflect.Type][]reflect.Value
|
||||||
Handlers []any
|
|
||||||
|
|
||||||
Ctx context.Context
|
Ctx context.Context
|
||||||
ErrChan chan error
|
ErrChan chan error
|
||||||
@@ -34,8 +35,7 @@ type Anything struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Start struct{}
|
Tick struct{}
|
||||||
Tick struct{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// HandlerDoneErr will stop the event loop and return the error if provided.
|
// HandlerDoneErr will stop the event loop and return the error if provided.
|
||||||
@@ -60,27 +60,28 @@ func (e HandlerDoneErr) Unwrap() error {
|
|||||||
var ErrDontForward = errors.New("dont forward")
|
var ErrDontForward = errors.New("dont forward")
|
||||||
var WhileClosingErr = errors.New("error while closing")
|
var WhileClosingErr = errors.New("error while closing")
|
||||||
|
|
||||||
// UpdateHandlerFunctions initializes and populates the handlerFunctions map by reflecting over methods of registered handlers.
|
func ValidateHandler(h any) (hv reflect.Value, eventType reflect.Type) {
|
||||||
func (l *Loop) UpdateHandlerFunctions() {
|
hv = reflect.ValueOf(h)
|
||||||
l.handlerFunctions = make(map[reflect.Type][]reflect.Value)
|
if hv.Kind() != reflect.Func {
|
||||||
for _, inst := range l.Handlers {
|
panic("expected method")
|
||||||
t := reflect.TypeOf(inst)
|
|
||||||
v := reflect.ValueOf(inst)
|
|
||||||
for i := 0; i < t.NumMethod(); i++ {
|
|
||||||
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]() {
|
|
||||||
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))
|
|
||||||
slog.Debug("Registered Handler", "at", t.String()+"."+method.Name, "for", eventType.String())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if hv.Type().NumIn() != 1 {
|
||||||
|
panic("expected method with one argument")
|
||||||
|
}
|
||||||
|
if hv.Type().NumOut() != 1 || hv.Type().Out(0) != reflect.TypeFor[error]() {
|
||||||
|
panic("expected method that returns an error")
|
||||||
|
}
|
||||||
|
eventType = hv.Type().In(0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Loop) Register(h any) {
|
||||||
|
hv, eventType := ValidateHandler(h)
|
||||||
|
l.RegisterDirect(eventType, hv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Loop) RegisterDirect(k reflect.Type, h reflect.Value) {
|
||||||
|
l.handlerFunctions[k] = append(l.handlerFunctions[k], h)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FireFound triggers all handlers registered for the specified event type and returns if handlers were found or an error occurred.
|
// FireFound triggers all handlers registered for the specified event type and returns if handlers were found or an error occurred.
|
||||||
@@ -118,11 +119,10 @@ func (l *Loop) Fire(event any) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Loop) startLoop() (err error) {
|
func (l *Loop) startLoop(handler Handler) (err error) {
|
||||||
_ = *l // exit early on nil loop
|
_ = *l // exit early on nil loop
|
||||||
l.UpdateHandlerFunctions()
|
l.handlerFunctions = make(map[reflect.Type][]reflect.Value)
|
||||||
|
err = handler.OnStart()
|
||||||
err = l.Fire(Start{})
|
|
||||||
for err == nil {
|
for err == nil {
|
||||||
tickStartTime := time.Now()
|
tickStartTime := time.Now()
|
||||||
err = l.Fire(Tick{})
|
err = l.Fire(Tick{})
|
||||||
@@ -141,11 +141,11 @@ func (l *Loop) startLoop() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Loop) Start() (err error) {
|
func (l *Loop) Start(handler Handler) (err error) {
|
||||||
l.ErrChan = make(chan error)
|
l.ErrChan = make(chan error)
|
||||||
l.Ctx, l.cancel = context.WithCancel(context.Background())
|
l.Ctx, l.cancel = context.WithCancel(context.Background())
|
||||||
go func() {
|
go func() {
|
||||||
l.ErrChan <- l.startLoop()
|
l.ErrChan <- l.startLoop(handler)
|
||||||
}()
|
}()
|
||||||
err = <-l.ErrChan
|
err = <-l.ErrChan
|
||||||
l.cancel()
|
l.cancel()
|
||||||
|
|||||||
+3
-10
@@ -13,8 +13,7 @@ type Stdin struct {
|
|||||||
type StdinHandler struct {
|
type StdinHandler struct {
|
||||||
*Loop
|
*Loop
|
||||||
stdInChan chan Stdin
|
stdInChan chan Stdin
|
||||||
ctx context.Context
|
Ctx context.Context
|
||||||
cancel context.CancelFunc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StdinHandler) OnTick(_ Tick) (err error) {
|
func (s *StdinHandler) OnTick(_ Tick) (err error) {
|
||||||
@@ -31,21 +30,15 @@ func (s *StdinHandler) ScanJob() {
|
|||||||
scanner := bufio.NewScanner(os.Stdin)
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
select {
|
select {
|
||||||
case <-s.ctx.Done():
|
case <-s.Ctx.Done():
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
s.stdInChan <- Stdin{scanner.Text()}
|
s.stdInChan <- Stdin{scanner.Text()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func (s *StdinHandler) OnStart(_ Start) (err error) {
|
func (s *StdinHandler) OnStart() (err error) {
|
||||||
s.stdInChan = make(chan Stdin)
|
s.stdInChan = make(chan Stdin)
|
||||||
s.ctx, s.cancel = context.WithCancel(context.Background())
|
|
||||||
go s.ScanJob()
|
go s.ScanJob()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StdinHandler) OnClose(_ Close) (err error) {
|
|
||||||
s.cancel()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|||||||
+62
-8
@@ -7,11 +7,13 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/admin-else/strom/data"
|
"github.com/admin-else/strom/data"
|
||||||
"github.com/admin-else/strom/event"
|
"github.com/admin-else/strom/event"
|
||||||
"github.com/admin-else/strom/proto_base"
|
"github.com/admin-else/strom/proto_base"
|
||||||
|
"github.com/admin-else/strom/proto_generated"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -25,6 +27,8 @@ var (
|
|||||||
PacketDoesntExistInFutureVersionErr = errors.New("packet doesnt exist in future version")
|
PacketDoesntExistInFutureVersionErr = errors.New("packet doesnt exist in future version")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var LatestVersion = proto_generated.SupportedVersions[len(proto_generated.SupportedVersions)-1]
|
||||||
|
|
||||||
// UnCodablePacket represents a packet that could not be decoded.
|
// UnCodablePacket represents a packet that could not be decoded.
|
||||||
type UnCodablePacket struct {
|
type UnCodablePacket struct {
|
||||||
Err error
|
Err error
|
||||||
@@ -51,10 +55,62 @@ type Conn struct {
|
|||||||
Version string
|
Version string
|
||||||
ProtocolVersion int32
|
ProtocolVersion int32
|
||||||
|
|
||||||
|
handler event.Handler
|
||||||
receiveRoutine bool
|
receiveRoutine bool
|
||||||
packetCh chan proto_base.EncodeDecodeAble
|
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)
|
||||||
|
return c.handler.OnStart()
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Conn) ReceiveJob() {
|
func (c *Conn) ReceiveJob() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@@ -120,14 +176,14 @@ func (c *Conn) translatePacketVersion(packet proto_base.EncodeDecodeAble, packet
|
|||||||
err = PacketDoesntExistInFutureVersionErr
|
err = PacketDoesntExistInFutureVersionErr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dstType := reflect.TypeOf(packetInfoReal.Type)
|
dstType := reflect.TypeOf(packetInfoReal.Type)
|
||||||
if !reflect.TypeOf(packetInfo.Type).ConvertibleTo(dstType) {
|
if !SmartConvertibleTo(reflect.TypeOf(packetInfo.Type), dstType) {
|
||||||
err = CantTranslateErr
|
err = CantTranslateErr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// can't fail cause pacetinfo.type is proto_base.EncodeDecodeAble
|
// can't fail cause pacetinfo.type is proto_base.EncodeDecodeAble
|
||||||
convertedPacket = reflect.ValueOf(packet).Convert(dstType).Interface().(proto_base.EncodeDecodeAble)
|
convertedPacket = SmartConvert(reflect.ValueOf(packet), dstType).Interface().(proto_base.EncodeDecodeAble)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,12 +282,10 @@ func (c *Conn) OnTick(_ event.Tick) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) Start(handlers ...any) (err error) {
|
func (c *Conn) Start(handler event.Handler) (err error) {
|
||||||
if c.Loop == nil {
|
if c.Loop == nil {
|
||||||
c.Loop = &event.Loop{}
|
c.Loop = &event.Loop{}
|
||||||
}
|
}
|
||||||
c.Handlers = []any{c}
|
c.handler = handler
|
||||||
c.Handlers = append(c.Handlers, handlers...)
|
return c.Loop.Start(c)
|
||||||
err = c.Loop.Start()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-16
@@ -29,22 +29,9 @@ func LookUpTypeByPacketInfoAndCopyType(direction proto_base.Direction, state pro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func LookupPacketInfoByType(packet proto_base.EncodeDecodeAble) (p proto_base.PacketInfo, ok bool) {
|
func LookupPacketInfoByType(packet proto_base.EncodeDecodeAble) (p proto_base.PacketInfo, ok bool) {
|
||||||
switch packet := packet.(type) {
|
for _, p := range proto_generated.Packets {
|
||||||
case *UnCodablePacket:
|
if reflect.TypeOf(p.Type) == reflect.TypeOf(packet) {
|
||||||
p = proto_base.PacketInfo{
|
return p, true
|
||||||
Type: &UnCodablePacket{},
|
|
||||||
Name: "undecodeable",
|
|
||||||
Direction: packet.Direction,
|
|
||||||
State: -1,
|
|
||||||
PacketId: -1,
|
|
||||||
ProtocolVersion: -1,
|
|
||||||
}
|
|
||||||
ok = true
|
|
||||||
default:
|
|
||||||
for _, p := range proto_generated.Packets {
|
|
||||||
if reflect.TypeOf(p.Type) == reflect.TypeOf(packet) {
|
|
||||||
return p, true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SmartConvertibleTo(from, to reflect.Type) bool {
|
||||||
|
switch from.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
if from.NumField() != to.NumField() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := range from.NumField() {
|
||||||
|
if !SmartConvertibleTo(from.Field(i).Type, to.Field(i).Type) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case reflect.Ptr:
|
||||||
|
return SmartConvertibleTo(from.Elem(), to.Elem())
|
||||||
|
default:
|
||||||
|
return from.ConvertibleTo(to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SmartConvert(from reflect.Value, to reflect.Type) (ret reflect.Value) {
|
||||||
|
switch from.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
ret = reflect.New(to).Elem()
|
||||||
|
for i := range from.NumField() {
|
||||||
|
ret.Field(i).Set(SmartConvert(from.Field(i), to.Field(i).Type))
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
case reflect.Ptr:
|
||||||
|
if from.IsNil() {
|
||||||
|
return reflect.Zero(to)
|
||||||
|
}
|
||||||
|
return SmartConvert(from.Elem(), to.Elem()).Addr()
|
||||||
|
default:
|
||||||
|
return from.Convert(to)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package proto_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/proto"
|
||||||
|
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
||||||
|
"github.com/admin-else/strom/proto_generated/v1_21_9"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCanSmartConvert(t *testing.T) {
|
||||||
|
packet := &v1_21_9.PlayToClientPacketPosition{Flags: v1_21_9.PlayToClientPositionUpdateRelatives{Val: 2}, X: 5}
|
||||||
|
can := proto.SmartConvertibleTo(reflect.TypeOf(packet), reflect.TypeFor[*v1_21_8.PlayToClientPacketPosition]())
|
||||||
|
if !can {
|
||||||
|
t.Fatal("can't smart convert")
|
||||||
|
}
|
||||||
|
ret := proto.SmartConvert(reflect.ValueOf(packet), reflect.TypeFor[*v1_21_8.PlayToClientPacketPosition]())
|
||||||
|
t.Logf("%#v", ret)
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -50,6 +50,12 @@ func GeneratePacketInfoFile(versions []string, packetInfos []PacketInfo) (err er
|
|||||||
KeyValueExpr(Ident("ProtocolVersion"), NumLit(protocolVersion)),
|
KeyValueExpr(Ident("ProtocolVersion"), NumLit(protocolVersion)),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var supportedVersionsExprs []ast.Expr
|
||||||
|
for _, v := range versions {
|
||||||
|
supportedVersionsExprs = append(supportedVersionsExprs, StrLit(v))
|
||||||
|
}
|
||||||
|
AppendDecl(f, VarValues("SupportedVersions", CompLit(Slice(Ident("string")), supportedVersionsExprs)))
|
||||||
AppendDecl(f, VarValues("Packets", CompLit(Slice(Selector("proto_base", "PacketInfo")), packetInfoExprs)))
|
AppendDecl(f, VarValues("Packets", CompLit(Slice(Selector("proto_base", "PacketInfo")), packetInfoExprs)))
|
||||||
out, err := os.Create("proto_generated/packet_info.go")
|
out, err := os.Create("proto_generated/packet_info.go")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func (c *ConfigServer) Default(event event.Anything) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigServer) OnStart(_ event.Start) (err error) {
|
func (c *ConfigServer) OnStart() (err error) {
|
||||||
n, err := data.LoadRegistry(c.Version)
|
n, err := data.LoadRegistry(c.Version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ func (l *LoginServer) OnDefault(event event.Unhandled) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LoginServer) OnStart(_ event.Start) (err error) {
|
func (l *LoginServer) OnStart() (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-2
@@ -5,6 +5,7 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/event"
|
||||||
"github.com/admin-else/strom/proto"
|
"github.com/admin-else/strom/proto"
|
||||||
"github.com/admin-else/strom/proto_base"
|
"github.com/admin-else/strom/proto_base"
|
||||||
"github.com/admin-else/strom/text"
|
"github.com/admin-else/strom/text"
|
||||||
@@ -22,7 +23,9 @@ func Servee(c net.Conn) (ret *proto.Conn) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func ServeClient(cNet net.Conn, factory func(c *proto.Conn) (h any, err error)) {
|
type Factory func(c *proto.Conn) (h event.Handler, err error)
|
||||||
|
|
||||||
|
func ServeClient(cNet net.Conn, factory Factory) {
|
||||||
c := Servee(cNet)
|
c := Servee(cNet)
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
h, err := factory(c)
|
h, err := factory(c)
|
||||||
@@ -43,7 +46,7 @@ func ServeClient(cNet net.Conn, factory func(c *proto.Conn) (h any, err error))
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartServerWithFactory(listenAddr string, factory func(c *proto.Conn) (h any, err error)) (err error) {
|
func StartServerWithFactory(listenAddr string, factory Factory) (err error) {
|
||||||
l, err := net.Listen("tcp", listenAddr)
|
l, err := net.Listen("tcp", listenAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -48,6 +48,10 @@ type StatusServer struct {
|
|||||||
Status string
|
Status string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *StatusServer) OnStart() (err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (p *StatusServer) Default(_ any) (err error) {
|
func (p *StatusServer) Default(_ any) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ var voidChunk = &v1_21_8.PlayToClientPacketMapChunk{X: 3, Z: 3, Heightmaps: []st
|
|||||||
Data []int64
|
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{}}
|
}{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.Start) (err error) {
|
func (s *Server) OnStart() (err error) {
|
||||||
//goland:noinspection GoResourceLeak captures the conn object which should NOT be closed
|
//goland:noinspection GoResourceLeak captures the conn object which should NOT be closed
|
||||||
playerData, err := server.ServeLogin(s.Conn, server.WithRawStatus(StatusResponse), server.WithCompatibleVersions(772))
|
playerData, err := server.ServeLogin(s.Conn, server.WithRawStatus(StatusResponse), server.WithCompatibleVersions(772))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -167,7 +167,7 @@ func (s *Server) SendKeepAlive() (err error) {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
slog.SetLogLoggerLevel(slog.LevelInfo)
|
slog.SetLogLoggerLevel(slog.LevelInfo)
|
||||||
err := server.StartServerWithFactory(":25565", func(c *proto.Conn) (h any, err error) {
|
err := server.StartServerWithFactory(":25565", func(c *proto.Conn) (h event.Handler, err error) {
|
||||||
s := &Server{Conn: c, MinCycle: 8297, MaxCycle: 8304, Current: 8297}
|
s := &Server{Conn: c, MinCycle: 8297, MaxCycle: 8304, Current: 8297}
|
||||||
s.Timer = &event.Timer{}
|
s.Timer = &event.Timer{}
|
||||||
h = s
|
h = s
|
||||||
|
|||||||
@@ -8,9 +8,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var addrFlag = flag.String("addr", "localhost:25565", "address to connect to")
|
var addrFlag = flag.String("addr", "localhost:25565", "address to connect to")
|
||||||
|
var srvFlag = flag.Bool("srv", false, "use SRV records to resolve the address")
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
var err error
|
||||||
|
if *srvFlag {
|
||||||
|
*addrFlag, err = client.DoDNS(*addrFlag)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
status, err := client.StatusRaw(*addrFlag)
|
status, err := client.StatusRaw(*addrFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|||||||
Reference in New Issue
Block a user