index.md logo  Blabber 

Are you a modpack maker who wants their players to talk to more than a quest book? Are you a modded server owner who wants to sprinkle some ✨immersion✨ in their world? Are you perhaps a modder who is looking for an easy-to-use dialogue library with expansive tooling?

Blabber is a mod and library for displaying interactive dialogues to your players. Just write your dialogue description files, then start them on demand!

How it looks

Like that:

Example Dialogue Screen

Or like that:

Example Alt Dialogue Screen

You get to choose per-dialogue.

How it works

Commands

Blabber adds the /blabber command, allowing server operators and mapmakers to interact with the mod through commands.

  • /blabber dialogue
    • /blabber dialogue start <dialogue> [<targets>] [<interlocutor>] : Starts a dialogue for one or more players.
      • <dialogue> : the unique identifier for the dialogue
      • [<targets>] (optional) : If specified, must be either a player’s username or a target selector. If unspecified, defaults to the player using the command. When used in a command block, [<targets>] is not optional.
      • [<interlocutor>] (optional) : If specified, must be a target selector for a single entity. If unspecified, defaults to no interlocutor.

Format

Blabber will automatically load all JSON files in the data/[namespace]/blabber/dialogues directory. Those can be provided either by mods, or by datapacks. They will also be reloaded when running /reload, just like tags and functions.

Each file describes the various states a dialogue can be in.

Here’s a super basic example:

Yes please!
Yes please!
No thanks.
No thanks.

start


Do you want potatoes?

start...
ok
ok

accept


Alright, have potatoes

accept...
yes
yes
I changed my mind.
I changed my mind.

refuse


Are you sure?

refuse...
end_success
end_success
end_failure
end_failure
Viewer does not support full SVG 1.1
State diagram for a dialogue with 2 intermediate states and 2 end states

This dialogue has 5 states: start, accept, end_success, refuse, and end_failure. When a player gets this dialogue, they will first be shown “Do you want potatoes?” with the options “Yes please!” and “No thanks.”.

  • If they click “Yes please!”, they will switch to the accept state and be shown the text “Alright, have potatoes” with the only choice “ok”. When they click on that choice, they will switch to the end_success state, which ends the dialogue.

  • If they click “No thanks.”, they will switch to the refuse state and be shown the text “Are you sure?” with the choices “yes” and “I changed my mind.” being available.

    • If they click on “yes”, they will switch to the end_failure state, which also ends the dialogue.
    • However, if they click on “I changed my mind”, they will switch to the accept state, and be shown the same text and only choice.

Of course, this would be quite useless if we didn’t give the player their potatoes. We can fix this by adding an action to the end_success state; a command action with the value /give @s potato 12 should do nicely.

Here’s the JSON file corresponding to what we just described:

basic-dialogue.json

{
  "$schema": "https://ladysnake.org/wiki/blabber/dialogue.schema.json",
  "start_at": "start",
  "states": {
    "start": {
      "text": "Do you want potatoes?",
      "choices": [
        { "text": "Yes please!", "next": "accept" },
        { "text": "No thanks.", "next": "refuse" }
      ]
    },
    "accept": {
      "text": "Alright, have potatoes",
      "choices": [
        { "text": "ok", "next": "end_success" }
      ]
    },
    "refuse": {
      "text": "Are you sure?",
      "choices": [
        { "text": "I changed my mind.", "next": "accept" },
        { "text": "yes", "next": "end_failure" }
      ]
    },
    "end_success": {
      "type": "end_dialogue",
      "action": { "type": "blabber:command", "value": "/give @s potato 12" }
    },
    "end_failure": {
      "type": "end_dialogue"
    }
  }
}

Text

Texts in dialogues use the standard minecraft text format.

A couple of things to keep in mind :

  • you can write \n in text literals or translations to make a new line
  • fancy text content like entity selectors should work as expected, however they are resolved only once at the start of a dialogue (which means if you use a “nearest entity” selector and entities around the player move, the selector will keep targeting the one that was closest when the dialogue started)

Layout

As noted in the previous section, you can choose if your dialogue uses the classic layout, or the RPG layout (first and second screenshot, respectively).

When using the RPG layout, be mindful of how many choices you have and how long they are. While they may fit on the screen just fine in languages like English, they may take up too much space once translated (this also applies to the classic layout, though to a lesser degree).

The JSON looks like this (goes at the top level, replace "blabber:classic" with "blabber:rpg" for the alternative look):

Simple layout JSON
{
  "layout": {
    "type": "blabber:classic"
  }
}

Conditional choices

So what if you want to add requirements for specific dialogue paths? You could always make a separate dialogue file for each possible combination and trigger one based on prior conditions, but that becomes quite tedious when you have multiple conditions in a single dialogue, and it also does not give players any indication of what choices they may have missed.

To help with that situation, Blabber gives you the ability to lock some dialogue choices behind predicates. Here is what a locked choice may look like:

Example locked dialogue choice

When you make a choice conditional, you specify when the choice should be available, and how it should be displayed if not. The condition is given as an identifier for a predicate.

Mods can register their own LootConditions to allow virtually any check in said predicates.

As for the display, you can either make it so the choice is grayed out, displaying a little lock icon and explanation message when hovered, or set it to be hidden.

Note that you should avoid the hidden option with choices that can enable or disable themselves mid-dialogue, as it may cause some frustration due to player misclicks.

Here is an example of conditional choices in JSON:

Grayed out choice JSON
{
  "text": "I have money.",
  "next": "barter",
  "only_if": {
    "predicate": "babblings:holding_emerald",
    "when_unavailable": {
      "display": "grayed_out",
      "message": "You must be holding an emerald to pick this option."
    }
  }
}
Hidden choice JSON
{
  "text": "I have money.",
  "next": "barter",
  "only_if": {
    "predicate": "babblings:holding_emerald",
    "when_unavailable": {
      "display": "hidden"
    }
  }
}

Interlocutors

Of course, dialogues often involve talking to someone (or something). Since version 1.2.0, Blabber lets you specify an interlocutor entity when starting a dialogue (using an optional argument in the command / in the API).

Example command to start a dialogue with the closest entity as interlocutor
blabber dialogue start <dialogue> @s @e[limit=1,sort=nearest]

This relationship can be used in several ways:

In commands and texts

Blabber adds a new @interlocutor selector, which targets the current interlocutor of the command’s source. It only works when @s is a player, which includes commands run from dialogue actions and commands run through execute as <player> run ....

Example use:

Command using @interlocutor
effect give @interlocutor regeneration
Text using @interlocutor
{
  "text": [
    {"text":"I am "},
    {"selector":"@interlocutor"},
    {"text":". Pleased to make your acquaintance..."}
  ]
}

You can use @interlocutor in any of a dialogue’s text, including choices and locked content hints. You can also use any other selector in the same way, keeping in mind that @s will refer to the player themselves.

Here is how the above text may be used:

Example of a dialogue referring to the interlocutor

In predicates

The interlocutor entity can also be used in predicates files by using the blabber:interlocutor_properties condition type. This new condition type lets you apply the same checks to a player’s current interlocutor as minecraft:entity_properties would on the player themselves.

For example:

{
  "condition": "blabber:interlocutor_properties",
  "predicate": {
    "type": "minecraft:villager"
  }
}

Online Dialogue Maker

If you are allergic to code, try this online tool: blabber dialogue editor

Validation

To help creators design dialogues, and prevent players from getting stuck in a (possibly non-skippable) dialogue with no ending, both the online dialogue maker and the mod itself will perform several validation checks on dialogue files.

The validation process checks for the following issues and reports them by either logging a warning or failing with an error:

Errors:

  • States with no choices: Every non-end state must have at least one choice leading out of it. If any state has no choices defined, an error will be thrown.
  • Softlock states: Every state must have a path leading to an ending (no infinite loops). If any state is lacking a path to an end state, an error will be thrown.

Warnings:

  • Conditional softlock states: Any state that only has conditional paths leading to an ending will be reported. Blabber cannot tell whether a condition will necessarily be fulfilled when getting to such a state, and thus cannot prove that the player will not be softlocked.
  • Unreachable states: Any state that is disconnected from the main dialogue graph will be reported with a warning message. While they do not cause immediate issues for players, you may want to connect or remove such orphan states.

Mod Compatibility

Heracles

The Heracles quest mod can be used with the Heracles for Blabber add-on to incorporate dialogues into your modpack’s or server’s quests.

Text Animator

The Text Animator mod can be used to add special effects to your dialogues’ texts :

Text animator with Blabber demo (warning: movement and color flashes)

Using Blabber (for developers)

If you are a developer, you can use Blabber as a library for your own project by inserting the following in your build.gradle :

You can then add the library version to your gradle.propertiesfile:

gradle.properties:

# Blabber
blabber_version = <BLABBER_VERSION>
# Fabric Permissions API
fpa_version = 0.2-SNAPSHOT
# Cardinal Components
cca_version = <CCA_VERSION>

build.gradle:

repositories {
    maven { 
        name = "Ladysnake Mods"
        url = "https://maven.ladysnake.org/releases"
        content {
            includeGroup 'io.github.ladysnake'
            includeGroup 'org.ladysnake'
            includeGroupByRegex 'dev\\.onyxstudios.*'
        }
    }
    maven {
        name = "Nexus Repository Manager"
        url = "https://oss.sonatype.org/content/repositories/snapshots"
    }
}

dependencies {
    modImplementation "org.ladysnake:blabber:${blabber_version}"
    include "org.ladysnake:blabber:${blabber_version}"
    // Blabber dependencies
    include "me.lucko:fabric-permissions-api:${fpa_version}"
    include "dev.onyxstudios.cardinal-components-api:cardinal-components-base:${cca_version}"
    include "dev.onyxstudios.cardinal-components-api:cardinal-components-entity:${cca_version}"
}

gradle.properties:

# Blabber
blabber_version = <BLABBER_VERSION>
# Fabric Permissions API
fpa_version = 0.2-SNAPSHOT
# Cardinal Components
cca_version = <CCA_VERSION>

build.gradle.kts:

repositories {
    maven {
        name = "Ladysnake Mods"
        url = "https://maven.ladysnake.org/releases"
        content {
            includeGroup("io.github.ladysnake")
            includeGroup("org.ladysnake")
            includeGroupByRegex("""dev\.onyxstudios.*""")
        }
    }
    maven {
        name = "Nexus Repository Manager"
        url = "https://oss.sonatype.org/content/repositories/snapshots"
    }
}

dependencies {
    val blabberVersion = property("blabber_version") as String
    val ccaVersion = property("cca_version") as String
    val fpaVersion = property("fpa_version") as String
    modImplementation("org.ladysnake:blabber:${blabber_version}")
    include("org.ladysnake:blabber:${blabber_version}")
    // Blabber dependencies
    include("me.lucko:fabric-permissions-api:${fpa_version}")
    include("dev.onyxstudios.cardinal-components-api:cardinal-components-base:${cca_version}")
    include("dev.onyxstudios.cardinal-components-api:cardinal-components-entity:${cca_version}")
}

libs.versions.toml:

[versions]
blabber = '<BLABBER_VERSION>'
cardinalComponentsApi = '<CCA_VERSION>'
fabricPermissionsApi = '0.2-SNAPSHOT'

[libraries]
cca-base = { module = "dev.onyxstudios.cardinal-components-api:cardinal-components-base", version.ref = "cardinalComponentsApi" }
cca-entity = { module = "dev.onyxstudios.cardinal-components-api:cardinal-components-entity", version.ref = "cardinalComponentsApi" }
fpa = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissionsApi" }
blabber = { module = "org.ladysnake:blabber", version.ref = "blabber" }

[bundles]
blabber = [ "cca-base", "cca-entity", "fpa", "blabber" ]

build.gradle or build.gradle.kts:

repositories {
    maven {
        name = "Ladysnake Mods"
        url = "https://maven.ladysnake.org/releases"
        content {
            includeGroup("io.github.ladysnake")
            includeGroup("org.ladysnake")
            includeGroupByRegex("""dev.onyxstudios.*""")
        }
    }
    maven {
        name = "Nexus Repository Manager"
        url = "https://oss.sonatype.org/content/repositories/snapshots"
    }
}

dependencies {
    // Replace modImplementation with modApi if you expose Blabber's interfaces in your own API
    modImplementation(libs.bundles.blabber)
    // Includes Blabber and its dependencies as a Jar-in-Jar dependency (optional but recommended)
    include(libs.bundles.blabber)
}

You can find the current version of Blabber in the releases tab of the repository on Github, and the latest CCA version in the appropriate repository.

API

The two most relevant methods are as follow:

  • Blabber#startDialogue(ServerPlayerEntity, Identifier): starts the dialogue with the given id for the given player
  • Blabber#registerAction: registers an action for use in dialogues, 2 overloads available:
    • registerAction(Identifier, DialogueAction): registers a simple action that takes no additional configuration from the dialogue description file.
    • registerAction(Identifier, Codec<? extends DialogueAction): registers an action type. The codec is used to create new dialogue actions based on the action value specified in the dialogue description file.

Custom layouts

It is possible to register your own custom layout with its completely custom screen by calling BlabberScreenRegistry#register in your client entrypoint. The API is however marked unstable, as the layout system is susceptible to being refactored to allow arbitrary data being passed to the screen (contributions welcome).

JSON Schema

The schema for Blabber dialogue files is available here: dialogue.schema.json