### Install Java on Fedora Source: https://docs.fabricmc.net/1.21.11/players/installing-java/linux Commands to install OpenJDK 21, headless version, or development kit on Fedora. ```bash sudo dnf install java-21-openjdk ``` ```bash sudo dnf install java-21-openjdk-headless ``` ```bash sudo dnf install java-21-openjdk-devel ``` -------------------------------- ### Install Java on Debian/Ubuntu Source: https://docs.fabricmc.net/1.21.11/players/installing-java/linux Commands to update package lists and install OpenJDK 21 JDK. ```bash sudo apt update sudo apt install openjdk-21-jdk ``` -------------------------------- ### Run the Fabric Installer Source: https://docs.fabricmc.net/1.21.11/players/installing-fabric/linux Execute the downloaded JAR file using the Java runtime environment in a terminal. ```sh java -jar fabric-installer.jar ``` -------------------------------- ### Install Java on Arch Linux Source: https://docs.fabricmc.net/1.21.11/players/installing-java/linux Commands to install JRE, headless JRE, or JDK on Arch Linux. ```bash sudo pacman -S jre-openjdk ``` ```bash sudo pacman -S jre-openjdk-headless ``` ```bash sudo pacman -S jdk-openjdk ``` -------------------------------- ### Example fabric.mod.json configuration Source: https://docs.fabricmc.net/1.21.11/develop/getting-started/project-structure A sample configuration file defining mod metadata, entrypoints, mixins, and dependencies. ```json { "schemaVersion": 1, "id": "example-mod", "version": "1.0.0", "name": "Example Mod", "icon": "assets/example-mod/icon.png", "environment": "*", "entrypoints": { "main": [ "com.example.docs.ExampleMod", "com.example.docs.event.ExampleModEvents", "com.example.docs.command.ExampleModCommands", "com.example.docs.effect.ExampleModEffects", "com.example.docs.potion.ExampleModPotions", "com.example.docs.sound.ExampleModSounds", "com.example.docs.damage.ExampleModDamageTypes", "com.example.docs.item.ExampleModItems", "com.example.docs.enchantment.ExampleModEnchantments", "com.example.docs.block.ExampleModBlocks", "com.example.docs.block.entity.ExampleModBlockEntities", "com.example.docs.menu.ExampleModMenuType", "com.example.docs.component.ExampleModComponents", "com.example.docs.advancement.ExampleModDatagenAdvancement", "com.example.docs.networking.ExampleModNetworking", "com.example.docs.networking.basic.ExampleModNetworkingBasic", "com.example.docs.debug.ExampleModDebug", "com.example.docs.saveddata.ExampleModSavedData", "com.example.docs.appearance.ExampleModAppearance", "com.example.docs.entity.attribute.ExampleModAttributes", "com.example.docs.recipe.ExampleModRecipes", "com.example.docs.entity.ExampleModEntity", "com.example.docs.gamerule.ExampleModGameRules" ], "client": [ "com.example.docs.client.command.ExampleModClientCommands", "com.example.docs.ExampleModBlockEntityRenderer", "com.example.docs.ExampleModDynamicSound", "com.example.docs.ExampleModClient", "com.example.docs.ExampleModScreens", "com.example.docs.rendering.CustomRenderPipeline", "com.example.docs.rendering.HudRenderingEntrypoint", "com.example.docs.rendering.RenderingConceptsEntrypoint", "com.example.docs.network.basic.ExampleModNetworkingBasicClient", "com.example.docs.appearance.ExampleModAppearanceClient", "com.example.docs.keymapping.ExampleModKeyMappingsClient", "com.example.docs.ExampleModRecipesClient", "com.example.docs.entity.ExampleModCustomEntityClient" ], "fabric-datagen": ["com.example.docs.datagen.ExampleModDataGenerator"] }, "mixins": [ "example-mod.mixins.json", { "config": "example-mod.client.mixins.json", "environment": "client" } ], "depends": { "fabricloader": ">=0.18.0", "minecraft": "~1.21.11", "java": ">=21", "fabric-api": "*" }, "accessWidener": "example-mod.classtweaker" } ``` -------------------------------- ### Access Widener Class Example Source: https://docs.fabricmc.net/1.21.11/develop/class-tweakers/access-widening An example entry making an inner class public. ```txt # Makes the inner class TypeSpecificTrade in VillagerTrades public accessible class net/minecraft/world/entity/npc/villager/VillagerTrades$TypeSpecificTrade ``` -------------------------------- ### Start the game via Gradle Source: https://docs.fabricmc.net/1.21.11/develop/getting-started/intellij-idea/launching-the-game Use these commands in the terminal to launch the game environment without IDE integration. ```bash ./gradlew runClient ``` ```bash ./gradlew runServer ``` -------------------------------- ### Bean Serialization Examples Source: https://docs.fabricmc.net/1.21.11/develop/codecs Example JSON output for different bean types. ```json { "type": "example:stringy_bean", "stringy_string": "This bean is stringy!" } ``` ```json { "type": "example:counting_bean", "counting_number": 42 } ``` -------------------------------- ### Implement Unit Test Class Source: https://docs.fabricmc.net/1.21.11/develop/automatic-testing Example of a JUnit test class using Assertions and @BeforeAll for registry setup. Ensure test classes are placed in the src/test/java directory. ```java public class BeanTypeTest { private static final Gson GSON = new GsonBuilder().create(); @BeforeAll static void beforeAll() { BeanTypes.register(); } @Test void testBeanCodec() { StringyBean expectedBean = new StringyBean("This bean is stringy!"); Bean actualBean = Bean.BEAN_CODEC.parse(JsonOps.INSTANCE, GSON.fromJson("{\"type\":\"example:stringy_bean\",\"stringy_string\":\"This bean is stringy!\"}", JsonObject.class)).getOrThrow(); Assertions.assertInstanceOf(StringyBean.class, actualBean); Assertions.assertEquals(expectedBean.getType(), actualBean.getType()); Assertions.assertEquals(expectedBean.getStringyString(), ((StringyBean) actualBean).getStringyString()); } @Test void testDiamondItemStack() { // I know this isn't related to beans, but I need an example :) ItemStack diamondStack = new ItemStack(Items.DIAMOND, 65); ``` -------------------------------- ### Example Crash Report Log Source: https://docs.fabricmc.net/1.21.11/players/troubleshooting/crash-reports A sample crash report demonstrating a ClassCastException during block tessellation. ```txt ---- Minecraft Crash Report ---- // There are four lights! Time: 2023-12-28 13:21:22 Description: Tessellating block in world - Indium Renderer java.lang.RuntimeException: java.lang.ClassCastException: class net.minecraft.class_3924 cannot be cast to class snownee.snow.block.entity.SnowCoveredBlockEntity (net.minecraft.class_3924 and snownee.snow.block.entity.SnowCoveredBlockEntity are in unnamed module of loader net.fabricmc.loader.impl.launch.knot.KnotClassLoader @57d5872c) at snownee.snow.block.ShapeCaches.get(ShapeCaches.java:51) at snownee.snow.block.SnowWallBlock.method_9549(SnowWallBlock.java:26) at net.minecraft.class_4970.method_25959(class_4970.java:295) at net.minecraft.class_4970$class_4971.method_26222(class_4970.java:1047) at net.minecraft.class_5431$1.method_30367(class_5431.java:15) at net.minecraft.class_4970$class_4971.method_30368(class_4970.java:1241) at me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockOcclusionCache.shouldDrawSide(BlockOcclusionCache.java:52) at link.infra.indium.renderer.render.TerrainBlockRenderInfo.shouldDrawFaceInner(TerrainBlockRenderInfo.java:31) at link.infra.indium.renderer.render.BlockRenderInfo.shouldDrawFace(BlockRenderInfo.java:115) at link.infra.indium.renderer.render.AbstractBlockRenderContext.isFaceCulled(AbstractBlockRenderContext.java:97) at net.fabricmc.fabric.impl.renderer.VanillaModelEncoder.emitBlockQuads(VanillaModelEncoder.java:54) at net.minecraft.class_1087.emitBlockQuads(class_1087.java:1039) at net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel.emitBlockQuads(ForwardingBakedModel.java:51) at dev.lambdaurora.lambdabettergrass.model.LBGLayerBakedModel.emitBlockQuads(LBGLayerBakedModel.java:79) at link.infra.indium.renderer.render.TerrainRenderContext.tessellateBlock(TerrainRenderContext.java:131) at me.jellysquid.mods.sodium.client.render.chunk.compile.tasks.ChunkBuilderMeshingTask.redirect$cji000$indium$onRenderBlock(ChunkBuilderMeshingTask.java:543) at me.jellysquid.mods.sodium.client.render.chunk.compile.tasks.ChunkBuilderMeshingTask.execute(ChunkBuilderMeshingTask.java:103) at me.jellysquid.mods.sodium.client.render.chunk.compile.tasks.ChunkBuilderMeshingTask.execute(ChunkBuilderMeshingTask.java:39) at me.jellysquid.mods.sodium.client.render.chunk.compile.executor.ChunkJobTyped.execute(ChunkJobTyped.java:44) at me.jellysquid.mods.sodium.client.render.chunk.compile.executor.ChunkBuilder$WorkerRunnable.run(ChunkBuilder.java:186) at java.base/java.lang.Thread.run(Thread.java:833) Caused by: java.lang.ClassCastException: class net.minecraft.class_3924 cannot be cast to class snownee.snow.block.entity.SnowCoveredBlockEntity (net.minecraft.class_3924 and snownee.snow.block.entity.SnowCoveredBlockEntity are in unnamed module of loader net.fabricmc.loader.impl.launch.knot.KnotClassLoader @57d5872c) at snownee.snow.block.ShapeCaches.get(ShapeCaches.java:40) ... 20 more A detailed walkthrough of the error, its code path and all known details is as follows: --------------------------------------------------------------------------------------- ``` -------------------------------- ### Handling Registry Initialization Errors Source: https://docs.fabricmc.net/1.21.11/develop/automatic-testing Example of the stack trace generated when Minecraft registries are accessed before bootstrapping. ```log java.lang.ExceptionInInitializerError at net.minecraft.item.ItemStack.(ItemStack.java:94) at com.example.docs.codec.BeanTypeTest.testBeanCodec(BeanTypeTest.java:20) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) Caused by: java.lang.IllegalArgumentException: Not bootstrapped (called from registry ResourceKey[minecraft:root / minecraft:game_event]) at net.minecraft.Bootstrap.createNotBootstrappedException(Bootstrap.java:118) at net.minecraft.Bootstrap.ensureBootstrapped(Bootstrap.java:111) at net.minecraft.registry.Registries.create(Registries.java:238) at net.minecraft.registry.Registries.create(Registries.java:229) at net.minecraft.registry.Registries.(Registries.java:139) ... 5 more Not bootstrapped (called from registry ResourceKey[minecraft:root / minecraft:game_event]) java.lang.IllegalArgumentException: Not bootstrapped (called from registry ResourceKey[minecraft:root / minecraft:game_event]) at net.minecraft.Bootstrap.createNotBootstrappedException(Bootstrap.java:118) at net.minecraft.Bootstrap.ensureBootstrapped(Bootstrap.java:111) at net.minecraft.registry.Registries.create(Registries.java:238) at net.minecraft.registry.Registries.create(Registries.java:229) at net.minecraft.registry.Registries.(Registries.java:139) at net.minecraft.item.ItemStack.(ItemStack.java:94) at com.example.docs.codec.BeanTypeTest.testBeanCodec(BeanTypeTest.java:20) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) ``` -------------------------------- ### Install Java via Homebrew Source: https://docs.fabricmc.net/1.21.11/players/installing-java/macos Alternative method to install OpenJDK 21 using the Homebrew package manager. ```sh brew install --cask temurin@21 ``` -------------------------------- ### Install Fabric via Homebrew Source: https://docs.fabricmc.net/1.21.11/players/installing-fabric/macos Use the Homebrew package manager to install the Fabric installer on macOS. ```sh brew install fabric-installer ``` -------------------------------- ### Example Item for Debugging Source: https://docs.fabricmc.net/1.21.11/develop/debugging A sample Item implementation used to demonstrate debugging logic and state inspection. ```java // problematic example code: public class TestItem extends Item { public TestItem(Properties properties) { super(properties); } @Override public InteractionResult useOn(UseOnContext context) { Level level = context.getLevel(); Player user = context.getPlayer(); BlockPos targetPos = context.getBlockPos(); ItemStack itemStack = context.getItemInHand(); BlockState state = level.getBlockState(targetPos); if (state.is(ConventionalBlockTags.STONES)) { Component newName = Component.literal("[").append(state.getBlock().getName()).append(Component.literal("]")); itemStack.set(DataComponents.CUSTOM_NAME, newName); if (user != null) { user.displayClientMessage(Component.literal("Changed Item Name"), true); } } return InteractionResult.SUCCESS; } } ``` -------------------------------- ### Clone the Fabric Example Mod Repository Source: https://docs.fabricmc.net/1.21.11/develop/getting-started/creating-a-project Use this command to download the base template for a Fabric mod project manually. ```sh git clone https://github.com/FabricMC/fabric-example-mod/ example-mod ``` -------------------------------- ### Check Java Version Source: https://docs.fabricmc.net/1.21.11/players/installing-java/linux Verify the currently installed Java version in the terminal. ```bash java -version ``` -------------------------------- ### Configure Server Production Run Task Source: https://docs.fabricmc.net/1.21.11/develop/loom/production-run-tasks Define specific installer, loader, and Minecraft versions for the server production task. ```groovy tasks.register("prodServer", net.fabricmc.loom.task.prod.ServerProductionRunTask) { // The version of the Fabric Installer to use. This must be specified. installerVersion = "1.0.1" // The version of Fabric Loader to use. // Defaults to the version of Fabric Loader that the project is using. loaderVersion = "0.16.10" // The version of Minecraft to use. // Defaults to the version of Minecraft that the project is using. minecraftVersion = "1.21.4" } ``` -------------------------------- ### Define Language File Source: https://docs.fabricmc.net/1.21.11/develop/text-and-translations Example structure for a JSON language file mapping keys to localized strings. ```json { "example-mod.text.hello": "Hello!", "example-mod.text.bye": "Goodbye :(" } ``` -------------------------------- ### Implement InteractionResult in Java Source: https://docs.fabricmc.net/1.21.11/develop/debugging Example of a useOn method implementation for a custom item, demonstrating conditional logic for block interaction. ```java @Override public InteractionResult useOn(UseOnContext context) { Level level = context.getLevel(); Player user = context.getPlayer(); BlockPos targetPos = context.getClickedPos(); ItemStack itemStack = context.getItemInHand(); BlockState state = level.getBlockState(targetPos); if (state.is(ConventionalBlockTags.STONES) || state.is(ConventionalBlockTags.COBBLESTONES)) { Component newName = Component.literal("[").append(state.getBlock().getName()).append(Component.literal("]")); itemStack.set(DataComponents.CUSTOM_NAME, newName); if (user != null) { user.displayClientMessage(Component.literal("Changed Item Name"), true); } return InteractionResult.SUCCESS; } return InteractionResult.PASS; } ``` -------------------------------- ### Implement Item Use Logic with Components Source: https://docs.fabricmc.net/1.21.11/develop/items/custom-data-components Example of reading a component value, incrementing it, and saving it back during an item use event. ```java public InteractionResult use(Level level, Player user, InteractionHand hand) { ItemStack stack = user.getItemInHand(hand); // Don't do anything on the client if (level.isClientSide()) { return InteractionResult.SUCCESS; } // Read the current count and increase it by one int count = stack.getOrDefault(ModComponents.CLICK_COUNT_COMPONENT, 0); stack.set(ModComponents.CLICK_COUNT_COMPONENT, ++count); return InteractionResult.SUCCESS; } ``` -------------------------------- ### Using the Stamina Helper Class Source: https://docs.fabricmc.net/1.21.11/develop/data-attachments Example usage of the Stamina helper class to interact with player data. ```java Player player = getPlayer(); Stamina.get(player).getCurrentStamina(); ``` -------------------------------- ### Locate Minecraft JAR path Source: https://docs.fabricmc.net/1.21.11/develop/getting-started/vscode/tips-and-tricks Example path to the merged Minecraft JAR file found within the Gradle cache. ```text C:/project/.gradle/loom-cache/minecraftMaven/net/minecraft/minecraft-merged-503b555a3d/1.21.8-net.fabricmc.yarn.1_21_8.1.21.8+build.1-v2/minecraft-merged-503b555a3d-1.21.8-net.fabricmc.yarn.1_21_8.1.21.8+build.1-v2.jar ``` -------------------------------- ### Defining Method Descriptors Source: https://docs.fabricmc.net/1.21.11/develop/mixins/bytecode Example method signature used to illustrate how parameter types and return types are mapped to bytecode descriptors. ```java void drawText(int x, int y, String text, int color) { // ... } ``` -------------------------------- ### Define custom recipe JSON Source: https://docs.fabricmc.net/1.21.11/develop/custom-recipe-types Example of a JSON configuration file for a custom recipe type, specifying the serializer type and required items. ```json { "type": "example-mod:upgrading", "baseItem": "minecraft:iron_pickaxe", "upgradeItem": "minecraft:diamond", "result": { "id": "minecraft:diamond_pickaxe" } } ``` -------------------------------- ### AbstractDynamicSoundInstance Constructor Source: https://docs.fabricmc.net/1.21.11/develop/sounds/dynamic-sounds Initializes the sound instance with transition settings, volume/pitch limits, and a callback reference. ```java // set up default settings of the SoundInstance in this constructor protected AbstractDynamicSoundInstance(DynamicSoundSource soundSource, SoundEvent soundEvent, SoundSource soundCategory, int startTransitionTicks, int endTransitionTicks, float maxVolume, float minPitch, float maxPitch, SoundInstanceCallback callback) { super(soundEvent, soundCategory, SoundInstance.createUnseededRandom()); // store important references to other objects this.soundSource = soundSource; this.callback = callback; // store the limits for the SoundInstance this.maxVolume = maxVolume; this.minPitch = minPitch; this.maxPitch = maxPitch; this.startTransitionTicks = startTransitionTicks; // starting phase duration this.endTransitionTicks = endTransitionTicks; // ending phase duration // set start values this.volume = 0.0f; this.pitch = minPitch; this.looping = true; this.transitionState = TransitionState.STARTING; this.setPositionToEntity(); } ``` -------------------------------- ### Configure Basic Tests Source: https://docs.fabricmc.net/1.21.11/develop/loom/fabric-api Sets up default run configurations for both server-side and client-side game tests. ```groovy fabricApi { configureTests() } ``` -------------------------------- ### Configure Client Production Run Task Source: https://docs.fabricmc.net/1.21.11/develop/loom/production-run-tasks Configure headless execution via XVFB and profile capture settings for the client production task. ```groovy tasks.register("prodClient", net.fabricmc.loom.task.prod.ClientProductionRunTask) { // Whether to use XVFB to run the game, using a virtual framebuffer. This is useful for headless CI environments. // Defaults to true only on Linux and when the "CI" environment variable is set. // XVFB must be installed, on Debian-based systems you can install it with: `apt install xvfb` useXVFB = true // Optionally configure the tracy-capture executable. tracy { // The path to the tracy-capture executable. tracyCapture = file("tracy-capture") // The output path of the captured tracy profile. output = file("profile.tracy") // The maximum number of seconds to wait for tracy-capture to stop on its own before killing it. // Defaults to 10 seconds. maxShutdownWaitSeconds = 10 } } ``` -------------------------------- ### Ticking error log example Source: https://docs.fabricmc.net/1.21.11/develop/blocks/block-entities Example of the console warning generated when a block entity is invalid for ticking. ```log [13:27:55] [Server thread/WARN] (Minecraft) Block entity example-mod:counter @ BlockPos{x=-29, y=125, z=18} state Block{example-mod:counter_block} invalid for ticking: ``` -------------------------------- ### Bootstrapping Minecraft for Tests Source: https://docs.fabricmc.net/1.21.11/develop/automatic-testing Add this to the beginning of your beforeAll method to ensure registries are initialized. ```java SharedConstants.tryDetectVersion(); Bootstrap.bootStrap(); ``` -------------------------------- ### Recursive Serialization Example Source: https://docs.fabricmc.net/1.21.11/develop/codecs JSON representation of a serialized ListNode structure. ```json { "value": 2, "next": { "value": 3, "next": { "value": 5 } } } ``` -------------------------------- ### Implement TooltipProvider Source: https://docs.fabricmc.net/1.21.11/develop/items/custom-data-components Recommended approach for tooltips in 1.21.5+ by creating a custom component class. ```java public record ComponentWithTooltip(int clickCount) implements TooltipProvider { @Override public void addToTooltip(TooltipContext tooltip, Consumer textConsumer, TooltipFlag type, DataComponentGetter components) { textConsumer.accept(Component.translatable("item.example-mod.counter.info", this.clickCount).withStyle(ChatFormatting.GOLD)); } } ``` -------------------------------- ### JSON Representation of Data Source: https://docs.fabricmc.net/1.21.11/develop/codecs Example JSON structure corresponding to the CoolBeansClass. ```json { "beans_amount": 5, "bean_type": "beanmod:mythical_beans", "bean_positions": [ [1, 2, 3], [4, 5, 6] ] } ``` -------------------------------- ### Register a Custom Block Instance Source: https://docs.fabricmc.net/1.21.11/develop/blocks/first-block Use the registration helper to define a new block with specific properties and sound settings. ```java public static final Block CONDENSED_DIRT = register( "condensed_dirt", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true ); ``` -------------------------------- ### Define AGGRO_RANGE Attribute Source: https://docs.fabricmc.net/1.21.11/develop/entities/attributes Example of defining a specific custom attribute using the registration helper. ```java public static final Holder AGGRO_RANGE = register( "aggro_range", 8.0, 0.0, Double.MAX_VALUE, false ); ``` -------------------------------- ### Check Java Version Source: https://docs.fabricmc.net/1.21.11/players/installing-java/macos Use this command in Terminal to verify if Java 21 is currently installed and accessible. ```sh $( /usr/libexec/java_home -v 21)/bin/java --version ``` -------------------------------- ### Initialize basic FoodProperties Source: https://docs.fabricmc.net/1.21.11/develop/items/food Creates a basic food component for an item using the FoodProperties builder. ```java new Item.Properties().food(new FoodProperties.Builder().build()) ``` -------------------------------- ### Create New Object in Bytecode Source: https://docs.fabricmc.net/1.21.11/develop/mixins/bytecode Demonstrates the two-step process of object instantiation in bytecode: allocating memory with 'new' and initializing with a constructor call. ```java static Creeper createCreeper(Level level) { return new Creeper(level); } ``` ```bytecode static createCreeper (Lnet/minecraft/world/level/Level;)Lnet/mineraft/world/entity/monster/Creeper; new net/minecraft/world/entity/monster/Creeper dup aload 0 // level invokespecial net/minecraft/world/entity/monster/Creeper. (Lnet/minecraft/world/level/Level;)V areturn ``` -------------------------------- ### Implement DataGeneratorEntrypoint Source: https://docs.fabricmc.net/1.21.11/develop/data-generation/setup Create an entrypoint class to initialize the data generator. ```java public class ExampleModDataGenerator implements DataGeneratorEntrypoint { @Override public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) { } } ``` -------------------------------- ### Implement a custom SuggestionProvider Source: https://docs.fabricmc.net/1.21.11/develop/commands/suggestions Create a class implementing SuggestionProvider and override getSuggestions to provide custom logic, such as listing online players. ```java public class PlayerSuggestionProvider implements SuggestionProvider { @Override public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) throws CommandSyntaxException { CommandSourceStack source = context.getSource(); // Thankfully, the ServerCommandSource has a method to get a list of player names. Collection playerNames = source.getOnlinePlayerNames(); // Add all player names to the builder. for (String playerName : playerNames) { builder.suggest(playerName); } // Lock the suggestions after we've modified them. return builder.buildFuture(); } } ``` -------------------------------- ### Defining Nested Classes Source: https://docs.fabricmc.net/1.21.11/develop/mixins/bytecode Example of a nested class structure used to demonstrate internal name formatting in bytecode. ```java package pkg; class Foo { class Bar { } } ``` -------------------------------- ### Create a CustomSounds Helper Class Source: https://docs.fabricmc.net/1.21.11/develop/sounds/custom Organize multiple SoundEvent registrations by creating a dedicated helper class to manage static sound variables. ```java public class CustomSounds { private CustomSounds() { // private empty constructor to avoid accidental instantiation } // ITEM_METAL_WHISTLE is the name of the custom sound event // and is called in the mod to use the custom sound public static final SoundEvent ITEM_METAL_WHISTLE = registerSound("metal_whistle"); public static final SoundEvent ENGINE_LOOP = registerSound("engine"); // actual registration of all the custom SoundEvents private static SoundEvent registerSound(String id) { Identifier identifier = Identifier.fromNamespaceAndPath(ExampleModSounds.MOD_ID, id); return Registry.register(BuiltInRegistries.SOUND_EVENT, identifier, SoundEvent.createVariableRangeEvent(identifier)); } // This static method starts class initialization, which then initializes // the static class variables (e.g. ITEM_METAL_WHISTLE). public static void initialize() { ExampleModSounds.LOGGER.info("Registering " + ExampleModSounds.MOD_ID + " Sounds"); // Technically this method can stay empty, but some developers like to notify // the console, that certain parts of the mod have been successfully initialized } } ``` -------------------------------- ### Generated Feature JSON Files Source: https://docs.fabricmc.net/1.21.11/develop/data-generation/features Examples of the JSON output generated by the datagen process for configured and placed features. ```json { "type": "minecraft:ore", "config": { "discard_chance_on_air_exposure": 0.0, "size": 10, "targets": [ { "state": { "Name": "minecraft:diamond_block" }, "target": { "predicate_type": "minecraft:tag_match", "tag": "minecraft:deepslate_ore_replaceables" } } ] } } ``` ```json { "feature": "example-mod:diamond_block_vein", "placement": [ { "type": "minecraft:count", "count": 6 }, { "type": "minecraft:biome" }, { "type": "minecraft:in_square" }, { "type": "minecraft:height_range", "height": { "type": "minecraft:biased_to_bottom", "inner": 3, "max_inclusive": { "absolute": 0 }, "min_inclusive": { "above_bottom": 0 } } } ] } ``` -------------------------------- ### Document Custom Event with Javadoc Source: https://docs.fabricmc.net/1.21.11/develop/events Example Javadoc for documenting the behavior of ActionResult return values in a custom callback. ```java /** * Callback for shearing a sheep. * Called before the sheep is sheared, items are dropped, and items are damaged. * Upon return: * - SUCCESS cancels further processing and continues with normal shearing behavior. * - PASS falls back to further processing and defaults to SUCCESS if no other listeners are available * - FAIL cancels further processing and does not shear the sheep. */ ``` -------------------------------- ### Configure Basic Data Generation Source: https://docs.fabricmc.net/1.21.11/develop/loom/fabric-api Initializes a default run configuration for data generation. ```groovy fabricApi { configureDataGeneration() } ``` -------------------------------- ### Play a custom sound instance Source: https://docs.fabricmc.net/1.21.11/develop/sounds/dynamic-sounds Instantiate and play a custom sound instance via the sound manager on the client. ```java CustomSoundInstance instance = new CustomSoundInstance(client.player, CustomSounds.ENGINE_LOOP, SoundSource.NEUTRAL); // play the sound instance client.getSoundManager().play(instance); ``` -------------------------------- ### Specify Cuboid Bounds Source: https://docs.fabricmc.net/1.21.11/develop/blocks/block-models The from and to tags define the starting and ending coordinates of a cuboid. Values must be between -16 and 32. ```json { "from": [0.0, 0.0, 0.0], "to": [0.0, 0.0, 0.0] } ``` -------------------------------- ### Instantiate and apply a DamageSource Source: https://docs.fabricmc.net/1.21.11/develop/entities/damage-types These snippets show how to look up the damage type from the registry and apply it to an entity. ```java DamageSource damageSource = new DamageSource( level.registryAccess() .lookupOrThrow(Registries.DAMAGE_TYPE) .get(ExampleModDamageTypes.TATER_DAMAGE.identifier()).get() ); ``` ```java entity.hurtServer(serverLevel, damageSource, 5.0f); ``` -------------------------------- ### Enable split environment source sets Source: https://docs.fabricmc.net/1.21.11/develop/loom Configures the build to separate client and common code at compile time. ```groovy loom { splitEnvironmentSourceSets() ``` -------------------------------- ### Implementing a HUD Layer Source: https://docs.fabricmc.net/1.21.11/develop/rendering/basic-concepts Demonstrates the implementation of a HUD layer using Matrix3x2fStack for scaling, translation, and rotation transformations. ```java private HudElement hudLayer() { return (graphics, deltaTracker) -> { // :::2 Matrix3x2fStack matrices = graphics.pose(); // Store the total tick delta in a field, so we can use it later. totalTickProgress += deltaTracker.getGameTimeDeltaPartialTick(true); // Push a new matrix onto the stack. matrices.pushMatrix(); // :::2 // :::2 // Scale the matrix by 0.5 to make the triangle smaller and larger over time. float scaleAmount = Mth.sin(totalTickProgress / 10F) / 2F + 1.5F; // Apply the scaling amount to the matrix. // We don't need to scale the Z axis since it's on the HUD and 2D. matrices.scale(scaleAmount, scaleAmount); // :::2 matrices.scale(1 / scaleAmount, 1 / scaleAmount); matrices.translate(60f, 60f); // :::3 // Lerp between 0 and 360 degrees over time. float rotationAmount = totalTickProgress / 50F % 360; matrices.rotate(rotationAmount); // Shift entire square so that it rotates in its center. matrices.translate(-20f, -40f); // :::3 // :::2 // We do not need to manually write to the buffer. GuiGraphics methods write to GUI buffer in `GuiRenderer` at the end of preparation. ``` -------------------------------- ### Implement Recipe Book Methods Source: https://docs.fabricmc.net/1.21.11/develop/custom-recipe-types Provides default implementations for recipe book integration and special recipe status. ```java @Override public @Nullable RecipeBookCategory recipeBookCategory() { return null; } @Override public PlacementInfo placementInfo() { return PlacementInfo.NOT_PLACEABLE; } ``` -------------------------------- ### Migrate mappings to Mojang Mappings Source: https://docs.fabricmc.net/1.21.11/develop/porting/mappings/loom Run this command to migrate current mappings to Mojang Mappings. Ensure you have a backup before running, as this replaces source files. ```powershell ./gradlew.bat migrateMappings --mappings "net.minecraft:mappings:1.21.11" --overrideInputsIHaveABackup ``` ```sh ./gradlew migrateMappings --mappings "net.minecraft:mappings:1.21.11" --overrideInputsIHaveABackup ``` ```sh migrateMappings --mappings "net.minecraft:mappings:1.21.11" --overrideInputsIHaveABackup ``` -------------------------------- ### Configure Gradle for Game Tests Source: https://docs.fabricmc.net/1.21.11/develop/automatic-testing Adds production runtime dependencies and registers a production client game test task with XVFB support for headless environments. ```gradle dependencies { productionRuntimeMods "net.fabricmc.fabric-api:fabric-api:${fabricApiVersion}" } // :::classtweaker-setup:gradle::: loom { accessWidenerPath = file("src/main/resources/example-mod.classtweaker") } // :::classtweaker-setup:gradle::: tasks.register("runProductionClientGameTest", net.fabricmc.loom.task.prod.ClientProductionRunTask) { jvmArgs.add("-Dfabric.client.gametest") // Whether to use XVFB to run the game, using a virtual framebuffer. This is useful for headless CI environments. // Defaults to true only on Linux and when the "CI" environment variable is set. // XVFB must be installed, on Debian-based systems you can install it with: `apt install xvfb` useXVFB = true } ``` -------------------------------- ### Handling Sound Instance Ticks Source: https://docs.fabricmc.net/1.21.11/develop/sounds/dynamic-sounds Manages the sound lifecycle, including position updates and state transitions between starting, running, and ending phases. ```java @Override public void tick() { // handle states where sound might be actually stopped instantly if (this.soundSource == null) { this.callback.onFinished(this); } // basic tick behaviour this.currentTick++; this.setPositionToEntity(); // SoundInstance phase switching switch (this.transitionState) { case STARTING -> { this.transitionTick++; // go into next phase if starting phase finished its duration if (this.transitionTick > this.startTransitionTicks) { this.transitionTick = 0; // reset tick for future ending phase this.transitionState = TransitionState.RUNNING; } } case ENDING -> { this.transitionTick++; // set SoundInstance as finished if ending phase finished its duration if (this.transitionTick > this.endTransitionTicks) { this.callback.onFinished(this); } } } // apply volume and pitch modulation here, // if you use a normal SoundInstance class } ``` -------------------------------- ### Implementing a ModInitializer Source: https://docs.fabricmc.net/1.21.11/develop/getting-started/project-structure A basic implementation of the ModInitializer interface to handle mod startup logic and logging. ```java public class ExampleMod implements ModInitializer { // This logger is used to write text to the console and the log file. // It is considered best practice to use your mod id as the logger's name. // That way, it's clear which mod wrote info, warnings, and errors. public static final String MOD_ID = "example-mod"; public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); @Override public void onInitialize() { // This code runs as soon as Minecraft is in a mod-load-ready state. // However, some things (like resources) may still be uninitialized. // Proceed with mild caution. LOGGER.info("Hello Fabric world!"); // #tooltip_provider ComponentTooltipAppenderRegistry.addAfter(DataComponents.DAMAGE, ModComponents.COMPONENT_WITH_TOOLTIP); // #tooltip_provider } } ``` -------------------------------- ### Implement Sound Registration in ModInitializer Source: https://docs.fabricmc.net/1.21.11/develop/sounds/custom Invoke the helper class within the ModInitializer to register all custom sounds cleanly. ```java public class ExampleModSounds implements ModInitializer { public static final String MOD_ID = ExampleMod.MOD_ID; public static final Logger LOGGER = ExampleMod.LOGGER; @Override public void onInitialize() { // This is the basic registering. Use a new class for registering sounds // instead, to keep the ModInitializer implementing class clean! Registry.register(BuiltInRegistries.SOUND_EVENT, Identifier.fromNamespaceAndPath(MOD_ID, "metal_whistle_simple"), SoundEvent.createVariableRangeEvent(Identifier.fromNamespaceAndPath(MOD_ID, "metal_whistle_simple"))); // ... the cleaner approach. CustomSounds.initialize(); } public static Identifier identifierOf(String path) { return Identifier.fromNamespaceAndPath(ExampleMod.MOD_ID, path); } } ``` -------------------------------- ### Create a TreeConfiguration Source: https://docs.fabricmc.net/1.21.11/develop/data-generation/features Builds a custom tree configuration defining trunk and leaf providers, placers, and size constraints. ```java TreeConfiguration diamondTree = new TreeConfiguration.TreeConfigurationBuilder( // Trunk / Logs BlockStateProvider.simple(Blocks.DIAMOND_BLOCK), new StraightTrunkPlacer(4, 2, 0), // Leaves BlockStateProvider.simple(Blocks.GOLD_BLOCK), new BlobFoliagePlacer(ConstantInt.of(2), ConstantInt.of(0), 3), new TwoLayersFeatureSize(0, 0, 0) ).build(); ``` -------------------------------- ### Initialize Client Model Layers Source: https://docs.fabricmc.net/1.21.11/develop/entities/first-entity Call the registration method within the mod's client initializer. ```java public class ExampleModCustomEntityClient implements ClientModInitializer { @Override public void onInitializeClient() { ModEntityModelLayers.registerModelLayers(); } } ``` -------------------------------- ### Implement Drawing Phase in Java Source: https://docs.fabricmc.net/1.21.11/develop/rendering/world Handles the building of vertex buffers, uploading data to the GPU, and executing the render pass. Ensure this is called after the extraction phase. ```java private static final Vector4f COLOR_MODULATOR = new Vector4f(1f, 1f, 1f, 1f); private static final Vector3f MODEL_OFFSET = new Vector3f(); private static final Matrix4f TEXTURE_MATRIX = new Matrix4f(); private MappableRingBuffer vertexBuffer; private void drawFilledThroughWalls(Minecraft client, @SuppressWarnings("SameParameterValue") RenderPipeline pipeline) { // Build the buffer MeshData builtBuffer = buffer.buildOrThrow(); MeshData.DrawState drawParameters = builtBuffer.drawState(); VertexFormat format = drawParameters.format(); GpuBuffer vertices = upload(drawParameters, format, builtBuffer); draw(client, pipeline, builtBuffer, drawParameters, vertices, format); // Rotate the vertex buffer so we are less likely to use buffers that the GPU is using vertexBuffer.rotate(); buffer = null; } private GpuBuffer upload(MeshData.DrawState drawParameters, VertexFormat format, MeshData builtBuffer) { // Calculate the size needed for the vertex buffer int vertexBufferSize = drawParameters.vertexCount() * format.getVertexSize(); // Initialize or resize the vertex buffer as needed if (vertexBuffer == null || vertexBuffer.size() < vertexBufferSize) { if (vertexBuffer != null) { vertexBuffer.close(); } vertexBuffer = new MappableRingBuffer(() -> ExampleMod.MOD_ID + " example render pipeline", GpuBuffer.USAGE_VERTEX | GpuBuffer.USAGE_MAP_WRITE, vertexBufferSize); } // Copy vertex data into the vertex buffer CommandEncoder commandEncoder = RenderSystem.getDevice().createCommandEncoder(); try (GpuBuffer.MappedView mappedView = commandEncoder.mapBuffer(vertexBuffer.currentBuffer().slice(0, builtBuffer.vertexBuffer().remaining()), false, true)) { MemoryUtil.memCopy(builtBuffer.vertexBuffer(), mappedView.data()); } return vertexBuffer.currentBuffer(); } private static void draw(Minecraft client, RenderPipeline pipeline, MeshData builtBuffer, MeshData.DrawState drawParameters, GpuBuffer vertices, VertexFormat format) { GpuBuffer indices; VertexFormat.IndexType indexType; if (pipeline.getVertexFormatMode() == VertexFormat.Mode.QUADS) { // Sort the quads if there is translucency builtBuffer.sortQuads(allocator, RenderSystem.getProjectionType().vertexSorting()); // Upload the index buffer indices = pipeline.getVertexFormat().uploadImmediateIndexBuffer(builtBuffer.indexBuffer()); indexType = builtBuffer.drawState().indexType(); } else { // Use the general shape index buffer for non-quad draw modes RenderSystem.AutoStorageIndexBuffer shapeIndexBuffer = RenderSystem.getSequentialBuffer(pipeline.getVertexFormatMode()); indices = shapeIndexBuffer.getBuffer(drawParameters.indexCount()); indexType = shapeIndexBuffer.type(); } // Actually execute the draw GpuBufferSlice dynamicTransforms = RenderSystem.getDynamicUniforms() .writeTransform(RenderSystem.getModelViewMatrix(), COLOR_MODULATOR, MODEL_OFFSET, TEXTURE_MATRIX); try (RenderPass renderPass = RenderSystem.getDevice() .createCommandEncoder() .createRenderPass(() -> ExampleMod.MOD_ID + " example render pipeline rendering", client.getMainRenderTarget().getColorTextureView(), OptionalInt.empty(), client.getMainRenderTarget().getDepthTextureView(), OptionalDouble.empty())) { renderPass.setPipeline(pipeline); RenderSystem.bindDefaultUniforms(renderPass); renderPass.setUniform("DynamicTransforms", dynamicTransforms); // Bind texture if applicable: // Sampler0 is used for texture inputs in vertices // renderPass.bindTexture("Sampler0", textureSetup.texure0(), textureSetup.sampler0()); renderPass.setVertexBuffer(0, vertices); renderPass.setIndexBuffer(indices, indexType); // The base vertex is the starting index when we copied the data into the vertex buffer divided by vertex size //noinspection ConstantValue renderPass.drawIndexed(0 / format.getVertexSize(), 0, drawParameters.indexCount(), 1); } builtBuffer.close(); } ``` -------------------------------- ### Implement a basic Command interface Source: https://docs.fabricmc.net/1.21.11/develop/commands/basics A simple implementation of the Command functional interface using a lambda expression. ```java Command command = context -> { return 0; }; ``` -------------------------------- ### Play a simple UI sound Source: https://docs.fabricmc.net/1.21.11/develop/sounds/dynamic-sounds Use SimpleSoundInstance to trigger a sound effect on the client side, such as a UI button click. ```java Minecraft client = Minecraft.getInstance(); client.getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F)); ``` -------------------------------- ### Create a FabricDynamicRegistryProvider Source: https://docs.fabricmc.net/1.21.11/develop/data-generation/features Extend FabricDynamicRegistryProvider to set up the base methods for world generation data providers. ```java public class ExampleModWorldgenProvider extends FabricDynamicRegistryProvider { public ExampleModWorldgenProvider(FabricDataOutput output, CompletableFuture registriesFuture) { super(output, registriesFuture); } @Override protected void configure(HolderLookup.Provider registries, Entries entries) { } @Override public String getName() { return ""; } } ``` -------------------------------- ### Simple Lambda Compilation Source: https://docs.fabricmc.net/1.21.11/develop/mixins/bytecode Demonstrates the basic structure of a lambda expression and its corresponding bytecode representation. ```java static void hello() { Runnable r = () -> System.out.println("Hello, World!"); r.run(); } ``` ```bytecode static hello ()V invokedynamic run ()Ljava/lang/Runnable; java/lang/invoke/LambdaMetafactory.metafactory ()V lambda$hello$1 ()V astore 0 // r aload 0 // r invokeinterface run return static lambda$hello$1 ()V getstatic System.out ldc "Hello, World!" invokevirtual println return ``` -------------------------------- ### Migrate Source Sets In-Place Source: https://docs.fabricmc.net/1.21.11/develop/porting/mappings/loom Use the migrateMappings task with custom input and output paths to migrate source sets, such as client-specific code, to new mappings. ```powershell ./gradlew.bat migrateMappings --input "src/client/java" --output "src/client/java" --mappings "net.minecraft:mappings:1.21.11" ``` ```sh ./gradlew migrateMappings --input "src/client/java" --output "src/client/java" --mappings "net.minecraft:mappings:1.21.11" ``` ```sh migrateMappings --input "src/client/java" --output "src/client/java" --mappings "net.minecraft:mappings:1.21.11" ``` -------------------------------- ### Define Client Item Model Source: https://docs.fabricmc.net/1.21.11/develop/blocks/first-block Create a client item file to link the block item to its corresponding block model. ```json { "model": { "type": "minecraft:model", "model": "example-mod:block/condensed_dirt" } } ``` -------------------------------- ### Implement RecipeInput for UpgradingRecipe Source: https://docs.fabricmc.net/1.21.11/develop/custom-recipe-types Defines a record implementing RecipeInput to hold the base item and upgrade item for the custom recipe. ```java public record UpgradingRecipeInput(ItemStack baseItem, ItemStack upgradeItem) implements RecipeInput { @Override public ItemStack getItem(int index) { return switch (index) { case 0 -> baseItem; case 1 -> upgradeItem; default -> ItemStack.EMPTY; }; } @Override public int size() { return 2; } } ``` -------------------------------- ### Define Language Strings Source: https://docs.fabricmc.net/1.21.11/develop/items/custom-data-components JSON configuration for item display names and tooltip text. ```json { "item.example-mod.counter": "Counter", "item.example-mod.counter.info": "Used %1$s times" } ``` -------------------------------- ### Create a Custom Screen Class Source: https://docs.fabricmc.net/1.21.11/develop/rendering/gui/custom-screens Extends the Screen class to implement custom UI logic, including widget initialization and manual text rendering. ```java public class CustomScreen extends Screen { public CustomScreen(Component title) { super(title); } @Override protected void init() { Button buttonWidget = Button.builder(Component.literal("Hello World"), (btn) -> { // When the button is clicked, we can display a toast to the screen. this.minecraft.getToastManager().addToast( SystemToast.multiline(this.minecraft, SystemToast.SystemToastId.NARRATOR_TOGGLE, Component.nullToEmpty("Hello World!"), Component.nullToEmpty("This is a toast.")) ); }).bounds(40, 40, 120, 20).build(); // x, y, width, height // It's recommended to use the fixed height of 20 to prevent rendering issues with the button // textures. // Register the button widget. this.addRenderableWidget(buttonWidget); } @Override public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) { super.render(graphics, mouseX, mouseY, delta); // Minecraft doesn't have a "label" widget, so we'll have to draw our own text. // We'll subtract the font height from the Y position to make the text appear above the button. // Subtracting an extra 10 pixels will give the text some padding. // textRenderer, text, x, y, color, hasShadow graphics.drawString(this.font, "Special Button", 40, 40 - this.font.lineHeight - 10, 0xFFFFFFFF, true); } } ``` -------------------------------- ### Implement Sound Management Methods Source: https://docs.fabricmc.net/1.21.11/develop/sounds/dynamic-sounds Provides methods to play, stop, and retrieve active SoundInstances within the manager. ```java // Plays a sound instance, if it doesn't already exist in the list public void play(T soundInstance) { if (this.activeSounds.contains(soundInstance)) return; client.getSoundManager().play(soundInstance); this.activeSounds.add(soundInstance); } // Stops a sound immediately. in most cases it is preferred to use // the sound's ending phase, which will clean it up after completion public void stop(T soundInstance) { client.getSoundManager().stop(soundInstance); this.activeSounds.remove(soundInstance); } // Finds a SoundInstance from a SoundEvent, if it exists and is currently playing public Optional getPlayingSoundInstance(SoundEvent soundEvent) { for (var activeSound : this.activeSounds) { // SoundInstances use their SoundEvent's id by default if (activeSound.getIdentifier().equals(soundEvent.location())) { return Optional.of(activeSound); } } return Optional.empty(); } ``` -------------------------------- ### Implement the type method Source: https://docs.fabricmc.net/1.21.11/develop/items/item-appearance Returns the codec implementation for the tint source. ```java @Override public @NotNull MapCodec type() { return MAP_CODEC; } ```