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.

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:

FromMarkup.java
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:

Builder.java
ImmersiveMessage msg = ImmersiveMessage.builder(100, "Welcome to the server!");

There’s also fromSpans when you’ve built a list of TextSpan objects yourself:

FromSpans.java
List<TextSpan> spans = ...; // built with TextSpan.Builder
ImmersiveMessage msg = ImmersiveMessage.fromSpans(100, spans);

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:

SinglePlayer.java
// ctx is a CommandContext<CommandSourceStack>
ServerPlayer player = ctx.getSource().getPlayerOrException();

All players in a level:

LevelPlayers.java
ServerLevel level = ...; // from event, command, or server
for (ServerPlayer player : level.players()) {
EmbersTextAPI.sendMessage(player, msg);
}

Filtered by predicate:

FilteredPlayers.java
server.getPlayerList().getPlayers().stream()
.filter(p -> p.hasPermissions(2))
.forEach(p -> EmbersTextAPI.sendMessage(p, msg));

By UUID:

ByUUID.java
ServerPlayer player = server.getPlayerList().getPlayer(uuid);
if (player != null) {
EmbersTextAPI.sendMessage(player, msg);
}

Once you have a player and a message, send it:

Send.java
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).

The fluent builder methods let you control the full presentation. Durations and tick counts are always in game ticks (20 ticks = 1 second):

Options.java
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:

MethodWhat 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 NeoForge player-join listener that sends an immersive greeting:

JoinListener.java
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());