rework ui code to allow writing uis for subcommands

This commit is contained in:
olebeck 2023-03-06 21:49:30 +01:00
parent 5565301f11
commit c9850772d5
23 changed files with 412 additions and 287 deletions

View File

@ -5,7 +5,6 @@ import (
"context" "context"
"flag" "flag"
"fmt" "fmt"
"io"
"os" "os"
"os/signal" "os/signal"
"runtime/debug" "runtime/debug"
@ -13,31 +12,30 @@ import (
"github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/locale"
"github.com/bedrock-tool/bedrocktool/utils" "github.com/bedrock-tool/bedrocktool/utils"
"github.com/bedrock-tool/bedrocktool/utils/crypt"
"gopkg.in/square/go-jose.v2/json" "gopkg.in/square/go-jose.v2/json"
_ "github.com/bedrock-tool/bedrocktool/subcommands" _ "github.com/bedrock-tool/bedrocktool/subcommands"
_ "github.com/bedrock-tool/bedrocktool/subcommands/skins" _ "github.com/bedrock-tool/bedrocktool/subcommands/skins"
_ "github.com/bedrock-tool/bedrocktool/subcommands/world" _ "github.com/bedrock-tool/bedrocktool/subcommands/world"
_ "github.com/bedrock-tool/bedrocktool/ui/gui" _ "github.com/bedrock-tool/bedrocktool/ui"
"github.com/google/subcommands" "github.com/google/subcommands"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
type CLI struct { type CLI struct {
utils.UI utils.BaseUI
} }
func (c *CLI) Init() { func (c *CLI) Init() bool {
utils.SetCurrentUI(c)
return true
} }
func (c *CLI) SetOptions(context.Context) bool { func (c *CLI) Start(ctx context.Context) error {
flag.Parse() flag.Parse()
return false utils.InitDNS()
} utils.InitExtraDebug()
func (c *CLI) Execute(ctx context.Context) error {
subcommands.Execute(ctx) subcommands.Execute(ctx)
return nil return nil
} }
@ -105,53 +103,6 @@ func main() {
ui = &CLI{} ui = &CLI{}
} }
ui.Init()
if ui.SetOptions(ctx) {
return
}
if ctx.Err() != nil {
return
}
if utils.Options.EnableDNS {
utils.InitDNS()
}
if utils.Options.ExtraDebug {
utils.Options.Debug = true
var logPlain, logCryptEnc io.WriteCloser = nil, nil
// open plain text log
logPlain, err = os.Create("packets.log")
if err != nil {
logrus.Error(err)
} else {
defer logPlain.Close()
}
// open gpg log
logCrypt, err := os.Create("packets.log.gpg")
if err != nil {
logrus.Error(err)
} else {
defer logCrypt.Close()
// encrypter for the log
logCryptEnc, err = crypt.Encer("packets.log", logCrypt)
if err != nil {
logrus.Error(err)
} else {
defer logCryptEnc.Close()
}
}
utils.FLog = io.MultiWriter(logPlain, logCryptEnc)
if err != nil {
logrus.Error(err)
}
}
// exit cleanup // exit cleanup
sigs := make(chan os.Signal, 1) sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
@ -161,12 +112,13 @@ func main() {
cancel() cancel()
}() }()
ui.Execute(ctx) if !ui.Init() {
logrus.Error("Failed to init UI!")
if utils.Options.IsInteractive { return
logrus.Info(locale.Loc("enter_to_exit", nil)) }
input := bufio.NewScanner(os.Stdin) err = ui.Start(ctx)
input.Scan() if err != nil {
logrus.Error(err)
} }
} }
@ -176,16 +128,10 @@ type TransCMD struct {
func (*TransCMD) Name() string { return "trans" } func (*TransCMD) Name() string { return "trans" }
func (*TransCMD) Synopsis() string { return "" } func (*TransCMD) Synopsis() string { return "" }
func (c *TransCMD) SetFlags(f *flag.FlagSet) { func (c *TransCMD) SetFlags(f *flag.FlagSet) {
f.BoolVar(&c.auth, "auth", false, locale.Loc("should_login_xbox", nil)) f.BoolVar(&c.auth, "auth", false, locale.Loc("should_login_xbox", nil))
} }
func (c *TransCMD) Execute(_ context.Context, ui utils.UI) error {
func (c *TransCMD) Usage() string {
return c.Name() + ": " + c.Synopsis() + "\n"
}
func (c *TransCMD) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
const ( const (
BlackFg = "\033[30m" BlackFg = "\033[30m"
Bold = "\033[1m" Bold = "\033[1m"
@ -198,7 +144,7 @@ func (c *TransCMD) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
utils.GetTokenSource() utils.GetTokenSource()
} }
fmt.Println(BlackFg + Bold + Blue + " Trans " + Pink + " Rights " + White + " Are " + Pink + " Human " + Blue + " Rights " + Reset) fmt.Println(BlackFg + Bold + Blue + " Trans " + Pink + " Rights " + White + " Are " + Pink + " Human " + Blue + " Rights " + Reset)
return 0 return nil
} }
type CreateCustomDataCMD struct { type CreateCustomDataCMD struct {
@ -212,11 +158,7 @@ func (c *CreateCustomDataCMD) SetFlags(f *flag.FlagSet) {
f.StringVar(&c.path, "path", "customdata.json", "where to save") f.StringVar(&c.path, "path", "customdata.json", "where to save")
} }
func (c *CreateCustomDataCMD) Usage() string { func (c *CreateCustomDataCMD) Execute(_ context.Context, ui utils.UI) error {
return c.Name() + ": " + c.Synopsis() + "\n"
}
func (c *CreateCustomDataCMD) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
var data utils.CustomClientData var data utils.CustomClientData
fio, err := os.Create(c.path) fio, err := os.Create(c.path)
if err == nil { if err == nil {
@ -226,10 +168,9 @@ func (c *CreateCustomDataCMD) Execute(_ context.Context, f *flag.FlagSet, _ ...i
fio.Write(bdata) fio.Write(bdata)
} }
if err != nil { if err != nil {
logrus.Error(err) return err
return 1
} }
return 0 return nil
} }
func init() { func init() {

View File

@ -14,7 +14,6 @@ import (
"github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/locale"
"github.com/bedrock-tool/bedrocktool/utils" "github.com/bedrock-tool/bedrocktool/utils"
"github.com/google/subcommands"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet" "github.com/sandertv/gophertunnel/minecraft/protocol/packet"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -49,27 +48,20 @@ type CaptureCMD struct {
func (*CaptureCMD) Name() string { return "capture" } func (*CaptureCMD) Name() string { return "capture" }
func (*CaptureCMD) Synopsis() string { return locale.Loc("capture_synopsis", nil) } func (*CaptureCMD) Synopsis() string { return locale.Loc("capture_synopsis", nil) }
func (c *CaptureCMD) SetFlags(f *flag.FlagSet) { func (c *CaptureCMD) SetFlags(f *flag.FlagSet) {
f.StringVar(&c.ServerAddress, "address", "", "remote server address") f.StringVar(&c.ServerAddress, "address", "", "remote server address")
} }
func (c *CaptureCMD) Usage() string { func (c *CaptureCMD) Execute(ctx context.Context, ui utils.UI) error {
return c.Name() + ": " + c.Synopsis() + "\n" + locale.Loc("server_address_help", nil)
}
func (c *CaptureCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
address, hostname, err := utils.ServerInput(ctx, c.ServerAddress) address, hostname, err := utils.ServerInput(ctx, c.ServerAddress)
if err != nil { if err != nil {
logrus.Fatal(err) return err
return 1
} }
os.Mkdir("captures", 0o775) os.Mkdir("captures", 0o775)
fio, err := os.Create("captures/" + hostname + "-" + time.Now().Format("2006-01-02_15-04-05") + ".pcap2") fio, err := os.Create("captures/" + hostname + "-" + time.Now().Format("2006-01-02_15-04-05") + ".pcap2")
if err != nil { if err != nil {
logrus.Fatal(err) return err
return 1
} }
defer fio.Close() defer fio.Close()
utils.WriteReplayHeader(fio) utils.WriteReplayHeader(fio)
@ -90,8 +82,7 @@ func (c *CaptureCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interfac
err = proxy.Run(ctx, address) err = proxy.Run(ctx, address)
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
if err != nil { if err != nil {
logrus.Fatal(err) return err
return 1
} }
return 0 return nil
} }

View File

@ -10,7 +10,6 @@ import (
"github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/locale"
"github.com/bedrock-tool/bedrocktool/utils" "github.com/bedrock-tool/bedrocktool/utils"
"github.com/google/subcommands"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet" "github.com/sandertv/gophertunnel/minecraft/protocol/packet"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -22,33 +21,27 @@ type ChatLogCMD struct {
func (*ChatLogCMD) Name() string { return "chat-log" } func (*ChatLogCMD) Name() string { return "chat-log" }
func (*ChatLogCMD) Synopsis() string { return locale.Loc("chat_log_synopsis", nil) } func (*ChatLogCMD) Synopsis() string { return locale.Loc("chat_log_synopsis", nil) }
func (c *ChatLogCMD) SetFlags(f *flag.FlagSet) { func (c *ChatLogCMD) SetFlags(f *flag.FlagSet) {
f.StringVar(&c.ServerAddress, "address", "", "remote server address") f.StringVar(&c.ServerAddress, "address", "", "remote server address")
f.BoolVar(&c.Verbose, "v", false, "verbose") f.BoolVar(&c.Verbose, "v", false, "verbose")
} }
func (c *ChatLogCMD) Usage() string { func (c *ChatLogCMD) Execute(ctx context.Context, ui utils.UI) error {
return c.Name() + ": " + c.Synopsis() + "\n" + locale.Loc("server_address_help", nil)
}
func (c *ChatLogCMD) Execute(ctx context.Context, flags *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
address, hostname, err := utils.ServerInput(ctx, c.ServerAddress) address, hostname, err := utils.ServerInput(ctx, c.ServerAddress)
if err != nil { if err != nil {
logrus.Error(err) return err
return 1
} }
filename := fmt.Sprintf("%s_%s_chat.log", hostname, time.Now().Format("2006-01-02_15-04-05_Z07")) filename := fmt.Sprintf("%s_%s_chat.log", hostname, time.Now().Format("2006-01-02_15-04-05_Z07"))
f, err := os.Create(filename) f, err := os.Create(filename)
if err != nil { if err != nil {
logrus.Fatal(err) return err
} }
defer f.Close() defer f.Close()
proxy, err := utils.NewProxy() proxy, err := utils.NewProxy()
if err != nil { if err != nil {
logrus.Fatal(err) return err
} }
proxy.PacketCB = func(pk packet.Packet, proxy *utils.ProxyContext, toServer bool, _ time.Time) (packet.Packet, error) { proxy.PacketCB = func(pk packet.Packet, proxy *utils.ProxyContext, toServer bool, _ time.Time) (packet.Packet, error) {
if text, ok := pk.(*packet.Text); ok { if text, ok := pk.(*packet.Text); ok {
@ -66,12 +59,8 @@ func (c *ChatLogCMD) Execute(ctx context.Context, flags *flag.FlagSet, _ ...inte
return pk, nil return pk, nil
} }
if err := proxy.Run(ctx, address); err != nil { err = proxy.Run(ctx, address)
logrus.Error(err) return err
return 1
}
return 0
} }
func init() { func init() {

View File

@ -7,9 +7,6 @@ import (
"github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/locale"
"github.com/bedrock-tool/bedrocktool/utils" "github.com/bedrock-tool/bedrocktool/utils"
"github.com/google/subcommands"
"github.com/sirupsen/logrus"
) )
type DebugProxyCMD struct { type DebugProxyCMD struct {
@ -19,21 +16,15 @@ type DebugProxyCMD struct {
func (*DebugProxyCMD) Name() string { return "debug-proxy" } func (*DebugProxyCMD) Name() string { return "debug-proxy" }
func (*DebugProxyCMD) Synopsis() string { return locale.Loc("debug_proxy_synopsis", nil) } func (*DebugProxyCMD) Synopsis() string { return locale.Loc("debug_proxy_synopsis", nil) }
func (c *DebugProxyCMD) SetFlags(f *flag.FlagSet) { func (c *DebugProxyCMD) SetFlags(f *flag.FlagSet) {
f.StringVar(&c.ServerAddress, "address", "", locale.Loc("remote_address", nil)) f.StringVar(&c.ServerAddress, "address", "", locale.Loc("remote_address", nil))
f.StringVar(&c.Filter, "filter", "", locale.Loc("packet_filter", nil)) f.StringVar(&c.Filter, "filter", "", locale.Loc("packet_filter", nil))
} }
func (c *DebugProxyCMD) Usage() string { func (c *DebugProxyCMD) Execute(ctx context.Context, ui utils.UI) error {
return c.Name() + ": " + c.Synopsis() + "\n" + locale.Loc("server_address_help", nil)
}
func (c *DebugProxyCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
address, _, err := utils.ServerInput(ctx, c.ServerAddress) address, _, err := utils.ServerInput(ctx, c.ServerAddress)
if err != nil { if err != nil {
logrus.Error(err) return err
return 1
} }
utils.Options.Debug = true utils.Options.Debug = true
@ -55,14 +46,10 @@ func (c *DebugProxyCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...inter
proxy, err := utils.NewProxy() proxy, err := utils.NewProxy()
if err != nil { if err != nil {
logrus.Error(err) return err
return 1
} }
if err := proxy.Run(ctx, address); err != nil { err = proxy.Run(ctx, address)
logrus.Error(err) return err
return 1
}
return 0
} }
func init() { func init() {

View File

@ -1,3 +1,5 @@
//go:build false
package subcommands package subcommands
import ( import (

View File

@ -9,12 +9,8 @@ import (
"github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/locale"
resourcepackd "github.com/bedrock-tool/bedrocktool/subcommands/resourcepack-d" resourcepackd "github.com/bedrock-tool/bedrocktool/subcommands/resourcepack-d"
"github.com/bedrock-tool/bedrocktool/utils" "github.com/bedrock-tool/bedrocktool/utils"
"github.com/google/subcommands"
"github.com/sirupsen/logrus"
) )
// decrypt using cfb with segmentsize = 1
type ResourcePackCMD struct { type ResourcePackCMD struct {
ServerAddress string ServerAddress string
SaveEncrypted bool SaveEncrypted bool
@ -30,17 +26,8 @@ func (c *ResourcePackCMD) SetFlags(f *flag.FlagSet) {
f.BoolVar(&c.OnlyKeys, "only-keys", false, locale.Loc("only_keys", nil)) f.BoolVar(&c.OnlyKeys, "only-keys", false, locale.Loc("only_keys", nil))
} }
func (c *ResourcePackCMD) Usage() string { func (c *ResourcePackCMD) Execute(ctx context.Context, ui utils.UI) error {
return c.Name() + ": " + c.Synopsis() + "\n" + locale.Loc("server_address_help", nil) return resourcepackd.Execute_cmd(ctx, c.ServerAddress, c.OnlyKeys, c.SaveEncrypted)
}
func (c *ResourcePackCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
err := resourcepackd.Execute_cmd(ctx, c.ServerAddress, c.OnlyKeys, c.SaveEncrypted)
if err != nil {
logrus.Error(err)
return 1
}
return 0
} }
func init() { func init() {

View File

@ -4,11 +4,10 @@ package subcommands
import ( import (
"context" "context"
"errors"
"flag" "flag"
"github.com/bedrock-tool/bedrocktool/utils" "github.com/bedrock-tool/bedrocktool/utils"
"github.com/google/subcommands"
"github.com/sirupsen/logrus"
) )
type ResourcePackCMD struct { type ResourcePackCMD struct {
@ -17,18 +16,11 @@ type ResourcePackCMD struct {
OnlyKeys bool OnlyKeys bool
} }
func (*ResourcePackCMD) Name() string { return "packs" } func (*ResourcePackCMD) Name() string { return "packs" }
func (*ResourcePackCMD) Synopsis() string { return "NOT COMPILED" } func (*ResourcePackCMD) Synopsis() string { return "NOT COMPILED" }
func (*ResourcePackCMD) SetFlags(f *flag.FlagSet) {}
func (c *ResourcePackCMD) SetFlags(f *flag.FlagSet) {} func (*ResourcePackCMD) Execute(ctx context.Context, ui utils.UI) error {
return errors.New("not compiled")
func (c *ResourcePackCMD) Usage() string {
return c.Name() + ": " + c.Synopsis()
}
func (c *ResourcePackCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
logrus.Error("not compiled")
return 1
} }
func init() { func init() {

View File

@ -12,7 +12,6 @@ import (
"github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/locale"
"github.com/bedrock-tool/bedrocktool/utils" "github.com/bedrock-tool/bedrocktool/utils"
"github.com/google/subcommands"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet" "github.com/sandertv/gophertunnel/minecraft/protocol/packet"
@ -124,15 +123,10 @@ func (c *SkinCMD) SetFlags(f *flag.FlagSet) {
f.BoolVar(&c.NoProxy, "no-proxy", false, "use headless version") f.BoolVar(&c.NoProxy, "no-proxy", false, "use headless version")
} }
func (c *SkinCMD) Usage() string { func (c *SkinCMD) Execute(ctx context.Context, ui utils.UI) error {
return c.Name() + ": " + c.Synopsis() + "\n" + locale.Loc("server_address_help", nil)
}
func (c *SkinCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
address, hostname, err := utils.ServerInput(ctx, c.ServerAddress) address, hostname, err := utils.ServerInput(ctx, c.ServerAddress)
if err != nil { if err != nil {
logrus.Error(err) return err
return 1
} }
proxy, _ := utils.NewProxy() proxy, _ := utils.NewProxy()
@ -158,10 +152,7 @@ func (c *SkinCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}
} }
err = proxy.Run(ctx, address) err = proxy.Run(ctx, address)
if err != nil { return err
logrus.Error(err)
}
return 0
} }
func init() { func init() {

View File

@ -8,40 +8,31 @@ import (
"github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/locale"
"github.com/bedrock-tool/bedrocktool/utils" "github.com/bedrock-tool/bedrocktool/utils"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/google/subcommands"
) )
type UpdateCMD struct{} type UpdateCMD struct{}
func (*UpdateCMD) Name() string { return "update" } func (*UpdateCMD) Name() string { return "update" }
func (*UpdateCMD) Synopsis() string { return locale.Loc("update_synopsis", nil) } func (*UpdateCMD) Synopsis() string { return locale.Loc("update_synopsis", nil) }
func (c *UpdateCMD) SetFlags(f *flag.FlagSet) {} func (c *UpdateCMD) SetFlags(f *flag.FlagSet) {}
func (c *UpdateCMD) Usage() string { func (c *UpdateCMD) Execute(ctx context.Context, ui utils.UI) error {
return c.Name() + ": " + c.Synopsis() + "\n"
}
func (c *UpdateCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
newVersion, err := utils.Updater.UpdateAvailable() newVersion, err := utils.Updater.UpdateAvailable()
if err != nil { if err != nil {
logrus.Error(err) return err
return 1
} }
if newVersion == "" { if newVersion == "" {
logrus.Info(locale.Loc("no_update", nil)) logrus.Info(locale.Loc("no_update", nil))
return 0 return nil
} }
logrus.Infof(locale.Loc("updating", locale.Strmap{"Version": newVersion})) logrus.Infof(locale.Loc("updating", locale.Strmap{"Version": newVersion}))
if err := utils.Updater.Update(); err != nil { if err := utils.Updater.Update(); err != nil {
logrus.Error(err) return err
return 1
} }
logrus.Infof(locale.Loc("updated", nil)) logrus.Infof(locale.Loc("updated", nil))
return 0 return nil
} }
func init() { func init() {

View File

@ -60,9 +60,9 @@ func (w *WorldState) processLevelChunk(pk *packet.LevelChunk) {
w.chunks[pk.Position] = ch w.chunks[pk.Position] = ch
if pk.SubChunkRequestMode == protocol.SubChunkRequestModeLegacy { if pk.SubChunkRequestMode == protocol.SubChunkRequestModeLegacy {
w.ui.SetChunk(pk.Position, ch) w.mapUI.SetChunk(pk.Position, ch)
} else { } else {
w.ui.SetChunk(pk.Position, nil) w.mapUI.SetChunk(pk.Position, nil)
// request all the subchunks // request all the subchunks
max := w.Dim.Range().Height() / 16 max := w.Dim.Range().Height() / 16
@ -109,9 +109,9 @@ func (w *WorldState) processSubChunk(pk *packet.SubChunk) {
// redraw the chunks // redraw the chunks
for pos := range posToRedraw { for pos := range posToRedraw {
w.ui.SetChunk(pos, w.chunks[pos]) w.mapUI.SetChunk(pos, w.chunks[pos])
} }
w.ui.SchedRedraw() w.mapUI.SchedRedraw()
} }
func (w *WorldState) ProcessChunkPackets(pk packet.Packet) packet.Packet { func (w *WorldState) ProcessChunkPackets(pk packet.Packet) packet.Packet {

View File

@ -43,7 +43,7 @@ var MapItemPacket packet.InventoryContent = packet.InventoryContent{
}, },
} }
func (m *MapUI) getBounds() (min, max protocol.ChunkPos) { func (m *MapUI) GetBounds() (min, max protocol.ChunkPos) {
// get the chunk coord bounds // get the chunk coord bounds
i := 0 i := 0
for _ch := range m.renderedChunks { for _ch := range m.renderedChunks {
@ -75,6 +75,7 @@ type MapUI struct {
renderQueue *lockfree.Queue renderQueue *lockfree.Queue
renderedChunks map[protocol.ChunkPos]*image.RGBA // prerendered chunks renderedChunks map[protocol.ChunkPos]*image.RGBA // prerendered chunks
needRedraw bool // when the map has updated this is true needRedraw bool // when the map has updated this is true
showOnGui bool
ticker *time.Ticker ticker *time.Ticker
w *WorldState w *WorldState
@ -93,6 +94,21 @@ func NewMapUI(w *WorldState) *MapUI {
} }
func (m *MapUI) Start() { func (m *MapUI) Start() {
r := m.w.gui.Message("init_map", struct {
GetTiles func() map[protocol.ChunkPos]*image.RGBA
GetBounds func() (min, max protocol.ChunkPos)
}{
GetTiles: func() map[protocol.ChunkPos]*image.RGBA {
return m.renderedChunks
},
GetBounds: func() (min, max protocol.ChunkPos) {
return m.GetBounds()
},
})
if r.Ok {
m.showOnGui = true
}
// init map // init map
if m.w.proxy.Client != nil { if m.w.proxy.Client != nil {
if err := m.w.proxy.Client.WritePacket(&packet.ClientBoundMapItemData{ if err := m.w.proxy.Client.WritePacket(&packet.ClientBoundMapItemData{
@ -224,11 +240,15 @@ func (m *MapUI) Redraw() {
bmp.Encode(buf, img2) bmp.Encode(buf, img2)
os.WriteFile("test.bmp", buf.Bytes(), 0o777) os.WriteFile("test.bmp", buf.Bytes(), 0o777)
} }
if m.showOnGui {
m.w.gui.Message("update_map", nil)
}
} }
func (m *MapUI) ToImage() *image.RGBA { func (m *MapUI) ToImage() *image.RGBA {
// get the chunk coord bounds // get the chunk coord bounds
min, max := m.getBounds() min, max := m.GetBounds()
chunksX := int(max[0] - min[0] + 1) // how many chunk lengths is x coordinate chunksX := int(max[0] - min[0] + 1) // how many chunk lengths is x coordinate
chunksY := int(max[1] - min[1] + 1) chunksY := int(max[1] - min[1] + 1)
@ -254,8 +274,8 @@ func (m *MapUI) SetChunk(pos protocol.ChunkPos, ch *chunk.Chunk) {
func (w *WorldState) ProcessAnimate(pk *packet.Animate) { func (w *WorldState) ProcessAnimate(pk *packet.Animate) {
if pk.ActionType == packet.AnimateActionSwingArm { if pk.ActionType == packet.AnimateActionSwingArm {
w.ui.ChangeZoom() w.mapUI.ChangeZoom()
w.proxy.SendPopup(locale.Loc("zoom_level", locale.Strmap{"Level": w.ui.zoomLevel})) w.proxy.SendPopup(locale.Loc("zoom_level", locale.Strmap{"Level": w.mapUI.zoomLevel}))
} }
} }
@ -267,7 +287,7 @@ func (w *WorldState) processMapPacketsClient(pk packet.Packet, forward *bool) pa
w.SetPlayerPos(pk.Position, pk.Pitch, pk.Yaw, pk.HeadYaw) w.SetPlayerPos(pk.Position, pk.Pitch, pk.Yaw, pk.HeadYaw)
case *packet.MapInfoRequest: case *packet.MapInfoRequest:
if pk.MapID == ViewMapID { if pk.MapID == ViewMapID {
w.ui.SchedRedraw() w.mapUI.SchedRedraw()
*forward = false *forward = false
} }
case *packet.Animate: case *packet.Animate:

View File

@ -1,4 +1,3 @@
// Package world Bedrock World Downloader
package world package world
import ( import (
@ -26,7 +25,6 @@ import (
"github.com/df-mc/dragonfly/server/world/mcdb" "github.com/df-mc/dragonfly/server/world/mcdb"
"github.com/df-mc/goleveldb/leveldb/opt" "github.com/df-mc/goleveldb/leveldb/opt"
"github.com/go-gl/mathgl/mgl32" "github.com/go-gl/mathgl/mgl32"
"github.com/google/subcommands"
"github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet" "github.com/sandertv/gophertunnel/minecraft/protocol/packet"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -44,7 +42,8 @@ type TPlayerPos struct {
type WorldState struct { type WorldState struct {
ctx context.Context ctx context.Context
proxy *utils.ProxyContext proxy *utils.ProxyContext
ui *MapUI mapUI *MapUI
gui utils.UI
bp *behaviourpack.BehaviourPack bp *behaviourpack.BehaviourPack
// save state // save state
@ -66,11 +65,12 @@ type WorldState struct {
experimentInventory bool experimentInventory bool
} }
func NewWorldState(ctx context.Context, proxy *utils.ProxyContext, ServerName string) *WorldState { func NewWorldState(ctx context.Context, proxy *utils.ProxyContext, ServerName string, ui utils.UI) *WorldState {
w := &WorldState{ w := &WorldState{
ctx: ctx, ctx: ctx,
proxy: proxy, proxy: proxy,
ui: nil, mapUI: nil,
gui: ui,
bp: behaviourpack.New(ServerName), bp: behaviourpack.New(ServerName),
ServerName: ServerName, ServerName: ServerName,
@ -82,7 +82,7 @@ func NewWorldState(ctx context.Context, proxy *utils.ProxyContext, ServerName st
WorldName: "world", WorldName: "world",
PlayerPos: TPlayerPos{}, PlayerPos: TPlayerPos{},
} }
w.ui = NewMapUI(w) w.mapUI = NewMapUI(w)
return w return w
} }
@ -130,24 +130,18 @@ func (c *WorldCMD) SetFlags(f *flag.FlagSet) {
f.BoolVar(&c.ExperimentInventory, "inv", false, locale.Loc("test_block_inv", nil)) f.BoolVar(&c.ExperimentInventory, "inv", false, locale.Loc("test_block_inv", nil))
} }
func (c *WorldCMD) Usage() string { func (c *WorldCMD) Execute(ctx context.Context, ui utils.UI) error {
return c.Name() + ": " + c.Synopsis() + "\n" + locale.Loc("server_address_help", nil) serverAddress, hostname, err := ui.ServerInput(ctx, c.ServerAddress)
}
func (c *WorldCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
serverAddress, hostname, err := utils.ServerInput(ctx, c.ServerAddress)
if err != nil { if err != nil {
logrus.Error(err) return err
return 1
} }
proxy, err := utils.NewProxy() proxy, err := utils.NewProxy()
if err != nil { if err != nil {
logrus.Error(err) return err
return 1
} }
w := NewWorldState(ctx, proxy, hostname) w := NewWorldState(ctx, proxy, hostname, ui)
w.voidGen = c.EnableVoid w.voidGen = c.EnableVoid
w.withPacks = c.Packs w.withPacks = c.Packs
w.saveImage = c.SaveImage w.saveImage = c.SaveImage
@ -177,11 +171,10 @@ func (c *WorldCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{
err = w.proxy.Run(ctx, serverAddress) err = w.proxy.Run(ctx, serverAddress)
if err != nil { if err != nil {
logrus.Error(err) return err
} else {
w.SaveAndReset()
} }
return 0 w.SaveAndReset()
return nil
} }
func (w *WorldState) SetPlayerPos(Position mgl32.Vec3, Pitch, Yaw, HeadYaw float32) { func (w *WorldState) SetPlayerPos(Position mgl32.Vec3, Pitch, Yaw, HeadYaw float32) {
@ -194,14 +187,14 @@ func (w *WorldState) SetPlayerPos(Position mgl32.Vec3, Pitch, Yaw, HeadYaw float
} }
if int(last.Position.X()) != int(w.PlayerPos.Position.X()) || int(last.Position.Z()) != int(w.PlayerPos.Position.Z()) { if int(last.Position.X()) != int(w.PlayerPos.Position.X()) || int(last.Position.Z()) != int(w.PlayerPos.Position.Z()) {
w.ui.SchedRedraw() w.mapUI.SchedRedraw()
} }
} }
func (w *WorldState) Reset() { func (w *WorldState) Reset() {
w.chunks = make(map[protocol.ChunkPos]*chunk.Chunk) w.chunks = make(map[protocol.ChunkPos]*chunk.Chunk)
w.WorldName = fmt.Sprintf("world-%d", w.worldCounter) w.WorldName = fmt.Sprintf("world-%d", w.worldCounter)
w.ui.Reset() w.mapUI.Reset()
} }
// SaveAndReset writes the world to a folder, resets all the chunks // SaveAndReset writes the world to a folder, resets all the chunks
@ -421,7 +414,7 @@ func (w *WorldState) SaveAndReset() {
if w.saveImage { if w.saveImage {
f, _ := os.Create(folder + ".png") f, _ := os.Create(folder + ".png")
png.Encode(f, w.ui.ToImage()) png.Encode(f, w.mapUI.ToImage())
f.Close() f.Close()
} }
@ -485,7 +478,7 @@ func (w *WorldState) OnConnect(proxy *utils.ProxyContext, err error) bool {
w.proxy.SendMessage(locale.Loc("use_setname", nil)) w.proxy.SendMessage(locale.Loc("use_setname", nil))
w.ui.Start() w.mapUI.Start()
proxy.AddCommand(utils.IngameCommand{ proxy.AddCommand(utils.IngameCommand{
Exec: func(cmdline []string) bool { Exec: func(cmdline []string) bool {

View File

@ -1,9 +1,10 @@
//go:build gui || android || true //go:build gui || android
package gui package ui
import ( import (
"context" "context"
"sync"
"fyne.io/fyne/v2/app" "fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
@ -12,6 +13,7 @@ import (
"github.com/bedrock-tool/bedrocktool/subcommands" "github.com/bedrock-tool/bedrocktool/subcommands"
"github.com/bedrock-tool/bedrocktool/subcommands/skins" "github.com/bedrock-tool/bedrocktool/subcommands/skins"
"github.com/bedrock-tool/bedrocktool/subcommands/world" "github.com/bedrock-tool/bedrocktool/subcommands/world"
"github.com/bedrock-tool/bedrocktool/ui/gui"
"github.com/bedrock-tool/bedrocktool/utils" "github.com/bedrock-tool/bedrocktool/utils"
) )
@ -86,12 +88,16 @@ var settings = map[string]func(utils.Command) *widget.Form{
} }
type GUI struct { type GUI struct {
utils.UI utils.BaseUI
selected binding.String commandUI gui.CommandUI
} }
func (g *GUI) SetOptions(ctx context.Context) bool { func (g *GUI) Init() bool {
return true
}
func (g *GUI) Start(ctx context.Context) error {
a := app.New() a := app.New()
w := a.NewWindow("Bedrocktool") w := a.NewWindow("Bedrocktool")
@ -120,11 +126,10 @@ func (g *GUI) SetOptions(ctx context.Context) bool {
} }
} }
g.selected = binding.NewString() selected := binding.NewString()
forms_box := container.NewVBox() forms_box := container.NewVBox()
start_button := widget.NewButton("Start", nil)
var quit = true l := sync.Mutex{}
w.SetContent(container.NewVBox( w.SetContent(container.NewVBox(
widget.NewRichTextFromMarkdown("## Settings"), widget.NewRichTextFromMarkdown("## Settings"),
@ -138,46 +143,58 @@ func (g *GUI) SetOptions(ctx context.Context) bool {
), ),
widget.NewRichTextFromMarkdown("# Commands"), widget.NewRichTextFromMarkdown("# Commands"),
widget.NewSelect(entries, func(s string) { widget.NewSelect(entries, func(s string) {
g.selected.Set(s) l.Lock()
quit = false selected.Set(s)
forms_box.RemoveAll()
forms_box.Add(forms[s])
l.Unlock()
}), }),
forms_box, forms_box,
widget.NewButton("Start", func() { start_button,
w.Close()
}),
)) ))
for _, f := range forms { start_button.OnTapped = func() {
forms_box.Add(f) sub, _ := selected.Get()
} cmd := utils.ValidCMDs[sub]
g.selected.AddListener(binding.NewDataListener(func() {
v, _ := g.selected.Get() u := gui.CommandUIs[sub]
for n, f := range forms { if u != nil {
if n == v { g.commandUI = u
f.Show() w.SetContent(u.Layout())
} else {
f.Hide()
}
} }
}))
utils.InitDNS()
utils.InitExtraDebug()
go cmd.Execute(ctx, g)
}
w.ShowAndRun() w.ShowAndRun()
return quit
}
func (g *GUI) Init() {
}
func (g *GUI) Execute(ctx context.Context) error {
sub, err := g.selected.Get()
if err != nil {
return err
}
cmd := utils.ValidCMDs[sub]
cmd.Execute(ctx, nil)
return nil return nil
} }
func (g *GUI) Message(name string, data interface{}) utils.MessageResponse {
h := g.commandUI.Handler()
if h != nil {
r := h(name, data)
if r.Ok {
return r
}
}
r := utils.MessageResponse{
Ok: false,
Data: nil,
}
switch name {
case "can_show_images":
r.Ok = true
}
return r
}
func init() { func init() {
utils.MakeGui = func() utils.UI { utils.MakeGui = func() utils.UI {
return &GUI{} return &GUI{}

59
ui/gui/map_widget.go Normal file
View File

@ -0,0 +1,59 @@
package gui
import (
"image"
"image/draw"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"github.com/sandertv/gophertunnel/minecraft/protocol"
)
type mapWidget struct {
widget.BaseWidget
GetTiles func() map[protocol.ChunkPos]*image.RGBA
GetBounds func() (min, max protocol.ChunkPos)
pixels image.Image
w, h int
}
func (m *mapWidget) MinSize() fyne.Size {
return fyne.NewSize(128, 128)
}
func (m *mapWidget) CreateRenderer() fyne.WidgetRenderer {
c := container.NewMax(canvas.NewRaster(m.draw))
return widget.NewSimpleRenderer(c)
}
func (m *mapWidget) draw(w, h int) image.Image {
if m.w != w || m.h != h {
m.pixels = image.NewNRGBA(image.Rect(0, 0, w, h))
}
if m.GetBounds == nil {
return m.pixels
}
min, max := m.GetBounds()
//chunksX := int(max[0] - min[0] + 1) // how many chunk lengths is x coordinate
//chunksY := int(max[1] - min[1] + 1)
_ = max
for pos, tile := range m.GetTiles() {
px := image.Pt(
int((pos[0]-min[0])*16),
int((pos[1]-min[1])*16),
)
draw.Draw(m.pixels.(*image.NRGBA), image.Rect(
px.X, px.Y,
px.X+16, px.Y+16,
), tile, image.Pt(0, 0), draw.Src)
}
return m.pixels
}

View File

@ -1 +0,0 @@
package gui

15
ui/gui/uis.go Normal file
View File

@ -0,0 +1,15 @@
package gui
import (
"fyne.io/fyne/v2"
"github.com/bedrock-tool/bedrocktool/utils"
)
type HandlerFunc func(name string, data interface{}) utils.MessageResponse
type CommandUI interface {
Layout() fyne.CanvasObject
Handler() HandlerFunc
}
var CommandUIs = map[string]CommandUI{}

54
ui/gui/worlds.go Normal file
View File

@ -0,0 +1,54 @@
package gui
import (
"image"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"github.com/bedrock-tool/bedrocktool/utils"
"github.com/sandertv/gophertunnel/minecraft/protocol"
)
type worldsUI struct {
mapElement *mapWidget
}
func (w *worldsUI) Layout() fyne.CanvasObject {
w.mapElement = &mapWidget{}
return container.NewVBox(
widget.NewRichTextFromMarkdown("# worlds Ui!"),
w.mapElement,
)
}
func (w *worldsUI) handler(name string, data interface{}) utils.MessageResponse {
r := utils.MessageResponse{
Ok: false,
Data: nil,
}
switch name {
case "init_map":
init_map := data.(struct {
GetTiles func() map[protocol.ChunkPos]*image.RGBA
GetBounds func() (min, max protocol.ChunkPos)
})
w.mapElement.GetBounds = init_map.GetBounds
w.mapElement.GetTiles = init_map.GetTiles
r.Ok = true
case "update_map":
w.mapElement.Refresh()
r.Ok = true
}
return r
}
func (w *worldsUI) Handler() HandlerFunc {
return w.handler
}
func init() {
CommandUIs["worlds"] = &worldsUI{}
}

1
ui/stub.go Normal file
View File

@ -0,0 +1 @@
package ui

View File

@ -1,16 +1,42 @@
package utils package utils
import ( import (
"context"
"flag"
"github.com/google/subcommands" "github.com/google/subcommands"
"github.com/sirupsen/logrus"
) )
var ValidCMDs = make(map[string]Command, 0) var ValidCMDs = make(map[string]Command, 0)
type Command interface { type Command interface {
Name() string
Synopsis() string
SetFlags(f *flag.FlagSet)
Execute(ctx context.Context, ui UI) error
}
type cmdWrap struct {
subcommands.Command subcommands.Command
cmd Command
}
func (c *cmdWrap) Name() string { return c.cmd.Name() }
func (c *cmdWrap) Synopsis() string { return c.cmd.Synopsis() }
func (c *cmdWrap) SetFlags(f *flag.FlagSet) { c.cmd.SetFlags(f) }
func (c *cmdWrap) Usage() string { return c.Name() + ": " + c.Synopsis() }
func (c *cmdWrap) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
err := c.cmd.Execute(ctx, currentUI)
if err != nil {
logrus.Error(err)
return 1
}
return 0
} }
func RegisterCommand(sub Command) { func RegisterCommand(sub Command) {
subcommands.Register(sub, "") subcommands.Register(&cmdWrap{cmd: sub}, "")
ValidCMDs[sub.Name()] = sub ValidCMDs[sub.Name()] = sub
} }

View File

@ -84,6 +84,9 @@ func (d *DNSServer) handler(w dns.ResponseWriter, req *dns.Msg) {
} }
func InitDNS() { func InitDNS() {
if !Options.EnableDNS {
return
}
d := DNSServer{} d := DNSServer{}
dns.HandleFunc(".", d.handler) dns.HandleFunc(".", d.handler)

View File

@ -1,6 +1,7 @@
package utils package utils
import ( import (
"bufio"
"context" "context"
"flag" "flag"
"fmt" "fmt"
@ -9,25 +10,56 @@ import (
"github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/locale"
"github.com/google/subcommands" "github.com/google/subcommands"
"github.com/sirupsen/logrus"
) )
type UI interface { type MessageResponse struct {
Init() Ok bool
SetOptions(context.Context) bool Data interface{}
Execute(context.Context) error
} }
type InteractiveCLI struct { type UI interface {
Init() bool
Start(context.Context) error
Message(name string, data interface{}) MessageResponse
ServerInput(context.Context, string) (string, string, error)
}
type BaseUI struct {
UI UI
} }
func (c *InteractiveCLI) Init() { func (u *BaseUI) Message(name string, data interface{}) MessageResponse {
return MessageResponse{
Ok: false,
Data: nil,
}
} }
func (c *InteractiveCLI) SetOptions(ctx context.Context) bool { func (u *BaseUI) ServerInput(ctx context.Context, server string) (string, string, error) {
address, name, err := ServerInput(ctx, server)
return address, name, err
}
var currentUI UI
func SetCurrentUI(ui UI) {
currentUI = ui
}
type InteractiveCLI struct {
BaseUI
}
func (c *InteractiveCLI) Init() bool {
currentUI = c
return true
}
func (c *InteractiveCLI) Start(ctx context.Context) error {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return true return nil
default: default:
fmt.Println(locale.Loc("available_commands", nil)) fmt.Println(locale.Loc("available_commands", nil))
for name, cmd := range ValidCMDs { for name, cmd := range ValidCMDs {
@ -37,18 +69,23 @@ func (c *InteractiveCLI) SetOptions(ctx context.Context) bool {
cmd, cancelled := UserInput(ctx, locale.Loc("input_command", nil)) cmd, cancelled := UserInput(ctx, locale.Loc("input_command", nil))
if cancelled { if cancelled {
return true return nil
} }
_cmd := strings.Split(cmd, " ") _cmd := strings.Split(cmd, " ")
os.Args = append(os.Args, _cmd...) os.Args = append(os.Args, _cmd...)
} }
flag.Parse() flag.Parse()
return false
}
func (c *InteractiveCLI) Execute(ctx context.Context) error { InitDNS()
InitExtraDebug()
subcommands.Execute(ctx) subcommands.Execute(ctx)
if Options.IsInteractive {
logrus.Info(locale.Loc("enter_to_exit", nil))
input := bufio.NewScanner(os.Stdin)
input.Scan()
}
return nil return nil
} }

View File

@ -7,8 +7,6 @@ import (
"strings" "strings"
"github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/locale"
"github.com/google/subcommands"
"github.com/sirupsen/logrus"
) )
func getRealm(ctx context.Context, realmName, id string) (name string, address string, err error) { func getRealm(ctx context.Context, realmName, id string) (name string, address string, err error) {
@ -34,25 +32,18 @@ func getRealm(ctx context.Context, realmName, id string) (name string, address s
type RealmListCMD struct{} type RealmListCMD struct{}
func (*RealmListCMD) Name() string { return "list-realms" } func (*RealmListCMD) Name() string { return "list-realms" }
func (*RealmListCMD) Synopsis() string { return locale.Loc("list_realms_synopsis", nil) } func (*RealmListCMD) Synopsis() string { return locale.Loc("list_realms_synopsis", nil) }
func (c *RealmListCMD) SetFlags(f *flag.FlagSet) {} func (c *RealmListCMD) SetFlags(f *flag.FlagSet) {}
func (c *RealmListCMD) Execute(ctx context.Context, ui UI) error {
func (c *RealmListCMD) Usage() string {
return c.Name() + ": " + c.Synopsis() + "\n"
}
func (c *RealmListCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
realms, err := GetRealmsAPI().Realms(ctx) realms, err := GetRealmsAPI().Realms(ctx)
if err != nil { if err != nil {
logrus.Error(err) return err
return 1
} }
for _, realm := range realms { for _, realm := range realms {
fmt.Println(locale.Loc("realm_list_line", locale.Strmap{"Name": realm.Name, "Id": realm.ID})) fmt.Println(locale.Loc("realm_list_line", locale.Strmap{"Name": realm.Name, "Id": realm.ID}))
} }
return 0 return nil
} }
func init() { func init() {

View File

@ -8,6 +8,7 @@ import (
"crypto/sha256" "crypto/sha256"
"encoding/json" "encoding/json"
"errors" "errors"
"io"
"net" "net"
"os" "os"
"path" "path"
@ -16,6 +17,7 @@ import (
"sync" "sync"
"github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/locale"
"github.com/bedrock-tool/bedrocktool/utils/crypt"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -195,3 +197,40 @@ func CfbDecrypt(data []byte, key []byte) ([]byte, error) {
} }
return data, nil return data, nil
} }
func InitExtraDebug() {
if !Options.ExtraDebug {
return
}
Options.Debug = true
var logPlain, logCryptEnc io.WriteCloser = nil, nil
// open plain text log
logPlain, err := os.Create("packets.log")
if err != nil {
logrus.Error(err)
} else {
defer logPlain.Close()
}
// open gpg log
logCrypt, err := os.Create("packets.log.gpg")
if err != nil {
logrus.Error(err)
} else {
defer logCrypt.Close()
// encrypter for the log
logCryptEnc, err = crypt.Encer("packets.log", logCrypt)
if err != nil {
logrus.Error(err)
} else {
defer logCryptEnc.Close()
}
}
FLog = io.MultiWriter(logPlain, logCryptEnc)
if err != nil {
logrus.Error(err)
}
}