package world import ( "maps" "slices" "sync" ) // layer stores the appearance overrides that a ViewLayer applies to an entity. type layer struct { nameTag *string scoreTag *string visibility VisibilityLevel } // ViewLayerUpdater handles immediate updates after a ViewLayer changes how an entity is viewed. type ViewLayerUpdater interface { // ViewLayerEntityChanged handles an entity whose view-layer overrides changed. ViewLayerEntityChanged(entity Entity) } type viewLayerViewer interface { ViewLayer() *ViewLayer } // ViewLayer holds overrides for how entities are viewed by a single viewer. It allows entities to be // viewed differently by different players, such as with a different name tag or visibility state. type ViewLayer struct { mu sync.RWMutex entities map[*EntityHandle]layer updater ViewLayerUpdater } // NewViewLayer returns a new ViewLayer. func NewViewLayer(updater ViewLayerUpdater) *ViewLayer { return &ViewLayer{ entities: map[*EntityHandle]layer{}, updater: updater, } } // Entities returns the handles of all entities with overrides in the view layer. func (v *ViewLayer) Entities() []*EntityHandle { v.mu.RLock() defer v.mu.RUnlock() return slices.Collect(maps.Keys(v.entities)) } // ViewNameTag overwrites the public name tag of the entity and allows this ViewLayer to view a different name tag. // Passing an empty name tag removes the name tag for this ViewLayer. func (v *ViewLayer) ViewNameTag(entity Entity, nameTag string) { v.update(entity, func(l *layer) { l.nameTag = &nameTag }) } // ViewPublicNameTag removes the name tag override from the entity, causing the public name tag to be // viewed again. func (v *ViewLayer) ViewPublicNameTag(entity Entity) { v.update(entity, func(l *layer) { l.nameTag = nil }) } // NameTag returns the overwritten name tag of the entity and whether an override was set. func (v *ViewLayer) NameTag(entity Entity) (string, bool) { v.mu.RLock() defer v.mu.RUnlock() nameTag := v.entities[entity.H()].nameTag if nameTag == nil { return "", false } return *nameTag, true } // ViewScoreTag overwrites the public score tag of the entity and allows this ViewLayer to view a different score tag. // Passing an empty score tag removes the score tag for this ViewLayer. func (v *ViewLayer) ViewScoreTag(entity Entity, scoreTag string) { v.update(entity, func(l *layer) { l.scoreTag = &scoreTag }) } // ViewPublicScoreTag removes the score tag override from the entity, causing the public score tag to be // viewed again. func (v *ViewLayer) ViewPublicScoreTag(entity Entity) { v.update(entity, func(l *layer) { l.scoreTag = nil }) } // ScoreTag returns the overwritten score tag of the entity and whether an override was set. func (v *ViewLayer) ScoreTag(entity Entity) (string, bool) { v.mu.RLock() defer v.mu.RUnlock() scoreTag := v.entities[entity.H()].scoreTag if scoreTag == nil { return "", false } return *scoreTag, true } // ViewVisibility overwrites the public visibility of the entity and allows this ViewLayer to view // this entity as (in)visible depending on the VisibilityLevel. func (v *ViewLayer) ViewVisibility(entity Entity, level VisibilityLevel) { v.update(entity, func(l *layer) { l.visibility = level }) } // Visibility returns the visibility of the entity. func (v *ViewLayer) Visibility(entity Entity) VisibilityLevel { v.mu.RLock() defer v.mu.RUnlock() return v.entities[entity.H()].visibility } // Remove removes all overrides for the entity from the ViewLayer. func (v *ViewLayer) Remove(entity Entity) { if v.remove(entity) { v.refresh(entity) } } // remove removes all overrides for the entity from the ViewLayer without refreshing entity metadata. It returns // whether any overrides were removed. func (v *ViewLayer) remove(entity Entity) bool { handle := entity.H() v.mu.Lock() _, ok := v.entities[handle] delete(v.entities, handle) v.mu.Unlock() return ok } // update applies a mutation to the entity's layer, removes the entry if no overrides remain, and refreshes // the entity for the layer's viewer. func (v *ViewLayer) update(entity Entity, update func(*layer)) { handle := entity.H() v.mu.Lock() l := v.entities[handle] update(&l) if l.empty() { delete(v.entities, handle) } else { v.entities[handle] = l } v.mu.Unlock() v.refresh(entity) } // Close closes the view layer. func (v *ViewLayer) Close() error { v.mu.Lock() defer v.mu.Unlock() clear(v.entities) return nil } // empty checks if the layer does not override any public entity metadata. func (l layer) empty() bool { return l.nameTag == nil && l.scoreTag == nil && l.visibility == PublicVisibility() } func (v *ViewLayer) refresh(entity Entity) { if v.updater != nil { v.updater.ViewLayerEntityChanged(entity) } }