-
Notifications
You must be signed in to change notification settings - Fork 30
State Management
States are a data management feature in InventoryFramework used to store and access context-specific data In practice, a state functions as a type-safe key-value store that exists for the duration of a context’s lifecycle.
States are bound to the context lifecycle, providing scoped, type-safe storage for dynamic or viewer-specific data. They make it easier and safer to manage information that changes per viewer or component.
There are three main state scopes:
- Global State — shared across all viewers of a context.
- Viewer State — isolated per viewer, enabling personalized data.
- Component State — scoped to a component, allowing customization of item type, name, lore, etc.
Each state is identified by an explicit key or an implicit key (automatically generated), and its value can be of any type. The system enforces type safety, allowing values to be stored and retrieved without manual casting.
States are automatically managed alongside the context lifecycle:
- A new state map is created when a context inventory is opened.
- Global and viewer-specific data persist while the inventory remains open.
- When the inventory is closed for all viewers, all related state data is automatically cleared.
This design guarantees clean, isolated data handling for each context instance.
Let's imagine a usage scenario
For a counter, we will have an "increment" button, a "decrement" button and an item that will represent the current value of the counter. The counter value is an integer so we have mutableIntState(initialValue) with a initial value of 0 for that.
1 class CounterView extends View {
2 private final MutableIntState counterState = mutableIntState(0);
3
4 }States are defined during the initialization of your view so put them at the top-level of the class or in the constructor.
Now we are going to create the "increase" and "decrease" button, here there is no secret, just as it is explained in the Basic Usage guide. They will be both arrow.
1 class CounterView extends View {
2 private final MutableIntState counterState = mutableIntState(0);
3
4 @Override
5 public void onFirstRender(RenderContext render) {
6 // Decrement button
7 render.slot(2, new ItemStack(Material.ARROW))
8 .cancelOnClick()
9 .onClick(counterState::increment);
10
11 // Increment button
12 render.slot(6, new ItemStack(Material.ARROW))
13 .cancelOnClick()
14 .onClick(counterState::decrement);
15 }
16 }States are used to store data for a context.
MutableState<String> textState = mutableState("");
textState.get(context); // ""
textState.set("abc", context);
textState.get(context); // "abc"Real-world example
A counter whose value increases each time the player clicks on the item.
-
watchWill trigger a update (re-render) on item every timecounterStatevalue changes. -
renderedDynamic renderization so the item is always up to date with the new state value. -
counterState::incrementIncrements state value by 1 everyone the player clicks on it
private final MutableIntState counterState = mutableState(0);
@Override
public void onFirstRender(RenderContext render) {
render.firstSlot()
.watch(counterState)
.renderWith(() -> new ItemStack(
/* type = */ Material.DIAMOND,
/* amount = */ counterState.get(render)
))
.onClick(counterState::increment);
}A computed state, is a state whose value that is returned when there is an attempt to obtain the value of that state is computed from its initial value.
The declaration is similar to the mutable state one except it asks for a Supplier instead of a static initial value.
State<Integer> randomNumberState = computedState(ThreadLocalRandom.current()::nextInt);
randomNumberState.get(host); // some random number
randomNumberState.get(host); // another random numberReal-world example
An item that sends a random number to the player with each click.
As it is a computed state each time the value is obtained a new value will be computed based on the factory provided when creating the state, in our case ThreadLocalRandom.current()::nextInt.
private final State<Integer> randomNumberState =
computedState(() -> ThreadLocalRandom.current().nextInt(1, 64));
@Override
public void onFirstRender(RenderContext render) {
render.firstSlot()
.onRender(slotRender -> slotRender(new ItemStack(
/* type */ Material.DIAMOND,
/* amount */ randomNumberState.get(slotRender)
))
.onClick(IFContext::updateSlot);
}A lazy state is one whose value is only set the first time some object tries to get the value of that state.
In short, you define what the value will be, try to get the value, and the value obtained from there will be the value that will be obtained in subsequent calls to get the value of the state.
State<Integer> intState = lazyState(ThreadLocalRandom.current()::nextInt);
randomIntState.get(...); // 54 - from initial computation of random integer ^^
randomIntState.get(...); // 54 - previously defined by the initial computationThe initial state is a immutable lazy state whose value is defined during the creation of the object that holds it.
For example: if the object that holds it is a ViewContext, the initial value of that state will be the initial data defined during the creation of the context, that is, when the view that the context originated from is opened for a player.
<T> State<T> initialState(String! key);Example of using the initial state to store the id of an object initially defined when opening a context container
State<String> someId = initialState("some-id");How someId is defined: before context creation, on open.
class MyAwesomeView extends View {
private static final String SOME_ID = "some-id";
final State<String> someId = initialState(SOME_ID);
}Set it on open through initial data map.
viewFrame.open(
MyAwesomeView.class,
player,
ImmutableMap.of(SOME_ID, "github")
);Welcome to the Inventory Framework documentation.
- Pagination — Display large collections of items across multiple pages.
- Layouts (a.k.a. Masks or Patterns) — Define visual patterns for item placement.
- Scheduled Updates
- Anvil Input — Capture text input from players using an anvil GUI.
- Dynamic Title Update — Update the inventory’s title in real time.
You can find practical examples in the examples directory.