Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions server/player/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/df-mc/dragonfly/server/entity"
"github.com/df-mc/dragonfly/server/entity/effect"
"github.com/df-mc/dragonfly/server/item/inventory"
"github.com/df-mc/dragonfly/server/player/permission"
"github.com/df-mc/dragonfly/server/player/skin"
"github.com/df-mc/dragonfly/server/session"
"github.com/df-mc/dragonfly/server/world"
Expand All @@ -27,6 +28,9 @@ type Config struct {
Locale language.Tag
GameMode world.GameMode

// PermissionLevel is the permission level the client displays for itself.
PermissionLevel permission.Level

Position mgl64.Vec3
Rotation cube.Rotation
Velocity mgl64.Vec3
Expand Down Expand Up @@ -72,6 +76,7 @@ func (cfg Config) Apply(data *world.EntityData) {
mc: &entity.MovementComputer{Gravity: 0.08, Drag: 0.02, DragBeforeGravity: true},
heldSlot: &slot,
gameMode: conf.GameMode,
permission: conf.PermissionLevel,
skin: conf.Skin,
enchantSeed: conf.EnchantmentSeed,
s: conf.Session,
Expand Down
34 changes: 34 additions & 0 deletions server/player/permission/level.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package permission

import (
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)

// Level represents the permission level a client displays for itself. It only decides what the client shows
// and allows locally, such as the command button in the chat window: the server does not use it to authorise
// anything. Whether a player may run a command is decided by cmd.Allower alone.
type Level struct {
level
}

type level uint8

// Member is the level of an ordinary client. It is the level a player has unless another one is set.
func Member() Level {
return Level{0}
}

// Operator is the level of a client that vanilla considers an operator.
func Operator() Level {
return Level{1}
}

// Permissions returns the player permission level and command permission level sent to a client for the
// Level.
func (l level) Permissions() (byte, byte) {
if l == 1 {
return packet.PermissionLevelOperator, protocol.CommandPermissionLevelGameDirectors
}
return packet.PermissionLevelMember, protocol.CommandPermissionLevelAny
}
30 changes: 26 additions & 4 deletions server/player/player.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/df-mc/dragonfly/server/player/form"
"github.com/df-mc/dragonfly/server/player/hud"
"github.com/df-mc/dragonfly/server/player/input"
"github.com/df-mc/dragonfly/server/player/permission"
"github.com/df-mc/dragonfly/server/player/scoreboard"
"github.com/df-mc/dragonfly/server/player/skin"
"github.com/df-mc/dragonfly/server/player/title"
Expand All @@ -48,10 +49,11 @@ type playerData struct {
absorptionHealth float64
scale float64

gameMode world.GameMode
skin skin.Skin
s *session.Session
h Handler
gameMode world.GameMode
permission permission.Level
skin skin.Skin
s *session.Session
h Handler

inv, offHand, enderChest, ui *inventory.Inventory
armour *inventory.Armour
Expand Down Expand Up @@ -1546,6 +1548,25 @@ func (p *Player) GameMode() world.GameMode {
return p.gameMode
}

// SetPermissionLevel changes the permission level the Player's client displays
// for itself. It unlocks vanilla functionality that the client restricts to a
// permission level, such as the command button in the chat window, but does not
// authorise the Player to do anything by itself: cmd.Allower decides which
// commands a Player may run.
func (p *Player) SetPermissionLevel(level permission.Level) {
if p.permission == level {
return
}
p.permission = level
p.session().SendAbilities(p)
}

// PermissionLevel returns the permission level the Player's client displays for
// itself, as set by SetPermissionLevel.
func (p *Player) PermissionLevel() permission.Level {
return p.permission
}

// HasCooldown returns true if the item passed has an active cooldown, meaning it currently cannot be used again. If the
// world.Item passed is nil, HasCooldown always returns false.
func (p *Player) HasCooldown(item world.Item) bool {
Expand Down Expand Up @@ -3388,6 +3409,7 @@ func (p *Player) Data() Config {
Name: p.nameTag,
Locale: p.locale,
GameMode: p.gameMode,
PermissionLevel: p.permission,
Position: p.Position(),
Rotation: p.Rotation(),
Velocity: p.Velocity(),
Expand Down
2 changes: 2 additions & 0 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,8 @@ func (srv *Server) finaliseConn(ctx context.Context, conn session.Conn, l Listen
data.Dimension = int32(dim)
data.Yaw, data.Pitch = float32(d.Rotation.Yaw()), float32(d.Rotation.Pitch())

data.PlayerPermissions, _ = d.PermissionLevel.Permissions()

data.EmoteChatMuted = srv.conf.MuteEmoteChat

if err := conn.StartGameContext(ctx, data); err != nil {
Expand Down
2 changes: 2 additions & 0 deletions server/session/controllable.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/df-mc/dragonfly/server/player/form"
"github.com/df-mc/dragonfly/server/player/hud"
"github.com/df-mc/dragonfly/server/player/input"
"github.com/df-mc/dragonfly/server/player/permission"
"github.com/df-mc/dragonfly/server/player/skin"
"github.com/df-mc/dragonfly/server/world"
"github.com/go-gl/mathgl/mgl64"
Expand Down Expand Up @@ -51,6 +52,7 @@ type Controllable interface {
Chat(msg ...any)
ExecuteCommand(commandLine string)
GameMode() world.GameMode
PermissionLevel() permission.Level
SetGameMode(mode world.GameMode)
Effects() []effect.Effect

Expand Down
5 changes: 3 additions & 2 deletions server/session/player.go
Original file line number Diff line number Diff line change
Expand Up @@ -545,10 +545,11 @@ func (s *Session) SendAbilities(c Controllable) {
if mode.AllowsInteraction() {
abilities |= protocol.AbilityDoorsAndSwitches | protocol.AbilityOpenContainers | protocol.AbilityAttackPlayers | protocol.AbilityAttackMobs
}
perm, cmdPerm := c.PermissionLevel().Permissions()
s.writePacket(&packet.UpdateAbilities{AbilityData: protocol.AbilityData{
EntityUniqueID: selfEntityRuntimeID,
PlayerPermissions: packet.PermissionLevelMember,
CommandPermissions: protocol.CommandPermissionLevelAny,
PlayerPermissions: perm,
CommandPermissions: cmdPerm,
Layers: []protocol.AbilityLayer{
{
Type: protocol.AbilityLayerTypeBase,
Expand Down