diff --git a/fabric-api-dev/build.gradle b/fabric-api-dev/build.gradle
new file mode 100644
index 0000000000..85b5e378d4
--- /dev/null
+++ b/fabric-api-dev/build.gradle
@@ -0,0 +1 @@
+version = getSubprojectVersion(project)
diff --git a/fabric-api-dev/src/main/java/net/fabricmc/fabric/api/dev/v1/FabricDevProperties.java b/fabric-api-dev/src/main/java/net/fabricmc/fabric/api/dev/v1/FabricDevProperties.java
new file mode 100644
index 0000000000..70cba0c585
--- /dev/null
+++ b/fabric-api-dev/src/main/java/net/fabricmc/fabric/api/dev/v1/FabricDevProperties.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.api.dev.v1;
+
+import java.util.function.Supplier;
+
+import com.mojang.brigadier.ParseResults;
+import org.jetbrains.annotations.ApiStatus;
+
+import net.minecraft.Bootstrap;
+import net.minecraft.server.command.CommandManager;
+import net.minecraft.util.collection.Weight;
+import net.minecraft.world.Heightmap;
+
+/** Mods should not directly use these fields; they only exist here as a reference of what Dev Properties exist */
+@ApiStatus.Internal
+@SuppressWarnings("JavadocReference")
+public class FabricDevProperties {
+ /**
+ * Logs an error when a weight is set to zero
+ * Property: fabric.dev.zeroWeightWarning
+ * {@link Weight#validate(int)}
+ */
+ public static final boolean ZERO_WEIGHT_WARNING = getProperty("zeroWeightWarning");
+
+ /**
+ * Logs an error when a translation is missing
+ * Property: fabric.dev.logMissingTranslations
+ * {@link Bootstrap#logMissing()}
+ */
+ public static final boolean LOG_MISSING_TRANSLATIONS = getProperty("logMissingTranslations");
+
+ /**
+ * Logs an error if Block classes don't end with "Block" and if Item classes don't end with "Item"
+ * Property: fabric.dev.logConventionIssues
+ * {@link net.minecraft.block.Block#Block} and {@link net.minecraft.item.Item#Item}
+ */
+ public static final boolean LOG_BLOCK_AND_ITEM_CONVENTION_ISSUES = getProperty("logBlockAndItemConventionIssues");
+
+ /**
+ * Registers Minecraft's debug commands
+ * (TestCommand, RaidCommand, DebugPathCommand, DebugMobSpawningCommand,
+ * WardenSpawnTrackerCommand, SpawnArmorTrimsCommand, ServerPackCommand),
+ * and if on the server DebugConfigCommand
+ * Property: fabric.dev.registerDebugCommands
+ * {@link CommandManager#CommandManager}
+ */
+ public static final boolean REGISTER_DEBUG_COMMANDS = getProperty("registerDebugCommands");
+
+ /**
+ * Logs an error if a command threw an exception
+ * Property: fabric.dev.enableCommandExceptionLogging
+ * {@link CommandManager#execute(ParseResults, String)}
+ */
+ public static final boolean ENABLE_COMMAND_EXCEPTION_LOGGING = getProperty("enableCommandExceptionLogging");
+
+ /**
+ * Logs an error regarding argument ambiguity and throws an exception if an argument type is not registered
+ * Property: fabric.dev.enableCommandArgumentLogging
+ * {@link CommandManager#checkMissing()}
+ */
+ public static final boolean ENABLE_COMMAND_ARGUMENT_LOGGING = getProperty("enableCommandArgumentLogging");
+
+ /**
+ * Throw's an exception if a bounding box is invalid
+ * Property: fabric.dev.throwOnInvalidBlockBoxes
+ * {@link net.minecraft.util.math.BlockBox#BlockBox(int, int, int, int, int, int)}
+ */
+ public static final boolean THROW_ON_INVALID_BLOCK_BOXES = getProperty("throwOnInvalidBlockBoxes");
+
+ /**
+ * Logs an error if the heightmap is null
+ * Property: fabric.dev.enableUnprimedHeightmapLogging
+ * {@link net.minecraft.world.chunk.Chunk#sampleHeightmap(Heightmap.Type, int, int)}
+ */
+ public static final boolean ENABLE_UNPRIMED_HEIGHTMAP_LOGGING = getProperty("enableUnprimedHeightmapLogging");
+
+ /**
+ * Set's the current thread's name to the activeThreadName if debugRunnable or debugSupplier is called
+ * Property: fabric.dev.enableSupplierAndRunnableDebugging
+ * {@link net.minecraft.util.Util#debugRunnable(String, Runnable)} and {@link net.minecraft.util.Util#debugSupplier(String, Supplier)}
+ */
+ public static final boolean ENABLE_SUPPLIER_AND_RUNNABLE_DEBUGGING = getProperty("enableSupplierAndRunnableDebugging");
+
+ /**
+ * Invokes a method in which you should have a breakpoint to debug errors
+ * thrown with Util#error and exceptions thrown with Util#throwOrPause
+ * Property: fabric.dev.enableExceptionIdePausing
+ * {@link net.minecraft.util.Util#error(String)}, {@link net.minecraft.util.Util#error(String, Throwable)}
+ * and {@link net.minecraft.util.Util#throwOrPause(Throwable)}
+ */
+ public static final boolean ENABLE_EXCEPTION_IDE_PAUSING = getProperty("enableExceptionIdePausing");
+
+ private static boolean getProperty(String name) {
+ return Boolean.getBoolean("fabric.dev." + name);
+ }
+}
diff --git a/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/BlockAndItemMixin.java b/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/BlockAndItemMixin.java
new file mode 100644
index 0000000000..1134aac056
--- /dev/null
+++ b/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/BlockAndItemMixin.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.dev;
+
+import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.Item;
+
+import net.fabricmc.fabric.api.dev.v1.FabricDevProperties;
+
+@Mixin({Block.class, Item.class})
+public class BlockAndItemMixin {
+ @ModifyExpressionValue(method = {
+ "(Lnet/minecraft/block/AbstractBlock$Settings;)V",
+ "(Lnet/minecraft/item/Item$Settings;)V"
+ }, at = @At(value = "FIELD", target = "Lnet/minecraft/SharedConstants;isDevelopment:Z"))
+ private boolean isDevelopmentForDevModule(boolean original) {
+ return original || FabricDevProperties.LOG_BLOCK_AND_ITEM_CONVENTION_ISSUES;
+ }
+}
diff --git a/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/BlockBoxMixin.java b/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/BlockBoxMixin.java
new file mode 100644
index 0000000000..2b7e1ce34f
--- /dev/null
+++ b/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/BlockBoxMixin.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.dev;
+
+import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+
+import net.minecraft.util.math.BlockBox;
+
+import net.fabricmc.fabric.api.dev.v1.FabricDevProperties;
+
+@Mixin(BlockBox.class)
+public class BlockBoxMixin {
+ @ModifyExpressionValue(method = "(IIIIII)V", at = @At(value = "FIELD", target = "Lnet/minecraft/SharedConstants;isDevelopment:Z"))
+ private boolean isDevelopmentForDevModule(boolean original) {
+ return original || FabricDevProperties.THROW_ON_INVALID_BLOCK_BOXES;
+ }
+}
diff --git a/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/BootstrapMixin.java b/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/BootstrapMixin.java
new file mode 100644
index 0000000000..a7fb07de7a
--- /dev/null
+++ b/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/BootstrapMixin.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.dev;
+
+import java.util.Set;
+import java.util.function.Consumer;
+
+import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
+import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+
+import net.minecraft.Bootstrap;
+
+import net.fabricmc.fabric.api.dev.v1.FabricDevProperties;
+
+@Mixin(Bootstrap.class)
+public class BootstrapMixin {
+ @ModifyExpressionValue(method = "logMissing", at = @At(value = "FIELD", target = "Lnet/minecraft/SharedConstants;isDevelopment:Z"))
+ private static boolean isDevelopmentForDevModule(boolean original) {
+ return original || FabricDevProperties.LOG_MISSING_TRANSLATIONS || FabricDevProperties.ENABLE_COMMAND_ARGUMENT_LOGGING;
+ }
+
+ @WrapWithCondition(method = "logMissing", at = @At(value = "INVOKE", target = "Ljava/util/Set;forEach(Ljava/util/function/Consumer;)V"))
+ private static boolean wrapWithConditionTranslationWarnings(Set instance, Consumer consumer) {
+ return FabricDevProperties.LOG_MISSING_TRANSLATIONS;
+ }
+
+ @WrapWithCondition(method = "logMissing", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/command/CommandManager;checkMissing()V"))
+ private static boolean wrapWithConditionCommandArgumentWarnings() {
+ return FabricDevProperties.ENABLE_COMMAND_ARGUMENT_LOGGING;
+ }
+}
diff --git a/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/ChunkMixin.java b/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/ChunkMixin.java
new file mode 100644
index 0000000000..aaecf247e9
--- /dev/null
+++ b/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/ChunkMixin.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.dev;
+
+import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+
+import net.minecraft.world.chunk.Chunk;
+
+import net.fabricmc.fabric.api.dev.v1.FabricDevProperties;
+
+@Mixin(Chunk.class)
+public class ChunkMixin {
+ @ModifyExpressionValue(method = "sampleHeightmap", at = @At(value = "FIELD", target = "Lnet/minecraft/SharedConstants;isDevelopment:Z"))
+ private boolean isDevelopmentForDevModule(boolean original) {
+ return original || FabricDevProperties.ENABLE_UNPRIMED_HEIGHTMAP_LOGGING;
+ }
+}
diff --git a/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/CommandManagerMixin.java b/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/CommandManagerMixin.java
new file mode 100644
index 0000000000..f803e9278c
--- /dev/null
+++ b/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/CommandManagerMixin.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.dev;
+
+import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+
+import net.minecraft.server.command.CommandManager;
+
+import net.fabricmc.fabric.api.dev.v1.FabricDevProperties;
+
+@Mixin(CommandManager.class)
+public class CommandManagerMixin {
+ @ModifyExpressionValue(method = "", at = @At(value = "FIELD", target = "Lnet/minecraft/SharedConstants;isDevelopment:Z"))
+ private static boolean isDevelopmentForDevModule(boolean original) {
+ return original || FabricDevProperties.REGISTER_DEBUG_COMMANDS;
+ }
+
+ @ModifyExpressionValue(method = "execute", at = @At(value = "FIELD", target = "Lnet/minecraft/SharedConstants;isDevelopment:Z"))
+ private static boolean isDevelopmentForDevModule2(boolean original) {
+ return original || FabricDevProperties.ENABLE_COMMAND_EXCEPTION_LOGGING;
+ }
+}
diff --git a/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/UtilMixin.java b/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/UtilMixin.java
new file mode 100644
index 0000000000..cc8699b0c2
--- /dev/null
+++ b/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/UtilMixin.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.dev;
+
+import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+
+import net.minecraft.util.Util;
+
+import net.fabricmc.fabric.api.dev.v1.FabricDevProperties;
+
+@Mixin(Util.class)
+public class UtilMixin {
+ @ModifyExpressionValue(method = {
+ "debugRunnable(Ljava/lang/String;Ljava/lang/Runnable;)Ljava/lang/Runnable;",
+ "debugSupplier(Ljava/lang/String;Ljava/util/function/Supplier;)Ljava/util/function/Supplier;"
+ }, at = @At(value = "FIELD", target = "Lnet/minecraft/SharedConstants;isDevelopment:Z"))
+ private static boolean isDevelopmentForDevModule2(boolean original) {
+ return original || FabricDevProperties.ENABLE_SUPPLIER_AND_RUNNABLE_DEBUGGING;
+ }
+
+ @ModifyExpressionValue(method = {"error(Ljava/lang/String;)V", "error(Ljava/lang/String;Ljava/lang/Throwable;)V", "throwOrPause"}, at = @At(value = "FIELD", target = "Lnet/minecraft/SharedConstants;isDevelopment:Z"))
+ private static boolean isDevelopmentForDevModule3(boolean original) {
+ return original || FabricDevProperties.ENABLE_EXCEPTION_IDE_PAUSING;
+ }
+}
diff --git a/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/WeightMixin.java b/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/WeightMixin.java
new file mode 100644
index 0000000000..83e8266ce0
--- /dev/null
+++ b/fabric-api-dev/src/main/java/net/fabricmc/fabric/mixin/dev/WeightMixin.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.dev;
+
+import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+
+import net.minecraft.util.collection.Weight;
+
+import net.fabricmc.fabric.api.dev.v1.FabricDevProperties;
+
+@Mixin(Weight.class)
+public class WeightMixin {
+ @ModifyExpressionValue(method = "validate", at = @At(value = "FIELD", target = "Lnet/minecraft/SharedConstants;isDevelopment:Z"))
+ private static boolean isDevelopmentForDevModule(boolean original) {
+ return original || FabricDevProperties.ZERO_WEIGHT_WARNING;
+ }
+}
diff --git a/fabric-api-dev/src/main/resources/assets/fabric-api-dev/icon.png b/fabric-api-dev/src/main/resources/assets/fabric-api-dev/icon.png
new file mode 100644
index 0000000000..2931efbf61
Binary files /dev/null and b/fabric-api-dev/src/main/resources/assets/fabric-api-dev/icon.png differ
diff --git a/fabric-api-dev/src/main/resources/fabric-api-dev.mixins.json b/fabric-api-dev/src/main/resources/fabric-api-dev.mixins.json
new file mode 100644
index 0000000000..5414533e11
--- /dev/null
+++ b/fabric-api-dev/src/main/resources/fabric-api-dev.mixins.json
@@ -0,0 +1,17 @@
+{
+ "required": true,
+ "package": "net.fabricmc.fabric.mixin.dev",
+ "compatibilityLevel": "JAVA_21",
+ "mixins": [
+ "BlockAndItemMixin",
+ "BlockBoxMixin",
+ "BootstrapMixin",
+ "ChunkMixin",
+ "CommandManagerMixin",
+ "UtilMixin",
+ "WeightMixin"
+ ],
+ "injectors": {
+ "defaultRequire": 1
+ }
+}
diff --git a/fabric-api-dev/src/main/resources/fabric.mod.json b/fabric-api-dev/src/main/resources/fabric.mod.json
new file mode 100644
index 0000000000..7c287d5f91
--- /dev/null
+++ b/fabric-api-dev/src/main/resources/fabric.mod.json
@@ -0,0 +1,28 @@
+{
+ "schemaVersion": 1,
+ "id": "fabric-api-dev",
+ "name": "Fabric API Dev Tools",
+ "version": "${version}",
+ "environment": "*",
+ "license": "Apache-2.0",
+ "icon": "assets/fabric-api-dev/icon.png",
+ "contact": {
+ "homepage": "https://fabricmc.net",
+ "irc": "irc://irc.esper.net:6667/fabric",
+ "issues": "https://github.com/FabricMC/fabric/issues",
+ "sources": "https://github.com/FabricMC/fabric"
+ },
+ "authors": [
+ "FabricMC"
+ ],
+ "depends": {
+ "fabricloader": ">=0.15.11"
+ },
+ "description": "Contains utilities for development environments.",
+ "mixins": [
+ "fabric-api-dev.mixins.json"
+ ],
+ "custom": {
+ "fabric-api:module-lifecycle": "stable"
+ }
+}
diff --git a/gradle.properties b/gradle.properties
index 53417403d3..0270ffeb78 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -13,6 +13,7 @@ curseforge_minecraft_version=1.21.1
# Do not manually update, use the bumpversions task:
fabric-api-base-version=0.4.42
+fabric-api-dev-version=1.0.0
fabric-api-lookup-api-v1-version=1.6.70
fabric-biome-api-v1-version=13.0.30
fabric-block-api-v1-version=1.0.22
diff --git a/settings.gradle b/settings.gradle
index 540cbccdbf..d6a95ecb6b 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -14,6 +14,7 @@ include 'fabric-api-bom'
include 'fabric-api-catalog'
include 'fabric-api-base'
+include 'fabric-api-dev'
include 'fabric-api-lookup-api-v1'
include 'fabric-biome-api-v1'