147 lines
5.1 KiB
Go
147 lines
5.1 KiB
Go
package proto
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"slices"
|
|
|
|
"git.anygate.cloud/anygatecloud/strom/mc/data"
|
|
"git.anygate.cloud/anygatecloud/strom/mc/event"
|
|
"git.anygate.cloud/anygatecloud/strom/mc/proto_base"
|
|
"git.anygate.cloud/anygatecloud/strom/mc/proto_generated"
|
|
)
|
|
|
|
// SmartConvertibleTo reports whether a value of type from can be recursively converted to type to.
|
|
func SmartConvertibleTo(from, to reflect.Type) bool {
|
|
switch from.Kind() {
|
|
case reflect.Struct:
|
|
if to.Kind() != reflect.Struct {
|
|
return false
|
|
}
|
|
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.Pointer, reflect.Slice:
|
|
if from.Kind() != to.Kind() {
|
|
return false
|
|
}
|
|
return SmartConvertibleTo(from.Elem(), to.Elem())
|
|
default:
|
|
return from.ConvertibleTo(to)
|
|
}
|
|
}
|
|
|
|
// SmartConvert recursively converts a reflect.Value from one type to another compatible type.
|
|
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))
|
|
}
|
|
case reflect.Pointer:
|
|
if from.IsNil() {
|
|
return reflect.Zero(to)
|
|
}
|
|
ret = reflect.New(to.Elem())
|
|
ret.Elem().Set(SmartConvert(from.Elem(), to.Elem()))
|
|
case reflect.Slice:
|
|
l := from.Len()
|
|
ret = reflect.MakeSlice(to, l, l)
|
|
for i := range l {
|
|
ret.Index(i).Set(SmartConvert(from.Index(i), to.Elem()))
|
|
}
|
|
default:
|
|
return from.Convert(to)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// 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(hs ...any) {
|
|
for _, h := range hs {
|
|
eventType, hv := event.ValidateHandler(h)
|
|
c.RegisterUntilLow(eventType, hv, 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(until string, hs ...any) {
|
|
for _, h := range hs {
|
|
eventType, hv := event.ValidateHandler(h)
|
|
c.RegisterUntilLow(eventType, hv, 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(hs ...any) {
|
|
for _, h := range hs {
|
|
eventType, hv := event.ValidateHandler(h)
|
|
c.RegisterUntilLow(eventType, hv, 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(until string, hs ...any) {
|
|
for _, h := range hs {
|
|
eventType, hv := event.ValidateHandler(h)
|
|
c.RegisterUntilLow(eventType, hv, until, true)
|
|
}
|
|
}
|
|
|
|
func (c *Conn) RegisterUntilLow(eventType reflect.Type, hv reflect.Value, until string, critical bool) {
|
|
c.RegisterLow(eventType, hv, critical)
|
|
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)
|
|
if startIndex > endIndex {
|
|
panic(fmt.Sprintf("handler version %s is above until version %s", versionInfo.MinecraftVersion, 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 this should not happen even if a wrong type is passed in")
|
|
}
|
|
var newPacketInfo proto_base.PacketInfo
|
|
newPacketInfo, found = LookupPacketInfoByNameProtocolVersionStateAndDirection(packetInfo.Name, versionInfo.Version, packetInfo.State, packetInfo.Direction)
|
|
if !found {
|
|
panic("packet not found in version " + versionName)
|
|
}
|
|
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.RegisterLow(newT, reflect.ValueOf(handleFunc), critical)
|
|
}
|
|
}
|