diff --git a/subcommands/world/items.go b/subcommands/world/items.go index da3f8ab..3f877d9 100644 --- a/subcommands/world/items.go +++ b/subcommands/world/items.go @@ -18,10 +18,6 @@ type itemContainer struct { } func (w *WorldState) processItemPacketsServer(pk packet.Packet) packet.Packet { - if !w.experimentInventory { - return pk - } - switch pk := pk.(type) { case *packet.ContainerOpen: // add to open containers @@ -109,6 +105,10 @@ func (w *WorldState) processItemPacketsServer(pk packet.Packet) packet.Packet { case *packet.ItemComponent: w.bp.ApplyComponentEntries(pk.Items) + case *packet.MobArmourEquipment: + if pk.EntityRuntimeID == w.proxy.Server.GameData().EntityRuntimeID { + + } } return pk } @@ -184,18 +184,198 @@ func stackToItem(it protocol.ItemStack) item.Stack { return nbtconv.ReadItem(it.NBTData, &s) } -func playerData(items []protocol.ItemInstance) (ret map[string]any) { - ret = map[string]any{} +func (w *WorldState) playerData() (ret map[string]any) { + ret = map[string]any{ + "format_version": "1.12.0", + "identifier": "minecraft:player", + } - if len(items) > 0 { - inv := inventory.New(len(items), nil) - for i, ii := range items { + if len(w.playerInventory) > 0 { + inv := inventory.New(len(w.playerInventory), nil) + for i, ii := range w.playerInventory { inv.SetItem(i, stackToItem(ii.Stack)) } ret["Inventory"] = nbtconv.InvToNBT(inv) } - ret["format_version"] = "1.12.0" + ret["abilities"] = map[string]any{ + "doorsandswitches": true, + "op": true, + "opencontainers": true, + "teleport": true, + "attackmobs": true, + "instabuild": true, + "permissionsLevel": int32(3), + "flying": false, + "lightning": false, + "playerPermissionsLevel": int32(2), + "attackplayers": true, + "build": true, + "flySpeed": float32(0.05), + "invulnerable": true, + "mayfly": true, + "mine": true, + "walkSpeed": float32(0.1), + } + + type attribute struct { + Name string + Base float32 + Current float32 + DefaultMax float32 + DefaultMin float32 + Max float32 + Min float32 + } + + ret["Attributes"] = []attribute{ + { + Base: 0, + Current: 0, + DefaultMax: 1024, + DefaultMin: -1024, + Max: 1024, + Min: -1024, + Name: "minecraft:luck", + }, + { + Base: 20, + Current: 20, + DefaultMax: 20, + DefaultMin: 0, + Max: 20, + Min: 0, + Name: "minecraft:health", + }, + { + Base: 0, + Current: 0, + DefaultMax: 16, + DefaultMin: 0, + Max: 16, + Min: 0, + Name: "minecraft:absorption", + }, + { + Base: 0, + Current: 0, + DefaultMax: 1, + DefaultMin: 0, + Max: 1, + Min: 0, + Name: "minecraft:knockback_resistance", + }, + { + Base: 0.1, + Current: 0.1, + DefaultMax: 3.4028235e+38, + DefaultMin: 0, + Max: 3.4028235e+38, + Min: 0, + Name: "minecraft:movement", + }, + { + Base: 0.02, + Current: 0.02, + DefaultMax: 3.4028235e+38, + DefaultMin: 0, + Max: 3.4028235e+38, + Min: 0, + Name: "minecraft:underwater_movement", + }, + { + Base: 0.02, + Current: 0.02, + DefaultMax: 3.4028235e+38, + DefaultMin: 0, + Max: 3.4028235e+38, + Min: 0, + Name: "minecraft:lava_movement", + }, + { + Base: 16, + Current: 16, + DefaultMax: 2048, + DefaultMin: 0, + Max: 2048, + Min: 0, + Name: "minecraft:follow_range", + }, + { + Base: 1, + Current: 1, + DefaultMax: 1, + DefaultMin: 1, + Max: 1, + Min: 1, + Name: "minecraft:attack_damage", + }, + { + Base: 20, + Current: 20, + DefaultMax: 20, + DefaultMin: 0, + Max: 20, + Min: 0, + Name: "minecraft:player.hunger", + }, + { + Base: 0, + Current: 0, + DefaultMax: 20, + DefaultMin: 0, + Max: 20, + Min: 0, + Name: "minecraft:player.exhaustion", + }, + { + Base: 5, + Current: 5, + DefaultMax: 20, + DefaultMin: 0, + Max: 20, + Min: 0, + Name: "minecraft:player.saturation", + }, + { + Base: 0, + Current: 0, + DefaultMax: 24791, + DefaultMin: 0, + Max: 24791, + Min: 0, + Name: "minecraft:player.level", + }, + { + Base: 0, + Current: 0, + DefaultMax: 1, + DefaultMin: 0, + Max: 1, + Min: 0, + Name: "minecraft:player.experience", + }, + } + + ret["Tags"] = []string{} + ret["OnGround"] = true + + spawn := w.proxy.Server.GameData().PlayerPosition + + ret["SpawnX"] = int32(spawn.X()) + ret["SpawnY"] = int32(spawn.Y()) + ret["SpawnZ"] = int32(spawn.Z()) + + ret["Pos"] = []float32{ + float32(spawn.X()), + float32(spawn.Y()), + float32(spawn.Z()), + } + + ret["Rotation"] = []float32{ + w.PlayerPos.Pitch, + w.PlayerPos.Yaw, + } return } diff --git a/subcommands/world/world.go b/subcommands/world/world.go index 824e189..9623481 100644 --- a/subcommands/world/world.go +++ b/subcommands/world/world.go @@ -192,6 +192,7 @@ func (c *WorldCMD) Execute(ctx context.Context, ui utils.UI) error { return err } w.SaveAndReset() + ui.Message(messages.SetUIState, messages.UIStateFinished) return nil } @@ -292,6 +293,10 @@ func (w *WorldState) SaveAndReset() { } logrus.Infof(locale.Loc("saving_world", locale.Strmap{"Name": w.WorldName, "Count": len(w.chunks)})) + w.gui.Message(messages.SavingWorld, messages.SavingWorldPayload{ + Name: w.WorldName, + Chunks: len(w.chunks), + }) // open world folder := path.Join("worlds", fmt.Sprintf("%s/%s", w.ServerName, w.WorldName)) @@ -335,21 +340,17 @@ func (w *WorldState) SaveAndReset() { } } - /* - err = provider.SaveLocalPlayerData(playerData(w.playerInventory)) - if err != nil { - logrus.Error(err) - } - */ + playerPos := w.proxy.Server.GameData().PlayerPosition + spawnPos := cube.Pos{int(playerPos.X()), int(playerPos.Y()), int(playerPos.Z())} + + err = provider.SaveLocalPlayerData(w.playerData()) + if err != nil { + logrus.Error(err) + } // write metadata s := provider.Settings() - player := w.proxy.Server.GameData().PlayerPosition - s.Spawn = cube.Pos{ - int(player.X()), - int(player.Y()), - int(player.Z()), - } + s.Spawn = spawnPos s.Name = w.WorldName // set gamerules @@ -536,6 +537,7 @@ func (w *WorldState) SaveAndReset() { logrus.Info(locale.Loc("saved", locale.Strmap{"Name": filename})) //os.RemoveAll(folder) w.Reset() + w.gui.Message(messages.SetUIState, messages.UIStateMain) } func (w *WorldState) OnConnect(err error) bool { diff --git a/ui/gui/pages/skins/skins.go b/ui/gui/pages/skins/skins.go index 41e34a4..da24edd 100644 --- a/ui/gui/pages/skins/skins.go +++ b/ui/gui/pages/skins/skins.go @@ -118,9 +118,9 @@ func (p *Page) handler(name string, data interface{}) messages.MessageResponse { p.l.Lock() new_skin := data.(messages.NewSkinPayload) p.Skins = append(p.Skins, new_skin) - r.Ok = true p.l.Unlock() p.Router.Invalidate() + r.Ok = true } return r } diff --git a/ui/gui/pages/worlds/worlds.go b/ui/gui/pages/worlds/worlds.go index 9be2e57..46933f1 100644 --- a/ui/gui/pages/worlds/worlds.go +++ b/ui/gui/pages/worlds/worlds.go @@ -1,8 +1,13 @@ package worlds import ( + "fmt" + "image" + "sync" + "gioui.org/layout" "gioui.org/unit" + "gioui.org/widget" "gioui.org/widget/material" "gioui.org/x/component" "github.com/bedrock-tool/bedrocktool/ui/gui" @@ -23,12 +28,21 @@ type Page struct { chunkCount int voidGen bool worldName string + + worldsList widget.List + worlds []messages.SavingWorldPayload + l sync.Mutex } func New(router *pages.Router) *Page { return &Page{ Router: router, worldMap: &Map{}, + worldsList: widget.List{ + List: layout.List{ + Axis: layout.Vertical, + }, + }, } } @@ -69,14 +83,36 @@ func (p *Page) Layout(gtx C, th *material.Theme) D { return layout.Flex{ Axis: layout.Vertical, }.Layout(gtx, - layout.Rigid(material.Label(th, 20, "World Downloader Basic UI").Layout), layout.Flexed(1, func(gtx C) D { return layout.Center.Layout(gtx, p.worldMap.Layout) }), ) + case messages.UIStateFinished: + return layout.Flex{Axis: layout.Vertical}.Layout(gtx, + layout.Rigid(func(gtx C) D { + return layout.UniformInset(20). + Layout(gtx, material.Label(th, 20, "Worlds Saved").Layout) + }), + layout.Flexed(1, func(gtx C) D { + p.l.Lock() + defer p.l.Unlock() + return material.List(th, &p.worldsList).Layout(gtx, len(p.worlds), func(gtx C, index int) D { + entry := p.worlds[len(p.worlds)-index-1] + return layout.UniformInset(25).Layout(gtx, func(gtx C) D { + return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, + layout.Rigid(material.Label(th, th.TextSize, entry.Name).Layout), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return layout.Dimensions{Size: image.Pt(20, 20)} + }), + layout.Rigid(material.Label(th, th.TextSize, fmt.Sprintf("%d chunks", entry.Chunks)).Layout), + ) + }) + }) + }), + ) } - return layout.Flex{}.Layout(gtx) + return layout.Dimensions{} } func (u *Page) handler(name string, data interface{}) messages.MessageResponse { @@ -116,6 +152,13 @@ func (u *Page) handler(name string, data interface{}) messages.MessageResponse { u.Router.Invalidate() r.Ok = true + case messages.SavingWorld: + u.l.Lock() + saving_world := data.(messages.SavingWorldPayload) + u.worlds = append(u.worlds, saving_world) + u.l.Unlock() + u.Router.Invalidate() + r.Ok = true } return r } diff --git a/ui/messages/messages.go b/ui/messages/messages.go index 928aaab..ae0fd65 100644 --- a/ui/messages/messages.go +++ b/ui/messages/messages.go @@ -17,6 +17,7 @@ const ( UIStateConnect = iota UIStateConnecting UIStateMain + UIStateFinished ) type HandlerFunc = func(name string, data interface{}) MessageResponse @@ -72,3 +73,10 @@ type NewSkinPayload struct { PlayerName string Skin *protocol.Skin } + +var SavingWorld = "saving_world" + +type SavingWorldPayload struct { + Name string + Chunks int +} diff --git a/utils/packet_logger.go b/utils/packet_logger.go index 9ce63f0..1b8241b 100644 --- a/utils/packet_logger.go +++ b/utils/packet_logger.go @@ -114,9 +114,9 @@ func dmpStruct(level int, inputStruct any, withType bool, isInList bool) (s stri if ii.Len() > 1000 { s += "[]" } else if ii.Len() == 0 { - s += typeString + "[]" + s += fmt.Sprintf("[0%s", typeName[1:]) } else { - s += typeString + "[" + s += fmt.Sprintf("[%d%s{", ii.Len(), typeString[1:]) if is_elem_struct { s += "\n" }