experimental transfer handler

This commit is contained in:
olebeck 2023-07-15 14:30:32 +02:00
parent f3d1a80985
commit 39be9c678b
7 changed files with 181 additions and 72 deletions

3
go.mod
View File

@ -3,7 +3,7 @@ module github.com/bedrock-tool/bedrocktool
go 1.20
//replace github.com/sandertv/gophertunnel => ./gophertunnel
replace github.com/sandertv/gophertunnel => github.com/olebeck/gophertunnel v1.30.0-3
replace github.com/sandertv/gophertunnel => github.com/olebeck/gophertunnel v1.31.0-2
//replace github.com/df-mc/dragonfly => ./dragonfly
replace github.com/df-mc/dragonfly => github.com/olebeck/dragonfly v0.9.6-2
@ -66,6 +66,7 @@ require (
github.com/muhammadmuzzammil1998/jsonc v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/segmentio/fasthash v1.0.3 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/tklauser/go-sysconf v0.3.11 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect

6
go.sum
View File

@ -96,6 +96,10 @@ github.com/olebeck/gophertunnel v1.30.0-2 h1:JbkADJTfpCfC7nL9O08LON0k8OBahfmOdBy
github.com/olebeck/gophertunnel v1.30.0-2/go.mod h1:HxQfl/8mZzvjzhekEH8RO6xLAgan9i/wIyrQzw0tIPY=
github.com/olebeck/gophertunnel v1.30.0-3 h1:D3PP7ttzZWgyHmacVMiPslWsTj3UNeRvO1x+5kA97wE=
github.com/olebeck/gophertunnel v1.30.0-3/go.mod h1:HxQfl/8mZzvjzhekEH8RO6xLAgan9i/wIyrQzw0tIPY=
github.com/olebeck/gophertunnel v1.31.0-1 h1:MeBwKKFT9SAe8FoeAT8o53TgrkPRJL5uAxLqWY3at5A=
github.com/olebeck/gophertunnel v1.31.0-1/go.mod h1:HxQfl/8mZzvjzhekEH8RO6xLAgan9i/wIyrQzw0tIPY=
github.com/olebeck/gophertunnel v1.31.0-2 h1:m/rUr/gjMTs/pSS5jPKlSxBJlC1WVjnxu8MHrVWfdkc=
github.com/olebeck/gophertunnel v1.31.0-2/go.mod h1:HxQfl/8mZzvjzhekEH8RO6xLAgan9i/wIyrQzw0tIPY=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@ -114,6 +118,8 @@ github.com/sanbornm/go-selfupdate v0.0.0-20210106163404-c9b625feac49 h1:LuxslTBx
github.com/sanbornm/go-selfupdate v0.0.0-20210106163404-c9b625feac49/go.mod h1:fY313ZGG810aWruFYcyq3coFpHDrWJVoMfSRI81y1r4=
github.com/sandertv/go-raknet v1.12.0 h1:olUzZlIJyX/pgj/mrsLCZYjKLNDsYiWdvQ4NIm3z0DA=
github.com/sandertv/go-raknet v1.12.0/go.mod h1:Gx+WgZBMQ0V2UoouGoJ8Wj6CDrMBQ4SB2F/ggpl5/+Y=
github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM=
github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY=
github.com/shirou/gopsutil/v3 v3.23.5 h1:5SgDCeQ0KW0S4N0znjeM/eFHXXOKyv2dVNgRq/c9P6Y=
github.com/shirou/gopsutil/v3 v3.23.5/go.mod h1:Ng3Maa27Q2KARVJ0SPZF5NdrQSC3XHKP8IIWrHgMeLY=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=

View File

@ -16,7 +16,6 @@ import (
"github.com/bedrock-tool/bedrocktool/locale"
"github.com/bedrock-tool/bedrocktool/ui/messages"
"github.com/repeale/fp-go"
"github.com/sandertv/gophertunnel/minecraft"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/login"
@ -95,6 +94,8 @@ type ProxyContext struct {
commands map[string]IngameCommand
handlers []*ProxyHandler
reconnectHandler *ProxyHandler
}
func NewProxy() (*ProxyContext, error) {
@ -103,6 +104,7 @@ func NewProxy() (*ProxyContext, error) {
AlwaysGetPacks: false,
WithClient: true,
IgnoreDisconnect: false,
reconnectHandler: NewTransferHandler(),
}
if Options.PathCustomUserData != "" {
if err := p.LoadCustomUserData(Options.PathCustomUserData); err != nil {
@ -247,6 +249,8 @@ func (p *ProxyContext) proxyLoop(ctx context.Context, toServer bool) error {
c2 = p.Client
}
var transferingErr error = nil
for {
if ctx.Err() != nil {
return ctx.Err()
@ -262,7 +266,12 @@ func (p *ProxyContext) proxyLoop(ctx context.Context, toServer bool) error {
if handler.PacketCB != nil {
pk, err = handler.PacketCB(pk, toServer, time.Now())
if err != nil {
return err
if errors.Is(err, transferingErr) {
transferingErr = err
err = nil
} else {
return err
}
}
if pk == nil {
logrus.Tracef("Dropped Packet: %s", pkName)
@ -279,6 +288,10 @@ func (p *ProxyContext) proxyLoop(ctx context.Context, toServer bool) error {
return err
}
}
if transferingErr != nil {
return transferingErr
}
}
}
@ -354,27 +367,19 @@ func (p *ProxyContext) connectClient(ctx context.Context, serverAddress string,
return nil
}
func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err error) {
if Options.Debug || Options.ExtraDebug {
p.AddHandler(NewDebugLogger(Options.ExtraDebug))
func (p *ProxyContext) packetFunc(header packet.Header, payload []byte, src, dst net.Addr) {
if header.PacketID == packet.IDRequestNetworkSettings {
p.clientAddr = src
}
if Options.Capture {
p.AddHandler(NewPacketCapturer())
}
p.AddHandler(&ProxyHandler{
Name: "Commands",
PacketCB: p.CommandHandlerPacketCB,
})
for _, handler := range p.handlers {
if handler.AddressAndName != nil {
handler.AddressAndName(serverAddress, name)
}
if handler.ProxyRef != nil {
handler.ProxyRef(p)
if handler.PacketFunc == nil {
continue
}
handler.PacketFunc(header, payload, src, dst)
}
}
func (p *ProxyContext) connect(ctx context.Context, serverAddress string) (err error) {
defer func() {
for _, handler := range p.handlers {
if handler.OnEnd != nil {
@ -410,7 +415,6 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err
if p.Client != nil {
p.Listener.Disconnect(p.Client.(*minecraft.Conn), DisconnectReason)
}
p.Listener.Close()
}
}()
}
@ -426,25 +430,13 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err
handler.OnClientConnect(p.Client)
}
packetFunc := func(header packet.Header, payload []byte, src, dst net.Addr) {
if header.PacketID == packet.IDRequestNetworkSettings {
p.clientAddr = src
}
for _, handler := range p.handlers {
if handler.PacketFunc == nil {
continue
}
handler.PacketFunc(header, payload, src, dst)
}
}
if isReplay {
p.Server, err = createReplayConnector(serverAddress[5:], packetFunc)
p.Server, err = createReplayConnector(serverAddress[5:], p.packetFunc)
if err != nil {
return err
}
} else {
p.Server, err = connectServer(ctx, serverAddress, cdp, p.AlwaysGetPacks, packetFunc, tokenSource)
p.Server, err = connectServer(ctx, serverAddress, cdp, p.AlwaysGetPacks, p.packetFunc, tokenSource)
}
if err != nil {
for _, handler := range p.handlers {
@ -498,51 +490,92 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err
}
}
ctx2, cancel := context.WithCancelCause(ctx)
wg := sync.WaitGroup{}
doProxy := func(client bool, onErr func()) {
doProxy := func(client bool) {
defer wg.Done()
if err := p.proxyLoop(ctx, client); err != nil {
logrus.Error(err)
if err := p.proxyLoop(ctx2, client); err != nil {
cancel(err)
return
}
}
// server to client
wg.Add(1)
go doProxy(false, func() {
p.DisconnectClient()
})
go doProxy(false)
// client to server
if p.Client != nil {
wg.Add(1)
go doProxy(true, func() {
p.DisconnectServer()
})
go doProxy(true)
}
go func() {
wg.Wait()
if ctx.Err() == nil {
cancel(nil)
}
}()
/*
wantSecondary := fp.Filter(func(handler *ProxyHandler) bool {
return handler.SecondaryClientCB != nil
})(p.handlers)
if len(wantSecondary) > 0 {
go func() {
for {
c, err := p.Listener.Accept()
if err != nil {
logrus.Error(err)
return
}
for _, handler := range wantSecondary {
go handler.SecondaryClientCB(c.(*minecraft.Conn))
}
}
}()
}
*/
<-ctx2.Done()
err = ctx2.Err()
if err, ok := err.(*transferingErr); ok {
logrus.Infof("Redirect to %s", err.To)
if p.Client != nil {
p.Listener.Disconnect(p.Client.(*minecraft.Conn), "please reconnect")
}
return p.connect(ctx, err.To)
}
wantSecondary := fp.Filter(func(handler *ProxyHandler) bool {
return handler.SecondaryClientCB != nil
})(p.handlers)
return nil
}
if len(wantSecondary) > 0 {
go func() {
for {
c, err := p.Listener.Accept()
if err != nil {
logrus.Error(err)
return
}
func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err error) {
if Options.Debug || Options.ExtraDebug {
p.AddHandler(NewDebugLogger(Options.ExtraDebug))
}
if Options.Capture {
p.AddHandler(NewPacketCapturer())
}
p.AddHandler(&ProxyHandler{
Name: "Commands",
PacketCB: p.CommandHandlerPacketCB,
})
for _, handler := range wantSecondary {
go handler.SecondaryClientCB(c.(*minecraft.Conn))
}
}
}()
p.AddHandler(p.reconnectHandler)
for _, handler := range p.handlers {
if handler.AddressAndName != nil {
handler.AddressAndName(serverAddress, name)
}
if handler.ProxyRef != nil {
handler.ProxyRef(p)
}
}
wg.Wait()
return err
return p.connect(ctx, serverAddress)
}
func DecodePacket(pool packet.Pool, header packet.Header, payload []byte) packet.Packet {

View File

@ -18,12 +18,22 @@ type Skin struct {
}
type SkinGeometry struct {
SkinGeometryDescription
Bones []any `json:"bones"`
}
type SkinGeometryDescription struct {
Identifier string `json:"identifier,omitempty"`
Texturewidth int `json:"texturewidth"`
Textureheight int `json:"textureheight"`
VisibleBoundsWidth float64 `json:"visible_bounds_width"`
VisibleBoundsHeight float64 `json:"visible_bounds_height"`
VisibleBoundsOffset []float64 `json:"visible_bounds_offset,omitempty"`
Bones []any `json:"bones"`
}
type SkinGeometry_1_12 struct {
Description SkinGeometryDescription `json:"description"`
Bones []any `json:"bones"`
}
func (skin *Skin) Hash() uuid.UUID {
@ -31,7 +41,7 @@ func (skin *Skin) Hash() uuid.UUID {
return uuid.NewSHA1(uuid.NameSpaceURL, h)
}
func (skin *Skin) getGeometry() (*SkinGeometry, string, error) {
func (skin *Skin) getGeometry() (*SkinGeometry_1_12, string, error) {
if !skin.HaveGeometry() {
return nil, "", errors.New("no geometry")
}
@ -65,13 +75,16 @@ func (skin *Skin) getGeometry() (*SkinGeometry, string, error) {
visible_bounds_height, _ := desc["visible_bounds_height"].(float64)
visibleOffset, _ := desc["visible_bounds_offset"].([]float64)
return &SkinGeometry{
Texturewidth: int(texture_width),
Textureheight: int(texture_height),
VisibleBoundsWidth: visible_bounds_width,
VisibleBoundsHeight: visible_bounds_height,
VisibleBoundsOffset: visibleOffset,
Bones: geom["bones"].([]any),
return &SkinGeometry_1_12{
Description: SkinGeometryDescription{
Identifier: desc["identifier"].(string),
Texturewidth: int(texture_width),
Textureheight: int(texture_height),
VisibleBoundsWidth: visible_bounds_width,
VisibleBoundsHeight: visible_bounds_height,
VisibleBoundsOffset: visibleOffset,
},
Bones: geom["bones"].([]any),
}, desc["identifier"].(string), nil
}

View File

@ -113,13 +113,16 @@ func (s *SkinPack) Save(fpath, serverName string) error {
e.SetIndent("", "\t")
if err := e.Encode(map[string]any{
"format_version": "1.12.0",
"minecraft:geometry": geometry,
"minecraft:geometry": []*SkinGeometry_1_12{geometry},
}); err != nil {
f.Close()
return err
}
f.Close()
geometryJson[geometryName] = *geometry
geometryJson[geometryName] = SkinGeometry{
SkinGeometryDescription: geometry.Description,
Bones: geometry.Bones,
}
entry.Geometry = geometryName
}
}

53
utils/transfer.go Normal file
View File

@ -0,0 +1,53 @@
package utils
import (
"fmt"
"net"
"strconv"
"time"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
type transferingErr struct {
To string
}
func (transferingErr) Error() string {
return "transferingErr"
}
type transferHandler struct {
p *ProxyContext
}
func NewTransferHandler() *ProxyHandler {
t := &transferHandler{}
return &ProxyHandler{
Name: "transfer",
ProxyRef: func(pc *ProxyContext) {
t.p = pc
},
PacketCB: t.packetCB,
}
}
func (t *transferHandler) packetCB(pk packet.Packet, toServer bool, timeReceived time.Time) (packet.Packet, error) {
switch pk := pk.(type) {
case *packet.Transfer:
var pk2 packet.Packet = nil
if t.p.Client != nil {
host, port, err := net.SplitHostPort(t.p.Client.ClientData().ServerAddress)
if err != nil {
return nil, err
}
_port, _ := strconv.Atoi(port)
pk2 = &packet.Transfer{Address: host, Port: uint16(_port)}
}
return pk2, &transferingErr{
To: fmt.Sprintf("%s:%d", pk.Address, pk.Port),
}
}
return pk, nil
}