πŸ”₯Join our Discord for support, updates, and custom commissions.
Polaroid Studio Logo
PolaroidSTUDIO
DiscordLog In

πŸ“‹ Placeholder & API

Placeholders

PlaceholderAPI, under the identifier polaroidchat. Written as %polaroidchat_<name>%.

PlaceholderGives
nickTheir nick, wearing its colour and style
nick_rawTheir nick as plain text
nick_hasWhether they have one at all
nick_cooldownHow long until they may change it
nickcolor_hasWhether a nick colour is applied
nickcolor_typelegacy, hex, gradient or animate
nickcolor_displaynameThe name of the colour they are wearing
nickcolor_stylesThe formats on top of it
nickcolor_previewA sample drawn in that colour
chatcolor_*The same five, for their chat colour

The API

PolaroidChatAPI.get() after the plugin has enabled.

PolaroidChatAPI api = PolaroidChatAPI.get();

String nick = api.getNick(uuid);
api.setNick(player, "Steve");
api.resetNick(player);

AppliedColor colour = api.getChatColor(uuid);
api.setChatColor(player, colour);
api.resetChatColor(player);

The nick-colour trio (getNickColor, setNickColor, resetNickColor) matches.

AppliedColor carries the colour's type, its values, its id and the formats on it β€” the same shape the storage holds.

Events

Two, both cancellable, fired in order.

PolaroidPreChatEvent β€” before anything is built. The place to change what was said or who hears it.

@EventHandler
public void onPreChat(PolaroidPreChatEvent event) {
    event.getSender();
    event.getRawText();
    event.setRawText("…");        // change the message
    event.getReceivers();         // mutable β€” add or remove
    event.getChannelId();
    event.addMarker(uuid, comp);  // attach a component for one receiver
    event.setCancelled(true);
}

PolaroidChatEvent β€” after the line is built, before it is sent.

@EventHandler
public void onChat(PolaroidChatEvent event) {
    event.getPrefixComponent();   // everything before the message
    event.getMessageComponent();
    event.setMessageComponent(c);
    event.getReceivers();
    event.getMarkers();
    event.setCancelled(true);
}

Markers are how a per-receiver difference is expressed: a component attached against one player's uuid reaches only them, so one message can read differently for one reader without being sent twice.

Optional integrations

None are required. Each is detected at startup and silently skipped if absent.

PluginUsed for
PlaceholderAPIPlaceholders in every field, and the expansion above
Vault%vault_group% in chat formats
LuckPermsPicking a chat format by group weight
FloodgateNative forms for Bedrock players
Nexo<glyph:…> in messages, and nexo:id items in menus
CustomNameplatesNameplates that follow the nick
SkinsRestorerSkins behind <head:…>
SimpleVoiceChatVoice groups alongside channels

Items in menus

Any of these can supply an item by name, written as plugin:id in a menu's material:

MythicMobs Β· ItemsAdder Β· Oraxen Β· CraftEngine Β· HeadDatabase Β· MMOItems Β· ItemEdit Β· WeaponMechanics Β· ExecutableItems Β· ExecutableBlocks Β· EcoItems Β· eco

Plus two that need no plugin: itemmodel:minecraft:trophy for an item_model component (1.21.4+), and a textured or named head.

Building from source

Needs JDK 21. Newer JDKs are not yet parsed by the Gradle version in use.

export JAVA_HOME=/path/to/jdk-21
./gradlew shadowJar        # build/libs/PolaroidChat-<version>-b<build>.jar
./gradlew test

./gradlew shadowJar -Pbuild=42 stamps a build number; without one it is local.

MySQLStorageContractTest needs Docker (Testcontainers) and fails without it. Every other test runs anywhere.