Compare commits

...

6 Commits

Author SHA1 Message Date
olebeck e13856a4ff show world download list 2023-04-30 23:04:41 +02:00
olebeck 2761cf094c fix block entities 2023-04-30 19:35:18 +02:00
olebeck 0eb3d09046 1.19.80 2023-04-29 20:57:22 +02:00
olebeck 4968181f0e add buttons to packs 2023-04-29 18:17:24 +02:00
olebeck bbf7cc1d75 update dragonfly 2023-04-29 17:22:24 +02:00
olebeck b867221a66 add progress 2023-04-29 17:12:13 +02:00
14 changed files with 354 additions and 188 deletions

6
go.mod
View File

@ -3,10 +3,10 @@ module github.com/bedrock-tool/bedrocktool
go 1.20
//replace github.com/sandertv/gophertunnel => ./gophertunnel
replace github.com/sandertv/gophertunnel => github.com/olebeck/gophertunnel v1.28.2-1
replace github.com/sandertv/gophertunnel => github.com/olebeck/gophertunnel v1.29.0-1
//replace github.com/df-mc/dragonfly => ./dragonfly
replace github.com/df-mc/dragonfly => github.com/olebeck/dragonfly v0.9.4-11
replace github.com/df-mc/dragonfly => github.com/olebeck/dragonfly v0.9.4-13
//replace gioui.org => ./gio
replace gioui.org => github.com/olebeck/gio v0.0.0-20230427194143-c9c9d8bc704d
@ -28,7 +28,7 @@ require (
github.com/repeale/fp-go v0.11.1
github.com/sanbornm/go-selfupdate v0.0.0-20210106163404-c9b625feac49
github.com/sandertv/go-raknet v1.12.0
github.com/sandertv/gophertunnel v1.28.2
github.com/sandertv/gophertunnel v1.29.0
github.com/shirou/gopsutil/v3 v3.23.3
github.com/sirupsen/logrus v1.9.0
golang.design/x/lockfree v0.0.1

8
go.sum
View File

@ -85,12 +85,12 @@ github.com/muhammadmuzzammil1998/jsonc v1.0.0 h1:8o5gBQn4ZA3NBA9DlTujCj2a4w0tqWr
github.com/muhammadmuzzammil1998/jsonc v1.0.0/go.mod h1:saF2fIVw4banK0H4+/EuqfFLpRnoy5S+ECwTOCcRcSU=
github.com/nicksnyder/go-i18n/v2 v2.2.1 h1:aOzRCdwsJuoExfZhoiXHy4bjruwCMdt5otbYojM/PaA=
github.com/nicksnyder/go-i18n/v2 v2.2.1/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0=
github.com/olebeck/dragonfly v0.9.4-11 h1:FkDKWUT1Kz6WRRxCLmCcFsZiocu9/htkyyEfTgKaVio=
github.com/olebeck/dragonfly v0.9.4-11/go.mod h1:1XIP+EcgRnxAEkFEbqZE3T4vXyMufzdpnigwczMBbyo=
github.com/olebeck/dragonfly v0.9.4-13 h1:JF72hfG3/BBCXU1GSBaEKKXQy/gt+0mEOua3RdKXdJ8=
github.com/olebeck/dragonfly v0.9.4-13/go.mod h1:ZNcbAATEeTNyN3Cumtwzox7STtFve469HHzL5c1K3nY=
github.com/olebeck/gio v0.0.0-20230427194143-c9c9d8bc704d h1:D+Ryca52xv37/p0FsEWfGwAGUZ1vPWpvimA2eMfBijc=
github.com/olebeck/gio v0.0.0-20230427194143-c9c9d8bc704d/go.mod h1:8CFQM/4LurRd9G3NUYdacFb9j2pK0LrAyVO2mAZo4mw=
github.com/olebeck/gophertunnel v1.28.2-1 h1:+3LaX37suEjMUnUTUKJMetoBqSg5PUkUBJc5dW02WI4=
github.com/olebeck/gophertunnel v1.28.2-1/go.mod h1:HxQfl/8mZzvjzhekEH8RO6xLAgan9i/wIyrQzw0tIPY=
github.com/olebeck/gophertunnel v1.29.0-1 h1:3x2cZoe8O54xVFgEZqTBJpFEXlzbjlLFoo/d9cWGv+g=
github.com/olebeck/gophertunnel v1.29.0-1/go.mod h1:HxQfl/8mZzvjzhekEH8RO6xLAgan9i/wIyrQzw0tIPY=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=

View File

@ -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"))

View File

@ -5,7 +5,6 @@ import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/chunk"
"github.com/google/uuid"
)
@ -38,27 +37,12 @@ func (p *provider) SavePlayerSpawnPosition(uuid uuid.UUID, pos cube.Pos) error {
return nil
}
func (p *provider) LoadChunk(position world.ChunkPos, dim world.Dimension) (c *chunk.Chunk, exists bool, err error) {
c, ok := p.s.chunks[position]
return c, ok, nil
func (p *provider) LoadColumn(pos world.ChunkPos, dim world.Dimension) (*world.Column, error) {
return &world.Column{
Chunk: p.s.chunks[pos],
}, nil
}
func (p *provider) SaveChunk(position world.ChunkPos, c *chunk.Chunk, dim world.Dimension) error {
return nil
}
func (p *provider) LoadEntities(position world.ChunkPos, dim world.Dimension, reg world.EntityRegistry) ([]world.Entity, error) {
return nil, nil
}
func (p *provider) SaveEntities(position world.ChunkPos, entities []world.Entity, dim world.Dimension) error {
return nil
}
func (p *provider) LoadBlockNBT(position world.ChunkPos, dim world.Dimension) ([]map[string]any, error) {
return nil, nil
}
func (p *provider) SaveBlockNBT(position world.ChunkPos, data []map[string]any, dim world.Dimension) error {
func (p *provider) StoreColumn(pos world.ChunkPos, dim world.Dimension, col *world.Column) error {
return nil
}

View File

@ -2,6 +2,7 @@ package worlds
import (
"github.com/bedrock-tool/bedrocktool/locale"
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/chunk"
"github.com/repeale/fp-go"
@ -42,13 +43,13 @@ func (w *worldsHandler) processLevelChunk(pk *packet.LevelChunk) {
return
}
for _, blockNBT := range blockNBTs {
x := blockNBT["x"].(int32)
y := blockNBT["y"].(int32)
z := blockNBT["z"].(int32)
w.worldState.blockNBTs[protocol.BlockPos{x, y, z}] = blockNBT
x := int(blockNBT["x"].(int32))
y := int(blockNBT["y"].(int32))
z := int(blockNBT["z"].(int32))
w.worldState.blockNBTs[cube.Pos{x, y, z}] = blockNBT
}
w.worldState.chunks[pk.Position] = ch
w.worldState.chunks[(world.ChunkPos)(pk.Position)] = ch
max := w.worldState.dimension.Range().Height() / 16
switch pk.SubChunkCount {
@ -76,20 +77,20 @@ func (w *worldsHandler) processLevelChunk(pk *packet.LevelChunk) {
return sub.Empty()
})(ch.Sub())
if !empty {
w.mapUI.SetChunk(pk.Position, ch, true)
w.mapUI.SetChunk((world.ChunkPos)(pk.Position), ch, true)
}
}
}
func (w *worldsHandler) processSubChunk(pk *packet.SubChunk) {
posToRedraw := make(map[protocol.ChunkPos]bool)
posToRedraw := make(map[world.ChunkPos]bool)
for _, sub := range pk.SubChunkEntries {
var (
absX = pk.Position[0] + int32(sub.Offset[0])
absY = pk.Position[1] + int32(sub.Offset[1])
absZ = pk.Position[2] + int32(sub.Offset[2])
pos = protocol.ChunkPos{absX, absZ}
pos = world.ChunkPos{absX, absZ}
)
ch, ok := w.worldState.chunks[pos]
if !ok {
@ -101,10 +102,10 @@ func (w *worldsHandler) processSubChunk(pk *packet.SubChunk) {
logrus.Error(err)
}
for _, blockNBT := range blockNBTs {
x := blockNBT["x"].(int32)
y := blockNBT["y"].(int32)
z := blockNBT["z"].(int32)
w.worldState.blockNBTs[protocol.BlockPos{x, y, z}] = blockNBT
x := int(blockNBT["x"].(int32))
y := int(blockNBT["y"].(int32))
z := int(blockNBT["z"].(int32))
w.worldState.blockNBTs[cube.Pos{x, y, z}] = blockNBT
}
posToRedraw[pos] = true
@ -134,10 +135,11 @@ func (w *worldsHandler) ProcessChunkPackets(pk packet.Packet) packet.Packet {
case *packet.SubChunk:
w.processSubChunk(pk)
case *packet.BlockActorData:
w.worldState.blockNBTs[pk.Position] = pk.NBTData
p := pk.Position
w.worldState.blockNBTs[cube.Pos{int(p.X()), int(p.Y()), int(p.Z())}] = pk.NBTData
case *packet.UpdateBlock:
if w.settings.BlockUpdates {
cp := protocol.ChunkPos{pk.Position.X() >> 4, pk.Position.Z() >> 4}
cp := world.ChunkPos{pk.Position.X() >> 4, pk.Position.Z() >> 4}
c, ok := w.worldState.chunks[cp]
if ok {
x, y, z := blockPosInChunk(pk.Position)
@ -147,7 +149,7 @@ func (w *worldsHandler) ProcessChunkPackets(pk packet.Packet) packet.Packet {
}
case *packet.UpdateSubChunkBlocks:
if w.settings.BlockUpdates {
cp := protocol.ChunkPos{pk.Position.X(), pk.Position.Z()}
cp := world.ChunkPos{pk.Position.X(), pk.Position.Z()}
c, ok := w.worldState.chunks[cp]
if ok {
for _, bce := range pk.Blocks {

View File

@ -4,6 +4,7 @@ import (
"github.com/bedrock-tool/bedrocktool/locale"
"github.com/bedrock-tool/bedrocktool/utils/nbtconv"
"github.com/df-mc/dragonfly/server/block"
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/item/inventory"
"github.com/df-mc/dragonfly/server/world"
@ -92,7 +93,8 @@ func (w *worldsHandler) processItemPacketsServer(pk packet.Packet) packet.Packet
}
// put into subchunk
nbt, ok := w.worldState.blockNBTs[existing.OpenPacket.ContainerPosition]
p := existing.OpenPacket.ContainerPosition
nbt, ok := w.worldState.blockNBTs[cube.Pos{int(p.X()), int(p.Y()), int(p.Z())}]
if ok {
nbt["Items"] = nbtconv.InvToNBT(inv)
}

View File

@ -13,6 +13,7 @@ import (
"github.com/go-gl/mathgl/mgl32"
"golang.design/x/lockfree"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/chunk"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
@ -262,8 +263,8 @@ func (m *MapUI) ToImage() *image.RGBA {
return img
}
func (m *MapUI) SetChunk(pos protocol.ChunkPos, ch *chunk.Chunk, complete bool) {
m.renderQueue.Enqueue(&RenderElem{pos, ch})
func (m *MapUI) SetChunk(pos world.ChunkPos, ch *chunk.Chunk, complete bool) {
m.renderQueue.Enqueue(&RenderElem{(protocol.ChunkPos)(pos), ch})
m.SchedRedraw()
}

View File

@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"image"
"image/color"
"image/png"
"math/rand"
"os"
@ -54,8 +55,8 @@ type WorldSettings struct {
type worldState struct {
dimension world.Dimension
chunks map[protocol.ChunkPos]*chunk.Chunk
blockNBTs map[protocol.BlockPos]map[string]any
chunks map[world.ChunkPos]*chunk.Chunk
blockNBTs map[cube.Pos]map[string]any
entities map[uint64]*entityState
openItemContainers map[byte]*itemContainer
Name string
@ -218,8 +219,8 @@ func (w *worldsHandler) currentName() string {
func (w *worldsHandler) Reset() {
w.worldState = worldState{
dimension: w.worldState.dimension,
chunks: make(map[protocol.ChunkPos]*chunk.Chunk),
blockNBTs: make(map[protocol.BlockPos]map[string]any),
chunks: make(map[world.ChunkPos]*chunk.Chunk),
blockNBTs: make(map[cube.Pos]map[string]any),
entities: make(map[uint64]*entityState),
openItemContainers: make(map[byte]*itemContainer),
Name: w.currentName(),
@ -228,11 +229,11 @@ func (w *worldsHandler) Reset() {
}
func (w *worldState) cullChunks() {
keys := make([]protocol.ChunkPos, 0, len(w.chunks))
keys := make([]world.ChunkPos, 0, len(w.chunks))
for cp := range w.chunks {
keys = append(keys, cp)
}
for _, cp := range fp.Filter(func(cp protocol.ChunkPos) bool {
for _, cp := range fp.Filter(func(cp world.ChunkPos) bool {
return !fp.Some(func(sc *chunk.SubChunk) bool {
return !sc.Empty()
})(w.chunks[cp].Sub())
@ -241,6 +242,35 @@ func (w *worldState) cullChunks() {
}
}
type dummyBlock struct {
id string
nbt map[string]any
}
func (d *dummyBlock) EncodeBlock() (string, map[string]any) {
return d.id, d.nbt
}
func (d *dummyBlock) Hash() uint64 {
return 0
}
func (d *dummyBlock) Model() world.BlockModel {
return nil
}
func (d *dummyBlock) Color() color.RGBA {
return color.RGBA{0, 0, 0, 0}
}
func (d *dummyBlock) DecodeNBT(data map[string]any) any {
return nil
}
func (d *dummyBlock) EncodeNBT() map[string]any {
return d.nbt
}
func (w *worldState) Save(folder string) (*mcdb.DB, error) {
provider, err := mcdb.Config{
Log: logrus.StandardLogger(),
@ -250,32 +280,32 @@ func (w *worldState) Save(folder string) (*mcdb.DB, error) {
return nil, err
}
// save chunk data
for cp, c := range w.chunks {
provider.SaveChunk((world.ChunkPos)(cp), c, w.dimension)
}
// save block nbt data
chunkBlockNBT := make(map[world.ChunkPos][]map[string]any)
chunkBlockNBT := make(map[world.ChunkPos]map[cube.Pos]world.Block)
for bp, blockNBT := range w.blockNBTs { // 3d to 2d
cp := world.ChunkPos{bp.X() >> 4, bp.Z() >> 4}
chunkBlockNBT[cp] = append(chunkBlockNBT[cp], blockNBT)
}
for cp, blockNBT := range chunkBlockNBT {
err = provider.SaveBlockNBT(cp, blockNBT, w.dimension)
if err != nil {
logrus.Error(err)
cp := world.ChunkPos{int32(bp.X()) >> 4, int32(bp.Z()) >> 4}
m, ok := chunkBlockNBT[cp]
if !ok {
m = make(map[cube.Pos]world.Block)
chunkBlockNBT[cp] = m
}
id := blockNBT["id"].(string)
m[bp] = &dummyBlock{id, blockNBT}
}
// save entities
chunkEntities := make(map[world.ChunkPos][]world.Entity)
for _, es := range w.entities {
cp := world.ChunkPos{int32(es.Position.X()) >> 4, int32(es.Position.Z()) >> 4}
chunkEntities[cp] = append(chunkEntities[cp], es.ToServerEntity())
}
for cp, v := range chunkEntities {
err = provider.SaveEntities(cp, v, w.dimension)
// save chunk data
for cp, c := range w.chunks {
column := &world.Column{
Chunk: c,
BlockEntities: chunkBlockNBT[cp],
Entities: chunkEntities[cp],
}
err = provider.StoreColumn(cp, w.dimension, column)
if err != nil {
logrus.Error(err)
}
@ -301,6 +331,9 @@ func (w *worldsHandler) SaveAndReset() {
img = w.mapUI.ToImage()
}
folder := fmt.Sprintf("worlds/%s/%s", w.serverState.Name, worldStateCopy.Name)
filename := folder + ".mcworld"
w.serverState.worldCounter += 1
w.Reset()
w.wg.Add(1)
@ -308,12 +341,14 @@ func (w *worldsHandler) SaveAndReset() {
defer w.wg.Done()
logrus.Infof(locale.Loc("saving_world", locale.Strmap{"Name": worldStateCopy.Name, "Count": len(worldStateCopy.chunks)}))
w.gui.Message(messages.SavingWorld{
Name: w.worldState.Name,
Chunks: len(w.worldState.chunks),
World: &messages.SavedWorld{
Name: worldStateCopy.Name,
Path: filename,
Chunks: len(worldStateCopy.chunks),
},
})
// open world
folder := fmt.Sprintf("worlds/%s/%s", w.serverState.Name, worldStateCopy.Name)
os.RemoveAll(folder)
os.MkdirAll(folder, 0o777)
provider, err := worldStateCopy.Save(folder)
@ -439,7 +474,6 @@ func (w *worldsHandler) SaveAndReset() {
w.AddPacks(folder)
// zip it
filename := folder + ".mcworld"
err = utils.ZipFolder(filename, folder)
if err != nil {
logrus.Error(err)
@ -533,6 +567,9 @@ func (w *worldsHandler) AddPacks(folder string) {
}
func (w *worldsHandler) OnConnect(err error) bool {
w.gui.Message(messages.SetWorldName{
WorldName: w.worldState.Name,
})
w.gui.Message(messages.SetUIState(messages.UIStateMain))
if err != nil {
return false

View File

@ -1,8 +1,10 @@
package packs
import (
"fmt"
"image"
"image/color"
"sort"
"sync"
"gioui.org/f32"
@ -26,19 +28,25 @@ type (
type Page struct {
*pages.Router
State messages.UIState
packsList widget.List
l sync.Mutex
Packs []*packEntry
State messages.UIState
packsList widget.List
packShowButtons map[string]*widget.Clickable
l sync.Mutex
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 +57,8 @@ func New(router *pages.Router) *Page {
Axis: layout.Vertical,
},
},
Packs: make(map[string]*packEntry),
packShowButtons: make(map[string]*widget.Clickable),
}
}
@ -68,48 +78,99 @@ func (p *Page) NavItem() component.NavItem {
//Icon: icon.OtherIcon,
}
}
func drawPackIcon(ops *op.Ops, imageOp paint.ImageOp, bounds image.Point) {
imageOp.Add(ops)
func drawPackIcon(gtx C, hasImage bool, imageOp paint.ImageOp, bounds image.Point) D {
return layout.Inset{
Top: unit.Dp(5),
Bottom: unit.Dp(5),
Right: unit.Dp(5),
Left: unit.Dp(5),
}.Layout(gtx, func(gtx C) D {
if hasImage {
imageOp.Add(gtx.Ops)
s := imageOp.Size()
p := f32.Pt(float32(s.X), float32(s.Y))
p.X = 1 / (p.X / float32(bounds.X))
p.Y = 1 / (p.Y / float32(bounds.Y))
defer op.Affine(f32.Affine2D{}.Scale(f32.Pt(0, 0), p)).Push(gtx.Ops).Pop()
paint.PaintOp{}.Add(gtx.Ops)
}
return D{Size: bounds}
})
s := imageOp.Size()
p := f32.Pt(float32(s.X), float32(s.Y))
p.X = 1 / (p.X / float32(bounds.X))
p.Y = 1 / (p.Y / float32(bounds.Y))
defer op.Affine(f32.Affine2D{}.Scale(f32.Pt(0, 0), p)).Push(ops).Pop()
paint.PaintOp{}.Add(ops)
}
func drawPackEntry(gtx C, th *material.Theme, entry *packEntry) D {
return layout.UniformInset(5).Layout(gtx, func(gtx C) D {
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
layout.Rigid(func(gtx C) D {
s := image.Pt(50, 50)
if entry.HasIcon {
drawPackIcon(gtx.Ops, entry.Icon, s)
}
return D{Size: s.Add(image.Pt(10, 10))}
}),
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(func(gtx C) D {
if entry.Err != nil {
return material.LabelStyle{
Color: color.NRGBA{0xbb, 0x00, 0x00, 0xff},
Text: entry.Err.Error(),
}.Layout(gtx)
}
return D{}
}),
)
}),
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, button *widget.Clickable) 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.Loaded)),
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 {
fn := func(gtx C) D {
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
layout.Rigid(func(gtx C) D {
return drawPackIcon(gtx, entry.HasIcon, entry.Icon, image.Pt(50, 50))
}),
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.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{
Color: color.NRGBA{0xbb, 0x00, 0x00, 0xff},
Text: entry.Err.Error(),
}.Layout(gtx)
}
return D{}
}),
)
}),
)
}
if entry.Path != "" {
return material.ButtonLayoutStyle{
Background: MulAlpha(th.Palette.Bg, 0x60),
Button: button,
CornerRadius: 3,
}.Layout(gtx, fn)
} else {
return fn(gtx)
}
})
}
func (p *Page) layoutFinished(gtx C, th *material.Theme) D {
for uuid, button := range p.packShowButtons {
if button.Clicked() {
pack := p.Packs[uuid]
if pack.IsFinished {
utils.ShowFile(pack.Path)
}
}
}
return layout.Center.Layout(gtx, func(gtx C) D {
return layout.Flex{
Axis: layout.Vertical,
@ -118,9 +179,17 @@ 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]
return drawPackEntry(gtx, th, entry)
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]]
button := p.packShowButtons[keys[index]]
return drawPackEntry(gtx, th, entry, button)
})
}),
)
@ -137,7 +206,9 @@ 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.UIStateConnecting:
return layout.Center.Layout(gtx, material.Label(th, 100, "Connecting").Layout)
case messages.UIStateMain:
return p.layoutFinished(gtx, th)
}
return layout.Dimensions{}
@ -155,19 +226,46 @@ 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.packShowButtons[e.UUID] = &widget.Clickable{}
}
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
e.Path = dp.Path
}
p.l.Unlock()
p.Router.Invalidate()
r.Ok = true
}

View File

@ -2,7 +2,6 @@ package worlds
import (
"fmt"
"image"
"sync"
"gioui.org/layout"
@ -29,7 +28,7 @@ type Page struct {
worldName string
worldsList widget.List
worlds []messages.SavingWorld
worlds []*messages.SavedWorld
l sync.Mutex
}
@ -62,6 +61,19 @@ func (p *Page) NavItem() component.NavItem {
}
}
func displayWorldEntry(gtx C, th *material.Theme, entry *messages.SavedWorld) D {
return layout.UniformInset(5).Layout(gtx, func(gtx C) D {
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
layout.Rigid(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, fmt.Sprintf("%d Chunks", entry.Chunks)).Layout),
)
}),
)
})
}
func (p *Page) Layout(gtx C, th *material.Theme) D {
margin := layout.Inset{
Top: unit.Dp(25),
@ -70,46 +82,42 @@ func (p *Page) Layout(gtx C, th *material.Theme) D {
Left: unit.Dp(35),
}
switch p.State {
case messages.UIStateConnect:
// display login page
return margin.Layout(gtx, material.Label(th, 100, "connect Client").Layout)
case messages.UIStateConnecting:
// display connecting to server
return margin.Layout(gtx, material.Label(th, 100, "Connecting").Layout)
case messages.UIStateMain:
// show the main ui
return layout.Flex{
Axis: layout.Vertical,
}.Layout(gtx,
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),
)
margin.Layout(gtx, func(gtx C) D {
switch p.State {
case messages.UIStateConnect:
// display login page
return layout.Center.Layout(gtx, material.Label(th, 100, "connect Client").Layout)
case messages.UIStateConnecting:
return layout.Center.Layout(gtx, material.Label(th, 100, "Connecting").Layout)
case messages.UIStateMain:
// show the main ui
return layout.Flex{
Axis: layout.Vertical,
}.Layout(gtx,
//layout.Rigid(material.Label(th, th.TextSize, p.worldName).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(15).
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 displayWorldEntry(gtx, th, entry)
})
})
}),
)
}
}),
)
}
return D{}
})
return layout.Dimensions{}
}
@ -140,7 +148,7 @@ func (u *Page) Handler(data any) messages.MessageResponse {
r.Ok = true
case messages.SavingWorld:
u.l.Lock()
u.worlds = append(u.worlds, m)
u.worlds = append(u.worlds, m.World)
u.l.Unlock()
u.Router.Invalidate()
r.Ok = true

View File

@ -63,13 +63,29 @@ type NewSkin struct {
}
type SavingWorld struct {
World *SavedWorld
}
type SavedWorld struct {
Name string
Path string
Chunks int
Image image.Image
}
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

View File

@ -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
}

View File

@ -183,7 +183,8 @@ func SizeofFmt(num float32) string {
func ShowFile(path string) {
path, _ = filepath.Abs(path)
if runtime.GOOS == "windows" {
exec.Command(`explorer`, `/select,`, path)
cmd := exec.Command(`explorer`, "/select,", path)
cmd.Start()
return
}
if runtime.GOOS == "linux" {