diff --git a/common/src/main/java/dev/architectury/registry/level/block/BlockFlammabilityRegistry.java b/common/src/main/java/dev/architectury/registry/level/block/BlockFlammabilityRegistry.java new file mode 100644 index 000000000..0ea235d0a --- /dev/null +++ b/common/src/main/java/dev/architectury/registry/level/block/BlockFlammabilityRegistry.java @@ -0,0 +1,84 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.level.block; + +import dev.architectury.injectables.annotations.ExpectPlatform; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.tags.TagKey; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; + +public final class BlockFlammabilityRegistry { + /** + * Gets the burn odds for the given block. + * + * @param level the level + * @param state the block state + * @param pos the position of the block + * @param direction the direction of where the fire is coming from + * @return the burn odds + */ + @ExpectPlatform + public static int getBurnOdds(BlockGetter level, BlockState state, BlockPos pos, Direction direction) { + throw new AssertionError(); + } + + /** + * Gets the flame odds for the given block. + * + * @param level the level + * @param state the block state + * @param pos the position of the block + * @param direction the direction of where the fire is spreading from + * @return the flame odds + */ + @ExpectPlatform + public static int getFlameOdds(BlockGetter level, BlockState state, BlockPos pos, Direction direction) { + throw new AssertionError(); + } + + /** + * Registers the flammability for the given blocks for a given fire block. + * + * @param fireBlock the specific fire block + * @param burnOdds the burn odds + * @param flameOdds the flame odds + * @param flammableBlocks the flammable blocks + */ + @ExpectPlatform + public static void register(Block fireBlock, int burnOdds, int flameOdds, Block... flammableBlocks) { + throw new AssertionError(); + } + + /** + * Registers the flammability for a given block tag for a given fire block. + * + * @param fireBlock the specific fire block + * @param burnOdds the burn odds + * @param flameOdds the flame odds + * @param flammableBlocks the flammable block tag + */ + @ExpectPlatform + public static void register(Block fireBlock, int burnOdds, int flameOdds, TagKey flammableBlocks) { + throw new AssertionError(); + } +} diff --git a/fabric/src/main/java/dev/architectury/registry/level/block/fabric/BlockFlammabilityRegistryImpl.java b/fabric/src/main/java/dev/architectury/registry/level/block/fabric/BlockFlammabilityRegistryImpl.java new file mode 100644 index 000000000..6339dcbca --- /dev/null +++ b/fabric/src/main/java/dev/architectury/registry/level/block/fabric/BlockFlammabilityRegistryImpl.java @@ -0,0 +1,67 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.level.block.fabric; + +import net.fabricmc.fabric.api.registry.FlammableBlockRegistry; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.tags.TagKey; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.FireBlock; +import net.minecraft.world.level.block.state.BlockState; + +public class BlockFlammabilityRegistryImpl { + public static int getBurnOdds(BlockGetter level, BlockState state, BlockPos pos, Direction direction) { + BlockState fireState = level.getBlockState(pos.relative(direction)); + + if (fireState.getBlock() instanceof FireBlock fireBlock) { + FlammableBlockRegistry.Entry entry = FlammableBlockRegistry.getInstance(fireBlock).get(state.getBlock()); + return entry.getBurnChance(); + } else { + FlammableBlockRegistry.Entry entry = FlammableBlockRegistry.getDefaultInstance().get(state.getBlock()); + return entry.getBurnChance(); + } + } + + public static int getFlameOdds(BlockGetter level, BlockState state, BlockPos pos, Direction direction) { + BlockState fireState = level.getBlockState(pos.relative(direction)); + + if (fireState.getBlock() instanceof FireBlock fireBlock) { + FlammableBlockRegistry.Entry entry = FlammableBlockRegistry.getInstance(fireBlock).get(state.getBlock()); + return entry.getSpreadChance(); + } else { + FlammableBlockRegistry.Entry entry = FlammableBlockRegistry.getDefaultInstance().get(state.getBlock()); + return entry.getSpreadChance(); + } + } + + public static void register(Block fireBlock, int burnOdds, int flameOdds, Block... flammableBlocks) { + FlammableBlockRegistry registry = FlammableBlockRegistry.getInstance(fireBlock); + for (Block block : flammableBlocks) { + registry.add(block, burnOdds, flameOdds); + } + } + + public static void register(Block fireBlock, int burnOdds, int flameOdds, TagKey flammableBlocks) { + FlammableBlockRegistry registry = FlammableBlockRegistry.getInstance(fireBlock); + registry.add(flammableBlocks, burnOdds, flameOdds); + } +} diff --git a/forge/src/main/java/dev/architectury/mixin/forge/MixinIForgeBlock.java b/forge/src/main/java/dev/architectury/mixin/forge/MixinIForgeBlock.java new file mode 100644 index 000000000..38c05f238 --- /dev/null +++ b/forge/src/main/java/dev/architectury/mixin/forge/MixinIForgeBlock.java @@ -0,0 +1,27 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.mixin.forge; + +import net.minecraftforge.common.extensions.IForgeBlock; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(IForgeBlock.class) +public interface MixinIForgeBlock { +} diff --git a/forge/src/main/java/dev/architectury/plugin/forge/ArchitecturyMixinPlugin.java b/forge/src/main/java/dev/architectury/plugin/forge/ArchitecturyMixinPlugin.java index 59171e257..5a44e093a 100644 --- a/forge/src/main/java/dev/architectury/plugin/forge/ArchitecturyMixinPlugin.java +++ b/forge/src/main/java/dev/architectury/plugin/forge/ArchitecturyMixinPlugin.java @@ -19,12 +19,15 @@ package dev.architectury.plugin.forge; -import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.*; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; import org.spongepowered.asm.mixin.extensibility.IMixinInfo; import java.util.List; import java.util.Set; +import java.util.stream.Stream; public class ArchitecturyMixinPlugin implements IMixinConfigPlugin { @Override @@ -59,6 +62,46 @@ public void preApply(String targetClassName, ClassNode targetClass, String mixin @Override public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - + if ("dev.architectury.mixin.forge.MixinIForgeBlock".equals(mixinClassName)) { + for (MethodNode method : targetClass.methods) { + if ("getFlammability".equals(method.name) && method.desc.endsWith(")I")) { + AbstractInsnNode last = method.instructions.getLast(); + // iterate backwards to find the last IRETURN + while (last != null && !(last instanceof InsnNode && last.getOpcode() == Opcodes.IRETURN)) { + last = last.getPrevious(); + } + + if (last != null) { + // insert a call to BlockFlammabilityRegistryImpl.handleFlammabilityHook + method.instructions.insertBefore(last, new VarInsnNode(Opcodes.ALOAD, 1)); // BlockState + method.instructions.insertBefore(last, new VarInsnNode(Opcodes.ALOAD, 2)); // BlockGetter + method.instructions.insertBefore(last, new VarInsnNode(Opcodes.ALOAD, 3)); // BlockPos + method.instructions.insertBefore(last, new VarInsnNode(Opcodes.ALOAD, 4)); // Direction + Type type = Type.getMethodType(method.desc); + Type[] types = type.getArgumentTypes(); + String newDesc = Type.getMethodDescriptor(type.getReturnType(), Stream.concat(Stream.of(Type.INT_TYPE), Stream.of(types)).toArray(Type[]::new)); + method.instructions.insertBefore(last, new MethodInsnNode(Opcodes.INVOKESTATIC, "dev/architectury/mixin/forge/BlockFlammabilityRegistryImpl", "handleBurnOddsHook", newDesc, false)); + } + } else if ("getFireSpreadSpeed".equals(method.name) && method.desc.endsWith(")I")) { + AbstractInsnNode last = method.instructions.getLast(); + // iterate backwards to find the last IRETURN + while (last != null && !(last instanceof InsnNode && last.getOpcode() == Opcodes.IRETURN)) { + last = last.getPrevious(); + } + + if (last != null) { + // insert a call to BlockFlammabilityRegistryImpl.handleFlammabilityHook + method.instructions.insertBefore(last, new VarInsnNode(Opcodes.ALOAD, 1)); // BlockState + method.instructions.insertBefore(last, new VarInsnNode(Opcodes.ALOAD, 2)); // BlockGetter + method.instructions.insertBefore(last, new VarInsnNode(Opcodes.ALOAD, 3)); // BlockPos + method.instructions.insertBefore(last, new VarInsnNode(Opcodes.ALOAD, 4)); // Direction + Type type = Type.getMethodType(method.desc); + Type[] types = type.getArgumentTypes(); + String newDesc = Type.getMethodDescriptor(type.getReturnType(), Stream.concat(Stream.of(Type.INT_TYPE), Stream.of(types)).toArray(Type[]::new)); + method.instructions.insertBefore(last, new MethodInsnNode(Opcodes.INVOKESTATIC, "dev/architectury/mixin/forge/BlockFlammabilityRegistryImpl", "handleSpreadOddsHook", newDesc, false)); + } + } + } + } } } diff --git a/forge/src/main/java/dev/architectury/registry/level/block/forge/BlockFlammabilityRegistryImpl.java b/forge/src/main/java/dev/architectury/registry/level/block/forge/BlockFlammabilityRegistryImpl.java new file mode 100644 index 000000000..2f45b347b --- /dev/null +++ b/forge/src/main/java/dev/architectury/registry/level/block/forge/BlockFlammabilityRegistryImpl.java @@ -0,0 +1,145 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.level.block.forge; + +import dev.architectury.registry.ReloadListenerRegistry; +import it.unimi.dsi.fastutil.objects.Reference2IntMap; +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2ObjectMap; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.server.packs.PackType; +import net.minecraft.tags.TagKey; +import net.minecraft.util.Unit; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.FireBlock; +import net.minecraft.world.level.block.state.BlockState; + +public class BlockFlammabilityRegistryImpl { + public static int getBurnOdds(BlockGetter level, BlockState state, BlockPos pos, Direction direction) { + return state.getFlammability(level, pos, direction); + } + + public static int getFlameOdds(BlockGetter level, BlockState state, BlockPos pos, Direction direction) { + return state.getFireSpreadSpeed(level, pos, direction); + } + + private static final Reference2ObjectMap DATAS = new Reference2ObjectOpenHashMap<>(); + + private static Data getData(Block fireBlock) { + if (fireBlock instanceof FireBlock) { + return DATAS.computeIfAbsent(fireBlock, $ -> new Data()); + } else { + throw new IllegalArgumentException("Expected fire block, got " + fireBlock); + } + } + + private static class Data { + private final Reference2IntMap burnOdds = new Reference2IntOpenHashMap<>(); + private final Reference2IntMap flameOdds = new Reference2IntOpenHashMap<>(); + private final Reference2IntMap> burnTagOdds = new Reference2IntOpenHashMap<>(); + private final Reference2IntMap> flameTagOdds = new Reference2IntOpenHashMap<>(); + private final Reference2IntMap burnCollectedOdds = new Reference2IntOpenHashMap<>(); + private final Reference2IntMap flameCollectedOdds = new Reference2IntOpenHashMap<>(); + + int getBurnOdds(Block block) { + return burnCollectedOdds.getOrDefault(block, -1); + } + + int getFlameOdds(Block block) { + return flameCollectedOdds.getOrDefault(block, -1); + } + } + + static { + ReloadListenerRegistry.register(PackType.SERVER_DATA, (preparationBarrier, resourceManager, profilerFiller, profiler2, executor, executor2) -> { + return preparationBarrier.wait(Unit.INSTANCE).thenRunAsync(() -> { + profiler2.startTick(); + profiler2.push("handle_arch_flammables"); + for (Data data : DATAS.values()) { + data.burnCollectedOdds.clear(); + data.flameCollectedOdds.clear(); + data.burnCollectedOdds.putAll(data.burnOdds); + data.flameCollectedOdds.putAll(data.flameOdds); + + for (Reference2IntMap.Entry> entry : data.burnTagOdds.reference2IntEntrySet()) { + for (Holder holder : Registry.BLOCK.getTagOrEmpty(entry.getKey())) { + data.burnCollectedOdds.put(holder.value(), entry.getIntValue()); + } + } + + for (Reference2IntMap.Entry> entry : data.flameTagOdds.reference2IntEntrySet()) { + for (Holder holder : Registry.BLOCK.getTagOrEmpty(entry.getKey())) { + data.flameCollectedOdds.put(holder.value(), entry.getIntValue()); + } + } + } + profiler2.pop(); + profiler2.endTick(); + }, executor2); + }); + } + + public static void register(Block fireBlock, int burnOdds, int flameOdds, Block... flammableBlocks) { + Data data = getData(fireBlock); + + for (Block block : flammableBlocks) { + data.burnOdds.put(block, burnOdds); + data.flameOdds.put(block, burnOdds); + } + } + + public static void register(Block fireBlock, int burnOdds, int flameOdds, TagKey flammableBlocks) { + Data data = getData(fireBlock); + + data.burnTagOdds.put(flammableBlocks, burnOdds); + data.flameTagOdds.put(flammableBlocks, burnOdds); + } + + public static int handleBurnOddsHook(int previousValue, BlockState state, BlockGetter level, BlockPos pos, Direction direction) { + BlockState fireState = level.getBlockState(pos.relative(direction)); + + if (fireState.getBlock() instanceof FireBlock fireBlock) { + int odds = getData(fireBlock).getBurnOdds(state.getBlock()); + if (odds >= 0) { + return odds; + } + } + + return previousValue; + } + + public static int handleSpreadOddsHook(int previousValue, BlockState state, BlockGetter level, BlockPos pos, Direction direction) { + BlockState fireState = level.getBlockState(pos.relative(direction)); + + if (fireState.getBlock() instanceof FireBlock fireBlock) { + int odds = getData(fireBlock).getFlameOdds(state.getBlock()); + if (odds >= 0) { + return odds; + } + } + + return previousValue; + } +} diff --git a/forge/src/main/resources/architectury.mixins.json b/forge/src/main/resources/architectury.mixins.json index ceb99d845..efe98b5b0 100644 --- a/forge/src/main/resources/architectury.mixins.json +++ b/forge/src/main/resources/architectury.mixins.json @@ -12,6 +12,7 @@ "MixinChunkSerializer", "MixinEntitySpawnExtension", "MixinFallingBlockEntity", + "MixinIForgeBlock", "MixinItemExtension", "MixinRegistryEntry", "MixinWorldEvent"