diff --git a/go.mod b/go.mod index fd644f4..5f60210 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index ae85e44..f9b7233 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/subcommands/resourcepack-d/resourcepack-d.go b/subcommands/resourcepack-d/resourcepack-d.go index 571045a..f137dae 100644 Binary files a/subcommands/resourcepack-d/resourcepack-d.go and b/subcommands/resourcepack-d/resourcepack-d.go differ diff --git a/utils/proxy.go b/utils/proxy.go index bcdee75..ca31e27 100644 --- a/utils/proxy.go +++ b/utils/proxy.go @@ -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 { diff --git a/utils/skin.go b/utils/skin.go index 1c757e5..5024a00 100644 --- a/utils/skin.go +++ b/utils/skin.go @@ -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 } diff --git a/utils/skinpack.go b/utils/skinpack.go index 81ffbba..e58c08f 100644 --- a/utils/skinpack.go +++ b/utils/skinpack.go @@ -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 } } diff --git a/utils/transfer.go b/utils/transfer.go new file mode 100644 index 0000000..3aea2a0 --- /dev/null +++ b/utils/transfer.go @@ -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 +}