### Install PlaceholderAPI Extension Source: https://docs.typewritermc.com/0.6.1/docs/troubleshooting These commands demonstrate how to download and install a PlaceholderAPI extension. This is a crucial step for ensuring placeholders are parsed correctly within the Typewriter plugin. ```bash /papi ecloud download [extension_name] ``` ```bash /papi ecloud download player ``` -------------------------------- ### Example Adapter Class (ExampleAdapter.kt) Source: https://docs.typewritermc.com/0.4.2/develop/adapters/getting_started A basic Kotlin class demonstrating how to create an adapter for Typewriter. It uses the @Adapter annotation to register the adapter and extends TypewriteAdapter. The initialize() method is where adapter-specific setup logic should reside. ```kotlin import me.gabber235.typewriter.adapters.Adapter import me.gabber235.typewriter.adapters.TypewriteAdapter @Adapter("Example", "An example adapter for documentation purposes", "0.0.1") object ExampleAdapter : TypewriteAdapter() { override fun initialize() { // Any initializations needed to run the adapter. } } ``` -------------------------------- ### SingleFilter Display Lifecycle Example Source: https://docs.typewritermc.com/develop/extensions/entries/manifest/audience_single_filter This Kotlin code illustrates the detailed lifecycle methods of a player display within a SingleFilter. It shows when `initialize`, `setup`, `tick`, `tearDown`, and `dispose` are called, crucial for managing display states. ```kotlin private class ComplexPlayerDisplay( player: Player, displayKClass: KClass>, current: Ref ) : PlayerSingleDisplay(player, displayKClass, current) { override fun initialize() { super.initialize() // Called once when the display is first created player.sendMessage("Display initialized!") } override fun setup() { super.setup() // Called every time this display becomes active player.sendMessage("Display setup!") } override fun tick() { // Called every tick while the display is active // Update display content here } override fun tearDown() { super.tearDown() // Called when this display becomes inactive player.sendMessage("Display torn down!") } override fun dispose() { super.dispose() // Called when the display is being completely removed player.sendMessage("Display disposed!") } } ``` -------------------------------- ### Implement Example Cinematic Action Lifecycle Source: https://docs.typewritermc.com/beta/develop/extensions/entries/cinematic This Kotlin code shows a custom implementation of the `CinematicAction` interface for `ExampleCinematicAction`. It includes the required `setup`, `tick`, `teardown`, and `canFinish` methods to manage the cinematic's behavior during playback, including handling active segments and determining when the cinematic is complete. ```kotlin class ExampleCinematicAction( val player: Player, val entry: ExampleCinematicEntry, ) : CinematicAction { override suspend fun setup() { // Initialize variables, spawn entities, etc. } override suspend fun tick(frame: Int) { val segment = entry.segments activeSegmentAt frame // Can be null if no segment is active // The `frame` parameter is not necessarily next frame: `frame != old(frame)+1` // Execute tick logic for the segment } override suspend fun teardown() { // Remove entities, etc. } override fun canFinish(frame: Int): Boolean = entry.segments canFinishAt frame } ``` -------------------------------- ### Create Example Typewriter Adapter Class Source: https://docs.typewritermc.com/0.5.1/develop/adapters/getting_started Defines a basic Typewriter adapter class named 'ExampleAdapter' with an annotation specifying its name, description, and version. Includes placeholder methods for initialization and shutdown. ```kotlin import me.gabber235.typewriter.adapters.Adapter import me.gabber235.typewriter.adapters.TypewriterAdapter @Adapter("Example", "An example adapter for documentation purposes", "0.0.1") object ExampleAdapter : TypewriterAdapter() { override fun initialize() { // Do something when the adapter is initialized } override fun shutdown() { // Do something when the adapter is shutdown } } ``` -------------------------------- ### Troubleshoot 'plugin.yml not found' Error Source: https://docs.typewritermc.com/0.6.1/docs/troubleshooting This snippet shows an example of the 'plugin.yml not found' error encountered when installing a Typewriter extension. It indicates that the extension JAR file is not in the correct directory or lacks a valid plugin descriptor file. ```java [08:49:35 ERROR]: [DirectoryProviderSource] Error loading plugin: Directory 'plugins/BasicExtension.jar' failed to load! java.lang.RuntimeException: Directory 'plugins/BasicExtension.jar' failed to load! ... Caused by: java.lang.IllegalArgumentException: Directory 'plugins/BasicExtension.jar' does not contain a paper-plugin.yml or plugin.yml! Could not determine plugin type, cannot load a plugin from it! ``` -------------------------------- ### Kotlin SoundSourceEntry Example Source: https://docs.typewritermc.com/beta/develop/extensions/entries/static/sound_source This Kotlin code defines an ExampleSoundSourceEntry class that extends SoundSourceEntry and StaticEntry. It demonstrates how to set up a sound source at a specific entity in the world, which can be a moving target like an NPC. The getEmitter method is crucial for returning the SoundEmitter associated with the player's entity ID. ```kotlin @Entry("example_sound_source", "An example sound source entry.", Colors.BLUE, "ic:round-spatial-audio-off") class ExampleSoundSourceEntry( override val id: String = "", override val name: String = "", ) : SoundSourceEntry, StaticEntry { override fun getEmitter(player: Player): SoundEmitter { // Return the emitter that should be used for the sound. // An entity should be provided. return SoundEmitter(player.entityId) } } ``` -------------------------------- ### Define Example Segment Data Class Source: https://docs.typewritermc.com/0.4.2/develop/adapters/entries/cinematic A simple data class in Kotlin representing a segment within a cinematic, requiring start and end frame integers. ```kotlin data class ExampleSegment( override val startFrame: Int, override val endFrame: Int, ): Segment ``` -------------------------------- ### Gradle Build Script Setup (build.gradle.kts) Source: https://docs.typewritermc.com/0.4.2/develop/adapters/getting_started Configures a Gradle project for a Typewriter adapter. Includes necessary plugins, repositories, and dependencies like Paper API and Typewriter itself. Ensure to replace placeholder group and version information. ```kotlin plugins { kotlin("jvm") version "1.8.20" id("com.github.johnrengelman.shadow") version "8.1.1" } // Replace with your own information group = "me.yourusername" version = "0.0.1" repositories { maven("https://jitpack.io") mavenCentral() maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") maven("https://oss.sonatype.org/content/groups/public/") maven("https://libraries.minecraft.net/") maven("https://repo.papermc.io/repository/maven-public/") maven("https://repo.codemc.io/repository/maven-snapshots/") maven("https://repo.opencollab.dev/maven-snapshots/") // Add other necessary repositories } dependencies { compileOnly(kotlin("stdlib")) compileOnly("io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT") compileOnly("com.github.gabber235:typewriter:main-SNAPSHOT") // Latest release version // Already included in the Typewriter plugin but still needed for compilation compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0-RC") compileOnly("com.github.dyam0:LirandAPI:96cc59d4fb") compileOnly("net.kyori:adventure-api:4.13.1") compileOnly("net.kyori:adventure-text-minimessage:4.13.1") // Add other dependencies as needed } java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } ``` -------------------------------- ### Implement a Custom Cinematic Action Source: https://docs.typewritermc.com/0.7.0/develop/extensions/entries/cinematic Provides an example implementation of the `CinematicAction` interface, demonstrating the `setup`, `tick`, `teardown`, and `canFinish` methods for managing cinematic playback logic. ```kotlin import com.cybersoft.typewriter.api.action.CinematicAction import com.cybersoft.typewriter.api.player.Player import com.cybersoft.typewriter.api.entry.CinematicEntry // Assuming ExampleCinematicEntry and Segment related classes are defined class ExampleCinematicAction( val player: Player, val entry: ExampleCinematicEntry, ) : CinematicAction { override suspend fun setup() { // Initialize variables, spawn entities, etc. println("Cinematic setup") } override suspend fun tick(frame: Int) { // val segment = entry.segments.activeSegmentAt(frame) // The `frame` parameter is not necessarily next frame: `frame != old(frame)+1` // Execute tick logic for the segment println("Cinematic tick at frame: $frame") } override suspend fun teardown() { // Remove entities, etc. println("Cinematic teardown") } override fun canFinish(frame: Int): Boolean { // return entry.segments.canFinishAt(frame) return true // Placeholder } } ``` -------------------------------- ### Example SoundSourceEntry Kotlin Implementation Source: https://docs.typewritermc.com/0.4.2/develop/adapters/entries/static/sound_source Demonstrates how to implement the SoundSourceEntry class in Kotlin. It shows the basic structure for defining a sound source associated with an entity, which can be used with a Sound parameter in game entries. Dependencies include TypewriterMC annotations and the Adventure API for sound emission. ```kotlin @Entry("example_sound_source", "An example sound source entry.", Colors.GREEN, Icons.VOLUME_HIGH) class ExampleSoundSourceEntry( override val id: String, override val name: String, ) : SoundSourceEntry { fun getEmitter(): net.kyori.adventure.sound.Sound.Emitter { // Return the emitter that should be used for the sound. // A bukkit entity can be used here. } } ``` -------------------------------- ### Define ExampleAssetEntry - Kotlin Source: https://docs.typewritermc.com/0.4.2/develop/adapters/entries/static/asset Demonstrates how to define a custom AssetEntry, 'ExampleAssetEntry', which extends the base AssetEntry. It requires a path and can be registered with an ID, description, color, and icon. ```kotlin @Entry("example_asset", "An example asset entry.", Colors.PINK, Icons.PERSON_WALKING) class ExampleAssetEntry( override val path: String = "", ): AssetEntry ``` -------------------------------- ### PlaceholderAPI Extension Management Commands Source: https://docs.typewritermc.com/0.7.0/docs/troubleshooting These commands are used to manage extensions for PlaceholderAPI, a plugin that provides placeholders for various server information. Use the 'ecloud download' command to install extensions and 'parse me' to test placeholder functionality. ```bash /papi ecloud download [extension_name] /papi ecloud download player /papi parse me /papi parse me %player_name% ``` -------------------------------- ### Error Message: 'plugin.yml not found' When Installing Adapter Source: https://docs.typewritermc.com/0.5.1/docs/troubleshooting This snippet details the error encountered when an adapter is not placed in the correct directory, leading to a failure to load due to a missing plugin.yml file. The solution involves ensuring correct adapter placement. ```log [08:49:35 ERROR]: [DirectoryProviderSource] Error loading plugin: Directory 'plugins/BasicAdapter.jar' failed to load! java.lang.RuntimeException: Directory 'plugins/BasicAdapter.jar' failed to load! ... Caused by: java.lang.IllegalArgumentException: Directory 'plugins/BasicAdapter.jar' does not contain a paper-plugin.yml or plugin.yml! Could not determine plugin type, cannot load a plugin from it! ``` -------------------------------- ### Configure Gradle Build Script for Typewriter Adapter Source: https://docs.typewritermc.com/0.5.1/develop/adapters/getting_started Sets up a Gradle project for a Typewriter adapter, including Kotlin JVM plugin, shadow plugin, repositories, and Typewriter dependency. It also configures task for shadowing and relocating CommandAPI. ```kotlin plugins { kotlin("jvm") version "2.0.0" id("io.github.goooler.shadow") version "8.1.7" } // Replace with your own information group = "me.yourusername" version = "0.0.1" repositories { // Typewriter required repositories mavenCentral() maven("https://repo.papermc.io/repository/maven-public/") maven("https://repo.codemc.io/repository/maven-releases/") maven("https://repo.opencollab.dev/maven-snapshots/") maven("https://jitpack.io") // Add other necessary repositories } dependencies { compileOnly("com.github.gabber235:typewriter:main-SNAPSHOT") // Latest release version // Add other dependencies as needed } tasks.withType { exclude("kotlin/**") exclude("META-INF/maven/**") // Important: Use the relocated commandapi which is shadowed by the plugin relocate("dev.jorel.commandapi", "com.github.gabber235.typewriter.extensions.commandapi") { include("dev.jorel.commandapi.**") } } kotlin { jvmToolchain(21) } ``` -------------------------------- ### Troubleshoot PlaceholderAPI Parsing in Typewriter Source: https://docs.typewritermc.com/0.5.1/docs/troubleshooting Steps to resolve issues where PlaceholderAPI placeholders are not parsed correctly within the Typewriter plugin. This involves identifying and installing necessary extensions, verifying placeholder functionality outside of Typewriter, and restarting the server if needed. ```bash /papi ecloud install [extension_name] /papi ecloud download player /papi parse me /papi parse me %player_name% ``` -------------------------------- ### Configure Typewriter Web Panel Source: https://docs.typewritermc.com/docs/getting-started/installation This YAML configuration enables the Typewriter web panel and WebSocket server. It requires specifying the server's hostname and opening specific ports for the panel and WebSocket. Ensure these ports are accessible and correctly configured within your server environment. ```yaml # Whether the web panel and web sockets are enabled. enabled: true # The hostname of the server. CHANGE THIS to your server's IP. hostname: "127.0.0.1" # The panel uses web sockets to sync changes to the server and allows you to work with multiple people at the same time. websocket: # The port of the websocket server. Make sure this port is open. port: 9092 # The authentication that is used. Leave unchanged if you don't know what you are doing. auth: "session" panel: # The panel can be disabled while the sockets are still open. Only disable this if you know what you are doing. # If the web sockets are disabled, then the panel will always be disabled. # The port of the web panel. Make sure this port is open. port: 8080 ``` -------------------------------- ### Implement Dialogue Entry with Context Interaction (Kotlin) Source: https://docs.typewritermc.com/beta/develop/extensions/entries/trigger/dialogue This snippet demonstrates the implementation of a dialogue entry (`ExampleStringInputDialogueEntry`) that exposes context keys defined in `ExampleEntryContextKeys`. It also includes the `ExampleDialogueMessenger` class, which extends `DialogueMessenger` and shows how to read and write to the `InteractionContext` in `init` and `tick` methods, utilizing both entry-specific and global context keys. ```kotlin @Entry("example_dialogue_with_context_keys", "A dialogue that captures string input", Colors.BLUE, "material-symbols:keyboard-rounded") // This tells Typewriter that this entry exposes some context @ContextKeys(ExampleEntryContextKeys::class) class ExampleStringInputDialogueEntry( override val id: String = "", override val name: String = "", override val criteria: List = emptyList(), override val modifiers: List = emptyList(), override val triggers: List> = emptyList(), override val speaker: Ref = emptyRef(), ) : DialogueEntry { override fun messenger(player: Player, context: InteractionContext): DialogueMessenger<*> { return ExampleDialogueMessenger(player, context, this) } } class ExampleDialogueMessenger( player: Player, context: InteractionContext, entry: ExampleStringInputDialogueEntry, ) : DialogueMessenger(player, context, entry) { override fun init() { super.init() // We can read and write the context in the init context[entry, ExampleEntryContextKeys.TEXT] = "Hey there" val text: String? = context[entry, ExampleEntryContextKeys.TEXT] } override fun tick(context: TickContext) { // We can also read and write the context in the tick method. // If we modify it, it will live modify the player.interactionContext values which other entries can use. this.context[entry, ExampleEntryContextKeys.NUMBER] = 42 this.context[entry, ExampleEntryContextKeys.POSITION] = player.position this.context[LuckyNumberKey] = 69 // And also read from the context val number: Int? = this.context[entry, ExampleEntryContextKeys.NUMBER] val position: Position? = this.context[entry, ExampleEntryContextKeys.POSITION] val luckyNumber = this.context[LuckyNumberKey] state = MessengerState.FINISHED super.tick(context) } } ```