Skip to main content

Effect Registry

EffectRegistry is the central factory for all effects. Built-in effects are registered and locked during client setup. Third-party mods can register custom effects with unique names.

Package: net.tysontheember.emberstextapi.immersivemessages.effects


Effect Interface

Every effect implements this interface:

public interface Effect {

/** Apply this effect to a character's rendering state. Called once per character per frame. */
void apply(@NotNull EffectSettings settings);

/** The canonical name of this effect. Used for serialization and registry lookup. */
@NotNull String getName();

/** Serialize to string: "effectName key1=value1 key2=value2". Default returns just the name. */
@NotNull default String serialize() { return getName(); }
}

BaseEffect

Abstract base class that most effects extend. Provides parameter handling and color parsing utilities.

public abstract class BaseEffect implements Effect {

public BaseEffect(Params params) { ... }

/** Get the raw parameters passed to this effect. */
public Params getParams() { ... }

/** Parse a hex color string to RGB float array [r, g, b]. Accepts "FF0000" or "#FF0000". */
protected static Optional<float[]> parseColor(String hex) { ... }

/** Parse a color from a named parameter with a default fallback. */
protected static float[] parseColor(Params params, String key, float[] defaultColor) { ... }

/** Convert packed RGB integer to float array [r, g, b]. */
protected static float[] intToRGB(int packed) { ... }

/** Convert RGB float values to packed integer. */
protected static int rgbToInt(float r, float g, float b) { ... }
}

EffectRegistry API

Registering an Effect

/**
* Register an effect factory.
* Names are case-insensitive (normalized to lowercase).
* After the registry is locked, built-in names cannot be overwritten.
*/
public static synchronized void register(String name, Function<Params, Effect> factory)

Example:

EffectRegistry.register("fan", FanEffect::new);

Creating an Effect

/** Create an effect by name with the given parameters. */
public static Effect create(String name, Params params)

/** Parse and create an effect from a tag content string: "effectName key1=value1" */
public static Effect parseTag(String tagContent)

Example:

Effect wave = EffectRegistry.create("wave", TypedParams.of("a", 2.0, "f", 1.5));
Effect neon = EffectRegistry.parseTag("neon r=3 i=2.0");

Query Methods

public static boolean isRegistered(String name)      // Check if registered
public static Set<String> getRegisteredEffects() // All registered names
public static boolean isBuiltIn(String name) // Is it a locked built-in?
public static boolean isLocked() // Is registry locked?

Initialization

/**
* Initialize all built-in effects and lock the registry.
* Called automatically during mod client setup.
* Safe to call multiple times (subsequent calls are no-ops).
*/
public static synchronized void initializeDefaultEffects()

This is called by ETA during its client setup. You don't need to call it yourself.


Built-In Effect Names

These names are locked after initialization and cannot be overwritten:

NameAliasesCategory
rainbow, rainbColor
grad, gradientColor
colorColor
pulseColor
fadeColor / Global
waveMotion
shakeMotion
bounceMotion
circleMotion
wiggleMotion
pend, pendulumMotion
swingMotion
scrollMotion
turb, turbulenceMotion
glitchSpecial
neon, glowSpecial
shadowSpecial
typewriter, typeAnimation
obfuscateAnimation

Params Interface

The Params interface provides typed access to effect parameters:

public interface Params {
OptionalDouble getDouble(String key);
OptionalBoolean getBoolean(String key);
Optional<String> getString(String key);
}

Implementations:

  • EmptyParams — no parameters (singleton, use Params.EMPTY)
  • TypedParams — wraps an immutable map of parsed key-value pairs

Creating TypedParams programmatically:

Params params = TypedParams.of("a", 2.0, "f", 1.5);

ValidationHelper

Utility for clamping parameter values to safe ranges:

/**
* Clamp a float value, logging a warning if it was out of range.
*
* @param effectName Name of the effect (for log messages)
* @param paramName Name of the parameter (for log messages)
* @param value The value to clamp
* @param min Minimum allowed value
* @param max Maximum allowed value
* @return Clamped value
*/
public static float clamp(String effectName, String paramName, float value, float min, float max)

Preset System

What Is a Preset?

A preset is a reusable bundle of effects and style overrides defined in a JSON file. Presets are used as markup tags (e.g., <epic>, <legendary>).

PresetDefinition

public record PresetDefinition(
String name,
int formatVersion,
List<EffectEntry> effects,
StyleOverrides styles
)

public record EffectEntry(
String type, // Effect name
Map<String, Object> params // Effect parameters
)

public record StyleOverrides(
Boolean bold,
Boolean italic,
Boolean underline,
Boolean strikethrough,
Boolean obfuscated,
String color, // Hex color string
String font // ResourceLocation string
)

JSON Format

Presets live in assets/emberstextapi/presets/ — the filename (without .json) is the tag name.

assets/emberstextapi/presets/fire.json
{
"format_version": 1,
"effects": [
{ "type": "grad", "params": { "from": "FF4400", "to": "FFAA00", "f": 1.5 } },
{ "type": "neon", "params": { "r": 2.0, "c": "FF3300", "p": 1.5 } }
],
"styles": {
"bold": true,
"color": "FF6600"
}
}

Used as: <fire>FIRE SALE!</fire>

Built-In Presets

FileTagEffectsStyle
epic.json<epic>Pulse (f=2.0) + WaveBold italic purple (#AA00FF)
legendary.json<legendary>Rainbow (f=1.5, w=0.8) + NeonBold gold (#FFD700)
spooky.json<spooky>Shake (a=0.5, f=3.0) + Fade (a=0.4, f=1.5)Italic dark purple (#2D1B4E)

PresetRegistry

public static PresetDefinition get(String name)     // Get by name; null if not found
public static boolean has(String name) // Check existence
public static void register(PresetDefinition preset) // Register programmatically

Adding Custom Presets

In a mod: Place JSON files at src/main/resources/assets/emberstextapi/presets/

In a resource pack: Place at assets/emberstextapi/presets/ within the pack.

PresetLoader automatically loads all JSON files from this path at mod initialization. Third-party presets load alongside built-in ones.