From 69ddf586000117f38ac516680c5a02db7a2e8280 Mon Sep 17 00:00:00 2001 From: Dennis C Date: Wed, 20 Oct 2021 20:57:33 +0200 Subject: [PATCH] Add accessor to an unmodifiable view of the fullPots map in FlowerPotBlock (#8108) --- .../level/block/FlowerPotBlock.java.patch | 6 +- .../debug/block/FullPotsAccessorDemo.java | 311 ++++++++++++++++++ src/test/resources/META-INF/mods.toml | 2 + .../blockstates/diorite_pot.json | 5 + .../models/block/diorite_pot.json | 10 + 5 files changed, 333 insertions(+), 1 deletion(-) create mode 100644 src/test/java/net/minecraftforge/debug/block/FullPotsAccessorDemo.java create mode 100644 src/test/resources/assets/full_pots_accessor_demo/blockstates/diorite_pot.json create mode 100644 src/test/resources/assets/full_pots_accessor_demo/models/block/diorite_pot.json diff --git a/patches/minecraft/net/minecraft/world/level/block/FlowerPotBlock.java.patch b/patches/minecraft/net/minecraft/world/level/block/FlowerPotBlock.java.patch index 4017fe7a53..41765d73a9 100644 --- a/patches/minecraft/net/minecraft/world/level/block/FlowerPotBlock.java.patch +++ b/patches/minecraft/net/minecraft/world/level/block/FlowerPotBlock.java.patch @@ -55,7 +55,7 @@ } p_53541_.m_142346_(p_53543_, GameEvent.f_157792_, p_53542_); -@@ -86,10 +_,27 @@ +@@ -86,10 +_,31 @@ } public Block m_53560_() { @@ -82,5 +82,9 @@ + } + fullPots.put(flower, fullPot); + } ++ ++ public Map> getFullPotsView() { ++ return java.util.Collections.unmodifiableMap(fullPots); ++ } + //Forge End } diff --git a/src/test/java/net/minecraftforge/debug/block/FullPotsAccessorDemo.java b/src/test/java/net/minecraftforge/debug/block/FullPotsAccessorDemo.java new file mode 100644 index 0000000000..31ec1c7d63 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/block/FullPotsAccessorDemo.java @@ -0,0 +1,311 @@ +package net.minecraftforge.debug.block; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonObject; +import com.mojang.datafixers.util.Pair; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.ItemBlockRenderTypes; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.model.*; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.*; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.stats.Stats; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.*; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.*; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.ModelRegistryEvent; +import net.minecraftforge.client.model.*; +import net.minecraftforge.client.model.data.*; +import net.minecraftforge.client.model.geometry.IModelGeometry; +import net.minecraftforge.common.util.Constants; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.fmllegacy.RegistryObject; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Mod(FullPotsAccessorDemo.MOD_ID) +public class FullPotsAccessorDemo +{ + public static final String MOD_ID = "full_pots_accessor_demo"; + private static final boolean ENABLED = true; + + private static final DeferredRegister BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MOD_ID); + private static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MOD_ID); + private static final DeferredRegister> BLOCK_ENTITIES = DeferredRegister.create(ForgeRegistries.BLOCK_ENTITIES, MOD_ID); + + private static final RegistryObject DIORITE_POT = BLOCKS.register("diorite_pot", DioriteFlowerPotBlock::new); + private static final RegistryObject DIORITE_POT_ITEM = ITEMS.register( + "diorite_pot", + () -> new BlockItem(DIORITE_POT.get(), new Item.Properties().tab(CreativeModeTab.TAB_MISC)) + ); + private static final RegistryObject> DIORITE_POT_BLOCK_ENTITY = BLOCK_ENTITIES.register( + "diorite_pot", + () -> BlockEntityType.Builder.of(DioriteFlowerPotBlockEntity::new, DIORITE_POT.get()).build(null) + ); + + public FullPotsAccessorDemo() + { + if (ENABLED) + { + IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus(); + BLOCKS.register(bus); + ITEMS.register(bus); + BLOCK_ENTITIES.register(bus); + } + } + + private static class DioriteFlowerPotBlock extends Block implements EntityBlock + { + private static final VoxelShape SHAPE = Block.box(5.0D, 0.0D, 5.0D, 11.0D, 6.0D, 11.0D); + + public DioriteFlowerPotBlock() + { + super(Properties.of(Material.DECORATION).instabreak().noOcclusion()); + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) + { + if (level.getBlockEntity(pos) instanceof DioriteFlowerPotBlockEntity be) + { + ItemStack stack = player.getItemInHand(hand); + boolean isFlower = stack.getItem() instanceof BlockItem item && ((FlowerPotBlock) Blocks.FLOWER_POT).getFullPotsView().containsKey(item.getRegistryName()); + boolean hasFlower = be.plant != Blocks.AIR; + + if (isFlower != hasFlower) + { + if (!level.isClientSide()) + { + if (isFlower) + { + be.setPlant(((BlockItem) stack.getItem()).getBlock()); + + player.awardStat(Stats.POT_FLOWER); + if (!player.getAbilities().instabuild) + { + stack.shrink(1); + } + } + else + { + ItemStack flowerStack = new ItemStack(be.getPlant()); + if (stack.isEmpty()) + { + player.setItemInHand(hand, flowerStack); + } + else if (!player.addItem(flowerStack)) + { + player.drop(flowerStack, false); + } + + be.setPlant(Blocks.AIR); + } + } + + level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos); + return InteractionResult.sidedSuccess(level.isClientSide()); + } + else + { + return InteractionResult.CONSUME; + } + } + return super.use(state, level, pos, player, hand, hit); + } + + @Override + @SuppressWarnings("deprecation") + public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) + { + return SHAPE; + } + + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) + { + return new DioriteFlowerPotBlockEntity(pos, state); + } + } + + private static class DioriteFlowerPotBlockEntity extends BlockEntity + { + public static final ModelProperty PLANT_PROPERTY = new ModelProperty<>(); + + private final IModelData modelData = new ModelDataMap.Builder().build(); + private Block plant = Blocks.AIR; + + public DioriteFlowerPotBlockEntity(BlockPos pos, BlockState state) + { + super(DIORITE_POT_BLOCK_ENTITY.get(), pos, state); + modelData.setData(PLANT_PROPERTY, plant); + } + + public void setPlant(Block plant) + { + this.plant = plant; + //noinspection ConstantConditions + level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), Constants.BlockFlags.DEFAULT); + setChanged(); + } + + public Block getPlant() { return plant; } + + @Override + public IModelData getModelData() { return modelData; } + + @Override + public CompoundTag getUpdateTag() + { + return save(new CompoundTag()); + } + + @Override + public void handleUpdateTag(CompoundTag tag) + { + super.handleUpdateTag(tag); + modelData.setData(PLANT_PROPERTY, plant); + requestModelDataUpdate(); + } + + @Override + public ClientboundBlockEntityDataPacket getUpdatePacket() + { + return new ClientboundBlockEntityDataPacket(getBlockPos(), -1, getUpdateTag()); + } + + @Override + public void onDataPacket(Connection net, ClientboundBlockEntityDataPacket pkt) + { + handleUpdateTag(pkt.getTag()); + //noinspection ConstantConditions + level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), Constants.BlockFlags.DEFAULT); + } + + @Override + public void load(CompoundTag tag) + { + super.load(tag); + plant = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(tag.getString("plant"))); + } + + @Override + public CompoundTag save(CompoundTag tag) + { + //noinspection ConstantConditions + tag.putString("plant", plant.getRegistryName().toString()); + return super.save(tag); + } + } + + @Mod.EventBusSubscriber(modid = MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD) + private static class ClientHandler + { + @SubscribeEvent + public static void onClientSetup(final FMLClientSetupEvent event) + { + if (!ENABLED) { return; } + + ItemBlockRenderTypes.setRenderLayer(DIORITE_POT.get(), RenderType.cutout()); + } + + @SubscribeEvent + public static void registerLoader(final ModelRegistryEvent event) + { + ModelLoaderRegistry.registerLoader(new ResourceLocation(MOD_ID, "diorite_pot"), new DioritePotModelLoader()); + } + + private static class DioritePotModelLoader implements IModelLoader + { + @Override + public void onResourceManagerReload(ResourceManager manager) { } + + @Override + public DioritePotModelGeometry read(JsonDeserializationContext context, JsonObject modelContents) + { + JsonObject wrappedModel = modelContents.getAsJsonObject("model"); + return new DioritePotModelGeometry(context.deserialize(wrappedModel, BlockModel.class)); + } + } + + private record DioritePotModelGeometry(UnbakedModel wrappedModel) implements IModelGeometry + { + @Override + public BakedModel bake(IModelConfiguration owner, ModelBakery bakery, Function spriteGetter, ModelState modelTransform, ItemOverrides overrides, ResourceLocation modelLocation) + { + return new DioritePotModel(wrappedModel.bake(bakery, spriteGetter, modelTransform, modelLocation)); + } + + @Override + public Collection getTextures(IModelConfiguration owner, Function modelGetter, Set> missingTextureErrors) + { + return wrappedModel.getMaterials(modelGetter, missingTextureErrors); + } + } + + private static class DioritePotModel extends BakedModelWrapper + { + private static final ResourceLocation POT_TEXTURE = new ResourceLocation("minecraft:block/flower_pot"); + private static final ResourceLocation DIRT_TEXTURE = new ResourceLocation("minecraft:block/dirt"); + + public DioritePotModel(BakedModel wrappedModel) { super(wrappedModel); } + + @Nonnull + @Override + public List getQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull Random rand, @Nonnull IModelData extraData) + { + List quads = new ArrayList<>(originalModel.getQuads(state, side, rand, extraData)); + + Block plant = extraData.getData(DioriteFlowerPotBlockEntity.PLANT_PROPERTY); + if (plant != null && plant != Blocks.AIR) + { + quads.addAll(getPlantQuads(plant, side, rand)); + } + + return quads; + } + + private List getPlantQuads(Block plant, @Nullable Direction face, Random rand) + { + BlockState potState = ((FlowerPotBlock) Blocks.FLOWER_POT).getFullPotsView().getOrDefault(plant.getRegistryName(), Blocks.AIR.delegate).get().defaultBlockState(); + BakedModel potModel = Minecraft.getInstance().getBlockRenderer().getBlockModel(potState); + + return potModel.getQuads(potState, face, rand, EmptyModelData.INSTANCE) + .stream() + .filter(q -> !q.getSprite().getName().equals(POT_TEXTURE)) + .filter(q -> !q.getSprite().getName().equals(DIRT_TEXTURE)) + .collect(Collectors.toList()); + } + } + } +} diff --git a/src/test/resources/META-INF/mods.toml b/src/test/resources/META-INF/mods.toml index c8e1566b80..3d2b7ede92 100644 --- a/src/test/resources/META-INF/mods.toml +++ b/src/test/resources/META-INF/mods.toml @@ -166,5 +166,7 @@ license="LGPL v2.1" modId="forge_spawnegg_test" [[mods]] modId="custom_tooltip_test" +[[mods]] + modId="full_pots_accessor_demo" # ADD ABOVE THIS LINE diff --git a/src/test/resources/assets/full_pots_accessor_demo/blockstates/diorite_pot.json b/src/test/resources/assets/full_pots_accessor_demo/blockstates/diorite_pot.json new file mode 100644 index 0000000000..240bcea0e9 --- /dev/null +++ b/src/test/resources/assets/full_pots_accessor_demo/blockstates/diorite_pot.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "full_pots_accessor_demo:block/diorite_pot" } + } +} diff --git a/src/test/resources/assets/full_pots_accessor_demo/models/block/diorite_pot.json b/src/test/resources/assets/full_pots_accessor_demo/models/block/diorite_pot.json new file mode 100644 index 0000000000..d5a34aee29 --- /dev/null +++ b/src/test/resources/assets/full_pots_accessor_demo/models/block/diorite_pot.json @@ -0,0 +1,10 @@ +{ + "loader": "full_pots_accessor_demo:diorite_pot", + "model": { + "parent": "minecraft:block/flower_pot", + "textures": { + "flowerpot": "minecraft:block/diorite", + "particle": "minecraft:block/diorite" + } + } +}