From 14c0ce9d3dda9b698639acda6fe739c26c994210 Mon Sep 17 00:00:00 2001
From: Samu
Date: Mon, 9 Feb 2026 13:49:39 +0100
Subject: [PATCH 01/10] Environment attribute API for adding custom attribute
layers
---
fabric-environment-attributes-v0/build.gradle | 11 +++
.../attribute/v0/AttributeLayerPosition.java | 48 ++++++++++++
.../v0/EnvironmentAttributeEvents.java | 64 ++++++++++++++++
.../EnvironmentAttributeEventsImpl.java | 55 ++++++++++++++
.../EnvironmentAttributeSystemMixin.java | 71 ++++++++++++++++++
.../fabric-environment-attributes-v0/icon.png | Bin 0 -> 1555 bytes
...bric-environment-attributes-v0.mixins.json | 14 ++++
.../src/main/resources/fabric.mod.json | 31 ++++++++
.../FabricEnvironmentAttributesTest.java | 37 +++++++++
.../src/testmod/resources/fabric.mod.json | 19 +++++
...FabricEnvironmentAttributesClientTest.java | 69 +++++++++++++++++
gradle.properties | 1 +
settings.gradle | 1 +
13 files changed, 421 insertions(+)
create mode 100644 fabric-environment-attributes-v0/build.gradle
create mode 100644 fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/api/environment/attribute/v0/AttributeLayerPosition.java
create mode 100644 fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/api/environment/attribute/v0/EnvironmentAttributeEvents.java
create mode 100644 fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/impl/environment/attribute/EnvironmentAttributeEventsImpl.java
create mode 100644 fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java
create mode 100644 fabric-environment-attributes-v0/src/main/resources/assets/fabric-environment-attributes-v0/icon.png
create mode 100644 fabric-environment-attributes-v0/src/main/resources/fabric-environment-attributes-v0.mixins.json
create mode 100644 fabric-environment-attributes-v0/src/main/resources/fabric.mod.json
create mode 100644 fabric-environment-attributes-v0/src/testmod/java/net/fabricmc/fabric/test/environment/attribute/FabricEnvironmentAttributesTest.java
create mode 100644 fabric-environment-attributes-v0/src/testmod/resources/fabric.mod.json
create mode 100644 fabric-environment-attributes-v0/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java
diff --git a/fabric-environment-attributes-v0/build.gradle b/fabric-environment-attributes-v0/build.gradle
new file mode 100644
index 00000000000..8bfe314df96
--- /dev/null
+++ b/fabric-environment-attributes-v0/build.gradle
@@ -0,0 +1,11 @@
+version = getSubprojectVersion(project)
+
+moduleDependencies(project, [
+ 'fabric-api-base',
+ 'fabric-lifecycle-events-v1'
+])
+
+testDependencies(project, [
+ ':fabric-resource-loader-v1',
+ ':fabric-client-gametest-api-v1'
+])
diff --git a/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/api/environment/attribute/v0/AttributeLayerPosition.java b/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/api/environment/attribute/v0/AttributeLayerPosition.java
new file mode 100644
index 00000000000..cbbf7cc1659
--- /dev/null
+++ b/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/api/environment/attribute/v0/AttributeLayerPosition.java
@@ -0,0 +1,48 @@
+/*
+ * 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.environment.attribute.v0;
+
+/**
+ * Positions at which environment attribute layers can be inserted. Each position is associated with an event in
+ * {@link EnvironmentAttributeEvents}. The enum is ordered by in which order Minecraft adds its default layers.
+ */
+public enum AttributeLayerPosition {
+ /**
+ * The position before all vanilla layers.
+ */
+ BEFORE_ALL,
+
+ /**
+ * The position between dimension and biome layers.
+ */
+ BETWEEN_DIMENSION_AND_BIOMES,
+
+ /**
+ * The position between biome and timeline layers.
+ */
+ BETWEEN_BIOMES_AND_TIMELINES,
+
+ /**
+ * The position between timeline and weather layers.
+ */
+ BETWEEN_TIMELINES_AND_WEATHER,
+
+ /**
+ * The position after all vanilla layers.
+ */
+ AFTER_ALL
+}
diff --git a/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/api/environment/attribute/v0/EnvironmentAttributeEvents.java b/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/api/environment/attribute/v0/EnvironmentAttributeEvents.java
new file mode 100644
index 00000000000..8fb8f0b4729
--- /dev/null
+++ b/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/api/environment/attribute/v0/EnvironmentAttributeEvents.java
@@ -0,0 +1,64 @@
+/*
+ * 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.environment.attribute.v0;
+
+import net.minecraft.world.attribute.EnvironmentAttributeSystem;
+import net.minecraft.world.level.Level;
+
+import net.fabricmc.fabric.api.event.Event;
+import net.fabricmc.fabric.impl.environment.attribute.EnvironmentAttributeEventsImpl;
+
+/**
+ * Events related to environment attributes.
+ */
+public class EnvironmentAttributeEvents {
+ /**
+ * Returns the {@link InsertLayers} event for the given {@link AttributeLayerPosition}. This event allows inserting
+ * extra attribute layers at that position during the environment attribute setup for a {@link Level}. By default,
+ * Minecraft adds layers for the dimension, then for biomes, then for timelines and lastly for weather. The event
+ * returned by this method is triggered before, in between and after each of these vanilla layers, depending on the
+ * selected position, in the following manner:
+ *
+ * - Event for {@link AttributeLayerPosition#BEFORE_ALL} is triggered.
+ * - Minecraft adds the layer for dimension type attribute configurations.
+ * - Event for {@link AttributeLayerPosition#BETWEEN_DIMENSION_AND_BIOMES} is triggered.
+ * - Minecraft adds the layer for biome attribute configurations.
+ * - Event for {@link AttributeLayerPosition#BETWEEN_BIOMES_AND_TIMELINES} is triggered.
+ * - Minecraft adds the layer for timeline attribute animations.
+ * - Event for {@link AttributeLayerPosition#BETWEEN_TIMELINES_AND_WEATHER} is triggered.
+ * - Minecraft adds the layer for some hardcoded weather attribute overrides.
+ * - Event for {@link AttributeLayerPosition#AFTER_ALL} is triggered.
+ *
+ * @param position The position at which you want to insert attribute layers.
+ * @return The event to listen for layer setup.
+ */
+ public static Event insertLayersEvent(AttributeLayerPosition position) {
+ return EnvironmentAttributeEventsImpl.getOrCreateInsertLayersEvent(position);
+ }
+
+ /**
+ * Callback for events returned from {@link #insertLayersEvent}.
+ */
+ public interface InsertLayers {
+ /**
+ * Insert custom attribute layers into the {@link EnvironmentAttributeSystem.Builder}.
+ * @param systemBuilder The environment attribute system builder to modify.
+ * @param level The level for which the attribute system is built.
+ */
+ void insertAttributeLayers(EnvironmentAttributeSystem.Builder systemBuilder, Level level);
+ }
+}
diff --git a/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/impl/environment/attribute/EnvironmentAttributeEventsImpl.java b/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/impl/environment/attribute/EnvironmentAttributeEventsImpl.java
new file mode 100644
index 00000000000..f93960f5f81
--- /dev/null
+++ b/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/impl/environment/attribute/EnvironmentAttributeEventsImpl.java
@@ -0,0 +1,55 @@
+/*
+ * 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.impl.environment.attribute;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+import org.jspecify.annotations.NonNull;
+
+import net.minecraft.world.attribute.EnvironmentAttributeSystem;
+import net.minecraft.world.level.Level;
+
+import net.fabricmc.fabric.api.environment.attribute.v0.AttributeLayerPosition;
+import net.fabricmc.fabric.api.environment.attribute.v0.EnvironmentAttributeEvents;
+import net.fabricmc.fabric.api.event.Event;
+import net.fabricmc.fabric.api.event.EventFactory;
+
+public class EnvironmentAttributeEventsImpl {
+ private static final Map> POSITION_EVENT_MAP = new EnumMap<>(AttributeLayerPosition.class);
+
+ @NonNull
+ public static Event getOrCreateInsertLayersEvent(AttributeLayerPosition position) {
+ return POSITION_EVENT_MAP.computeIfAbsent(position, (p -> createInsertEvent()));
+ }
+
+ public static void insertLayers(AttributeLayerPosition position, EnvironmentAttributeSystem.Builder systemBuilder, Level level) {
+ Event event = POSITION_EVENT_MAP.get(position);
+
+ if (event != null) {
+ event.invoker().insertAttributeLayers(systemBuilder, level);
+ }
+ }
+
+ private static Event createInsertEvent() {
+ return EventFactory.createArrayBacked(EnvironmentAttributeEvents.InsertLayers.class, callbacks -> (systemBuilder, level) -> {
+ for (EnvironmentAttributeEvents.InsertLayers callback : callbacks) {
+ callback.insertAttributeLayers(systemBuilder, level);
+ }
+ });
+ }
+}
diff --git a/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java b/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java
new file mode 100644
index 00000000000..01f6bfe52de
--- /dev/null
+++ b/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java
@@ -0,0 +1,71 @@
+/*
+ * 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.environment.attribute;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import net.minecraft.world.attribute.EnvironmentAttributeSystem;
+import net.minecraft.world.level.Level;
+
+import net.fabricmc.fabric.api.environment.attribute.v0.AttributeLayerPosition;
+import net.fabricmc.fabric.impl.environment.attribute.EnvironmentAttributeEventsImpl;
+
+@Mixin(EnvironmentAttributeSystem.class)
+public class EnvironmentAttributeSystemMixin {
+ @Inject(
+ method = "addDefaultLayers",
+ at = @At(value = "HEAD")
+ )
+ private static void addLayersBeforeAll(EnvironmentAttributeSystem.Builder builder, Level level, CallbackInfo ci) {
+ EnvironmentAttributeEventsImpl.insertLayers(AttributeLayerPosition.BEFORE_ALL, builder, level);
+ }
+
+ @Inject(
+ method = "addDefaultLayers",
+ at = @At(value = "INVOKE", target = "Lnet/minecraft/world/attribute/EnvironmentAttributeSystem;addBiomeLayer(Lnet/minecraft/world/attribute/EnvironmentAttributeSystem$Builder;Lnet/minecraft/core/HolderLookup;Lnet/minecraft/world/level/biome/BiomeManager;)V")
+ )
+ private static void addLayersAfterDimension(EnvironmentAttributeSystem.Builder builder, Level level, CallbackInfo ci) {
+ EnvironmentAttributeEventsImpl.insertLayers(AttributeLayerPosition.BETWEEN_DIMENSION_AND_BIOMES, builder, level);
+ }
+
+ @Inject(
+ method = "addDefaultLayers",
+ at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;dimensionType()Lnet/minecraft/world/level/dimension/DimensionType;")
+ )
+ private static void addLayersAfterBiomes(EnvironmentAttributeSystem.Builder builder, Level level, CallbackInfo ci) {
+ EnvironmentAttributeEventsImpl.insertLayers(AttributeLayerPosition.BETWEEN_BIOMES_AND_TIMELINES, builder, level);
+ }
+
+ @Inject(
+ method = "addDefaultLayers",
+ at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;canHaveWeather()Z")
+ )
+ private static void addLayersAfterTimelines(EnvironmentAttributeSystem.Builder builder, Level level, CallbackInfo ci) {
+ EnvironmentAttributeEventsImpl.insertLayers(AttributeLayerPosition.BETWEEN_TIMELINES_AND_WEATHER, builder, level);
+ }
+
+ @Inject(
+ method = "addDefaultLayers",
+ at = @At(value = "TAIL")
+ )
+ private static void addLayersAfterAll(EnvironmentAttributeSystem.Builder builder, Level level, CallbackInfo ci) {
+ EnvironmentAttributeEventsImpl.insertLayers(AttributeLayerPosition.AFTER_ALL, builder, level);
+ }
+}
diff --git a/fabric-environment-attributes-v0/src/main/resources/assets/fabric-environment-attributes-v0/icon.png b/fabric-environment-attributes-v0/src/main/resources/assets/fabric-environment-attributes-v0/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..12c4531de92fe0544a0e15347f84381c132a7feb
GIT binary patch
literal 1555
zcmbVMONbmr817YeS0`Bvm<_8$sdPkC($if%&o0utJLwrS1L<{`3}loX(o5`x6Ep1Z4+am|^rTyU-RHPnPe)^ld+*!=$4&3I
z>W!eGA48bhNyDT~k_>H^p*imGQs^3Zl?0$k+Loj8KVQ7W1ItwT6B%97U5#|C^1vg<
z2P<_vSjCFTFD-(@Az}nJ2@DY0UB^eE$`5%FTSvzt4~CFnRpkqjLeS8wK%*W3nPgVL
zFfD_el7v}Fk<*8OEWw;8R1=sseC60TqKJ9em~hy
zC8^gIp`s|FB#W{vFofW*JAn}jj(>2%P$WL~EH|*I10qJFN!J3EXO@m!u-%x}@yB6e
z0TV;R6=70}Tp9vR9OK+IuRBz3Vv%%-O`O1ISQum74h^W^p?^aii~pp6g;v*N9S^m|
zwqq53Q0g%^#sPUK+OMy>M63~?u6dZ0dd$p&kvA^VJYodYt5e#YJXCdJGSIZ>Ve;Um
z6P9DrzW?%$JEUj?MCBv70A&GY>oAKlX#{hElt+>@g6hlcrFNcIG_C5bC_#`dML=rKZ5X)F-wenaX=`<9S6l@7;C1a*r
zFpYEBk-O}Ek>a%|3nur?|9Ss4&tg?*bRU@~s?8{UP}%a?!>*63=Qw$Dyy{wDm@&w}
zQ6;E6j#7Y_{P@^<utq@Kv^7o)Nrxg!46%b{#X
ziBI6HZ$!(uVX;lz@`%IwoW~m4((?!2X3g;ZO0iH6ziV#JEKKiv@r~aK;fD%m+`qeP
zqj!gUd>JIZzIXQGa=BdwQ+vS+TaVp2d4HjC{TKZ1T^OEP^h*yupZ((7eYf^3ZRqE(
zWln<`?)nV)~_M|=0.18.4",
+ "minecraft": ">=1.16-rc.3",
+ "fabric-api-base": "*"
+ },
+ "description": "Fabric Environment Attributes API.",
+ "mixins": [
+ "fabric-environment-attributes-v0.mixins.json"
+ ],
+ "custom": {
+ "fabric-api:module-lifecycle": "stable"
+ }
+}
diff --git a/fabric-environment-attributes-v0/src/testmod/java/net/fabricmc/fabric/test/environment/attribute/FabricEnvironmentAttributesTest.java b/fabric-environment-attributes-v0/src/testmod/java/net/fabricmc/fabric/test/environment/attribute/FabricEnvironmentAttributesTest.java
new file mode 100644
index 00000000000..1d7427a8994
--- /dev/null
+++ b/fabric-environment-attributes-v0/src/testmod/java/net/fabricmc/fabric/test/environment/attribute/FabricEnvironmentAttributesTest.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.test.environment.attribute;
+
+import net.minecraft.core.Registry;
+import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.resources.Identifier;
+import net.minecraft.world.attribute.AttributeTypes;
+import net.minecraft.world.attribute.EnvironmentAttribute;
+
+import net.fabricmc.api.ModInitializer;
+
+public class FabricEnvironmentAttributesTest implements ModInitializer {
+ public static final EnvironmentAttribute TEST_COLOR = EnvironmentAttribute.builder(AttributeTypes.RGB_COLOR)
+ .defaultValue(0xFFFFFF)
+ .syncable()
+ .build();
+
+ @Override
+ public void onInitialize() {
+ Registry.register(BuiltInRegistries.ENVIRONMENT_ATTRIBUTE, Identifier.fromNamespaceAndPath("fabric_environment_attributes", "test_color"), TEST_COLOR);
+ }
+}
diff --git a/fabric-environment-attributes-v0/src/testmod/resources/fabric.mod.json b/fabric-environment-attributes-v0/src/testmod/resources/fabric.mod.json
new file mode 100644
index 00000000000..897dd1bb2a7
--- /dev/null
+++ b/fabric-environment-attributes-v0/src/testmod/resources/fabric.mod.json
@@ -0,0 +1,19 @@
+{
+ "schemaVersion": 1,
+ "id": "fabric-environment-attributes-v0-testmod",
+ "name": "Fabric Environment Attributes (v0) Test Mod",
+ "version": "1.0.0",
+ "environment": "*",
+ "license": "Apache-2.0",
+ "depends": {
+ "fabric-environment-attributes-v0": "*"
+ },
+ "entrypoints": {
+ "main": [
+ "net.fabricmc.fabric.test.environment.attribute.FabricEnvironmentAttributesTest"
+ ],
+ "fabric-client-gametest": [
+ "net.fabricmc.fabric.test.environment.attribute.client.FabricEnvironmentAttributesClientTest"
+ ]
+ }
+}
diff --git a/fabric-environment-attributes-v0/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java b/fabric-environment-attributes-v0/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java
new file mode 100644
index 00000000000..91552bc8195
--- /dev/null
+++ b/fabric-environment-attributes-v0/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.test.environment.attribute.client;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.attribute.EnvironmentAttributes;
+import net.minecraft.world.level.Level;
+
+import net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest;
+import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext;
+import net.fabricmc.fabric.api.client.gametest.v1.context.TestSingleplayerContext;
+import net.fabricmc.fabric.api.environment.attribute.v0.AttributeLayerPosition;
+import net.fabricmc.fabric.api.environment.attribute.v0.EnvironmentAttributeEvents;
+import net.fabricmc.fabric.test.environment.attribute.FabricEnvironmentAttributesTest;
+
+public class FabricEnvironmentAttributesClientTest implements FabricClientGameTest {
+ public static final int TEST_COLOR = 0xFFFF00FF;
+
+ @Override
+ public void runTest(ClientGameTestContext context) {
+ EnvironmentAttributeEvents.insertLayersEvent(AttributeLayerPosition.BEFORE_ALL).register((systemBuilder, level) -> {
+ // Test color is not overridden in any way, we should see the layer
+ systemBuilder.addConstantLayer(FabricEnvironmentAttributesTest.TEST_COLOR, base -> TEST_COLOR);
+
+ // Cloud color is overridden in overworld dimension, we should not see it
+ systemBuilder.addConstantLayer(EnvironmentAttributes.CLOUD_COLOR, base -> TEST_COLOR);
+ });
+
+ EnvironmentAttributeEvents.insertLayersEvent(AttributeLayerPosition.AFTER_ALL).register((systemBuilder, level) -> {
+ systemBuilder.addConstantLayer(EnvironmentAttributes.SKY_COLOR, base -> TEST_COLOR);
+ });
+
+ try (TestSingleplayerContext spContext = context.worldBuilder().create()) {
+ spContext.getServer().runOnServer(server -> {
+ ServerLevel overworld = server.getLevel(Level.OVERWORLD);
+ int testColor = overworld.environmentAttributes().getValue(FabricEnvironmentAttributesTest.TEST_COLOR, BlockPos.ZERO);
+ int cloudColor = overworld.environmentAttributes().getValue(EnvironmentAttributes.CLOUD_COLOR, BlockPos.ZERO);
+ int skyColor = overworld.environmentAttributes().getValue(EnvironmentAttributes.SKY_COLOR, BlockPos.ZERO);
+
+ if (testColor != TEST_COLOR) {
+ throw new AssertionError("Expected test color to be (%d) but was (%d)".formatted(TEST_COLOR, testColor));
+ }
+
+ if (cloudColor == TEST_COLOR) {
+ throw new AssertionError("Expected cloud color to not be (%d), but it was".formatted(TEST_COLOR));
+ }
+
+ if (skyColor != TEST_COLOR) {
+ throw new AssertionError("Expected sky color to be (%d) but was (%d)".formatted(TEST_COLOR, skyColor));
+ }
+ });
+ }
+ }
+}
diff --git a/gradle.properties b/gradle.properties
index 8a8b11b2f5c..7f479645fe9 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -25,6 +25,7 @@ fabric-data-generation-api-v1-version=24.0.9
fabric-debug-api-v1-version=1.0.0
fabric-dimensions-v1-version=5.1.1
fabric-entity-events-v1-version=5.0.0
+fabric-environment-attributes-v0-version=1.0.0
fabric-events-interaction-v0-version=5.1.4
fabric-game-rule-api-v1-version=4.0.3
fabric-gametest-api-v1-version=4.0.6
diff --git a/settings.gradle b/settings.gradle
index 8e249715bc4..edb0db1b8f6 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -40,6 +40,7 @@ include 'fabric-data-generation-api-v1'
include 'fabric-debug-api-v1'
include 'fabric-dimensions-v1'
include 'fabric-entity-events-v1'
+include 'fabric-environment-attributes-v0'
include 'fabric-events-interaction-v0'
include 'fabric-game-rule-api-v1'
include 'fabric-gametest-api-v1'
From 02d8b0305f1e9ac821c9a45ee8b734f7ab1b7864 Mon Sep 17 00:00:00 2001
From: Samu
Date: Mon, 9 Feb 2026 15:01:26 +0100
Subject: [PATCH 02/10] Change v0 into v1
---
.../build.gradle | 0
.../attribute/v1}/AttributeLayerPosition.java | 2 +-
.../attribute/v1}/EnvironmentAttributeEvents.java | 2 +-
.../attribute/EnvironmentAttributeEventsImpl.java | 4 ++--
.../attribute/EnvironmentAttributeSystemMixin.java | 2 +-
.../fabric-environment-attributes-v1}/icon.png | Bin
.../fabric-environment-attributes-v1.mixins.json | 0
.../src/main/resources/fabric.mod.json | 8 ++++----
.../attribute/FabricEnvironmentAttributesTest.java | 0
.../src/testmod/resources/fabric.mod.json | 6 +++---
.../FabricEnvironmentAttributesClientTest.java | 4 ++--
gradle.properties | 2 +-
settings.gradle | 2 +-
13 files changed, 16 insertions(+), 16 deletions(-)
rename {fabric-environment-attributes-v0 => fabric-environment-attributes-v1}/build.gradle (100%)
rename {fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/api/environment/attribute/v0 => fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1}/AttributeLayerPosition.java (95%)
rename {fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/api/environment/attribute/v0 => fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1}/EnvironmentAttributeEvents.java (98%)
rename {fabric-environment-attributes-v0 => fabric-environment-attributes-v1}/src/main/java/net/fabricmc/fabric/impl/environment/attribute/EnvironmentAttributeEventsImpl.java (94%)
rename {fabric-environment-attributes-v0 => fabric-environment-attributes-v1}/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java (97%)
rename {fabric-environment-attributes-v0/src/main/resources/assets/fabric-environment-attributes-v0 => fabric-environment-attributes-v1/src/main/resources/assets/fabric-environment-attributes-v1}/icon.png (100%)
rename fabric-environment-attributes-v0/src/main/resources/fabric-environment-attributes-v0.mixins.json => fabric-environment-attributes-v1/src/main/resources/fabric-environment-attributes-v1.mixins.json (100%)
rename {fabric-environment-attributes-v0 => fabric-environment-attributes-v1}/src/main/resources/fabric.mod.json (74%)
rename {fabric-environment-attributes-v0 => fabric-environment-attributes-v1}/src/testmod/java/net/fabricmc/fabric/test/environment/attribute/FabricEnvironmentAttributesTest.java (100%)
rename {fabric-environment-attributes-v0 => fabric-environment-attributes-v1}/src/testmod/resources/fabric.mod.json (71%)
rename {fabric-environment-attributes-v0 => fabric-environment-attributes-v1}/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java (96%)
diff --git a/fabric-environment-attributes-v0/build.gradle b/fabric-environment-attributes-v1/build.gradle
similarity index 100%
rename from fabric-environment-attributes-v0/build.gradle
rename to fabric-environment-attributes-v1/build.gradle
diff --git a/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/api/environment/attribute/v0/AttributeLayerPosition.java b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerPosition.java
similarity index 95%
rename from fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/api/environment/attribute/v0/AttributeLayerPosition.java
rename to fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerPosition.java
index cbbf7cc1659..0a004a86d05 100644
--- a/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/api/environment/attribute/v0/AttributeLayerPosition.java
+++ b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerPosition.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package net.fabricmc.fabric.api.environment.attribute.v0;
+package net.fabricmc.fabric.api.environment.attribute.v1;
/**
* Positions at which environment attribute layers can be inserted. Each position is associated with an event in
diff --git a/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/api/environment/attribute/v0/EnvironmentAttributeEvents.java b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/EnvironmentAttributeEvents.java
similarity index 98%
rename from fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/api/environment/attribute/v0/EnvironmentAttributeEvents.java
rename to fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/EnvironmentAttributeEvents.java
index 8fb8f0b4729..6738a0cb673 100644
--- a/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/api/environment/attribute/v0/EnvironmentAttributeEvents.java
+++ b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/EnvironmentAttributeEvents.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package net.fabricmc.fabric.api.environment.attribute.v0;
+package net.fabricmc.fabric.api.environment.attribute.v1;
import net.minecraft.world.attribute.EnvironmentAttributeSystem;
import net.minecraft.world.level.Level;
diff --git a/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/impl/environment/attribute/EnvironmentAttributeEventsImpl.java b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/EnvironmentAttributeEventsImpl.java
similarity index 94%
rename from fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/impl/environment/attribute/EnvironmentAttributeEventsImpl.java
rename to fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/EnvironmentAttributeEventsImpl.java
index f93960f5f81..7f3f255ac05 100644
--- a/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/impl/environment/attribute/EnvironmentAttributeEventsImpl.java
+++ b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/EnvironmentAttributeEventsImpl.java
@@ -24,8 +24,8 @@
import net.minecraft.world.attribute.EnvironmentAttributeSystem;
import net.minecraft.world.level.Level;
-import net.fabricmc.fabric.api.environment.attribute.v0.AttributeLayerPosition;
-import net.fabricmc.fabric.api.environment.attribute.v0.EnvironmentAttributeEvents;
+import net.fabricmc.fabric.api.environment.attribute.v1.AttributeLayerPosition;
+import net.fabricmc.fabric.api.environment.attribute.v1.EnvironmentAttributeEvents;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
diff --git a/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java
similarity index 97%
rename from fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java
rename to fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java
index 01f6bfe52de..3244ba41130 100644
--- a/fabric-environment-attributes-v0/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java
+++ b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java
@@ -24,7 +24,7 @@
import net.minecraft.world.attribute.EnvironmentAttributeSystem;
import net.minecraft.world.level.Level;
-import net.fabricmc.fabric.api.environment.attribute.v0.AttributeLayerPosition;
+import net.fabricmc.fabric.api.environment.attribute.v1.AttributeLayerPosition;
import net.fabricmc.fabric.impl.environment.attribute.EnvironmentAttributeEventsImpl;
@Mixin(EnvironmentAttributeSystem.class)
diff --git a/fabric-environment-attributes-v0/src/main/resources/assets/fabric-environment-attributes-v0/icon.png b/fabric-environment-attributes-v1/src/main/resources/assets/fabric-environment-attributes-v1/icon.png
similarity index 100%
rename from fabric-environment-attributes-v0/src/main/resources/assets/fabric-environment-attributes-v0/icon.png
rename to fabric-environment-attributes-v1/src/main/resources/assets/fabric-environment-attributes-v1/icon.png
diff --git a/fabric-environment-attributes-v0/src/main/resources/fabric-environment-attributes-v0.mixins.json b/fabric-environment-attributes-v1/src/main/resources/fabric-environment-attributes-v1.mixins.json
similarity index 100%
rename from fabric-environment-attributes-v0/src/main/resources/fabric-environment-attributes-v0.mixins.json
rename to fabric-environment-attributes-v1/src/main/resources/fabric-environment-attributes-v1.mixins.json
diff --git a/fabric-environment-attributes-v0/src/main/resources/fabric.mod.json b/fabric-environment-attributes-v1/src/main/resources/fabric.mod.json
similarity index 74%
rename from fabric-environment-attributes-v0/src/main/resources/fabric.mod.json
rename to fabric-environment-attributes-v1/src/main/resources/fabric.mod.json
index b68ab5279dc..23d53115601 100644
--- a/fabric-environment-attributes-v0/src/main/resources/fabric.mod.json
+++ b/fabric-environment-attributes-v1/src/main/resources/fabric.mod.json
@@ -1,10 +1,10 @@
{
"schemaVersion": 1,
- "id": "fabric-environment-attributes-v0",
- "name": "Fabric Environment Attributes API (v0)",
+ "id": "fabric-environment-attributes-v1",
+ "name": "Fabric Environment Attributes API (v1)",
"version": "${version}",
"license": "Apache-2.0",
- "icon": "assets/fabric-environment-attributes-v0/icon.png",
+ "icon": "assets/fabric-environment-attributes-v1/icon.png",
"contact" : {
"homepage": "https://fabricmc.net",
"irc": "irc://irc.esper.net:6667/fabric",
@@ -23,7 +23,7 @@
},
"description": "Fabric Environment Attributes API.",
"mixins": [
- "fabric-environment-attributes-v0.mixins.json"
+ "fabric-environment-attributes-v1.mixins.json"
],
"custom": {
"fabric-api:module-lifecycle": "stable"
diff --git a/fabric-environment-attributes-v0/src/testmod/java/net/fabricmc/fabric/test/environment/attribute/FabricEnvironmentAttributesTest.java b/fabric-environment-attributes-v1/src/testmod/java/net/fabricmc/fabric/test/environment/attribute/FabricEnvironmentAttributesTest.java
similarity index 100%
rename from fabric-environment-attributes-v0/src/testmod/java/net/fabricmc/fabric/test/environment/attribute/FabricEnvironmentAttributesTest.java
rename to fabric-environment-attributes-v1/src/testmod/java/net/fabricmc/fabric/test/environment/attribute/FabricEnvironmentAttributesTest.java
diff --git a/fabric-environment-attributes-v0/src/testmod/resources/fabric.mod.json b/fabric-environment-attributes-v1/src/testmod/resources/fabric.mod.json
similarity index 71%
rename from fabric-environment-attributes-v0/src/testmod/resources/fabric.mod.json
rename to fabric-environment-attributes-v1/src/testmod/resources/fabric.mod.json
index 897dd1bb2a7..843bcd0f1d1 100644
--- a/fabric-environment-attributes-v0/src/testmod/resources/fabric.mod.json
+++ b/fabric-environment-attributes-v1/src/testmod/resources/fabric.mod.json
@@ -1,12 +1,12 @@
{
"schemaVersion": 1,
- "id": "fabric-environment-attributes-v0-testmod",
- "name": "Fabric Environment Attributes (v0) Test Mod",
+ "id": "fabric-environment-attributes-v1-testmod",
+ "name": "Fabric Environment Attributes (v1) Test Mod",
"version": "1.0.0",
"environment": "*",
"license": "Apache-2.0",
"depends": {
- "fabric-environment-attributes-v0": "*"
+ "fabric-environment-attributes-v1": "*"
},
"entrypoints": {
"main": [
diff --git a/fabric-environment-attributes-v0/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java b/fabric-environment-attributes-v1/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java
similarity index 96%
rename from fabric-environment-attributes-v0/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java
rename to fabric-environment-attributes-v1/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java
index 91552bc8195..743609670d2 100644
--- a/fabric-environment-attributes-v0/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java
+++ b/fabric-environment-attributes-v1/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java
@@ -24,8 +24,8 @@
import net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest;
import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext;
import net.fabricmc.fabric.api.client.gametest.v1.context.TestSingleplayerContext;
-import net.fabricmc.fabric.api.environment.attribute.v0.AttributeLayerPosition;
-import net.fabricmc.fabric.api.environment.attribute.v0.EnvironmentAttributeEvents;
+import net.fabricmc.fabric.api.environment.attribute.v1.AttributeLayerPosition;
+import net.fabricmc.fabric.api.environment.attribute.v1.EnvironmentAttributeEvents;
import net.fabricmc.fabric.test.environment.attribute.FabricEnvironmentAttributesTest;
public class FabricEnvironmentAttributesClientTest implements FabricClientGameTest {
diff --git a/gradle.properties b/gradle.properties
index 7f479645fe9..1a3509bdeca 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -25,7 +25,7 @@ fabric-data-generation-api-v1-version=24.0.9
fabric-debug-api-v1-version=1.0.0
fabric-dimensions-v1-version=5.1.1
fabric-entity-events-v1-version=5.0.0
-fabric-environment-attributes-v0-version=1.0.0
+fabric-environment-attributes-v1-version=1.0.0
fabric-events-interaction-v0-version=5.1.4
fabric-game-rule-api-v1-version=4.0.3
fabric-gametest-api-v1-version=4.0.6
diff --git a/settings.gradle b/settings.gradle
index edb0db1b8f6..042b4bb7640 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -40,7 +40,7 @@ include 'fabric-data-generation-api-v1'
include 'fabric-debug-api-v1'
include 'fabric-dimensions-v1'
include 'fabric-entity-events-v1'
-include 'fabric-environment-attributes-v0'
+include 'fabric-environment-attributes-v1'
include 'fabric-events-interaction-v0'
include 'fabric-game-rule-api-v1'
include 'fabric-gametest-api-v1'
From d05868b7ad3cf285786e44cc727f2480618b126d Mon Sep 17 00:00:00 2001
From: Samu
Date: Tue, 10 Feb 2026 22:11:40 +0100
Subject: [PATCH 03/10] Replace events with proper sortable registry.
---
.../attribute/v1/AttributeLayerPosition.java | 48 ---
.../attribute/v1/AttributeLayerProvider.java | 84 ++++++
.../attribute/v1/AttributeLayerRegistry.java | 50 ++++
.../v1/EnvironmentAttributeEvents.java | 64 ----
.../attribute/AttributeLayerRegistryImpl.java | 274 ++++++++++++++++++
.../EnvironmentAttributeEventsImpl.java | 55 ----
.../EnvironmentAttributeSystemMixin.java | 15 +-
...FabricEnvironmentAttributesClientTest.java | 15 +-
8 files changed, 426 insertions(+), 179 deletions(-)
delete mode 100644 fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerPosition.java
create mode 100644 fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java
create mode 100644 fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerRegistry.java
delete mode 100644 fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/EnvironmentAttributeEvents.java
create mode 100644 fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/AttributeLayerRegistryImpl.java
delete mode 100644 fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/EnvironmentAttributeEventsImpl.java
diff --git a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerPosition.java b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerPosition.java
deleted file mode 100644
index 0a004a86d05..00000000000
--- a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerPosition.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.environment.attribute.v1;
-
-/**
- * Positions at which environment attribute layers can be inserted. Each position is associated with an event in
- * {@link EnvironmentAttributeEvents}. The enum is ordered by in which order Minecraft adds its default layers.
- */
-public enum AttributeLayerPosition {
- /**
- * The position before all vanilla layers.
- */
- BEFORE_ALL,
-
- /**
- * The position between dimension and biome layers.
- */
- BETWEEN_DIMENSION_AND_BIOMES,
-
- /**
- * The position between biome and timeline layers.
- */
- BETWEEN_BIOMES_AND_TIMELINES,
-
- /**
- * The position between timeline and weather layers.
- */
- BETWEEN_TIMELINES_AND_WEATHER,
-
- /**
- * The position after all vanilla layers.
- */
- AFTER_ALL
-}
diff --git a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java
new file mode 100644
index 00000000000..422ab0f7686
--- /dev/null
+++ b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java
@@ -0,0 +1,84 @@
+/*
+ * 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.environment.attribute.v1;
+
+import net.minecraft.resources.Identifier;
+import net.minecraft.world.attribute.EnvironmentAttributeLayer;
+import net.minecraft.world.attribute.EnvironmentAttributeSystem;
+import net.minecraft.world.level.Level;
+
+/**
+ * Provides {@link EnvironmentAttributeLayer}s to an {@link EnvironmentAttributeSystem}. You may register custom
+ * {@link AttributeLayerProvider} implementations using {@link AttributeLayerRegistry#registerLayerProvider}.
+ *
+ *
+ * Attribute layers can be ordered relative to vanilla's layers or other modded layers using
+ * {@link AttributeLayerRegistry#addLayerOrdering}. The order defines which layers override which other layers: layers
+ * that come first in the ordering are overriden by layers that come later in the ordering. For example, in vanilla,
+ * biome layers come after dimension layers, since biome-local attributes override dimension-global attributes.
+ *
+ *
+ *
+ * Minecraft adds layers in four phases: dimension-global attributes, then biome-local attributes, then
+ * timeline-interpolated attributes, and finally some hardcoded weather attributes. Each of these phases, as well modded
+ * layer providers, are associated with an identifier that can be sorted against.
+ *
+ */
+public interface AttributeLayerProvider {
+ /**
+ * Identifier associated to vanilla's dimension attribute layers.
+ */
+ Identifier DIMENSION = Identifier.withDefaultNamespace("dimensions");
+
+ /**
+ * Identifier associated to vanilla's biome attribute layers.
+ */
+ Identifier BIOMES = Identifier.withDefaultNamespace("biomes");
+
+ /**
+ * Identifier associated to vanilla's timeline attribute layers.
+ */
+ Identifier TIMELINES = Identifier.withDefaultNamespace("timelines");
+
+ /**
+ * Identifier associated to vanilla's weather attribute layers.
+ */
+ Identifier WEATHER = Identifier.withDefaultNamespace("weather");
+
+ /**
+ * The identifier associated to the first vanilla phase. Currently, that is {@link #DIMENSION}.
+ * This constant exists purely for compatibility: if Minecraft ever adds another layer before its dimension phase,
+ * then this constant is updated.
+ */
+ Identifier FIRST_VANILLA_PHASE = DIMENSION;
+
+ /**
+ * The identifier associated to the last vanilla phase. Currently, that is {@link #WEATHER}.
+ * This constant exists purely for compatibility: if Minecraft ever adds another layer after its weather phase,
+ * then this constant is updated.
+ */
+ Identifier LAST_VANILLA_PHASE = WEATHER;
+
+ /**
+ * Called to add attribute layers to an {@link EnvironmentAttributeSystem.Builder} for the given {@link Level}.
+ * This is called both on the client and on the server for every {@link Level} that is created.
+ *
+ * @param systemBuilder The {@link EnvironmentAttributeSystem.Builder} to add layers to.
+ * @param level The {@link Level} that the environment attribute system is being created for.
+ */
+ void addAttributeLayers(EnvironmentAttributeSystem.Builder systemBuilder, Level level);
+}
diff --git a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerRegistry.java b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerRegistry.java
new file mode 100644
index 00000000000..bda6521402a
--- /dev/null
+++ b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerRegistry.java
@@ -0,0 +1,50 @@
+/*
+ * 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.environment.attribute.v1;
+
+import org.jspecify.annotations.NullMarked;
+
+import net.minecraft.resources.Identifier;
+
+import net.fabricmc.fabric.impl.environment.attribute.AttributeLayerRegistryImpl;
+
+@NullMarked
+public class AttributeLayerRegistry {
+ /**
+ * Register a {@link AttributeLayerProvider}. If a layer with the given identifier already exists, an exception
+ * is thrown.
+ *
+ * @param id The identifier of the layer provider. This can be ordered against by other layer providers.
+ * @param layer The layer provider to register.
+ */
+ public static void registerLayerProvider(Identifier id, AttributeLayerProvider layer) {
+ AttributeLayerRegistryImpl.registerLayerProvider(id, layer);
+ }
+
+ /**
+ * Declares that the layer provider with the first identifier should activate before the layer provider with the
+ * second identifier. Unless this causes a cyclic dependency, the two layer providers are guaranteed to activate in
+ * said order. You may use this to order your layer provider against vanilla phases using any of the constants in
+ * {@link AttributeLayerProvider}. If both layer identifiers are the same, then an exception is thrown.
+ *
+ * @param firstLayer The ID of the layer that should activate earlier.
+ * @param secondLayer The ID of the layer that should activate later.
+ */
+ public static void addLayerOrdering(Identifier firstLayer, Identifier secondLayer) {
+ AttributeLayerRegistryImpl.addLayerOrdering(firstLayer, secondLayer);
+ }
+}
diff --git a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/EnvironmentAttributeEvents.java b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/EnvironmentAttributeEvents.java
deleted file mode 100644
index 6738a0cb673..00000000000
--- a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/EnvironmentAttributeEvents.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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.environment.attribute.v1;
-
-import net.minecraft.world.attribute.EnvironmentAttributeSystem;
-import net.minecraft.world.level.Level;
-
-import net.fabricmc.fabric.api.event.Event;
-import net.fabricmc.fabric.impl.environment.attribute.EnvironmentAttributeEventsImpl;
-
-/**
- * Events related to environment attributes.
- */
-public class EnvironmentAttributeEvents {
- /**
- * Returns the {@link InsertLayers} event for the given {@link AttributeLayerPosition}. This event allows inserting
- * extra attribute layers at that position during the environment attribute setup for a {@link Level}. By default,
- * Minecraft adds layers for the dimension, then for biomes, then for timelines and lastly for weather. The event
- * returned by this method is triggered before, in between and after each of these vanilla layers, depending on the
- * selected position, in the following manner:
- *
- * - Event for {@link AttributeLayerPosition#BEFORE_ALL} is triggered.
- * - Minecraft adds the layer for dimension type attribute configurations.
- * - Event for {@link AttributeLayerPosition#BETWEEN_DIMENSION_AND_BIOMES} is triggered.
- * - Minecraft adds the layer for biome attribute configurations.
- * - Event for {@link AttributeLayerPosition#BETWEEN_BIOMES_AND_TIMELINES} is triggered.
- * - Minecraft adds the layer for timeline attribute animations.
- * - Event for {@link AttributeLayerPosition#BETWEEN_TIMELINES_AND_WEATHER} is triggered.
- * - Minecraft adds the layer for some hardcoded weather attribute overrides.
- * - Event for {@link AttributeLayerPosition#AFTER_ALL} is triggered.
- *
- * @param position The position at which you want to insert attribute layers.
- * @return The event to listen for layer setup.
- */
- public static Event insertLayersEvent(AttributeLayerPosition position) {
- return EnvironmentAttributeEventsImpl.getOrCreateInsertLayersEvent(position);
- }
-
- /**
- * Callback for events returned from {@link #insertLayersEvent}.
- */
- public interface InsertLayers {
- /**
- * Insert custom attribute layers into the {@link EnvironmentAttributeSystem.Builder}.
- * @param systemBuilder The environment attribute system builder to modify.
- * @param level The level for which the attribute system is built.
- */
- void insertAttributeLayers(EnvironmentAttributeSystem.Builder systemBuilder, Level level);
- }
-}
diff --git a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/AttributeLayerRegistryImpl.java b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/AttributeLayerRegistryImpl.java
new file mode 100644
index 00000000000..0433e2107c6
--- /dev/null
+++ b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/AttributeLayerRegistryImpl.java
@@ -0,0 +1,274 @@
+/*
+ * 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.impl.environment.attribute;
+
+import java.util.ArrayList;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import net.minecraft.resources.Identifier;
+import net.minecraft.world.attribute.EnvironmentAttributeSystem;
+import net.minecraft.world.level.Level;
+
+import net.fabricmc.fabric.api.environment.attribute.v1.AttributeLayerProvider;
+import net.fabricmc.fabric.impl.base.toposort.NodeSorting;
+import net.fabricmc.fabric.impl.base.toposort.SortableNode;
+
+public class AttributeLayerRegistryImpl {
+ // Markes for each vanilla phase. Used to ensure vanilla ordering remains the same.
+ private static final Map MARKERS = Map.copyOf(Stream.of(VanillaLayerMarker.values())
+ .collect(Collectors.toMap(marker -> marker.id, marker -> marker)));
+
+ private static final Map LAYER_MAP = new HashMap<>();
+ private static final Set DEPENDENCIES = new HashSet<>();
+
+ // Lock used to ensure thread safety.
+ private static final Object LOCK = new Object();
+
+ // As long as this is true, we skip sorting and inserting layers all together.
+ // Becomes false once a modded layer is registered.
+ private static volatile boolean hasOnlyVanillaLayers;
+
+ // As long as this is true, the ordering in the fields below is valid.
+ // Becomes false once a modded layer is registered or once a depencency order is added.
+ private static volatile boolean orderValid;
+
+ // Layers that should go before vanilla layers.
+ private static final List FIRST_PHASES = new ArrayList<>();
+
+ // Layers that should go in between or after vanilla layers.
+ private static final Map> AFTER_VANILLA_PHASES = new EnumMap<>(VanillaLayerMarker.class);
+
+ static {
+ // Initialize sorted phase lists
+ for (VanillaLayerMarker layer : VanillaLayerMarker.values()) {
+ AFTER_VANILLA_PHASES.put(layer, new ArrayList<>());
+ }
+
+ // Register vanilla ordering
+ registerLayerProvider(AttributeLayerProvider.DIMENSION, VanillaLayerMarker.DIMENSION);
+ registerLayerProvider(AttributeLayerProvider.BIOMES, VanillaLayerMarker.BIOMES);
+ registerLayerProvider(AttributeLayerProvider.TIMELINES, VanillaLayerMarker.TIMELINES);
+ registerLayerProvider(AttributeLayerProvider.WEATHER, VanillaLayerMarker.WEATHER);
+
+ addLayerOrdering(AttributeLayerProvider.DIMENSION, AttributeLayerProvider.BIOMES);
+ addLayerOrdering(AttributeLayerProvider.BIOMES, AttributeLayerProvider.TIMELINES);
+ addLayerOrdering(AttributeLayerProvider.TIMELINES, AttributeLayerProvider.WEATHER);
+
+ // Validate cache
+ hasOnlyVanillaLayers = true; // Set to true here because registerLayerProvider used above sets it to false
+ orderValid = true; // Vanilla layers are not included in sorted phase lists
+ }
+
+ public static void registerLayerProvider(Identifier id, AttributeLayerProvider layer) {
+ Objects.requireNonNull(id, "The layer identifier should not be null.");
+ Objects.requireNonNull(layer, "The layer should not be null.");
+
+ if (LAYER_MAP.containsKey(id)) {
+ throw new IllegalArgumentException("Layer with ID %s was already registered.".formatted(id));
+ }
+
+ synchronized (LOCK) {
+ LAYER_MAP.put(id, layer);
+ orderValid = false;
+ hasOnlyVanillaLayers = false;
+ }
+ }
+
+ public static void addLayerOrdering(Identifier firstLayer, Identifier secondLayer) {
+ Objects.requireNonNull(firstLayer, "The first layer identifier should not be null.");
+ Objects.requireNonNull(secondLayer, "The second layer identifier should not be null.");
+
+ if (firstLayer.equals(secondLayer)) {
+ throw new IllegalArgumentException("Tried to add a layer that depends on itself.");
+ }
+
+ synchronized (LOCK) {
+ if (DEPENDENCIES.add(new Dependency(firstLayer, secondLayer))) {
+ // Adding a dependency only affects order if both IDs are associated with registered layers.
+ // Dependencies with missing registrations are simply ignored during sorting.
+
+ if (LAYER_MAP.containsKey(firstLayer) && LAYER_MAP.containsKey(secondLayer)) {
+ orderValid = false;
+ }
+ }
+ }
+ }
+
+ private static void addLayers(List providers, EnvironmentAttributeSystem.Builder systemBuilder, Level level) {
+ synchronized (LOCK) {
+ for (AttributeLayerProvider provider : providers) {
+ provider.addAttributeLayers(systemBuilder, level);
+ }
+ }
+ }
+
+ public static void addPreEverythingLayers(EnvironmentAttributeSystem.Builder systemBuilder, Level level) {
+ if (!hasOnlyVanillaLayers) {
+ sortIfNeeded();
+ addLayers(FIRST_PHASES, systemBuilder, level);
+ }
+ }
+
+ public static void addPostDimensionLayers(EnvironmentAttributeSystem.Builder systemBuilder, Level level) {
+ if (!hasOnlyVanillaLayers) {
+ sortIfNeeded();
+ addLayers(AFTER_VANILLA_PHASES.get(VanillaLayerMarker.DIMENSION), systemBuilder, level);
+ }
+ }
+
+ public static void addPostBiomesLayers(EnvironmentAttributeSystem.Builder systemBuilder, Level level) {
+ if (!hasOnlyVanillaLayers) {
+ sortIfNeeded();
+ addLayers(AFTER_VANILLA_PHASES.get(VanillaLayerMarker.BIOMES), systemBuilder, level);
+ }
+ }
+
+ public static void addPostTimelinesLayers(EnvironmentAttributeSystem.Builder systemBuilder, Level level) {
+ if (!hasOnlyVanillaLayers) {
+ sortIfNeeded();
+ addLayers(AFTER_VANILLA_PHASES.get(VanillaLayerMarker.TIMELINES), systemBuilder, level);
+ }
+ }
+
+ public static void addPostWeatherLayers(EnvironmentAttributeSystem.Builder systemBuilder, Level level) {
+ if (!hasOnlyVanillaLayers) {
+ sortIfNeeded();
+ addLayers(AFTER_VANILLA_PHASES.get(VanillaLayerMarker.WEATHER), systemBuilder, level);
+ }
+ }
+
+ private static void sortIfNeeded() {
+ Map layers;
+
+ // Collect sorting data from registry
+ synchronized (LOCK) {
+ if (orderValid) {
+ return;
+ }
+
+ layers = new HashMap<>();
+
+ for (Map.Entry entry : LAYER_MAP.entrySet()) {
+ layers.put(entry.getKey(), new Layer(entry.getKey(), entry.getValue()));
+ }
+
+ for (Dependency dependency : DEPENDENCIES) {
+ Layer firstLayer = layers.get(dependency.firstLayer());
+ Layer secondLayer = layers.get(dependency.secondLayer());
+
+ if (firstLayer != null && secondLayer != null) {
+ Layer.link(firstLayer, secondLayer);
+ }
+ }
+ }
+
+ // Sort layers
+ List sorted = new ArrayList<>(layers.values());
+ NodeSorting.sort(sorted, "environment attribute layers", AttributeLayerRegistryImpl::compareIds);
+
+ // Categorize layer providers into vanilla phases
+ synchronized (LOCK) {
+ FIRST_PHASES.clear();
+ AFTER_VANILLA_PHASES.forEach((_, list) -> list.clear());
+
+ List phase = FIRST_PHASES;
+
+ for (Layer layer : sorted) {
+ AttributeLayerProvider provider = layer.provider;
+
+ if (provider instanceof VanillaLayerMarker marker) {
+ phase = AFTER_VANILLA_PHASES.get(marker);
+ } else {
+ phase.add(provider);
+ }
+ }
+ }
+ }
+
+ // Tiebreaker: put vanilla layers before others, and otherwise sort by lexicographic ordering
+ // This also makes sure that layers that were not tied to vanilla ordering will come last in the ordering
+ private static int compareIds(Layer a, Layer b) {
+ Identifier idA = a.id;
+ Identifier idB = b.id;
+
+ VanillaLayerMarker markerA = MARKERS.get(idA);
+ VanillaLayerMarker markerB = MARKERS.get(idB);
+
+ // If both are vanilla layers, ensure they remain the same order as defined by Minecraft
+ if (markerA != null && markerB != null) {
+ return markerA.compareTo(markerB);
+ }
+
+ // If one of them is a vanilla layer and the other is not, then put the vanilla layer first
+ if (markerA != null) {
+ return -1;
+ }
+
+ if (markerB != null) {
+ return 1;
+ }
+
+ // Otherwise just mess with the mod devs that like their mod IDs to start with an A
+ return idA.compareTo(idB);
+ }
+
+ // Markers for vanilla layers. It's important that these enum constants stay in the order that vanilla layers should appear,
+ // since this order will be used to fix dependency cycles (and we don't want a dependency cycle to mess up the order).
+ private enum VanillaLayerMarker implements AttributeLayerProvider {
+ DIMENSION(AttributeLayerProvider.DIMENSION),
+ BIOMES(AttributeLayerProvider.BIOMES),
+ TIMELINES(AttributeLayerProvider.TIMELINES),
+ WEATHER(AttributeLayerProvider.WEATHER);
+
+ final Identifier id;
+
+ VanillaLayerMarker(Identifier id) {
+ this.id = id;
+ }
+
+ @Override
+ public void addAttributeLayers(EnvironmentAttributeSystem.Builder systemBuilder, Level level) {
+ // N/A, done through mixin
+ }
+ }
+
+ private static class Layer extends SortableNode {
+ private final Identifier id;
+ private final AttributeLayerProvider provider;
+
+ private Layer(Identifier id, AttributeLayerProvider provider) {
+ this.id = id;
+ this.provider = provider;
+ }
+
+ @Override
+ protected String getDescription() {
+ return id.toString();
+ }
+ }
+
+ private record Dependency(Identifier firstLayer, Identifier secondLayer) {
+ }
+}
diff --git a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/EnvironmentAttributeEventsImpl.java b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/EnvironmentAttributeEventsImpl.java
deleted file mode 100644
index 7f3f255ac05..00000000000
--- a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/EnvironmentAttributeEventsImpl.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.impl.environment.attribute;
-
-import java.util.EnumMap;
-import java.util.Map;
-
-import org.jspecify.annotations.NonNull;
-
-import net.minecraft.world.attribute.EnvironmentAttributeSystem;
-import net.minecraft.world.level.Level;
-
-import net.fabricmc.fabric.api.environment.attribute.v1.AttributeLayerPosition;
-import net.fabricmc.fabric.api.environment.attribute.v1.EnvironmentAttributeEvents;
-import net.fabricmc.fabric.api.event.Event;
-import net.fabricmc.fabric.api.event.EventFactory;
-
-public class EnvironmentAttributeEventsImpl {
- private static final Map> POSITION_EVENT_MAP = new EnumMap<>(AttributeLayerPosition.class);
-
- @NonNull
- public static Event getOrCreateInsertLayersEvent(AttributeLayerPosition position) {
- return POSITION_EVENT_MAP.computeIfAbsent(position, (p -> createInsertEvent()));
- }
-
- public static void insertLayers(AttributeLayerPosition position, EnvironmentAttributeSystem.Builder systemBuilder, Level level) {
- Event event = POSITION_EVENT_MAP.get(position);
-
- if (event != null) {
- event.invoker().insertAttributeLayers(systemBuilder, level);
- }
- }
-
- private static Event createInsertEvent() {
- return EventFactory.createArrayBacked(EnvironmentAttributeEvents.InsertLayers.class, callbacks -> (systemBuilder, level) -> {
- for (EnvironmentAttributeEvents.InsertLayers callback : callbacks) {
- callback.insertAttributeLayers(systemBuilder, level);
- }
- });
- }
-}
diff --git a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java
index 3244ba41130..2afd529e377 100644
--- a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java
+++ b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java
@@ -24,8 +24,7 @@
import net.minecraft.world.attribute.EnvironmentAttributeSystem;
import net.minecraft.world.level.Level;
-import net.fabricmc.fabric.api.environment.attribute.v1.AttributeLayerPosition;
-import net.fabricmc.fabric.impl.environment.attribute.EnvironmentAttributeEventsImpl;
+import net.fabricmc.fabric.impl.environment.attribute.AttributeLayerRegistryImpl;
@Mixin(EnvironmentAttributeSystem.class)
public class EnvironmentAttributeSystemMixin {
@@ -34,7 +33,7 @@ public class EnvironmentAttributeSystemMixin {
at = @At(value = "HEAD")
)
private static void addLayersBeforeAll(EnvironmentAttributeSystem.Builder builder, Level level, CallbackInfo ci) {
- EnvironmentAttributeEventsImpl.insertLayers(AttributeLayerPosition.BEFORE_ALL, builder, level);
+ AttributeLayerRegistryImpl.addPreEverythingLayers(builder, level);
}
@Inject(
@@ -42,7 +41,7 @@ private static void addLayersBeforeAll(EnvironmentAttributeSystem.Builder builde
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/attribute/EnvironmentAttributeSystem;addBiomeLayer(Lnet/minecraft/world/attribute/EnvironmentAttributeSystem$Builder;Lnet/minecraft/core/HolderLookup;Lnet/minecraft/world/level/biome/BiomeManager;)V")
)
private static void addLayersAfterDimension(EnvironmentAttributeSystem.Builder builder, Level level, CallbackInfo ci) {
- EnvironmentAttributeEventsImpl.insertLayers(AttributeLayerPosition.BETWEEN_DIMENSION_AND_BIOMES, builder, level);
+ AttributeLayerRegistryImpl.addPostDimensionLayers(builder, level);
}
@Inject(
@@ -50,7 +49,7 @@ private static void addLayersAfterDimension(EnvironmentAttributeSystem.Builder b
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;dimensionType()Lnet/minecraft/world/level/dimension/DimensionType;")
)
private static void addLayersAfterBiomes(EnvironmentAttributeSystem.Builder builder, Level level, CallbackInfo ci) {
- EnvironmentAttributeEventsImpl.insertLayers(AttributeLayerPosition.BETWEEN_BIOMES_AND_TIMELINES, builder, level);
+ AttributeLayerRegistryImpl.addPostBiomesLayers(builder, level);
}
@Inject(
@@ -58,14 +57,14 @@ private static void addLayersAfterBiomes(EnvironmentAttributeSystem.Builder buil
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;canHaveWeather()Z")
)
private static void addLayersAfterTimelines(EnvironmentAttributeSystem.Builder builder, Level level, CallbackInfo ci) {
- EnvironmentAttributeEventsImpl.insertLayers(AttributeLayerPosition.BETWEEN_TIMELINES_AND_WEATHER, builder, level);
+ AttributeLayerRegistryImpl.addPostTimelinesLayers(builder, level);
}
@Inject(
method = "addDefaultLayers",
at = @At(value = "TAIL")
)
- private static void addLayersAfterAll(EnvironmentAttributeSystem.Builder builder, Level level, CallbackInfo ci) {
- EnvironmentAttributeEventsImpl.insertLayers(AttributeLayerPosition.AFTER_ALL, builder, level);
+ private static void addLayersAfterWeather(EnvironmentAttributeSystem.Builder builder, Level level, CallbackInfo ci) {
+ AttributeLayerRegistryImpl.addPostWeatherLayers(builder, level);
}
}
diff --git a/fabric-environment-attributes-v1/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java b/fabric-environment-attributes-v1/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java
index 743609670d2..c74845bd149 100644
--- a/fabric-environment-attributes-v1/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java
+++ b/fabric-environment-attributes-v1/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java
@@ -17,6 +17,7 @@
package net.fabricmc.fabric.test.environment.attribute.client;
import net.minecraft.core.BlockPos;
+import net.minecraft.resources.Identifier;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.attribute.EnvironmentAttributes;
import net.minecraft.world.level.Level;
@@ -24,16 +25,19 @@
import net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest;
import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext;
import net.fabricmc.fabric.api.client.gametest.v1.context.TestSingleplayerContext;
-import net.fabricmc.fabric.api.environment.attribute.v1.AttributeLayerPosition;
-import net.fabricmc.fabric.api.environment.attribute.v1.EnvironmentAttributeEvents;
+import net.fabricmc.fabric.api.environment.attribute.v1.AttributeLayerProvider;
+import net.fabricmc.fabric.api.environment.attribute.v1.AttributeLayerRegistry;
import net.fabricmc.fabric.test.environment.attribute.FabricEnvironmentAttributesTest;
public class FabricEnvironmentAttributesClientTest implements FabricClientGameTest {
public static final int TEST_COLOR = 0xFFFF00FF;
+ private static final Identifier BEFORE_ALL = Identifier.fromNamespaceAndPath("fabric", "before_all");
+ private static final Identifier AFTER_ALL = Identifier.fromNamespaceAndPath("fabric", "after_all");
+
@Override
public void runTest(ClientGameTestContext context) {
- EnvironmentAttributeEvents.insertLayersEvent(AttributeLayerPosition.BEFORE_ALL).register((systemBuilder, level) -> {
+ AttributeLayerRegistry.registerLayerProvider(BEFORE_ALL, (systemBuilder, level) -> {
// Test color is not overridden in any way, we should see the layer
systemBuilder.addConstantLayer(FabricEnvironmentAttributesTest.TEST_COLOR, base -> TEST_COLOR);
@@ -41,10 +45,13 @@ public void runTest(ClientGameTestContext context) {
systemBuilder.addConstantLayer(EnvironmentAttributes.CLOUD_COLOR, base -> TEST_COLOR);
});
- EnvironmentAttributeEvents.insertLayersEvent(AttributeLayerPosition.AFTER_ALL).register((systemBuilder, level) -> {
+ AttributeLayerRegistry.registerLayerProvider(AFTER_ALL, (systemBuilder, level) -> {
systemBuilder.addConstantLayer(EnvironmentAttributes.SKY_COLOR, base -> TEST_COLOR);
});
+ AttributeLayerRegistry.addLayerOrdering(BEFORE_ALL, AttributeLayerProvider.FIRST_VANILLA_PHASE);
+ AttributeLayerRegistry.addLayerOrdering(AttributeLayerProvider.LAST_VANILLA_PHASE, AFTER_ALL);
+
try (TestSingleplayerContext spContext = context.worldBuilder().create()) {
spContext.getServer().runOnServer(server -> {
ServerLevel overworld = server.getLevel(Level.OVERWORLD);
From 14e89bcc54b93fb607ca0709ac8efff2ec9e1c35 Mon Sep 17 00:00:00 2001
From: Samu
Date: Tue, 3 Mar 2026 14:03:57 +0100
Subject: [PATCH 04/10] Be more consistent with terms used in docs and code
---
.../attribute/v1/AttributeLayerProvider.java | 93 ++++++++++++++-----
.../attribute/v1/AttributeLayerRegistry.java | 18 ++--
.../attribute/AttributeLayerRegistryImpl.java | 12 +--
...FabricEnvironmentAttributesClientTest.java | 4 +-
4 files changed, 90 insertions(+), 37 deletions(-)
diff --git a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java
index 422ab0f7686..b90236af362 100644
--- a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java
+++ b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java
@@ -23,59 +23,108 @@
/**
* Provides {@link EnvironmentAttributeLayer}s to an {@link EnvironmentAttributeSystem}. You may register custom
- * {@link AttributeLayerProvider} implementations using {@link AttributeLayerRegistry#registerLayerProvider}.
+ * {@link AttributeLayerProvider} implementations using {@link AttributeLayerRegistry#registerLayerProvider}. Using
+ * {@link AttributeLayerProvider}, you can implement custom logic for modify and overriding attributes, based on
+ * things like weather, location or time. You can set restrictions on when your layer provider should apply in relation
+ * to vanilla providers or other modded providers using {@link AttributeLayerRegistry#addProviderOrdering}
+ *
+ * Background
*
*
- * Attribute layers can be ordered relative to vanilla's layers or other modded layers using
- * {@link AttributeLayerRegistry#addLayerOrdering}. The order defines which layers override which other layers: layers
- * that come first in the ordering are overriden by layers that come later in the ordering. For example, in vanilla,
- * biome layers come after dimension layers, since biome-local attributes override dimension-global attributes.
+ * The {@link EnvironmentAttributeSystem} assigns zero or more layers to every environment attribute. A layer here is
+ * represented by a {@link EnvironmentAttributeLayer}. Layers modify or
+ * override the value of an environment attribute by some implementation-specific logic. In vanilla Minecraft, defining an
+ * environment attribute in a dimension type, biome or timeline will cause a layer for that attribute to be added that
+ * modifies the attribute accordingly.
*
*
*
- * Minecraft adds layers in four phases: dimension-global attributes, then biome-local attributes, then
- * timeline-interpolated attributes, and finally some hardcoded weather attributes. Each of these phases, as well modded
- * layer providers, are associated with an identifier that can be sorted against.
+ * Minecraft creates an {@link EnvironmentAttributeSystem} for each {@link Level}, both on the client and the server,
+ * and adds layers to this system through four different providers:
+ *
+ * - Dimension type overrides: a layer is added for each attribute defined in the dimension type of the {@link Level}.
+ * - Biome overrides: if one or more biomes define an attribute, a layer is added for that attribute that alters the
+ * attribute throughout different biomes.
+ * - Timeline overrides: if one or more timelines define an attribute, a layer isadded for that attribute that alters
+ * the attribute according to how the timeline defines it.
+ * - Weather overrides: if the {@link Level} {@linkplain Level#canHaveWeather() has weather}, Minecraft defines some
+ * hardcoded weather layers for weather for some attributes.
+ *
+ * Each layer is given the value outputted by the previous layer, and must modify or replace
+ * this value based on some context parameters. The first layer is given the default value of the attribute.
+ *
+ *
+ * Adding modded layers
+ *
+ *
+ * As mentioned, the {@link AttributeLayerProvider} allows you to add modded layers to environment attributes during
+ * the creation of the {@link EnvironmentAttributeSystem}. Every provider is associated to an {@link Identifier},
+ * including vanilla's providers (despite that vanilla's providers are not implemented as separate classes).
+ * As order matters, attribute layers can be ordered relative to vanilla's layers or other modded layers using
+ * {@link AttributeLayerRegistry#addProviderOrdering}.
*
+ *
+ *
+ * Depending on the type of {@link EnvironmentAttributeLayer}, Minecraft caches values of environment attributes. Minecraft
+ * allows three different types of layers:
+ *
+ * - {@linkplain EnvironmentAttributeLayer.Constant Constant layers}: these modify the value in a context-independent
+ * manner. Minecraft uses these for dimensions and expects them to be constant - do not depend on external variables
+ * when implementing constant layers. If an attribute has only constant layers, the attribute value is simply cached and
+ * never recomputed again. Minecraft uses constant layers to provide a dimension's attribute values.
+ * - {@linkplain EnvironmentAttributeLayer.Positional Positional layers}: these modify the value based on a position
+ * in the world. If a positional layer is added to an attribute, Minecraft will recompute the value every time.
+ * Minecraft uses positional layers for biome interpolated attribute values. Note that environment attributes can be
+ * sampled without a position. In this case, positional layers are completely ignored.
+ * - {@linkplain EnvironmentAttributeLayer.TimeBased Time based layers}: these modify the value based on time.
+ * Minecraft will cache the value per tick, so that if it is sampled multiple times per tick, it is only evaluated once.
+ * Minecraft uses time based layers for timelines and weather.
+ *
+ * It is important that modded implementations take into account the constraints of different layer types, otherwise
+ * they may not work properly.
+ *
+ *
*/
public interface AttributeLayerProvider {
/**
- * Identifier associated to vanilla's dimension attribute layers.
+ * Identifier associated to vanilla's dimension attribute layer provider.
*/
- Identifier DIMENSION = Identifier.withDefaultNamespace("dimensions");
+ Identifier DIMENSIONS = Identifier.withDefaultNamespace("dimensions");
/**
- * Identifier associated to vanilla's biome attribute layers.
+ * Identifier associated to vanilla's biome attribute layer provider.
*/
Identifier BIOMES = Identifier.withDefaultNamespace("biomes");
/**
- * Identifier associated to vanilla's timeline attribute layers.
+ * Identifier associated to vanilla's timeline attribute layer provider.
*/
Identifier TIMELINES = Identifier.withDefaultNamespace("timelines");
/**
- * Identifier associated to vanilla's weather attribute layers.
+ * Identifier associated to vanilla's weather attribute layer provider.
*/
Identifier WEATHER = Identifier.withDefaultNamespace("weather");
/**
- * The identifier associated to the first vanilla phase. Currently, that is {@link #DIMENSION}.
- * This constant exists purely for compatibility: if Minecraft ever adds another layer before its dimension phase,
- * then this constant is updated.
+ * The identifier associated to the first vanilla provider. Currently, this is an alias for {@link #DIMENSIONS}.
+ * This constant exists purely for compatibility: if Minecraft ever adds another provider before its dimension provider,
+ * then this constant is updated so that mods can guarantee that their layer provider activates before all
+ * vanilla providers.
*/
- Identifier FIRST_VANILLA_PHASE = DIMENSION;
+ Identifier FIRST_VANILLA_PROVIDER = DIMENSIONS;
/**
- * The identifier associated to the last vanilla phase. Currently, that is {@link #WEATHER}.
- * This constant exists purely for compatibility: if Minecraft ever adds another layer after its weather phase,
- * then this constant is updated.
+ * The identifier associated to the last vanilla provider. Currently, this is an alias for {@link #WEATHER}.
+ * This constant exists purely for compatibility: if Minecraft ever adds another provider after its weather provider,
+ * then this constant is updated so that mods can guarantee that their layer provider activates after all
+ * vanilla providers.
*/
- Identifier LAST_VANILLA_PHASE = WEATHER;
+ Identifier LAST_VANILLA_PROVIDER = WEATHER;
/**
* Called to add attribute layers to an {@link EnvironmentAttributeSystem.Builder} for the given {@link Level}.
- * This is called both on the client and on the server for every {@link Level} that is created.
+ * This is called both on the client and on the server each time a {@link Level} is initialized.
*
* @param systemBuilder The {@link EnvironmentAttributeSystem.Builder} to add layers to.
* @param level The {@link Level} that the environment attribute system is being created for.
diff --git a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerRegistry.java b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerRegistry.java
index bda6521402a..37ec784cf76 100644
--- a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerRegistry.java
+++ b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerRegistry.java
@@ -22,13 +22,17 @@
import net.fabricmc.fabric.impl.environment.attribute.AttributeLayerRegistryImpl;
+/**
+ * Utility class allowing you to register and reorder {@link AttributeLayerProvider}s.
+ */
@NullMarked
public class AttributeLayerRegistry {
/**
- * Register a {@link AttributeLayerProvider}. If a layer with the given identifier already exists, an exception
+ * Register a {@link AttributeLayerProvider}. If a layer provider with the given identifier already exists, an exception
* is thrown.
*
- * @param id The identifier of the layer provider. This can be ordered against by other layer providers.
+ * @param id The identifier of the layer provider. This identifier can be used to set an ordering via
+ * {@link #addProviderOrdering}. .
* @param layer The layer provider to register.
*/
public static void registerLayerProvider(Identifier id, AttributeLayerProvider layer) {
@@ -38,13 +42,13 @@ public static void registerLayerProvider(Identifier id, AttributeLayerProvider l
/**
* Declares that the layer provider with the first identifier should activate before the layer provider with the
* second identifier. Unless this causes a cyclic dependency, the two layer providers are guaranteed to activate in
- * said order. You may use this to order your layer provider against vanilla phases using any of the constants in
+ * said order. You may use this to order your layer provider against vanilla layer providers using any of the constants in
* {@link AttributeLayerProvider}. If both layer identifiers are the same, then an exception is thrown.
*
- * @param firstLayer The ID of the layer that should activate earlier.
- * @param secondLayer The ID of the layer that should activate later.
+ * @param firstLayer The ID of the layer provider that should activate earlier.
+ * @param secondLayer The ID of the layer provider that should activate later.
*/
- public static void addLayerOrdering(Identifier firstLayer, Identifier secondLayer) {
- AttributeLayerRegistryImpl.addLayerOrdering(firstLayer, secondLayer);
+ public static void addProviderOrdering(Identifier firstLayer, Identifier secondLayer) {
+ AttributeLayerRegistryImpl.addProviderOrdering(firstLayer, secondLayer);
}
}
diff --git a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/AttributeLayerRegistryImpl.java b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/AttributeLayerRegistryImpl.java
index 0433e2107c6..a5c0db69668 100644
--- a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/AttributeLayerRegistryImpl.java
+++ b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/AttributeLayerRegistryImpl.java
@@ -67,14 +67,14 @@ public class AttributeLayerRegistryImpl {
}
// Register vanilla ordering
- registerLayerProvider(AttributeLayerProvider.DIMENSION, VanillaLayerMarker.DIMENSION);
+ registerLayerProvider(AttributeLayerProvider.DIMENSIONS, VanillaLayerMarker.DIMENSION);
registerLayerProvider(AttributeLayerProvider.BIOMES, VanillaLayerMarker.BIOMES);
registerLayerProvider(AttributeLayerProvider.TIMELINES, VanillaLayerMarker.TIMELINES);
registerLayerProvider(AttributeLayerProvider.WEATHER, VanillaLayerMarker.WEATHER);
- addLayerOrdering(AttributeLayerProvider.DIMENSION, AttributeLayerProvider.BIOMES);
- addLayerOrdering(AttributeLayerProvider.BIOMES, AttributeLayerProvider.TIMELINES);
- addLayerOrdering(AttributeLayerProvider.TIMELINES, AttributeLayerProvider.WEATHER);
+ addProviderOrdering(AttributeLayerProvider.DIMENSIONS, AttributeLayerProvider.BIOMES);
+ addProviderOrdering(AttributeLayerProvider.BIOMES, AttributeLayerProvider.TIMELINES);
+ addProviderOrdering(AttributeLayerProvider.TIMELINES, AttributeLayerProvider.WEATHER);
// Validate cache
hasOnlyVanillaLayers = true; // Set to true here because registerLayerProvider used above sets it to false
@@ -96,7 +96,7 @@ public static void registerLayerProvider(Identifier id, AttributeLayerProvider l
}
}
- public static void addLayerOrdering(Identifier firstLayer, Identifier secondLayer) {
+ public static void addProviderOrdering(Identifier firstLayer, Identifier secondLayer) {
Objects.requireNonNull(firstLayer, "The first layer identifier should not be null.");
Objects.requireNonNull(secondLayer, "The second layer identifier should not be null.");
@@ -237,7 +237,7 @@ private static int compareIds(Layer a, Layer b) {
// Markers for vanilla layers. It's important that these enum constants stay in the order that vanilla layers should appear,
// since this order will be used to fix dependency cycles (and we don't want a dependency cycle to mess up the order).
private enum VanillaLayerMarker implements AttributeLayerProvider {
- DIMENSION(AttributeLayerProvider.DIMENSION),
+ DIMENSION(AttributeLayerProvider.DIMENSIONS),
BIOMES(AttributeLayerProvider.BIOMES),
TIMELINES(AttributeLayerProvider.TIMELINES),
WEATHER(AttributeLayerProvider.WEATHER);
diff --git a/fabric-environment-attributes-v1/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java b/fabric-environment-attributes-v1/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java
index c74845bd149..6d3db5cb032 100644
--- a/fabric-environment-attributes-v1/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java
+++ b/fabric-environment-attributes-v1/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java
@@ -49,8 +49,8 @@ public void runTest(ClientGameTestContext context) {
systemBuilder.addConstantLayer(EnvironmentAttributes.SKY_COLOR, base -> TEST_COLOR);
});
- AttributeLayerRegistry.addLayerOrdering(BEFORE_ALL, AttributeLayerProvider.FIRST_VANILLA_PHASE);
- AttributeLayerRegistry.addLayerOrdering(AttributeLayerProvider.LAST_VANILLA_PHASE, AFTER_ALL);
+ AttributeLayerRegistry.addProviderOrdering(BEFORE_ALL, AttributeLayerProvider.FIRST_VANILLA_PROVIDER);
+ AttributeLayerRegistry.addProviderOrdering(AttributeLayerProvider.LAST_VANILLA_PROVIDER, AFTER_ALL);
try (TestSingleplayerContext spContext = context.worldBuilder().create()) {
spContext.getServer().runOnServer(server -> {
From 250d6d69e4bb7200b43c6c014745ab231205c3c2 Mon Sep 17 00:00:00 2001
From: Samu
Date: Tue, 3 Mar 2026 14:27:08 +0100
Subject: [PATCH 05/10] Fix javadoc errors
---
.../attribute/v1/AttributeLayerProvider.java | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java
index b90236af362..1ab25ff907b 100644
--- a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java
+++ b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java
@@ -28,7 +28,7 @@
* things like weather, location or time. You can set restrictions on when your layer provider should apply in relation
* to vanilla providers or other modded providers using {@link AttributeLayerRegistry#addProviderOrdering}
*
- * Background
+ * Background
*
*
* The {@link EnvironmentAttributeSystem} assigns zero or more layers to every environment attribute. A layer here is
@@ -41,6 +41,8 @@
*
* Minecraft creates an {@link EnvironmentAttributeSystem} for each {@link Level}, both on the client and the server,
* and adds layers to this system through four different providers:
+ *
+ *
*
* - Dimension type overrides: a layer is added for each attribute defined in the dimension type of the {@link Level}.
* - Biome overrides: if one or more biomes define an attribute, a layer is added for that attribute that alters the
@@ -50,11 +52,13 @@
*
- Weather overrides: if the {@link Level} {@linkplain Level#canHaveWeather() has weather}, Minecraft defines some
* hardcoded weather layers for weather for some attributes.
*
+ *
+ *
* Each layer is given the value outputted by the previous layer, and must modify or replace
* this value based on some context parameters. The first layer is given the default value of the attribute.
*
*
- * Adding modded layers
+ * Adding modded layers
*
*
* As mentioned, the {@link AttributeLayerProvider} allows you to add modded layers to environment attributes during
@@ -67,6 +71,8 @@
*
* Depending on the type of {@link EnvironmentAttributeLayer}, Minecraft caches values of environment attributes. Minecraft
* allows three different types of layers:
+ *
+ *
*
* - {@linkplain EnvironmentAttributeLayer.Constant Constant layers}: these modify the value in a context-independent
* manner. Minecraft uses these for dimensions and expects them to be constant - do not depend on external variables
@@ -80,6 +86,8 @@
* Minecraft will cache the value per tick, so that if it is sampled multiple times per tick, it is only evaluated once.
* Minecraft uses time based layers for timelines and weather.
*
+ *
+ *
* It is important that modded implementations take into account the constraints of different layer types, otherwise
* they may not work properly.
*
From 66ebdd3dda695330070f9a76a3c5632fbde53a6a Mon Sep 17 00:00:00 2001
From: Samu
Date: Tue, 3 Mar 2026 14:33:34 +0100
Subject: [PATCH 06/10] Add a small essential comment in the docs about the
nature of layers
---
.../api/environment/attribute/v1/AttributeLayerProvider.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java
index 1ab25ff907b..2d88f98fe87 100644
--- a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java
+++ b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java
@@ -35,7 +35,8 @@
* represented by a {@link EnvironmentAttributeLayer}. Layers modify or
* override the value of an environment attribute by some implementation-specific logic. In vanilla Minecraft, defining an
* environment attribute in a dimension type, biome or timeline will cause a layer for that attribute to be added that
- * modifies the attribute accordingly.
+ * modifies the attribute accordingly. Note that each attribute has its own stack of layers: layers apply to individual
+ * attributes, not to the system as a whole.
*
*
*
From ece2392d0601677daa56ab0017842b652e6f4886 Mon Sep 17 00:00:00 2001
From: Samu
Date: Mon, 9 Mar 2026 13:37:00 +0100
Subject: [PATCH 07/10] Make implementation naming match API naming
---
.../attribute/v1/AttributeLayerRegistry.java | 18 +--
.../attribute/AttributeLayerRegistryImpl.java | 108 +++++++++---------
2 files changed, 63 insertions(+), 63 deletions(-)
diff --git a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerRegistry.java b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerRegistry.java
index 37ec784cf76..1734baeeb02 100644
--- a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerRegistry.java
+++ b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerRegistry.java
@@ -31,12 +31,12 @@ public class AttributeLayerRegistry {
* Register a {@link AttributeLayerProvider}. If a layer provider with the given identifier already exists, an exception
* is thrown.
*
- * @param id The identifier of the layer provider. This identifier can be used to set an ordering via
- * {@link #addProviderOrdering}. .
- * @param layer The layer provider to register.
+ * @param id The identifier of the layer provider. This identifier can be used to set an ordering via
+ * {@link #addProviderOrdering}. .
+ * @param provider The layer provider to register.
*/
- public static void registerLayerProvider(Identifier id, AttributeLayerProvider layer) {
- AttributeLayerRegistryImpl.registerLayerProvider(id, layer);
+ public static void registerLayerProvider(Identifier id, AttributeLayerProvider provider) {
+ AttributeLayerRegistryImpl.registerLayerProvider(id, provider);
}
/**
@@ -45,10 +45,10 @@ public static void registerLayerProvider(Identifier id, AttributeLayerProvider l
* said order. You may use this to order your layer provider against vanilla layer providers using any of the constants in
* {@link AttributeLayerProvider}. If both layer identifiers are the same, then an exception is thrown.
*
- * @param firstLayer The ID of the layer provider that should activate earlier.
- * @param secondLayer The ID of the layer provider that should activate later.
+ * @param firstProvider The ID of the layer provider that should activate earlier.
+ * @param secondProvider The ID of the layer provider that should activate later.
*/
- public static void addProviderOrdering(Identifier firstLayer, Identifier secondLayer) {
- AttributeLayerRegistryImpl.addProviderOrdering(firstLayer, secondLayer);
+ public static void addProviderOrdering(Identifier firstProvider, Identifier secondProvider) {
+ AttributeLayerRegistryImpl.addProviderOrdering(firstProvider, secondProvider);
}
}
diff --git a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/AttributeLayerRegistryImpl.java b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/AttributeLayerRegistryImpl.java
index a5c0db69668..f0940618c55 100644
--- a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/AttributeLayerRegistryImpl.java
+++ b/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/AttributeLayerRegistryImpl.java
@@ -40,24 +40,24 @@ public class AttributeLayerRegistryImpl {
private static final Map MARKERS = Map.copyOf(Stream.of(VanillaLayerMarker.values())
.collect(Collectors.toMap(marker -> marker.id, marker -> marker)));
- private static final Map LAYER_MAP = new HashMap<>();
+ private static final Map PROVIDER_MAP = new HashMap<>();
private static final Set DEPENDENCIES = new HashSet<>();
// Lock used to ensure thread safety.
private static final Object LOCK = new Object();
- // As long as this is true, we skip sorting and inserting layers all together.
- // Becomes false once a modded layer is registered.
- private static volatile boolean hasOnlyVanillaLayers;
+ // As long as this is true, we skip sorting and inserting providers all together.
+ // Becomes false once a modded provider is registered.
+ private static volatile boolean hasOnlyVanillaMarkers;
// As long as this is true, the ordering in the fields below is valid.
- // Becomes false once a modded layer is registered or once a depencency order is added.
+ // Becomes false once a modded provider is registered or once a depencency order is added.
private static volatile boolean orderValid;
- // Layers that should go before vanilla layers.
+ // Layers that should go before vanilla providers.
private static final List FIRST_PHASES = new ArrayList<>();
- // Layers that should go in between or after vanilla layers.
+ // Layers that should go in between or after vanilla providers.
private static final Map> AFTER_VANILLA_PHASES = new EnumMap<>(VanillaLayerMarker.class);
static {
@@ -77,46 +77,46 @@ public class AttributeLayerRegistryImpl {
addProviderOrdering(AttributeLayerProvider.TIMELINES, AttributeLayerProvider.WEATHER);
// Validate cache
- hasOnlyVanillaLayers = true; // Set to true here because registerLayerProvider used above sets it to false
- orderValid = true; // Vanilla layers are not included in sorted phase lists
+ hasOnlyVanillaMarkers = true; // Set to true here because registerLayerProvider used above sets it to false
+ orderValid = true; // Vanilla provider are not included in sorted phase lists
}
- public static void registerLayerProvider(Identifier id, AttributeLayerProvider layer) {
+ public static void registerLayerProvider(Identifier id, AttributeLayerProvider provider) {
Objects.requireNonNull(id, "The layer identifier should not be null.");
- Objects.requireNonNull(layer, "The layer should not be null.");
+ Objects.requireNonNull(provider, "The provider should not be null.");
- if (LAYER_MAP.containsKey(id)) {
+ if (PROVIDER_MAP.containsKey(id)) {
throw new IllegalArgumentException("Layer with ID %s was already registered.".formatted(id));
}
synchronized (LOCK) {
- LAYER_MAP.put(id, layer);
+ PROVIDER_MAP.put(id, provider);
orderValid = false;
- hasOnlyVanillaLayers = false;
+ hasOnlyVanillaMarkers = false;
}
}
- public static void addProviderOrdering(Identifier firstLayer, Identifier secondLayer) {
- Objects.requireNonNull(firstLayer, "The first layer identifier should not be null.");
- Objects.requireNonNull(secondLayer, "The second layer identifier should not be null.");
+ public static void addProviderOrdering(Identifier firstProvider, Identifier secondProvider) {
+ Objects.requireNonNull(firstProvider, "The first provider identifier should not be null.");
+ Objects.requireNonNull(secondProvider, "The second provider identifier should not be null.");
- if (firstLayer.equals(secondLayer)) {
- throw new IllegalArgumentException("Tried to add a layer that depends on itself.");
+ if (firstProvider.equals(secondProvider)) {
+ throw new IllegalArgumentException("Tried to make a provider depend on itself.");
}
synchronized (LOCK) {
- if (DEPENDENCIES.add(new Dependency(firstLayer, secondLayer))) {
- // Adding a dependency only affects order if both IDs are associated with registered layers.
+ if (DEPENDENCIES.add(new Dependency(firstProvider, secondProvider))) {
+ // Adding a dependency only affects order if both IDs are associated with registered providers.
// Dependencies with missing registrations are simply ignored during sorting.
- if (LAYER_MAP.containsKey(firstLayer) && LAYER_MAP.containsKey(secondLayer)) {
+ if (PROVIDER_MAP.containsKey(firstProvider) && PROVIDER_MAP.containsKey(secondProvider)) {
orderValid = false;
}
}
}
}
- private static void addLayers(List providers, EnvironmentAttributeSystem.Builder systemBuilder, Level level) {
+ private static void insertModdedLayers(List providers, EnvironmentAttributeSystem.Builder systemBuilder, Level level) {
synchronized (LOCK) {
for (AttributeLayerProvider provider : providers) {
provider.addAttributeLayers(systemBuilder, level);
@@ -125,42 +125,42 @@ private static void addLayers(List providers, Environmen
}
public static void addPreEverythingLayers(EnvironmentAttributeSystem.Builder systemBuilder, Level level) {
- if (!hasOnlyVanillaLayers) {
+ if (!hasOnlyVanillaMarkers) {
sortIfNeeded();
- addLayers(FIRST_PHASES, systemBuilder, level);
+ insertModdedLayers(FIRST_PHASES, systemBuilder, level);
}
}
public static void addPostDimensionLayers(EnvironmentAttributeSystem.Builder systemBuilder, Level level) {
- if (!hasOnlyVanillaLayers) {
+ if (!hasOnlyVanillaMarkers) {
sortIfNeeded();
- addLayers(AFTER_VANILLA_PHASES.get(VanillaLayerMarker.DIMENSION), systemBuilder, level);
+ insertModdedLayers(AFTER_VANILLA_PHASES.get(VanillaLayerMarker.DIMENSION), systemBuilder, level);
}
}
public static void addPostBiomesLayers(EnvironmentAttributeSystem.Builder systemBuilder, Level level) {
- if (!hasOnlyVanillaLayers) {
+ if (!hasOnlyVanillaMarkers) {
sortIfNeeded();
- addLayers(AFTER_VANILLA_PHASES.get(VanillaLayerMarker.BIOMES), systemBuilder, level);
+ insertModdedLayers(AFTER_VANILLA_PHASES.get(VanillaLayerMarker.BIOMES), systemBuilder, level);
}
}
public static void addPostTimelinesLayers(EnvironmentAttributeSystem.Builder systemBuilder, Level level) {
- if (!hasOnlyVanillaLayers) {
+ if (!hasOnlyVanillaMarkers) {
sortIfNeeded();
- addLayers(AFTER_VANILLA_PHASES.get(VanillaLayerMarker.TIMELINES), systemBuilder, level);
+ insertModdedLayers(AFTER_VANILLA_PHASES.get(VanillaLayerMarker.TIMELINES), systemBuilder, level);
}
}
public static void addPostWeatherLayers(EnvironmentAttributeSystem.Builder systemBuilder, Level level) {
- if (!hasOnlyVanillaLayers) {
+ if (!hasOnlyVanillaMarkers) {
sortIfNeeded();
- addLayers(AFTER_VANILLA_PHASES.get(VanillaLayerMarker.WEATHER), systemBuilder, level);
+ insertModdedLayers(AFTER_VANILLA_PHASES.get(VanillaLayerMarker.WEATHER), systemBuilder, level);
}
}
private static void sortIfNeeded() {
- Map layers;
+ Map providersById;
// Collect sorting data from registry
synchronized (LOCK) {
@@ -168,25 +168,25 @@ private static void sortIfNeeded() {
return;
}
- layers = new HashMap<>();
+ providersById = new HashMap<>();
- for (Map.Entry entry : LAYER_MAP.entrySet()) {
- layers.put(entry.getKey(), new Layer(entry.getKey(), entry.getValue()));
+ for (Map.Entry entry : PROVIDER_MAP.entrySet()) {
+ providersById.put(entry.getKey(), new LayerProvider(entry.getKey(), entry.getValue()));
}
for (Dependency dependency : DEPENDENCIES) {
- Layer firstLayer = layers.get(dependency.firstLayer());
- Layer secondLayer = layers.get(dependency.secondLayer());
+ LayerProvider firstLayerProvider = providersById.get(dependency.first());
+ LayerProvider secondLayerProvider = providersById.get(dependency.second());
- if (firstLayer != null && secondLayer != null) {
- Layer.link(firstLayer, secondLayer);
+ if (firstLayerProvider != null && secondLayerProvider != null) {
+ LayerProvider.link(firstLayerProvider, secondLayerProvider);
}
}
}
- // Sort layers
- List sorted = new ArrayList<>(layers.values());
- NodeSorting.sort(sorted, "environment attribute layers", AttributeLayerRegistryImpl::compareIds);
+ // Sort providers
+ List sorted = new ArrayList<>(providersById.values());
+ NodeSorting.sort(sorted, "environment attribute providers", AttributeLayerRegistryImpl::compareIds);
// Categorize layer providers into vanilla phases
synchronized (LOCK) {
@@ -195,8 +195,8 @@ private static void sortIfNeeded() {
List phase = FIRST_PHASES;
- for (Layer layer : sorted) {
- AttributeLayerProvider provider = layer.provider;
+ for (LayerProvider layerProvider : sorted) {
+ AttributeLayerProvider provider = layerProvider.provider;
if (provider instanceof VanillaLayerMarker marker) {
phase = AFTER_VANILLA_PHASES.get(marker);
@@ -207,16 +207,16 @@ private static void sortIfNeeded() {
}
}
- // Tiebreaker: put vanilla layers before others, and otherwise sort by lexicographic ordering
- // This also makes sure that layers that were not tied to vanilla ordering will come last in the ordering
- private static int compareIds(Layer a, Layer b) {
+ // Tiebreaker: put vanilla providers before others, and otherwise sort by lexicographic ordering
+ // This also makes sure that providers that were not tied to vanilla ordering will come last in the ordering
+ private static int compareIds(LayerProvider a, LayerProvider b) {
Identifier idA = a.id;
Identifier idB = b.id;
VanillaLayerMarker markerA = MARKERS.get(idA);
VanillaLayerMarker markerB = MARKERS.get(idB);
- // If both are vanilla layers, ensure they remain the same order as defined by Minecraft
+ // If both are vanilla providers, ensure they remain the same order as defined by Minecraft
if (markerA != null && markerB != null) {
return markerA.compareTo(markerB);
}
@@ -234,7 +234,7 @@ private static int compareIds(Layer a, Layer b) {
return idA.compareTo(idB);
}
- // Markers for vanilla layers. It's important that these enum constants stay in the order that vanilla layers should appear,
+ // Markers for vanilla providers. It's important that these enum constants stay in the order that vanilla providers should appear,
// since this order will be used to fix dependency cycles (and we don't want a dependency cycle to mess up the order).
private enum VanillaLayerMarker implements AttributeLayerProvider {
DIMENSION(AttributeLayerProvider.DIMENSIONS),
@@ -254,11 +254,11 @@ public void addAttributeLayers(EnvironmentAttributeSystem.Builder systemBuilder,
}
}
- private static class Layer extends SortableNode {
+ private static class LayerProvider extends SortableNode {
private final Identifier id;
private final AttributeLayerProvider provider;
- private Layer(Identifier id, AttributeLayerProvider provider) {
+ private LayerProvider(Identifier id, AttributeLayerProvider provider) {
this.id = id;
this.provider = provider;
}
@@ -269,6 +269,6 @@ protected String getDescription() {
}
}
- private record Dependency(Identifier firstLayer, Identifier secondLayer) {
+ private record Dependency(Identifier first, Identifier second) {
}
}
From 9af08dec391b13c967628dfbd3834f269d19b3a5 Mon Sep 17 00:00:00 2001
From: Samu
Date: Mon, 9 Mar 2026 13:51:49 +0100
Subject: [PATCH 08/10] Change module name to match convention
---
.../build.gradle | 0
.../attribute/v1/AttributeLayerProvider.java | 0
.../attribute/v1/AttributeLayerRegistry.java | 0
.../attribute/AttributeLayerRegistryImpl.java | 0
.../attribute/EnvironmentAttributeSystemMixin.java | 0
.../fabric-environment-attribute-api-v1}/icon.png | Bin
.../fabric-environment-attribute-api-v1.mixins.json | 0
.../src/main/resources/fabric.mod.json | 10 +++++-----
.../attribute/FabricEnvironmentAttributesTest.java | 0
.../src/testmod/resources/fabric.mod.json | 0
.../FabricEnvironmentAttributesClientTest.java | 0
gradle.properties | 2 +-
settings.gradle | 2 +-
13 files changed, 7 insertions(+), 7 deletions(-)
rename {fabric-environment-attributes-v1 => fabric-environment-attribute-api-v1}/build.gradle (100%)
rename {fabric-environment-attributes-v1 => fabric-environment-attribute-api-v1}/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java (100%)
rename {fabric-environment-attributes-v1 => fabric-environment-attribute-api-v1}/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerRegistry.java (100%)
rename {fabric-environment-attributes-v1 => fabric-environment-attribute-api-v1}/src/main/java/net/fabricmc/fabric/impl/environment/attribute/AttributeLayerRegistryImpl.java (100%)
rename {fabric-environment-attributes-v1 => fabric-environment-attribute-api-v1}/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java (100%)
rename {fabric-environment-attributes-v1/src/main/resources/assets/fabric-environment-attributes-v1 => fabric-environment-attribute-api-v1/src/main/resources/assets/fabric-environment-attribute-api-v1}/icon.png (100%)
rename fabric-environment-attributes-v1/src/main/resources/fabric-environment-attributes-v1.mixins.json => fabric-environment-attribute-api-v1/src/main/resources/fabric-environment-attribute-api-v1.mixins.json (100%)
rename {fabric-environment-attributes-v1 => fabric-environment-attribute-api-v1}/src/main/resources/fabric.mod.json (66%)
rename {fabric-environment-attributes-v1 => fabric-environment-attribute-api-v1}/src/testmod/java/net/fabricmc/fabric/test/environment/attribute/FabricEnvironmentAttributesTest.java (100%)
rename {fabric-environment-attributes-v1 => fabric-environment-attribute-api-v1}/src/testmod/resources/fabric.mod.json (100%)
rename {fabric-environment-attributes-v1 => fabric-environment-attribute-api-v1}/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java (100%)
diff --git a/fabric-environment-attributes-v1/build.gradle b/fabric-environment-attribute-api-v1/build.gradle
similarity index 100%
rename from fabric-environment-attributes-v1/build.gradle
rename to fabric-environment-attribute-api-v1/build.gradle
diff --git a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java b/fabric-environment-attribute-api-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java
similarity index 100%
rename from fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java
rename to fabric-environment-attribute-api-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerProvider.java
diff --git a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerRegistry.java b/fabric-environment-attribute-api-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerRegistry.java
similarity index 100%
rename from fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerRegistry.java
rename to fabric-environment-attribute-api-v1/src/main/java/net/fabricmc/fabric/api/environment/attribute/v1/AttributeLayerRegistry.java
diff --git a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/AttributeLayerRegistryImpl.java b/fabric-environment-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/AttributeLayerRegistryImpl.java
similarity index 100%
rename from fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/AttributeLayerRegistryImpl.java
rename to fabric-environment-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/environment/attribute/AttributeLayerRegistryImpl.java
diff --git a/fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java b/fabric-environment-attribute-api-v1/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java
similarity index 100%
rename from fabric-environment-attributes-v1/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java
rename to fabric-environment-attribute-api-v1/src/main/java/net/fabricmc/fabric/mixin/environment/attribute/EnvironmentAttributeSystemMixin.java
diff --git a/fabric-environment-attributes-v1/src/main/resources/assets/fabric-environment-attributes-v1/icon.png b/fabric-environment-attribute-api-v1/src/main/resources/assets/fabric-environment-attribute-api-v1/icon.png
similarity index 100%
rename from fabric-environment-attributes-v1/src/main/resources/assets/fabric-environment-attributes-v1/icon.png
rename to fabric-environment-attribute-api-v1/src/main/resources/assets/fabric-environment-attribute-api-v1/icon.png
diff --git a/fabric-environment-attributes-v1/src/main/resources/fabric-environment-attributes-v1.mixins.json b/fabric-environment-attribute-api-v1/src/main/resources/fabric-environment-attribute-api-v1.mixins.json
similarity index 100%
rename from fabric-environment-attributes-v1/src/main/resources/fabric-environment-attributes-v1.mixins.json
rename to fabric-environment-attribute-api-v1/src/main/resources/fabric-environment-attribute-api-v1.mixins.json
diff --git a/fabric-environment-attributes-v1/src/main/resources/fabric.mod.json b/fabric-environment-attribute-api-v1/src/main/resources/fabric.mod.json
similarity index 66%
rename from fabric-environment-attributes-v1/src/main/resources/fabric.mod.json
rename to fabric-environment-attribute-api-v1/src/main/resources/fabric.mod.json
index 23d53115601..85b906d7b3b 100644
--- a/fabric-environment-attributes-v1/src/main/resources/fabric.mod.json
+++ b/fabric-environment-attribute-api-v1/src/main/resources/fabric.mod.json
@@ -1,10 +1,10 @@
{
"schemaVersion": 1,
- "id": "fabric-environment-attributes-v1",
- "name": "Fabric Environment Attributes API (v1)",
+ "id": "fabric-environment-attribute-api-v1",
+ "name": "Fabric Environment Attribute API (v1)",
"version": "${version}",
"license": "Apache-2.0",
- "icon": "assets/fabric-environment-attributes-v1/icon.png",
+ "icon": "assets/fabric-environment-attribute-api-v1/icon.png",
"contact" : {
"homepage": "https://fabricmc.net",
"irc": "irc://irc.esper.net:6667/fabric",
@@ -21,9 +21,9 @@
"minecraft": ">=1.16-rc.3",
"fabric-api-base": "*"
},
- "description": "Fabric Environment Attributes API.",
+ "description": "Fabric Environment Attribute API.",
"mixins": [
- "fabric-environment-attributes-v1.mixins.json"
+ "fabric-environment-attribute-api-v1.mixins.json"
],
"custom": {
"fabric-api:module-lifecycle": "stable"
diff --git a/fabric-environment-attributes-v1/src/testmod/java/net/fabricmc/fabric/test/environment/attribute/FabricEnvironmentAttributesTest.java b/fabric-environment-attribute-api-v1/src/testmod/java/net/fabricmc/fabric/test/environment/attribute/FabricEnvironmentAttributesTest.java
similarity index 100%
rename from fabric-environment-attributes-v1/src/testmod/java/net/fabricmc/fabric/test/environment/attribute/FabricEnvironmentAttributesTest.java
rename to fabric-environment-attribute-api-v1/src/testmod/java/net/fabricmc/fabric/test/environment/attribute/FabricEnvironmentAttributesTest.java
diff --git a/fabric-environment-attributes-v1/src/testmod/resources/fabric.mod.json b/fabric-environment-attribute-api-v1/src/testmod/resources/fabric.mod.json
similarity index 100%
rename from fabric-environment-attributes-v1/src/testmod/resources/fabric.mod.json
rename to fabric-environment-attribute-api-v1/src/testmod/resources/fabric.mod.json
diff --git a/fabric-environment-attributes-v1/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java b/fabric-environment-attribute-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java
similarity index 100%
rename from fabric-environment-attributes-v1/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java
rename to fabric-environment-attribute-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/environment/attribute/client/FabricEnvironmentAttributesClientTest.java
diff --git a/gradle.properties b/gradle.properties
index 4052a2c0ac4..40c69c09037 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -25,7 +25,7 @@ fabric-data-generation-api-v1-version=24.0.13
fabric-debug-api-v1-version=1.0.0
fabric-dimensions-v1-version=5.1.2
fabric-entity-events-v1-version=5.0.1
-fabric-environment-attributes-v1-version=1.0.0
+fabric-environment-attribute-api-v1-version=1.0.0
fabric-events-interaction-v0-version=5.1.7
fabric-game-rule-api-v1-version=4.0.3
fabric-gametest-api-v1-version=4.0.9
diff --git a/settings.gradle b/settings.gradle
index b02d9271188..b19e42f0d47 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -40,7 +40,7 @@ include 'fabric-data-generation-api-v1'
include 'fabric-debug-api-v1'
include 'fabric-dimensions-v1'
include 'fabric-entity-events-v1'
-include 'fabric-environment-attributes-v1'
+include 'fabric-environment-attribute-api-v1'
include 'fabric-events-interaction-v0'
include 'fabric-game-rule-api-v1'
include 'fabric-gametest-api-v1'
From f0fc207941168880a71398e2b4a5c001b34e9362 Mon Sep 17 00:00:00 2001
From: Samu
Date: Mon, 9 Mar 2026 14:07:48 +0100
Subject: [PATCH 09/10] Fix environment attribute api dependencies
---
fabric-environment-attribute-api-v1/build.gradle | 3 +--
.../src/main/resources/fabric.mod.json | 2 +-
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/fabric-environment-attribute-api-v1/build.gradle b/fabric-environment-attribute-api-v1/build.gradle
index 8bfe314df96..1c4e0279f2f 100644
--- a/fabric-environment-attribute-api-v1/build.gradle
+++ b/fabric-environment-attribute-api-v1/build.gradle
@@ -1,8 +1,7 @@
version = getSubprojectVersion(project)
moduleDependencies(project, [
- 'fabric-api-base',
- 'fabric-lifecycle-events-v1'
+ 'fabric-api-base'
])
testDependencies(project, [
diff --git a/fabric-environment-attribute-api-v1/src/main/resources/fabric.mod.json b/fabric-environment-attribute-api-v1/src/main/resources/fabric.mod.json
index 85b906d7b3b..c6870a8ca0b 100644
--- a/fabric-environment-attribute-api-v1/src/main/resources/fabric.mod.json
+++ b/fabric-environment-attribute-api-v1/src/main/resources/fabric.mod.json
@@ -18,7 +18,7 @@
},
"depends": {
"fabricloader": ">=0.18.4",
- "minecraft": ">=1.16-rc.3",
+ "minecraft": ">=1.21.11-rc1",
"fabric-api-base": "*"
},
"description": "Fabric Environment Attribute API.",
From 6fa1af77a58aaac80609c53a850a884d4263f87a Mon Sep 17 00:00:00 2001
From: Samu
Date: Mon, 9 Mar 2026 14:21:14 +0100
Subject: [PATCH 10/10] Fix test mod dependency
---
.../attribute/FabricEnvironmentAttributesTest.java | 2 +-
.../src/testmod/resources/fabric.mod.json | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/fabric-environment-attribute-api-v1/src/testmod/java/net/fabricmc/fabric/test/environment/attribute/FabricEnvironmentAttributesTest.java b/fabric-environment-attribute-api-v1/src/testmod/java/net/fabricmc/fabric/test/environment/attribute/FabricEnvironmentAttributesTest.java
index 1d7427a8994..5f12f9a109d 100644
--- a/fabric-environment-attribute-api-v1/src/testmod/java/net/fabricmc/fabric/test/environment/attribute/FabricEnvironmentAttributesTest.java
+++ b/fabric-environment-attribute-api-v1/src/testmod/java/net/fabricmc/fabric/test/environment/attribute/FabricEnvironmentAttributesTest.java
@@ -32,6 +32,6 @@ public class FabricEnvironmentAttributesTest implements ModInitializer {
@Override
public void onInitialize() {
- Registry.register(BuiltInRegistries.ENVIRONMENT_ATTRIBUTE, Identifier.fromNamespaceAndPath("fabric_environment_attributes", "test_color"), TEST_COLOR);
+ Registry.register(BuiltInRegistries.ENVIRONMENT_ATTRIBUTE, Identifier.fromNamespaceAndPath("fabric", "test_color"), TEST_COLOR);
}
}
diff --git a/fabric-environment-attribute-api-v1/src/testmod/resources/fabric.mod.json b/fabric-environment-attribute-api-v1/src/testmod/resources/fabric.mod.json
index 843bcd0f1d1..90bf83a1cdd 100644
--- a/fabric-environment-attribute-api-v1/src/testmod/resources/fabric.mod.json
+++ b/fabric-environment-attribute-api-v1/src/testmod/resources/fabric.mod.json
@@ -1,12 +1,12 @@
{
"schemaVersion": 1,
- "id": "fabric-environment-attributes-v1-testmod",
- "name": "Fabric Environment Attributes (v1) Test Mod",
+ "id": "fabric-environment-attribute-api-v1-testmod",
+ "name": "Fabric Environment Attribute API (v1) Test Mod",
"version": "1.0.0",
"environment": "*",
"license": "Apache-2.0",
"depends": {
- "fabric-environment-attributes-v1": "*"
+ "fabric-environment-attribute-api-v1": "*"
},
"entrypoints": {
"main": [