This document contains important information about porting the ChatCalc Fabric mod to NeoForge for Minecraft 1.21.10.
- Source Mod: ChatCalc (Fabric) for Minecraft 1.20.2
- Target Platform: NeoForge 21.10.49-beta for Minecraft 1.21.10
- Java Version: 21
- Gradle Version: 9.2.0
- Parchment Mappings: 2025.10.12
Problem: The original Fabric mod used keyPressed(III)Z signature.
Solution: In Minecraft 1.21.10, the signature changed to keyPressed(KeyEvent).
Fabric 1.20.2:
@Inject(method = "keyPressed", at = @At("HEAD"))
public boolean keyPressed(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable<Boolean> cir) {
if (keyCode == GLFW.GLFW_KEY_TAB) {
// ...
}
}NeoForge 1.21.10:
@Inject(method = "keyPressed", at = @At("HEAD"))
public void keyPressed(KeyEvent event, CallbackInfoReturnable<Boolean> cir) {
if (event.key() == 258) { // TAB key code
// ...
}
}How to Research: Always check decompiled sources in Gradle cache before guessing:
C:\Users\<USER>\.gradle\caches\ng_execute\<hash>\transformed\net\minecraft\client\gui\screens\ChatScreen.java
Problem: sendSystemMessage method no longer exists.
Solution: Use displayClientMessage with boolean parameter.
Fabric 1.20.2:
client.player.sendSystemMessage(Component.literal("message"));NeoForge 1.21.10:
client.player.displayClientMessage(Component.literal("message"), false);Problem: In Fabric, these were abstract classes with constructors. In 1.21.10, they are interfaces with record implementations.
Fabric 1.20.2:
new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, "text")
new HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal("tooltip"))NeoForge 1.21.10:
new ClickEvent.CopyToClipboard("text")
new HoverEvent.ShowText(Component.literal("tooltip"))Available ClickEvent Types:
ClickEvent.OpenUrl(String url)ClickEvent.RunCommand(String command)ClickEvent.SuggestCommand(String command)ClickEvent.CopyToClipboard(String value)ClickEvent.ChangePage(int page)
Available HoverEvent Types:
HoverEvent.ShowText(Component value)HoverEvent.ShowItem(ItemStack item)HoverEvent.ShowEntity(EntityType<?>, UUID, Component)
Problem: Fabric used DrawContext.drawTooltip(), which doesn't exist in NeoForge.
Solution: Use GuiGraphics.setTooltipForNextFrame().
Fabric 1.20.2:
context.drawTooltip(textRenderer, text, x - 8, y - 4);NeoForge 1.21.10:
context.setTooltipForNextFrame(font, component, x - 8, y - 4);Note: Tooltips are rendered on the next frame, not immediately.
Problem: Method names changed from renderButton to renderWidget.
Solution: Update Mixin injection point.
Fabric 1.20.2:
@Inject(method = "renderButton", at = @At("TAIL"))
private void renderButton(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
// ...
}NeoForge 1.21.10:
@Inject(method = "renderWidget", at = @At("TAIL"))
private void renderWidget(GuiGraphics context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
// ...
}Fabric 1.20.2:
MutableComponent line = Component.literal(text);
line.setStyle(line.getStyle()
.withClickEvent(clickEvent)
.withHoverEvent(hoverEvent));NeoForge 1.21.10:
MutableComponent line = Component.literal(text);
line.withStyle(style -> style
.withClickEvent(clickEvent)
.withHoverEvent(hoverEvent));ca.rttv.chatcalc/
├── ChatCalc.java
├── ChatHelper.java
├── Config.java
├── mixin/
│ ├── ChatInputSuggesterMixin.java
│ ├── ChatScreenMixin.java
│ └── TextFieldWidgetMixin.java
└── duck/
└── ChatInputSuggesterDuck.java
de.smallinger.chatcal/
├── ChatCal.java
├── ChatCalClient.java
├── ChatCalculator.java
├── ChatHelper.java
├── Config.java
├── mixin/
│ ├── ChatScreenMixin.java
│ └── EditBoxMixin.java
└── (all math-related classes)
{
"required": true,
"minVersion": "0.8",
"package": "de.smallinger.chatcal.mixin",
"compatibilityLevel": "JAVA_21",
"mixins": [
"ChatScreenMixin",
"EditBoxMixin"
],
"client": [],
"injectors": {
"defaultRequire": 1
}
}Important Notes:
"mixins"array is for common mixins"client"array is for client-only mixins- Set
compatibilityLeveltoJAVA_21for Java 21 - Both mixins are technically client-side but work in the common array
# Minecraft classes are cached here after first run:
C:\Users\<USER>\.gradle\caches\ng_execute\<hash>\transformed\
# Common locations:
net\minecraft\client\gui\screens\ChatScreen.java
net\minecraft\client\gui\components\EditBox.java
net\minecraft\network\chat\ClickEvent.java
net\minecraft\network\chat\HoverEvent.java
net\minecraft\client\gui\GuiGraphics.javaCheck the console output for:
[Render thread/DEBUG] [mixin/]: Mixing ChatScreenMixin from chatcal.mixins.json into net.minecraft.client.gui.screens.ChatScreen
[Render thread/DEBUG] [mixin/]: Mixing EditBoxMixin from chatcal.mixins.json into net.minecraft.client.gui.components.EditBox
-
Target method not found:
- Method signature changed
- Method was removed
- Method was renamed
- Solution: Search in decompiled sources
-
Injection point not found:
- Target instruction doesn't exist
- Bytecode changed
- Solution: Use
@At("TAIL")or@At("HEAD")for simpler injections
-
ClassCastException:
- Type mismatch in cast
- Solution: Check actual class hierarchy in decompiled sources
minecraft {
accessTransformers {
file('src/main/resources/META-INF/accesstransformer.cfg')
}
}
dependencies {
implementation "net.neoforged:neoforge:21.10.49-beta"
}
minecraft {
runs {
client {
workingDirectory project.file('run')
property 'forge.logging.console.level', 'debug'
property 'mixin.env.remapRefMap', 'true'
property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg"
mods {
chatcal {
source sourceSets.main
}
}
}
}
}- TAB key triggers calculation in chat
- Mathematical expressions evaluate correctly
- Custom functions can be defined and used
- Custom constants can be defined and used
-
functions?command lists functions with click-to-copy -
constants?command lists constants with click-to-copy - Hover tooltips show on function/constant listings
- Live preview shows above chat input
- Config file is created and loaded correctly
- No console errors during startup
- Mixins inject successfully
NEVER guess API changes. Always research first:
-
Check decompiled sources:
Get-ChildItem -Path "$env:USERPROFILE\.gradle\caches" -Recurse -Filter "ClassName.java"
-
Search for method signatures:
Select-String -Path "path\to\Class.java" -Pattern "methodName"
-
Read method bodies:
Get-Content "path\to\Class.java" | Select-String -Pattern "methodName" -Context 10
-
Test compilation after each change:
./gradlew build
-
Test in-game to verify functionality:
./gradlew runClient
-
Tooltips are not clickable: Standard Minecraft tooltips don't support click events. The tooltip uses
setTooltipForNextFramewhich doesn't process clicks. -
Chat message character limit: Chat messages are limited to 256 characters, so complex expressions may fail.
-
Development environment detection: The original Fabric mod had
FabricLoader.getInstance().isDevelopmentEnvironment(), which doesn't have a direct NeoForge equivalent. Removed timing display for now.
- Evaluation Cache: The EditBoxMixin uses a cache (
chatcal$evaluationCache) to avoid re-evaluating the same expression multiple times per frame. - Clear Tables: Always clear
CONSTANT_TABLEandFUNCTION_TABLEbefore evaluation to prevent memory leaks.
Build the mod:
./gradlew buildThe output JAR will be in:
build/libs/chatcal-1.0.0.jar
Naming Convention: chatcal-<version>.jar or chatcal-<mcversion>-<version>.jar
- Consider adding a custom tooltip renderer that supports click events
- Add configuration GUI using NeoForge's config screen API
- Consider adding more mathematical functions
- Add localization support for multiple languages
- Performance profiling for complex expressions
# Clean build
./gradlew clean build
# Run client
./gradlew runClient
# Run with debug output
./gradlew runClient --debug
# Refresh dependencies
./gradlew --refresh-dependencies
# Generate IntelliJ project files
./gradlew idea
# Generate Eclipse project files
./gradlew eclipse