From 87875183dcd8239404cbddbe8717db1dbe4f64ee Mon Sep 17 00:00:00 2001 From: IThundxr Date: Thu, 8 Aug 2024 13:01:53 -0400 Subject: [PATCH] Waxing & Oxidizing DataMaps (#1406) --- .../data/recipes/RecipeProvider.java.patch | 10 ++ .../world/item/HoneycombItem.java.patch | 33 ++++++ .../level/block/WeatheringCopper.java.patch | 45 +++++++ .../neoforge/data_maps/block/oxidizables.json | 85 +++++++++++++ .../neoforge/data_maps/block/waxables.json | 112 ++++++++++++++++++ .../neoforge/common/DataMapHooks.java | 96 +++++++++++++++ .../neoforge/common/NeoForgeMod.java | 2 + .../internal/NeoForgeDataMapsProvider.java | 14 +++ .../common/extensions/IBlockExtension.java | 5 +- .../datamaps/builtin/NeoForgeDataMaps.java | 33 ++++++ .../datamaps/builtin/Oxidizable.java | 26 ++++ .../registries/datamaps/builtin/Waxable.java | 26 ++++ .../neoforge/data_maps/block/oxidizables.json | 7 ++ .../neoforge/data_maps/block/waxables.json | 7 ++ .../neoforge/debug/data/DataMapTests.java | 101 ++++++++++++++++ 15 files changed, 600 insertions(+), 2 deletions(-) create mode 100644 patches/net/minecraft/world/item/HoneycombItem.java.patch create mode 100644 patches/net/minecraft/world/level/block/WeatheringCopper.java.patch create mode 100644 src/generated/resources/data/neoforge/data_maps/block/oxidizables.json create mode 100644 src/generated/resources/data/neoforge/data_maps/block/waxables.json create mode 100644 src/main/java/net/neoforged/neoforge/common/DataMapHooks.java create mode 100644 src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/Oxidizable.java create mode 100644 src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/Waxable.java create mode 100644 tests/src/generated/resources/data/neoforge/data_maps/block/oxidizables.json create mode 100644 tests/src/generated/resources/data/neoforge/data_maps/block/waxables.json diff --git a/patches/net/minecraft/data/recipes/RecipeProvider.java.patch b/patches/net/minecraft/data/recipes/RecipeProvider.java.patch index b9d2ae0ca9..ec1d44e063 100644 --- a/patches/net/minecraft/data/recipes/RecipeProvider.java.patch +++ b/patches/net/minecraft/data/recipes/RecipeProvider.java.patch @@ -57,3 +57,13 @@ BlockFamilies.getAllFamilies().filter(BlockFamily::shouldGenerateRecipe).forEach(p_313461_ -> generateRecipes(p_301146_, p_313461_, p_251836_)); } +@@ -611,8 +_,7 @@ + } + + protected static void waxRecipes(RecipeOutput p_301254_, FeatureFlagSet p_313879_) { +- HoneycombItem.WAXABLES +- .get() ++ net.neoforged.neoforge.common.DataMapHooks.INVERSE_WAXABLES_DATAMAP + .forEach( + (p_337490_, p_337491_) -> { + if (p_337491_.requiredFeatures().isSubsetOf(p_313879_)) { diff --git a/patches/net/minecraft/world/item/HoneycombItem.java.patch b/patches/net/minecraft/world/item/HoneycombItem.java.patch new file mode 100644 index 0000000000..46a6d312b6 --- /dev/null +++ b/patches/net/minecraft/world/item/HoneycombItem.java.patch @@ -0,0 +1,33 @@ +--- a/net/minecraft/world/item/HoneycombItem.java ++++ b/net/minecraft/world/item/HoneycombItem.java +@@ -20,6 +_,10 @@ + import net.minecraft.world.level.gameevent.GameEvent; + + public class HoneycombItem extends Item implements SignApplicator { ++ /** ++ * @deprecated Neo: Use the {@link net.neoforged.neoforge.registries.datamaps.builtin.NeoForgeDataMaps#WAXABLES data map}, this field will be ignored in a future version ++ */ ++ @Deprecated + public static final Supplier> WAXABLES = Suppliers.memoize( + () -> ImmutableBiMap.builder() + .put(Blocks.COPPER_BLOCK, Blocks.WAXED_COPPER_BLOCK) +@@ -60,6 +_,10 @@ + .put(Blocks.OXIDIZED_COPPER_BULB, Blocks.WAXED_OXIDIZED_COPPER_BULB) + .build() + ); ++ /** ++ * @deprecated Use the {@link net.neoforged.neoforge.common.DataMapHooks#INVERSE_WAXABLES_DATAMAP inverse map} generated from the data map, this field will be ignored in a future version ++ */ ++ @Deprecated + public static final Supplier> WAX_OFF_BY_BLOCK = Suppliers.memoize(() -> WAXABLES.get().inverse()); + + public HoneycombItem(Item.Properties p_150867_) { +@@ -87,7 +_,7 @@ + } + + public static Optional getWaxed(BlockState p_150879_) { +- return Optional.ofNullable(WAXABLES.get().get(p_150879_.getBlock())).map(p_150877_ -> p_150877_.withPropertiesOf(p_150879_)); ++ return Optional.ofNullable(net.neoforged.neoforge.common.DataMapHooks.getBlockWaxed(p_150879_.getBlock())).map(p_150877_ -> p_150877_.withPropertiesOf(p_150879_)); + } + + @Override diff --git a/patches/net/minecraft/world/level/block/WeatheringCopper.java.patch b/patches/net/minecraft/world/level/block/WeatheringCopper.java.patch new file mode 100644 index 0000000000..2efc36e582 --- /dev/null +++ b/patches/net/minecraft/world/level/block/WeatheringCopper.java.patch @@ -0,0 +1,45 @@ +--- a/net/minecraft/world/level/block/WeatheringCopper.java ++++ b/net/minecraft/world/level/block/WeatheringCopper.java +@@ -10,6 +_,10 @@ + import net.minecraft.world.level.block.state.BlockState; + + public interface WeatheringCopper extends ChangeOverTimeBlock { ++ /** ++ * @deprecated Neo: Use {@link net.neoforged.neoforge.registries.datamaps.builtin.NeoForgeDataMaps#OXIDIZABLES the data map}, this field will be ignored in a future version ++ */ ++ @Deprecated + Supplier> NEXT_BY_BLOCK = Suppliers.memoize( + () -> ImmutableBiMap.builder() + .put(Blocks.COPPER_BLOCK, Blocks.EXPOSED_COPPER) +@@ -41,16 +_,20 @@ + .put(Blocks.WEATHERED_COPPER_BULB, Blocks.OXIDIZED_COPPER_BULB) + .build() + ); ++ /** ++ * @deprecated Neo: Use the {@link net.neoforged.neoforge.common.DataMapHooks#INVERSE_OXIDIZABLES_DATAMAP inverse map} generated from the data map, this field will be ignored in a future version ++ */ ++ @Deprecated + Supplier> PREVIOUS_BY_BLOCK = Suppliers.memoize(() -> NEXT_BY_BLOCK.get().inverse()); + + static Optional getPrevious(Block p_154891_) { +- return Optional.ofNullable(PREVIOUS_BY_BLOCK.get().get(p_154891_)); ++ return Optional.ofNullable(net.neoforged.neoforge.common.DataMapHooks.getPreviousOxidizedStage(p_154891_)); + } + + static Block getFirst(Block p_154898_) { + Block block = p_154898_; + +- for (Block block1 = PREVIOUS_BY_BLOCK.get().get(p_154898_); block1 != null; block1 = PREVIOUS_BY_BLOCK.get().get(block1)) { ++ for (Block block1 = net.neoforged.neoforge.common.DataMapHooks.getPreviousOxidizedStage(p_154898_); block1 != null; block1 = net.neoforged.neoforge.common.DataMapHooks.getPreviousOxidizedStage(block1)) { + block = block1; + } + +@@ -62,7 +_,7 @@ + } + + static Optional getNext(Block p_154905_) { +- return Optional.ofNullable(NEXT_BY_BLOCK.get().get(p_154905_)); ++ return Optional.ofNullable(net.neoforged.neoforge.common.DataMapHooks.getNextOxidizedStage(p_154905_)); + } + + static BlockState getFirst(BlockState p_154907_) { diff --git a/src/generated/resources/data/neoforge/data_maps/block/oxidizables.json b/src/generated/resources/data/neoforge/data_maps/block/oxidizables.json new file mode 100644 index 0000000000..77202e8199 --- /dev/null +++ b/src/generated/resources/data/neoforge/data_maps/block/oxidizables.json @@ -0,0 +1,85 @@ +{ + "values": { + "minecraft:chiseled_copper": { + "next_oxidation_stage": "minecraft:exposed_chiseled_copper" + }, + "minecraft:copper_block": { + "next_oxidation_stage": "minecraft:exposed_copper" + }, + "minecraft:copper_bulb": { + "next_oxidation_stage": "minecraft:exposed_copper_bulb" + }, + "minecraft:copper_door": { + "next_oxidation_stage": "minecraft:exposed_copper_door" + }, + "minecraft:copper_grate": { + "next_oxidation_stage": "minecraft:exposed_copper_grate" + }, + "minecraft:copper_trapdoor": { + "next_oxidation_stage": "minecraft:exposed_copper_trapdoor" + }, + "minecraft:cut_copper": { + "next_oxidation_stage": "minecraft:exposed_cut_copper" + }, + "minecraft:cut_copper_slab": { + "next_oxidation_stage": "minecraft:exposed_cut_copper_slab" + }, + "minecraft:cut_copper_stairs": { + "next_oxidation_stage": "minecraft:exposed_cut_copper_stairs" + }, + "minecraft:exposed_chiseled_copper": { + "next_oxidation_stage": "minecraft:weathered_chiseled_copper" + }, + "minecraft:exposed_copper": { + "next_oxidation_stage": "minecraft:weathered_copper" + }, + "minecraft:exposed_copper_bulb": { + "next_oxidation_stage": "minecraft:weathered_copper_bulb" + }, + "minecraft:exposed_copper_door": { + "next_oxidation_stage": "minecraft:weathered_copper_door" + }, + "minecraft:exposed_copper_grate": { + "next_oxidation_stage": "minecraft:weathered_copper_grate" + }, + "minecraft:exposed_copper_trapdoor": { + "next_oxidation_stage": "minecraft:weathered_copper_trapdoor" + }, + "minecraft:exposed_cut_copper": { + "next_oxidation_stage": "minecraft:weathered_cut_copper" + }, + "minecraft:exposed_cut_copper_slab": { + "next_oxidation_stage": "minecraft:weathered_cut_copper_slab" + }, + "minecraft:exposed_cut_copper_stairs": { + "next_oxidation_stage": "minecraft:weathered_cut_copper_stairs" + }, + "minecraft:weathered_chiseled_copper": { + "next_oxidation_stage": "minecraft:oxidized_chiseled_copper" + }, + "minecraft:weathered_copper": { + "next_oxidation_stage": "minecraft:oxidized_copper" + }, + "minecraft:weathered_copper_bulb": { + "next_oxidation_stage": "minecraft:oxidized_copper_bulb" + }, + "minecraft:weathered_copper_door": { + "next_oxidation_stage": "minecraft:oxidized_copper_door" + }, + "minecraft:weathered_copper_grate": { + "next_oxidation_stage": "minecraft:oxidized_copper_grate" + }, + "minecraft:weathered_copper_trapdoor": { + "next_oxidation_stage": "minecraft:oxidized_copper_trapdoor" + }, + "minecraft:weathered_cut_copper": { + "next_oxidation_stage": "minecraft:oxidized_cut_copper" + }, + "minecraft:weathered_cut_copper_slab": { + "next_oxidation_stage": "minecraft:oxidized_cut_copper_slab" + }, + "minecraft:weathered_cut_copper_stairs": { + "next_oxidation_stage": "minecraft:oxidized_cut_copper_stairs" + } + } +} \ No newline at end of file diff --git a/src/generated/resources/data/neoforge/data_maps/block/waxables.json b/src/generated/resources/data/neoforge/data_maps/block/waxables.json new file mode 100644 index 0000000000..da63fb4c2d --- /dev/null +++ b/src/generated/resources/data/neoforge/data_maps/block/waxables.json @@ -0,0 +1,112 @@ +{ + "values": { + "minecraft:chiseled_copper": { + "waxed": "minecraft:waxed_chiseled_copper" + }, + "minecraft:copper_block": { + "waxed": "minecraft:waxed_copper_block" + }, + "minecraft:copper_bulb": { + "waxed": "minecraft:waxed_copper_bulb" + }, + "minecraft:copper_door": { + "waxed": "minecraft:waxed_copper_door" + }, + "minecraft:copper_grate": { + "waxed": "minecraft:waxed_copper_grate" + }, + "minecraft:copper_trapdoor": { + "waxed": "minecraft:waxed_copper_trapdoor" + }, + "minecraft:cut_copper": { + "waxed": "minecraft:waxed_cut_copper" + }, + "minecraft:cut_copper_slab": { + "waxed": "minecraft:waxed_cut_copper_slab" + }, + "minecraft:cut_copper_stairs": { + "waxed": "minecraft:waxed_cut_copper_stairs" + }, + "minecraft:exposed_chiseled_copper": { + "waxed": "minecraft:waxed_exposed_chiseled_copper" + }, + "minecraft:exposed_copper": { + "waxed": "minecraft:waxed_exposed_copper" + }, + "minecraft:exposed_copper_bulb": { + "waxed": "minecraft:waxed_exposed_copper_bulb" + }, + "minecraft:exposed_copper_door": { + "waxed": "minecraft:waxed_exposed_copper_door" + }, + "minecraft:exposed_copper_grate": { + "waxed": "minecraft:waxed_exposed_copper_grate" + }, + "minecraft:exposed_copper_trapdoor": { + "waxed": "minecraft:waxed_exposed_copper_trapdoor" + }, + "minecraft:exposed_cut_copper": { + "waxed": "minecraft:waxed_exposed_cut_copper" + }, + "minecraft:exposed_cut_copper_slab": { + "waxed": "minecraft:waxed_exposed_cut_copper_slab" + }, + "minecraft:exposed_cut_copper_stairs": { + "waxed": "minecraft:waxed_exposed_cut_copper_stairs" + }, + "minecraft:oxidized_chiseled_copper": { + "waxed": "minecraft:waxed_oxidized_chiseled_copper" + }, + "minecraft:oxidized_copper": { + "waxed": "minecraft:waxed_oxidized_copper" + }, + "minecraft:oxidized_copper_bulb": { + "waxed": "minecraft:waxed_oxidized_copper_bulb" + }, + "minecraft:oxidized_copper_door": { + "waxed": "minecraft:waxed_oxidized_copper_door" + }, + "minecraft:oxidized_copper_grate": { + "waxed": "minecraft:waxed_oxidized_copper_grate" + }, + "minecraft:oxidized_copper_trapdoor": { + "waxed": "minecraft:waxed_oxidized_copper_trapdoor" + }, + "minecraft:oxidized_cut_copper": { + "waxed": "minecraft:waxed_oxidized_cut_copper" + }, + "minecraft:oxidized_cut_copper_slab": { + "waxed": "minecraft:waxed_oxidized_cut_copper_slab" + }, + "minecraft:oxidized_cut_copper_stairs": { + "waxed": "minecraft:waxed_oxidized_cut_copper_stairs" + }, + "minecraft:weathered_chiseled_copper": { + "waxed": "minecraft:waxed_weathered_chiseled_copper" + }, + "minecraft:weathered_copper": { + "waxed": "minecraft:waxed_weathered_copper" + }, + "minecraft:weathered_copper_bulb": { + "waxed": "minecraft:waxed_weathered_copper_bulb" + }, + "minecraft:weathered_copper_door": { + "waxed": "minecraft:waxed_weathered_copper_door" + }, + "minecraft:weathered_copper_grate": { + "waxed": "minecraft:waxed_weathered_copper_grate" + }, + "minecraft:weathered_copper_trapdoor": { + "waxed": "minecraft:waxed_weathered_copper_trapdoor" + }, + "minecraft:weathered_cut_copper": { + "waxed": "minecraft:waxed_weathered_cut_copper" + }, + "minecraft:weathered_cut_copper_slab": { + "waxed": "minecraft:waxed_weathered_cut_copper_slab" + }, + "minecraft:weathered_cut_copper_stairs": { + "waxed": "minecraft:waxed_weathered_cut_copper_stairs" + } + } +} \ No newline at end of file diff --git a/src/main/java/net/neoforged/neoforge/common/DataMapHooks.java b/src/main/java/net/neoforged/neoforge/common/DataMapHooks.java new file mode 100644 index 0000000000..5b61e8a4a2 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/common/DataMapHooks.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.common; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.world.item.HoneycombItem; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.WeatheringCopper; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.neoforge.registries.datamaps.DataMapsUpdatedEvent; +import net.neoforged.neoforge.registries.datamaps.builtin.NeoForgeDataMaps; +import net.neoforged.neoforge.registries.datamaps.builtin.Oxidizable; +import net.neoforged.neoforge.registries.datamaps.builtin.Waxable; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +// TODO: 1.21.2 remove fallback to vanilla map for waxing and oxidizing +public class DataMapHooks { + // will be removed in 1.21.2 + /** Used in a gametest */ + @ApiStatus.Internal + public static boolean didHaveToFallbackToVanillaMaps = false; + + private static final Map INVERSE_OXIDIZABLES_DATAMAP_INTERNAL = new HashMap<>(); + private static final Map INVERSE_WAXABLES_DATAMAP_INTERNAL = new HashMap<>(); + + /** The inverse map of the oxidizables data map, used in vanilla when scraping oxidization off of a block */ + public static final Map INVERSE_OXIDIZABLES_DATAMAP = Collections.unmodifiableMap(INVERSE_OXIDIZABLES_DATAMAP_INTERNAL); + /** The inverse map of the waxables data map, used in vanilla when scraping wax off of a block */ + public static final Map INVERSE_WAXABLES_DATAMAP = Collections.unmodifiableMap(INVERSE_WAXABLES_DATAMAP_INTERNAL); + + @Nullable + @SuppressWarnings("deprecation") + public static Block getNextOxidizedStage(Block block) { + Oxidizable oxidizable = block.builtInRegistryHolder().getData(NeoForgeDataMaps.OXIDIZABLES); + return oxidizable != null ? oxidizable.nextOxidationStage() : WeatheringCopper.NEXT_BY_BLOCK.get().get(block); + } + + @Nullable + @SuppressWarnings("deprecation") + public static Block getPreviousOxidizedStage(Block block) { + return INVERSE_OXIDIZABLES_DATAMAP.containsKey(block) ? INVERSE_OXIDIZABLES_DATAMAP.get(block) : WeatheringCopper.PREVIOUS_BY_BLOCK.get().get(block); + } + + @Nullable + @SuppressWarnings("deprecation") + public static Block getBlockWaxed(Block block) { + Waxable waxable = block.builtInRegistryHolder().getData(NeoForgeDataMaps.WAXABLES); + return waxable != null ? waxable.waxed() : HoneycombItem.WAXABLES.get().get(block); + } + + @Nullable + @SuppressWarnings("deprecation") + public static Block getBlockUnwaxed(Block block) { + return INVERSE_WAXABLES_DATAMAP.containsKey(block) ? INVERSE_WAXABLES_DATAMAP.get(block) : HoneycombItem.WAX_OFF_BY_BLOCK.get().get(block); + } + + @SubscribeEvent + static void onDataMapsUpdated(DataMapsUpdatedEvent event) { + event.ifRegistry(Registries.BLOCK, registry -> { + INVERSE_OXIDIZABLES_DATAMAP_INTERNAL.clear(); + INVERSE_WAXABLES_DATAMAP_INTERNAL.clear(); + + registry.getDataMap(NeoForgeDataMaps.OXIDIZABLES).forEach((resourceKey, oxidizable) -> { + INVERSE_OXIDIZABLES_DATAMAP_INTERNAL.put(oxidizable.nextOxidationStage(), BuiltInRegistries.BLOCK.get(resourceKey)); + }); + + //noinspection deprecation + WeatheringCopper.PREVIOUS_BY_BLOCK.get().forEach((after, before) -> { + if (!INVERSE_OXIDIZABLES_DATAMAP_INTERNAL.containsKey(after)) { + INVERSE_OXIDIZABLES_DATAMAP_INTERNAL.put(after, before); + didHaveToFallbackToVanillaMaps = true; + } + }); + + registry.getDataMap(NeoForgeDataMaps.WAXABLES).forEach((resourceKey, waxable) -> { + INVERSE_WAXABLES_DATAMAP_INTERNAL.put(waxable.waxed(), BuiltInRegistries.BLOCK.get(resourceKey)); + }); + + //noinspection deprecation + HoneycombItem.WAX_OFF_BY_BLOCK.get().forEach((after, before) -> { + if (!INVERSE_WAXABLES_DATAMAP_INTERNAL.containsKey(after)) { + INVERSE_OXIDIZABLES_DATAMAP_INTERNAL.put(after, before); + didHaveToFallbackToVanillaMaps = true; + } + }); + }); + } +} diff --git a/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java b/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java index c564c80654..3c0ca35bb5 100644 --- a/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java +++ b/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java @@ -563,6 +563,8 @@ public NeoForgeMod(IEventBus modEventBus, Dist dist, ModContainer container) { NeoForge.EVENT_BUS.addListener(CapabilityHooks::invalidateCapsOnChunkUnload); NeoForge.EVENT_BUS.addListener(CapabilityHooks::cleanCapabilityListenerReferencesOnTick); + NeoForge.EVENT_BUS.addListener(DataMapHooks::onDataMapsUpdated); + modEventBus.register(NeoForgeDataMaps.class); if (isPRBuild(container.getModInfo().getVersion().toString())) { diff --git a/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeDataMapsProvider.java b/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeDataMapsProvider.java index c94cc2a5ca..9e5604aa69 100644 --- a/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeDataMapsProvider.java +++ b/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeDataMapsProvider.java @@ -24,8 +24,10 @@ import net.minecraft.world.entity.ai.behavior.WorkAtComposter; import net.minecraft.world.entity.animal.Parrot; import net.minecraft.world.entity.npc.VillagerProfession; +import net.minecraft.world.item.HoneycombItem; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.ComposterBlock; +import net.minecraft.world.level.block.WeatheringCopper; import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.gameevent.vibrations.VibrationSystem; @@ -37,9 +39,11 @@ import net.neoforged.neoforge.registries.datamaps.builtin.FurnaceFuel; import net.neoforged.neoforge.registries.datamaps.builtin.MonsterRoomMob; import net.neoforged.neoforge.registries.datamaps.builtin.NeoForgeDataMaps; +import net.neoforged.neoforge.registries.datamaps.builtin.Oxidizable; import net.neoforged.neoforge.registries.datamaps.builtin.ParrotImitation; import net.neoforged.neoforge.registries.datamaps.builtin.RaidHeroGift; import net.neoforged.neoforge.registries.datamaps.builtin.VibrationFrequency; +import net.neoforged.neoforge.registries.datamaps.builtin.Waxable; public class NeoForgeDataMapsProvider extends DataMapProvider { public NeoForgeDataMapsProvider(PackOutput packOutput, CompletableFuture lookupProvider) { @@ -74,5 +78,15 @@ protected void gather() { Arrays.stream(ObfuscationReflectionHelper.[], MonsterRoomFeature>getPrivateValue(MonsterRoomFeature.class, null, "MOBS")) .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) .forEach((type, weight) -> monsterRoomMobs.add(BuiltInRegistries.ENTITY_TYPE.wrapAsHolder(type), new MonsterRoomMob(Weight.of((int) (weight * 100))), false)); + + final var oxidizables = builder(NeoForgeDataMaps.OXIDIZABLES); + WeatheringCopper.NEXT_BY_BLOCK.get().forEach((now, after) -> { + oxidizables.add(now.builtInRegistryHolder(), new Oxidizable(after), false); + }); + + final var waxables = builder(NeoForgeDataMaps.WAXABLES); + HoneycombItem.WAXABLES.get().forEach((now, after) -> { + waxables.add(now.builtInRegistryHolder(), new Waxable(after), false); + }); } } diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java index de277227ea..f193bfff1e 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java @@ -27,7 +27,6 @@ import net.minecraft.world.entity.projectile.FishingHook; import net.minecraft.world.entity.projectile.WitherSkull; import net.minecraft.world.item.AxeItem; -import net.minecraft.world.item.HoneycombItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.ShovelItem; @@ -80,6 +79,7 @@ import net.neoforged.neoforge.capabilities.BlockCapabilityCache; import net.neoforged.neoforge.client.ClientHooks; import net.neoforged.neoforge.client.model.data.ModelData; +import net.neoforged.neoforge.common.DataMapHooks; import net.neoforged.neoforge.common.ItemAbilities; import net.neoforged.neoforge.common.ItemAbility; import net.neoforged.neoforge.common.enums.BubbleColumnDirection; @@ -772,7 +772,8 @@ default BlockState getToolModifiedState(BlockState state, UseOnContext context, } else if (ItemAbilities.AXE_SCRAPE == itemAbility) { return WeatheringCopper.getPrevious(state).orElse(null); } else if (ItemAbilities.AXE_WAX_OFF == itemAbility) { - return Optional.ofNullable(HoneycombItem.WAX_OFF_BY_BLOCK.get().get(state.getBlock())).map(block -> block.withPropertiesOf(state)).orElse(null); + Block waxOffBlock = DataMapHooks.getBlockUnwaxed(state.getBlock()); + return Optional.ofNullable(waxOffBlock).map(block -> block.withPropertiesOf(state)).orElse(null); } else if (ItemAbilities.SHOVEL_FLATTEN == itemAbility) { return ShovelItem.getShovelPathingState(state); } else if (ItemAbilities.HOE_TILL == itemAbility) { diff --git a/src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/NeoForgeDataMaps.java b/src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/NeoForgeDataMaps.java index 42f962de79..330857d541 100644 --- a/src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/NeoForgeDataMaps.java +++ b/src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/NeoForgeDataMaps.java @@ -11,15 +11,20 @@ import net.minecraft.world.entity.ai.behavior.GiveGiftToHero; import net.minecraft.world.entity.animal.Parrot; import net.minecraft.world.entity.npc.VillagerProfession; +import net.minecraft.world.item.HoneycombItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.ComposterBlock; +import net.minecraft.world.level.block.WeatheringCopper; import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.gameevent.vibrations.VibrationSystem; import net.minecraft.world.level.levelgen.feature.MonsterRoomFeature; import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.neoforge.common.DataMapHooks; +import net.neoforged.neoforge.common.ItemAbilities; import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; import net.neoforged.neoforge.registries.datamaps.DataMapType; import net.neoforged.neoforge.registries.datamaps.RegisterDataMapTypesEvent; @@ -73,6 +78,19 @@ public class NeoForgeDataMaps { public static final DataMapType, MonsterRoomMob> MONSTER_ROOM_MOBS = DataMapType.builder( id("monster_room_mobs"), Registries.ENTITY_TYPE, MonsterRoomMob.CODEC).synced(MonsterRoomMob.WEIGHT_CODEC, false).build(); + /** + * The {@linkplain Block} data map that replaces {@link WeatheringCopper#NEXT_BY_BLOCK}. + *

+ * The location of this data map is {@code neoforge/data_maps/block/oxidizables.json}, and the values are objects with 1 field: + *

    + *
  • {@code next_oxidized_stage}, a block that the object should convert into once it changes oxidizing states
  • + *
+ * + * The inverted map of this can be found at {@link DataMapHooks#getInverseOxidizablesMap()} + */ + public static final DataMapType OXIDIZABLES = DataMapType.builder( + id("oxidizables"), Registries.BLOCK, Oxidizable.CODEC).synced(Oxidizable.OXIDIZABLE_CODEC, false).build(); + /** * The {@linkplain EntityType} data map that replaces {@link Parrot#MOB_SOUND_MAP}. *

@@ -110,6 +128,19 @@ public class NeoForgeDataMaps { public static final DataMapType VIBRATION_FREQUENCIES = DataMapType.builder( id("vibration_frequencies"), Registries.GAME_EVENT, VibrationFrequency.CODEC).synced(VibrationFrequency.FREQUENCY_CODEC, false).build(); + /** + * The {@linkplain Block} data map that replaces {@link HoneycombItem#WAXABLES}. + *

+ * The location of this data map is {@code neoforge/data_maps/block/waxables.json}, and the values are objects with 1 field: + *

    + *
  • {@code waxed}, a block that the object should convert into once it is right clicked with a {@link ItemAbilities#AXE_WAX_OFF} ability
  • + *
+ * + * The inverted map of this can be found at {@link DataMapHooks#INVERSE_WAXABLES_DATAMAP} + */ + public static final DataMapType WAXABLES = DataMapType.builder( + id("waxables"), Registries.BLOCK, Waxable.CODEC).synced(Waxable.WAXABLE_CODEC, false).build(); + private static ResourceLocation id(final String name) { return ResourceLocation.fromNamespaceAndPath(NeoForgeVersion.MOD_ID, name); } @@ -119,8 +150,10 @@ private static void register(final RegisterDataMapTypesEvent event) { event.register(COMPOSTABLES); event.register(FURNACE_FUELS); event.register(MONSTER_ROOM_MOBS); + event.register(OXIDIZABLES); event.register(PARROT_IMITATIONS); event.register(RAID_HERO_GIFTS); event.register(VIBRATION_FREQUENCIES); + event.register(WAXABLES); } } diff --git a/src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/Oxidizable.java b/src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/Oxidizable.java new file mode 100644 index 0000000000..471c6e401e --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/Oxidizable.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.registries.datamaps.builtin; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.level.block.Block; + +/** + * Data map value for {@linkplain NeoForgeDataMaps#OXIDIZABLES oxidizable blocks} allowing mods to easily register basic + * oxidizing interactions for their blocks. + * + * @param nextOxidationStage the block that the key value will transform into when its oxidation stage changes + */ +public record Oxidizable(Block nextOxidationStage) { + public static final Codec OXIDIZABLE_CODEC = BuiltInRegistries.BLOCK.byNameCodec() + .xmap(Oxidizable::new, Oxidizable::nextOxidationStage); + public static final Codec CODEC = Codec.withAlternative( + RecordCodecBuilder.create(in -> in.group( + BuiltInRegistries.BLOCK.byNameCodec().fieldOf("next_oxidation_stage").forGetter(Oxidizable::nextOxidationStage)).apply(in, Oxidizable::new)), + OXIDIZABLE_CODEC); +} diff --git a/src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/Waxable.java b/src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/Waxable.java new file mode 100644 index 0000000000..0c30cf8c39 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/Waxable.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.registries.datamaps.builtin; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.level.block.Block; + +/** + * Data map value for {@linkplain NeoForgeDataMaps#WAXABLES waxable blocks} allowing mods to easily register basic + * waxing interactions for their blocks. + * + * @param waxed the block that the key value will transform into when waxed with a honeycomb + */ +public record Waxable(Block waxed) { + public static final Codec WAXABLE_CODEC = BuiltInRegistries.BLOCK.byNameCodec() + .xmap(Waxable::new, Waxable::waxed); + public static final Codec CODEC = Codec.withAlternative( + RecordCodecBuilder.create(in -> in.group( + BuiltInRegistries.BLOCK.byNameCodec().fieldOf("waxed").forGetter(Waxable::waxed)).apply(in, Waxable::new)), + WAXABLE_CODEC); +} diff --git a/tests/src/generated/resources/data/neoforge/data_maps/block/oxidizables.json b/tests/src/generated/resources/data/neoforge/data_maps/block/oxidizables.json new file mode 100644 index 0000000000..c34d68020a --- /dev/null +++ b/tests/src/generated/resources/data/neoforge/data_maps/block/oxidizables.json @@ -0,0 +1,7 @@ +{ + "values": { + "neotests_oxidizables_and_waxables_map_test:lightly_oxidized_iron": { + "next_oxidation_stage": "neotests_oxidizables_and_waxables_map_test:more_oxidized_iron" + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/data/neoforge/data_maps/block/waxables.json b/tests/src/generated/resources/data/neoforge/data_maps/block/waxables.json new file mode 100644 index 0000000000..5ed8b1c096 --- /dev/null +++ b/tests/src/generated/resources/data/neoforge/data_maps/block/waxables.json @@ -0,0 +1,7 @@ +{ + "values": { + "neotests_oxidizables_and_waxables_map_test:lightly_oxidized_iron": { + "waxed": "neotests_oxidizables_and_waxables_map_test:lightly_oxidized_waxed_iron" + } + } +} \ No newline at end of file diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/data/DataMapTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/data/DataMapTests.java index bc28fee373..5e7c5bbfb3 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/data/DataMapTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/data/DataMapTests.java @@ -17,6 +17,8 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; import net.minecraft.gametest.framework.GameTest; @@ -31,10 +33,16 @@ import net.minecraft.world.damagesource.DamageType; import net.minecraft.world.damagesource.DamageTypes; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.HoneycombItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.ComposterBlock; +import net.minecraft.world.level.block.WeatheringCopper; +import net.minecraft.world.level.block.WeatheringCopperFullBlock; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.neoforged.neoforge.common.DataMapHooks; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.common.data.DataMapProvider; import net.neoforged.neoforge.debug.EventTests; @@ -49,6 +57,8 @@ import net.neoforged.neoforge.registries.datamaps.RegisterDataMapTypesEvent; import net.neoforged.neoforge.registries.datamaps.builtin.Compostable; import net.neoforged.neoforge.registries.datamaps.builtin.NeoForgeDataMaps; +import net.neoforged.neoforge.registries.datamaps.builtin.Oxidizable; +import net.neoforged.neoforge.registries.datamaps.builtin.Waxable; import net.neoforged.testframework.DynamicTest; import net.neoforged.testframework.annotation.ForEachTest; import net.neoforged.testframework.annotation.TestHolder; @@ -347,6 +357,97 @@ protected void gather() { }); } + /* + * 1. Lightly Oxidized Iron should oxidize into More Oxidized Iron + * 2. Lightly Oxidized Iron should wax into Lightly Oxidized Waxed Iron + * 3. Lightly Oxidized Waxed Iron should scrape off into Lightly Oxidized Iron + */ + @SuppressWarnings("DataFlowIssue") + @GameTest + @EmptyTemplate + @TestHolder(description = "Tests if existing and custom oxidizables and waxables work") + static void oxidizablesAndWaxablesMapTest(final DynamicTest test, final RegistrationHelper reg) { + BlockPos blockPos = new BlockPos(1, 1, 1); + + Holder lightlyOxidizedIron = reg.blocks().register("lightly_oxidized_iron", () -> new WeatheringCopperFullBlock(WeatheringCopper.WeatherState.EXPOSED, BlockBehaviour.Properties.of())); + Holder moreOxidizedIron = reg.blocks().register("more_oxidized_iron", () -> new WeatheringCopperFullBlock(WeatheringCopper.WeatherState.WEATHERED, BlockBehaviour.Properties.of())); + + Holder lightlyOxidizedWaxedIron = reg.blocks().register("lightly_oxidized_waxed_iron", () -> new Block(BlockBehaviour.Properties.of())); + + reg.addProvider(event -> new DataMapProvider(event.getGenerator().getPackOutput(), event.getLookupProvider()) { + @Override + protected void gather() { + builder(NeoForgeDataMaps.OXIDIZABLES) + .add(lightlyOxidizedIron, new Oxidizable(moreOxidizedIron.value()), false); + + builder(NeoForgeDataMaps.WAXABLES) + .add(lightlyOxidizedIron, new Waxable(lightlyOxidizedWaxedIron.value()), false); + } + }); + test.onGameTest(helper -> { + helper.assertFalse( + DataMapHooks.didHaveToFallbackToVanillaMaps, + "The Oxidizable and Waxable Data Map's should not have to fallback to vanilla maps in this gametest, something is very wrong!"); + + // -------------- Test added blocks -------------- \\ + // Test Lightly Oxidized Iron -> More Oxidized Iron + helper.setBlock(blockPos, lightlyOxidizedIron.value()); + if (DataMapHooks.getNextOxidizedStage(lightlyOxidizedIron.value()) == null) + helper.fail("Next oxidization state for lightly oxidized iron was null!"); + helper.setBlock(blockPos, DataMapHooks.getNextOxidizedStage(lightlyOxidizedIron.value())); + helper.assertBlock(blockPos, block -> moreOxidizedIron.value().equals(block), "Wanted: More Oxidized Iron but found something else!"); + + // Test Lightly Oxidized Iron -> Lightly Oxidized Waxed Iron + helper.setBlock(blockPos, lightlyOxidizedIron.value()); + if (DataMapHooks.getBlockWaxed(lightlyOxidizedIron.value()) == null) + helper.fail("Waxed state for lightly oxidized iron was null!"); + helper.setBlock(blockPos, DataMapHooks.getBlockWaxed(lightlyOxidizedIron.value())); + helper.assertBlock(blockPos, block -> lightlyOxidizedWaxedIron.value().equals(block), "Wanted: Lightly Oxidized Waxed Iron but found something else!"); + + // Test Lightly Oxidized Waxed Iron -> Lightly Oxidized Iron + helper.useOn(blockPos, Items.IRON_AXE.getDefaultInstance(), helper.makeMockPlayer(), Direction.NORTH); + helper.assertBlock(blockPos, block -> lightlyOxidizedIron.value().equals(block), "Wanted: Lightly Oxidized Iron but found something else!"); + + // -------------- Test vanilla blocks -------------- \\ + // Test Block of Copper -> Exposed Copper + helper.setBlock(blockPos, Blocks.COPPER_BLOCK); + if (DataMapHooks.getNextOxidizedStage(Blocks.COPPER_BLOCK) == null) + helper.fail("Next oxidization state for copper block was null!"); + helper.setBlock(blockPos, DataMapHooks.getNextOxidizedStage(Blocks.COPPER_BLOCK)); + helper.assertBlock(blockPos, Blocks.EXPOSED_COPPER::equals, "Wanted: Exposed Copper but found something else!"); + + // Test Block of Copper -> Waxed Block of Copper + helper.setBlock(blockPos, Blocks.COPPER_BLOCK); + if (DataMapHooks.getBlockWaxed(Blocks.COPPER_BLOCK) == null) + helper.fail("Waxed state for block of copper was null!"); + helper.setBlock(blockPos, DataMapHooks.getBlockWaxed(Blocks.COPPER_BLOCK)); + helper.assertBlock(blockPos, Blocks.WAXED_COPPER_BLOCK::equals, "Wanted: Waxed Copper of Block but found something else!"); + + // Test Waxed Block of Copper -> Block of Copper + helper.useOn(blockPos, Items.IRON_AXE.getDefaultInstance(), helper.makeMockPlayer(), Direction.NORTH); + helper.assertBlock(blockPos, Blocks.COPPER_BLOCK::equals, "Wanted: Block of Copper but found something else!"); + + // Test vanilla stuff + WeatheringCopper.NEXT_BY_BLOCK.get().forEach((before, after) -> { + helper.assertValueEqual(DataMapHooks.getNextOxidizedStage(before), after, "next oxidized stage of " + before.getName()); + }); + + WeatheringCopper.PREVIOUS_BY_BLOCK.get().forEach((after, before) -> { + helper.assertValueEqual(DataMapHooks.getPreviousOxidizedStage(after), before, "previous oxidized stage of " + before.getName()); + }); + + HoneycombItem.WAXABLES.get().forEach((before, after) -> { + helper.assertValueEqual(DataMapHooks.getBlockWaxed(before), after, "waxed version of " + before.getName()); + }); + + HoneycombItem.WAX_OFF_BY_BLOCK.get().forEach((after, before) -> { + helper.assertValueEqual(DataMapHooks.getBlockUnwaxed(after), before, "unwaxed version of " + before.getName()); + }); + + helper.succeed(); + }); + } + public record SomeObject( int intValue, String stringValue) {