Data Registry

Data Registry

DataRegistry is Lotus's type-safe state bag for temporary menu data.

Use it when you need a view or a button to remember something like:

  • the current page number
  • the selected category
  • the UUID of the model behind a clicked button

Key<T>

A Key<T> names one piece of data and declares the expected type:

public final class Keys {
    public static final Key<Integer> PAGE = Key.of("page", Integer.class);
    public static final Key<UUID> TARGET = Key.of("target", UUID.class);
    public static final Key<String> CATEGORY = Key.of("category", String.class);
}

Declare keys once in a shared Keys class and reuse them. That keeps menu code consistent and searchable.

One Crucial Rule About Keys

Key equality is based on the name, not the Java type.

Key<Integer> PAGE = Key.of("page", Integer.class);
Key<String> PAGE_TEXT = Key.of("page", String.class);

Those two keys conflict because they share the same name.

Do not reuse the same key name for different types. Pick one name per concept and stick to it.

DataRegistry

The registry API is intentionally small:

DataRegistry data = DataRegistry.empty();

data.put(Keys.PAGE, 2);
data.put(Keys.TARGET, player.getUniqueId());

int page = data.require(Keys.PAGE);
Optional<UUID> target = data.get(Keys.TARGET);
boolean hasPage = data.contains(Keys.PAGE);
data.remove(Keys.PAGE);

If the value is missing, require(...) throws. If the value exists but has the wrong type, Lotus throws a ClassCastException.

That type-checking happens on write and on read, which is exactly what makes the API safer than Map<String, Object>.

Where You Will Use It

1. Per-view state

Every MenuView has a registry:

DataRegistry seed = DataRegistry.empty()
    .put(Keys.TARGET, target.getUniqueId());

lotus.openMenu(player, new ProfileMenu(), seed);

Later inside the menu:

UUID target = view.data().require(Keys.TARGET);

2. Per-button state

Every Button also has a registry:

Button entry = Button.clickable(icon, (view, event) -> {
    view.content().get(Slot.of(event.getSlot()))
        .flatMap(button -> button.data().get(Keys.TARGET))
        .ifPresent(this::inspectTarget);
});

entry.data().put(Keys.TARGET, targetUuid);

This is a great fit for paginated item lists.

Good Retrieval Patterns

Use get(...).orElse(...) for defaults:

int page = view.data().get(Keys.PAGE).orElse(0);

Use require(...) for invariants:

UUID target = view.data().require(Keys.TARGET);

Use mutation when the view state changes:

view.data().put(Keys.PAGE, view.data().require(Keys.PAGE) + 1);
view.refresh();

What Not To Do

  • Do not share one DataRegistry across several open views.
  • Do not use it for long-lived global plugin state.
  • Do not invent lots of throwaway key names inline; centralize them.

The Simple Heuristic

If the data should live only as long as the menu or the button lives, DataRegistry is a good fit.

Next: Pagination, where per-button data becomes especially useful.