Fluidlogged API

Fluidlogged API

356k Downloads

How do I implement this for custom tile entities?

embeddedt opened this issue ยท 7 comments

commented

Context: I'm trying to backport cable waterlogging to More Refined Storage 1.12 using this mod.

I have added IFluidloggable to the cable block, and changed the cable's tile entity class (TileCable) to extend TileEntityFluidloggable. However, this is not working properly.

Upon doing some more tinkering with other blocks (BlockImporter implements IFluidloggable, TileImporter extends TileEntityFluidlogged), I have run into this exception:

Caused by: java.lang.ClassCastException: git.jbredwards.fluidlogged_api.common.block.TileEntityFluidlogged cannot be cast to com.raoulvdberge.refinedstorage.tile.TileImporter
        at com.raoulvdberge.refinedstorage.gui.GuiHandler.getContainer(GuiHandler.java:26) ~[GuiHandler.class:?]
        at com.raoulvdberge.refinedstorage.gui.GuiHandler.getServerGuiElement(GuiHandler.java:78) ~[GuiHandler.class:?]
        at net.minecraftforge.fml.common.network.NetworkRegistry.getRemoteGuiContainer(NetworkRegistry.java:253) ~[NetworkRegistry.class:?]
        at net.minecraftforge.fml.common.network.internal.FMLNetworkHandler.openGui(FMLNetworkHandler.java:88) ~[FMLNetworkHandler.class:?]
        at net.minecraft.entity.player.EntityPlayer.openGui(EntityPlayer.java:2870) ~[EntityPlayer.class:?]
        at com.raoulvdberge.refinedstorage.block.BlockNodeProxy.lambda$openNetworkGui$0(BlockNodeProxy.java:63) ~[BlockNodeProxy.class:?]
        at com.raoulvdberge.refinedstorage.block.BlockNodeProxy.openNetworkGui(BlockNodeProxy.java:92) ~[BlockNodeProxy.class:?]
        at com.raoulvdberge.refinedstorage.block.BlockNodeProxy.openNetworkGui(BlockNodeProxy.java:63) ~[BlockNodeProxy.class:?]
        at com.raoulvdberge.refinedstorage.block.BlockNodeProxy.openNetworkGui(BlockNodeProxy.java:59) ~[BlockNodeProxy.class:?]
        at com.raoulvdberge.refinedstorage.block.BlockImporter.onBlockActivated(BlockImporter.java:77) ~[BlockImporter.class:?]
        at git.jbredwards.fluidlogged_api.common.block.BlockFluidloggedTE.onBlockActivated(BlockFluidloggedTE.java:359) ~[BlockFluidloggedTE.class:?]
        at net.minecraft.server.management.PlayerInteractionManager.processRightClickBlock(PlayerInteractionManager.java:475) ~[PlayerInteractionManager.class:?]
        at net.minecraft.network.NetHandlerPlayServer.processTryUseItemOnBlock(NetHandlerPlayServer.java:769) ~[NetHandlerPlayServer.class:?]
        at net.minecraft.network.play.client.CPacketPlayerTryUseItemOnBlock.processPacket(CPacketPlayerTryUseItemOnBlock.java:68) ~[CPacketPlayerTryUseItemOnBlock.class:?]
        at net.minecraft.network.play.client.CPacketPlayerTryUseItemOnBlock.processPacket(CPacketPlayerTryUseItemOnBlock.java:13) ~[CPacketPlayerTryUseItemOnBlock.class:?]
        at net.minecraft.network.PacketThreadUtil$1.run(PacketThreadUtil.java:21) ~[PacketThreadUtil$1.class:?]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[?:1.8.0_281]
        at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[?:1.8.0_281]
        at net.minecraft.util.Util.runTask(Util.java:53) ~[Util.class:?]
        ... 5 more

The only explanation I see for this is that all fluidlogged blocks are forced to have the exact class TileEntityFluidlogged as their tile entity, and not a subclass, but this doesn't make sense.

Also, I'm not sure if extending TileEntityFluidlogged is even the right approach here, as this would make the mod dependent on having Fluidlogged API installed.

Any help would be great!

commented

You're correct, extending TileEntityFluidlogged isn't the intended way of handling fluidloggable tile entity data. The TileEntityFluidlogged class has an internal cache intended for storing your own tile entity's data for when your block is fluidlogged. To transfer your tile entity's data to the built-in fluidlogged one when the block gets fluidlogged, and to set your tile entity in the world when the block gets un-fluidlogged, you'll have to use the Fluidlog & UnFluidlog events. You can also look at the FluidloggedUtils class to get a better idea of this kind of stuff. I should really make a wiki to help explain, but I'm very limited on time rn. I hope this helped until then though!

Here's some example code of what using this looks like:

public static final boolean isFluidloggedAPI = Loader.isModLoaded("fluidlogged_api");

//lets just pretend that TileCable stores an EnumFacing value for this example, though any value type can be used
public static EnumFacing getCableFacing(TileEntity te) {
        //base mod function
        if(te instanceof TileCable) return ((TileCable)te).getCableFacing();
        //fluidlogged api mod compat
        else if(isFluidloggedAPI && te instanceof TileEntityFluidlogged) 
            //the fluidlogged te cache stores all custom values in an array, for this example 0 is the index you've 
            //previously stored the value
            return ((TileEntityFluidlogged)te).getValue(0, fallbackValue);
        //default return
        else return fallbackValue;
    }
commented

Hmm, okay. So the issue here boils down to the fact that the original tile entity is not used at all when a block is fluidlogged?

If so, that's going to be a roadblock, as the entire codebase is assuming that the original tile entity is still available. Changing that would be a major refactoring.

Is it possible on your end to inject all of the fluidlogged TE logic into TileEntity itself using ASM, but only save/read extra NBT if the attached block is IFluidloggable? This should maintain 100% compatibility with existing code, as no existing mods will be implementing that interface. You could then turn TileEntityFluidlogged into a simple stub which is attached to the vanilla blocks which don't already have a tile entity. This might provide a solution to the chest problem as well.

Just trying to think of some ways to simplify the implementation in individual mods, as the current approach seems a bit primitive compared to the abstraction tile entities normally provide.

commented

This sounds like it has a lot of potential, I'll see what I can do!

commented

Sorry if this is disappointing, but I'm starting college tomorrow, so unfortunately I wasn't able to overcome some of the issues in time, and probably won't be able to for a little while. I've created a new branch with the changes I've been able to get done these past few days, so you could see and maybe expand on them if you'd like this to be done quickly. Getting the existing tile entities to remain when fluidlogged & unfluidlogged was what I couldn't figure out.

commented

Overall things are going very well! The biggest change from v1.6.2 I think is the use of Chunk Capabilities to store the fluidlogged fluids in a Chunk as FluidState objects. This fixes all the problems of having the fluidlogged fluid blocks, and frees up tile entities from being used at all! Unfortunately I've hit a roadblock with this, the ICapabilitySerializable I'm using isn't reloading the saved FluidStates properly on world load, and I'm not sure why.

Edit: while I was working on another problem I accidently fixed the roadblock as well. I'll commit the changes later today, I still don't know why it works, but I'm glad it does lol :)

commented

Hi @jbredwards, how are things going? I've checked out the latest 1.12.2-v1.7-Rewrite commit and it's very impressive! I see that you got it working for chests as well. I just needed to remove two semicolons in some ASM hooks to prevent a crash:

diff --git a/src/main/java/git/jbredwards/fluidlogged_api/asm/plugins/vanilla/DragonSpawnManagerPlugin.java b/src/main/java/git/jbredwards/fluidlogged_api/asm/plugins/vanilla/DragonSpawnManagerPlugin.java
index 795586d..8037de9 100644
--- a/src/main/java/git/jbredwards/fluidlogged_api/asm/plugins/vanilla/DragonSpawnManagerPlugin.java
+++ b/src/main/java/git/jbredwards/fluidlogged_api/asm/plugins/vanilla/DragonSpawnManagerPlugin.java
@@ -23,7 +23,7 @@ public final class DragonSpawnManagerPlugin implements IASMPlugin
             final InsnList list = new InsnList();
             list.add(new FieldInsnNode(GETSTATIC, "net/minecraft/init/Blocks", obfuscated ? "field_150350_a" : "AIR", "Lnet/minecraft/block/Block;"));
             list.add(new MethodInsnNode(INVOKEVIRTUAL, "net/minecraft/block/Block", obfuscated ? "func_176223_P" : "getDefaultState", "()Lnet/minecraft/block/state/IBlockState;", false));
-            list.add(new MethodInsnNode(INVOKEVIRTUAL, "net/minecraft/world/World;", obfuscated ? "func_175656_a" : "setBlockState", "(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/state/IBlockState;)Z", false));
+            list.add(new MethodInsnNode(INVOKEVIRTUAL, "net/minecraft/world/World", obfuscated ? "func_175656_a" : "setBlockState", "(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/state/IBlockState;)Z", false));
 
             instructions.insert(insn, list);
             instructions.remove(insn);
diff --git a/src/main/java/git/jbredwards/fluidlogged_api/asm/plugins/vanilla/WorldGenDungeonsPlugin.java b/src/main/java/git/jbredwards/fluidlogged_api/asm/plugins/vanilla/WorldGenDungeonsPlugin.java
index 53a0efe..57dc17e 100644
--- a/src/main/java/git/jbredwards/fluidlogged_api/asm/plugins/vanilla/WorldGenDungeonsPlugin.java
+++ b/src/main/java/git/jbredwards/fluidlogged_api/asm/plugins/vanilla/WorldGenDungeonsPlugin.java
@@ -23,7 +23,7 @@ public final class WorldGenDungeonsPlugin implements IASMPlugin
             final InsnList list = new InsnList();
             list.add(new FieldInsnNode(GETSTATIC, "net/minecraft/init/Blocks", obfuscated ? "field_150350_a" : "AIR", "Lnet/minecraft/block/Block;"));
             list.add(new MethodInsnNode(INVOKEVIRTUAL, "net/minecraft/block/Block", obfuscated ? "func_176223_P" : "getDefaultState", "()Lnet/minecraft/block/state/IBlockState;", false));
-            list.add(new MethodInsnNode(INVOKEVIRTUAL, "net/minecraft/world/World;", obfuscated ? "func_175656_a" : "setBlockState", "(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/state/IBlockState;)Z", false));
+            list.add(new MethodInsnNode(INVOKEVIRTUAL, "net/minecraft/world/World", obfuscated ? "func_175656_a" : "setBlockState", "(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/state/IBlockState;)Z", false));
 
             instructions.insert(insn, list);
             instructions.remove(insn);
commented

while I was working on another problem I accidently fixed the roadblock as well. The patch will be out later today, I still don't know why it works, but I'm glad it does lol :)

I've just spent an hour or two trying to track it down, so that works out. ๐Ÿ˜†