-
-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Write & Update developer documentation
- Loading branch information
Showing
18 changed files
with
684 additions
and
6 deletions.
There are no files selected for viewing
32 changes: 32 additions & 0 deletions
32
documentation/docs/develop/02-extensions/04-entries/manifest/audience_filter.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import CodeSnippet from "@site/src/components/CodeSnippet"; | ||
|
||
# AudienceFilter | ||
|
||
The `AudienceFilterEntry` is a specialized version of `AudienceEntry` designed to filter audience members based on specific criteria. | ||
It accepts a list of children that can include both `AudienceEntry` and `AudienceFilterEntry` components. | ||
|
||
## Basic Usage | ||
|
||
### Static Filtering | ||
Here's an example of a filter that only allows players whose names start with 'g': | ||
|
||
<CodeSnippet tag="audience_filter" json={require("../../../snippets.json")} /> | ||
|
||
This filter is static because player names cannot be changed while on the server. | ||
Players are either filtered at the start or not at all. | ||
|
||
### Dynamic Filtering | ||
For more dynamic filtering, you can filter based on changing player attributes, such as health: | ||
|
||
<CodeSnippet tag="audience_filter_dynamic" json={require("../../../snippets.json")} /> | ||
|
||
This example filters players based on their health value, which is provided as a parameter from the entry. | ||
Like `AudienceDisplay`, we can use both Bukkit events and `TickableDisplay` to update which players pass through the filter. | ||
|
||
### Invertible Filters | ||
Sometimes you may want to invert a filter's behavior. | ||
This is particularly useful when the opposite condition would be complex to define explicitly, such as filtering all players that are not in a specific world: | ||
|
||
<CodeSnippet tag="audience_filter_invertable" json={require("../../../snippets.json")} /> | ||
|
||
Simply implementing the `Invertible` interface enables this functionality in Typewriter. |
73 changes: 73 additions & 0 deletions
73
...ation/docs/develop/02-extensions/04-entries/manifest/audience_single_filter.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import CodeSnippet from "@site/src/components/CodeSnippet"; | ||
|
||
# SingleFilter | ||
|
||
The `SingleFilter` is a specialized variant of `AudienceFilter` that ensures only one filter instance can be active for a player at any given time. This is particularly useful for managing exclusive displays like tab list headers/footers or scoreboard sidebars. | ||
|
||
## Basic Usage | ||
|
||
Here's a basic example of implementing a single filter: | ||
|
||
<CodeSnippet tag="single_filter_basic" json={require("../../../snippets.json")} /> | ||
|
||
The `SingleFilter` consists of three main components: | ||
1. An entry class that defines the configuration and creates the filter | ||
2. A filter class that manages the shared state between instances | ||
3. A display class that handles the player-specific display logic | ||
|
||
## Display Lifecycle | ||
|
||
The display lifecycle is a crucial concept in SingleFilter as it manages how displays are created, updated, and removed. Let's look at a practical example: | ||
|
||
<CodeSnippet tag="single_filter_lifecycle" json={require("../../../snippets.json")} /> | ||
|
||
Let's walk through what happens in different scenarios: | ||
|
||
### Scenario 1: Player Joins Server | ||
``` | ||
1. Player joins | ||
2. initialize() called once - Creates the display instance | ||
3. setup() called - Activates the display with initial entry | ||
4. tick() starts running periodically | ||
``` | ||
|
||
### Scenario 2: Higher Priority Entry Becomes Active | ||
``` | ||
1. New entry with higher priority includes player in its audience | ||
2. tearDown() called on current display | ||
3. setup() called with new entry | ||
4. Same display instance continues, but with new entry reference | ||
5. tick() continues running with new entry data | ||
``` | ||
|
||
### Scenario 3: Player Leaves Entry's Audience | ||
``` | ||
1. Player is filtered out of current entry's audience | ||
2. System looks for next highest priority entry where player is in audience | ||
3. If found: | ||
- tearDown() called with old entry | ||
- setup() called with new entry | ||
- Display continues with new entry | ||
4. If not found: | ||
- tearDown() called | ||
- dispose() called | ||
- Display is completely removed | ||
``` | ||
|
||
The display lifecycle ensures smooth transitions between different entries while maintaining the single display state. This is particularly important for exclusive displays where you want to avoid flickering or interruptions when switching between different configurations. | ||
|
||
### Priority Behavior | ||
|
||
The priority of entries determines which display becomes active when multiple entries include a player in their audience. For example: | ||
|
||
```kotlin | ||
Entry A (priority: 50) - Basic display | ||
Entry B (priority: 100) - Special display | ||
|
||
Scenario: | ||
1. Player joins -> Entry B becomes active (higher priority) | ||
2. Player leaves Entry B's audience -> Entry A becomes active (next highest) | ||
3. Player returns to Entry B's audience -> Switches back to Entry B | ||
``` | ||
|
||
This priority system ensures that more important displays can temporarily override less important ones, while maintaining a fallback when a player is no longer in the audience of higher priority entries. |
47 changes: 47 additions & 0 deletions
47
documentation/docs/develop/02-extensions/05-interactions/bound.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import CodeSnippet from "@site/src/components/CodeSnippet"; | ||
|
||
# Interaction Bound | ||
|
||
The InteractionBounds allows you to constrain the lifetime of an interactions to specific conditions, such as player location or actions. | ||
For example, a player staying within range of an NPC during a conversation or while they are sitting on a bench. | ||
|
||
## States and Behavior | ||
|
||
InteractionBounds operate in three states: | ||
|
||
1. **INTERRUPTING**: When the bound condition is broken (e.g., player moves too far), the interaction ends | ||
2. **BLOCKING**: Prevents the bound from being broken (e.g., cancels movement beyond the allowed range) | ||
3. **IGNORING**: Bound conditions are not enforced (e.g., player can move freely) | ||
|
||
:::warning | ||
The bound itself doesn't determine the state - it only responds to the current state provided by the system. | ||
::: | ||
|
||
## Implementation | ||
|
||
Here's how to create a interaction bound: | ||
|
||
<CodeSnippet tag="interaction_bound" json={require("../../snippets.json")} /> | ||
|
||
## Implementation Tips | ||
|
||
1. Always use `handleEvent()` for consistent state handling | ||
2. Check player UUID to ensure you're handling the right player | ||
3. Clean up resources in `teardown()` | ||
4. Consider performance for frequently triggered events | ||
|
||
## Example Flow | ||
|
||
Here's how a typical radius bound might work: | ||
|
||
``` | ||
1. Player starts interaction | ||
2. Bound initializes with start location | ||
3. Player moves: | ||
- Within range: No action | ||
- Beyond range: | ||
- If BLOCKING: Cancel movement | ||
- If INTERRUPTING: End interaction | ||
- If IGNORING: Allow movement | ||
4. Interaction ends, bound tears down | ||
``` |
88 changes: 88 additions & 0 deletions
88
documentation/docs/develop/02-extensions/05-interactions/index.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import CodeSnippet from "@site/src/components/CodeSnippet"; | ||
|
||
# Interaction System | ||
|
||
The Interaction system provides a framework for managing stateful player interactions that need to run over time. | ||
Examples include cinematics, conversations, or custom game modes. | ||
The system consists of two main components: Trigger Handlers, and Interactions. | ||
|
||
:::info | ||
At any given moment, a player can only be in one interaction at a time. | ||
Regardless of Interaction type. If you need multiple interactions, you can use the [AudienceEntry](entries/manifest/audience) to manage them. | ||
::: | ||
|
||
## Core Concepts | ||
|
||
An Interaction follows a specific lifecycle: | ||
1. An Entry raises an event with a start trigger | ||
2. A TriggerHandler processes this event and creates the Interaction | ||
3. The Interaction is initialized and begins ticking | ||
4. When the Interaction should end, it raises a stop trigger | ||
5. The TriggerHandler processes the stop trigger and ends the Interaction | ||
|
||
## Basic Implementation | ||
|
||
Here's a basic example of implementing an interaction: | ||
|
||
<CodeSnippet tag="basic_interaction" json={require("../../snippets.json")} /> | ||
|
||
The Interaction class handles the actual state and behavior. Key points: | ||
- `initialize()` sets up the initial state | ||
- `tick()` updates the state over time | ||
- `teardown()` cleans up when the interaction ends | ||
- Priority comes from the entry that started the interaction | ||
|
||
## Trigger System | ||
|
||
The trigger system manages starting and stopping interactions. | ||
|
||
Interaction should be started by raising its specific start trigger. | ||
And they can be gracefully stopped by raising its specific stop trigger. | ||
|
||
<CodeSnippet tag="interaction_triggers" json={require("../../snippets.json")} /> | ||
|
||
### Trigger Handlers | ||
|
||
The handler is responsible for: | ||
1. Creating new interactions from start triggers | ||
2. Ending interactions when stop triggers are received | ||
|
||
Though it could also change things on the current interaction. | ||
For example, it could swap out the dialogue. | ||
|
||
<CodeSnippet tag="interaction_trigger_handler" json={require("../../snippets.json")} /> | ||
|
||
## Activating the interaction | ||
|
||
Something needs to start the interaction. | ||
This could be any entry. | ||
For this example, we have an action entry which when triggered starts the interaction. | ||
|
||
<CodeSnippet tag="interaction_entry" json={require("../../snippets.json")} /> | ||
|
||
## Flow Example | ||
|
||
Here's how everything works together: | ||
|
||
``` | ||
1. Player triggers ExampleInteractionActionEntry | ||
2. Entry creates ExampleStartTrigger | ||
3. ExampleTriggerHandler receives the trigger | ||
4. Handler creates ExampleInteraction | ||
5. Interaction get initialized (automatically) | ||
6. Interaction runs until end condition met | ||
7. Interaction raises ExampleStopTrigger | ||
8. Handler processes stop trigger | ||
9. Interaction teardown is called (automatically) | ||
``` | ||
|
||
## Priority System | ||
|
||
Some interactions are more important than others. | ||
Since only 1 interaction can be active at a time, the priority system ensures that the most important interactions will be active. | ||
To make sure that some random idle dialogue doesn't interrupt a story critical cinematic. | ||
|
||
An interaction can only be started if the priority of the running interaction is lower or equal to the new interaction. | ||
|
||
For example, if the player is in a cinematic with priority 1, and some idle dialogue with priority 0 is triggered, the idle dialogue will not interrupt the cinematic. | ||
|
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Oops, something went wrong.