Skip to content

Parsing placeholders

There are few ways (and types) of placeholders you can parse with PlaceholderAPI. So depending on your use case some of these will be more useful than others.

Placeholder Context

Placeholders should be provided with a context at all times. The context allows the placeholder parser to inject placeholders that require an optional context, such as a player.

A PlaceholderContext is created using one of the PlaceholderContext.of(...) variants. Depending on the objects passed, different placeholders may be available.

PlaceholderContext.of(...) Variants
  • MinecraftServer
    • May use placeholders that depend on the server
    • May use placeholders that depend on ServerCommandSource (Note: uses the command source from MinecraftServer.getCommandSource())
  • GameProfile
    • May use placeholders that depend on the server
    • May use placeholders that depend on ServerCommandSource (Note: creates a new dummy command source at (0,0,0))
    • May use placeholders that depend on GameProfile
  • ServerPlayerEntity
    • May use placeholders that depend on the server
    • May use placeholders that depend on ServerCommandSource
    • May use placeholders that depend on ServerWorld
    • May use placeholders that depend on ServerPlayerEntity
    • May use placeholders that depend on Entity
    • May use placeholders that depend on GameProfile
  • ServerCommandSource
    • May use placeholders that depend on the server
    • May use placeholders that depend on ServerCommandSource
    • May use placeholders that depend on ServerWorld
    • May use placeholders that depend on ServerPlayerEntity only if the source has a player
    • May use placeholders that depend on Entity only if the source has an entity
    • May use placeholders that depend on GameProfile only if the source has a player
  • Entity
    • May use placeholders that depend on the server
    • May use placeholders that depend on ServerCommandSource
    • May use placeholders that depend on ServerWorld
    • May use placeholders that depend on ServerPlayerEntity only if the source has a player
    • May use placeholders that depend on Entity
    • May use placeholders that depend on GameProfile only if the source has a player

Parsing global placeholders

Parsing global placeholders is really simple, as long as you have access to ServerPlayerEntity or MinecraftServer object. You just need to simply import eu.pb4.placeholders.api.Placeholders and call parseText. This method will return fully parsed Text, which can be displayed to the user.

Example

Text message = Placeholders.parseText(textInput, PlaceholderContext.of(...));
val message = Placeholders.parseText(textInput, PlaceholderContext.of(...))

Placeholders itself will use default formatting of %category:placeholder%. If you want to use other formatting for them (which is recommended), you can use parseText(Text, PlaceholderContext, Pattern). Prefer those listed in Preferred Patterns for static.

Parsing own/custom/predefined placeholders

If you want to parse your own placeholders, you can do this in 2 ways.

Static placeholders

To parse static placeholders you need to create a Map with String as a key and Text as a value. You also need a Pattern object (which can be taken from predefined ones). Then it's as simple as calling a parseText static method on PlaceholderAPI class.

Example

StaticPlaceholders.java
public class StaticPlaceholders {
    /**
     * Formats a player message according to <code>inputText</code>
     *
     * Example input:
     * <code>
     * ${playerName} says "${message}"
     * </code>
     *
     * @param inputText   The text that is parsed for placeholders.
     *                    Example input:
     *                    <code>${playerName} says "${message}"</code>
     * @param player      The player name used to replace the ${playerName}
     *                    variable in the input. Example input:
     *                    the player who's name is 'ThePlayerUsername'
     * @param messageText The message text used to replace the ${message}
     *                    variable in the input. Example input:
     *                    <code>this is the message</code>
     *
     * @return The formatted message. Example return:
     *         <code>ThePlayerUsername says "this is the message"</code>
     */
    public static Text formatPlayerMessage(Text inputText, ServerPlayerEntity player,
                                           Text messageText) {
        Map<String, Text> placeholders = Map.of(
                "message", messageText,        // replace ${message} with the messageText
                "playerName", player.getName() // replace ${playerName} with the player's name
                                               );

        return Placeholders.parseText(inputText,
                                      Placeholders.PREDEFINED_PLACEHOLDER_PATTERN,
                                      placeholders); // parse the inputText
    }
}
StaticPlaceholders.kt
object StaticPlaceholders {
    /**
     * Formats a player message according to [inputText]
     *
     * Example input:
     * ```
     * ${playerName} says "${message}"!
     * ```
     *
     * @param inputText The text that is parsed for placeholders.
     *                  Example input:
     *                  `${playerName} says "${message}"`
     *
     * @param player The player name used to replace the `${playerName}` variable in the input.
     *               Example input:
     *               the player who's name is `ThePlayerUsername`
     *
     * @param messageText The text used to replace the `${message}` variable in the input.
     *                    Example input:
     *                    `this is the message`
     *
     * @return The formatted message.
     *         Example return:
     *         ```
     *         ThePlayerUsername says "this is the message"!
     *         ```
     */
    fun formatPlayerMessage(inputText: Text?, player: ServerPlayerEntity, messageText: Text): Text {
        val placeholders = mapOf(
                "message" to messageText,    // replace ${message} with the messageText
                "playerName" to player.name, // replace ${playerName} with the player's name
                                )
        return Placeholders.parseText(inputText, Placeholders.PREDEFINED_PLACEHOLDER_PATTERN,
                                      placeholders) // parse the inputText
    }
}

Dynamic placeholders

In case where you want to parse placeholder with a context similar to global one, you need to create a Map with Identifier as a key and PlaceholderHandler as a value (same as adding global ones). You also will need a pattern object, which is the same as with static ones.

As opposite to global ones, you don't need to define namespace/category as it can default to minecraft one (for simpler user input). Then you just parse it with parseText(Text, PlaceholderContext, Pattern, PlaceholderGetter).

Example

DynamicPlaceholders.java
public class DynamicPlaceholders {
    private static final Random random = new Random();

    /**
     * Example input:
     * <code>
     * Hello! ${player blue}. Random number between 0 and 20: ${random 20}
     * </code>
     *
     * Example output:
     * <code>
     * Hello! <blue>ThePlayerName</blue>. Random number: 13
     * </code>
     */
    public static Text parseInputText(ServerPlayerEntity player, Text inputText) {
        // parse the inputText message
        return Placeholders.parseText(inputText, PlaceholderContext.of(player),
                                      Placeholders.PREDEFINED_PLACEHOLDER_PATTERN,
                                      DynamicPlaceholders::getPlaceholder);
    }

    private static PlaceholderHandler getPlaceholder(String id) {
        return switch (id) {
            case "player" -> DynamicPlaceholders::playerPlaceholder;
            case "random" -> DynamicPlaceholders::randomPlaceholder;
            default -> null;
        };
    }

    private static PlaceholderResult playerPlaceholder(PlaceholderContext ctx, String arg) {
        if (arg == null)
            return PlaceholderResult.invalid("No argument!");

        if (!ctx.hasPlayer())
            return PlaceholderResult.value(
                    Text.literal("You are not a player!")
                        .setStyle(Style.EMPTY.withColor(TextColor.parse(arg)))
                                          );

        return PlaceholderResult.value(
                ctx.player().getName().copy()
                   .setStyle(Style.EMPTY.withColor(TextColor.parse(arg)))
                                      );
    }

    private static PlaceholderResult randomPlaceholder(PlaceholderContext ctx, String arg) {
        if (arg == null) {
            int randomNumber = random.nextInt(10);
            return PlaceholderResult.value(String.valueOf(randomNumber));
        }

        try {
            int randomNumber = random.nextInt(Integer.parseInt(arg));
            return PlaceholderResult.value(String.valueOf(randomNumber));
        } catch (NumberFormatException e) {
            return PlaceholderResult.invalid("Invalid number!");
        }
    }
}
DynamicPlaceholders.kt
object DynamicPlaceholders {
    private val random = Random()

    /**
     * Example input:
     * ```
     * Hello! ${player blue}. Random number between 0 and 20: ${random 20}
     * ```
     *
     * Example output:
     * ```
     * Hello! <blue>ThePlayerName</blue>. Random number: 13
     * ```
     */
    fun parseInputText(player: ServerPlayerEntity, inputText: Text): Text {
        // parse the inputText message
        return Placeholders.parseText(inputText, PlaceholderContext.of(player),
                                      Placeholders.PREDEFINED_PLACEHOLDER_PATTERN,
                                      DynamicPlaceholders::getPlaceholder)
    }

    private fun getPlaceholder(id: String): PlaceholderHandler? {
        return when (id) {
            "player" -> PlaceholderHandler(DynamicPlaceholders::playerPlaceholder)
            "random" -> PlaceholderHandler(DynamicPlaceholders::randomPlaceholder)
            else     -> null
        }
    }

    private fun playerPlaceholder(ctx: PlaceholderContext, arg: String?): PlaceholderResult {
        if (arg == null)
            return PlaceholderResult.invalid("No argument!")

        if (ctx.player == null)
            return PlaceholderResult.value(
                    Text.literal("You are not a player!")
                            .setStyle(Style.EMPTY.withColor(TextColor.parse(arg)))
                                          )

        return PlaceholderResult.value(
                ctx.player!!.name.copy()
                        .setStyle(Style.EMPTY.withColor(TextColor.parse(arg)))
                                      )
    }

    private fun randomPlaceholder(ctx: PlaceholderContext, arg: String?): PlaceholderResult {
        return try {
            val randomNumber = random.nextInt(arg?.toInt() ?: 10)
            PlaceholderResult.value(randomNumber.toString())
        } catch (e: NumberFormatException) {
            PlaceholderResult.invalid("Invalid number!")
        }
    }
}

Preferred Patterns for static

PlaceholderAPI has few Patterns you can use, which are accessible as static objects on Placeholders class.

  • Placeholders.PREDEFINED_PLACEHOLDER_PATTERN (${placeholder}) - works the best in most cases, doesn't collide with other ones.
  • Placeholders.ALT_PLACEHOLDER_PATTERN_CUSTOM ({placeholder}) - second best, but have more chance of colliding with user formatting.

There are other ones, which usage is allowed, but they might work worse.

  • Placeholders.PLACEHOLDER_PATTERN_CUSTOM (%placeholder%) - is the same as default one, but doesn't require :.
  • Placeholders.PLACEHOLDER_PATTERN (%category:placeholder%) - used by default global placeholders (requires category).
  • Placeholders.PLACEHOLDER_PATTERN_ALT ({category:placeholder}) - used as alternative formatting for global ones (requires category).