performance optimizations

This commit is contained in:
olebeck 2023-03-30 13:48:03 +02:00
parent d3a8219d99
commit 1e990d7fcb
15 changed files with 320 additions and 263 deletions

View File

@ -41,6 +41,15 @@ func (c *CLI) Start(ctx context.Context, cancel context.CancelFunc) error {
} }
func main() { func main() {
/*
cf, _ := os.Create("cpu.pprof")
err := pprof.StartCPUProfile(cf)
if err != nil {
logrus.Error(err)
}
defer pprof.StopCPUProfile()
*/
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
logrus.Errorf(locale.Loc("fatal_error", nil)) logrus.Errorf(locale.Loc("fatal_error", nil))

5
go.mod
View File

@ -6,7 +6,7 @@ go 1.20
replace github.com/sandertv/gophertunnel => github.com/olebeck/gophertunnel v1.28.1-1 replace github.com/sandertv/gophertunnel => github.com/olebeck/gophertunnel v1.28.1-1
//replace github.com/df-mc/dragonfly => ./dragonfly //replace github.com/df-mc/dragonfly => ./dragonfly
replace github.com/df-mc/dragonfly => github.com/olebeck/dragonfly v0.9.3-10 replace github.com/df-mc/dragonfly => github.com/olebeck/dragonfly v0.9.4-3
//replace gioui.org => ./gio //replace gioui.org => ./gio
replace gioui.org => github.com/olebeck/gio v0.0.0-20230321105529-d424f1a59af9 replace gioui.org => github.com/olebeck/gio v0.0.0-20230321105529-d424f1a59af9
@ -15,7 +15,7 @@ require (
gioui.org v0.0.0-20230323230841-d7b1c7c33b33 gioui.org v0.0.0-20230323230841-d7b1c7c33b33
gioui.org/x v0.0.0-20230313161557-05b40af72ed0 gioui.org/x v0.0.0-20230313161557-05b40af72ed0
github.com/cloudfoundry-attic/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 github.com/cloudfoundry-attic/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
github.com/df-mc/dragonfly v0.9.3 github.com/df-mc/dragonfly v0.9.4
github.com/df-mc/goleveldb v1.1.9 github.com/df-mc/goleveldb v1.1.9
github.com/fatih/color v1.15.0 github.com/fatih/color v1.15.0
github.com/flytam/filenamify v1.1.2 github.com/flytam/filenamify v1.1.2
@ -48,6 +48,7 @@ require (
github.com/changkun/lockfree v0.0.1 // indirect github.com/changkun/lockfree v0.0.1 // indirect
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 // indirect github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 // indirect
github.com/df-mc/atomic v1.10.0 // indirect github.com/df-mc/atomic v1.10.0 // indirect
github.com/df-mc/worldupgrader v1.0.3 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-text/typesetting v0.0.0-20221214153724-0399769901d5 // indirect github.com/go-text/typesetting v0.0.0-20221214153724-0399769901d5 // indirect
github.com/golang/protobuf v1.5.2 // indirect github.com/golang/protobuf v1.5.2 // indirect

6
go.sum
View File

@ -30,6 +30,8 @@ github.com/df-mc/atomic v1.10.0 h1:0ZuxBKwR/hxcFGorKiHIp+hY7hgY+XBTzhCYD2NqSEg=
github.com/df-mc/atomic v1.10.0/go.mod h1:Gw9rf+rPIbydMjA329Jn4yjd/O2c/qusw3iNp4tFGSc= github.com/df-mc/atomic v1.10.0/go.mod h1:Gw9rf+rPIbydMjA329Jn4yjd/O2c/qusw3iNp4tFGSc=
github.com/df-mc/goleveldb v1.1.9 h1:ihdosZyy5jkQKrxucTQmN90jq/2lUwQnJZjIYIC/9YU= github.com/df-mc/goleveldb v1.1.9 h1:ihdosZyy5jkQKrxucTQmN90jq/2lUwQnJZjIYIC/9YU=
github.com/df-mc/goleveldb v1.1.9/go.mod h1:+NHCup03Sci5q84APIA21z3iPZCuk6m6ABtg4nANCSk= github.com/df-mc/goleveldb v1.1.9/go.mod h1:+NHCup03Sci5q84APIA21z3iPZCuk6m6ABtg4nANCSk=
github.com/df-mc/worldupgrader v1.0.3 h1:3nbthy6vfSNQZdqHBR+E5Fh3mCeWmCwLtqrYDiPUG5I=
github.com/df-mc/worldupgrader v1.0.3/go.mod h1:6ybkJ/BV9b0XkcPzcLmvgT9Nv/xgBXdDQTmRhu7b8zQ=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/flytam/filenamify v1.1.2 h1:dGlfWU4zrhDlsmvob4IFcfgjG5vIjfo4UwLyec6Wx94= github.com/flytam/filenamify v1.1.2 h1:dGlfWU4zrhDlsmvob4IFcfgjG5vIjfo4UwLyec6Wx94=
@ -83,6 +85,10 @@ github.com/nicksnyder/go-i18n/v2 v2.2.1 h1:aOzRCdwsJuoExfZhoiXHy4bjruwCMdt5otbYo
github.com/nicksnyder/go-i18n/v2 v2.2.1/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0= github.com/nicksnyder/go-i18n/v2 v2.2.1/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0=
github.com/olebeck/dragonfly v0.9.3-10 h1:USruaK+8c3gsX9CtDzInLbwsdoZxcYjmnC8t/H4/Zjw= github.com/olebeck/dragonfly v0.9.3-10 h1:USruaK+8c3gsX9CtDzInLbwsdoZxcYjmnC8t/H4/Zjw=
github.com/olebeck/dragonfly v0.9.3-10/go.mod h1:nnnmYWgSTNQb9x33nBthqN/2vyHlUaijfo+e2y3W5j4= github.com/olebeck/dragonfly v0.9.3-10/go.mod h1:nnnmYWgSTNQb9x33nBthqN/2vyHlUaijfo+e2y3W5j4=
github.com/olebeck/dragonfly v0.9.4-1 h1:AvP0JALUJqkDBTCT/7tOrIndKIrIol1rX6XFcbOwpLI=
github.com/olebeck/dragonfly v0.9.4-1/go.mod h1:Iu46xugbkTQ9CcUj+zPC4VvKP94CmxxLGtW8pHgAPr4=
github.com/olebeck/dragonfly v0.9.4-2 h1:e1bzsTxcPMaUUs2B6wnCbc1FE96XcdBbL7mp1LE92NI=
github.com/olebeck/dragonfly v0.9.4-2/go.mod h1:k8OQvjmfj+JbrwQf1qHfMETlFHOp0WJLjILN+QVqh+c=
github.com/olebeck/gio v0.0.0-20230321105529-d424f1a59af9 h1:TqDsMHwjW5ZYfh+RE8ussT62m0qXqo+QjzSXb7BCVA4= github.com/olebeck/gio v0.0.0-20230321105529-d424f1a59af9 h1:TqDsMHwjW5ZYfh+RE8ussT62m0qXqo+QjzSXb7BCVA4=
github.com/olebeck/gio v0.0.0-20230321105529-d424f1a59af9/go.mod h1:+W1Kpf96YcfissZocFqIp6O42FDTuphkObbEybp+Ffc= github.com/olebeck/gio v0.0.0-20230321105529-d424f1a59af9/go.mod h1:+W1Kpf96YcfissZocFqIp6O42FDTuphkObbEybp+Ffc=
github.com/olebeck/gophertunnel v1.28.1-1 h1:bw2jeMz94YHF5qQYhq1Yq/6fALkklGu7k26YbPI4DSs= github.com/olebeck/gophertunnel v1.28.1-1 h1:bw2jeMz94YHF5qQYhq1Yq/6fALkklGu7k26YbPI4DSs=

View File

@ -3,7 +3,6 @@ package subcommands
import ( import (
"context" "context"
"flag" "flag"
"strings"
"github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/locale"
"github.com/bedrock-tool/bedrocktool/utils" "github.com/bedrock-tool/bedrocktool/utils"
@ -11,14 +10,12 @@ import (
type DebugProxyCMD struct { type DebugProxyCMD struct {
ServerAddress string ServerAddress string
Filter string
} }
func (*DebugProxyCMD) Name() string { return "debug-proxy" } func (*DebugProxyCMD) Name() string { return "debug-proxy" }
func (*DebugProxyCMD) Synopsis() string { return locale.Loc("debug_proxy_synopsis", nil) } func (*DebugProxyCMD) Synopsis() string { return locale.Loc("debug_proxy_synopsis", nil) }
func (c *DebugProxyCMD) SetFlags(f *flag.FlagSet) { func (c *DebugProxyCMD) SetFlags(f *flag.FlagSet) {
f.StringVar(&c.ServerAddress, "address", "", locale.Loc("remote_address", nil)) f.StringVar(&c.ServerAddress, "address", "", locale.Loc("remote_address", nil))
f.StringVar(&c.Filter, "filter", "", locale.Loc("packet_filter", nil))
} }
func (c *DebugProxyCMD) Execute(ctx context.Context, ui utils.UI) error { func (c *DebugProxyCMD) Execute(ctx context.Context, ui utils.UI) error {
@ -29,27 +26,11 @@ func (c *DebugProxyCMD) Execute(ctx context.Context, ui utils.UI) error {
utils.Options.Debug = true utils.Options.Debug = true
filters := strings.Split(c.Filter, ",")
if len(filters) > 0 {
for _, v := range filters {
if len(v) == 0 {
continue
}
if string(v[0]) == "*" {
v = v[1:]
}
v = strings.TrimPrefix(v, "packet.")
v = "packet." + v
utils.ExtraVerbose = append(utils.ExtraVerbose, v)
}
}
proxy, err := utils.NewProxy() proxy, err := utils.NewProxy()
if err != nil { if err != nil {
return err return err
} }
err = proxy.Run(ctx, address) return proxy.Run(ctx, address)
return err
} }
func init() { func init() {

View File

@ -9,21 +9,21 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
func (w *WorldState) processChangeDimension(pk *packet.ChangeDimension) { func (w *worldsServer) processChangeDimension(pk *packet.ChangeDimension) {
if len(w.chunks) > 0 { if len(w.worldState.chunks) > 0 {
w.SaveAndReset() w.SaveAndReset()
} else { } else {
logrus.Info(locale.Loc("not_saving_empty", nil)) logrus.Info(locale.Loc("not_saving_empty", nil))
w.Reset() w.Reset(w.CurrentName())
} }
dimensionID := pk.Dimension dimensionID := pk.Dimension
if w.ispre118 { if w.serverState.ispre118 {
dimensionID += 10 dimensionID += 10
} }
w.Dim = dimensionIDMap[uint8(dimensionID)] w.worldState.Dim = dimensionIDMap[uint8(dimensionID)]
} }
func (w *WorldState) processLevelChunk(pk *packet.LevelChunk) { func (w *worldsServer) processLevelChunk(pk *packet.LevelChunk) {
// ignore empty chunks THANKS WEIRD SERVER SOFTWARE DEVS // ignore empty chunks THANKS WEIRD SERVER SOFTWARE DEVS
if len(pk.RawPayload) == 0 { if len(pk.RawPayload) == 0 {
logrus.Info(locale.Loc("empty_chunk", nil)) logrus.Info(locale.Loc("empty_chunk", nil))
@ -40,32 +40,35 @@ func (w *WorldState) processLevelChunk(pk *packet.LevelChunk) {
subChunkCount = int(pk.SubChunkCount) subChunkCount = int(pk.SubChunkCount)
} }
ch, blockNBTs, err := chunk.NetworkDecode(world.AirRID(), pk.RawPayload, subChunkCount, w.Dim.Range(), w.ispre118, w.bp.HasBlocks()) ch, blockNBTs, err := chunk.NetworkDecode(world.AirRID(), pk.RawPayload, subChunkCount, w.worldState.Dim.Range(), w.serverState.ispre118, w.bp.HasBlocks())
if err != nil { if err != nil {
logrus.Error(err) logrus.Error(err)
return return
} }
if blockNBTs != nil { if blockNBTs != nil {
w.blockNBT[protocol.SubChunkPos{ w.worldState.blockNBT[protocol.SubChunkPos{
pk.Position.X(), 0, pk.Position.Z(), pk.Position.X(), 0, pk.Position.Z(),
}] = blockNBTs }] = blockNBTs
} }
w.chunks[pk.Position] = ch w.worldState.chunks[pk.Position] = ch
max := w.Dim.Range().Height() / 16 max := w.worldState.Dim.Range().Height() / 16
switch pk.SubChunkCount { switch pk.SubChunkCount {
case protocol.SubChunkRequestModeLimited: case protocol.SubChunkRequestModeLimited:
max = int(pk.HighestSubChunk) max = int(pk.HighestSubChunk)
fallthrough fallthrough
case protocol.SubChunkRequestModeLimitless: case protocol.SubChunkRequestModeLimitless:
var offsetTable []protocol.SubChunkOffset var offsetTable []protocol.SubChunkOffset
r := w.Dim.Range() r := w.worldState.Dim.Range()
for y := int8(r.Min() / 16); y < int8(r.Max()); y++ { for y := int8(r.Min() / 16); y < int8(r.Max()); y++ {
offsetTable = append(offsetTable, protocol.SubChunkOffset{0, y, 0}) offsetTable = append(offsetTable, protocol.SubChunkOffset{0, y, 0})
} }
dimId, ok := world.DimensionID(w.worldState.Dim)
_ = ok
w.proxy.Server.WritePacket(&packet.SubChunkRequest{ w.proxy.Server.WritePacket(&packet.SubChunkRequest{
Dimension: int32(w.Dim.EncodeDimension()), Dimension: int32(dimId),
Position: protocol.SubChunkPos{ Position: protocol.SubChunkPos{
pk.Position.X(), 0, pk.Position.Z(), pk.Position.X(), 0, pk.Position.Z(),
}, },
@ -86,7 +89,7 @@ func (w *WorldState) processLevelChunk(pk *packet.LevelChunk) {
} }
} }
func (w *WorldState) processSubChunk(pk *packet.SubChunk) { func (w *worldsServer) processSubChunk(pk *packet.SubChunk) {
posToRedraw := make(map[protocol.ChunkPos]bool) posToRedraw := make(map[protocol.ChunkPos]bool)
for _, sub := range pk.SubChunkEntries { for _, sub := range pk.SubChunkEntries {
@ -97,7 +100,7 @@ func (w *WorldState) processSubChunk(pk *packet.SubChunk) {
subPos = protocol.SubChunkPos{absX, absY, absZ} subPos = protocol.SubChunkPos{absX, absY, absZ}
pos = protocol.ChunkPos{absX, absZ} pos = protocol.ChunkPos{absX, absZ}
) )
ch, ok := w.chunks[pos] ch, ok := w.worldState.chunks[pos]
if !ok { if !ok {
logrus.Error(locale.Loc("subchunk_before_chunk", nil)) logrus.Error(locale.Loc("subchunk_before_chunk", nil))
continue continue
@ -107,7 +110,7 @@ func (w *WorldState) processSubChunk(pk *packet.SubChunk) {
logrus.Error(err) logrus.Error(err)
} }
if blockNBT != nil { if blockNBT != nil {
w.blockNBT[subPos] = blockNBT w.worldState.blockNBT[subPos] = blockNBT
} }
posToRedraw[pos] = true posToRedraw[pos] = true
@ -115,7 +118,7 @@ func (w *WorldState) processSubChunk(pk *packet.SubChunk) {
// redraw the chunks // redraw the chunks
for pos := range posToRedraw { for pos := range posToRedraw {
w.mapUI.SetChunk(pos, w.chunks[pos], true) w.mapUI.SetChunk(pos, w.worldState.chunks[pos], true)
} }
w.mapUI.SchedRedraw() w.mapUI.SchedRedraw()
} }
@ -124,22 +127,22 @@ func blockPosInChunk(pos protocol.BlockPos) (uint8, int16, uint8) {
return uint8(pos.X() & 0x0f), int16(pos.Y() & 0x0f), uint8(pos.Z() & 0x0f) return uint8(pos.X() & 0x0f), int16(pos.Y() & 0x0f), uint8(pos.Z() & 0x0f)
} }
func (w *WorldState) ProcessChunkPackets(pk packet.Packet) packet.Packet { func (w *worldsServer) ProcessChunkPackets(pk packet.Packet) packet.Packet {
switch pk := pk.(type) { switch pk := pk.(type) {
case *packet.ChangeDimension: case *packet.ChangeDimension:
w.processChangeDimension(pk) w.processChangeDimension(pk)
case *packet.LevelChunk: case *packet.LevelChunk:
w.processLevelChunk(pk) w.processLevelChunk(pk)
w.proxy.SendPopup(locale.Locm("popup_chunk_count", locale.Strmap{"Count": len(w.chunks), "Name": w.WorldName}, len(w.chunks))) w.proxy.SendPopup(locale.Locm("popup_chunk_count", locale.Strmap{"Count": len(w.worldState.chunks), "Name": w.worldState.Name}, len(w.worldState.chunks)))
case *packet.SubChunk: case *packet.SubChunk:
w.processSubChunk(pk) w.processSubChunk(pk)
case *packet.BlockActorData: case *packet.BlockActorData:
if w.blockUpdates { if w.settings.blockUpdates {
sp := protocol.SubChunkPos{pk.Position.X() << 4, 0, pk.Position.Z() << 4} sp := protocol.SubChunkPos{pk.Position.X() << 4, 0, pk.Position.Z() << 4}
b, ok := w.blockNBT[sp] b, ok := w.worldState.blockNBT[sp]
if !ok { if !ok {
w.blockNBT[sp] = []map[string]any{pk.NBTData} w.worldState.blockNBT[sp] = []map[string]any{pk.NBTData}
} else { } else {
for i, v := range b { for i, v := range b {
x, y, z := v["x"].(int32), v["y"].(int32), v["z"].(int32) x, y, z := v["x"].(int32), v["y"].(int32), v["z"].(int32)
@ -151,19 +154,19 @@ func (w *WorldState) ProcessChunkPackets(pk packet.Packet) packet.Packet {
} }
} }
case *packet.UpdateBlock: case *packet.UpdateBlock:
if w.blockUpdates { if w.settings.blockUpdates {
cp := protocol.ChunkPos{pk.Position.X() >> 4, pk.Position.Z() >> 4} cp := protocol.ChunkPos{pk.Position.X() >> 4, pk.Position.Z() >> 4}
c, ok := w.chunks[cp] c, ok := w.worldState.chunks[cp]
if ok { if ok {
x, y, z := blockPosInChunk(pk.Position) x, y, z := blockPosInChunk(pk.Position)
c.SetBlock(x, y, z, uint8(pk.Layer), pk.NewBlockRuntimeID) c.SetBlock(x, y, z, uint8(pk.Layer), pk.NewBlockRuntimeID)
w.mapUI.SetChunk(cp, w.chunks[cp], true) w.mapUI.SetChunk(cp, w.worldState.chunks[cp], true)
} }
} }
case *packet.UpdateSubChunkBlocks: case *packet.UpdateSubChunkBlocks:
if w.blockUpdates { if w.settings.blockUpdates {
cp := protocol.ChunkPos{pk.Position.X(), pk.Position.Z()} cp := protocol.ChunkPos{pk.Position.X(), pk.Position.Z()}
c, ok := w.chunks[cp] c, ok := w.worldState.chunks[cp]
if ok { if ok {
for _, bce := range pk.Blocks { for _, bce := range pk.Blocks {
x, y, z := blockPosInChunk(bce.BlockPos) x, y, z := blockPosInChunk(bce.BlockPos)
@ -173,7 +176,7 @@ func (w *WorldState) ProcessChunkPackets(pk packet.Packet) packet.Packet {
c.SetBlock(x, y, z, 0, bce.BlockRuntimeID) c.SetBlock(x, y, z, 0, bce.BlockRuntimeID)
} }
} }
w.mapUI.SetChunk(cp, w.chunks[cp], true) w.mapUI.SetChunk(cp, w.worldState.chunks[cp], true)
} }
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 513 B

After

Width:  |  Height:  |  Size: 432 B

View File

@ -3,6 +3,7 @@ package world_test
import ( import (
"image/png" "image/png"
"os" "os"
"runtime/pprof"
"testing" "testing"
"github.com/bedrock-tool/bedrocktool/subcommands/world" "github.com/bedrock-tool/bedrocktool/subcommands/world"
@ -21,19 +22,32 @@ func Test(t *testing.T) {
func Benchmark_chunk_decode(b *testing.B) { func Benchmark_chunk_decode(b *testing.B) {
data, _ := os.ReadFile("chunk.bin") data, _ := os.ReadFile("chunk.bin")
cf, _ := os.Create("cpu.pprof")
err := pprof.StartCPUProfile(cf)
if err != nil {
b.Error(err)
}
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
_, _, err := chunk.NetworkDecode(33, data, 6, cube.Range{0, 255}, true, false) _, _, err := chunk.NetworkDecode(33, data, 6, cube.Range{0, 255}, true, false)
if err != nil { if err != nil {
b.Error(err) b.Error(err)
} }
} }
pprof.StopCPUProfile()
} }
func Benchmark_render_chunk(b *testing.B) { func Benchmark_render_chunk(b *testing.B) {
data, _ := os.ReadFile("chunk.bin") data, _ := os.ReadFile("chunk.bin")
ch, _, _ := chunk.NetworkDecode(33, data, 6, cube.Range{0, 255}, true, false) ch, _, _ := chunk.NetworkDecode(33, data, 6, cube.Range{0, 255}, true, false)
cf, _ := os.Create("cpu.pprof")
err := pprof.StartCPUProfile(cf)
if err != nil {
b.Error(err)
}
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
world.Chunk2Img(ch, true) world.Chunk2Img(ch, true)
} }
pprof.StopCPUProfile()
} }

View File

@ -61,8 +61,8 @@ func (e serverEntity) Type() world.EntityType {
return e.EntityType return e.EntityType
} }
func (w *WorldState) processAddActor(pk *packet.AddActor) { func (w *worldsServer) processAddActor(pk *packet.AddActor) {
e, ok := w.entities[pk.EntityRuntimeID] e, ok := w.worldState.entities[pk.EntityRuntimeID]
if !ok { if !ok {
e = &entityState{ e = &entityState{
RuntimeID: pk.EntityRuntimeID, RuntimeID: pk.EntityRuntimeID,
@ -71,7 +71,7 @@ func (w *WorldState) processAddActor(pk *packet.AddActor) {
Inventory: make(map[byte]map[byte]protocol.ItemInstance), Inventory: make(map[byte]map[byte]protocol.ItemInstance),
Metadata: make(map[uint32]any), Metadata: make(map[uint32]any),
} }
w.entities[pk.EntityRuntimeID] = e w.worldState.entities[pk.EntityRuntimeID] = e
w.bp.AddEntity(behaviourpack.EntityIn{ w.bp.AddEntity(behaviourpack.EntityIn{
Identifier: pk.EntityType, Identifier: pk.EntityType,
@ -211,25 +211,25 @@ func (s *entityState) ToServerEntity() serverEntity {
return e return e
} }
func (w *WorldState) ProcessEntityPackets(pk packet.Packet) packet.Packet { func (w *worldsServer) ProcessEntityPackets(pk packet.Packet) packet.Packet {
switch pk := pk.(type) { switch pk := pk.(type) {
case *packet.AddActor: case *packet.AddActor:
w.processAddActor(pk) w.processAddActor(pk)
case *packet.RemoveActor: case *packet.RemoveActor:
case *packet.SetActorData: case *packet.SetActorData:
e, ok := w.entities[pk.EntityRuntimeID] e, ok := w.worldState.entities[pk.EntityRuntimeID]
if ok { if ok {
for k, v := range pk.EntityMetadata { for k, v := range pk.EntityMetadata {
e.Metadata[k] = v e.Metadata[k] = v
} }
} }
case *packet.SetActorMotion: case *packet.SetActorMotion:
e, ok := w.entities[pk.EntityRuntimeID] e, ok := w.worldState.entities[pk.EntityRuntimeID]
if ok { if ok {
e.Velocity = pk.Velocity e.Velocity = pk.Velocity
} }
case *packet.MoveActorDelta: case *packet.MoveActorDelta:
e, ok := w.entities[pk.EntityRuntimeID] e, ok := w.worldState.entities[pk.EntityRuntimeID]
if ok { if ok {
if pk.Flags&packet.MoveActorDeltaFlagHasX != 0 { if pk.Flags&packet.MoveActorDeltaFlagHasX != 0 {
e.Position[0] = pk.Position[0] e.Position[0] = pk.Position[0]
@ -251,14 +251,14 @@ func (w *WorldState) ProcessEntityPackets(pk packet.Packet) packet.Packet {
//} //}
} }
case *packet.MoveActorAbsolute: case *packet.MoveActorAbsolute:
e, ok := w.entities[pk.EntityRuntimeID] e, ok := w.worldState.entities[pk.EntityRuntimeID]
if ok { if ok {
e.Position = pk.Position e.Position = pk.Position
e.Pitch = pk.Rotation.X() e.Pitch = pk.Rotation.X()
e.Yaw = pk.Rotation.Y() e.Yaw = pk.Rotation.Y()
} }
case *packet.MobEquipment: case *packet.MobEquipment:
e, ok := w.entities[pk.EntityRuntimeID] e, ok := w.worldState.entities[pk.EntityRuntimeID]
if ok { if ok {
w, ok := e.Inventory[pk.WindowID] w, ok := e.Inventory[pk.WindowID]
if !ok { if !ok {
@ -268,7 +268,7 @@ func (w *WorldState) ProcessEntityPackets(pk packet.Packet) packet.Packet {
w[pk.HotBarSlot] = pk.NewItem w[pk.HotBarSlot] = pk.NewItem
} }
case *packet.MobArmourEquipment: case *packet.MobArmourEquipment:
e, ok := w.entities[pk.EntityRuntimeID] e, ok := w.worldState.entities[pk.EntityRuntimeID]
if ok { if ok {
e.Helmet = &pk.Helmet e.Helmet = &pk.Helmet
e.Chestplate = &pk.Chestplate e.Chestplate = &pk.Chestplate

View File

@ -17,25 +17,25 @@ type itemContainer struct {
Content *packet.InventoryContent Content *packet.InventoryContent
} }
func (w *WorldState) processItemPacketsServer(pk packet.Packet) packet.Packet { func (w *worldsServer) processItemPacketsServer(pk packet.Packet) packet.Packet {
switch pk := pk.(type) { switch pk := pk.(type) {
case *packet.ContainerOpen: case *packet.ContainerOpen:
// add to open containers // add to open containers
existing, ok := w.openItemContainers[pk.WindowID] existing, ok := w.worldState.openItemContainers[pk.WindowID]
if !ok { if !ok {
existing = &itemContainer{} existing = &itemContainer{}
} }
w.openItemContainers[pk.WindowID] = &itemContainer{ w.worldState.openItemContainers[pk.WindowID] = &itemContainer{
OpenPacket: pk, OpenPacket: pk,
Content: existing.Content, Content: existing.Content,
} }
case *packet.InventoryContent: case *packet.InventoryContent:
if pk.WindowID == 0x0 { // inventory if pk.WindowID == 0x0 { // inventory
w.playerInventory = pk.Content w.serverState.playerInventory = pk.Content
} else { } else {
// save content // save content
existing, ok := w.openItemContainers[byte(pk.WindowID)] existing, ok := w.worldState.openItemContainers[byte(pk.WindowID)]
if ok { if ok {
existing.Content = pk existing.Content = pk
} }
@ -43,10 +43,10 @@ func (w *WorldState) processItemPacketsServer(pk packet.Packet) packet.Packet {
case *packet.InventorySlot: case *packet.InventorySlot:
if pk.WindowID == 0x0 { if pk.WindowID == 0x0 {
w.playerInventory[pk.Slot] = pk.NewItem w.serverState.playerInventory[pk.Slot] = pk.NewItem
} else { } else {
// save content // save content
existing, ok := w.openItemContainers[byte(pk.WindowID)] existing, ok := w.worldState.openItemContainers[byte(pk.WindowID)]
if ok { if ok {
existing.Content.Content[pk.Slot] = pk.NewItem existing.Content.Content[pk.Slot] = pk.NewItem
} }
@ -56,7 +56,7 @@ func (w *WorldState) processItemPacketsServer(pk packet.Packet) packet.Packet {
case *packet.ContainerClose: case *packet.ContainerClose:
// find container info // find container info
existing, ok := w.openItemContainers[byte(pk.WindowID)] existing, ok := w.worldState.openItemContainers[byte(pk.WindowID)]
switch pk.WindowID { switch pk.WindowID {
case protocol.WindowIDArmour: // todo handle case protocol.WindowIDArmour: // todo handle
@ -88,11 +88,11 @@ func (w *WorldState) processItemPacketsServer(pk packet.Packet) packet.Packet {
} }
// put into subchunk // put into subchunk
nbts := w.blockNBT[cp] nbts := w.worldState.blockNBT[cp]
for i, v := range nbts { for i, v := range nbts {
NBTPos := protocol.BlockPos{v["x"].(int32), v["y"].(int32), v["z"].(int32)} NBTPos := protocol.BlockPos{v["x"].(int32), v["y"].(int32), v["z"].(int32)}
if NBTPos == pos { if NBTPos == pos {
w.blockNBT[cp][i]["Items"] = nbtconv.InvToNBT(inv) w.worldState.blockNBT[cp][i]["Items"] = nbtconv.InvToNBT(inv)
break break
} }
} }
@ -100,7 +100,7 @@ func (w *WorldState) processItemPacketsServer(pk packet.Packet) packet.Packet {
w.proxy.SendMessage(locale.Loc("saved_block_inv", nil)) w.proxy.SendMessage(locale.Loc("saved_block_inv", nil))
// remove it again // remove it again
delete(w.openItemContainers, byte(pk.WindowID)) delete(w.worldState.openItemContainers, byte(pk.WindowID))
} }
case *packet.ItemComponent: case *packet.ItemComponent:
@ -113,7 +113,7 @@ func (w *WorldState) processItemPacketsServer(pk packet.Packet) packet.Packet {
return pk return pk
} }
func (w *WorldState) processItemPacketsClient(pk packet.Packet, forward *bool) packet.Packet { func (w *worldsServer) processItemPacketsClient(pk packet.Packet, forward *bool) packet.Packet {
switch pk := pk.(type) { switch pk := pk.(type) {
case *packet.ItemStackRequest: case *packet.ItemStackRequest:
var requests []protocol.ItemStackRequest var requests []protocol.ItemStackRequest
@ -184,15 +184,15 @@ func stackToItem(it protocol.ItemStack) item.Stack {
return nbtconv.ReadItem(it.NBTData, &s) return nbtconv.ReadItem(it.NBTData, &s)
} }
func (w *WorldState) playerData() (ret map[string]any) { func (w *worldsServer) playerData() (ret map[string]any) {
ret = map[string]any{ ret = map[string]any{
"format_version": "1.12.0", "format_version": "1.12.0",
"identifier": "minecraft:player", "identifier": "minecraft:player",
} }
if len(w.playerInventory) > 0 { if len(w.serverState.playerInventory) > 0 {
inv := inventory.New(len(w.playerInventory), nil) inv := inventory.New(len(w.serverState.playerInventory), nil)
for i, ii := range w.playerInventory { for i, ii := range w.serverState.playerInventory {
inv.SetItem(i, stackToItem(ii.Stack)) inv.SetItem(i, stackToItem(ii.Stack))
} }
ret["Inventory"] = nbtconv.InvToNBT(inv) ret["Inventory"] = nbtconv.InvToNBT(inv)
@ -373,8 +373,8 @@ func (w *WorldState) playerData() (ret map[string]any) {
} }
ret["Rotation"] = []float32{ ret["Rotation"] = []float32{
w.PlayerPos.Pitch, w.serverState.PlayerPos.Pitch,
w.PlayerPos.Yaw, w.serverState.PlayerPos.Yaw,
} }
return return

View File

@ -79,10 +79,10 @@ type MapUI struct {
l sync.RWMutex l sync.RWMutex
ticker *time.Ticker ticker *time.Ticker
w *WorldState w *worldsServer
} }
func NewMapUI(w *WorldState) *MapUI { func NewMapUI(w *worldsServer) *MapUI {
m := &MapUI{ m := &MapUI{
img: image.NewRGBA(image.Rect(0, 0, 128, 128)), img: image.NewRGBA(image.Rect(0, 0, 128, 128)),
zoomLevel: 16, zoomLevel: 16,
@ -198,8 +198,8 @@ func (m *MapUI) Redraw() {
m.l.Unlock() m.l.Unlock()
middle := protocol.ChunkPos{ middle := protocol.ChunkPos{
int32(m.w.PlayerPos.Position.X()), int32(m.w.serverState.PlayerPos.Position.X()),
int32(m.w.PlayerPos.Position.Z()), int32(m.w.serverState.PlayerPos.Position.Z()),
} }
chunksPerLine := float64(128 / m.zoomLevel) chunksPerLine := float64(128 / m.zoomLevel)
@ -227,7 +227,7 @@ func (m *MapUI) Redraw() {
min, max := m.GetBounds() min, max := m.GetBounds()
m.w.gui.Message(messages.UpdateMap, messages.UpdateMapPayload{ m.w.gui.Message(messages.UpdateMap, messages.UpdateMapPayload{
ChunkCount: len(m.renderedChunks), ChunkCount: len(m.renderedChunks),
Rotation: m.w.PlayerPos.Yaw, Rotation: m.w.serverState.PlayerPos.Yaw,
UpdatedTiles: updatedChunks, UpdatedTiles: updatedChunks,
Tiles: m.renderedChunks, Tiles: m.renderedChunks,
BoundsMin: min, BoundsMin: min,
@ -265,14 +265,14 @@ func (m *MapUI) SetChunk(pos protocol.ChunkPos, ch *chunk.Chunk, complete bool)
m.SchedRedraw() m.SchedRedraw()
} }
func (w *WorldState) ProcessAnimate(pk *packet.Animate) { func (w *worldsServer) ProcessAnimate(pk *packet.Animate) {
if pk.ActionType == packet.AnimateActionSwingArm { if pk.ActionType == packet.AnimateActionSwingArm {
w.mapUI.ChangeZoom() w.mapUI.ChangeZoom()
w.proxy.SendPopup(locale.Loc("zoom_level", locale.Strmap{"Level": w.mapUI.zoomLevel})) w.proxy.SendPopup(locale.Loc("zoom_level", locale.Strmap{"Level": w.mapUI.zoomLevel}))
} }
} }
func (w *WorldState) processMapPacketsClient(pk packet.Packet, forward *bool) packet.Packet { func (w *worldsServer) processMapPacketsClient(pk packet.Packet, forward *bool) packet.Packet {
switch pk := pk.(type) { switch pk := pk.(type) {
case *packet.MovePlayer: case *packet.MovePlayer:
w.SetPlayerPos(pk.Position, pk.Pitch, pk.Yaw, pk.HeadYaw) w.SetPlayerPos(pk.Position, pk.Pitch, pk.Yaw, pk.HeadYaw)

View File

@ -1,7 +1,6 @@
package world package world
import ( import (
"bytes"
"context" "context"
"encoding/json" "encoding/json"
"flag" "flag"
@ -41,53 +40,69 @@ type TPlayerPos struct {
// the state used for drawing and saving // the state used for drawing and saving
type WorldState struct { type worldSettings struct {
// settings
voidGen bool
withPacks bool
saveImage bool
blockUpdates bool
}
type worldState struct {
Dim world.Dimension
chunks map[protocol.ChunkPos]*chunk.Chunk
blockNBT map[protocol.SubChunkPos][]map[string]any
entities map[uint64]*entityState
openItemContainers map[byte]*itemContainer
Name string
}
type serverState struct {
ChunkRadius int
ispre118 bool
worldCounter int
playerInventory []protocol.ItemInstance
PlayerPos TPlayerPos
Name string
}
type worldsServer struct {
ctx context.Context ctx context.Context
proxy *utils.ProxyContext proxy *utils.ProxyContext
mapUI *MapUI mapUI *MapUI
gui utils.UI gui utils.UI
bp *behaviourpack.BehaviourPack bp *behaviourpack.BehaviourPack
// save state worldState worldState
ChunkRadius int serverState serverState
chunks map[protocol.ChunkPos]*chunk.Chunk settings worldSettings
blockNBT map[protocol.SubChunkPos][]map[string]any
openItemContainers map[byte]*itemContainer
playerInventory []protocol.ItemInstance
entities map[uint64]*entityState
Dim world.Dimension
PlayerPos TPlayerPos
worldCounter int
WorldName string
ServerName string
ispre118 bool
// settings
voidGen bool
withPacks bool
saveImage bool
experimentInventory bool
blockUpdates bool
} }
func NewWorldState(ctx context.Context, proxy *utils.ProxyContext, ServerName string, ui utils.UI) *WorldState { func NewWorldsServer(ctx context.Context, proxy *utils.ProxyContext, ServerName string, ui utils.UI) *worldsServer {
w := &WorldState{ w := &worldsServer{
ctx: ctx, ctx: ctx,
proxy: proxy, proxy: proxy,
mapUI: nil, mapUI: nil,
gui: ui, gui: ui,
bp: behaviourpack.New(ServerName), bp: behaviourpack.New(ServerName),
ServerName: ServerName,
chunks: make(map[protocol.ChunkPos]*chunk.Chunk), serverState: serverState{
blockNBT: make(map[protocol.SubChunkPos][]map[string]any), ispre118: false,
openItemContainers: make(map[byte]*itemContainer), worldCounter: 0,
entities: make(map[uint64]*entityState), ChunkRadius: 0,
Dim: nil,
WorldName: "world", playerInventory: nil,
PlayerPos: TPlayerPos{}, PlayerPos: TPlayerPos{},
Name: ServerName,
},
settings: worldSettings{},
} }
w.mapUI = NewMapUI(w) w.mapUI = NewMapUI(w)
w.Reset(w.CurrentName())
w.gui.Message(messages.Init, messages.InitPayload{ w.gui.Message(messages.Init, messages.InitPayload{
Handler: w.uiMessage, Handler: w.uiMessage,
@ -118,11 +133,10 @@ func init() {
} }
type WorldCMD struct { type WorldCMD struct {
ServerAddress string ServerAddress string
Packs bool Packs bool
EnableVoid bool EnableVoid bool
SaveImage bool SaveImage bool
ExperimentInventory bool
} }
func (*WorldCMD) Name() string { return "worlds" } func (*WorldCMD) Name() string { return "worlds" }
@ -133,7 +147,6 @@ func (c *WorldCMD) SetFlags(f *flag.FlagSet) {
f.BoolVar(&c.Packs, "packs", false, locale.Loc("save_packs_with_world", nil)) f.BoolVar(&c.Packs, "packs", false, locale.Loc("save_packs_with_world", nil))
f.BoolVar(&c.EnableVoid, "void", true, locale.Loc("enable_void", nil)) f.BoolVar(&c.EnableVoid, "void", true, locale.Loc("enable_void", nil))
f.BoolVar(&c.SaveImage, "image", false, locale.Loc("save_image", nil)) f.BoolVar(&c.SaveImage, "image", false, locale.Loc("save_image", nil))
f.BoolVar(&c.ExperimentInventory, "inv", false, locale.Loc("test_block_inv", nil))
} }
func (c *WorldCMD) Execute(ctx context.Context, ui utils.UI) error { func (c *WorldCMD) Execute(ctx context.Context, ui utils.UI) error {
@ -147,11 +160,12 @@ func (c *WorldCMD) Execute(ctx context.Context, ui utils.UI) error {
return err return err
} }
w := NewWorldState(ctx, proxy, hostname, ui) w := NewWorldsServer(ctx, proxy, hostname, ui)
w.voidGen = c.EnableVoid w.settings = worldSettings{
w.withPacks = c.Packs voidGen: c.EnableVoid,
w.saveImage = c.SaveImage withPacks: c.Packs,
w.experimentInventory = c.ExperimentInventory saveImage: c.SaveImage,
}
proxy.AlwaysGetPacks = true proxy.AlwaysGetPacks = true
proxy.GameDataModifier = func(gd *minecraft.GameData) { proxy.GameDataModifier = func(gd *minecraft.GameData) {
@ -172,7 +186,7 @@ func (c *WorldCMD) Execute(ctx context.Context, ui utils.UI) error {
// from server // from server
switch pk := pk.(type) { switch pk := pk.(type) {
case *packet.ChunkRadiusUpdated: case *packet.ChunkRadiusUpdated:
w.ChunkRadius = int(pk.ChunkRadius) w.serverState.ChunkRadius = int(pk.ChunkRadius)
pk.ChunkRadius = 80 pk.ChunkRadius = 80
} }
pk = w.processItemPacketsServer(pk) pk = w.processItemPacketsServer(pk)
@ -196,7 +210,7 @@ func (c *WorldCMD) Execute(ctx context.Context, ui utils.UI) error {
return nil return nil
} }
func (w *WorldState) uiMessage(name string, data interface{}) messages.MessageResponse { func (w *worldsServer) uiMessage(name string, data interface{}) messages.MessageResponse {
r := messages.MessageResponse{ r := messages.MessageResponse{
Ok: false, Ok: false,
Data: nil, Data: nil,
@ -212,24 +226,25 @@ func (w *WorldState) uiMessage(name string, data interface{}) messages.MessageRe
return r return r
} }
func (w *WorldState) SetPlayerPos(Position mgl32.Vec3, Pitch, Yaw, HeadYaw float32) { func (w *worldsServer) SetPlayerPos(Position mgl32.Vec3, Pitch, Yaw, HeadYaw float32) {
last := w.PlayerPos last := w.serverState.PlayerPos
w.PlayerPos = TPlayerPos{ current := TPlayerPos{
Position: Position, Position: Position,
Pitch: Pitch, Pitch: Pitch,
Yaw: Yaw, Yaw: Yaw,
HeadYaw: HeadYaw, HeadYaw: HeadYaw,
} }
w.serverState.PlayerPos = current
if int(last.Position.X()) != int(w.PlayerPos.Position.X()) || int(last.Position.Z()) != int(w.PlayerPos.Position.Z()) { if int(last.Position.X()) != int(current.Position.X()) || int(last.Position.Z()) != int(current.Position.Z()) {
w.mapUI.SchedRedraw() w.mapUI.SchedRedraw()
} }
} }
func (w *WorldState) setVoidGen(val bool, fromUI bool) bool { func (w *worldsServer) setVoidGen(val bool, fromUI bool) bool {
w.voidGen = val w.settings.voidGen = val
var s string var s string
if w.voidGen { if w.settings.voidGen {
s = locale.Loc("void_generator_true", nil) s = locale.Loc("void_generator_true", nil)
} else { } else {
s = locale.Loc("void_generator_false", nil) s = locale.Loc("void_generator_false", nil)
@ -238,68 +253,79 @@ func (w *WorldState) setVoidGen(val bool, fromUI bool) bool {
if !fromUI { if !fromUI {
w.gui.Message(messages.SetVoidGen, messages.SetVoidGenPayload{ w.gui.Message(messages.SetVoidGen, messages.SetVoidGenPayload{
Value: w.voidGen, Value: w.settings.voidGen,
}) })
} }
return true return true
} }
func (w *WorldState) setWorldName(val string, fromUI bool) bool { func (w *worldsServer) setWorldName(val string, fromUI bool) bool {
w.WorldName = val w.worldState.Name = val
w.proxy.SendMessage(locale.Loc("worldname_set", locale.Strmap{"Name": w.WorldName})) w.proxy.SendMessage(locale.Loc("worldname_set", locale.Strmap{"Name": w.worldState.Name}))
if !fromUI { if !fromUI {
w.gui.Message(messages.SetWorldName, messages.SetWorldNamePayload{ w.gui.Message(messages.SetWorldName, messages.SetWorldNamePayload{
WorldName: w.WorldName, WorldName: w.worldState.Name,
}) })
} }
return true return true
} }
func (w *WorldState) Reset() { func (w *worldsServer) CurrentName() string {
w.chunks = make(map[protocol.ChunkPos]*chunk.Chunk) worldName := "world"
w.entities = make(map[uint64]*entityState) if w.serverState.worldCounter > 1 {
w.blockNBT = make(map[protocol.SubChunkPos][]map[string]any) worldName = fmt.Sprintf("world-%d", w.serverState.worldCounter)
w.openItemContainers = make(map[byte]*itemContainer) }
w.WorldName = fmt.Sprintf("world-%d", w.worldCounter) return worldName
}
func (w *worldsServer) Reset(newName string) {
w.worldState = worldState{
Dim: nil,
chunks: make(map[protocol.ChunkPos]*chunk.Chunk),
blockNBT: make(map[protocol.SubChunkPos][]map[string]any),
entities: make(map[uint64]*entityState),
openItemContainers: make(map[byte]*itemContainer),
Name: newName,
}
w.mapUI.Reset() w.mapUI.Reset()
} }
// SaveAndReset writes the world to a folder, resets all the chunks // SaveAndReset writes the world to a folder, resets all the chunks
func (w *WorldState) SaveAndReset() { func (w *worldsServer) SaveAndReset() {
// cull empty chunks // cull empty chunks
keys := make([]protocol.ChunkPos, 0, len(w.chunks)) keys := make([]protocol.ChunkPos, 0, len(w.worldState.chunks))
for cp := range w.chunks { for cp := range w.worldState.chunks {
keys = append(keys, cp) keys = append(keys, cp)
} }
for _, cp := range keys { for _, cp := range keys {
has_any := false has_any := false
for _, sc := range w.chunks[cp].Sub() { for _, sc := range w.worldState.chunks[cp].Sub() {
has_any = !sc.Empty() has_any = !sc.Empty()
if has_any { if has_any {
break break
} }
} }
if !has_any { if !has_any {
delete(w.chunks, cp) delete(w.worldState.chunks, cp)
} }
} }
if len(w.chunks) == 0 { if len(w.worldState.chunks) == 0 {
w.Reset() w.Reset(w.CurrentName())
return return
} }
logrus.Infof(locale.Loc("saving_world", locale.Strmap{"Name": w.WorldName, "Count": len(w.chunks)})) logrus.Infof(locale.Loc("saving_world", locale.Strmap{"Name": w.worldState.Name, "Count": len(w.worldState.chunks)}))
w.gui.Message(messages.SavingWorld, messages.SavingWorldPayload{ w.gui.Message(messages.SavingWorld, messages.SavingWorldPayload{
Name: w.WorldName, Name: w.worldState.Name,
Chunks: len(w.chunks), Chunks: len(w.worldState.chunks),
}) })
// open world // open world
folder := path.Join("worlds", fmt.Sprintf("%s/%s", w.ServerName, w.WorldName)) folder := path.Join("worlds", fmt.Sprintf("%s/%s", w.serverState.Name, w.worldState.Name))
os.RemoveAll(folder) os.RemoveAll(folder)
os.MkdirAll(folder, 0o777) os.MkdirAll(folder, 0o777)
@ -309,18 +335,18 @@ func (w *WorldState) SaveAndReset() {
} }
// save chunk data // save chunk data
for cp, c := range w.chunks { for cp, c := range w.worldState.chunks {
provider.SaveChunk((world.ChunkPos)(cp), c, w.Dim) provider.SaveChunk((world.ChunkPos)(cp), c, w.worldState.Dim)
} }
// save block nbt data // save block nbt data
blockNBT := make(map[world.ChunkPos][]map[string]any) blockNBT := make(map[world.ChunkPos][]map[string]any)
for scp, v := range w.blockNBT { // 3d to 2d for scp, v := range w.worldState.blockNBT { // 3d to 2d
cp := world.ChunkPos{scp.X(), scp.Z()} cp := world.ChunkPos{scp.X(), scp.Z()}
blockNBT[cp] = append(blockNBT[cp], v...) blockNBT[cp] = append(blockNBT[cp], v...)
} }
for cp, v := range blockNBT { for cp, v := range blockNBT {
err = provider.SaveBlockNBT(cp, v, w.Dim) err = provider.SaveBlockNBT(cp, v, w.worldState.Dim)
if err != nil { if err != nil {
logrus.Error(err) logrus.Error(err)
} }
@ -328,30 +354,30 @@ func (w *WorldState) SaveAndReset() {
// save entities // save entities
chunkEntities := make(map[world.ChunkPos][]world.Entity) chunkEntities := make(map[world.ChunkPos][]world.Entity)
for _, es := range w.entities { for _, es := range w.worldState.entities {
cp := world.ChunkPos{int32(es.Position.X()) >> 4, int32(es.Position.Z()) >> 4} cp := world.ChunkPos{int32(es.Position.X()) >> 4, int32(es.Position.Z()) >> 4}
chunkEntities[cp] = append(chunkEntities[cp], es.ToServerEntity()) chunkEntities[cp] = append(chunkEntities[cp], es.ToServerEntity())
} }
for cp, v := range chunkEntities { for cp, v := range chunkEntities {
err = provider.SaveEntities(cp, v, w.Dim) err = provider.SaveEntities(cp, v, w.worldState.Dim)
if err != nil { if err != nil {
logrus.Error(err) 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()) err = provider.SaveLocalPlayerData(w.playerData())
if err != nil { if err != nil {
logrus.Error(err) logrus.Error(err)
} }
playerPos := w.proxy.Server.GameData().PlayerPosition
spawnPos := cube.Pos{int(playerPos.X()), int(playerPos.Y()), int(playerPos.Z())}
// write metadata // write metadata
s := provider.Settings() s := provider.Settings()
s.Spawn = spawnPos s.Spawn = spawnPos
s.Name = w.WorldName s.Name = w.worldState.Name
// set gamerules // set gamerules
ld := provider.LevelDat() ld := provider.LevelDat()
@ -427,7 +453,7 @@ func (w *WorldState) SaveAndReset() {
ld.RandomSeed = int64(gd.WorldSeed) ld.RandomSeed = int64(gd.WorldSeed)
// void world // void world
if w.voidGen { if w.settings.voidGen {
ld.FlatWorldLayers = `{"biome_id":1,"block_layers":[{"block_data":0,"block_id":0,"count":1},{"block_data":0,"block_id":0,"count":2},{"block_data":0,"block_id":0,"count":1}],"encoding_version":3,"structure_options":null}` ld.FlatWorldLayers = `{"biome_id":1,"block_layers":[{"block_data":0,"block_id":0,"count":1},{"block_data":0,"block_id":0,"count":2},{"block_data":0,"block_id":0,"count":1}],"encoding_version":3,"structure_options":null}`
ld.Generator = 2 ld.Generator = 2
} }
@ -446,7 +472,7 @@ func (w *WorldState) SaveAndReset() {
logrus.Error(err) logrus.Error(err)
} }
w.worldCounter += 1 w.serverState.worldCounter += 1
type dep struct { type dep struct {
PackID string `json:"pack_id"` PackID string `json:"pack_id"`
@ -467,7 +493,7 @@ func (w *WorldState) SaveAndReset() {
// save behaviourpack // save behaviourpack
if w.bp.HasContent() { if w.bp.HasContent() {
name := strings.ReplaceAll(w.ServerName, "./", "") name := strings.ReplaceAll(w.serverState.Name, "./", "")
name = strings.ReplaceAll(name, "/", "-") name = strings.ReplaceAll(name, "/", "-")
packFolder := path.Join(folder, "behavior_packs", name) packFolder := path.Join(folder, "behavior_packs", name)
os.MkdirAll(packFolder, 0o755) os.MkdirAll(packFolder, 0o755)
@ -484,16 +510,16 @@ func (w *WorldState) SaveAndReset() {
}}) }})
// force resource packs for worlds with custom blocks // force resource packs for worlds with custom blocks
w.withPacks = true w.settings.withPacks = true
} }
// add resource packs // add resource packs
if w.withPacks { if w.settings.withPacks {
packs, err := utils.GetPacks(w.proxy.Server) packs, err := utils.GetPacks(w.proxy.Server)
if err != nil { if err != nil {
logrus.Error(err) logrus.Error(err)
} else { } else {
//var rdeps []dep var rdeps []dep
for k, p := range packs { for k, p := range packs {
if p.Encrypted() && !p.CanDecrypt() { if p.Encrypted() && !p.CanDecrypt() {
logrus.Warnf("Cant add %s, it is encrypted", p.Name()) logrus.Warnf("Cant add %s, it is encrypted", p.Name())
@ -502,17 +528,14 @@ func (w *WorldState) SaveAndReset() {
logrus.Infof(locale.Loc("adding_pack", locale.Strmap{"Name": k})) logrus.Infof(locale.Loc("adding_pack", locale.Strmap{"Name": k}))
packFolder := path.Join(folder, "resource_packs", p.Name()) packFolder := path.Join(folder, "resource_packs", p.Name())
os.MkdirAll(packFolder, 0o755) os.MkdirAll(packFolder, 0o755)
data := make([]byte, p.Len()) utils.UnpackZip(p, int64(p.Len()), packFolder)
p.ReadAt(data, 0)
utils.UnpackZip(bytes.NewReader(data), int64(len(data)), packFolder)
/* rdeps = append(rdeps, dep{
rdeps = append(rdeps, dep{ PackID: p.Manifest().Header.UUID,
PackID: p.Manifest().Header.UUID, Version: p.Manifest().Header.Version,
Version: p.Manifest().Header.Version, })
})
*/
} }
_ = rdeps
/* /*
if len(rdeps) > 0 { if len(rdeps) > 0 {
addPacksJSON("world_resource_packs.json", rdeps) addPacksJSON("world_resource_packs.json", rdeps)
@ -521,7 +544,7 @@ func (w *WorldState) SaveAndReset() {
} }
} }
if w.saveImage { if w.settings.saveImage {
f, _ := os.Create(folder + ".png") f, _ := os.Create(folder + ".png")
png.Encode(f, w.mapUI.ToImage()) png.Encode(f, w.mapUI.ToImage())
f.Close() f.Close()
@ -532,25 +555,27 @@ func (w *WorldState) SaveAndReset() {
// zip it // zip it
filename := folder + ".mcworld" filename := folder + ".mcworld"
if err := utils.ZipFolder(filename, folder); err != nil { if err := utils.ZipFolder(filename, folder); err != nil {
fmt.Println(err) logrus.Error(err)
} }
logrus.Info(locale.Loc("saved", locale.Strmap{"Name": filename})) logrus.Info(locale.Loc("saved", locale.Strmap{"Name": filename}))
//os.RemoveAll(folder) //os.RemoveAll(folder)
w.Reset() w.Reset(w.CurrentName())
w.gui.Message(messages.SetUIState, messages.UIStateMain) w.gui.Message(messages.SetUIState, messages.UIStateMain)
} }
func (w *WorldState) OnConnect(err error) bool { func (w *worldsServer) OnConnect(err error) bool {
w.gui.Message(messages.SetUIState, messages.UIStateMain) w.gui.Message(messages.SetUIState, messages.UIStateMain)
if err != nil { if err != nil {
return false return false
} }
gd := w.proxy.Server.GameData() gd := w.proxy.Server.GameData()
w.ChunkRadius = int(gd.ChunkRadius) w.serverState.ChunkRadius = int(gd.ChunkRadius)
w.proxy.ClientWritePacket(&packet.ChunkRadiusUpdated{
ChunkRadius: 80,
})
world.InsertCustomItems(gd.Items) world.InsertCustomItems(gd.Items)
for _, ie := range gd.Items { for _, ie := range gd.Items {
w.bp.AddItem(ie) w.bp.AddItem(ie)
} }
@ -576,18 +601,18 @@ func (w *WorldState) OnConnect(err error) bool {
if len(gv) > 1 { if len(gv) > 1 {
var ver int var ver int
ver, err = strconv.Atoi(gv[1]) ver, err = strconv.Atoi(gv[1])
w.ispre118 = ver < 18 w.serverState.ispre118 = ver < 18
} }
if err != nil || len(gv) <= 1 { if err != nil || len(gv) <= 1 {
logrus.Info(locale.Loc("guessing_version", nil)) logrus.Info(locale.Loc("guessing_version", nil))
} }
dimensionID := gd.Dimension dimensionID := gd.Dimension
if w.ispre118 { if w.serverState.ispre118 {
logrus.Info(locale.Loc("using_under_118", nil)) logrus.Info(locale.Loc("using_under_118", nil))
dimensionID += 10 dimensionID += 10
} }
w.Dim = dimensionIDMap[uint8(dimensionID)] w.worldState.Dim = dimensionIDMap[uint8(dimensionID)]
} }
w.proxy.SendMessage(locale.Loc("use_setname", nil)) w.proxy.SendMessage(locale.Loc("use_setname", nil))
@ -611,7 +636,7 @@ func (w *WorldState) OnConnect(err error) bool {
w.proxy.AddCommand(utils.IngameCommand{ w.proxy.AddCommand(utils.IngameCommand{
Exec: func(cmdline []string) bool { Exec: func(cmdline []string) bool {
return w.setVoidGen(!w.voidGen, false) return w.setVoidGen(!w.settings.voidGen, false)
}, },
Cmd: protocol.Command{ Cmd: protocol.Command{
Name: "void", Name: "void",
@ -620,9 +645,5 @@ func (w *WorldState) OnConnect(err error) bool {
}) })
w.mapUI.Start() w.mapUI.Start()
w.proxy.ClientWritePacket(&packet.ChunkRadiusUpdated{
ChunkRadius: 80,
})
return true return true
} }

View File

@ -2,7 +2,6 @@ package utils
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"io" "io"
"net" "net"
@ -18,8 +17,6 @@ import (
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
) )
var pool = packet.NewPool()
var MutedPackets = []string{ var MutedPackets = []string{
"packet.UpdateBlock", "packet.UpdateBlock",
"packet.MoveActorAbsolute", "packet.MoveActorAbsolute",
@ -49,9 +46,8 @@ var MutedPackets = []string{
} }
var ( var (
ExtraVerbose []string FLog io.Writer
FLog io.Writer dmpLock sync.Mutex
dmpLock sync.Mutex
) )
func dmpStruct(level int, inputStruct any, withType bool, isInList bool) (s string) { func dmpStruct(level int, inputStruct any, withType bool, isInList bool) (s string) {
@ -63,11 +59,14 @@ func dmpStruct(level int, inputStruct any, withType bool, isInList bool) (s stri
ii := reflect.Indirect(reflect.ValueOf(inputStruct)) ii := reflect.Indirect(reflect.ValueOf(inputStruct))
typeName := reflect.TypeOf(inputStruct).String() typeName := reflect.TypeOf(inputStruct).String()
if typeName == "[]interface {}" {
typeName = "[]any"
}
typeString := "" typeString := ""
if withType { if withType {
if slices.Contains([]string{"bool", "string"}, typeName) { if slices.Contains([]string{"bool", "string"}, typeName) {
} else { } else {
typeString = typeName + " " typeString = typeName
} }
} }
@ -84,7 +83,8 @@ func dmpStruct(level int, inputStruct any, withType bool, isInList bool) (s stri
return return
} }
if ii.Kind() == reflect.Struct { switch ii.Kind() {
case reflect.Struct:
if ii.NumField() == 0 { if ii.NumField() == 0 {
s += typeName + "{}" s += typeName + "{}"
} else { } else {
@ -93,30 +93,24 @@ func dmpStruct(level int, inputStruct any, withType bool, isInList bool) (s stri
fieldType := ii.Type().Field(i) fieldType := ii.Type().Field(i)
if fieldType.IsExported() { if fieldType.IsExported() {
field := ii.Field(i).Interface() s += fmt.Sprintf("%s\t%s: %s,\n", tBase, fieldType.Name, dmpStruct(level+1, ii.Field(i).Interface(), true, false))
d := dmpStruct(level+1, field, true, false)
s += tBase + fmt.Sprintf("\t%s = %s\n", fieldType.Name, d)
} else { } else {
s += tBase + " " + fieldType.Name + " (unexported)" s += tBase + " " + fieldType.Name + " (unexported)"
} }
} }
s += tBase + "}" s += tBase + "}"
} }
} else if ii.Kind() == reflect.Slice { case reflect.Slice:
var t reflect.Type s += typeName + "{"
is_elem_struct := false
if ii.Len() > 0 {
e := ii.Index(0)
t = reflect.TypeOf(e.Interface())
is_elem_struct = t.Kind() == reflect.Struct
}
if ii.Len() > 1000 { if ii.Len() > 1000 {
s += "[<slice too long>]" s += "<slice too long>"
} else if ii.Len() == 0 { } else if ii.Len() == 0 {
s += fmt.Sprintf("[0%s", typeName[1:])
} else { } else {
s += fmt.Sprintf("[%d%s{", ii.Len(), typeString[1:]) e := ii.Index(0)
t := reflect.TypeOf(e.Interface())
is_elem_struct := t.Kind() == reflect.Struct
if is_elem_struct { if is_elem_struct {
s += "\n" s += "\n"
} }
@ -124,32 +118,55 @@ func dmpStruct(level int, inputStruct any, withType bool, isInList bool) (s stri
if is_elem_struct { if is_elem_struct {
s += tBase + "\t" s += tBase + "\t"
} }
s += dmpStruct(level+1, ii.Index(i).Interface(), false, true) s += dmpStruct(level+1, ii.Index(i).Interface(), false, true) + ","
if is_elem_struct { if is_elem_struct {
s += "\n" s += "\n"
} else { } else {
s += " " if i != ii.Len()-1 {
s += " "
}
} }
} }
if is_elem_struct { if is_elem_struct {
s += tBase s += tBase
} }
s += "]"
} }
} else if ii.Kind() == reflect.Map { s += "}"
j, err := json.MarshalIndent(ii.Interface(), tBase, "\t") case reflect.Map:
if err != nil { it := reflect.TypeOf(inputStruct)
s += err.Error() valType := it.Elem().String()
if valType == "interface {}" {
valType = "any"
} }
s += string(j) keyType := it.Key().String()
} else {
s += fmt.Sprintf("map[%s]%s{", keyType, valType)
if ii.Len() > 0 {
s += "\n"
}
iter := ii.MapRange()
for iter.Next() {
k := iter.Key()
v := iter.Value()
s += fmt.Sprintf("%s\t%#v: %s,\n", tBase, k.Interface(), dmpStruct(level+1, v.Interface(), true, false))
}
if ii.Len() > 0 {
s += tBase
}
s += "}"
default:
is_array := ii.Kind() == reflect.Array is_array := ii.Kind() == reflect.Array
if !isInList && !is_array { add_type := !isInList && !is_array && len(typeString) > 0
s += typeString if add_type {
s += typeString + "("
} }
s += fmt.Sprintf("%#v", ii.Interface()) s += fmt.Sprintf("%#v", ii.Interface())
if add_type {
s += ")"
}
} }
return s return s
} }
@ -163,17 +180,18 @@ func DumpStruct(data interface{}) {
} }
var ClientAddr net.Addr var ClientAddr net.Addr
var pool = packet.NewPool()
func PacketLogger(header packet.Header, payload []byte, src, dst net.Addr) { func PacketLogger(header packet.Header, payload []byte, src, dst net.Addr) {
if header.PacketID == packet.IDRequestNetworkSettings {
ClientAddr = src
}
var pk packet.Packet var pk packet.Packet
if pkFunc, ok := pool[header.PacketID]; ok { if pkFunc, ok := pool[header.PacketID]; ok {
pk = pkFunc() pk = pkFunc()
} else { } else {
pk = &packet.Unknown{PacketID: header.PacketID} pk = &packet.Unknown{PacketID: header.PacketID, Payload: payload}
}
if pk.ID() == packet.IDRequestNetworkSettings {
ClientAddr = src
} }
defer func() { defer func() {
@ -186,9 +204,8 @@ func PacketLogger(header packet.Header, payload []byte, src, dst net.Addr) {
if FLog != nil { if FLog != nil {
dmpLock.Lock() dmpLock.Lock()
defer dmpLock.Unlock() FLog.Write([]byte(dmpStruct(0, pk, true, false) + "\n\n\n"))
FLog.Write([]byte(dmpStruct(0, pk, true, false))) dmpLock.Unlock()
FLog.Write([]byte("\n\n\n"))
} }
pkName := reflect.TypeOf(pk).String()[1:] pkName := reflect.TypeOf(pk).String()[1:]
@ -217,8 +234,4 @@ func PacketLogger(header packet.Header, payload []byte, src, dst net.Addr) {
} }
logrus.Debugf("%s 0x%02x, %s", dir, pk.ID(), pkName) logrus.Debugf("%s 0x%02x, %s", dir, pk.ID(), pkName)
if slices.Contains(ExtraVerbose, pkName) {
logrus.Debugf("%+v", pk)
}
} }

View File

@ -2,6 +2,7 @@
package utils package utils
import ( import (
"bufio"
"bytes" "bytes"
"context" "context"
"crypto/aes" "crypto/aes"
@ -207,11 +208,6 @@ func InitExtraDebug(ctx context.Context) {
logPlain, err := os.Create("packets.log") logPlain, err := os.Create("packets.log")
if err != nil { if err != nil {
logrus.Error(err) logrus.Error(err)
} else {
go func() {
<-ctx.Done()
logPlain.Close()
}()
} }
// open gpg log // open gpg log
@ -219,24 +215,30 @@ func InitExtraDebug(ctx context.Context) {
if err != nil { if err != nil {
logrus.Error(err) logrus.Error(err)
} else { } else {
go func() {
<-ctx.Done()
logCrypt.Close()
}()
// encrypter for the log // encrypter for the log
logCryptEnc, err = crypt.Encer("packets.log", logCrypt) logCryptEnc, err = crypt.Encer("packets.log", logCrypt)
if err != nil { if err != nil {
logrus.Error(err) logrus.Error(err)
} else {
go func() {
<-ctx.Done()
logCryptEnc.Close()
}()
} }
} }
FLog = io.MultiWriter(logPlain, logCryptEnc) b := bufio.NewWriter(io.MultiWriter(logPlain, logCryptEnc))
FLog = b
if err != nil { if err != nil {
logrus.Error(err) logrus.Error(err)
} }
go func() {
<-ctx.Done()
b.Flush()
if logPlain != nil {
logPlain.Close()
}
if logCrypt != nil {
logCrypt.Close()
}
if logCryptEnc != nil {
logCryptEnc.Close()
}
}()
} }

View File

@ -2,6 +2,7 @@ package utils
import ( import (
"archive/zip" "archive/zip"
"compress/flate"
"io" "io"
"io/fs" "io/fs"
"os" "os"
@ -36,6 +37,12 @@ func ZipFolder(filename, folder string) error {
logrus.Fatal(err) logrus.Fatal(err)
} }
zw := zip.NewWriter(f) zw := zip.NewWriter(f)
// Register a custom Deflate compressor.
zw.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) {
return flate.NewWriter(out, flate.NoCompression)
})
err = filepath.WalkDir(folder, func(path string, d fs.DirEntry, err error) error { err = filepath.WalkDir(folder, func(path string, d fs.DirEntry, err error) error {
if !d.Type().IsDir() { if !d.Type().IsDir() {
rel := path[len(folder)+1:] rel := path[len(folder)+1:]