0.9.X API changes
Typewriter v0.9 requires the Kotlin JVM plugin version 2.2.10 and the Typewriter module plugin version 2.0.0. Update your Gradle configuration:
plugins {
kotlin("jvm") version "2.2.10"
id("com.typewritermc.module-plugin") version "2.0.0"
}
Extensions targeting v0.9 will fail to compile with older Kotlin versions.
ThreadType Deprecation
Originally, ThreadType was used to invoke the coroutine context for different processes in Typewriter.
This is now deprecated in favor of actual coroutines dispatchers and contexts.
Primary Bukkit Thread
This operates on the Paper main thread and should be used for all Paper API interactions.
ThreadType.SYNC.launch {}
Dispatchers.Sync.launch {}
Async Bukkit Thread
This executes on the asynchronous Paper thread, suitable for tasks without strict timing requirements or those intended for the Paper scheduler.
ThreadType.ASYNC.launch {}
Dispatchers.TickedAsync.launch {}
Async Typewriter Thread Pool
Utilize the Typewriter thread pool for time-sensitive tasks. As the most asynchronous of the three dispatchers and independent of the paper scheduler, it continues operating even during scheduler lag.
ThreadType.DISPATCHERS_ASYNC.launch {}
Dispatchers.UntickedAsync.launch {}
If working in IntelliJ, it will automatically suggest the correct replacements.
SpeakerEntry and EntityInstanceEntry Changes
The way sound is handled in SpeakerEntry and, by extension, EntityInstanceEntry has been updated to use variables.
Introduction of InteractionContext
Many updated methods now require a nullable InteractionContext. This context is essential for resolving Var values, allowing properties like sound or volume to be dynamic based on the current game state.
You can typically obtain the InteractionContext in two ways:
-
From the
Playerobject using theinteractionContextextension property:val interactionContext: InteractionContext? = player.interactionContext -
From the execution scope of an entry, where it is often provided as a
contextproperty (e.g., inActionTrigger).override fun ActionTrigger.execute() {
// The 'context' property is directly available here
val dynamicSound = sound.get(player, context)
player.playSound(dynamicSound, context)
}
Sound Field
The sound field now utilizes a Var<Sound> type. This change affects how you set and get sound values.
class CustomEntityDefinition(
...
override val sound: Sound = Sound.EMPTY,
...
) : SimpleEntityDefinition {
class CustomEntityDefinition(
...
override val sound: Var<Sound> = ConstVar(Sound.EMPTY),
...
) : SimpleEntityDefinition {
To get the sound:
Retrieving the sound now requires the context of a player and an optional interactionContext.
val sound: Sound = entry.sound
val sound: Sound = entry.sound.get(player, interactionContext)
Sound Class Changes
Several fields and methods within the Sound class have been updated.
Volume and Pitch Fields
The volume and pitch fields have been changed from Float to Var<Float>.
To set the volume and pitch:
When creating a new Sound instance, you now need to wrap the volume and pitch values in a Var, such as ConstVar.
val sound = Sound(DefaultSoundId("minecraft:ambient.cave"), SelfSoundSource, AdventureSound.Source.MASTER, 1.0f, 1.0f)
val sound = Sound(DefaultSoundId("minecraft:ambient.cave"), SelfSoundSource, AdventureSound.Source.MASTER, ConstVar(1.0f), ConstVar(1.0f))
To get the volume and pitch:
Accessing the volume and pitch now requires a player and an optional interactionContext.
val volume: Float = sound.volume
val pitch: Float = sound.pitch
val volume: Float = sound.volume.get(player, interactionContext)
val pitch: Float = sound.pitch.get(player, interactionContext)
play Method
The play method's signature has been updated. It now takes a Player and a nullable InteractionContext instead of an Audience.
sound.play(audience)
sound.play(player, interactionContext)
The corresponding extension functions have also been updated to reflect this change.
fun Audience.playSound(sound: Sound)
fun Audience.stopSound(sound: Sound)
fun Player.playSound(sound: Sound, context: InteractionContext?)
fun Player.stopSound(sound: Sound)
Interaction teardown changes
The Interaction.teardown method removed the force parameter. This is no longer needed and was ambiguous.
override suspend fun teardown(force: Boolean) {
override suspend fun teardown() {
interaction.teardown(force = true)
interaction.teardown()
FactTracker Listener Changes
The FactTracker listener callback signature has been updated to provide access to old and new fact values.
Updated Listener Usage
player.listenForFacts(listOf(factRef)) { player, factRef ->
// Handle fact change
player.refresh()
}
player.listenForFacts(listOf(factRef)) {
// Access player, oldValue, newValue properties
this.player.refresh()
}
The listener now receives a context with player, ref, oldValue, and newValue properties instead of separate parameters.