Sending Messages
All the same messages you can send from /eta send are available as first-class Java objects. You construct an ImmersiveMessage, choose your targets, and call EmbersTextAPI.sendMessage.
Building a message
Section titled “Building a message”ImmersiveMessage.fromMarkup is the shortest path when you want styled text. The first argument is the duration in ticks; the second is a markup string:
ImmersiveMessage msg = ImmersiveMessage.fromMarkup(100, "<gold>Welcome to the server!");If you don’t need markup and just want a plain string, use builder instead:
ImmersiveMessage msg = ImmersiveMessage.builder(100, "Welcome to the server!");There’s also fromSpans when you’ve built a list of TextSpan objects yourself:
List<TextSpan> spans = ...; // built with TextSpan.BuilderImmersiveMessage msg = ImmersiveMessage.fromSpans(100, spans);Choosing targets
Section titled “Choosing targets”EmbersTextAPI.sendMessage takes a single ServerPlayer. You’re responsible for collecting the players you want before you call it.
From a command or event that hands you a player directly:
// ctx is a CommandContext<CommandSourceStack>ServerPlayer player = ctx.getSource().getPlayerOrException();All players in a level:
ServerLevel level = ...; // from event, command, or serverfor (ServerPlayer player : level.players()) { EmbersTextAPI.sendMessage(player, msg);}Filtered by predicate:
server.getPlayerList().getPlayers().stream() .filter(p -> p.hasPermissions(2)) .forEach(p -> EmbersTextAPI.sendMessage(p, msg));By UUID:
ServerPlayer player = server.getPlayerList().getPlayer(uuid);if (player != null) { EmbersTextAPI.sendMessage(player, msg);}Sending
Section titled “Sending”Once you have a player and a message, send it:
EmbersTextAPI.sendMessage(player, msg);That’s the entire call. There’s no return value; the packet is dispatched immediately on the calling thread (this must be the server thread).
Lifetime, fade, anchor, scale
Section titled “Lifetime, fade, anchor, scale”The fluent builder methods let you control the full presentation. Durations and tick counts are always in game ticks (20 ticks = 1 second):
ImmersiveMessage msg = ImmersiveMessage.fromMarkup(100, "<gold>New area discovered") .fadeInTicks(10) .fadeOutTicks(20) .anchor(TextAnchor.MIDDLE) .scale(2.0f) .offset(0f, -20f);A few commonly used methods:
| Method | What it changes |
|---|---|
fadeInTicks(int) | ticks to fade from invisible to full opacity |
fadeOutTicks(int) | ticks to fade from full opacity to invisible |
anchor(TextAnchor) | screen anchor point (TOP_CENTER, MIDDLE, etc.) |
align(TextAlign) | text alignment relative to the anchor (LEFT, CENTER, RIGHT) |
scale(float) | uniform text scale multiplier |
offset(float x, float y) | pixel offset from the anchor position |
shadow(boolean) | enable or disable the text drop shadow |
wrap(int maxWidth) | wrap text at this pixel width |
A complete example
Section titled “A complete example”A NeoForge player-join listener that sends an immersive greeting:
package com.example.mymod.server;
import net.neoforged.bus.api.SubscribeEvent;import net.neoforged.neoforge.event.entity.player.PlayerEvent;import net.minecraft.server.level.ServerPlayer;import net.tysontheember.emberstextapi.EmbersTextAPI;import net.tysontheember.emberstextapi.immersivemessages.api.ImmersiveMessage;import net.tysontheember.emberstextapi.immersivemessages.api.TextAnchor;
public class JoinListener {
@SubscribeEvent public void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) { if (!(event.getEntity() instanceof ServerPlayer player)) return;
ImmersiveMessage greeting = ImmersiveMessage .fromMarkup(80, "<gold>" + player.getScoreboardName() + "</gold> has joined") .fadeInTicks(10) .fadeOutTicks(20) .anchor(TextAnchor.MIDDLE) .scale(1.5f);
EmbersTextAPI.sendMessage(player, greeting); }}Register it on the NeoForge event bus in your mod constructor:
NeoForge.EVENT_BUS.register(new JoinListener());