diff --git a/cmd/bedrocktool/main.go b/cmd/bedrocktool/main.go index 760b3d8..450e4f9 100644 --- a/cmd/bedrocktool/main.go +++ b/cmd/bedrocktool/main.go @@ -5,7 +5,6 @@ import ( "context" "flag" "fmt" - "io" "os" "os/signal" "runtime/debug" @@ -13,31 +12,30 @@ import ( "github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/utils" - "github.com/bedrock-tool/bedrocktool/utils/crypt" "gopkg.in/square/go-jose.v2/json" _ "github.com/bedrock-tool/bedrocktool/subcommands" _ "github.com/bedrock-tool/bedrocktool/subcommands/skins" _ "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/sirupsen/logrus" ) 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() - return false -} - -func (c *CLI) Execute(ctx context.Context) error { + utils.InitDNS() + utils.InitExtraDebug() subcommands.Execute(ctx) return nil } @@ -105,53 +103,6 @@ func main() { 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 sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) @@ -161,12 +112,13 @@ func main() { cancel() }() - ui.Execute(ctx) - - if utils.Options.IsInteractive { - logrus.Info(locale.Loc("enter_to_exit", nil)) - input := bufio.NewScanner(os.Stdin) - input.Scan() + if !ui.Init() { + logrus.Error("Failed to init UI!") + return + } + err = ui.Start(ctx) + if err != nil { + logrus.Error(err) } } @@ -176,16 +128,10 @@ type TransCMD struct { func (*TransCMD) Name() string { return "trans" } func (*TransCMD) Synopsis() string { return "" } - func (c *TransCMD) SetFlags(f *flag.FlagSet) { f.BoolVar(&c.auth, "auth", false, locale.Loc("should_login_xbox", nil)) } - -func (c *TransCMD) Usage() string { - return c.Name() + ": " + c.Synopsis() + "\n" -} - -func (c *TransCMD) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { +func (c *TransCMD) Execute(_ context.Context, ui utils.UI) error { const ( BlackFg = "\033[30m" Bold = "\033[1m" @@ -198,7 +144,7 @@ func (c *TransCMD) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) utils.GetTokenSource() } fmt.Println(BlackFg + Bold + Blue + " Trans " + Pink + " Rights " + White + " Are " + Pink + " Human " + Blue + " Rights " + Reset) - return 0 + return nil } type CreateCustomDataCMD struct { @@ -212,11 +158,7 @@ func (c *CreateCustomDataCMD) SetFlags(f *flag.FlagSet) { f.StringVar(&c.path, "path", "customdata.json", "where to save") } -func (c *CreateCustomDataCMD) Usage() string { - return c.Name() + ": " + c.Synopsis() + "\n" -} - -func (c *CreateCustomDataCMD) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { +func (c *CreateCustomDataCMD) Execute(_ context.Context, ui utils.UI) error { var data utils.CustomClientData fio, err := os.Create(c.path) if err == nil { @@ -226,10 +168,9 @@ func (c *CreateCustomDataCMD) Execute(_ context.Context, f *flag.FlagSet, _ ...i fio.Write(bdata) } if err != nil { - logrus.Error(err) - return 1 + return err } - return 0 + return nil } func init() { diff --git a/subcommands/capture.go b/subcommands/capture.go index 32096e4..f24be93 100644 --- a/subcommands/capture.go +++ b/subcommands/capture.go @@ -14,7 +14,6 @@ import ( "github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/utils" - "github.com/google/subcommands" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" "github.com/sirupsen/logrus" ) @@ -49,27 +48,20 @@ type CaptureCMD struct { func (*CaptureCMD) Name() string { return "capture" } func (*CaptureCMD) Synopsis() string { return locale.Loc("capture_synopsis", nil) } - func (c *CaptureCMD) SetFlags(f *flag.FlagSet) { f.StringVar(&c.ServerAddress, "address", "", "remote server address") } -func (c *CaptureCMD) Usage() string { - 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 { +func (c *CaptureCMD) Execute(ctx context.Context, ui utils.UI) error { address, hostname, err := utils.ServerInput(ctx, c.ServerAddress) if err != nil { - logrus.Fatal(err) - return 1 + return err } os.Mkdir("captures", 0o775) fio, err := os.Create("captures/" + hostname + "-" + time.Now().Format("2006-01-02_15-04-05") + ".pcap2") if err != nil { - logrus.Fatal(err) - return 1 + return err } defer fio.Close() utils.WriteReplayHeader(fio) @@ -90,8 +82,7 @@ func (c *CaptureCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interfac err = proxy.Run(ctx, address) time.Sleep(2 * time.Second) if err != nil { - logrus.Fatal(err) - return 1 + return err } - return 0 + return nil } diff --git a/subcommands/chat_log.go b/subcommands/chat_log.go index 1a284e6..5a234f1 100644 --- a/subcommands/chat_log.go +++ b/subcommands/chat_log.go @@ -10,7 +10,6 @@ import ( "github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/utils" - "github.com/google/subcommands" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" "github.com/sirupsen/logrus" ) @@ -22,33 +21,27 @@ type ChatLogCMD struct { func (*ChatLogCMD) Name() string { return "chat-log" } func (*ChatLogCMD) Synopsis() string { return locale.Loc("chat_log_synopsis", nil) } - func (c *ChatLogCMD) SetFlags(f *flag.FlagSet) { f.StringVar(&c.ServerAddress, "address", "", "remote server address") f.BoolVar(&c.Verbose, "v", false, "verbose") } -func (c *ChatLogCMD) Usage() string { - 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 { +func (c *ChatLogCMD) Execute(ctx context.Context, ui utils.UI) error { address, hostname, err := utils.ServerInput(ctx, c.ServerAddress) if err != nil { - logrus.Error(err) - return 1 + return err } filename := fmt.Sprintf("%s_%s_chat.log", hostname, time.Now().Format("2006-01-02_15-04-05_Z07")) f, err := os.Create(filename) if err != nil { - logrus.Fatal(err) + return err } defer f.Close() proxy, err := utils.NewProxy() if err != nil { - logrus.Fatal(err) + return err } proxy.PacketCB = func(pk packet.Packet, proxy *utils.ProxyContext, toServer bool, _ time.Time) (packet.Packet, error) { 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 } - if err := proxy.Run(ctx, address); err != nil { - logrus.Error(err) - return 1 - } - - return 0 + err = proxy.Run(ctx, address) + return err } func init() { diff --git a/subcommands/debug.go b/subcommands/debug.go index 63723cc..52dbb2d 100644 --- a/subcommands/debug.go +++ b/subcommands/debug.go @@ -7,9 +7,6 @@ import ( "github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/utils" - - "github.com/google/subcommands" - "github.com/sirupsen/logrus" ) type DebugProxyCMD struct { @@ -19,21 +16,15 @@ type DebugProxyCMD struct { func (*DebugProxyCMD) Name() string { return "debug-proxy" } func (*DebugProxyCMD) Synopsis() string { return locale.Loc("debug_proxy_synopsis", nil) } - func (c *DebugProxyCMD) SetFlags(f *flag.FlagSet) { f.StringVar(&c.ServerAddress, "address", "", locale.Loc("remote_address", nil)) f.StringVar(&c.Filter, "filter", "", locale.Loc("packet_filter", nil)) } -func (c *DebugProxyCMD) Usage() string { - 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 { +func (c *DebugProxyCMD) Execute(ctx context.Context, ui utils.UI) error { address, _, err := utils.ServerInput(ctx, c.ServerAddress) if err != nil { - logrus.Error(err) - return 1 + return err } utils.Options.Debug = true @@ -55,14 +46,10 @@ func (c *DebugProxyCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...inter proxy, err := utils.NewProxy() if err != nil { - logrus.Error(err) - return 1 + return err } - if err := proxy.Run(ctx, address); err != nil { - logrus.Error(err) - return 1 - } - return 0 + err = proxy.Run(ctx, address) + return err } func init() { diff --git a/subcommands/merge.go b/subcommands/merge.go index fb9bd44..cd432c1 100644 --- a/subcommands/merge.go +++ b/subcommands/merge.go @@ -1,3 +1,5 @@ +//go:build false + package subcommands import ( diff --git a/subcommands/resourcepack-d.go b/subcommands/resourcepack-d.go index 8b3f00d..2a91219 100644 --- a/subcommands/resourcepack-d.go +++ b/subcommands/resourcepack-d.go @@ -9,12 +9,8 @@ import ( "github.com/bedrock-tool/bedrocktool/locale" resourcepackd "github.com/bedrock-tool/bedrocktool/subcommands/resourcepack-d" "github.com/bedrock-tool/bedrocktool/utils" - "github.com/google/subcommands" - "github.com/sirupsen/logrus" ) -// decrypt using cfb with segmentsize = 1 - type ResourcePackCMD struct { ServerAddress string 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)) } -func (c *ResourcePackCMD) Usage() string { - return c.Name() + ": " + c.Synopsis() + "\n" + locale.Loc("server_address_help", nil) -} - -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 (c *ResourcePackCMD) Execute(ctx context.Context, ui utils.UI) error { + return resourcepackd.Execute_cmd(ctx, c.ServerAddress, c.OnlyKeys, c.SaveEncrypted) } func init() { diff --git a/subcommands/resourcepack-stub.go b/subcommands/resourcepack-stub.go index 893b6a8..a54566d 100644 --- a/subcommands/resourcepack-stub.go +++ b/subcommands/resourcepack-stub.go @@ -4,11 +4,10 @@ package subcommands import ( "context" + "errors" "flag" "github.com/bedrock-tool/bedrocktool/utils" - "github.com/google/subcommands" - "github.com/sirupsen/logrus" ) type ResourcePackCMD struct { @@ -17,18 +16,11 @@ type ResourcePackCMD struct { OnlyKeys bool } -func (*ResourcePackCMD) Name() string { return "packs" } -func (*ResourcePackCMD) Synopsis() string { return "NOT COMPILED" } - -func (c *ResourcePackCMD) SetFlags(f *flag.FlagSet) {} - -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 (*ResourcePackCMD) Name() string { return "packs" } +func (*ResourcePackCMD) Synopsis() string { return "NOT COMPILED" } +func (*ResourcePackCMD) SetFlags(f *flag.FlagSet) {} +func (*ResourcePackCMD) Execute(ctx context.Context, ui utils.UI) error { + return errors.New("not compiled") } func init() { diff --git a/subcommands/skins/skins.go b/subcommands/skins/skins.go index 4d2cb78..2ef9ba8 100644 --- a/subcommands/skins/skins.go +++ b/subcommands/skins/skins.go @@ -12,7 +12,6 @@ import ( "github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/utils" - "github.com/google/subcommands" "github.com/google/uuid" "github.com/sandertv/gophertunnel/minecraft/protocol" "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") } -func (c *SkinCMD) Usage() string { - 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 { +func (c *SkinCMD) Execute(ctx context.Context, ui utils.UI) error { address, hostname, err := utils.ServerInput(ctx, c.ServerAddress) if err != nil { - logrus.Error(err) - return 1 + return err } proxy, _ := utils.NewProxy() @@ -158,10 +152,7 @@ func (c *SkinCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{} } err = proxy.Run(ctx, address) - if err != nil { - logrus.Error(err) - } - return 0 + return err } func init() { diff --git a/subcommands/update.go b/subcommands/update.go index 1af4d3b..314e595 100644 --- a/subcommands/update.go +++ b/subcommands/update.go @@ -8,40 +8,31 @@ import ( "github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/utils" "github.com/sirupsen/logrus" - - "github.com/google/subcommands" ) type UpdateCMD struct{} -func (*UpdateCMD) Name() string { return "update" } -func (*UpdateCMD) Synopsis() string { return locale.Loc("update_synopsis", nil) } - +func (*UpdateCMD) Name() string { return "update" } +func (*UpdateCMD) Synopsis() string { return locale.Loc("update_synopsis", nil) } func (c *UpdateCMD) SetFlags(f *flag.FlagSet) {} -func (c *UpdateCMD) Usage() string { - return c.Name() + ": " + c.Synopsis() + "\n" -} - -func (c *UpdateCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { +func (c *UpdateCMD) Execute(ctx context.Context, ui utils.UI) error { newVersion, err := utils.Updater.UpdateAvailable() if err != nil { - logrus.Error(err) - return 1 + return err } if newVersion == "" { logrus.Info(locale.Loc("no_update", nil)) - return 0 + return nil } logrus.Infof(locale.Loc("updating", locale.Strmap{"Version": newVersion})) if err := utils.Updater.Update(); err != nil { - logrus.Error(err) - return 1 + return err } logrus.Infof(locale.Loc("updated", nil)) - return 0 + return nil } func init() { diff --git a/subcommands/world/chunk.go b/subcommands/world/chunk.go index 10f76cf..204e637 100644 --- a/subcommands/world/chunk.go +++ b/subcommands/world/chunk.go @@ -60,9 +60,9 @@ func (w *WorldState) processLevelChunk(pk *packet.LevelChunk) { w.chunks[pk.Position] = ch if pk.SubChunkRequestMode == protocol.SubChunkRequestModeLegacy { - w.ui.SetChunk(pk.Position, ch) + w.mapUI.SetChunk(pk.Position, ch) } else { - w.ui.SetChunk(pk.Position, nil) + w.mapUI.SetChunk(pk.Position, nil) // request all the subchunks max := w.Dim.Range().Height() / 16 @@ -109,9 +109,9 @@ func (w *WorldState) processSubChunk(pk *packet.SubChunk) { // redraw the chunks 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 { diff --git a/subcommands/world/map_item.go b/subcommands/world/map_item.go index 3fb0ae7..cf8d444 100644 --- a/subcommands/world/map_item.go +++ b/subcommands/world/map_item.go @@ -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 i := 0 for _ch := range m.renderedChunks { @@ -75,6 +75,7 @@ type MapUI struct { renderQueue *lockfree.Queue renderedChunks map[protocol.ChunkPos]*image.RGBA // prerendered chunks needRedraw bool // when the map has updated this is true + showOnGui bool ticker *time.Ticker w *WorldState @@ -93,6 +94,21 @@ func NewMapUI(w *WorldState) *MapUI { } 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 if m.w.proxy.Client != nil { if err := m.w.proxy.Client.WritePacket(&packet.ClientBoundMapItemData{ @@ -224,11 +240,15 @@ func (m *MapUI) Redraw() { bmp.Encode(buf, img2) os.WriteFile("test.bmp", buf.Bytes(), 0o777) } + + if m.showOnGui { + m.w.gui.Message("update_map", nil) + } } func (m *MapUI) ToImage() *image.RGBA { // 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 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) { if pk.ActionType == packet.AnimateActionSwingArm { - w.ui.ChangeZoom() - w.proxy.SendPopup(locale.Loc("zoom_level", locale.Strmap{"Level": w.ui.zoomLevel})) + w.mapUI.ChangeZoom() + 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) case *packet.MapInfoRequest: if pk.MapID == ViewMapID { - w.ui.SchedRedraw() + w.mapUI.SchedRedraw() *forward = false } case *packet.Animate: diff --git a/subcommands/world/world.go b/subcommands/world/world.go index 9ab1a96..708bd48 100644 --- a/subcommands/world/world.go +++ b/subcommands/world/world.go @@ -1,4 +1,3 @@ -// Package world Bedrock World Downloader package world import ( @@ -26,7 +25,6 @@ import ( "github.com/df-mc/dragonfly/server/world/mcdb" "github.com/df-mc/goleveldb/leveldb/opt" "github.com/go-gl/mathgl/mgl32" - "github.com/google/subcommands" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" "github.com/sirupsen/logrus" @@ -44,7 +42,8 @@ type TPlayerPos struct { type WorldState struct { ctx context.Context proxy *utils.ProxyContext - ui *MapUI + mapUI *MapUI + gui utils.UI bp *behaviourpack.BehaviourPack // save state @@ -66,11 +65,12 @@ type WorldState struct { 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{ ctx: ctx, proxy: proxy, - ui: nil, + mapUI: nil, + gui: ui, bp: behaviourpack.New(ServerName), ServerName: ServerName, @@ -82,7 +82,7 @@ func NewWorldState(ctx context.Context, proxy *utils.ProxyContext, ServerName st WorldName: "world", PlayerPos: TPlayerPos{}, } - w.ui = NewMapUI(w) + w.mapUI = NewMapUI(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)) } -func (c *WorldCMD) Usage() string { - return c.Name() + ": " + c.Synopsis() + "\n" + locale.Loc("server_address_help", nil) -} - -func (c *WorldCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { - serverAddress, hostname, err := utils.ServerInput(ctx, c.ServerAddress) +func (c *WorldCMD) Execute(ctx context.Context, ui utils.UI) error { + serverAddress, hostname, err := ui.ServerInput(ctx, c.ServerAddress) if err != nil { - logrus.Error(err) - return 1 + return err } proxy, err := utils.NewProxy() if err != nil { - logrus.Error(err) - return 1 + return err } - w := NewWorldState(ctx, proxy, hostname) + w := NewWorldState(ctx, proxy, hostname, ui) w.voidGen = c.EnableVoid w.withPacks = c.Packs 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) if err != nil { - logrus.Error(err) - } else { - w.SaveAndReset() + return err } - return 0 + w.SaveAndReset() + return nil } 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()) { - w.ui.SchedRedraw() + w.mapUI.SchedRedraw() } } func (w *WorldState) Reset() { w.chunks = make(map[protocol.ChunkPos]*chunk.Chunk) 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 @@ -421,7 +414,7 @@ func (w *WorldState) SaveAndReset() { if w.saveImage { f, _ := os.Create(folder + ".png") - png.Encode(f, w.ui.ToImage()) + png.Encode(f, w.mapUI.ToImage()) 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.ui.Start() + w.mapUI.Start() proxy.AddCommand(utils.IngameCommand{ Exec: func(cmdline []string) bool { diff --git a/ui/gui/gui.go b/ui/gui.go similarity index 80% rename from ui/gui/gui.go rename to ui/gui.go index 6dc3ad2..2a897b9 100644 --- a/ui/gui/gui.go +++ b/ui/gui.go @@ -1,9 +1,10 @@ -//go:build gui || android || true +//go:build gui || android -package gui +package ui import ( "context" + "sync" "fyne.io/fyne/v2/app" "fyne.io/fyne/v2/container" @@ -12,6 +13,7 @@ import ( "github.com/bedrock-tool/bedrocktool/subcommands" "github.com/bedrock-tool/bedrocktool/subcommands/skins" "github.com/bedrock-tool/bedrocktool/subcommands/world" + "github.com/bedrock-tool/bedrocktool/ui/gui" "github.com/bedrock-tool/bedrocktool/utils" ) @@ -86,12 +88,16 @@ var settings = map[string]func(utils.Command) *widget.Form{ } 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() 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() - - var quit = true + start_button := widget.NewButton("Start", nil) + l := sync.Mutex{} w.SetContent(container.NewVBox( widget.NewRichTextFromMarkdown("## Settings"), @@ -138,46 +143,58 @@ func (g *GUI) SetOptions(ctx context.Context) bool { ), widget.NewRichTextFromMarkdown("# Commands"), widget.NewSelect(entries, func(s string) { - g.selected.Set(s) - quit = false + l.Lock() + selected.Set(s) + forms_box.RemoveAll() + forms_box.Add(forms[s]) + l.Unlock() }), forms_box, - widget.NewButton("Start", func() { - w.Close() - }), + start_button, )) - for _, f := range forms { - forms_box.Add(f) - } - g.selected.AddListener(binding.NewDataListener(func() { - v, _ := g.selected.Get() - for n, f := range forms { - if n == v { - f.Show() - } else { - f.Hide() - } + start_button.OnTapped = func() { + sub, _ := selected.Get() + cmd := utils.ValidCMDs[sub] + + u := gui.CommandUIs[sub] + if u != nil { + g.commandUI = u + w.SetContent(u.Layout()) } - })) + + utils.InitDNS() + utils.InitExtraDebug() + + go cmd.Execute(ctx, g) + } 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 } +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() { utils.MakeGui = func() utils.UI { return &GUI{} diff --git a/ui/gui/map_widget.go b/ui/gui/map_widget.go new file mode 100644 index 0000000..e2d4b0f --- /dev/null +++ b/ui/gui/map_widget.go @@ -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 +} diff --git a/ui/gui/stub.go b/ui/gui/stub.go deleted file mode 100644 index 194757a..0000000 --- a/ui/gui/stub.go +++ /dev/null @@ -1 +0,0 @@ -package gui diff --git a/ui/gui/uis.go b/ui/gui/uis.go new file mode 100644 index 0000000..7f3eea4 --- /dev/null +++ b/ui/gui/uis.go @@ -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{} diff --git a/ui/gui/worlds.go b/ui/gui/worlds.go new file mode 100644 index 0000000..774f8df --- /dev/null +++ b/ui/gui/worlds.go @@ -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{} +} diff --git a/ui/stub.go b/ui/stub.go new file mode 100644 index 0000000..5b1faa2 --- /dev/null +++ b/ui/stub.go @@ -0,0 +1 @@ +package ui diff --git a/utils/command_register.go b/utils/command_register.go index 6688b41..6b1da03 100644 --- a/utils/command_register.go +++ b/utils/command_register.go @@ -1,16 +1,42 @@ package utils import ( + "context" + "flag" + "github.com/google/subcommands" + "github.com/sirupsen/logrus" ) var ValidCMDs = make(map[string]Command, 0) type Command interface { + Name() string + Synopsis() string + SetFlags(f *flag.FlagSet) + Execute(ctx context.Context, ui UI) error +} + +type cmdWrap struct { 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) { - subcommands.Register(sub, "") + subcommands.Register(&cmdWrap{cmd: sub}, "") ValidCMDs[sub.Name()] = sub } diff --git a/utils/dns.go b/utils/dns.go index fb20bb0..536146e 100644 --- a/utils/dns.go +++ b/utils/dns.go @@ -84,6 +84,9 @@ func (d *DNSServer) handler(w dns.ResponseWriter, req *dns.Msg) { } func InitDNS() { + if !Options.EnableDNS { + return + } d := DNSServer{} dns.HandleFunc(".", d.handler) diff --git a/utils/iui.go b/utils/iui.go index a0b7fa2..765ad56 100644 --- a/utils/iui.go +++ b/utils/iui.go @@ -1,6 +1,7 @@ package utils import ( + "bufio" "context" "flag" "fmt" @@ -9,25 +10,56 @@ import ( "github.com/bedrock-tool/bedrocktool/locale" "github.com/google/subcommands" + "github.com/sirupsen/logrus" ) -type UI interface { - Init() - SetOptions(context.Context) bool - Execute(context.Context) error +type MessageResponse struct { + Ok bool + Data interface{} } -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 } -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 { case <-ctx.Done(): - return true + return nil default: fmt.Println(locale.Loc("available_commands", nil)) 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)) if cancelled { - return true + return nil } _cmd := strings.Split(cmd, " ") os.Args = append(os.Args, _cmd...) } - flag.Parse() - return false -} -func (c *InteractiveCLI) Execute(ctx context.Context) error { + InitDNS() + InitExtraDebug() + subcommands.Execute(ctx) + + if Options.IsInteractive { + logrus.Info(locale.Loc("enter_to_exit", nil)) + input := bufio.NewScanner(os.Stdin) + input.Scan() + } return nil } diff --git a/utils/realms.go b/utils/realms.go index 6021899..12230ab 100644 --- a/utils/realms.go +++ b/utils/realms.go @@ -7,8 +7,6 @@ import ( "strings" "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) { @@ -34,25 +32,18 @@ func getRealm(ctx context.Context, realmName, id string) (name string, address s type RealmListCMD struct{} -func (*RealmListCMD) Name() string { return "list-realms" } -func (*RealmListCMD) Synopsis() string { return locale.Loc("list_realms_synopsis", nil) } - +func (*RealmListCMD) Name() string { return "list-realms" } +func (*RealmListCMD) Synopsis() string { return locale.Loc("list_realms_synopsis", nil) } func (c *RealmListCMD) SetFlags(f *flag.FlagSet) {} - -func (c *RealmListCMD) Usage() string { - return c.Name() + ": " + c.Synopsis() + "\n" -} - -func (c *RealmListCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { +func (c *RealmListCMD) Execute(ctx context.Context, ui UI) error { realms, err := GetRealmsAPI().Realms(ctx) if err != nil { - logrus.Error(err) - return 1 + return err } for _, realm := range realms { fmt.Println(locale.Loc("realm_list_line", locale.Strmap{"Name": realm.Name, "Id": realm.ID})) } - return 0 + return nil } func init() { diff --git a/utils/utils.go b/utils/utils.go index e32aac1..a54ab7b 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -8,6 +8,7 @@ import ( "crypto/sha256" "encoding/json" "errors" + "io" "net" "os" "path" @@ -16,6 +17,7 @@ import ( "sync" "github.com/bedrock-tool/bedrocktool/locale" + "github.com/bedrock-tool/bedrocktool/utils/crypt" "github.com/google/uuid" "github.com/sandertv/gophertunnel/minecraft" "github.com/sirupsen/logrus" @@ -195,3 +197,40 @@ func CfbDecrypt(data []byte, key []byte) ([]byte, error) { } 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) + } +}