From 05f5ffebdf58d8da1e0e13bb09130977a2ba62d3 Mon Sep 17 00:00:00 2001 From: oq Date: Sun, 1 Sep 2024 18:06:02 +0300 Subject: [PATCH] ticked movement (untested) --- .gitignore | 4 +- go.mod | 2 +- main.go | 12 ++- protocol/nbt/qnbt/primitive.go | 4 - protocol/nbt/qnbt/qnbt_test.go | 6 ++ protocol/nbt/qnbt/struct.go | 5 +- protocol/nbt/qnbt/testdata/cpu.prof | Bin 0 -> 8281 bytes protocol/nbt/qnbt/testdata/mem.prof | Bin 3698 -> 3221 bytes protocol/net/conn.go | 52 ++++++++++- protocol/net/io/io.go | 34 ++++++- server/command/builder.go | 43 +++++++++ server/player/atomic.go | 44 +++++++++ server/player/player.go | 114 +++++++++++------------ server/plugin.go | 12 +++ server/server.go | 2 +- server/session/std/handler.go | 11 +-- server/session/std/handler/movement.go | 68 ++------------ server/session/std/handler/useItemOn.go | 2 +- server/session/std/session.go | 10 +- server/session/std/tick.go | 116 ++++++++++++++++++++++++ server/world/level/playerdata.go | 12 +-- util/console/console.go | 13 +++ util/unit.go | 74 +++++++++++++++ 23 files changed, 492 insertions(+), 148 deletions(-) create mode 100644 protocol/nbt/qnbt/testdata/cpu.prof create mode 100644 server/player/atomic.go create mode 100644 server/session/std/tick.go create mode 100644 util/unit.go diff --git a/.gitignore b/.gitignore index bc4f570..647d0a4 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,6 @@ server.properties data.qfg /blockstates server-icon.png -/resources \ No newline at end of file +/resources +/server/session/atomicSlice.go +/server/session/a_test.go \ No newline at end of file diff --git a/go.mod b/go.mod index ac03362..9bfe96f 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/zeppelinmc/zeppelin -go 1.22.5 +go 1.23 require ( github.com/fatih/color v1.17.0 diff --git a/main.go b/main.go index 3524dc4..e514ce1 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "net/http" "os" "runtime" + "runtime/debug" "runtime/pprof" "slices" "strconv" @@ -17,6 +18,7 @@ import ( "github.com/zeppelinmc/zeppelin/server/command" "github.com/zeppelinmc/zeppelin/server/world" "github.com/zeppelinmc/zeppelin/server/world/chunk/section" + "github.com/zeppelinmc/zeppelin/util" "github.com/zeppelinmc/zeppelin/util/console" "github.com/zeppelinmc/zeppelin/util/log" "golang.org/x/term" @@ -26,11 +28,19 @@ var timeStart = time.Now() func main() { log.Infolnf("Zeppelin 1.21 Minecraft server with %s on platform %s-%s", runtime.Version(), runtime.GOOS, runtime.GOARCH) - if !loadStates() { return } + max, ok := console.GetFlag("xmem") + if ok { + m, err := util.ParseSizeUnit(max) + if err == nil { + debug.SetMemoryLimit(int64(m)) + log.Infolnf("Memory usage is limited to %s", util.FormatSizeUnit(m)) + } + } + if slices.Index(os.Args, "--cpuprof") != -1 { if f, err := os.Create("zeppelin-cpu-profile"); err == nil { pprof.StartCPUProfile(f) diff --git a/protocol/nbt/qnbt/primitive.go b/protocol/nbt/qnbt/primitive.go index 32efa4b..dcb3cb1 100644 --- a/protocol/nbt/qnbt/primitive.go +++ b/protocol/nbt/qnbt/primitive.go @@ -247,10 +247,6 @@ func (d *Decoder) decodeIntArray(len int, ptr unsafe.Pointer) error { return err } - if !native.SystemBigEndian { - native.Convert32(unsafe.Slice((*byte)(ptr), 4)) - } - ptr = unsafe.Add(ptr, 4) } diff --git a/protocol/nbt/qnbt/qnbt_test.go b/protocol/nbt/qnbt/qnbt_test.go index d64afca..dc46f5a 100644 --- a/protocol/nbt/qnbt/qnbt_test.go +++ b/protocol/nbt/qnbt/qnbt_test.go @@ -103,6 +103,9 @@ type chunk struct { } func BenchmarkXxx(b *testing.B) { + f1, _ := os.Create("testdata/cpu.prof") + pprof.StartCPUProfile(f1) + for i := 0; i < b.N; i++ { Unmarshal(chunkData, &chunk{}) } @@ -110,4 +113,7 @@ func BenchmarkXxx(b *testing.B) { f, _ := os.Create("testdata/mem.prof") pprof.Lookup("allocs").WriteTo(f, 0) f.Close() + + pprof.StopCPUProfile() + f1.Close() } diff --git a/protocol/nbt/qnbt/struct.go b/protocol/nbt/qnbt/struct.go index 172c4cc..df546ac 100644 --- a/protocol/nbt/qnbt/struct.go +++ b/protocol/nbt/qnbt/struct.go @@ -18,11 +18,11 @@ var structs = sync.Pool{ func newStruct(t *structType, ptr unsafe.Pointer) *struct_t { s := structs.Get().(*struct_t) + clear(s.names) + s.t = t s.ptr = ptr - clear(s.names) - for i, f := range t.fields { if !f.name.exported() { continue @@ -68,6 +68,7 @@ type struct_t struct { func (s *struct_t) field(n string) (f *structField, ptr unsafe.Pointer, ok bool) { i, ok := s.names[n] + if !ok { return nil, nil, ok } diff --git a/protocol/nbt/qnbt/testdata/cpu.prof b/protocol/nbt/qnbt/testdata/cpu.prof new file mode 100644 index 0000000000000000000000000000000000000000..a3fa826d8c2e391ce489bb6d6b218cca36464a3c GIT binary patch literal 8281 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 X$D@oGI^q8Z00960gscN|2uA<_qjvx- literal 0 HcmV?d00001 diff --git a/protocol/nbt/qnbt/testdata/mem.prof b/protocol/nbt/qnbt/testdata/mem.prof index 3544a19619ac778c642e7cb15809e652b64b7f6d..27e9d63c740d5a2689dce1c920d2a58554dd5909 100644 GIT binary patch literal 3221 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 H!5aVocG^sU literal 3698 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 32 { + return value, fmt.Errorf("VarInt is too big") + } + } + + return value, nil +} + func AppendVarLong(data []byte, value int64) []byte { var ( CONTINUE_BIT int64 = 128 diff --git a/server/command/builder.go b/server/command/builder.go index 0371580..04670f5 100644 --- a/server/command/builder.go +++ b/server/command/builder.go @@ -56,6 +56,12 @@ const ( UUID ) +const ( + StringSingleWord = iota + StringQuotablePhrase + StringGreedyPhrase +) + type Node struct { play.Node children []Node @@ -89,3 +95,40 @@ func NewBoolArgument(name string, nodes ...Node) Node { children: nodes, } } + +func NewIntegerArgument(name string, min, max *int32, nodes ...Node) Node { + flags := byte(0) + + var m, x int32 + + if min != nil { + flags &= 0x01 + m = *min + } + if max != nil { + flags &= 0x02 + x = *max + } + + return Node{ + Node: play.Node{ + Flags: play.NodeArgument, + Name: name, + ParserId: Integer, + Properties: []any{flags, m, x}, + }, + children: nodes, + } +} + +func NewStringArgument(name string, typ int32, nodes ...Node) Node { + return Node{ + Node: play.Node{ + Flags: play.NodeArgument, + Name: name, + ParserId: Integer, + Properties: []any{typ}, + }, + children: nodes, + } +} diff --git a/server/player/atomic.go b/server/player/atomic.go new file mode 100644 index 0000000..6fcbab2 --- /dev/null +++ b/server/player/atomic.go @@ -0,0 +1,44 @@ +package player + +import ( + "sync/atomic" + "unsafe" +) + +func i64f(i int64) float64 { + return *(*float64)(unsafe.Pointer(&i)) +} +func f64i(f float64) int64 { + return *(*int64)(unsafe.Pointer(&f)) +} + +func i32f(i int32) float32 { + return *(*float32)(unsafe.Pointer(&i)) +} +func f32i(f float32) int32 { + return *(*int32)(unsafe.Pointer(&f)) +} + +func atomicFloat64(f float64) *atomic.Int64 { + var v *atomic.Int64 + v.Store(f64i(f)) + + return v +} +func atomicFloat32(f float32) *atomic.Int32 { + return atomicInt32(f32i(f)) +} + +func atomicInt32(i int32) *atomic.Int32 { + var v *atomic.Int32 + v.Store(i) + + return v +} + +func atomicBool(b bool) *atomic.Bool { + var v *atomic.Bool + v.Store(b) + + return v +} diff --git a/server/player/player.go b/server/player/player.go index 5dac3b9..761769e 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -4,6 +4,7 @@ import ( "maps" "slices" "sync" + a "sync/atomic" "github.com/google/uuid" "github.com/zeppelinmc/zeppelin/protocol/net/metadata" @@ -19,16 +20,16 @@ var _ entity.LivingEntity = (*Player)(nil) type Player struct { entityId int32 - data level.PlayerData - x, y, z atomic.AtomicValue[float64] - vx, vy, vz atomic.AtomicValue[float64] - yaw, pitch atomic.AtomicValue[float32] - onGround atomic.AtomicValue[bool] + data level.Player + x, y, z, + vx, vy, vz a.Int64 + yaw, pitch a.Int32 + onGround a.Bool - health atomic.AtomicValue[float32] - food atomic.AtomicValue[int32] - foodExhaustion atomic.AtomicValue[float32] - foodSaturation atomic.AtomicValue[float32] + health, + food, + foodExhaustion, + foodSaturation a.Int32 abilities atomic.AtomicValue[level.PlayerAbilities] @@ -36,7 +37,7 @@ type Player struct { gameMode atomic.AtomicValue[level.GameMode] - selectedItemSlot atomic.AtomicValue[int32] + selectedItemSlot a.Int32 recipeBook atomic.AtomicValue[level.RecipeBook] @@ -50,7 +51,7 @@ type Player struct { } // looks up a player in the cache or creates one if not found -func (mgr *PlayerManager) New(data level.PlayerData) *Player { +func (mgr *PlayerManager) New(data level.Player) *Player { if p, ok := mgr.lookup(data.UUID.UUID()); ok { return p } @@ -82,18 +83,18 @@ func (mgr *PlayerManager) New(data level.PlayerData) *Player { metadata.PlayerMainHandIndex: metadata.Byte(1), }, - x: atomic.Value(data.Pos[0]), - y: atomic.Value(data.Pos[1]), - z: atomic.Value(data.Pos[2]), + x: *atomicFloat64(data.Pos[0]), + y: *atomicFloat64(data.Pos[1]), + z: *atomicFloat64(data.Pos[2]), - vx: atomic.Value(data.Motion[0]), - vy: atomic.Value(data.Motion[1]), - vz: atomic.Value(data.Motion[2]), + vx: *atomicFloat64(data.Motion[0]), + vy: *atomicFloat64(data.Motion[1]), + vz: *atomicFloat64(data.Motion[2]), - yaw: atomic.Value(data.Rotation[0]), - pitch: atomic.Value(data.Rotation[1]), + yaw: *atomicFloat32(data.Rotation[0]), + pitch: *atomicFloat32(data.Rotation[1]), - onGround: atomic.Value(data.OnGround), + onGround: *atomicBool(data.OnGround), dimension: atomic.Value(data.Dimension), @@ -101,12 +102,12 @@ func (mgr *PlayerManager) New(data level.PlayerData) *Player { recipeBook: atomic.Value(data.RecipeBook), - selectedItemSlot: atomic.Value(data.SelectedItemSlot), + selectedItemSlot: *atomicInt32(data.SelectedItemSlot), - health: atomic.Value(data.Health), - food: atomic.Value(data.FoodLevel), - foodExhaustion: atomic.Value(data.FoodExhaustionLevel), - foodSaturation: atomic.Value(data.FoodSaturationLevel), + health: *atomicFloat32(data.Health), + food: *atomicInt32(data.FoodLevel), + foodExhaustion: *atomicFloat32(data.FoodExhaustionLevel), + foodSaturation: *atomicFloat32(data.FoodSaturationLevel), abilities: atomic.Value(data.Abilities), @@ -130,40 +131,40 @@ func (p *Player) UUID() uuid.UUID { } func (p *Player) Position() (x, y, z float64) { - return p.x.Get(), p.y.Get(), p.z.Get() + return i64f(p.x.Load()), i64f(p.y.Load()), i64f(p.z.Load()) } func (p *Player) Rotation() (yaw, pitch float32) { - return p.yaw.Get(), p.pitch.Get() + return i32f(p.yaw.Load()), i32f(p.pitch.Load()) } func (p *Player) OnGround() bool { - return p.onGround.Get() + return p.onGround.Load() } func (p *Player) SetPosition(x, y, z float64) { - p.x.Set(x) - p.y.Set(y) - p.z.Set(z) + p.x.Store(f64i(x)) + p.y.Store(f64i(y)) + p.z.Store(f64i(z)) } func (p *Player) SetRotation(yaw, pitch float32) { - p.yaw.Set(yaw) - p.pitch.Set(pitch) + p.yaw.Store(f32i(yaw)) + p.pitch.Store(f32i(pitch)) } func (p *Player) SetOnGround(val bool) { - p.onGround.Set(val) + p.onGround.Store(val) } func (p *Player) Motion() (x, y, z float64) { - return p.vx.Get(), p.vy.Get(), p.vz.Get() + return i64f(p.vx.Load()), i64f(p.vy.Load()), i64f(p.vz.Load()) } func (p *Player) SetMotion(x, y, z float64) { - p.vx.Set(x) - p.vy.Set(y) - p.vz.Set(z) + p.vx.Store(f64i(x)) + p.vy.Store(f64i(y)) + p.vz.Store(f64i(z)) } func (p *Player) EntityId() int32 { @@ -212,35 +213,35 @@ func (p *Player) SetDimension(dim string) { } func (p *Player) Health() float32 { - return p.health.Get() + return i32f(p.health.Load()) } func (p *Player) SetHealth(h float32) { - p.health.Set(h) + p.health.Store(f32i(h)) } func (p *Player) Food() int32 { - return p.food.Get() + return p.food.Load() } func (p *Player) SetFood(f int32) { - p.food.Set(f) + p.food.Store(f) } func (p *Player) FoodSaturation() float32 { - return p.foodSaturation.Get() + return i32f(p.foodSaturation.Load()) } func (p *Player) SetFoodSaturation(fs float32) { - p.foodSaturation.Set(fs) + p.foodSaturation.Store(f32i(fs)) } func (p *Player) FoodExhaustion() float32 { - return p.foodExhaustion.Get() + return i32f(p.foodExhaustion.Load()) } func (p *Player) SetFoodExhaustion(fh float32) { - p.foodExhaustion.Set(fh) + p.foodExhaustion.Store(f32i(fh)) } func (p *Player) Abilities() level.PlayerAbilities { @@ -310,7 +311,7 @@ func (p *Player) Inventory() *container.Container { // if negative, returns 0 and if over 8, returns 8 func (p *Player) SelectedItemSlot() int32 { - slot := p.selectedItemSlot.Get() + slot := p.selectedItemSlot.Load() if slot < 0 { slot = 0 } @@ -328,31 +329,32 @@ func (p *Player) SetSelectedItemSlot(slot int32) { if slot > 8 { slot = 8 } - p.selectedItemSlot.Set(slot) + p.selectedItemSlot.Store(slot) } func (p *Player) sync() { x, y, z := p.Position() yaw, pitch := p.Rotation() + vx, vy, vz := p.Motion() p.data.Abilities = p.abilities.Get() p.data.Pos = [3]float64{x, y, z} p.data.Rotation = [2]float32{yaw, pitch} - p.data.OnGround = p.onGround.Get() + p.data.OnGround = p.onGround.Load() p.data.Dimension = p.dimension.Get() p.data.Inventory = *p.inventory p.data.RecipeBook = p.recipeBook.Get() + p.data.Motion = [3]float64{vx, vy, vz} + p.att_mu.RLock() p.data.Attributes = p.attributes p.att_mu.RUnlock() - p.data.Health = p.health.Get() - p.data.FoodLevel = p.food.Get() - p.data.FoodExhaustionLevel = p.foodExhaustion.Get() - p.data.FoodSaturationLevel = p.foodSaturation.Get() + p.data.Health = i32f(p.health.Load()) + p.data.FoodLevel = p.food.Load() + p.data.FoodExhaustionLevel = i32f(p.foodExhaustion.Load()) + p.data.FoodSaturationLevel = i32f(p.foodSaturation.Load()) p.data.PlayerGameType = p.gameMode.Get() - p.data.SelectedItemSlot = p.selectedItemSlot.Get() - - //TODO motion(velocity), xp, etc + p.data.SelectedItemSlot = p.selectedItemSlot.Load() } diff --git a/server/plugin.go b/server/plugin.go index d50214a..3e203d2 100644 --- a/server/plugin.go +++ b/server/plugin.go @@ -9,6 +9,8 @@ import ( ) type Plugin struct { + basePluginsPath string + srv *Server Identifier string @@ -16,6 +18,15 @@ type Plugin struct { Unload func(*Plugin) } +func (p Plugin) FS() fs.FS { + return os.DirFS(p.Dir()) +} + +// Dir returns the base directory for the plugin (plugins/) +func (p Plugin) Dir() string { + return p.basePluginsPath + "/" + p.Identifier +} + func (p Plugin) Server() *Server { return p.srv } @@ -48,6 +59,7 @@ func (srv *Server) loadPlugin(name string) { log.Errorlnf("Invalid plugin export for %s", name) return } + plugin.basePluginsPath = "plugins" plugin.srv = srv plugin.OnLoad(plugin) } diff --git a/server/server.go b/server/server.go index fe58f21..1b9e578 100644 --- a/server/server.go +++ b/server/server.go @@ -130,7 +130,7 @@ func (srv *Server) Properties() properties.ServerProperties { } func (srv *Server) Start(ts time.Time) { - if slices.Index(os.Args, "--no-plugins") != -1 { + if slices.Index(os.Args, "--no-plugins") == -1 { if runtime.GOOS == "darwin" || runtime.GOOS == "linux" || runtime.GOOS == "freebsd" { log.Infoln("Loading plugins") srv.loadPlugins() diff --git a/server/session/std/handler.go b/server/session/std/handler.go index f6a0b65..7fe76d9 100644 --- a/server/session/std/handler.go +++ b/server/session/std/handler.go @@ -12,8 +12,8 @@ import ( "github.com/zeppelinmc/zeppelin/util/log" ) -var PacketReadInterceptor func(s *StandardSession, pk packet.Decodeable, stop *bool) -var PacketWriteInterceptor func(s *StandardSession, pk packet.Encodeable, stop *bool) +var PacketReadInterceptor func(s *StandardSession, pk packet.Decodeable) bool +var PacketWriteInterceptor func(s *StandardSession, pk packet.Encodeable) bool type handler func(*StandardSession, packet.Decodeable) @@ -28,7 +28,7 @@ func (session *StandardSession) inConfiguration() bool { func (session *StandardSession) readIntercept(pk packet.Decodeable) (stop bool) { if PacketReadInterceptor != nil { - PacketReadInterceptor(session, pk, &stop) + return PacketReadInterceptor(session, pk) } return @@ -36,7 +36,6 @@ func (session *StandardSession) readIntercept(pk packet.Decodeable) (stop bool) func (session *StandardSession) handlePackets() { keepAlive := time.NewTicker(time.Second * 20) - //ticker := session.tick.New() for { select { case <-keepAlive.C: @@ -72,12 +71,12 @@ func (session *StandardSession) handlePackets() { session.broadcast.PlayerInfoUpdateSession(session) case *configuration.ServerboundPluginMessage: if pk.Channel == "minecraft:brand" { - _, data, _ := io.ReadVarInt(pk.Data) + _, data, _ := io.VarInt(pk.Data) session.clientName = string(data) } case *play.ServerboundPluginMessage: if pk.Channel == "minecraft:brand" { - _, data, _ := io.ReadVarInt(pk.Data) + _, data, _ := io.VarInt(pk.Data) session.clientName = string(data) } case *configuration.AcknowledgeFinishConfiguration: diff --git a/server/session/std/handler/movement.go b/server/session/std/handler/movement.go index e4ecc3e..cda4e85 100644 --- a/server/session/std/handler/movement.go +++ b/server/session/std/handler/movement.go @@ -1,13 +1,10 @@ package handler import ( - "math" - "github.com/zeppelinmc/zeppelin/protocol/net" "github.com/zeppelinmc/zeppelin/protocol/net/packet" "github.com/zeppelinmc/zeppelin/protocol/net/packet/play" "github.com/zeppelinmc/zeppelin/server/session/std" - "github.com/zeppelinmc/zeppelin/util/log" ) func init() { @@ -17,71 +14,22 @@ func init() { std.RegisterHandler(net.PlayState, play.PacketIdSetPlayerOnGround, handleMovement) } -func chunkPos(x, z float64) (cx, cz int32) { - return int32(math.Floor(x / 16)), int32(math.Floor(z / 16)) -} - func handleMovement(s *std.StandardSession, p packet.Decodeable) { if s.AwaitingTeleportAcknowledgement.Get() { return } switch pk := p.(type) { case *play.SetPlayerPosition: - oldX, oldY, oldZ := s.Player().Position() - oldChunkPosX, oldChunkPosZ := chunkPos(oldX, oldZ) - newChunkPosX, newChunkPosZ := chunkPos(pk.X, pk.Z) - - if oldChunkPosX != newChunkPosX || oldChunkPosZ != newChunkPosZ { - s.Conn().WritePacket(&play.SetCenterChunk{ChunkX: newChunkPosX, ChunkZ: newChunkPosZ}) - s.ChunkLoadWorker.SendChunksRadius(newChunkPosX, newChunkPosZ) - } - - yaw, pitch := s.Player().Rotation() - - distance := math.Sqrt((pk.X-oldX)*(pk.X-oldX) + (pk.Y-oldY)*(pk.Y-oldY) + (pk.Z-oldZ)*(pk.Z-oldZ)) - - if distance > 100 { - s.SynchronizePosition(oldX, oldY, oldZ, yaw, pitch) - log.Infof("%s moved too quickly! (%f %f %f)\n", s.Username(), pk.X-oldX, pk.Y-oldY, pk.Z-oldZ) - return - } - - s.Broadcast().BroadcastPlayerMovement(s, pk.X, pk.Y, pk.Z, yaw, pitch) - - s.Player().SetPosition(pk.X, pk.Y, pk.Z) - s.Player().SetOnGround(pk.OnGround) + s.Input.SetPosition(pk.X, pk.Y, pk.Z) + s.Input.SetOnGround(pk.OnGround) case *play.SetPlayerPositionAndRotation: - oldX, oldY, oldZ := s.Player().Position() - oldChunkPosX, oldChunkPosZ := chunkPos(oldX, oldZ) - newChunkPosX, newChunkPosZ := chunkPos(pk.X, pk.Z) - - if oldChunkPosX != newChunkPosX || oldChunkPosZ != newChunkPosZ { - s.Conn().WritePacket(&play.SetCenterChunk{ChunkX: newChunkPosX, ChunkZ: newChunkPosZ}) - s.ChunkLoadWorker.SendChunksRadius(newChunkPosX, newChunkPosZ) - } - - distance := math.Sqrt((pk.X-oldX)*(pk.X-oldX) + (pk.Y-oldY)*(pk.Y-oldY) + (pk.Z-oldZ)*(pk.Z-oldZ)) - - if distance > 100 { - s.SynchronizePosition(oldX, oldY, oldZ, pk.Yaw, pk.Pitch) - log.Infof("%s moved too quickly! (%f %f %f)\n", s.Username(), pk.X-oldX, pk.Y-oldY, pk.Z-oldZ) - return - } - - s.Broadcast().BroadcastPlayerMovement(s, pk.X, pk.Y, pk.Z, pk.Yaw, pk.Pitch) - - s.Player().SetPosition(pk.X, pk.Y, pk.Z) - s.Player().SetRotation(pk.Yaw, pk.Pitch) - s.Player().SetOnGround(pk.OnGround) + s.Input.SetPosition(pk.X, pk.Y, pk.Z) + s.Input.SetRotation(pk.Yaw, pk.Pitch) + s.Input.SetOnGround(pk.OnGround) case *play.SetPlayerRotation: - // you can never rotate too much :) - x, y, z := s.Player().Position() - - s.Broadcast().BroadcastPlayerMovement(s, x, y, z, pk.Yaw, pk.Pitch) - - s.Player().SetRotation(pk.Yaw, pk.Pitch) - s.Player().SetOnGround(pk.OnGround) + s.Input.SetRotation(pk.Yaw, pk.Pitch) + s.Input.SetOnGround(pk.OnGround) case *play.SetPlayerOnGround: - s.Player().SetOnGround(pk.OnGround) + s.Input.SetOnGround(pk.OnGround) } } diff --git a/server/session/std/handler/useItemOn.go b/server/session/std/handler/useItemOn.go index 57a006f..b3819d9 100644 --- a/server/session/std/handler/useItemOn.go +++ b/server/session/std/handler/useItemOn.go @@ -96,7 +96,7 @@ func handleUseItemOn(s *std.StandardSession, pk packet.Decodeable) { blockSet = blockSet.New(map[string]string{"axis": axis, "facing": facing}) - s.Conn().WritePacket(&play.AcknowledgeBlockChange{SequenceId: use.Sequence}) + s.WritePacket(&play.AcknowledgeBlockChange{SequenceId: use.Sequence}) pos := pos.New(blockX, blockY, blockZ) dimension.SetBlock(pos, blockSet, true) diff --git a/server/session/std/session.go b/server/session/std/session.go index f46a608..fa1ca84 100644 --- a/server/session/std/session.go +++ b/server/session/std/session.go @@ -46,6 +46,7 @@ type StandardSession struct { broadcast *session.Broadcast config properties.ServerProperties + Input Input ChunkLoadWorker *ChunkLoadWorker conn *net.Conn @@ -121,6 +122,10 @@ func New( } s.ChunkLoadWorker = NewChunkLoadWorker(s) + s.Input.SetPosition(s.player.Position()) + s.Input.SetRotation(s.player.Rotation()) + s.Input.SetOnGround(s.player.OnGround()) + return s } @@ -130,9 +135,7 @@ func (session *StandardSession) CommandManager() *command.Manager { func (session *StandardSession) WritePacket(pk packet.Encodeable) error { if PacketWriteInterceptor != nil { - var stop bool - PacketWriteInterceptor(session, pk, &stop) - if stop { + if PacketWriteInterceptor(session, pk) { return nil } } @@ -388,6 +391,7 @@ func (session *StandardSession) login() error { } session.broadcast.SpawnPlayer(session) + session.createTicker() return nil } diff --git a/server/session/std/tick.go b/server/session/std/tick.go new file mode 100644 index 0000000..4520c06 --- /dev/null +++ b/server/session/std/tick.go @@ -0,0 +1,116 @@ +package std + +import ( + "math" + "sync/atomic" + + "github.com/zeppelinmc/zeppelin/protocol/net/packet/play" + "github.com/zeppelinmc/zeppelin/util/log" +) + +func chunkPos(x, z float64) (cx, cz int32) { + return int32(math.Floor(x / 16)), int32(math.Floor(z / 16)) +} + +type Input struct { + x, y, z atomic.Uint64 + yaw, pitch atomic.Uint32 + onGround atomic.Bool +} + +func (i *Input) SetPosition(x, y, z float64) { + i.x.Store(math.Float64bits(x)) + i.y.Store(math.Float64bits(y)) + i.z.Store(math.Float64bits(z)) +} + +func (i *Input) SetRotation(yaw, pitch float32) { + i.yaw.Store(math.Float32bits(yaw)) + i.pitch.Store(math.Float32bits(pitch)) +} + +func (i *Input) SetOnGround(b bool) { + i.onGround.Store(b) +} + +func (i *Input) Position() (x, y, z float64) { + return math.Float64frombits(i.x.Load()), math.Float64frombits(i.y.Load()), math.Float64frombits(i.z.Load()) +} + +func (i *Input) Rotation() (yaw, pitch float32) { + return math.Float32frombits(i.yaw.Load()), math.Float32frombits(i.pitch.Load()) +} + +func (i *Input) OnGround() bool { + return i.onGround.Load() +} + +func (session *StandardSession) createTicker() { + ticker := session.tick.New() + go func() { + for range ticker.C { + session.processInput() + } + }() +} + +func (session *StandardSession) processInput() { + x, y, z, yaw, pitch, onGround := session.input() + oldX, oldY, oldZ, oldYaw, oldPitch, oldOnGround := session.state() + + posC, rotC, onC := session.inputUpdated(x, y, z, yaw, pitch, onGround, oldX, oldY, oldZ, oldYaw, oldPitch, oldOnGround) + if !posC && !rotC && !onC { + return + } + + if posC { + oldChunkPosX, oldChunkPosZ := chunkPos(oldX, oldZ) + newChunkPosX, newChunkPosZ := chunkPos(x, z) + + if oldChunkPosX != newChunkPosX || oldChunkPosZ != newChunkPosZ { + session.WritePacket(&play.SetCenterChunk{ChunkX: newChunkPosX, ChunkZ: newChunkPosZ}) + session.ChunkLoadWorker.SendChunksRadius(newChunkPosX, newChunkPosZ) + } + + distance := math.Sqrt((x-oldX)*(x-oldX) + (y-oldY)*(y-oldY) + (z-oldZ)*(z-oldZ)) + + if distance > 100 { + session.SynchronizePosition(oldX, oldY, oldZ, oldYaw, oldPitch) + log.Infof("%s moved too quickly! (%f %f %f)\n", session.Username(), x-oldX, y-oldY, z-oldZ) + return + } + defer session.player.SetPosition(x, y, z) + } + if rotC { + defer session.player.SetRotation(yaw, pitch) + } + if onC { + defer session.player.SetOnGround(onGround) + } + + session.broadcast.BroadcastPlayerMovement(session, x, y, z, yaw, pitch) +} + +func (session *StandardSession) inputUpdated( + x, y, z float64, yaw, pitch float32, onGround bool, + oldX, oldY, oldZ float64, oldYaw, oldPitch float32, oldOnGround bool, +) (posChanged, rotChanged, onGroundChanged bool) { + + return x != oldX || y != oldY || z != oldZ, yaw != oldYaw || pitch != oldPitch, onGround != oldOnGround +} + +func (session *StandardSession) input() (x, y, z float64, yaw, pitch float32, onGround bool) { + x, y, z = session.Input.Position() + yaw, pitch = session.Input.Rotation() + onGround = session.Input.OnGround() + + return +} + +func (session *StandardSession) state() (oldX, oldY, oldZ float64, oldYaw, oldPitch float32, oldOnGround bool) { + oldX, oldY, oldZ = session.player.Position() + oldYaw, oldPitch = session.player.Rotation() + oldOnGround = session.player.OnGround() + + return +} diff --git a/server/world/level/playerdata.go b/server/world/level/playerdata.go index c1dbdd6..bbb5672 100644 --- a/server/world/level/playerdata.go +++ b/server/world/level/playerdata.go @@ -15,7 +15,7 @@ import ( datauuid "github.com/zeppelinmc/zeppelin/server/world/level/uuid" ) -type PlayerData struct { +type Player struct { // the path of this playerdata file, not a field in the nbt path string `nbt:"-"` // the base path of the world @@ -88,7 +88,7 @@ type PlayerData struct { } `nbt:"warden_spawn_tracker"` } -func (data *PlayerData) Save() error { +func (data *Player) Save() error { os.MkdirAll(data.basePath+"/playerdata", 0755) os.Rename(data.path, data.path+"_old") file, err := os.Create(data.path) @@ -107,8 +107,8 @@ func (data *PlayerData) Save() error { return file.Close() } -func (w *Level) PlayerData(uuid string) (PlayerData, error) { - var playerData PlayerData +func (w *Level) PlayerData(uuid string) (Player, error) { + var playerData Player path := fmt.Sprintf("%s/playerdata/%s.dat", w.basePath, uuid) file, err := os.Open(path) @@ -131,8 +131,8 @@ func (w *Level) PlayerData(uuid string) (PlayerData, error) { return playerData, err } -func (w *Level) NewPlayerData(uuid uuid.UUID) PlayerData { - return PlayerData{ +func (w *Level) NewPlayerData(uuid uuid.UUID) Player { + return Player{ path: fmt.Sprintf("%s/playerdata/%s.dat", w.basePath, uuid), basePath: w.basePath, Pos: [3]float64{float64(w.Data.SpawnX), float64(w.Data.SpawnY), float64(w.Data.SpawnZ)}, diff --git a/util/console/console.go b/util/console/console.go index 5c5ffec..bce4c85 100644 --- a/util/console/console.go +++ b/util/console/console.go @@ -10,6 +10,19 @@ import ( "github.com/zeppelinmc/zeppelin/server" ) +func GetFlag(name string) (string, bool) { + name = "--" + name + "=" + for _, a := range os.Args { + if i := strings.Index(a, name); i == 0 { + if len(name)+i < len(a) { + return a[len(name)+i:], true + } + } + } + + return "", false +} + func StartConsole(srv *server.Server) { var line string var scanner = bufio.NewScanner(os.Stdin) diff --git a/util/unit.go b/util/unit.go new file mode 100644 index 0000000..6392227 --- /dev/null +++ b/util/unit.go @@ -0,0 +1,74 @@ +package util + +import ( + "fmt" + "strconv" + "strings" +) + +var sizeUnits = map[string]uint64{ + "kb": 1000, // kilobyte (decimal) + "kib": 1024, // kibibyte (binary) + "mb": 1000 * 1000, // megabyte (decimal) + "mib": 1024 * 1024, // mebibyte (binary) + "gb": 1000 * 1000 * 1000, // gigabyte (decimal) + "gib": 1024 * 1024 * 1024, // gibibyte (binary) + "tb": 1000 * 1000 * 1000 * 1000, // terabyte (decimal) + "tib": 1024 * 1024 * 1024 * 1024, // tebibyte (binary) +} + +// ParseSizeUnit parses a size string into a number of bytes. +func ParseSizeUnit(sizeStr string) (uint64, error) { + // Trim any surrounding whitespace and convert to lowercase. + sizeStr = strings.TrimSpace(strings.ToLower(sizeStr)) + + // Find the position where the letters start. + for unit := range sizeUnits { + if strings.HasSuffix(sizeStr, unit) { + // Extract the numeric part of the string. + numberStr := strings.TrimSuffix(sizeStr, unit) + number, err := strconv.ParseUint(numberStr, 10, 64) + if err != nil { + return 0, fmt.Errorf("invalid number format: %w", err) + } + + // Convert the number to bytes using the corresponding unit. + return number * sizeUnits[unit], nil + } + } + + number, err := strconv.ParseUint(sizeStr, 10, 64) + return number, err +} + +var sizeUnitsS = []struct { + Unit string + Factor uint64 +}{ + {"B", 1}, + {"KiB", 1024}, + {"MiB", 1024 * 1024}, + {"GiB", 1024 * 1024 * 1024}, + {"TiB", 1024 * 1024 * 1024 * 1024}, + {"PiB", 1024 * 1024 * 1024 * 1024 * 1024}, +} + +// FormatSizeUnit formats a number of bytes into a human-readable string. +func FormatSizeUnit(bytes uint64) string { + if bytes == 0 { + return "0 B" + } + + var result strings.Builder + for i := len(sizeUnitsS) - 1; i >= 0; i-- { + unit := sizeUnitsS[i] + if bytes >= unit.Factor { + value := float64(bytes) / float64(unit.Factor) + result.WriteString(fmt.Sprintf("%.2f %s", value, unit.Unit)) + return result.String() + } + } + + // This should never be reached since we handle all sizes + return "0 B" +}