From 1b94867f05942b41a85674ff9b06f0575c58d24e Mon Sep 17 00:00:00 2001 From: Flonja <20887403+Flonja@users.noreply.github.com> Date: Tue, 23 Jan 2024 14:55:38 +0100 Subject: [PATCH 1/2] Implementation of a view layer Co-authored-by: RestartFU <45609733+restartfu@users.noreply.github.com> --- server/player/player.go | 5 ++ server/session/session.go | 4 ++ server/session/view_layer.go | 11 +++++ server/session/world.go | 12 ++++- server/world/view_layer.go | 92 ++++++++++++++++++++++++++++++++++++ 5 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 server/session/view_layer.go create mode 100644 server/world/view_layer.go diff --git a/server/player/player.go b/server/player/player.go index dcda971d3..b5bf05e33 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -2286,6 +2286,11 @@ func (p *Player) Tick(w *world.World, current int64) { } } +// ViewLayer ... +func (p *Player) ViewLayer() *world.ViewLayer { + return p.session().ViewLayer() +} + // tickAirSupply tick's the player's air supply, consuming it when underwater, and replenishing it when out of water. func (p *Player) tickAirSupply(w *world.World) { if !p.canBreathe(w) { diff --git a/server/session/session.go b/server/session/session.go index f91dbb13f..44b7fed6d 100644 --- a/server/session/session.go +++ b/server/session/session.go @@ -81,6 +81,8 @@ type Session struct { joinMessage, quitMessage string + viewLayer *world.ViewLayer + closeBackground chan struct{} } @@ -164,6 +166,7 @@ func New(conn Conn, maxChunkRadius int, log Logger, joinMessage, quitMessage str heldSlot: atomic.NewUint32(0), joinMessage: joinMessage, quitMessage: quitMessage, + viewLayer: world.NewViewLayer(), openedWindow: *atomic.NewValue(inventory.New(1, nil)), } @@ -232,6 +235,7 @@ func (s *Session) Close() error { // close closes the session, which in turn closes the controllable and the connection that the session // manages. func (s *Session) close() { + _ = s.viewLayer.Close() _ = s.c.Close() // Move UI inventory items to the main inventory. diff --git a/server/session/view_layer.go b/server/session/view_layer.go new file mode 100644 index 000000000..edd9971da --- /dev/null +++ b/server/session/view_layer.go @@ -0,0 +1,11 @@ +package session + +import "github.com/df-mc/dragonfly/server/world" + +type LayerViewer interface { + ViewLayer() *world.ViewLayer +} + +func (s *Session) ViewLayer() *world.ViewLayer { + return s.viewLayer +} diff --git a/server/session/world.go b/server/session/world.go index 4624fb85d..0c7a2824f 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -908,9 +908,19 @@ func (s *Session) ViewEntityAction(e world.Entity, a world.EntityAction) { // ViewEntityState ... func (s *Session) ViewEntityState(e world.Entity) { + metadata := s.parseEntityMetadata(e) + if v, ok := e.(LayerViewer); ok { + if nt := s.viewLayer.NameTag(v); len(nt) > 0 { + metadata[protocol.EntityDataKeyName] = nt + } + invisFlag := metadata.Flag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagInvisible) + if invis := s.viewLayer.Invisible(v); !invisFlag && invis { + metadata.SetFlag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagInvisible) + } + } s.writePacket(&packet.SetActorData{ EntityRuntimeID: s.entityRuntimeID(e), - EntityMetadata: s.parseEntityMetadata(e), + EntityMetadata: metadata, }) } diff --git a/server/world/view_layer.go b/server/world/view_layer.go new file mode 100644 index 000000000..b67a28628 --- /dev/null +++ b/server/world/view_layer.go @@ -0,0 +1,92 @@ +package world + +import ( + "sync" +) + +type LayerViewer interface { + ViewLayer() *ViewLayer +} + +type Layer struct { + nameTag string + invisible bool +} + +// ViewLayer is a view layer that can be used to add a layer to the view of a player. +type ViewLayer struct { + viewerMu sync.RWMutex + viewers map[LayerViewer]Layer +} + +// NewViewLayer returns a new ViewLayer. +func NewViewLayer() *ViewLayer { + return &ViewLayer{ + viewers: map[LayerViewer]Layer{}, + } +} + +// Viewers returns all viewers of the view layer. +func (v *ViewLayer) Viewers() []LayerViewer { + v.viewerMu.Lock() + defer v.viewerMu.Unlock() + viewers := make([]LayerViewer, 0, len(v.viewers)) + for viewer := range v.viewers { + viewers = append(viewers, viewer) + } + return viewers +} + +// ViewNameTag adds a name tag to a viewer. +func (v *ViewLayer) ViewNameTag(viewer LayerViewer, nameTag string) { + v.viewerMu.Lock() + defer v.viewerMu.Unlock() + + l := v.viewers[viewer] + l.nameTag = nameTag + v.viewers[viewer] = l +} + +// NameTag returns the name tag of a viewer. +func (v *ViewLayer) NameTag(viewer LayerViewer) string { + v.viewerMu.Lock() + defer v.viewerMu.Unlock() + return v.viewers[viewer].nameTag +} + +// ViewVisible makes a viewer be visible. +func (v *ViewLayer) ViewVisible(viewer LayerViewer) { + v.viewerMu.Lock() + defer v.viewerMu.Unlock() + + l := v.viewers[viewer] + l.invisible = false + v.viewers[viewer] = l +} + +// ViewInvisible makes a viewer be invisible. +func (v *ViewLayer) ViewInvisible(viewer LayerViewer) { + v.viewerMu.Lock() + defer v.viewerMu.Unlock() + + l := v.viewers[viewer] + l.invisible = true + v.viewers[viewer] = l +} + +// Invisible returns the invisibility of a viewer. +func (v *ViewLayer) Invisible(viewer LayerViewer) bool { + v.viewerMu.Lock() + defer v.viewerMu.Unlock() + return v.viewers[viewer].invisible +} + +// Close closes the view layer. +func (v *ViewLayer) Close() error { + v.viewerMu.Lock() + defer v.viewerMu.Unlock() + for viewer := range v.viewers { + delete(v.viewers, viewer) + } + return nil +} From fab010443fd97573432dcd4d63656f8818db4a3b Mon Sep 17 00:00:00 2001 From: Flonja <20887403+Flonja@users.noreply.github.com> Date: Tue, 9 Apr 2024 19:37:44 +0200 Subject: [PATCH 2/2] Touch up the documentation and revised (in)visibility --- server/session/world.go | 9 ++++++--- server/world/view_layer.go | 32 ++++++++++++-------------------- server/world/visibility_level.go | 28 ++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 23 deletions(-) create mode 100644 server/world/visibility_level.go diff --git a/server/session/world.go b/server/session/world.go index 0c7a2824f..e7d2ffe84 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -913,9 +913,12 @@ func (s *Session) ViewEntityState(e world.Entity) { if nt := s.viewLayer.NameTag(v); len(nt) > 0 { metadata[protocol.EntityDataKeyName] = nt } - invisFlag := metadata.Flag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagInvisible) - if invis := s.viewLayer.Invisible(v); !invisFlag && invis { - metadata.SetFlag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagInvisible) + if visibility := s.viewLayer.Visibility(v); visibility.EnforceVisibility() { + invisibleFlag := metadata.Flag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagInvisible) + if (visibility == world.EnforceVisible() && invisibleFlag) || + visibility == world.EnforceInvisible() && !invisibleFlag { + metadata.SetFlag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagInvisible) + } } } s.writePacket(&packet.SetActorData{ diff --git a/server/world/view_layer.go b/server/world/view_layer.go index b67a28628..1d39c994e 100644 --- a/server/world/view_layer.go +++ b/server/world/view_layer.go @@ -9,8 +9,8 @@ type LayerViewer interface { } type Layer struct { - nameTag string - invisible bool + nameTag string + visibility VisibilityLevel } // ViewLayer is a view layer that can be used to add a layer to the view of a player. @@ -37,7 +37,8 @@ func (v *ViewLayer) Viewers() []LayerViewer { return viewers } -// ViewNameTag adds a name tag to a viewer. +// ViewNameTag overwrites the public name tag of the viewer and allows this ViewLayer to view a different name tag. +// Leaving the name tag empty reverts this behaviour. func (v *ViewLayer) ViewNameTag(viewer LayerViewer, nameTag string) { v.viewerMu.Lock() defer v.viewerMu.Unlock() @@ -47,38 +48,29 @@ func (v *ViewLayer) ViewNameTag(viewer LayerViewer, nameTag string) { v.viewers[viewer] = l } -// NameTag returns the name tag of a viewer. +// NameTag returns the overwritten name tag of the viewer. func (v *ViewLayer) NameTag(viewer LayerViewer) string { v.viewerMu.Lock() defer v.viewerMu.Unlock() return v.viewers[viewer].nameTag } -// ViewVisible makes a viewer be visible. -func (v *ViewLayer) ViewVisible(viewer LayerViewer) { +// ViewVisibility overwrites the public visibility of the viewer and allows this ViewLayer to view +// this viewer as (in)visible depending on the VisibilityLevel. +func (v *ViewLayer) ViewVisibility(viewer LayerViewer, level VisibilityLevel) { v.viewerMu.Lock() defer v.viewerMu.Unlock() l := v.viewers[viewer] - l.invisible = false + l.visibility = level v.viewers[viewer] = l } -// ViewInvisible makes a viewer be invisible. -func (v *ViewLayer) ViewInvisible(viewer LayerViewer) { +// Visibility returns the visibility of the viewer. +func (v *ViewLayer) Visibility(viewer LayerViewer) VisibilityLevel { v.viewerMu.Lock() defer v.viewerMu.Unlock() - - l := v.viewers[viewer] - l.invisible = true - v.viewers[viewer] = l -} - -// Invisible returns the invisibility of a viewer. -func (v *ViewLayer) Invisible(viewer LayerViewer) bool { - v.viewerMu.Lock() - defer v.viewerMu.Unlock() - return v.viewers[viewer].invisible + return v.viewers[viewer].visibility } // Close closes the view layer. diff --git a/server/world/visibility_level.go b/server/world/visibility_level.go new file mode 100644 index 000000000..f951c9180 --- /dev/null +++ b/server/world/visibility_level.go @@ -0,0 +1,28 @@ +package world + +type VisibilityLevel struct { + visibility +} + +// PublicVisibility is the default visibility level where the entity is (in)visible +// depending on how it already is publicly viewed. +func PublicVisibility() VisibilityLevel { + return VisibilityLevel{0} +} + +// EnforceInvisible is the visibility level where the entity is always invisible to the viewer. +func EnforceInvisible() VisibilityLevel { + return VisibilityLevel{1} +} + +// EnforceVisible is the visibility level where the entity is always visible to the viewer. +func EnforceVisible() VisibilityLevel { + return VisibilityLevel{2} +} + +type visibility uint8 + +// EnforceVisibility returns whether metadata should be changed. +func (v visibility) EnforceVisibility() bool { + return v > 0 +}