From d83eda4c60bd5b09beb25de7f1cf184852ed6f3a Mon Sep 17 00:00:00 2001 From: Seb Date: Mon, 2 Sep 2024 11:09:05 +0100 Subject: [PATCH] dragonfly: Migrate from df-mc/atomic to sync/atomic (#915) --- go.mod | 1 - go.sum | 4 - server/block/ender_chest.go | 8 +- server/player/player.go | 234 +++++++++--------- server/server.go | 4 +- server/session/handler_anvil.go | 2 +- server/session/handler_beacon.go | 2 +- server/session/handler_enchanting.go | 2 +- server/session/handler_grindstone.go | 2 +- server/session/handler_item_stack_request.go | 6 +- server/session/handler_loom.go | 2 +- server/session/handler_modal_form_response.go | 2 +- server/session/handler_player_auth_input.go | 2 +- .../handler_server_bound_loading_screen.go | 2 +- server/session/player.go | 30 +-- server/session/session.go | 24 +- server/session/text.go | 15 +- server/session/world.go | 24 +- server/world/conf.go | 6 +- server/world/settings.go | 2 +- server/world/world.go | 10 +- 21 files changed, 201 insertions(+), 183 deletions(-) diff --git a/go.mod b/go.mod index 6de57f6c3..f44fdcf50 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ toolchain go1.22.1 require ( github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9 github.com/cespare/xxhash/v2 v2.2.0 - github.com/df-mc/atomic v1.10.0 github.com/df-mc/goleveldb v1.1.9 github.com/df-mc/worldupgrader v1.0.16 github.com/go-gl/mathgl v1.1.0 diff --git a/go.sum b/go.sum index 3ea662d61..ddf78eaaf 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,6 @@ github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -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/goleveldb v1.1.9 h1:ihdosZyy5jkQKrxucTQmN90jq/2lUwQnJZjIYIC/9YU= github.com/df-mc/goleveldb v1.1.9/go.mod h1:+NHCup03Sci5q84APIA21z3iPZCuk6m6ABtg4nANCSk= github.com/df-mc/worldupgrader v1.0.16 h1:3n9yvLFNCe8IDJnUEliTGbhDvV1frjtPX/y5zl3Q5EE= @@ -56,8 +54,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= diff --git a/server/block/ender_chest.go b/server/block/ender_chest.go index e97f6dbc5..7ad3e6622 100644 --- a/server/block/ender_chest.go +++ b/server/block/ender_chest.go @@ -1,13 +1,13 @@ package block import ( - "github.com/df-mc/atomic" "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" "github.com/df-mc/dragonfly/server/world/sound" "github.com/go-gl/mathgl/mgl64" + "sync/atomic" ) // enderChestOwner represents an entity that has an ender chest inventory. @@ -32,7 +32,7 @@ type EnderChest struct { // NewEnderChest creates a new initialised ender chest. func NewEnderChest() EnderChest { - return EnderChest{viewers: atomic.NewInt64(0)} + return EnderChest{} } // BreakInfo ... @@ -75,7 +75,7 @@ func (c EnderChest) Activate(pos cube.Pos, _ cube.Face, _ *world.World, u item.U // AddViewer ... func (c EnderChest) AddViewer(w *world.World, pos cube.Pos) { - if c.viewers.Inc() == 1 { + if c.viewers.Add(1) == 1 { c.open(w, pos) } } @@ -85,7 +85,7 @@ func (c EnderChest) RemoveViewer(w *world.World, pos cube.Pos) { if c.viewers.Load() == 0 { return } - if c.viewers.Dec() == 0 { + if c.viewers.Add(-1) == 0 { c.close(w, pos) } } diff --git a/server/player/player.go b/server/player/player.go index 3718a3eaa..a6230c53a 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -10,7 +10,6 @@ import ( "sync" "time" - "github.com/df-mc/atomic" "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/model" @@ -35,6 +34,7 @@ import ( "github.com/go-gl/mathgl/mgl64" "github.com/google/uuid" "golang.org/x/text/language" + "sync/atomic" ) // Player is an implementation of a player entity. It has methods that implement the behaviour that players @@ -44,20 +44,20 @@ type Player struct { uuid uuid.UUID xuid string locale language.Tag - pos, vel atomic.Value[mgl64.Vec3] - nameTag atomic.Value[string] - scoreTag atomic.Value[string] - yaw, pitch, absorptionHealth, scale atomic.Float64 + pos, vel atomic.Pointer[mgl64.Vec3] + nameTag atomic.Pointer[string] + scoreTag atomic.Pointer[string] + yaw, pitch, absorptionHealth, scale atomic.Uint64 once sync.Once - gameMode atomic.Value[world.GameMode] + gameMode atomic.Pointer[world.GameMode] - skin atomic.Value[skin.Skin] + skin atomic.Pointer[skin.Skin] // s holds the session of the player. This field should not be used directly, but instead, // Player.session() should be called. - s atomic.Value[*session.Session] + s atomic.Pointer[*session.Session] // h holds the current Handler of the player. It may be changed at any time by calling the Handle method. - h atomic.Value[Handler] + h atomic.Pointer[Handler] inv, offHand, enderChest *inventory.Inventory armour *inventory.Armour @@ -69,7 +69,7 @@ type Player struct { glideTicks atomic.Int64 fireTicks atomic.Int64 - fallDistance atomic.Float64 + fallDistance atomic.Uint64 breathing bool airSupplyTicks atomic.Int64 @@ -80,12 +80,12 @@ type Player struct { // lastTickedWorld holds the world that the player was in, in the last tick. lastTickedWorld *world.World - speed atomic.Float64 + speed atomic.Uint64 health *entity.HealthManager experience *entity.ExperienceManager effects *entity.EffectManager - lastXPPickup atomic.Value[time.Time] + lastXPPickup atomic.Pointer[time.Time] immunityTicks atomic.Int64 deathMu sync.Mutex @@ -99,7 +99,7 @@ type Player struct { collidedVertically, collidedHorizontally atomic.Bool breaking atomic.Bool - breakingPos atomic.Value[cube.Pos] + breakingPos atomic.Pointer[cube.Pos] lastBreakDuration time.Duration breakParticleCounter atomic.Uint32 @@ -118,31 +118,33 @@ func New(name string, skin skin.Skin, pos mgl64.Vec3) *Player { p.broadcastItems(slot, before, after) } }), - enderChest: inventory.New(27, nil), - uuid: uuid.New(), - offHand: inventory.New(1, p.broadcastItems), - armour: inventory.NewArmour(p.broadcastArmour), - hunger: newHungerManager(), - health: entity.NewHealthManager(20, 20), - experience: entity.NewExperienceManager(), - effects: entity.NewEffectManager(), - gameMode: *atomic.NewValue[world.GameMode](world.GameModeSurvival), - h: *atomic.NewValue[Handler](NopHandler{}), - name: name, - skin: *atomic.NewValue(skin), - speed: *atomic.NewFloat64(0.1), - nameTag: *atomic.NewValue(name), - heldSlot: atomic.NewUint32(0), - locale: language.BritishEnglish, - breathing: true, - airSupplyTicks: *atomic.NewInt64(300), - maxAirSupplyTicks: *atomic.NewInt64(300), - enchantSeed: *atomic.NewInt64(rand.Int63()), - scale: *atomic.NewFloat64(1), - pos: *atomic.NewValue(pos), - cooldowns: make(map[string]time.Time), - mc: &entity.MovementComputer{Gravity: 0.08, Drag: 0.02, DragBeforeGravity: true}, - } + enderChest: inventory.New(27, nil), + uuid: uuid.New(), + offHand: inventory.New(1, p.broadcastItems), + armour: inventory.NewArmour(p.broadcastArmour), + hunger: newHungerManager(), + health: entity.NewHealthManager(20, 20), + experience: entity.NewExperienceManager(), + effects: entity.NewEffectManager(), + name: name, + locale: language.BritishEnglish, + breathing: true, + cooldowns: make(map[string]time.Time), + mc: &entity.MovementComputer{Gravity: 0.08, Drag: 0.02, DragBeforeGravity: true}, + } + var scoreTag string + var gm world.GameMode = world.GameModeSurvival + p.gameMode.Store(&gm) + p.Handle(nil) + p.skin.Store(&skin) + p.speed.Store(math.Float64bits(0.1)) + p.nameTag.Store(&name) + p.scoreTag.Store(&scoreTag) + p.airSupplyTicks.Store(300) + p.maxAirSupplyTicks.Store(300) + p.enchantSeed.Store(rand.Int63()) + p.scale.Store(math.Float64bits(1)) + p.pos.Store(&pos) return p } @@ -153,7 +155,9 @@ func New(name string, skin skin.Skin, pos mgl64.Vec3) *Player { // you can leave the data as nil to use default data. func NewWithSession(name, xuid string, uuid uuid.UUID, skin skin.Skin, s *session.Session, pos mgl64.Vec3, data *Data) *Player { p := New(name, skin, pos) - p.s, p.uuid, p.xuid, p.skin = *atomic.NewValue(s), uuid, xuid, *atomic.NewValue(skin) + p.s.Store(&s) + p.skin.Store(&skin) + p.uuid, p.xuid = uuid, xuid p.inv, p.offHand, p.enderChest, p.armour, p.heldSlot = s.HandleInventories() p.locale, _ = language.Parse(strings.Replace(s.ClientData().LanguageCode, "_", "-", 1)) if data != nil { @@ -230,7 +234,7 @@ func (p *Player) Addr() net.Addr { // that the player is shown to. // If the player was not connected to a network session, a default skin will be set. func (p *Player) Skin() skin.Skin { - return p.skin.Load() + return *p.skin.Load() } // SetSkin changes the skin of the player. This skin will be visible to other players that the player @@ -244,7 +248,7 @@ func (p *Player) SetSkin(skin skin.Skin) { p.session().ViewSkin(p) return } - p.skin.Store(skin) + p.skin.Store(&skin) for _, v := range p.viewers() { v.ViewSkin(p) } @@ -262,7 +266,7 @@ func (p *Player) Handle(h Handler) { if h == nil { h = NopHandler{} } - p.h.Store(h) + p.h.Store(&h) } // Message sends a formatted message to the player. The message is formatted following the rules of @@ -309,7 +313,7 @@ func (p *Player) ResetFallDistance() { // FallDistance returns the player's fall distance. func (p *Player) FallDistance() float64 { - return p.fallDistance.Load() + return math.Float64frombits(p.fallDistance.Load()) } // SendTitle sends a title to the player. The title may be configured to change the duration it is displayed @@ -447,38 +451,39 @@ func (p *Player) DisableInstantRespawn() { // SetNameTag changes the name tag displayed over the player in-game. Changing the name tag does not change // the player's name in, for example, the player list or the chat. func (p *Player) SetNameTag(name string) { - p.nameTag.Store(name) + p.nameTag.Store(&name) p.updateState() } // NameTag returns the current name tag of the Player as shown in-game. It can be changed using SetNameTag. func (p *Player) NameTag() string { - return p.nameTag.Load() + return *p.nameTag.Load() } // SetScoreTag changes the score tag displayed over the player in-game. The score tag is displayed under the player's // name tag. func (p *Player) SetScoreTag(a ...any) { - p.scoreTag.Store(format(a)) + tag := format(a) + p.scoreTag.Store(&tag) p.updateState() } // ScoreTag returns the current score tag of the player. It can be changed using SetScoreTag and by default is empty. func (p *Player) ScoreTag() string { - return p.scoreTag.Load() + return *p.scoreTag.Load() } // SetSpeed sets the speed of the player. The value passed is the blocks/tick speed that the player will then // obtain. func (p *Player) SetSpeed(speed float64) { - p.speed.Store(speed) + p.speed.Store(math.Float64bits(speed)) p.session().SendSpeed(speed) } // Speed returns the speed of the player, returning a value that indicates the blocks/tick speed. The default // speed of a player is 0.1. func (p *Player) Speed() float64 { - return p.speed.Load() + return math.Float64frombits(p.speed.Load()) } // Health returns the current health of the player. It will always be lower than Player.MaxHealth(). @@ -524,14 +529,14 @@ func (p *Player) Heal(health float64, source world.HealingSource) { // updateFallState is called to update the entities falling state. func (p *Player) updateFallState(distanceThisTick float64) { - fallDistance := p.fallDistance.Load() + fallDistance := math.Float64frombits(p.fallDistance.Load()) if p.OnGround() { if fallDistance > 0 { p.fall(fallDistance) p.ResetFallDistance() } } else if distanceThisTick < fallDistance { - p.fallDistance.Sub(distanceThisTick) + p.fallDistance.Store(math.Float64bits(fallDistance - distanceThisTick)) } else { p.ResetFallDistance() } @@ -690,13 +695,13 @@ func (p *Player) Explode(explosionPos mgl64.Vec3, impact float64, c block.Explos // Nothing happens if a negative number is passed. func (p *Player) SetAbsorption(health float64) { health = math.Max(health, 0) - p.absorptionHealth.Store(health) + p.absorptionHealth.Store(math.Float64bits(health)) p.session().SendAbsorption(health) } // Absorption returns the absorption health that the player has. func (p *Player) Absorption() float64 { - return p.absorptionHealth.Load() + return math.Float64frombits(p.absorptionHealth.Load()) } // KnockBack knocks the player back with a given force and height. A source is passed which indicates the @@ -883,7 +888,8 @@ func (p *Player) kill(src world.DamageSource) { // We have an actual client connected to this player: We change its position server side so that in // the future, the client won't respawn on the death location when disconnecting. The client should // not see the movement itself yet, though. - p.pos.Store(w.Spawn().Vec3()) + newPos := w.Spawn().Vec3() + p.pos.Store(&newPos) } }) } @@ -947,7 +953,7 @@ func (p *Player) StartSprinting() { if p.Handler().HandleToggleSprint(ctx, true); ctx.Cancelled() { return } - if !p.sprinting.CAS(false, true) { + if !p.sprinting.CompareAndSwap(false, true) { return } p.StopSneaking() @@ -967,7 +973,7 @@ func (p *Player) StopSprinting() { if p.Handler().HandleToggleSprint(ctx, false); ctx.Cancelled() { return } - if !p.sprinting.CAS(true, false) { + if !p.sprinting.CompareAndSwap(true, false) { return } p.SetSpeed(p.Speed() / 1.3) @@ -983,7 +989,7 @@ func (p *Player) StartSneaking() { if p.Handler().HandleToggleSneak(ctx, true); ctx.Cancelled() { return } - if !p.sneaking.CAS(false, true) { + if !p.sneaking.CompareAndSwap(false, true) { return } if !p.Flying() { @@ -1004,7 +1010,7 @@ func (p *Player) StopSneaking() { if p.Handler().HandleToggleSneak(ctx, false); ctx.Cancelled() { return } - if !p.sneaking.CAS(true, false) { + if !p.sneaking.CompareAndSwap(true, false) { return } p.updateState() @@ -1013,7 +1019,7 @@ func (p *Player) StopSneaking() { // StartSwimming makes the player start swimming if it is not currently doing so. If the player is sneaking // while StartSwimming is called, the sneaking is stopped. func (p *Player) StartSwimming() { - if !p.swimming.CAS(false, true) { + if !p.swimming.CompareAndSwap(false, true) { return } p.StopSneaking() @@ -1027,7 +1033,7 @@ func (p *Player) Swimming() bool { // StopSwimming makes the player stop swimming if it is currently doing so. func (p *Player) StopSwimming() { - if !p.swimming.CAS(true, false) { + if !p.swimming.CompareAndSwap(true, false) { return } p.updateState() @@ -1035,7 +1041,7 @@ func (p *Player) StopSwimming() { // StartGliding makes the player start gliding if it is not currently doing so. func (p *Player) StartGliding() { - if !p.gliding.CAS(false, true) { + if !p.gliding.CompareAndSwap(false, true) { return } chest := p.Armour().Chestplate() @@ -1052,7 +1058,7 @@ func (p *Player) Gliding() bool { // StopGliding makes the player stop gliding if it is currently doing so. func (p *Player) StopGliding() { - if !p.gliding.CAS(true, false) { + if !p.gliding.CompareAndSwap(true, false) { return } p.glideTicks.Store(0) @@ -1062,7 +1068,7 @@ func (p *Player) StopGliding() { // StartFlying makes the player start flying if they aren't already. It requires the player to be in a gamemode which // allows flying. func (p *Player) StartFlying() { - if !p.GameMode().AllowsFlying() || !p.flying.CAS(false, true) { + if !p.GameMode().AllowsFlying() || !p.flying.CompareAndSwap(false, true) { return } p.session().SendGameMode(p.GameMode()) @@ -1075,7 +1081,7 @@ func (p *Player) Flying() bool { // StopFlying makes the player stop flying if it currently is. func (p *Player) StopFlying() { - if !p.flying.CAS(true, false) { + if !p.flying.CompareAndSwap(true, false) { return } p.session().SendGameMode(p.GameMode()) @@ -1094,7 +1100,7 @@ func (p *Player) Jump() { if e, ok := p.Effect(effect.JumpBoost{}); ok { jumpVel = float64(e.Level()) / 10 } - p.vel.Store(mgl64.Vec3{0, jumpVel}) + p.vel.Store(&mgl64.Vec3{0, jumpVel}) } if p.Sprinting() { p.Exhaust(0.2) @@ -1105,7 +1111,7 @@ func (p *Player) Jump() { // SetInvisible sets the player invisible, so that other players will not be able to see it. func (p *Player) SetInvisible() { - if !p.invisible.CAS(false, true) { + if !p.invisible.CompareAndSwap(false, true) { return } p.updateState() @@ -1120,7 +1126,7 @@ func (p *Player) SetVisible() { if _, ok := p.Effect(effect.Invisibility{}); ok { return } - if !p.invisible.CAS(true, false) { + if !p.invisible.CompareAndSwap(true, false) { return } p.updateState() @@ -1133,7 +1139,7 @@ func (p *Player) Invisible() bool { // SetImmobile prevents the player from moving around, but still allows them to look around. func (p *Player) SetImmobile() { - if !p.immobile.CAS(false, true) { + if !p.immobile.CompareAndSwap(false, true) { return } p.updateState() @@ -1141,7 +1147,7 @@ func (p *Player) SetImmobile() { // SetMobile allows the player to freely move around again after being immobile. func (p *Player) SetMobile() { - if !p.immobile.CAS(true, false) { + if !p.immobile.CompareAndSwap(true, false) { return } p.updateState() @@ -1219,7 +1225,7 @@ func (p *Player) EnderChestInventory() *inventory.Inventory { // SetGameMode sets the game mode of a player. The game mode specifies the way that the player can interact // with the world that it is in. func (p *Player) SetGameMode(mode world.GameMode) { - previous := p.gameMode.Swap(mode) + previous := *p.gameMode.Swap(&mode) p.session().SendGameMode(mode) for _, v := range p.viewers() { v.ViewEntityGameMode(p) @@ -1239,7 +1245,7 @@ func (p *Player) SetGameMode(mode world.GameMode) { // be the same as that of the world that the player spawns in. // The game mode may be changed using Player.SetGameMode(). func (p *Player) GameMode() world.GameMode { - return p.gameMode.Load() + return *p.gameMode.Load() } // HasCooldown returns true if the item passed has an active cooldown, meaning it currently cannot be used again. If the @@ -1329,7 +1335,7 @@ func (p *Player) UseItem() { p.ReleaseItem() return } - if p.usingItem.CAS(false, true) { + if p.usingItem.CompareAndSwap(false, true) { // Consumable starts being consumed: Set the start timestamp and update the using state to viewers. p.usingSince.Store(time.Now().UnixNano()) p.updateState() @@ -1364,7 +1370,7 @@ func (p *Player) UseItem() { // ReleaseItem either aborts the using of the item or finished it, depending on the time that elapsed since // the item started being used. func (p *Player) ReleaseItem() { - if !p.usingItem.CAS(true, false) || !p.canRelease() || !p.GameMode().AllowsInteraction() { + if !p.usingItem.CompareAndSwap(true, false) || !p.canRelease() || !p.GameMode().AllowsInteraction() { return } ctx := p.useContext() @@ -1622,7 +1628,7 @@ func (p *Player) StartBreaking(pos cube.Pos, face cube.Face) { } // Note: We intentionally store this regardless of whether the breaking proceeds, so that we // can resend the block to the client when it tries to break the block regardless. - p.breakingPos.Store(pos) + p.breakingPos.Store(&pos) ctx := event.C() if p.Handler().HandleStartBreak(ctx, pos); ctx.Cancelled() { @@ -1674,7 +1680,7 @@ func (p *Player) breakTime(pos cube.Pos) time.Duration { // if the player isn't breaking anything. // FinishBreaking will stop the animation and break the block. func (p *Player) FinishBreaking() { - pos := p.breakingPos.Load() + pos := *p.breakingPos.Load() if !p.breaking.Load() { p.resendBlock(pos, p.World()) return @@ -1687,11 +1693,11 @@ func (p *Player) FinishBreaking() { // if the player isn't breaking anything. // Unlike FinishBreaking, AbortBreaking does not stop the animation. func (p *Player) AbortBreaking() { - if !p.breaking.CAS(true, false) { + if !p.breaking.CompareAndSwap(true, false) { return } p.breakParticleCounter.Store(0) - pos := p.breakingPos.Load() + pos := *p.breakingPos.Load() for _, viewer := range p.viewers() { viewer.ViewBlockAction(pos, block.StopCrackAction{}) } @@ -1704,7 +1710,7 @@ func (p *Player) ContinueBreaking(face cube.Face) { if !p.breaking.Load() { return } - pos := p.breakingPos.Load() + pos := *p.breakingPos.Load() p.SwingArm() @@ -1933,8 +1939,8 @@ func (p *Player) teleport(pos mgl64.Vec3) { for _, v := range p.viewers() { v.ViewEntityTeleport(p, pos) } - p.pos.Store(pos) - p.vel.Store(mgl64.Vec3{}) + p.pos.Store(&pos) + p.vel.Store(&mgl64.Vec3{}) p.ResetFallDistance() } @@ -1972,12 +1978,12 @@ func (p *Player) Move(deltaPos mgl64.Vec3, deltaYaw, deltaPitch float64) { v.ViewEntityMovement(p, res, cube.Rotation{resYaw, resPitch}, p.OnGround()) } - p.pos.Store(res) - p.yaw.Store(resYaw) - p.pitch.Store(resPitch) + p.pos.Store(&res) + p.yaw.Store(math.Float64bits(resYaw)) + p.pitch.Store(math.Float64bits(resPitch)) if deltaPos.Len() <= 3 { // Only update velocity if the player is not moving too fast to prevent potential OOMs. - p.vel.Store(deltaPos) + p.vel.Store(&deltaPos) p.checkBlockCollisions(deltaPos, w) } @@ -2022,19 +2028,19 @@ func (p *Player) World() *world.World { // Position returns the current position of the player. It may be changed as the player moves or is moved // around the world. func (p *Player) Position() mgl64.Vec3 { - return p.pos.Load() + return *p.pos.Load() } // Velocity returns the players current velocity. If there is an attached session, this will be empty. func (p *Player) Velocity() mgl64.Vec3 { - return p.vel.Load() + return *p.vel.Load() } // SetVelocity updates the player's velocity. If there is an attached session, this will just send // the velocity to the player session for the player to update. func (p *Player) SetVelocity(velocity mgl64.Vec3) { if p.session() == session.Nop { - p.vel.Store(velocity) + p.vel.Store(&velocity) return } for _, v := range p.viewers() { @@ -2046,7 +2052,7 @@ func (p *Player) SetVelocity(velocity mgl64.Vec3) { // vertical axis, 0 when facing forward), pitch is vertical rotation (rotation around the horizontal axis, also 0 // when facing forward). func (p *Player) Rotation() cube.Rotation { - return cube.Rotation{p.yaw.Load(), p.pitch.Load()} + return cube.Rotation{math.Float64frombits(p.yaw.Load()), math.Float64frombits(p.pitch.Load())} } // ChangingDimension returns whether the player is currently changing dimension or not. @@ -2140,11 +2146,13 @@ func (p *Player) CollectExperience(value int) bool { if p.Dead() || !p.GameMode().AllowsInteraction() { return false } - if time.Since(p.lastXPPickup.Load()) < time.Millisecond*100 { + last := p.lastXPPickup.Load() + if last == nil || time.Since(*last) < time.Millisecond*100 { return false } value = p.mendItems(value) - p.lastXPPickup.Store(time.Now()) + now := time.Now() + p.lastXPPickup.Store(&now) if value > 0 { return p.AddExperience(value) > 0 } @@ -2263,7 +2271,7 @@ func (p *Player) Tick(w *world.World, current int64) { } if _, ok := p.Armour().Chestplate().Item().(item.Elytra); ok && p.Gliding() { - if t := p.glideTicks.Inc(); t%20 == 0 { + if t := p.glideTicks.Add(1); t%20 == 0 { d := p.damageItem(p.Armour().Chestplate(), 1) p.armour.SetChestplate(d) if d.Durability() < 2 { @@ -2272,7 +2280,7 @@ func (p *Player) Tick(w *world.World, current int64) { } } - p.checkBlockCollisions(p.vel.Load(), w) + p.checkBlockCollisions(*p.vel.Load(), w) p.onGround.Store(p.checkOnGround(w)) p.effects.Tick(p) @@ -2280,7 +2288,7 @@ func (p *Player) Tick(w *world.World, current int64) { p.tickFood(w) p.tickAirSupply(w) if p.immunityTicks.Load() > 0 { - p.immunityTicks.Dec() + p.immunityTicks.Add(-1) } if p.Position()[1] < float64(w.Range()[0]) && p.GameMode().AllowsTakingDamage() && current%10 == 0 { p.Hurt(4, entity.VoidDamageSource{}) @@ -2290,7 +2298,7 @@ func (p *Player) Tick(w *world.World, current int64) { } if p.OnFireDuration() > 0 { - p.fireTicks.Sub(1) + p.fireTicks.Add(-1) if !p.GameMode().AllowsTakingDamage() || p.OnFireDuration() <= 0 || w.RainingAt(cube.PosFromVec3(p.Position())) { p.Extinguish() } @@ -2318,13 +2326,14 @@ func (p *Player) Tick(w *world.World, current int64) { p.cooldownMu.Unlock() if p.session() == session.Nop && !p.Immobile() { - m := p.mc.TickMovement(p, p.Position(), p.Velocity(), cube.Rotation{p.yaw.Load(), p.pitch.Load()}) + m := p.mc.TickMovement(p, p.Position(), p.Velocity(), cube.Rotation{math.Float64frombits(p.yaw.Load()), math.Float64frombits(p.pitch.Load())}) m.Send() - p.vel.Store(m.Velocity()) + vel := m.Velocity() + p.vel.Store(&vel) p.Move(m.Position().Sub(p.Position()), 0, 0) } else { - p.vel.Store(mgl64.Vec3{}) + p.vel.Store(&mgl64.Vec3{}) } } @@ -2336,7 +2345,7 @@ func (p *Player) tickAirSupply(w *world.World) { return } - if ticks := p.airSupplyTicks.Dec(); ticks <= -20 { + if ticks := p.airSupplyTicks.Add(-1); ticks <= -20 { p.airSupplyTicks.Store(0) if !p.AttackImmune() { p.Hurt(2, entity.DrowningDamageSource{}) @@ -2583,13 +2592,13 @@ func (p *Player) checkOnGround(w *world.World) bool { // Scale returns the scale modifier of the Player. The default value for a normal scale is 1. A scale of 0 // will make the Player completely invisible. func (p *Player) Scale() float64 { - return p.scale.Load() + return math.Float64frombits(p.scale.Load()) } // SetScale changes the scale modifier of the Player. The default value for a normal scale is 1. A scale of 0 // will make the Player completely invisible. func (p *Player) SetScale(s float64) { - p.scale.Store(s) + p.scale.Store(math.Float64bits(s)) p.updateState() } @@ -2820,11 +2829,12 @@ func (p *Player) close(msg string) { if p.Dead() && p.session() != nil { p.Respawn() } - p.h.Swap(NopHandler{}).HandleQuit() + var h Handler = NopHandler{} + (*p.h.Swap(&h)).HandleQuit() if s := p.s.Swap(nil); s != nil { - s.Disconnect(msg) - s.CloseConnection() + (*s).Disconnect(msg) + (*s).CloseConnection() return } // Only remove the player from the world if it's not attached to a session. If it is attached to a session, the @@ -2835,15 +2845,15 @@ func (p *Player) close(msg string) { // load reads the player data from the provider. It uses the default values if the provider // returns false. func (p *Player) load(data Data) { - p.yaw.Store(data.Yaw) - p.pitch.Store(data.Pitch) + p.yaw.Store(math.Float64bits(data.Yaw)) + p.pitch.Store(math.Float64bits(data.Pitch)) p.health.SetMaxHealth(data.MaxHealth) p.health.AddHealth(data.Health - p.Health()) p.session().SendHealth(p.health) - p.absorptionHealth.Store(data.AbsorptionLevel) - p.session().SendAbsorption(p.absorptionHealth.Load()) + p.absorptionHealth.Store(math.Float64bits(data.AbsorptionLevel)) + p.session().SendAbsorption(data.AbsorptionLevel) p.hunger.SetFood(data.Hunger) p.hunger.foodTick = data.FoodTick @@ -2858,12 +2868,12 @@ func (p *Player) load(data Data) { p.enchantSeed.Store(data.EnchantmentSeed) - p.gameMode.Store(data.GameMode) + p.gameMode.Store(&data.GameMode) for _, potion := range data.Effects { p.AddEffect(potion) } p.fireTicks.Store(data.FireTicks) - p.fallDistance.Store(data.FallDistance) + p.fallDistance.Store(math.Float64bits(data.FallDistance)) p.loadInventory(data.Inventory) for slot, stack := range data.EnderChestInventory { @@ -2920,7 +2930,7 @@ func (p *Player) Data() Data { EnderChestInventory: p.enderChest.Slots(), Effects: p.Effects(), FireTicks: p.fireTicks.Load(), - FallDistance: p.fallDistance.Load(), + FallDistance: math.Float64frombits(p.fallDistance.Load()), World: p.World(), } } @@ -2929,7 +2939,7 @@ func (p *Player) Data() Data { // is returned. func (p *Player) session() *session.Session { if s := p.s.Load(); s != nil { - return s + return *s } return session.Nop } @@ -2976,7 +2986,7 @@ func (p *Player) useContext() *item.UseContext { // Handler returns the Handler of the player. func (p *Player) Handler() Handler { - return p.h.Load() + return *p.h.Load() } // broadcastItems broadcasts the items held to viewers. diff --git a/server/server.go b/server/server.go index 8508305e8..8c7f560d2 100644 --- a/server/server.go +++ b/server/server.go @@ -6,7 +6,6 @@ import ( _ "embed" "encoding/base64" "fmt" - "github.com/df-mc/atomic" "github.com/df-mc/dragonfly/server/cmd" "github.com/df-mc/dragonfly/server/internal/blockinternal" "github.com/df-mc/dragonfly/server/internal/iteminternal" @@ -33,6 +32,7 @@ import ( "runtime" "strings" "sync" + "sync/atomic" "syscall" ) @@ -81,7 +81,7 @@ func New() *Server { // are closed using a call to Close. Once started, players may be accepted // using Server.Accept(). func (srv *Server) Listen() { - if !srv.started.CAS(false, true) { + if !srv.started.CompareAndSwap(false, true) { panic("start server: already started") } diff --git a/server/session/handler_anvil.go b/server/session/handler_anvil.go index 36780689d..322b76135 100644 --- a/server/session/handler_anvil.go +++ b/server/session/handler_anvil.go @@ -25,7 +25,7 @@ func (h *ItemStackRequestHandler) handleCraftRecipeOptional(a *protocol.CraftRec } w := s.c.World() - pos := s.openedPos.Load() + pos := *s.openedPos.Load() anvil, ok := w.Block(pos).(block.Anvil) if !ok { return fmt.Errorf("no anvil container opened") diff --git a/server/session/handler_beacon.go b/server/session/handler_beacon.go index 264ddf9f1..a3ce27966 100644 --- a/server/session/handler_beacon.go +++ b/server/session/handler_beacon.go @@ -22,7 +22,7 @@ func (h *ItemStackRequestHandler) handleBeaconPayment(a *protocol.BeaconPaymentS if !s.containerOpened.Load() { return fmt.Errorf("no beacon container opened") } - pos := s.openedPos.Load() + pos := *s.openedPos.Load() beacon, ok := s.c.World().Block(pos).(block.Beacon) if !ok { return fmt.Errorf("no beacon container opened") diff --git a/server/session/handler_enchanting.go b/server/session/handler_enchanting.go index 01d1ac21b..3aa828878 100644 --- a/server/session/handler_enchanting.go +++ b/server/session/handler_enchanting.go @@ -38,7 +38,7 @@ func (h *ItemStackRequestHandler) handleEnchant(a *protocol.CraftRecipeStackRequ } // Determine the available enchantments using the session's enchantment seed. - allCosts, allEnchants := s.determineAvailableEnchantments(s.c.World(), s.openedPos.Load(), input) + allCosts, allEnchants := s.determineAvailableEnchantments(s.c.World(), *s.openedPos.Load(), input) if len(allEnchants) == 0 { return fmt.Errorf("can't enchant non-enchantable item") } diff --git a/server/session/handler_grindstone.go b/server/session/handler_grindstone.go index c32821916..166f0742f 100644 --- a/server/session/handler_grindstone.go +++ b/server/session/handler_grindstone.go @@ -25,7 +25,7 @@ func (h *ItemStackRequestHandler) handleGrindstoneCraft(s *Session) error { if !s.containerOpened.Load() { return fmt.Errorf("no grindstone container opened") } - if _, ok := s.c.World().Block(s.openedPos.Load()).(block.Grindstone); !ok { + if _, ok := s.c.World().Block(*s.openedPos.Load()).(block.Grindstone); !ok { return fmt.Errorf("no grindstone container opened") } diff --git a/server/session/handler_item_stack_request.go b/server/session/handler_item_stack_request.go index aa665fbf1..55915f349 100644 --- a/server/session/handler_item_stack_request.go +++ b/server/session/handler_item_stack_request.go @@ -90,7 +90,7 @@ func (h *ItemStackRequestHandler) handleRequest(req protocol.ItemStackRequest, s case *protocol.CraftRecipeStackRequestAction: if s.containerOpened.Load() { var special bool - switch s.c.World().Block(s.openedPos.Load()).(type) { + switch s.c.World().Block(*s.openedPos.Load()).(type) { case block.SmithingTable: err, special = h.handleSmithing(a, s), true case block.Stonecutter: @@ -208,8 +208,8 @@ func (h *ItemStackRequestHandler) handleSwap(a *protocol.SwapStackRequestAction, // smelting. If it does, it will drop the rewards at the player's location. func (h *ItemStackRequestHandler) collectRewards(s *Session, inv *inventory.Inventory, slot int) { w := s.c.World() - if inv == s.openedWindow.Load() && s.containerOpened.Load() && slot == inv.Size()-1 { - if f, ok := w.Block(s.openedPos.Load()).(smelter); ok { + if inv == *s.openedWindow.Load() && s.containerOpened.Load() && slot == inv.Size()-1 { + if f, ok := w.Block(*s.openedPos.Load()).(smelter); ok { for _, o := range entity.NewExperienceOrbs(entity.EyePosition(s.c), f.ResetExperience()) { o.SetVelocity(mgl64.Vec3{(rand.Float64()*0.2 - 0.1) * 2, rand.Float64() * 0.4, (rand.Float64()*0.2 - 0.1) * 2}) w.AddEntity(o) diff --git a/server/session/handler_loom.go b/server/session/handler_loom.go index 299b112ca..4d73ee14e 100644 --- a/server/session/handler_loom.go +++ b/server/session/handler_loom.go @@ -22,7 +22,7 @@ func (h *ItemStackRequestHandler) handleLoomCraft(a *protocol.CraftLoomRecipeSta if !s.containerOpened.Load() { return fmt.Errorf("no loom container opened") } - if _, ok := s.c.World().Block(s.openedPos.Load()).(block.Loom); !ok { + if _, ok := s.c.World().Block(*s.openedPos.Load()).(block.Loom); !ok { return fmt.Errorf("no loom container opened") } diff --git a/server/session/handler_modal_form_response.go b/server/session/handler_modal_form_response.go index ac846b6d1..aed3d450f 100644 --- a/server/session/handler_modal_form_response.go +++ b/server/session/handler_modal_form_response.go @@ -2,10 +2,10 @@ package session import ( "fmt" - "github.com/df-mc/atomic" "github.com/df-mc/dragonfly/server/player/form" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" "sync" + "sync/atomic" ) // ModalFormResponseHandler handles the ModalFormResponse packet. diff --git a/server/session/handler_player_auth_input.go b/server/session/handler_player_auth_input.go index bc81cb18f..38ec6542c 100644 --- a/server/session/handler_player_auth_input.go +++ b/server/session/handler_player_auth_input.go @@ -51,7 +51,7 @@ func (h PlayerAuthInputHandler) handleMovement(pk *packet.PlayerAuthInput, s *Se } if expected := s.teleportPos.Load(); expected != nil { - if newPos.Sub(*expected).Len() > 1 { + if newPos.Sub(**expected).Len() > 1 { // The player has moved before it received the teleport packet. Ignore this movement entirely and // wait for the client to sync itself back to the server. Once we get a movement that is close // enough to the teleport position, we'll allow the player to move around again. diff --git a/server/session/handler_server_bound_loading_screen.go b/server/session/handler_server_bound_loading_screen.go index 66a1bd1b8..fd9c46663 100644 --- a/server/session/handler_server_bound_loading_screen.go +++ b/server/session/handler_server_bound_loading_screen.go @@ -2,8 +2,8 @@ package session import ( "fmt" - "github.com/df-mc/atomic" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" + "sync/atomic" ) // ServerBoundLoadingScreenHandler handles loading screen updates from the clients. It is used to ensure that diff --git a/server/session/player.go b/server/session/player.go index c2b953240..7de374ccd 100644 --- a/server/session/player.go +++ b/server/session/player.go @@ -3,7 +3,6 @@ package session import ( "encoding/json" "fmt" - "github.com/df-mc/atomic" "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/entity/effect" @@ -21,6 +20,7 @@ import ( "github.com/sandertv/gophertunnel/minecraft/protocol/packet" "math" "net" + "sync/atomic" "time" _ "unsafe" // Imported for compiler directives. ) @@ -64,7 +64,7 @@ func (s *Session) closeCurrentContainer() { } s.closeWindow() - pos := s.openedPos.Load() + pos := *s.openedPos.Load() w := s.c.World() b := w.Block(pos) if container, ok := b.(block.Container); ok { @@ -246,61 +246,61 @@ func (s *Session) invByID(id int32) (*inventory.Inventory, bool) { return s.armour.Inventory(), true case protocol.ContainerLevelEntity: if s.containerOpened.Load() { - return s.openedWindow.Load(), true + return *s.openedWindow.Load(), true } case protocol.ContainerBarrel: if s.containerOpened.Load() { - if _, barrel := s.c.World().Block(s.openedPos.Load()).(block.Barrel); barrel { - return s.openedWindow.Load(), true + if _, barrel := s.c.World().Block(*s.openedPos.Load()).(block.Barrel); barrel { + return *s.openedWindow.Load(), true } } case protocol.ContainerBeaconPayment: if s.containerOpened.Load() { - if _, beacon := s.c.World().Block(s.openedPos.Load()).(block.Beacon); beacon { + if _, beacon := s.c.World().Block(*s.openedPos.Load()).(block.Beacon); beacon { return s.ui, true } } case protocol.ContainerAnvilInput, protocol.ContainerAnvilMaterial: if s.containerOpened.Load() { - if _, anvil := s.c.World().Block(s.openedPos.Load()).(block.Anvil); anvil { + if _, anvil := s.c.World().Block(*s.openedPos.Load()).(block.Anvil); anvil { return s.ui, true } } case protocol.ContainerSmithingTableTemplate, protocol.ContainerSmithingTableInput, protocol.ContainerSmithingTableMaterial: if s.containerOpened.Load() { - if _, smithing := s.c.World().Block(s.openedPos.Load()).(block.SmithingTable); smithing { + if _, smithing := s.c.World().Block(*s.openedPos.Load()).(block.SmithingTable); smithing { return s.ui, true } } case protocol.ContainerLoomInput, protocol.ContainerLoomDye, protocol.ContainerLoomMaterial: if s.containerOpened.Load() { - if _, loom := s.c.World().Block(s.openedPos.Load()).(block.Loom); loom { + if _, loom := s.c.World().Block(*s.openedPos.Load()).(block.Loom); loom { return s.ui, true } } case protocol.ContainerStonecutterInput: if s.containerOpened.Load() { - if _, ok := s.c.World().Block(s.openedPos.Load()).(block.Stonecutter); ok { + if _, ok := s.c.World().Block(*s.openedPos.Load()).(block.Stonecutter); ok { return s.ui, true } } case protocol.ContainerGrindstoneInput, protocol.ContainerGrindstoneAdditional: if s.containerOpened.Load() { - if _, ok := s.c.World().Block(s.openedPos.Load()).(block.Grindstone); ok { + if _, ok := s.c.World().Block(*s.openedPos.Load()).(block.Grindstone); ok { return s.ui, true } } case protocol.ContainerEnchantingInput, protocol.ContainerEnchantingMaterial: if s.containerOpened.Load() { - if _, enchanting := s.c.World().Block(s.openedPos.Load()).(block.EnchantingTable); enchanting { + if _, enchanting := s.c.World().Block(*s.openedPos.Load()).(block.EnchantingTable); enchanting { return s.ui, true } } case protocol.ContainerFurnaceIngredient, protocol.ContainerFurnaceFuel, protocol.ContainerFurnaceResult, protocol.ContainerBlastFurnaceIngredient, protocol.ContainerSmokerIngredient: if s.containerOpened.Load() { - if _, ok := s.c.World().Block(s.openedPos.Load()).(smelter); ok { - return s.openedWindow.Load(), true + if _, ok := s.c.World().Block(*s.openedPos.Load()).(smelter); ok { + return *s.openedWindow.Load(), true } } } @@ -658,7 +658,7 @@ func (s *Session) HandleInventories() (inv, offHand, enderChest *inventory.Inven return } if !s.inTransaction.Load() { - if _, ok := s.c.World().Block(s.openedPos.Load()).(block.EnderChest); ok { + if _, ok := s.c.World().Block(*s.openedPos.Load()).(block.EnderChest); ok { s.ViewSlotChange(slot, item) } } diff --git a/server/session/session.go b/server/session/session.go index ce24c2e67..5c5fbad7c 100644 --- a/server/session/session.go +++ b/server/session/session.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "github.com/df-mc/atomic" "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/internal/sliceutil" @@ -24,6 +23,7 @@ import ( "io" "net" "sync" + "sync/atomic" "time" ) @@ -41,13 +41,13 @@ type Session struct { // session controls. onStop func(controllable Controllable) - currentScoreboard atomic.Value[string] - currentLines atomic.Value[[]string] + currentScoreboard atomic.Pointer[string] + currentLines atomic.Pointer[[]string] chunkLoader *world.Loader chunkRadius, maxChunkRadius int32 - teleportPos atomic.Value[*mgl64.Vec3] + teleportPos atomic.Pointer[*mgl64.Vec3] entityMutex sync.RWMutex // currentEntityRuntimeID holds the runtime ID assigned to the last entity. It is incremented for every @@ -68,8 +68,8 @@ type Session struct { inTransaction, containerOpened atomic.Bool openedWindowID atomic.Uint32 openedContainerID atomic.Uint32 - openedWindow atomic.Value[*inventory.Inventory] - openedPos atomic.Value[cube.Pos] + openedWindow atomic.Pointer[*inventory.Inventory] + openedPos atomic.Pointer[cube.Pos] swingingArm atomic.Bool changingDimension atomic.Bool @@ -162,11 +162,17 @@ func New(conn Conn, maxChunkRadius int, log Logger, joinMessage, quitMessage str conn: conn, log: log, currentEntityRuntimeID: 1, - heldSlot: atomic.NewUint32(0), + heldSlot: new(atomic.Uint32), joinMessage: joinMessage, quitMessage: quitMessage, - openedWindow: *atomic.NewValue(inventory.New(1, nil)), } + inv := inventory.New(1, nil) + s.openedWindow.Store(&inv) + + var scoreboardName string + var scoreboardLines []string + s.currentScoreboard.Store(&scoreboardName) + s.currentLines.Store(&scoreboardLines) s.registerHandlers() return s @@ -487,7 +493,7 @@ func (s *Session) registerHandlers() { // in the future. func (s *Session) handleInterfaceUpdate(slot int, _, item item.Stack) { if slot == enchantingInputSlot && s.containerOpened.Load() { - pos := s.openedPos.Load() + pos := *s.openedPos.Load() b := s.c.World().Block(pos) if _, enchanting := b.(block.EnchantingTable); enchanting { s.sendEnchantmentOptions(s.c.World(), pos, item) diff --git a/server/session/text.go b/server/session/text.go index 935d81e8e..240957f9b 100644 --- a/server/session/text.go +++ b/server/session/text.go @@ -60,7 +60,7 @@ func (s *Session) SendScoreboard(sb *scoreboard.Scoreboard) { if s == Nop { return } - currentName, currentLines := s.currentScoreboard.Load(), s.currentLines.Load() + currentName, currentLines := *s.currentScoreboard.Load(), *s.currentLines.Load() if currentName != sb.Name() { s.RemoveScoreboard() @@ -70,8 +70,9 @@ func (s *Session) SendScoreboard(sb *scoreboard.Scoreboard) { DisplayName: sb.Name(), CriteriaName: "dummy", }) - s.currentScoreboard.Store(sb.Name()) - s.currentLines.Store(append([]string(nil), sb.Lines()...)) + name, lines := sb.Name(), append([]string(nil), sb.Lines()...) + s.currentScoreboard.Store(&name) + s.currentLines.Store(&lines) } else { // Remove all current lines from the scoreboard. We can't replace them without removing them. pk := &packet.SetScore{ActionType: packet.ScoreboardActionRemove} @@ -109,9 +110,11 @@ var colours = [15]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", // RemoveScoreboard ... func (s *Session) RemoveScoreboard() { - s.writePacket(&packet.RemoveObjective{ObjectiveName: s.currentScoreboard.Load()}) - s.currentScoreboard.Store("") - s.currentLines.Store([]string{}) + s.writePacket(&packet.RemoveObjective{ObjectiveName: *s.currentScoreboard.Load()}) + var name string + var lines []string + s.currentScoreboard.Store(&name) + s.currentLines.Store(&lines) } // SendBossBar sends a boss bar to the player with the text passed and the health percentage of the bar. diff --git a/server/session/world.go b/server/session/world.go index fc2f287db..96ffe288e 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -249,7 +249,8 @@ func (s *Session) ViewEntityTeleport(e world.Entity, position mgl64.Vec3) { yaw, pitch := e.Rotation().Elem() if id == selfEntityRuntimeID { s.chunkLoader.Move(position) - s.teleportPos.Store(&position) + pos := &position + s.teleportPos.Store(&pos) } s.writePacket(&packet.SetActorMotion{EntityRuntimeID: id}) @@ -957,7 +958,7 @@ func (s *Session) ViewEntityAnimation(e world.Entity, animationName string) { // OpenBlockContainer ... func (s *Session) OpenBlockContainer(pos cube.Pos) { - if s.containerOpened.Load() && s.openedPos.Load() == pos { + if s.containerOpened.Load() && *s.openedPos.Load() == pos { return } s.closeCurrentContainer() @@ -971,8 +972,9 @@ func (s *Session) OpenBlockContainer(pos cube.Pos) { // We hit a special kind of window like beacons, which are not actually opened server-side. nextID := s.nextWindowID() s.containerOpened.Store(true) - s.openedWindow.Store(inventory.New(1, nil)) - s.openedPos.Store(pos) + inv := inventory.New(1, nil) + s.openedWindow.Store(&inv) + s.openedPos.Store(&pos) var containerType byte switch b := b.(type) { @@ -996,7 +998,7 @@ func (s *Session) OpenBlockContainer(pos cube.Pos) { b.AddViewer(w, pos) inv := s.c.EnderChestInventory() - s.openedWindow.Store(inv) + s.openedWindow.Store(&inv) defer s.sendInv(inv, uint32(nextID)) } @@ -1016,8 +1018,9 @@ func (s *Session) openNormalContainer(b block.Container, pos cube.Pos) { nextID := s.nextWindowID() s.containerOpened.Store(true) - s.openedWindow.Store(b.Inventory(s.c.World(), pos)) - s.openedPos.Store(pos) + inv := b.Inventory(s.c.World(), pos) + s.openedWindow.Store(&inv) + s.openedPos.Store(&pos) var containerType byte switch b.(type) { @@ -1144,7 +1147,7 @@ func (s *Session) ViewWeather(raining, thunder bool) { // nextWindowID produces the next window ID for a new window. It is an int of 1-99. func (s *Session) nextWindowID() byte { - if s.openedWindowID.CAS(99, 1) { + if s.openedWindowID.CompareAndSwap(99, 1) { return 1 } return byte(s.openedWindowID.Add(1)) @@ -1153,11 +1156,12 @@ func (s *Session) nextWindowID() byte { // closeWindow closes the container window currently opened. If no window is open, closeWindow will do // nothing. func (s *Session) closeWindow() { - if !s.containerOpened.CAS(true, false) { + if !s.containerOpened.CompareAndSwap(true, false) { return } s.openedContainerID.Store(0) - s.openedWindow.Store(inventory.New(1, nil)) + inv := inventory.New(1, nil) + s.openedWindow.Store(&inv) s.writePacket(&packet.ContainerClose{WindowID: byte(s.openedWindowID.Load())}) } diff --git a/server/world/conf.go b/server/world/conf.go index c164952e0..87ae975be 100644 --- a/server/world/conf.go +++ b/server/world/conf.go @@ -1,7 +1,6 @@ package world import ( - "github.com/df-mc/atomic" "github.com/df-mc/dragonfly/server/block/cube" "github.com/sirupsen/logrus" "math/rand" @@ -76,14 +75,15 @@ func (conf Config) New() *World { viewers: make(map[*Loader]Viewer), chunks: make(map[ChunkPos]*Column), closing: make(chan struct{}), - handler: *atomic.NewValue[Handler](NopHandler{}), r: rand.New(conf.RandSource), - advance: s.ref.Inc() == 1, + advance: s.ref.Add(1) == 1, conf: conf, ra: conf.Dim.Range(), set: s, } w.weather, w.ticker = weather{w: w}, ticker{w: w} + var h Handler = NopHandler{} + w.handler.Store(&h) go w.tickLoop() go w.chunkCacheJanitor() diff --git a/server/world/settings.go b/server/world/settings.go index e8ea8f4d0..bd029c773 100644 --- a/server/world/settings.go +++ b/server/world/settings.go @@ -1,9 +1,9 @@ package world import ( - "github.com/df-mc/atomic" "github.com/df-mc/dragonfly/server/block/cube" "sync" + "sync/atomic" ) // Settings holds the settings of a World. These are typically saved to a level.dat file. It is safe to pass the same diff --git a/server/world/world.go b/server/world/world.go index c987aa9e6..965be6b8f 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -10,7 +10,6 @@ import ( "slices" - "github.com/df-mc/atomic" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/event" "github.com/df-mc/dragonfly/server/internal/sliceutil" @@ -18,6 +17,7 @@ import ( "github.com/go-gl/mathgl/mgl64" "github.com/google/uuid" "golang.org/x/exp/maps" + "sync/atomic" ) // World implements a Minecraft world. It manages all aspects of what players can see, such as blocks, @@ -35,7 +35,7 @@ type World struct { o sync.Once set *Settings - handler atomic.Value[Handler] + handler atomic.Pointer[Handler] weather ticker @@ -989,7 +989,7 @@ func (w *World) Handle(h Handler) { if h == nil { h = NopHandler{} } - w.handler.Store(h) + w.handler.Store(&h) } // Viewers returns a list of all viewers viewing the position passed. A viewer will be assumed to be watching @@ -1061,7 +1061,7 @@ func (w *World) close() { w.saveChunk(pos, c, true) } - w.set.ref.Dec() + w.set.ref.Add(-1) if !w.advance { return } @@ -1163,7 +1163,7 @@ func (w *World) Handler() Handler { if w == nil { return NopHandler{} } - return w.handler.Load() + return *w.handler.Load() } // chunkFromCache attempts to fetch a chunk at the chunk position passed from the cache. If not found, the