Compare commits
6 Commits
3cc13a7bbf
...
e13856a4ff
Author | SHA1 | Date |
---|---|---|
olebeck | e13856a4ff | |
olebeck | 2761cf094c | |
olebeck | 0eb3d09046 | |
olebeck | 4968181f0e | |
olebeck | bbf7cc1d75 | |
olebeck | b867221a66 |
6
go.mod
6
go.mod
|
@ -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
8
go.sum
|
@ -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=
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Binary file not shown.
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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" {
|
||||
|
|
Loading…
Reference in New Issue