diff --git a/handlers/packet_logger.go b/handlers/packet_logger.go index 62121a3..b537d12 100644 --- a/handlers/packet_logger.go +++ b/handlers/packet_logger.go @@ -2,7 +2,6 @@ package handlers import ( "bufio" - "bytes" "io" "net" "os" @@ -12,7 +11,6 @@ import ( "github.com/bedrock-tool/bedrocktool/utils" "github.com/bedrock-tool/bedrocktool/utils/crypt" "github.com/fatih/color" - "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" "github.com/sirupsen/logrus" "golang.org/x/exp/slices" @@ -79,20 +77,11 @@ func NewDebugLogger(extraVerbose bool) *utils.ProxyHandler { proxy = pc }, PacketFunc: func(header packet.Header, payload []byte, src, dst net.Addr) { - var pk packet.Packet - if pkFunc, ok := pool[header.PacketID]; ok { - pk = pkFunc() - } else { - pk = &packet.Unknown{PacketID: header.PacketID, Payload: payload} + pk := utils.DecodePacket(header, payload) + if pk == nil { + return } - defer func() { - if recoveredErr := recover(); recoveredErr != nil { - logrus.Errorf("%T: %s", pk, recoveredErr.(error)) - } - }() - pk.Marshal(protocol.NewReader(bytes.NewBuffer(payload), 0)) - if packetsLogF != nil { dmpLock.Lock() packetsLogF.Write([]byte(utils.DumpStruct(0, pk, true, false) + "\n\n\n")) diff --git a/subcommands/resourcepack-d/resourcepack-d.go b/subcommands/resourcepack-d/resourcepack-d.go index d72550a..fed4ea4 100644 Binary files a/subcommands/resourcepack-d/resourcepack-d.go and b/subcommands/resourcepack-d/resourcepack-d.go differ diff --git a/ui/gui/pages/packs/packs.go b/ui/gui/pages/packs/packs.go index 47a7073..6a601f3 100644 --- a/ui/gui/pages/packs/packs.go +++ b/ui/gui/pages/packs/packs.go @@ -1,8 +1,10 @@ package packs import ( + "fmt" "image" "image/color" + "sort" "sync" "gioui.org/f32" @@ -29,16 +31,21 @@ type Page struct { State messages.UIState packsList widget.List l sync.Mutex - Packs []*packEntry + Packs map[string]*packEntry } type packEntry struct { + IsFinished bool + UUID string + HasIcon bool Icon paint.ImageOp - Size string - Name string - Path string - Err error + + Size uint64 + Loaded uint64 + Name string + Path string + Err error } func New(router *pages.Router) *Page { @@ -49,6 +56,7 @@ func New(router *pages.Router) *Page { Axis: layout.Vertical, }, }, + Packs: make(map[string]*packEntry), } } @@ -80,7 +88,25 @@ func drawPackIcon(ops *op.Ops, imageOp paint.ImageOp, bounds image.Point) { paint.PaintOp{}.Add(ops) } +func MulAlpha(c color.NRGBA, alpha uint8) color.NRGBA { + c.A = uint8(uint32(c.A) * uint32(alpha) / 0xFF) + return c +} + func drawPackEntry(gtx C, th *material.Theme, entry *packEntry) D { + var size = "" + var colorSize = th.Palette.Fg + if entry.IsFinished { + size = utils.SizeofFmt(float32(entry.Size)) + } else { + size = fmt.Sprintf("%s / %s %.02f%%", + utils.SizeofFmt(float32(entry.Size)), + utils.SizeofFmt(float32(entry.Size)), + float32(entry.Loaded)/float32(entry.Size)*100, + ) + colorSize = color.NRGBA{0x00, 0xc9, 0xc9, 0xff} + } + return layout.UniformInset(5).Layout(gtx, func(gtx C) D { return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, layout.Rigid(func(gtx C) D { @@ -93,7 +119,13 @@ func drawPackEntry(gtx C, th *material.Theme, entry *packEntry) D { layout.Flexed(1, func(gtx C) D { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(material.Label(th, th.TextSize, entry.Name).Layout), - layout.Rigid(material.Label(th, th.TextSize, entry.Size).Layout), + layout.Rigid(material.LabelStyle{ + Text: size, + Color: colorSize, + SelectionColor: MulAlpha(th.Palette.ContrastBg, 0x60), + TextSize: th.TextSize, + Shaper: th.Shaper, + }.Layout), layout.Rigid(func(gtx C) D { if entry.Err != nil { return material.LabelStyle{ @@ -118,8 +150,15 @@ func (p *Page) layoutFinished(gtx C, th *material.Theme) D { layout.Flexed(1, func(gtx C) D { p.l.Lock() defer p.l.Unlock() - return material.List(th, &p.packsList).Layout(gtx, len(p.Packs), func(gtx C, index int) D { - entry := p.Packs[len(p.Packs)-index-1] + + keys := make([]string, 0, len(p.Packs)) + for k := range p.Packs { + keys = append(keys, k) + } + sort.Strings(keys) + + return material.List(th, &p.packsList).Layout(gtx, len(keys), func(gtx C, index int) D { + entry := p.Packs[keys[index]] return drawPackEntry(gtx, th, entry) }) }), @@ -137,7 +176,7 @@ func (p *Page) Layout(gtx C, th *material.Theme) D { return margin.Layout(gtx, func(gtx layout.Context) layout.Dimensions { switch p.State { - case messages.UIStateFinished: + case messages.UIStateMain: return p.layoutFinished(gtx, th) } return layout.Dimensions{} @@ -155,19 +194,44 @@ func (p *Page) Handler(data interface{}) messages.MessageResponse { p.State = m p.Router.Invalidate() r.Ok = true - case messages.FinishedDownloadingPacks: - p.State = messages.UIStateFinished + + case messages.InitialPacksInfo: + p.State = messages.UIStateMain + p.l.Lock() for _, dp := range m.Packs { e := &packEntry{ - Name: dp.Name, - Size: utils.SizeofFmt(float32(dp.Size)), + IsFinished: false, + UUID: dp.UUID, + Name: dp.SubPackName + "v" + dp.Version, + Size: dp.Size, } + p.Packs[e.UUID] = e + } + p.l.Unlock() + p.Router.Invalidate() + + case messages.PackDownloadProgress: + p.l.Lock() + e := p.Packs[m.UUID] + e.Loaded += m.LoadedAdd + if e.Loaded == e.Size { + e.IsFinished = true + } + p.l.Unlock() + p.Router.Invalidate() + + case messages.FinishedDownloadingPacks: + p.l.Lock() + for _, dp := range m.Packs { + e := p.Packs[dp.UUID] if dp.Icon != nil { e.Icon = paint.NewImageOpFilter(dp.Icon, paint.FilterNearest) e.HasIcon = true } - p.Packs = append(p.Packs, e) + e.Err = dp.Err + e.IsFinished = true } + p.l.Unlock() p.Router.Invalidate() r.Ok = true } diff --git a/ui/messages/messages.go b/ui/messages/messages.go index 9a79a89..8db0de4 100644 --- a/ui/messages/messages.go +++ b/ui/messages/messages.go @@ -69,7 +69,17 @@ type SavingWorld struct { type CanShowImages struct{} +type InitialPacksInfo struct { + Packs []protocol.TexturePackInfo +} + +type PackDownloadProgress struct { + UUID string + LoadedAdd uint64 +} + type DownloadedPack struct { + UUID string Name string Path string Size int diff --git a/utils/proxy.go b/utils/proxy.go index 973c4e8..be82234 100644 --- a/utils/proxy.go +++ b/utils/proxy.go @@ -1,6 +1,7 @@ package utils import ( + "bytes" "context" "encoding/base64" "encoding/json" @@ -70,6 +71,8 @@ type ProxyHandler struct { OnClientConnect func(conn minecraft.IConn) SecondaryClientCB func(conn minecraft.IConn) + // called after server connected & downloaded resource packs + OnServerConnect func() (cancel bool) // called after game started ConnectCB func(err error) bool @@ -351,11 +354,6 @@ func (p *ProxyContext) connectClient(ctx context.Context, serverAddress string, return nil } -func (p *ProxyContext) connectServer(ctx context.Context, serverAddress string, cdp *login.ClientData, packetFunc PacketFunc) (err error) { - p.Server, err = connectServer(ctx, serverAddress, cdp, p.AlwaysGetPacks, packetFunc) - return err -} - func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err error) { if Options.Debug || Options.ExtraDebug { p.AddHandler(NewDebugLogger(Options.ExtraDebug)) @@ -436,7 +434,7 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err return err } } else { - err = p.connectServer(ctx, serverAddress, cdp, packetFunc) + p.Server, err = connectServer(ctx, serverAddress, cdp, p.AlwaysGetPacks, packetFunc) } if err != nil { for _, handler := range p.handlers { @@ -457,6 +455,16 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err } defer p.Server.Close() + for _, handler := range p.handlers { + if handler.OnServerConnect == nil { + continue + } + cancel := handler.OnServerConnect() + if cancel { + return nil + } + } + gd := p.Server.GameData() for _, handler := range p.handlers { if handler.GameDataModifier != nil { @@ -475,7 +483,8 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err continue } if !handler.ConnectCB(nil) { - return errors.New("Cancelled") + logrus.Info("Disconnecting") + return nil } } @@ -525,3 +534,22 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err wg.Wait() return err } + +var pool = packet.NewPool() + +func DecodePacket(header packet.Header, payload []byte) packet.Packet { + var pk packet.Packet + if pkFunc, ok := pool[header.PacketID]; ok { + pk = pkFunc() + } else { + pk = &packet.Unknown{PacketID: header.PacketID, Payload: payload} + } + + defer func() { + if recoveredErr := recover(); recoveredErr != nil { + logrus.Errorf("%T: %s", pk, recoveredErr.(error)) + } + }() + pk.Marshal(protocol.NewReader(bytes.NewBuffer(payload), 0)) + return pk +}