version compat system
This commit is contained in:
+62
-8
@@ -7,11 +7,13 @@ import (
|
||||
"io"
|
||||
"log/slog"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -25,6 +27,8 @@ var (
|
||||
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.
|
||||
type UnCodablePacket struct {
|
||||
Err error
|
||||
@@ -51,10 +55,62 @@ type Conn struct {
|
||||
Version string
|
||||
ProtocolVersion int32
|
||||
|
||||
handler event.Handler
|
||||
receiveRoutine bool
|
||||
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() {
|
||||
for {
|
||||
select {
|
||||
@@ -120,14 +176,14 @@ func (c *Conn) translatePacketVersion(packet proto_base.EncodeDecodeAble, packet
|
||||
err = PacketDoesntExistInFutureVersionErr
|
||||
return
|
||||
}
|
||||
|
||||
dstType := reflect.TypeOf(packetInfoReal.Type)
|
||||
if !reflect.TypeOf(packetInfo.Type).ConvertibleTo(dstType) {
|
||||
if !SmartConvertibleTo(reflect.TypeOf(packetInfo.Type), dstType) {
|
||||
err = CantTranslateErr
|
||||
return
|
||||
}
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -226,12 +282,10 @@ func (c *Conn) OnTick(_ event.Tick) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Conn) Start(handlers ...any) (err error) {
|
||||
func (c *Conn) Start(handler event.Handler) (err error) {
|
||||
if c.Loop == nil {
|
||||
c.Loop = &event.Loop{}
|
||||
}
|
||||
c.Handlers = []any{c}
|
||||
c.Handlers = append(c.Handlers, handlers...)
|
||||
err = c.Loop.Start()
|
||||
return
|
||||
c.handler = handler
|
||||
return c.Loop.Start(c)
|
||||
}
|
||||
|
||||
+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) {
|
||||
switch packet := packet.(type) {
|
||||
case *UnCodablePacket:
|
||||
p = proto_base.PacketInfo{
|
||||
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
|
||||
}
|
||||
for _, p := range proto_generated.Packets {
|
||||
if reflect.TypeOf(p.Type) == reflect.TypeOf(packet) {
|
||||
return p, true
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user