Skip to content

Commit

Permalink
Write Audience Concept page
Browse files Browse the repository at this point in the history
  • Loading branch information
gabber235 committed Dec 18, 2024
1 parent 3db0ae4 commit 022ad5c
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 4 deletions.
Binary file not shown.
63 changes: 63 additions & 0 deletions documentation/docs/docs/04-concepts/04-Audience/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
difficulty: Easy
---

import RiveBoard from "@site/src/components/RiveBoard";

# Audience Entries

Audience entries are a specific type of [Manifest Entry](./Manifest) that is used to conditionally display content to a group of players.
Players can be in the audience of an entry.
In other words, the content of an audience entry will be displayed to the players in its audience.

There are two types of Audience entries:

### Audience Display Entry

The Audience Display is meant to display content to a group of players.
It can be things like: applying a potion effect, or showing a boss bar.

### Audience Filter Entry

Audience Filter entries have a list of children that can include both Audience entries and Audience Filter entries.
They are used as a way to filter the audience of an Audience Display Entry.

Audience Filters can both:

#### Filter Players
Filter the audience of itself and its children. Like the `CriteriaAudience`, where players are allowed to be in the audience if they meet the criteria.

#### Display Content
Display content just like a normal Audience Display Entry, but use the children for the content on what to display. Like the `Sidebar` does with `Lines` entries.


# How does filtering work?

When a player joins the server, they are added to all of the root audience entries.
A root audience entry is an entry that has no parents.

## Root Entries

<RiveBoard stateMachines="State Machine 1" artboard="Roots" aspectRatio={500/300} src={require('./assets/audience_entries.riv').default} />


As seen above, any entry that has no parents is a root entry.
The filters can still allow or deny players from being in their own audience.
However, if nothing is linked, they don't influence the audience of other entries.

## Chaining

We can chain filter together to create a more complex filter.
Now the player is in the `Display` only if both conditions are met.

<RiveBoard stateMachines="State Machine 1" artboard="Chained" aspectRatio={500/350} src={require('./assets/audience_entries.riv').default} />

Try turning on and off some conditions and see what happens.

## Multiple Parents

When an entry has multiple parents, the player is in the audience of the entry if any of the parents allow the player to be in the audience.

<RiveBoard stateMachines="State Machine 1" artboard="Or" aspectRatio={500/310} src={require('./assets/audience_entries.riv').default} />

Try turning on and off some conditions and see what happens.
6 changes: 3 additions & 3 deletions documentation/plugins/code-snippets/snippets.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
},
"interaction_bound": {
"file": "src/main/kotlin/com/typewritermc/example/entries/interaction/ExampleInteractionBound.kt",
"content": "@Entry(\"example_bound\", \"An example interaction bound\", Colors.MEDIUM_PURPLE, \"mdi:square-rounded\")\nclass ExampleBoundEntry(\n override val id: String = \"\",\n override val name: String = \"\",\n override val criteria: List<Criteria> = emptyList(),\n override val modifiers: List<Modifier> = emptyList(),\n override val triggers: List<Ref<TriggerableEntry>> = emptyList(),\n) : InteractionBoundEntry {\n override fun build(player: Player): InteractionBound =\n ExampleBound(player, priority)\n}\n\nclass ExampleBound(\n private val player: Player,\n override val priority: Int\n) : ListenerInteractionBound {\n\n override fun initialize() {\n super.initialize()\n // Setup initial state\n }\n\n @EventHandler(priority = EventPriority.HIGHEST)\n private fun onPlayerAction(event: SomeCancellablePlayerEvent) {\n if (event.player.uniqueId != player.uniqueId) return\n\n if (boundConditionBroken()) {\n // For PlayerEvents, we have a handy method to handle the breaking\n handleEvent(event)\n\n // A manual version of the above\n when (event.player.boundState) {\n InteractionBoundState.BLOCKING -> event.cancelled = true\n InteractionBoundState.INTERRUPTING -> InteractionEndTrigger.triggerFor(event.player, context())\n InteractionBoundState.IGNORING -> {}\n }\n }\n }\n\n private fun boundConditionBroken(): Boolean {\n // Check if the bound condition is broken\n return false\n }\n\n override fun teardown() {\n // Cleanup any state\n super.teardown()\n }\n}"
"content": "@Entry(\"example_bound\", \"An example interaction bound\", Colors.MEDIUM_PURPLE, \"mdi:square-rounded\")\nclass ExampleBoundEntry(\n override val id: String = \"\",\n override val name: String = \"\",\n override val criteria: List<Criteria> = emptyList(),\n override val modifiers: List<Modifier> = emptyList(),\n override val triggers: List<Ref<TriggerableEntry>> = emptyList(),\n) : InteractionBoundEntry {\n override fun build(player: Player): InteractionBound =\n ExampleBound(player, priority)\n}\n\nclass ExampleBound(\n private val player: Player,\n override val priority: Int\n) : ListenerInteractionBound {\n\n override fun initialize() {\n super.initialize()\n // Setup initial state\n }\n\n @EventHandler(priority = EventPriority.HIGHEST)\n private fun onPlayerAction(event: SomeCancellablePlayerEvent) {\n if (event.player.uniqueId != player.uniqueId) return\n\n if (boundConditionBroken()) {\n // For PlayerEvents, we have a handy method to handle the breaking\n handleEvent(event)\n\n // A manual version of the above\n when (event.player.boundState) {\n InteractionBoundState.BLOCKING -> event.isCancelled = true\n InteractionBoundState.INTERRUPTING -> InteractionEndTrigger.triggerFor(event.player, context())\n InteractionBoundState.IGNORING -> {}\n }\n }\n }\n\n private fun boundConditionBroken(): Boolean {\n // Check if the bound condition is broken\n return false\n }\n\n override fun teardown() {\n // Cleanup any state\n super.teardown()\n }\n}"
},
"audience_entry": {
"file": "src/main/kotlin/com/typewritermc/example/entries/manifest/ExampleAudienceEntry.kt",
Expand Down Expand Up @@ -129,11 +129,11 @@
},
"single_filter_basic": {
"file": "src/main/kotlin/com/typewritermc/example/entries/manifest/ExampleAudienceSingleFilter.kt",
"content": "@Entry(\"example_single_filter\", \"An example single filter entry\", Colors.MYRTLE_GREEN, \"material-symbols:filter-alt\")\nclass ExampleSingleFilterEntry(\n override val id: String = \"\",\n override val name: String = \"\",\n override val priorityOverride: Optional<Int> = Optional.empty(),\n) : AudienceFilterEntry, PriorityEntry {\n override val children: List<Ref<out AudienceEntry>> = emptyList()\n\n override fun display(): AudienceFilter = ExampleSingleFilter(ref()) { player ->\n PlayerExampleDisplay(player, ExampleSingleFilter::class, ref())\n }\n}\n\nprivate class ExampleSingleFilter(\n ref: Ref<ExampleSingleFilterEntry>,\n createDisplay: (Player) -> PlayerExampleDisplay,\n) : SingleFilter<ExampleSingleFilterEntry, PlayerExampleDisplay>(ref, createDisplay) {\n // highlight-start\n // This must be a references to a shared map.\n // It CANNOT cache the map itself.\n override val displays: MutableMap<UUID, PlayerExampleDisplay>\n get() = map\n // highlight-end\n\n // highlight-start\n companion object {\n // This map is shared between all instances of the filter.\n private val map = ConcurrentHashMap<UUID, PlayerExampleDisplay>()\n }\n // highlight-end\n}\n\nprivate class PlayerExampleDisplay(\n player: Player,\n displayKClass: KClass<out SingleFilter<ExampleSingleFilterEntry, *>>,\n current: Ref<ExampleSingleFilterEntry>\n) : PlayerSingleDisplay<ExampleSingleFilterEntry>(player, displayKClass, current) {\n override fun setup() {\n super.setup()\n player.sendMessage(\"Display activated!\")\n }\n\n override fun tearDown() {\n super.tearDown()\n player.sendMessage(\"Display deactivated!\")\n }\n}"
"content": "@Entry(\"example_single_filter\", \"An example single filter entry\", Colors.MYRTLE_GREEN, \"material-symbols:filter-alt\")\nclass ExampleSingleFilterEntry(\n override val id: String = \"\",\n override val name: String = \"\",\n override val priorityOverride: Optional<Int> = Optional.empty(),\n) : AudienceFilterEntry, PriorityEntry {\n override val children: List<Ref<out AudienceEntry>> get() = emptyList()\n\n override fun display(): AudienceFilter = ExampleSingleFilter(ref()) { player ->\n PlayerExampleDisplay(player, ExampleSingleFilter::class, ref())\n }\n}\n\nprivate class ExampleSingleFilter(\n ref: Ref<ExampleSingleFilterEntry>,\n createDisplay: (Player) -> PlayerExampleDisplay,\n) : SingleFilter<ExampleSingleFilterEntry, PlayerExampleDisplay>(ref, createDisplay) {\n // highlight-start\n // This must be a references to a shared map.\n // It CANNOT cache the map itself.\n override val displays: MutableMap<UUID, PlayerExampleDisplay>\n get() = map\n // highlight-end\n\n // highlight-start\n companion object {\n // This map is shared between all instances of the filter.\n private val map = ConcurrentHashMap<UUID, PlayerExampleDisplay>()\n }\n // highlight-end\n}\n\nprivate class PlayerExampleDisplay(\n player: Player,\n displayKClass: KClass<out SingleFilter<ExampleSingleFilterEntry, *>>,\n current: Ref<ExampleSingleFilterEntry>\n) : PlayerSingleDisplay<ExampleSingleFilterEntry>(player, displayKClass, current) {\n override fun setup() {\n super.setup()\n player.sendMessage(\"Display activated!\")\n }\n\n override fun tearDown() {\n super.tearDown()\n player.sendMessage(\"Display deactivated!\")\n }\n}"
},
"single_filter_lifecycle": {
"file": "src/main/kotlin/com/typewritermc/example/entries/manifest/ExampleAudienceSingleFilter.kt",
"content": "private class ComplexPlayerDisplay(\n player: Player,\n displayKClass: KClass<out SingleFilter<ExampleSingleFilterEntry, *>>,\n current: Ref<ExampleSingleFilterEntry>\n) : PlayerSingleDisplay<ExampleSingleFilterEntry>(player, displayKClass, current) {\n override fun initialize() {\n super.initialize()\n // Called once when the display is first created\n player.sendMessage(\"Display initialized!\")\n }\n\n override fun setup() {\n super.setup()\n // Called every time this display becomes active\n player.sendMessage(\"Display setup!\")\n }\n\n override fun tick() {\n // Called every tick while the display is active\n // Update display content here\n }\n\n override fun tearDown() {\n super.tearDown()\n // Called when this display becomes inactive\n player.sendMessage(\"Display torn down!\")\n }\n\n override fun dispose() {\n super.dispose()\n // Called when the display is being completely removed\n player.sendMessage(\"Display disposed!\")\n }\n}"
"content": "private class ComplexPlayerDisplay(\n player: Player,\n displayKClass: KClass<out SingleFilter<ExampleSingleFilterEntry, *>>,\n current: Ref<ExampleSingleFilterEntry>\n) : PlayerSingleDisplay<ExampleSingleFilterEntry>(player, displayKClass, current) {\n override fun initialize() {\n super.initialize()\n // Called once when the display is first created\n player.sendMessage(\"Display initialized!\")\n }\n\n override fun setup() {\n super.setup()\n // Called every time this display becomes active\n player.sendMessage(\"Display setup!\")\n }\n\n override fun tick() {\n // Called every tick while the display is active\n // Update display content here\n }\n\n override fun tearDown() {\n super.tearDown()\n // Called when this display becomes inactive\n player.sendMessage(\"Display torn down!\")\n }\n\n override fun dispose() {\n super.dispose()\n // Called when the display is being completely removed\n player.sendMessage(\"Display disposed!\")\n }\n}\n"
},
"artifact_entry": {
"file": "src/main/kotlin/com/typewritermc/example/entries/static/ExampleArtifactEntry.kt",
Expand Down
5 changes: 4 additions & 1 deletion documentation/src/components/RiveBoard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ interface RiveBoardProps {
src: any;
artboard?: string;
stateMachines?: string | string[];
aspectRatio?: number;
}

export default function RiveBoard(props: RiveBoardProps) {
Expand All @@ -16,12 +17,14 @@ export default function RiveBoard(props: RiveBoardProps) {
stateMachines: props.stateMachines,
autoplay: true,
});
const aspectRatio = props.aspectRatio || 1;
const usedWidth = width * 0.8;
const usedHeight = usedWidth / aspectRatio;
return (
<div ref={ref} style={{ width: "100%", height: "100%", display: "flex", justifyContent: "center" }} >
<div style={{
width: usedWidth,
height: usedWidth,
height: usedHeight,
}}>
<RiveComponent />
</div >
Expand Down

0 comments on commit 022ad5c

Please sign in to comment.