Elements
Values are set by us; we decide what to display and when. Often it's easier to instead describe how something is calculated and let the visual keep it up to date, which is what elements are for.
A PlayerSpecificElement generates what a given player is displayed:
// An element that returns the player's display name
val element = PlayerSpecificElement<Component> { player -> player.displayName }Displaying Elements
Every visual has a Dynamic variant which generates its values from elements. These take the server that they belong to, since that's what the elements are generated against:
val bossbar = DynamicVirtualBossbar(server)
bossbar.setTitle { player -> player.displayName }
bossbar.setProgress(UniversalElement { server -> (server.tickCount % 100) / 100.0F })Each tick, every element is generated and displayed. Since only values that change are sent, an element that generates the same thing twice doesn't send anything.
The type of element decides what it generates; a UniversalElement generates the base value, and any other element generates per-player overrides. Every value has a slot for both, so we can generate a shared value for most players while generating something different for specific players:
sidebar.setRows(UniversalElement { server -> sharedRows(server) })
sidebar.setRows { player -> rowsFor(player) }Any value that doesn't have an element can still be set directly.
NOTE
Overrides that were generated by an element are removed when a player stops observing the visual, since they'd be generated again anyway. Overrides that you set yourself are always kept.
Types Of Element
There are other type-specific elements: LevelSpecificElement, TeamSpecificElement, and UniversalElement, these all inherit from PlayerSpecificElement:
LevelSpecificElement: this is an element that displays based on the world. This may be useful if you want to display information about the current world a player is in, this can then be cached on a level basis.TeamSpecificElement: this is an element that displays based on a player team. Similarly to the level element, this may be useful if you want to display information about a player's team, which can then be cached on a team basis.UniversalElement: this is an element that displays the same universally. This may be useful for elements that will always be displayed the same to all players.
UniversalElements can also be constant, if there is no need to constantly update its content:
val element: UniversalElement<Component> = UniversalElement.constant(Component.literal("This is constant!"))There's also AnimatedElement, which cycles through elements over a given duration:
val builder = AnimatedElement.Builder<Component>()
builder.add(Component.literal("Tick..."), 1.Seconds)
builder.add(Component.literal("Tock..."), 1.Seconds)
bossbar.setTitle(builder.build())Caching And Merging
An element is generated for every player being displayed the visual every tick, so if your generator is expensive, it may be worth considering caching your element:
val element = PlayerSpecificElement<Component> { player ->
/* Expensive function */
Component.literal("Foo Bar")
}.cached()A cached element is only generated once per player per tick, and a cached UniversalElement only once per tick.
Elements can also be merged:
val teamElement: PlayerSpecificElement<PlayerTeam> = // ...
val messageElement: PlayerSpecificElement<String> = // ...
val componentElement: PlayerSpecificElement<Component> = teamElement.merge(messageElement) { team, message ->
team.formattedDisplayName.append(" ").append(message)
}Tickables
Elements should only read state, they shouldn't modify it. If we've got state that changes over time, and multiple elements which display it, we can implement a TickableElement and register it with the visual:
val timer = TimerElement(10.Minutes)
val bossbar = DynamicVirtualBossbar(server)
bossbar.addTickable(timer)
bossbar.setProgress(timer.progress())
bossbar.setTitle(timer.remaining { duration -> Component.literal(duration.formatHHMMSS()) })Tickables are ticked before any elements are generated, so our elements always read state that's up to date. Registering the same tickable more than once won't tick it more than once, and we can stop ticking it with removeTickable.
TimerElement is the built-in example of this, see the Bossbars Section for more information on countdowns.
