From 76951d847aa5a2ea1c4aeef3921d52745fe33fe7 Mon Sep 17 00:00:00 2001 From: oqammx86 <84847714+oq-x@users.noreply.github.com> Date: Wed, 29 Nov 2023 21:40:47 +0200 Subject: [PATCH] generic atomic values and reduced use of mutexes --- core_commands/ban.go | 3 +- core_commands/gamemode.go | 4 +- core_commands/kill.go | 2 +- core_commands/list.go | 4 +- core_commands/nick.go | 2 +- core_commands/tp.go | 12 +- server/access.go | 6 +- server/broadcast.go | 2 +- server/entity.go | 2 +- server/entity/pos/pos.go | 78 ++++++----- server/handler/chat_command.go | 3 +- server/handler/player_abilities.go | 2 +- server/handler/player_action.go | 4 +- server/handler/player_movement.go | 2 +- server/handler/set_creative_mode_slot.go | 4 +- server/handler/set_held_item.go | 2 +- server/handler/teleport_to_entity.go | 2 +- server/handler/use_item_on.go | 2 +- server/inventory/inventory.go | 19 ++- server/player/broadcast.go | 18 +-- server/player/chat.go | 12 +- server/player/movement.go | 19 ++- server/player/player.go | 163 ++++++++++------------- server/player/private.go | 155 +++------------------ server/server.go | 28 ++-- server/session/session.go | 11 ++ server/world/tick/tick.go | 24 +++- util/atomic/atomic.go | 32 +++++ 28 files changed, 269 insertions(+), 348 deletions(-) create mode 100644 util/atomic/atomic.go diff --git a/core_commands/ban.go b/core_commands/ban.go index d01dd7f..5acce61 100644 --- a/core_commands/ban.go +++ b/core_commands/ban.go @@ -5,6 +5,7 @@ import ( "github.com/dynamitemc/dynamite/server/commands" "github.com/dynamitemc/dynamite/server/lang/placeholder" + "github.com/google/uuid" ) var ban_cmd = &commands.Command{ @@ -31,7 +32,7 @@ var ban_cmd = &commands.Command{ if len(ctx.Arguments) > 1 { reason = server.Lang.Translate("disconnect.banned.reason", placeholder.New(map[string]string{"reason": strings.Join(ctx.Arguments[1:], " ")}, player.PlaceholderContext)) } - server.Ban(player.Name(), player.UUID().String(), strings.Join(ctx.Arguments[1:], " ")) + server.Ban(player.Session.Name(), uuid.UUID(player.Session.UUID()).String(), strings.Join(ctx.Arguments[1:], " ")) player.Disconnect(reason) }, } diff --git a/core_commands/gamemode.go b/core_commands/gamemode.go index 7df7dd6..1dc0f50 100644 --- a/core_commands/gamemode.go +++ b/core_commands/gamemode.go @@ -52,13 +52,13 @@ var gamemode_cmd = &commands.Command{ } pl = p } - if int(pl.GameMode()) == gm { + if int(pl.GameMode.Get()) == gm { return } pl.SetGameMode(byte(gm)) ph := placeholder.New(map[string]string{"gamemode": pascalify(ctx.Arguments[0])}, pl.PlaceholderContext) msg := pl.Server.(*server.Server).Lang.Translate("commands.gamemode.success.other", ph) - if exe, ok := ctx.Executor.(*player.Player); ok && pl.UUID() == exe.UUID() { + if exe, ok := ctx.Executor.(*player.Player); ok && pl.Session.UUID() == exe.Session.UUID() { msg = pl.Server.(*server.Server).Lang.Translate("commands.gamemode.success.self", ph) } ctx.Reply(msg) diff --git a/core_commands/kill.go b/core_commands/kill.go index 0e5021c..69cf5d9 100644 --- a/core_commands/kill.go +++ b/core_commands/kill.go @@ -30,7 +30,7 @@ var kill_cmd = &commands.Command{ } pl = p } - name := pl.Name() + name := pl.Session.Name() pl.Kill(name + " was killed") ctx.Reply(pl.Server.(*server.Server).Lang.Translate("commands.kill.success.single", pl.PlaceholderContext)) pl.Server.(*server.Server).GlobalMessage(chat.NewMessage(name + " was killed")) diff --git a/core_commands/list.go b/core_commands/list.go index 84d470f..d85b3f7 100644 --- a/core_commands/list.go +++ b/core_commands/list.go @@ -30,9 +30,9 @@ var list_cmd = &commands.Command{ msg := fmt.Sprintf("There are %d of a max of %d players online: ", l, srv.Config.MaxPlayers) var index int srv.Players.Range(func(_ uuid.UUID, p *player.Player) bool { - msg += p.Name() + msg += p.Session.Name() if len(ctx.Arguments) == 1 && ctx.Arguments[0] == "uuids" { - msg += fmt.Sprintf(" (%s)", p.UUID()) + msg += fmt.Sprintf(" (%s)", uuid.UUID(p.Session.UUID())) } if index != l-1 { msg += ", " diff --git a/core_commands/nick.go b/core_commands/nick.go index d9d3645..5957cef 100644 --- a/core_commands/nick.go +++ b/core_commands/nick.go @@ -29,7 +29,7 @@ var nick_cmd = &commands.Command{ } } else { p = srv.Players.Find(func(_ uuid.UUID, pl *player.Player) bool { - return pl.Name() == ctx.Arguments[0] + return pl.Session.Name() == ctx.Arguments[0] }) } if p == nil { diff --git a/core_commands/tp.go b/core_commands/tp.go index f774afe..ff2b04e 100644 --- a/core_commands/tp.go +++ b/core_commands/tp.go @@ -33,10 +33,10 @@ var tp_cmd = &commands.Command{ ep, es := exe.GetPrefixSuffix() pp, ps := player.GetPrefixSuffix() ctx.Reply(srv.Lang.Translate("commands.teleport.success.entity.single", placeholder.New(map[string]string{ - "player": exe.Name(), + "player": exe.Session.Name(), "player_prefix": ep, "player_suffx": es, - "player1": player.Name(), + "player1": player.Session.Name(), "player1_prefix": pp, "player1_suffx": ps, }))) @@ -54,10 +54,10 @@ var tp_cmd = &commands.Command{ ep, es := player1.GetPrefixSuffix() pp, ps := player2.GetPrefixSuffix() ctx.Reply(srv.Lang.Translate("commands.teleport.success.entity.single", placeholder.New(map[string]string{ - "player": player1.Name(), + "player": player1.Session.Name(), "player_prefix": ep, "player_suffx": es, - "player1": player2.Name(), + "player1": player2.Session.Name(), "player1_prefix": pp, "player1_suffx": ps, }))) @@ -90,7 +90,7 @@ var tp_cmd = &commands.Command{ prefix, suffix := exe.GetPrefixSuffix() ctx.Reply(srv.Lang.Translate("commands.teleport.success.location.single", placeholder.New( map[string]string{ - "player": exe.Name(), + "player": exe.Session.Name(), "player_prefix": prefix, "player_suffx": suffix, "x": fmt.Sprint(x), @@ -125,7 +125,7 @@ var tp_cmd = &commands.Command{ prefix, suffix := player.GetPrefixSuffix() ctx.Reply(srv.Lang.Translate("commands.teleport.success.location.single", placeholder.New( map[string]string{ - "player": player.Name(), + "player": player.Session.Name(), "player_prefix": prefix, "player_suffx": suffix, "x": fmt.Sprint(x), diff --git a/server/access.go b/server/access.go index c830787..99fc629 100644 --- a/server/access.go +++ b/server/access.go @@ -153,8 +153,8 @@ func (srv *Server) MakeOperator(p *player.Player) { srv.mu.Lock() defer srv.mu.Unlock() srv.Operators = append(srv.Operators, user{ - UUID: p.UUID().String(), - Name: p.Name(), + UUID: uuid.UUID(p.Session.UUID()).String(), + Name: p.Session.Name(), }) } @@ -164,7 +164,7 @@ func (srv *Server) MakeNotOperator(p *player.Player) { srv.mu.Lock() defer srv.mu.Unlock() for i, op := range srv.Operators { - if op.UUID == p.UUID().String() { + if op.UUID == uuid.UUID(p.Session.UUID()).String() { srv.Operators = slices.Delete(srv.Operators, i, i+1) return } diff --git a/server/broadcast.go b/server/broadcast.go index ab3d371..58e5a54 100644 --- a/server/broadcast.go +++ b/server/broadcast.go @@ -23,7 +23,7 @@ func (srv *Server) GlobalMessage(message chat.Message) { func (srv *Server) OperatorMessage(message chat.Message) { srv.Players.Range(func(_ uuid.UUID, p *player.Player) bool { - if p.ClientSettings().ChatMode == 2 || !p.Operator() { + if p.ClientSettings().ChatMode == 2 || !p.Operator.Get() { return true } p.SendPacket(&packet.SystemChatMessage{ diff --git a/server/entity.go b/server/entity.go index 0562fbc..7400dc5 100644 --- a/server/entity.go +++ b/server/entity.go @@ -25,7 +25,7 @@ func (srv *Server) FindEntity(id int32) interface{} { func (srv *Server) FindEntityByUUID(id [16]byte) interface{} { if _, p := srv.Players.Range(func(_ uuid.UUID, p *player.Player) bool { - return p.UUID() != id + return p.Session.UUID() != id }); p != nil { return p } diff --git a/server/entity/pos/pos.go b/server/entity/pos/pos.go index a685029..7b0ddf7 100644 --- a/server/entity/pos/pos.go +++ b/server/entity/pos/pos.go @@ -1,100 +1,98 @@ package pos import ( + "fmt" "math" - "sync/atomic" + + "github.com/dynamitemc/dynamite/util/atomic" ) type EntityPosition struct { - x, y, z, - yaw, pitch atomic.Value - onGround *atomic.Bool + x, y, z *atomic.Value[float64] + yaw, pitch *atomic.Value[float32] + onGround *atomic.Value[bool] } -func NewEntityPosition() *EntityPosition { +func NewEntityPosition(x, y, z float64, yaw, pitch float32, ong bool) *EntityPosition { var e EntityPosition - e.SetX(0) - e.SetY(0) - e.SetZ(0) - e.SetYaw(0) - e.SetPitch(0) - - e.onGround = &atomic.Bool{} + e.x = atomic.NewValue(x) + e.y = atomic.NewValue(y) + e.z = atomic.NewValue(z) + e.yaw = atomic.NewValue(yaw) + e.pitch = atomic.NewValue(pitch) + e.onGround = atomic.NewValue(ong) return &e } func (pos *EntityPosition) X() float64 { - return pos.x.Load().(float64) + return pos.x.Get() } func (pos *EntityPosition) Y() float64 { - return pos.y.Load().(float64) + return pos.y.Get() } func (pos *EntityPosition) Z() float64 { - return pos.z.Load().(float64) + return pos.z.Get() } func (pos *EntityPosition) Yaw() float32 { - return pos.yaw.Load().(float32) + return pos.yaw.Get() } func (pos *EntityPosition) Pitch() float32 { - return pos.pitch.Load().(float32) + return pos.pitch.Get() } func (pos *EntityPosition) OnGround() bool { - return pos.onGround.Load() + return pos.onGround.Get() } func (pos *EntityPosition) SetX(x float64) { - pos.x.Store(x) + pos.x.Set(x) } func (pos *EntityPosition) SetY(y float64) { - pos.y.Store(y) + pos.y.Set(y) } func (pos *EntityPosition) SetZ(z float64) { - pos.z.Store(z) + pos.z.Set(z) } func (pos *EntityPosition) SetPosition(x, y, z float64) { - pos.x.Store(x) - pos.y.Store(y) - pos.z.Store(z) + pos.x.Set(x) + pos.y.Set(y) + pos.z.Set(z) } func (pos *EntityPosition) SetRotation(y, p float32) { - pos.yaw.Store(y) - pos.pitch.Store(p) + pos.yaw.Set(y) + pos.pitch.Set(p) } -func (pos *EntityPosition) All() (x, y, z float64, yaw, pitch float32, ong bool) { - return pos.X(), pos.Y(), pos.Z(), pos.Yaw(), pos.Pitch(), pos.OnGround() +func (pos *EntityPosition) Position() (x, y, z float64) { + return pos.x.Get(), pos.y.Get(), pos.z.Get() } -func (pos *EntityPosition) SetAll(x, y, z float64, yaw, pitch float32, ong bool) { - pos.SetX(x) - pos.SetY(y) - pos.SetZ(z) - - pos.SetYaw(yaw) - pos.SetPitch(pitch) - - pos.SetOnGround(ong) +func (pos *EntityPosition) Rotation() (yaw, pitch float32) { + return pos.yaw.Get(), pos.pitch.Get() } func (pos *EntityPosition) SetYaw(y float32) { - pos.yaw.Store(y) + pos.yaw.Set(y) } func (pos *EntityPosition) SetPitch(p float32) { - pos.pitch.Store(p) + pos.pitch.Set(p) } func (pos *EntityPosition) SetOnGround(ong bool) { - pos.onGround.Store(ong) + pos.onGround.Set(ong) +} + +func (pos *EntityPosition) String() string { + return fmt.Sprintf("(%f %f %f | %f%f)", pos.X(), pos.Y(), pos.Z(), pos.Yaw(), pos.Pitch()) } func DegreesToAngle(degrees float32) byte { diff --git a/server/handler/chat_command.go b/server/handler/chat_command.go index f805125..1bee6b0 100644 --- a/server/handler/chat_command.go +++ b/server/handler/chat_command.go @@ -10,10 +10,11 @@ import ( "github.com/dynamitemc/dynamite/logger/color" "github.com/dynamitemc/dynamite/server/commands" "github.com/dynamitemc/dynamite/server/player" + "github.com/google/uuid" ) func ChatCommandPacket(state *player.Player, graph *commands.Graph, log *logger.Logger, content string, timestamp, salt int64, sigs []packet.Argument) { - log.Info(color.FromChat(chat.NewMessage(fmt.Sprintf("[%s] Player %s (%s) issued server command /%s", state.IP(), state.Name(), state.UUID(), content)))) + log.Info(color.FromChat(chat.NewMessage(fmt.Sprintf("[%s] Player %s (%s) issued server command /%s", state.IP(), state.Session.Name(), uuid.UUID(state.Session.UUID()).String(), content)))) args := strings.Split(content, " ") cmd := args[0] var command *commands.Command diff --git a/server/handler/player_abilities.go b/server/handler/player_abilities.go index 4af34ba..00aa9c8 100644 --- a/server/handler/player_abilities.go +++ b/server/handler/player_abilities.go @@ -6,5 +6,5 @@ import ( ) func PlayerAbilities(state *player.Player, flags byte) { - state.SetFlying(flags == enum.PlayerAbilityFlying) + state.Flying.Set(flags == enum.PlayerAbilityFlying) } diff --git a/server/handler/player_action.go b/server/handler/player_action.go index 487e109..73e0c5f 100644 --- a/server/handler/player_action.go +++ b/server/handler/player_action.go @@ -9,7 +9,7 @@ import ( func PlayerAction(state *player.Player, pk *packet.PlayerActionServer) { switch pk.Status { case enum.PlayerActionStartedDigging: - if state.GameMode() == enum.GameModeCreative { + if state.GameMode.Get() == enum.GameModeCreative { state.BreakBlock(int64(pk.Location)) } state.BroadcastMetadataInArea(&packet.SetEntityMetadata{ @@ -27,7 +27,7 @@ func PlayerAction(state *player.Player, pk *packet.PlayerActionServer) { }) case enum.PlayerActionDropItemStack, enum.PlayerActionDropItem: if s, ok := state.Inventory.HeldItem(); ok { - state.SetPreviousSelectedSlot(s) + state.PreviousSelectedSlot.Set(s) state.Inventory.DeleteSlot(int8(s.Slot)) } //controller.DropSlot() diff --git a/server/handler/player_movement.go b/server/handler/player_movement.go index 6e76560..2b98ed6 100644 --- a/server/handler/player_movement.go +++ b/server/handler/player_movement.go @@ -9,7 +9,7 @@ func PlayerMovement( state *player.Player, p packet.Packet, ) { - if state.IsDead() { + if state.IsDead.Get() { return } x, y, z := state.Position.X(), state.Position.Y(), state.Position.Z() diff --git a/server/handler/set_creative_mode_slot.go b/server/handler/set_creative_mode_slot.go index d92f079..8e5bd83 100644 --- a/server/handler/set_creative_mode_slot.go +++ b/server/handler/set_creative_mode_slot.go @@ -10,14 +10,14 @@ import ( ) func SetCreativeModeSlot(state *player.Player, slot int16, data packet.Slot) { - if state.GameMode() != enum.GameModeCreative { + if state.GameMode.Get() != enum.GameModeCreative { state.Disconnect(chat.NewMessage("bruh cant use the creative button without creative")) return } s := inventory.NetworkSlotToDataSlot(slot) if !data.Present { if s, ok := state.Inventory.Slot(s); ok { - state.SetPreviousSelectedSlot(s) + state.PreviousSelectedSlot.Set(s) } //state.ClearItem(s) } else { diff --git a/server/handler/set_held_item.go b/server/handler/set_held_item.go index d0c1abc..52aaef5 100644 --- a/server/handler/set_held_item.go +++ b/server/handler/set_held_item.go @@ -3,5 +3,5 @@ package handler import "github.com/dynamitemc/dynamite/server/player" func SetHeldItem(state *player.Player, heldItem int16) { - state.SetSelectedSlot(int32(heldItem)) + state.Inventory.SelectedSlot.Set(int32(heldItem)) } diff --git a/server/handler/teleport_to_entity.go b/server/handler/teleport_to_entity.go index ecbaaa0..4c46471 100644 --- a/server/handler/teleport_to_entity.go +++ b/server/handler/teleport_to_entity.go @@ -7,7 +7,7 @@ import ( ) func TeleportToEntity(state *player.Player, uuid [16]byte) { - if state.GameMode() != enum.GameModeSpectator { + if state.GameMode.Get() != enum.GameModeSpectator { state.Disconnect(chat.NewMessage("Yo how do you do dat without gamemode spectator?")) return } diff --git a/server/handler/use_item_on.go b/server/handler/use_item_on.go index 90bea39..d5136c5 100644 --- a/server/handler/use_item_on.go +++ b/server/handler/use_item_on.go @@ -16,7 +16,7 @@ func UseItemOn(state *player.Player, pk *packet.UseItemOnServer, f func(d *world // todo check for snow/ flowers etc //return } - i, ok := state.Inventory.Slot(int8(state.SelectedSlot())) + i, ok := state.Inventory.Slot(int8(state.Inventory.SelectedSlot.Get())) if !ok { return } diff --git a/server/inventory/inventory.go b/server/inventory/inventory.go index fa64bfd..7188144 100644 --- a/server/inventory/inventory.go +++ b/server/inventory/inventory.go @@ -8,16 +8,17 @@ import ( "github.com/aimjel/minecraft/packet" "github.com/dynamitemc/dynamite/server/item" + "github.com/dynamitemc/dynamite/util/atomic" ) type Inventory struct { mu sync.RWMutex - selectedSlot int32 + SelectedSlot atomic.Value[int32] carriedItem item.Item inv []item.Item } -func From(inv []item.Item, selectedSlot int32) *Inventory { +func Import(inv []item.Item, selectedSlot int32) *Inventory { return &Inventory{inv: inv} } @@ -27,13 +28,15 @@ func (inv *Inventory) Clear() { clear(inv.inv) } -func (inv *Inventory) Data() []item.Item { +// Export returns the inventory slots for the player NBT data +func (inv *Inventory) Export() []item.Item { inv.mu.RLock() defer inv.mu.RUnlock() return inv.inv } -func (inv *Inventory) Packet() (i []packet.Slot) { +// Data returns the inventory slots for the set container content packet +func (inv *Inventory) Data() (i []packet.Slot) { inv.mu.RLock() defer inv.mu.RUnlock() i = make([]packet.Slot, 46) @@ -140,13 +143,7 @@ func (inv *Inventory) Collect(slot int8) { } func (inv *Inventory) HeldItem() (item.Item, bool) { - return inv.Slot(NetworkSlotToDataSlot(int16((inv.selectedSlot + 36)))) -} - -func (inv *Inventory) SetSelectedSlot(s int32) { - inv.mu.Lock() - defer inv.mu.Unlock() - inv.selectedSlot = s + return inv.Slot(NetworkSlotToDataSlot(int16((inv.SelectedSlot.Get() + 36)))) } func (inv *Inventory) SetCarriedItem(slot int8) { diff --git a/server/player/broadcast.go b/server/player/broadcast.go index 45e32e3..47f9479 100644 --- a/server/player/broadcast.go +++ b/server/player/broadcast.go @@ -20,7 +20,7 @@ import ( func (p *Player) BroadcastAnimation(animation uint8) { p.playerController.Range(func(u uuid.UUID, pl *Player) bool { - if !pl.IsSpawned(p.EntityID()) || u == p.UUID() { + if !pl.IsSpawned(p.EntityID()) || u == p.Session.UUID() { return true } @@ -58,11 +58,11 @@ func (p *Player) Attack(entityId int32) { x, y, z := p.Position.X(), p.Position.Y(), p.Position.Z() soundId := int32(519) if pl, ok := e.(*Player); ok { - if pl.GameMode() == enum.GameModeCreative { + if pl.GameMode.Get() == enum.GameModeCreative { return } - health := pl.Health() + health := pl.Health.Get() pl.SetHealth(health - 1) pl.SendPacket(&packet.DamageEvent{ EntityID: entityId, @@ -120,13 +120,13 @@ func (p *Player) Despawn() { } func (p *Player) BroadcastGamemode() { - gm := int32(p.GameMode()) + gm := int32(p.GameMode.Get()) p.playerController.Range(func(_ uuid.UUID, pl *Player) bool { pl.SendPacket(&packet.PlayerInfoUpdate{ Actions: 0x04, Players: []types.PlayerInfo{{ - UUID: p.UUID(), + UUID: p.Session.UUID(), GameMode: gm, }}, }) @@ -137,9 +137,9 @@ func (p *Player) BroadcastGamemode() { func (p *Player) sendEquipment(pl *Player) { slots := make(map[int8]packet.Slot) inv := p.Inventory - sel := p.SelectedSlot() + sel := p.Inventory.SelectedSlot.Get() - for _, s := range inv.Data() { + for _, s := range inv.Export() { switch s.Slot { case int8(sel): s, _ := s.ToPacketSlot() @@ -183,7 +183,7 @@ func (p *Player) BroadcastMetadataInArea(pk *packet.SetEntityMetadata) { func (p *Player) broadcastMetadataGlobal(pk *packet.SetEntityMetadata) { p.playerController.Range(func(u uuid.UUID, pl *Player) bool { - if u == p.UUID() { + if u == p.Session.UUID() { return true } pl.SendPacket(pk) @@ -215,7 +215,7 @@ func findEntityByUUID( id [16]byte, ) interface{} { if _, p := players.Range(func(_ uuid.UUID, p *Player) bool { - return p.UUID() != id + return p.Session.UUID() != id }); p != nil { return p } diff --git a/server/player/chat.go b/server/player/chat.go index 4b0dea1..ec33d6a 100644 --- a/server/player/chat.go +++ b/server/player/chat.go @@ -23,7 +23,7 @@ func (p *Player) HandleChat(pk *packet.ChatMessageServer) { prefix, suffix := p.GetPrefixSuffix() - net := chat.NewMessage(prefix + p.Name() + suffix).WithSuggestCommandClickEvent(fmt.Sprintf("/msg %s", p.Name())) + net := chat.NewMessage(prefix + p.Session.Name() + suffix).WithSuggestCommandClickEvent(fmt.Sprintf("/msg %s", p.Session.Name())) if !p.config.Chat.Secure { if !p.config.Chat.Colors || !p.HasPermissions([]string{"server.chat.colors"}) { @@ -88,9 +88,9 @@ func (p *Player) HandleChat(pk *packet.ChatMessageServer) { } } pl.SendPacket(&packet.PlayerChatMessage{ - Sender: p.UUID(), + Sender: p.Session.UUID(), MessageSignature: pk.Signature, - Index: pl.Index(), + Index: pl.index.Get(), Message: pk.Message, Timestamp: pk.Timestamp, Salt: pk.Salt, @@ -107,15 +107,15 @@ func (p *Player) HandleChat(pk *packet.ChatMessageServer) { func (p *Player) Whisper(pl *Player, msg string, timestamp, salt int64, sig []byte) { prefix, suffix := p.GetPrefixSuffix() prefix1, suffix1 := pl.GetPrefixSuffix() - tgt := chat.NewMessage(prefix1 + pl.Name() + suffix1) + tgt := chat.NewMessage(prefix1 + pl.Session.Name() + suffix1) p.SendPacket(&packet.PlayerChatMessage{ - Sender: p.UUID(), + Sender: p.Session.UUID(), Message: msg, //MessageSignature: sig, Salt: salt, Timestamp: timestamp, ChatType: enum.ChatTypeMsgCommandIncoming, - NetworkName: chat.NewMessage(prefix + p.Name() + suffix), + NetworkName: chat.NewMessage(prefix + p.Session.Name() + suffix), NetworkTargetName: &tgt, }) } diff --git a/server/player/movement.go b/server/player/movement.go index 4ecdfe7..ce41550 100644 --- a/server/player/movement.go +++ b/server/player/movement.go @@ -4,6 +4,7 @@ import ( "math" "github.com/aimjel/minecraft/packet" + "github.com/dynamitemc/dynamite/logger" "github.com/dynamitemc/dynamite/server/entity/pos" "github.com/dynamitemc/dynamite/server/enum" "github.com/google/uuid" @@ -26,25 +27,29 @@ func (p *Player) handleCenterChunk(oldx, oldz, newx, newz float64) { func (p *Player) HandleMovement(packetid int32, x1, y1, z1 float64, ya, pi float32, ong bool, teleport bool) { oldx, oldy, oldz := p.Position.X(), p.Position.Y(), p.Position.Z() - p.Position.SetAll(x1, y1, z1, ya, pi, ong) + p.Position.SetPosition(x1, y1, z1) + p.Position.SetRotation(ya, pi) + p.Position.SetOnGround(ong) p.handleCenterChunk(oldx, oldy, x1, y1) - if y1 > p.HighestY() { - p.SetHighestY(y1) + if y1 > p.HighestY.Get() { + p.HighestY.Set(y1) } else { - if g := p.GameMode(); g == enum.GameModeSurvival || g == enum.GameModeAdventure { - d := p.HighestY() - y1 + if g := p.GameMode.Get(); g == enum.GameModeSurvival || g == enum.GameModeAdventure { + d := p.HighestY.Get() - y1 if d > 3 { if b := p.OnBlock(); b != nil && b.EncodedName() != "minecraft:air" && b.EncodedName() != "minecraft:water" { d -= 3 if int(d) > 0 { p.Damage(float32(d), enum.DamageTypeFall) } - p.SetHighestY(0) + p.HighestY.Set(0) } } } } + logger.Println(p.Position) + distance := math.Sqrt((x1-oldx)*(x1-oldx) + (y1-oldy)*(y1-oldy) + (z1-oldz)*(z1-oldz)) /*if distance > 100 && !teleport { p.Teleport(oldx, oldy, oldz, yaw, pitch) @@ -110,7 +115,7 @@ func (p *Player) HandleMovement(packetid int32, x1, y1, z1 float64, ya, pi float } p.playerController.Range(func(u uuid.UUID, pl *Player) bool { - if p.UUID() == u { + if p.Session.UUID() == u { return true } if !pl.InView(p.Position.X(), p.Position.Y(), p.Position.Z()) { diff --git a/server/player/player.go b/server/player/player.go index b80a8c1..46f5cd1 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -5,7 +5,6 @@ import ( "math/rand" "slices" "sync" - "sync/atomic" "github.com/aimjel/minecraft/chat" "github.com/aimjel/minecraft/packet" @@ -26,6 +25,7 @@ import ( "github.com/dynamitemc/dynamite/server/session" "github.com/dynamitemc/dynamite/server/world" "github.com/dynamitemc/dynamite/server/world/chunk" + "github.com/dynamitemc/dynamite/util/atomic" "github.com/google/uuid" ) @@ -69,61 +69,47 @@ type Player struct { playerController *controller.Controller[uuid.UUID, *Player] entityController *controller.Controller[int32, entity.Entity] - session session.Session + Session session.Session entityID int32 - isHardCore *atomic.Bool - gameMode byte + GameMode *atomic.Value[byte] - dead *atomic.Bool - health float32 - food *atomic.Int32 - foodSaturation float32 + IsDead *atomic.Value[bool] + Health *atomic.Value[float32] + FoodLevel *atomic.Value[int32] + FoodSaturation *atomic.Value[float32] data *world.PlayerData Inventory *inventory.Inventory - previousSelectedSlot item.Item - selectedSlot *atomic.Int32 + PreviousSelectedSlot *atomic.Value[item.Item] dimension *world.Dimension clientInfo clientInfo Position *epos.EntityPosition - operator, flying *atomic.Bool - highestY float64 + Operator, Flying *atomic.Value[bool] + HighestY *atomic.Value[float64] spawnedEntities []int32 loadedChunks map[[2]int32]struct{} - sessionID [16]byte - publicKey []byte - keySignature []byte - expires int64 + sessionID *atomic.Value[[16]byte] + publicKey *atomic.Value[[]byte] + keySignature *atomic.Value[[]byte] + expires *atomic.Value[int64] previousMessages []packet.PreviousMessage acknowledgedMessageSignatures [][]byte - index *atomic.Int32 + index *atomic.Value[int32] newID func() int32 mu sync.RWMutex } -func newAtomicInt32(val int32) *atomic.Int32 { - v := &atomic.Int32{} - v.Add(val) - return v -} - -func newAtomicBool(val bool) *atomic.Bool { - v := &atomic.Bool{} - v.Store(val) - return v -} - func New( players *controller.Controller[uuid.UUID, *Player], entities *controller.Controller[int32, entity.Entity], @@ -146,31 +132,28 @@ func New( lang: lang, playerController: players, entityController: entities, - session: session, + Session: session, entityID: entityId, - isHardCore: &atomic.Bool{}, - gameMode: byte(data.PlayerGameType), - dead: &atomic.Bool{}, - food: newAtomicInt32(data.FoodLevel), data: data, - Inventory: inventory.From(data.Inventory, data.SelectedItemSlot), - selectedSlot: newAtomicInt32(data.SelectedItemSlot), - dimension: dimension, - operator: &atomic.Bool{}, - flying: newAtomicBool(data.Abilities.Flying), - index: &atomic.Int32{}, - health: data.Health, - foodSaturation: data.FoodSaturationLevel, - newID: newID, - Position: epos.NewEntityPosition(), + + GameMode: atomic.NewValue(byte(data.PlayerGameType)), + Inventory: inventory.Import(data.Inventory, data.SelectedItemSlot), + dimension: dimension, + Flying: atomic.NewValue(data.Abilities.Flying), + + Health: atomic.NewValue(data.Health), + FoodLevel: atomic.NewValue(data.FoodLevel), + FoodSaturation: atomic.NewValue(data.FoodSaturationLevel), + + newID: newID, + Position: epos.NewEntityPosition(data.Pos[0], data.Pos[1], data.Pos[2], data.Rotation[0], data.Rotation[1], data.OnGround), } - pl.Position.SetAll(data.Pos[0], data.Pos[1], data.Pos[2], data.Rotation[0], data.Rotation[1], data.OnGround) pl.clientInfo.ViewDistance = vd prefix, suffix := pl.GetPrefixSuffix() pl.PlaceholderContext = placeholder.New(map[string]string{ - "player": pl.Name(), + "player": pl.Session.Name(), "player_prefix": prefix, "player_suffix": suffix, }, ph) @@ -179,34 +162,33 @@ func New( } func (p *Player) Save() { - p.mu.Lock() - p.data.Pos[0], p.data.Pos[1], p.data.Pos[2], p.data.Rotation[0], p.data.Rotation[1], p.data.OnGround = p.Position.All() - p.data.PlayerGameType = int32(p.gameMode) - p.data.Inventory = p.Inventory.Data() - p.data.Abilities.Flying = p.flying.Load() + p.data.Pos[0], p.data.Pos[1], p.data.Pos[2], p.data.Rotation[0], p.data.Rotation[1], p.data.OnGround = p.Position.X(), p.Position.Y(), p.Position.Z(), p.Position.Yaw(), p.Position.Pitch(), p.Position.OnGround() + p.data.PlayerGameType = int32(p.GameMode.Get()) + p.data.Inventory = p.Inventory.Export() + p.data.Abilities.Flying = p.Flying.Get() p.data.Dimension = p.dimension.Type() - p.data.SelectedItemSlot = p.selectedSlot.Load() - p.data.Health = p.health - p.data.FoodSaturationLevel = p.foodSaturation - p.data.FoodLevel = p.food.Load() - p.mu.Unlock() + p.data.SelectedItemSlot = p.Inventory.SelectedSlot.Get() + p.data.Health = p.Health.Get() + p.data.FoodSaturationLevel = p.FoodSaturation.Get() + p.data.FoodLevel = p.FoodLevel.Get() p.data.Save() } func (p *Player) Respawn(d *world.Dimension) { - p.SetDead(false) + p.IsDead.Set(false) p.SetHealth(20) - p.SetFoodLevel(20) - p.SetFoodSaturationLevel(5) + p.FoodLevel.Set(20) + p.FoodSaturation.Set(5) - p.SendPacket(&packet.Respawn{ - GameMode: p.GameMode(), - PreviousGameMode: -1, + p.Session.SendPacket(&packet.Respawn{ DimensionType: d.Type(), DimensionName: d.Type(), + GameMode: p.GameMode.Get(), + PreviousGameMode: -1, HashedSeed: d.Seed(), }) + p.SetDimension(d) var x1, y1, z1 int32 @@ -228,14 +210,13 @@ func (p *Player) Respawn(d *world.Dimension) { p.Inventory.Clear() } - p.SendPacket(&packet.SetContainerContent{ - WindowID: 0, - StateID: 1, - Slots: p.Inventory.Packet(), + p.Session.SendPacket(&packet.SetContainerContent{ + StateID: 1, + Slots: p.Inventory.Data(), }) chunkX, chunkZ := math.Floor(float64(x1)/16), math.Floor(float64(z1)/16) - p.SendPacket(&packet.SetCenterChunk{ChunkX: int32(chunkX), ChunkZ: int32(chunkZ)}) + p.Session.SendPacket(&packet.SetCenterChunk{ChunkX: int32(chunkX), ChunkZ: int32(chunkZ)}) p.Teleport(float64(x1), float64(y1), float64(z1), yaw, pitch) p.SendChunks(d) @@ -251,11 +232,11 @@ func (p *Player) Login(d *world.Dimension) { x1, y1, z1 := p.Position.X(), p.Position.Y(), p.Position.Z() yaw, pitch := p.Position.Yaw(), p.Position.Pitch() - p.logger.Info("[%s] Player %s (%s) has joined the server with entity id %d at [%s]%f %f %f", p.IP(), p.Name(), p.UUID(), p.entityID, p.Dimension().Type(), x1, y1, z1) + p.logger.Info("[%s] Player %s (%s) has joined the server with entity id %d at [%s]%f %f %f", p.IP(), p.Session.Name(), uuid.UUID(p.Session.UUID()), p.entityID, p.Dimension().Type(), x1, y1, z1) p.SendPacket(&packet.JoinGame{ EntityID: p.entityID, - IsHardcore: p.IsHardcore(), - GameMode: p.GameMode(), + IsHardcore: p.config.Hardcore, + GameMode: p.GameMode.Get(), PreviousGameMode: -1, DimensionNames: []string{"minecraft:overworld", "minecraft:the_nether", "minecraft:the_end"}, DimensionType: d.Type(), @@ -264,14 +245,14 @@ func (p *Player) Login(d *world.Dimension) { ViewDistance: int32(p.clientInfo.ViewDistance), SimulationDistance: int32(p.clientInfo.ViewDistance), //todo fix this }) - p.SendPacket(&packet.PluginMessage{ + p.Session.SendPacket(&packet.PluginMessage{ Channel: "minecraft:brand", Data: []byte("Dynamite"), }) chunkX, chunkZ := math.Floor(x1/16), math.Floor(z1/16) - p.SendPacket(&packet.SetCenterChunk{ChunkX: int32(chunkX), ChunkZ: int32(chunkZ)}) - p.Position.SetAll(x1, y1, z1, yaw, pitch, false) + p.Session.SendPacket(&packet.SetCenterChunk{ChunkX: int32(chunkX), ChunkZ: int32(chunkZ)}) + p.SendChunks(d) logger.Println("sent chunks") @@ -327,11 +308,11 @@ func (p *Player) Damage(health float32, typ int32) { }) return true }) - p.SetHealth(p.Health() - health) + p.SetHealth(p.Health.Get() - health) } func (p *Player) Kill(message string) { - p.SetDead(true) + p.IsDead.Set(true) if f, _ := world.GameRule(p.Dimension().World().Gamerules()["doImmediateRespawn"]).Bool(); !f { p.SendPacket(&packet.GameEvent{ Event: enum.GameEventEnableRespawnScreen, @@ -401,7 +382,7 @@ func (p *Player) Disconnect(reason chat.Message) { pk := &packet.DisconnectPlay{} pk.Reason = reason p.SendPacket(pk) - p.session.Close(nil) + p.Session.Close(nil) } func (p *Player) IsChunkLoaded(x, z int32) bool { @@ -477,7 +458,7 @@ func (p *Player) ChunkPosition() (x int32, z int32) { } func (p *Player) GetPrefixSuffix() (prefix string, suffix string) { - group := permission.GetGroup(permission.GetPlayer(p.UUID().String()).Group) + group := permission.GetGroup(permission.GetPlayer(uuid.UUID(p.Session.UUID()).String()).Group) return group.Prefix, group.Suffix } @@ -500,7 +481,7 @@ func (p *Player) SpawnPlayer(pl *Player) { p.SendPacket(&packet.SpawnPlayer{ EntityID: entityId, - PlayerUUID: pl.session.UUID(), + PlayerUUID: pl.Session.UUID(), X: x, Y: y, Z: z, @@ -542,13 +523,13 @@ func (p *Player) IntitializeData() { p.SendPacket(&packet.SetContainerContent{ WindowID: 0, StateID: 1, - Slots: p.Inventory.Packet(), + Slots: p.Inventory.Data(), }) - p.SendPacket(&packet.SetHeldItem{Slot: int8(p.SelectedSlot())}) + p.SendPacket(&packet.SetHeldItem{Slot: int8(p.Inventory.SelectedSlot.Get())}) p.SendPacket(&packet.SetHealth{ - Health: p.Health(), - FoodSaturation: p.FoodSaturationLevel(), - Food: p.FoodLevel(), + Health: p.Health.Get(), + FoodSaturation: p.FoodSaturation.Get(), + Food: p.FoodLevel.Get(), }) } @@ -573,7 +554,7 @@ func (p *Player) SetSlot(slot int8, data item.Item) { } func (p *Player) DropSlot() { - item := p.PreviousSelectedSlot() + item := p.PreviousSelectedSlot.Get() s, _ := item.ToPacketSlot() x, y, z := p.Position.X(), p.Position.Y(), p.Position.Z() @@ -631,15 +612,15 @@ func (p *Player) TeleportToEntity(uuid [16]byte) { } func (p *Player) IP() string { - return p.session.RemoteAddr().String() + return p.Session.RemoteAddr().String() } func (s *Player) HasPermissions(perms []string) bool { - if s.Operator() { + if s.Operator.Get() { return true } - return permission.HasPermissions(s.Name(), perms) + return permission.HasPermissions(s.Session.Name(), perms) } func (p *Player) InView(x2, y2, z2 float64) bool { @@ -673,10 +654,6 @@ func (p *Player) CacheMessage(s []byte) { p.acknowledgedMessageSignatures = append(p.acknowledgedMessageSignatures, s) } -func (p *Player) Index() int32 { - return p.index.Load() -} - func (p *Player) PreviousMessages() []packet.PreviousMessage { p.mu.RLock() defer p.mu.RUnlock() @@ -688,11 +665,11 @@ func (p *Player) AddMessage(sig []byte) { if len(p.previousMessages) != 20 { p.previousMessages = append([]packet.PreviousMessage{ { - MessageID: p.index.Load(), + MessageID: p.index.Get(), Signature: sig, }, }, p.previousMessages...) } p.mu.Unlock() - p.index.Add(1) + p.index.Set(p.index.Get() + 1) } diff --git a/server/player/private.go b/server/player/private.go index 15dcd03..2f95630 100644 --- a/server/player/private.go +++ b/server/player/private.go @@ -7,31 +7,18 @@ import ( "github.com/aimjel/minecraft/packet" "github.com/aimjel/minecraft/protocol/types" "github.com/dynamitemc/dynamite/server/enum" - "github.com/dynamitemc/dynamite/server/item" "github.com/dynamitemc/dynamite/server/world" "github.com/google/uuid" ) -func (p *Player) UUID() uuid.UUID { - return p.session.UUID() -} - -func (p *Player) Name() string { - return p.session.Name() +func (p *Player) SendPacket(pk packet.Packet) error { + return p.Session.SendPacket(pk) } func (p *Player) EntityID() int32 { return p.entityID } -func (p *Player) SendPacket(pk packet.Packet) error { - return p.session.SendPacket(pk) -} - -func (p *Player) ReadPacket() (packet.Packet, error) { - return p.session.ReadPacket() -} - func (p *Player) ClientSettings() clientInfo { p.mu.RLock() defer p.mu.RUnlock() @@ -56,7 +43,7 @@ func (p *Player) SetClientSettings(pk *packet.ClientSettings) { } func (p *Player) Properties() []types.Property { - return p.session.Properties() + return p.Session.Properties() } func (p *Player) Dimension() *world.Dimension { @@ -71,33 +58,14 @@ func (p *Player) SetDimension(d *world.Dimension) { p.dimension = d } -func (p *Player) IsDead() bool { - return p.dead.Load() -} - -func (p *Player) SetDead(a bool) { - p.dead.Store(a) -} - -func (p *Player) Health() float32 { - p.mu.RLock() - defer p.mu.RUnlock() - return p.health -} - func (p *Player) SetHealth(health float32) { if health < 0 { health = 0 } - p.mu.Lock() - p.health = health - p.mu.Unlock() - food, saturation := p.FoodLevel(), p.FoodSaturationLevel() - p.SendPacket(&packet.SetHealth{ - Health: health, - Food: food, - FoodSaturation: saturation, - }) + + p.Health.Set(health) + food, saturation := p.FoodLevel.Get(), p.FoodSaturation.Get() + p.Session.SendPacket(&packet.SetHealth{Health: health, Food: food, FoodSaturation: saturation}) /*p.broadcastMetadataGlobal(&packet.SetEntityMetadata{ EntityID: p.EntityID(), Health: &health, @@ -107,118 +75,39 @@ func (p *Player) SetHealth(health float32) { } } -func (p *Player) FoodLevel() int32 { - return p.food.Load() -} - -func (p *Player) SetFoodLevel(level int32) { - p.food.Store(level) -} - -func (p *Player) FoodSaturationLevel() float32 { - p.mu.RLock() - defer p.mu.RUnlock() - return p.foodSaturation -} - -func (p *Player) SetFoodSaturationLevel(level float32) { - p.mu.Lock() - defer p.mu.Unlock() - p.foodSaturation = level -} - func (p *Player) SavedAbilities() world.Abilities { return p.data.Abilities } -func (p *Player) SetFlying(val bool) { - p.flying.Store(val) -} - -func (p *Player) IsHardcore() bool { - return p.isHardCore.Load() -} - func (p *Player) SetGameMode(gm byte) { - p.mu.Lock() - p.gameMode = gm - p.mu.Unlock() - p.SendPacket(&packet.GameEvent{ - Event: enum.GameEventChangeGamemode, - Value: float32(gm), - }) - p.BroadcastGamemode() -} - -func (p *Player) GameMode() byte { - p.mu.RLock() - defer p.mu.RUnlock() - return p.gameMode -} - -func (p *Player) SetHighestY(y float64) { - p.mu.Lock() - defer p.mu.Unlock() - p.highestY = y -} - -func (p *Player) HighestY() float64 { - p.mu.RLock() - defer p.mu.RUnlock() - return p.highestY -} + p.GameMode.Set(gm) -func (p *Player) Operator() bool { - return p.operator.Load() + p.Session.SendPacket(&packet.GameEvent{Event: enum.GameEventChangeGamemode, Value: float32(gm)}) + p.BroadcastGamemode() } func (p *Player) SetOperator(op bool) { - p.operator.Store(op) + p.Operator.Set(op) v := enum.EntityStatusPlayerOpPermissionLevel0 if op { v = enum.EntityStatusPlayerOpPermissionLevel4 } - p.SendPacket(&packet.EntityEvent{ - EntityID: p.entityID, - Status: v, - }) -} -func (p *Player) SetSelectedSlot(h int32) { - p.selectedSlot.Store(h) - p.Inventory.SetSelectedSlot(h) -} + p.Session.SendPacket(&packet.EntityEvent{EntityID: p.entityID, Status: v}) -func (p *Player) SelectedSlot() int32 { - return p.selectedSlot.Load() } - -func (p *Player) PreviousSelectedSlot() item.Item { - p.mu.RLock() - defer p.mu.RUnlock() - return p.previousSelectedSlot -} - -func (p *Player) SetPreviousSelectedSlot(s item.Item) { - p.mu.Lock() - defer p.mu.Unlock() - p.previousSelectedSlot = s -} - func (p *Player) SetSessionID(id [16]byte, pk, ks []byte, expires int64) { - p.mu.Lock() - p.sessionID = id - p.publicKey = bytes.Clone(pk) - p.keySignature = bytes.Clone(ks) - p.expires = expires - p.mu.Unlock() + p.sessionID.Set(id) + p.publicKey.Set(bytes.Clone(pk)) + p.keySignature.Set(bytes.Clone(ks)) + p.expires.Set(expires) p.playerController.Range(func(_ uuid.UUID, pl *Player) bool { - pl.SendPacket(&packet.PlayerInfoUpdate{ + pl.Session.SendPacket(&packet.PlayerInfoUpdate{ Actions: 0x02, Players: []types.PlayerInfo{ { - UUID: p.session.UUID(), + UUID: p.Session.UUID(), ChatSessionID: id, PublicKey: bytes.Clone(pk), KeySignature: bytes.Clone(ks), @@ -231,9 +120,7 @@ func (p *Player) SetSessionID(id [16]byte, pk, ks []byte, expires int64) { } func (p *Player) SessionID() (id [16]byte, pk, ks []byte, expires int64) { - p.mu.RLock() - defer p.mu.RUnlock() - return p.sessionID, p.publicKey, p.keySignature, p.expires + return p.sessionID.Get(), p.publicKey.Get(), p.keySignature.Get(), p.expires.Get() } /*// SetSkin allows you to set the player's skin. @@ -268,11 +155,11 @@ func (p *Player) SetCape(url string) { func (p *Player) SetDisplayName(name *chat.Message) { p.playerController.Range(func(_ uuid.UUID, pl *Player) bool { - pl.SendPacket(&packet.PlayerInfoUpdate{ + pl.Session.SendPacket(&packet.PlayerInfoUpdate{ Actions: 0x20, Players: []types.PlayerInfo{ { - UUID: p.UUID(), + UUID: p.Session.UUID(), DisplayName: name, }, }, diff --git a/server/server.go b/server/server.go index 405167a..ccd3897 100644 --- a/server/server.go +++ b/server/server.go @@ -191,10 +191,10 @@ func (srv *Server) handleNewConn(conn *minecraft.Conn) { plyr.IntitializeData() if err := srv.handlePackets(plyr); err != nil { - srv.Logger.Info("[%s] Player %s (%s) has left the server", conn.RemoteAddr().String(), conn.Name(), plyr.UUID()) + srv.Logger.Info("[%s] Player %s (%s) has left the server", conn.RemoteAddr().String(), conn.Name(), uuid) srv.GlobalMessage(srv.Lang.Translate("player.leave", plyr.PlaceholderContext)) - srv.Players.Delete(plyr.UUID()) + srv.Players.Delete(plyr.Session.UUID()) plyr.Save() srv.playerlistRemove(conn.UUID()) plyr.Despawn() @@ -209,20 +209,20 @@ func (srv *Server) addPlayer(p *player.Player) { EnforcesSecureChat: true, }) newPlayer := types.PlayerInfo{ - UUID: p.UUID(), - Name: p.Name(), + UUID: p.Session.UUID(), + Name: p.Session.Name(), Properties: p.Properties(), Listed: true, } - srv.Players.Set(p.UUID(), p) + srv.Players.Set(p.Session.UUID(), p) players := make([]types.PlayerInfo, 0, srv.Players.Count()) srv.Players.Range(func(_ uuid.UUID, pl *player.Player) bool { id, pk, ks, ex := pl.SessionID() players = append(players, types.PlayerInfo{ - UUID: pl.UUID(), - Name: pl.Name(), + UUID: pl.Session.UUID(), + Name: pl.Session.Name(), Properties: pl.Properties(), Listed: true, PublicKey: bytes.Clone(pk), @@ -230,7 +230,7 @@ func (srv *Server) addPlayer(p *player.Player) { ChatSessionID: id, ExpiresAt: ex, }) - if pl.UUID() != p.UUID() { + if pl.Session.UUID() != p.Session.UUID() { pl.SendPacket(&packet.PlayerInfoUpdate{ Actions: 0x01 | 0x08, Players: []types.PlayerInfo{newPlayer}, @@ -250,7 +250,7 @@ func (srv *Server) addPlayer(p *player.Player) { }) if srv.Config.Web.Enable { - web.AddPlayer(p.Name(), p.UUID().String()) + web.AddPlayer(p.Session.Name(), uuid.UUID(p.Session.UUID()).String()) } srv.GlobalMessage(srv.Lang.Translate("player.join", p.PlaceholderContext)) @@ -265,7 +265,7 @@ func (srv *Server) handlePackets(p *player.Player) error { default: } - packt, err := p.ReadPacket() + packt, err := p.Session.ReadPacket() if err != nil { return err } @@ -321,12 +321,12 @@ func (srv *Server) Reload() error { permission.Clear() srv.Players.Range(func(_ uuid.UUID, p *player.Player) bool { - if srv.Config.Whitelist.Enforce && srv.Config.Whitelist.Enable && !srv.IsWhitelisted(p.UUID()) { + if srv.Config.Whitelist.Enforce && srv.Config.Whitelist.Enable && !srv.IsWhitelisted(p.Session.UUID()) { p.Disconnect(srv.Lang.Translate("disconnect.not_whitelisted", nil)) return true } - p.SetOperator(srv.IsOperator(p.UUID())) + p.SetOperator(srv.IsOperator(p.Session.UUID())) p.SendCommands(srv.commandGraph) return true }) @@ -335,7 +335,7 @@ func (srv *Server) Reload() error { func (srv *Server) FindPlayer(username string) *player.Player { _, p := srv.Players.Range(func(_ uuid.UUID, p *player.Player) bool { - return !strings.EqualFold(p.Name(), username) + return !strings.EqualFold(p.Session.Name(), username) }) return p @@ -362,7 +362,7 @@ func (srv *Server) Close() { permission.Save() srv.Players.Range(func(_ uuid.UUID, p *player.Player) bool { - p.Disconnect(srv.Lang.Translate("disconnect.server_shutdown", nil)) + p.Disconnect(srv.Lang.Translate("disconnect.server_shutdown", srv.PlaceholderContext)) p.Save() return true }) diff --git a/server/session/session.go b/server/session/session.go index 3c357e9..b916414 100644 --- a/server/session/session.go +++ b/server/session/session.go @@ -15,4 +15,15 @@ type Session interface { UUID() [16]byte Properties() []types.Property RemoteAddr() net.Addr + + /*SetHealth(health float32, food int32, foodSaturation float32) + GameEvent(event uint8, value float32) + EntityEvent(entityId int32, event int8) + PlayerInfoUpdate(actions uint8, players []types.PlayerInfo) + Respawn(dimension *world.Dimension, gameMode uint8, dataKept uint8, deathDimensionName string, deathLocation uint64, partialCooldown int32) + SetContainerContent(windowId uint8, stateId int32, slots []packet.Slot) + SetCenterChunk(x, z int32) + SetDefaultSpawnPosition(pos types.Position, angle float32) + PluginMessage(channel string, data []byte) + SystemChatMessage()*/ } diff --git a/server/world/tick/tick.go b/server/world/tick/tick.go index dd09934..c94a59e 100644 --- a/server/world/tick/tick.go +++ b/server/world/tick/tick.go @@ -17,9 +17,11 @@ import ( var pcg32 = pcg.NewPCG32() type Ticker struct { - ticker *time.Ticker - server *server.Server - started bool + ticker *time.Ticker + server *server.Server + started bool + currentTick uint + paused bool } func New(srv *server.Server, tps int64) *Ticker { @@ -38,11 +40,21 @@ func (t *Ticker) Restart(tps int64) { t.ticker = time.NewTicker(time.Second / time.Duration(tps)) } +func (t *Ticker) Pause() { + t.paused = true +} + +func (t *Ticker) Resume() { + t.paused = false +} + func (t *Ticker) tickLoop() { - var n uint for range t.ticker.C { - t.tick(n) - n++ + if t.paused { + continue + } + t.tick(t.currentTick) + t.currentTick++ } } diff --git a/util/atomic/atomic.go b/util/atomic/atomic.go new file mode 100644 index 0000000..c9873d1 --- /dev/null +++ b/util/atomic/atomic.go @@ -0,0 +1,32 @@ +package atomic + +import "sync/atomic" + +type Value[T any] struct { + val atomic.Value +} + +func (v *Value[T]) Get() T { + var val T + if v == nil { + return val + } + if v, ok := v.val.Load().(T); ok { + val = v + } + return val +} + +func (v *Value[T]) Set(val T) { + if v == nil { + v = &Value[T]{} + } + v.val.Store(val) +} + +func NewValue[T any](initialValue T) *Value[T] { + v := &Value[T]{} + v.Set(initialValue) + + return v +}