-
Notifications
You must be signed in to change notification settings - Fork 0
3. Mod Developer Things
Want to integrate with CreateWorldUI? Here's what you need to know.
Want to know what APIs you can work with? Here's the lineup:
Package: decok.dfcdvadstf.createworldui.api.gamerule.GameRuleApplier
Handles applying game rules when a world loads.
| Method | What it does |
|---|---|
setPendingGameRules(Map<String, String>) |
Queue up game rules to apply when the next world is created. |
getPendingGameRules() |
Get the currently pending rules (may be null). |
Pending rules are applied via WorldEvent.Load and then cleared.
Package: decok.dfcdvadstf.createworldui.api.gamerule.GameRuleMonitorNSetter
For getting, setting, and adding game rules.
| Method | What it does |
|---|---|
getAllGamerules(World) |
Get all game rules from a world as Map<String, GameruleValue>. |
setGamerule(World, String, String) |
Set a game rule on a world. |
The GameruleValue inner class stores values in multiple types (String, boolean, int, double) and has getOptimalValue() to pick the best one.
Package: decok.dfcdvadstf.createworldui.api.gamerule.GameRuleTooltipRegistry
This is your go-to API for registering and managing game rule tooltips. We pulled it out from GuiScreenGameRuleEditor to keep things cleaner and easier to access.
| Method | What it does |
|---|---|
registerTooltip(String ruleName, String tooltip) |
Register a single tooltip for a game rule |
registerTooltips(Map<String, String> tooltips) |
Register multiple tooltips at once |
getTooltip(String ruleName) |
Get tooltip for a rule (follows priority: localization > registered > default) |
hasRegisteredTooltip(String ruleName) |
Check if a tooltip is already registered |
removeTooltip(String ruleName) |
Remove a registered tooltip |
clearAllTooltips() |
Clear all registered tooltips |
getRegisteredCount() |
Get the number of registered tooltips |
getAllRegisteredTooltips() |
Get a read-only copy of all registered tooltips |
How it works:
When you call getTooltip(), the system checks in this order:
-
Localization file —
gamerule.{ruleName}.tooltip.description - Registered tooltips — via this API
- Built-in defaults — only for vanilla rules
This means your mod's tooltip can be overridden by a resource pack, which is great for localization support!
Migration note: If you're using the old GuiScreenGameRuleEditor.registerTooltip(), don't worry — it still works (marked as @Deprecated), but we recommend switching to the new API when you have time.
Package: decok.dfcdvadstf.createworldui.api.gamerule.GameRuleNameRegistry
Need to set display names for game rules? This API's got you covered. Before 5.0.0, you were stuck with localization files only. Now you can do it through code too!
| Method | What it does |
|---|---|
registerName(String ruleName, String displayName) |
Register a display name for a game rule |
registerNames(Map<String, String> names) |
Register multiple display names at once |
getName(String ruleName) |
Get display name for a rule (follows priority: localization > registered > raw name) |
hasRegisteredName(String ruleName) |
Check if a display name is already registered |
removeName(String ruleName) |
Remove a registered display name |
clearAllNames() |
Clear all registered display names |
getRegisteredCount() |
Get the number of registered display names |
getAllRegisteredNames() |
Get a read-only copy of all registered display names |
How it works:
When you call getName(), the system checks in this order:
-
Localization file —
gamerule.{ruleName}.name - Registered names — via this API
-
Raw rule name — e.g.,
doFireTick
This way, resource packs can still override your display names for localization purposes!
Package: decok.dfcdvadstf.createworldui.api.gamerule.GameRuleCategoryRegistry
Looking to organize game rules into categories? Think of this API like folders for your rules — makes the editor way more organized!
| Method | What it does |
|---|---|
createCategory(String categoryKey, List<String> ruleNames) |
Create a new category with rules |
addRuleToCategory(String categoryKey, String ruleName) |
Add a single rule to a category |
addRulesToCategory(String categoryKey, List<String> ruleNames) |
Add multiple rules to a category |
getRulesInCategory(String categoryKey) |
Get all rules in a category (read-only list) |
getCategoryForRule(String ruleName) |
Get the category that a rule belongs to |
getAllCategories() |
Get all category keys (in registration order) |
getCategoryDisplayName(String categoryKey) |
Get localized display name for a category |
removeRuleFromCategory(String ruleName) |
Remove a rule from its category |
removeCategory(String categoryKey) |
Remove an entire category |
clearCustomCategories() |
Clear custom categories (keep defaults) |
clearAllCategories() |
Clear all categories (including defaults) |
getCategoryCount() |
Get the number of categories |
getAllCategoriesMap() |
Get complete category mapping (read-only) |
Default Categories: Vanilla rules come pre-categorized:
-
World— doFireTick, doTileDrops, doDaylightCycle -
Mobs— doMobSpawning, doMobLoot, mobGriefing -
Player— naturalRegeneration, keepInventory -
Chat— commandBlockOutput
Localizing category names:
gamerule.category.myMod=My Mod RulesAnd then the category will display with that name in the editor!
Package: decok.dfcdvadstf.createworldui.api.DifficultyApplier
Applies difficulty settings when a world is created. Works with the tab system to set difficulty on the IntegratedServer via Mixin.
And here's the thing — vanilla GuiCreateWorld has no difficulty field at all, so the mod caches it locally and syncs it to mc.gameSettings.difficulty on every write. DifficultyApplier then queues it up as a "pending" value, and MixinIntegratedServer consumes it when the world finishes loading.
| Method | What it does |
|---|---|
setPendingDifficulty(EnumDifficulty) |
Queue a difficulty to apply on the next world load. |
consumePendingDifficulty() |
Get and clear the pending difficulty (consume pattern). |
hasPendingDifficulty() |
Check if there's a pending difficulty waiting. |
Notes: The
ruleNamein it
Package: decok.dfcdvadstf.createworldui.api.GuiCyclableButton
A button that cycles through values — click it or scroll your mouse wheel over it to switch. Left-click or scroll down goes forward (+1), scroll up goes backward (−1). Used for game mode, difficulty, and world type in the built-in tabs, and you can use it in your own tabs too.
It takes two callbacks:
-
TextSupplier— agetText()method that returns the current display string. Called every time the button updates. -
CycleHandler— anonCycle(int direction)method that gets called when the user clicks or scrolls.directionis+1or−1.
// A button that cycles through "Easy / Normal / Hard"
GuiCyclableButton diffButton = new GuiCyclableButton(
200, // button ID
width / 2 - 104, // x
height / 2, // y
208, 20, // width, height
this::getDifficultyText, // TextSupplier — returns current text
direction -> cycleDifficulty(direction) // CycleHandler — direction is +1 or -1
);
addButton(diffButton);
// Your TextSupplier
private String getDifficultyText() {
return "Difficulty: " + difficulties[currentIndex];
}
// Your CycleHandler
private void cycleDifficulty(int direction) {
currentIndex += direction;
if (currentIndex >= difficulties.length) currentIndex = 0;
if (currentIndex < 0) currentIndex = difficulties.length - 1;
}Tip: Call
diffButton.updateText()in yourdrawScreen()to keep the display text in sync — especially if the value can change from somewhere else (like hardcore mode locking difficulty to Hard).
Package: decok.dfcdvadstf.createworldui.mixin.access.IGuiCreateWorldAccess
Vanilla GuiCreateWorld hides everything behind private — so this is an Accessor mixin interface that auto-generates getters/setters at runtime. External mods (or our own non-mixin code) can just cast the screen instance to IGuiCreateWorldAccess and read/write state directly — no need to write another mixin or reflect into private fields.
Here's where it gets interesting — this interface lives under the mixin.* package (not api/) because Mixin's package config "seals" that package tree. Classes inside become mixin templates. But don't worry — external code can still import and cast it just fine. The sealing only blocks Class.forName-style direct loads from outside the mixin pipeline.
| Accessor method | Vanilla field | Type |
|---|---|---|
createWorldUI$getWorldName() / setWorldName(String)
|
field_146330_J |
String |
createWorldUI$getGameMode() / setGameMode(String)
|
field_146342_r |
String |
createWorldUI$getSeed() / setSeed(String)
|
field_146329_I |
String |
createWorldUI$getWorldTypeIndex() / setWorldTypeIndex(int)
|
field_146331_K |
int |
createWorldUI$getGenerateStructures() / setGenerateStructures(boolean)
|
field_146341_s |
boolean |
createWorldUI$getBonusChest() / setBonusChest(boolean)
|
field_146338_v |
boolean |
createWorldUI$getAllowCheats() / setAllowCheats(boolean)
|
field_146340_t |
boolean |
createWorldUI$getHardcore() / setHardcore(boolean)
|
field_146337_w |
boolean |
createWorldUI$getParentScreen() |
field_146332_f |
GuiScreen (read-only) |
GuiCreateWorld gui = ...;
IGuiCreateWorldAccess access = (IGuiCreateWorldAccess)(Object) gui;
String name = access.createWorldUI$getWorldName();
access.createWorldUI$setWorldName("My Shiny New World");Note: All accessor methods carry the
createWorldUI$prefix to avoid name clashes with other mods that might define their own accessor interfaces.
Package: decok.dfcdvadstf.createworldui.api.ContentPanelRenderer
A shared renderer for the content panel — gives you the header separator, a tiled panel background, and the footer separator. Use the pieces you want individually, or let drawContentPanel() do the whole thing in one shot.(After 4.2.0)
Pretty straightforward, really — the GameRule Editor and other screens all draw these panels the same way, so this thing packages it all up so you don't have to copy-paste rendering code into your own screen.
| Method | What it draws |
|---|---|
drawContentPanel(x, top, width, bottom) |
The whole thing — header line, tiled background, footer line. |
drawHeaderSeparator(x, y, width) |
Just the top separator line (2px tall, tiled). |
drawFooterSeparator(x, y, width) |
Just the bottom separator line (2px tall, tiled). |
drawSeparator(x, y, width, texture) |
A separator line with your own 32×2 texture. |
drawPanelBackground(x, y, width, height) |
Just the panel background (tiles the 16×16 texture). |
| Field | Path | Size |
|---|---|---|
HEADER_SEPARATOR |
createworldui:textures/gui/header_separator.png |
32×2 |
FOOTER_SEPARATOR |
createworldui:textures/gui/footer_separator.png |
32×2 |
PANEL_BACKGROUND |
createworldui:textures/gui/panel_background.png |
16×16 |
You can override these in your resource pack — or pass a custom ResourceLocation to drawSeparator() if you want a different style.
// Draw a full content panel in one call
ContentPanelRenderer.drawContentPanel(panelX, panelTop, panelWidth, panelBottom);
// Or draw pieces separately
ContentPanelRenderer.drawHeaderSeparator(panelX, panelTop, panelWidth);
ContentPanelRenderer.drawPanelBackground(panelX, panelTop + 2, panelWidth, panelHeight - 4);
ContentPanelRenderer.drawFooterSeparator(panelX, panelBottom - 2, panelWidth);Tip: The separator height is always 2 GUI pixels (
SEPARATOR_HEIGHT = 2). When usingdrawContentPanel(), the background is automatically placed between the two separators.
Package: decok.dfcdvadstf.createworldui.api.tab.TabRegistry
The static registry for external mods to register custom tabs. See Adding a Custom Tab for details and examples.
The world creation screen uses a tab system. Each tab implements AbstractScreenTab.
| Class | Tab ID | What it does |
|---|---|---|
GameTab |
100 | World name, game mode, difficulty, cheats |
WorldTab |
101 | Seed, world type, structures, bonus chest |
MoreTab |
102 | GameRule Editor, experiments, data packs |
Package: decok.dfcdvadstf.createworldui.api.tab.TabManager
TabManager handles tab switching, state persistence (survives window resizes), and gives you accessors for the shared state. After 5.0.0, all 8 vanilla-field accessors proxy straight to GuiCreateWorld through IGuiCreateWorldAccess — no local cache, no bridge, no drift. The only exception is difficulty — vanilla GuiCreateWorld has no difficulty field, so it's cached locally and synced to mc.gameSettings.difficulty on write.
| Method | What it does |
|---|---|
getWorldName() / setWorldName(String)
|
Get or set the world name. Proxies to vanilla via accessor. |
getGameMode() / setGameMode(String)
|
Get or set the game mode. Proxies to vanilla via accessor. |
getDifficulty() / setDifficulty(EnumDifficulty)
|
Get or set the difficulty. Cached locally — synced to mc.gameSettings. |
getHardcore() / setHardcore(boolean)
|
Get or set hardcore mode. Proxies to vanilla via accessor. |
getAllowCheats() / setAllowCheats(boolean)
|
Get or set allow cheats. Proxies to vanilla via accessor. |
getSeed() / setSeed(String)
|
Get or set the world seed. Proxies to vanilla via accessor. |
getWorldTypeIndex() / setWorldTypeIndex(int)
|
Get or set the world type. Proxies to vanilla via accessor. |
getGenerateStructures() / setGenerateStructures(boolean)
|
Get or set generate structures. Proxies to vanilla via accessor. |
getBonusChest() / setBonusChest(boolean)
|
Get or set bonus chest. Proxies to vanilla via accessor. |
getWorldNameForCreation() |
Returns the actual name used for world creation — falls back to default if blank (depends on config). |
Package: decok.dfcdvadstf.createworldui.api.tab.TabState
An enum that defines texture coordinates and text colors for the four tab interaction states — NORMAL, HOVER, SELECTED, SELECTED_HOVER. Used by the tab button renderer to pick the right texture slice and color.
And there's a little twist — when ArchaicFix is loaded and topTabCharatorModernWhite config is on, the highlight states use white instead of yellow. Just so the tab text matches the rest of the modern UI look.
You can register custom tabs into the world creation screen using the TabRegistry API.
Package: decok.dfcdvadstf.createworldui.api.tab.TabRegistry
It's a static registry — register your tabs during mod init, and when the world creation screen opens, TabManager freezes the registry and creates all the tabs. Simple as that.
| Method | What it does |
|---|---|
registerTab(Supplier<Tab>, int, String, int) |
Register a tab with factory, ID, name key, and priority. |
registerTab(Supplier<Tab>, int, String) |
Same thing, but priority defaults to tabId. |
getEntries() |
Get all entries sorted by priority (unmodifiable). |
isFrozen() |
Check if the registry is frozen (GUI already initialized). |
freeze() |
Freeze the registry. Called internally by TabManager. |
| Parameter | Type | What it means |
|---|---|---|
factory |
Supplier<Tab> |
A factory that creates your Tab instance. Called once when TabManager is constructed. |
tabId |
int |
Unique tab ID. Built-in tabs use 100–102, so start from 103. |
nameKey |
String |
Localization key for the tab name (like "mymod.tab.custom"). |
priority |
int |
Sort order — lower comes first. Defaults to tabId if not specified. Built-in tabs use 0, 1, 2. |
Entries are sorted by priority ascending, then tabId ascending as a tiebreaker.
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import decok.dfcdvadstf.createworldui.api.tab.AbstractScreenTab;
import decok.dfcdvadstf.createworldui.api.tab.Tab;
import decok.dfcdvadstf.createworldui.api.tab.TabManager;
import decok.dfcdvadstf.createworldui.api.tab.TabRegistry;
import net.minecraft.client.gui.GuiButton;
@Mod(modid = "mymod", name = "My Mod", version = "1.0")
public class MyMod {
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
TabRegistry.registerTab(
MyCustomTab::new, // Supplier<Tab> factory
103, // Unique tab ID (start from 103)
"mymod.tab.custom", // Localization key
5 // Sort priority (lower = earlier)
);
}
}
// Your custom tab
public class MyCustomTab extends AbstractScreenTab {
public MyCustomTab() {
super(103, "mymod.tab.custom"); // Must match what you registered
}
@Override
public void initGui(TabManager tabManager, int width, int height) {
super.initGui(tabManager, width, height);
// Add your buttons and fields here
setVisible(false);
}
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
if (!visible) return;
// Draw your tab content here
}
@Override
public void actionPerformed(GuiButton button) {
// Handle button clicks here
}
@Override
public void mouseClicked(int mouseX, int mouseY, int mouseButton) {
// Handle mouse clicks here
}
@Override
public void keyTyped(char typedChar, int keyCode) {
// Handle key input here
}
}And don't forget the localization in your lang file:
mymod.tab.custom=My Tab-
Register early — like
FMLPreInitializationEvent. OnceTabManageris constructed the registry freezes, and registering after that throws anIllegalStateException. -
Tab IDs must be unique — duplicate IDs throw an
IllegalArgumentException. -
AbstractScreenTabconstructor must match — thetabIdandnameKeyyou pass there need to be the same ones you gaveTabRegistry.registerTab(). -
initGui()gets called byTabManager— on init and on resize. Always callsuper.initGui()and setsetVisible(false)at the end. -
Use
tabManageraccessors (likegetWorldName(),setGameMode()) to read/write the shared world creation state.
Want to add custom game rules to the editor? Pretty straightforward — just add the game rules through the GameRule class like you used to do, name your Rule name then set a default boolean or number value. And just like that, they pop up on the page with their names and default values ready to go.
But here's the catch — what if you want tooltip descriptions for your rules? Or customize how vanilla rule names appear? That's where things get interesting, and we've got a couple of approaches for you.
The modpack usage is showing how to use it. Just add entries like this:
gamerule.yourRuleName.tooltip.description=Your description hereThat's it.
It can only be achieved by the hard code, and this doesn't like the simple way if you cannot handle it carefully it cannot show the localized name. so it's not welcomed to the only using simple ways.
Note for 5.0.0+: This method is now deprecated. Please use the new GameRuleTooltipRegistry API (see API Overview section below). But if you're on older versions, here's how it works:
Codes are belowed:
package idk.unknown.which;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import decok.dfcdvadstf.createworldui.gamerule.GuiScreenGameRuleEditor;
@Mod(modid = "idk", name = "idk", version = "idk")
public class idk {
@Mod.EventHandler
public void init(FMLInitializationEvent event){
// There are two ways to register this
// registerTooltip() can add one single tooltip
// registerTooltips() can add a Map, which contains many rules and tooltips, to the tooltip list.
GuiScreenGameRuleEditor.registerTooltip("randomTickSpeed", "Controls how fast random ticks occur");
GuiScreenGameRuleEditor.registerTooltip("myCustomRule", "This is a rule added by another mod"); // Former is ruleName, latter is tooltips.
Map<String,String> myCustomTooltips = new HashMap<>();
myCustomTooltips.put("myCustomRule2","This is a rule added by another mod");
myCustomTooltips.put("myCustomRule3","This is a rule added by another mod");
myCustomTooltips.put("myCustomRule4","This is a rule added by another mod");
// myCustomTooltips.put(........);
GuiScreenGameRuleEditor.registerTooltips(myCustomTooltips);
}
}Here's the deal — starting from 5.0.0, we pulled the tooltip registration out into its own API class. So much cleaner!
Package: decok.dfcdvadstf.createworldui.api.gamerule.GameRuleTooltipRegistry
package idk.unknown.which;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import decok.dfcdvadstf.createworldui.api.gamerule.GameRuleTooltipRegistry;
@Mod(modid = "idk", name = "idk", version = "idk")
public class idk {
@Mod.EventHandler
public void init(FMLInitializationEvent event){
// Method 1: Register a single tooltip
GameRuleTooltipRegistry.registerTooltip("randomTickSpeed", "Controls how fast random ticks occur");
GameRuleTooltipRegistry.registerTooltip("myCustomRule", "This is a rule added by another mod");
// Method 2: Register multiple tooltips at once
Map<String, String> myCustomTooltips = new HashMap<>();
myCustomTooltips.put("myCustomRule2", "This is a rule added by another mod");
myCustomTooltips.put("myCustomRule3", "This is a rule added by another mod");
GameRuleTooltipRegistry.registerTooltips(myCustomTooltips);
}
}Why the new API is better:
- ✅ Separate from GUI class — cleaner architecture
- ✅ More methods:
removeTooltip(),clearAllTooltips(),hasRegisteredTooltip(), etc. - ✅ Better organized and easier to maintain
- ✅ Still backward compatible!
Same idea as tooltips — just add this to your lang file:
gamerule.yourRuleName.name=Your Display NameIf no name localization is provided, the raw rule name is shown instead.
Guess what — you can register display names through code now too! Follows the exact same pattern as the tooltip API:
Package: decok.dfcdvadstf.createworldui.api.gamerule.GameRuleNameRegistry
import decok.dfcdvadstf.createworldui.api.gamerule.GameRuleNameRegistry;
// Register a single display name
GameRuleNameRegistry.registerName("myCustomRule", "My Custom Rule");
// Or register multiple at once
Map<String, String> names = new HashMap<>();
names.put("customRule1", "Custom Rule One");
names.put("customRule2", "Custom Rule Two");
GameRuleNameRegistry.registerNames(names);Priority: Just like tooltips, it checks localization file first, then registered names, then falls back to raw rule name.
Got a ton of custom game rules and wondering how to keep them organized? You're in luck — we built a category system just for that!
Out of the box, vanilla game rules are already categorized:
- 世界 (World): doFireTick, doTileDrops, doDaylightCycle
- 生物 (Mobs): doMobSpawning, doMobLoot, mobGriefing
- 玩家 (Player): naturalRegeneration, keepInventory
- 聊天 (Chat): commandBlockOutput
In the GameRule Editor, categories are displayed like this:
|--------------|
| 生物 | ← Category title (centered, localizable)
| ..... [] | ← Game rule controls
| ..... [] |
|--------------|
The category title is centered and always visible (not collapsible). Below it are all the game rule controls for that category.
Package: decok.dfcdvadstf.createworldui.api.gamerule.GameRuleCategoryRegistry
import decok.dfcdvadstf.createworldui.api.gamerule.GameRuleCategoryRegistry;
import java.util.Arrays;
// Method 1: Create a new category with rules
GameRuleCategoryRegistry.createCategory(
"gamerule.category.myMod", // Category key (will be localized)
Arrays.asList("myCustomRule1", "myCustomRule2", "myCustomRule3")
);
// Method 2: Add rules to an existing category
GameRuleCategoryRegistry.addRuleToCategory("gamerule.category.myMod", "anotherRule");
// Method 3: Add multiple rules at once
GameRuleCategoryRegistry.addRulesToCategory(
"gamerule.category.myMod",
Arrays.asList("rule1", "rule2", "rule3")
);Just add this to your lang file:
gamerule.category.world=世界
gamerule.category.mobs=生物
gamerule.category.player=玩家
gamerule.category.chat=聊天
gamerule.category.myMod=My Mod RulesSo there you have it — your categories show up with nice localized names!
Class: decok.dfcdvadstf.createworldui.command.CommandGameRuleEditor
Opens the GameRule Editor in-game. You need either:
-
igGameruleEditconfig option enabled, or - ModernDifficultyLocker installed.
Requires permission level 2 (same as /gamerule).
Want to see how it all works together? Here are some practical examples.
import decok.dfcdvadstf.createworldui.api.gamerule.GameRuleTooltipRegistry;
// Call this during your mod initialization
GameRuleTooltipRegistry.registerTooltip(
"myCustomRule",
"This is my custom game rule description"
);import decok.dfcdvadstf.createworldui.api.gamerule.GameRuleTooltipRegistry;
import java.util.HashMap;
import java.util.Map;
// Create a tooltip map
Map<String, String> tooltips = new HashMap<>();
tooltips.put("customRule1", "Description for custom rule 1");
tooltips.put("customRule2", "Description for custom rule 2");
tooltips.put("customRule3", "Description for custom rule 3");
// Register all at once
GameRuleTooltipRegistry.registerTooltips(tooltips);import decok.dfcdvadstf.createworldui.api.gamerule.GameRuleNameRegistry;
// Register a single display name
GameRuleNameRegistry.registerName("myCustomRule", "My Custom Rule");
// Or register multiple at once
Map<String, String> names = new HashMap<>();
names.put("customRule1", "Custom Rule One");
names.put("customRule2", "Custom Rule Two");
GameRuleNameRegistry.registerNames(names);import decok.dfcdvadstf.createworldui.api.gamerule.GameRuleCategoryRegistry;
import java.util.Arrays;
// Create a new category with rules
GameRuleCategoryRegistry.createCategory(
"gamerule.category.myMod", // Category key
Arrays.asList("myCustomRule1", "myCustomRule2", "myCustomRule3")
);
// Add more rules later
GameRuleCategoryRegistry.addRuleToCategory("gamerule.category.myMod", "anotherRule");Here's how you'd do it all together — names, tooltips, and categories:
package com.example.mymod;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import decok.dfcdvadstf.createworldui.api.gamerule.GameRuleTooltipRegistry;
import decok.dfcdvadstf.createworldui.api.gamerule.GameRuleNameRegistry;
import decok.dfcdvadstf.createworldui.api.gamerule.GameRuleCategoryRegistry;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@Mod(modid = "mymod", name = "My Mod", version = "1.0")
public class MyMod {
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
// 1. Register display names
Map<String, String> names = new HashMap<>();
names.put("enableCustomFeature", "Enable Custom Feature");
names.put("customDamageMultiplier", "Custom Damage Multiplier");
names.put("allowCustomSpawning", "Allow Custom Spawning");
GameRuleNameRegistry.registerNames(names);
// 2. Register tooltip descriptions
Map<String, String> tooltips = new HashMap<>();
tooltips.put("enableCustomFeature", "Enable or disable the custom feature");
tooltips.put("customDamageMultiplier", "Set the damage multiplier for custom entities (0.0 - 10.0)");
tooltips.put("allowCustomSpawning", "Allow custom entities to spawn naturally in the world");
GameRuleTooltipRegistry.registerTooltips(tooltips);
// 3. Create category and organize rules
GameRuleCategoryRegistry.createCategory(
"gamerule.category.mymod",
Arrays.asList("enableCustomFeature", "customDamageMultiplier", "allowCustomSpawning")
);
}
}import decok.dfcdvadstf.createworldui.api.gamerule.GameRuleTooltipRegistry;
import decok.dfcdvadstf.createworldui.api.gamerule.GameRuleNameRegistry;
import decok.dfcdvadstf.createworldui.api.gamerule.GameRuleCategoryRegistry;
// Check if tooltip is already registered
if (!GameRuleTooltipRegistry.hasRegisteredTooltip("myRule")) {
GameRuleTooltipRegistry.registerTooltip("myRule", "My description");
}
// Check if display name is already registered
if (!GameRuleNameRegistry.hasRegisteredName("myRule")) {
GameRuleNameRegistry.registerName("myRule", "My Rule");
}
// Get tooltip (automatically handles priority)
String tooltip = GameRuleTooltipRegistry.getTooltip("myRule");
// Get display name (automatically handles priority)
String displayName = GameRuleNameRegistry.getName("myRule");
// Get all rules in a category
List<String> rules = GameRuleCategoryRegistry.getRulesInCategory("gamerule.category.mymod");
// Get category for a rule
String category = GameRuleCategoryRegistry.getCategoryForRule("myCustomRule");
// Remove registrations
GameRuleTooltipRegistry.removeTooltip("myRule");
GameRuleNameRegistry.removeName("myRule");
// Clear all registrations
GameRuleTooltipRegistry.clearAllTooltips();
GameRuleNameRegistry.clearAllNames();When you call getTooltip() or getName(), the system checks in this order:
Tooltip Priority:
-
Localization file -
gamerule.{ruleName}.tooltip.description - Tooltips registered via API
- Built-in default descriptions (vanilla rules only)
Name Priority:
-
Localization file -
gamerule.{ruleName}.name - Display names registered via API
-
Raw rule name (e.g.,
doFireTick)
This means resource packs can override your API-registered content, perfect for multi-language support!
For example:
- You register the English name "My Custom Rule" via API
- A Chinese resource pack can override it with
gamerule.myCustomRule.name=我的自定义规则 - Pretty neat, right? Chinese players see the localized name!
Vanilla game rules are pre-categorized as follows:
UI Display:
Each category is displayed as a section with a centered title:
|-------------------|
| Mobs | ← Category title (centered, localizable)
| doMobSpawning [] | ← Game rule controls
| doMobLoot [] |
| mobGriefing [] |
|-------------------|
Note: Categories are not collapsible — they're always expanded and visible.
World
├─ doFireTick
├─ doTileDrops
└─ doDaylightCycle
Mobs
├─ doMobSpawning
├─ doMobLoot
└─ mobGriefing
Player
├─ naturalRegeneration
└─ keepInventory
Chat
└─ commandBlockOutput
- ✅ Better organization - Categories make the UI clearer when you have many rules
- ✅ User experience - Players can quickly find related rules
- ✅ Extensibility - Each mod can have its own category
- ✅ Backward compatible - Old code still works!
So there you go — your game rules are all neatly organized in the editor!
Here's what gets mixed into what:
| Mixin Class | Target | What it does |
|---|---|---|
MixinModernCreateWorld |
GuiCreateWorld |
Replaces the world creation screen with the tabbed UI. |
MixinGuiSelectWorld |
GuiSelectWorld |
Enhances the world selection screen. |
MixinIntegratedServer |
IntegratedServer |
Applies difficulty and game rules when the world loads. |
access.IGuiCreateWorldAccess |
GuiCreateWorld |
Accessor mixin — exposes private fields via getters/setters for external use. |
To use CreateWorldUI as a dependency, add this to your build.gradle:
dependencies {
implementation 'com.github.song682:CreateWorldUI:Tag'
}Replace Tag with the version you want from the repository.
Also, the -deobf suffix jar is available.