Getting Started with GUIs - CottonMC/LibGui GitHub Wiki
Prepare the Block and BlockEntity for a GUI
If you prefer to watch a video tutorial, there is one available on YouTube for Minecraft 1.15: https://www.youtube.com/watch?v=Ca769FY4pOg
Required Interfaces:
- If your block has an inventory,
InventoryProvideron the Block and/or BlockEntity (More information on how to add an inventory in the fabric docs) - If you want numeric fields,
PropertyDelegateHolderon the Block and/or BlockEntity NamedScreenHandlerFactoryon the BlockEntity- An implementation of
Block.createScreenHandlerFactoryin your block class for accessing the block entity. Vanilla'sBlockWithEntityimplements this for you if you extend it.
- An implementation of
Later in the process we'll implement the onUse method on the Block.
Creating the container GUI
SyncedGuiDescription is the abstract superclass for shared gui state. Subclass this and add whatever widgets you'll need to your root panel:
public class ExampleGuiDescription extends SyncedGuiDescription {
private static final int INVENTORY_SIZE = 1;
public ExampleGuiDescription(int syncId, PlayerInventory playerInventory, ScreenHandlerContext context) {
super(MyMod.SCREEN_HANDLER_TYPE, syncId, playerInventory, getBlockInventory(context, INVENTORY_SIZE), getBlockPropertyDelegate(context));
WGridPanel root = new WGridPanel();
setRootPanel(root);
root.setSize(300, 200);
root.setInsets(Insets.ROOT_PANEL);
WItemSlot itemSlot = WItemSlot.of(blockInventory, 0);
root.add(itemSlot, 4, 1);
root.add(this.createPlayerInventoryPanel(), 0, 3);
root.validate(this);
}
}
The last line in your constructor should be validating the root panel. This finds the right size for your GUI and registers all the itemslots with vanilla so that they sync and hover properly.
public class ExampleBlockScreen extends CottonInventoryScreen<ExampleGuiDescription> {
public ExampleBlockScreen(ExampleGuiDescription gui, PlayerEntity player, Text title) {
super(gui, player, title);
}
}
Subclassing CottonInventoryScreen is optional but highly recommended. You could use the class as provided, but many GUI addons identify a gui by its Screen class, so creating a per-gui subclass helps sync up things like "recipes" buttons and helps other modders interact with your code.
Register the GUI
Somewhere in a "main" ModInitializer so that it runs on both client and server:
SCREEN_HANDLER_TYPE = Registry.register(Registries.SCREEN_HANDLER, ExampleBlock.ID,
new ScreenHandlerType<>((syncId, inventory) -> new ExampleGuiDescription(syncId, inventory, ScreenHandlerContext.EMPTY),
FeatureFlags.VANILLA_FEATURES));
Somewhere in a "client" ModInitializer so that it runs only on client startup:
HandledScreens.<ExampleGuiDescription, ExampleBlockScreen>register(MyMod.SCREEN_HANDLER_TYPE, (gui, inventory, title) -> new ExampleBlockScreen(gui, inventory.player, title));
Note that you in most cases have to have the manual generics specified for the HandledScreens.register call; the compiler has trouble with calling that method.
This is the same thing, but for the client. Again, the other side of this is coming right up. To configure the initializers, you will see an entrypoints object in the fabric.mod.json file. Make sure you have the entrypoints "main" and "client" configured to your wishes.
Implement NamedScreenHandlerFactory on your BlockEntity
Vanilla has an implementation in LootableContainerBlockEntity that most container block entities
should extend. Other block entities can do it themselves like this:
@Override
public Text getDisplayName() {
// Using the block name as the screen title
return Text.translatable(getCachedState().getBlock().getTranslationKey());
}
@Override
public ScreenHandler createMenu(int syncId, PlayerInventory inventory, PlayerEntity player) {
return new ExampleGuiDescription(syncId, inventory, ScreenHandlerContext.create(world, pos));
}
Implement the onUse method on your Block
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
// You need a Block.createScreenHandlerFactory implementation that delegates to the block entity,
// such as the one from BlockWithEntity
player.openHandledScreen(state.createScreenHandlerFactory(world, pos));
return ActionResult.SUCCESS;
}
Test your GUI
At this point, you should be up and running. Try it out!