Posted November 15, 20168 yr I have a block and an associated ITickable tile entity. Everything works fine, until I quit the world and re-load it. After that, the server-side tile entity loads up fine, but the client-side one seems to disappear. (Tested by having the tile entity's update() method output this.worldObj.isRemote to the system log. It outputs for both client and server after placing the block, but only for the server after reloading the world.) I've organized my code in such a way as to try and make adding new blocks, items, etc. easier on myself later, so there's lots of inheritance and classes which register themselves on instantiation. I'm wondering if, while setting up that structure, I've missed something somewhere? Here's the relevant code: ChaoticaBlockBase.java: package com.icemetalpunk.chaotica.blocks; import com.icemetalpunk.chaotica.Chaotica; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.util.ResourceLocation; import net.minecraftforge.fml.common.registry.GameRegistry; import net.minecraftforge.oredict.OreDictionary; public class ChaoticaBlockBase extends Block { public ChaoticaBlockBase(String name, Material materialIn) { super(materialIn); this.setUnlocalizedName(name).setRegistryName(new ResourceLocation(Chaotica.MODID, name)) .setCreativeTab(Chaotica.tab); } protected void register() { GameRegistry.register(this); if (this instanceof ChaoticaTEBlock) { GameRegistry.registerTileEntity(((ChaoticaTEBlock) this).getTileEntityClass(), ((ChaoticaTEBlock) this).getTileEntityName()); } String[] oreDict = this.getOreDict(); if (oreDict != null) { for (String entry : oreDict) { OreDictionary.registerOre(entry, this); } } } // Override this if this block has an oredict entry. public String[] getOreDict() { return null; } } ChaoticaTEBlockBase.java (For tile entity providing blocks.) package com.icemetalpunk.chaotica.blocks; import net.minecraft.block.ITileEntityProvider; import net.minecraft.block.material.Material; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; public abstract class ChaoticaTEBlock extends ChaoticaBlockBase implements ITileEntityProvider { public ChaoticaTEBlock(String name, Material materialIn) { super(name, materialIn); } // Tile entity providers should provide the class and name of their tile // entity here for registration. public abstract Class<? extends TileEntity> getTileEntityClass(); public abstract String getTileEntityName(); // Generic createNewTileEntity so only the getTileEntityClass needs to be // specified. @Override public TileEntity createNewTileEntity(World world, int meta) { try { return this.getTileEntityClass().newInstance(); } catch (InstantiationException e) { e.printStackTrace(); return null; } catch (IllegalAccessException e) { e.printStackTrace(); return null; } } } Here's the specific block in question: BlockChaoticCondenser.java: package com.icemetalpunk.chaotica.blocks; import javax.annotation.Nullable; import com.icemetalpunk.chaotica.Chaotica; import com.icemetalpunk.chaotica.gui.ChaoticaGuiHandler; import com.icemetalpunk.chaotica.tileentities.TileEntityChaoticCondenser; import net.minecraft.block.SoundType; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; public class BlockChaoticCondenser extends ChaoticaTEBlock { public BlockChaoticCondenser() { super("chaotic_condenser", Material.ROCK); this.setHardness(3.5F); this.setSoundType(SoundType.STONE); this.register(); } @Override public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, @Nullable ItemStack item, EnumFacing side, float hitX, float hitY, float hitZ) { if (!world.isRemote) { player.openGui(Chaotica.instance, ChaoticaGuiHandler.Guis.CONDENSER.ordinal(), world, pos.getX(), pos.getY(), pos.getZ()); } return true; } @Override public Class<? extends TileEntity> getTileEntityClass() { return TileEntityChaoticCondenser.class; } @Override public String getTileEntityName() { return "ChaoticCondenser"; } } And the tile entity, or at least the relevant code: TileEntityChaoticCondenser.java: package com.icemetalpunk.chaotica.tileentities; import java.util.Iterator; import java.util.Map; import com.icemetalpunk.chaotica.Chaotica; import com.icemetalpunk.chaotica.ChaoticaUtils; import com.icemetalpunk.chaotica.fluids.FluidTankChaos; import com.icemetalpunk.chaotica.sounds.ChaoticaSoundRegistry; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.NetworkManager; import net.minecraft.network.play.server.SPacketUpdateTileEntity; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ITickable; import net.minecraft.util.SoundCategory; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.FluidTankPropertiesWrapper; import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.fluids.capability.IFluidTankProperties; public class TileEntityChaoticCondenser extends TileEntity implements IFluidHandler, ITickable { protected Fluid fluid = Chaotica.fluids.CORROSIVE_CHAOS; protected int capacity = 5 * Fluid.BUCKET_VOLUME; protected FluidStack fluidStack = new FluidStack(this.fluid, 0); protected FluidTankChaos tank = new FluidTankChaos(this.capacity); protected int countdown = 40; protected int maxCountdown = 40; // Max amount it resets to public TileEntityChaoticCondenser() { this.tank.setCanFill(false); } // Ticks until the next check for blocks to convert to chaos public int getCountdown() { return this.countdown; } public int getMaxCountdown() { return this.maxCountdown; } @Override public IFluidTankProperties[] getTankProperties() { return new IFluidTankProperties[] { new FluidTankPropertiesWrapper(tank) }; }; public Fluid getFluid() { return this.fluidStack.getFluid(); } @Override public void readFromNBT(NBTTagCompound tag) { super.readFromNBT(tag); NBTTagCompound tankTag = tag.getCompoundTag("Tank"); this.tank.readFromNBT(tankTag); this.countdown = tag.getInteger("Countdown"); } @Override public NBTTagCompound writeToNBT(NBTTagCompound tag) { super.writeToNBT(tag); NBTTagCompound tankTag = new NBTTagCompound(); tank.writeToNBT(tankTag); tag.setTag("Tank", tankTag); tag.setInteger("Countdown", this.countdown); return tag; } public int fill(int amount, boolean doFill) { return this.tank.fill(new FluidStack(this.fluid, amount), doFill); } @Override public int fill(FluidStack resource, boolean doFill) { return this.tank.fill(resource, doFill); } @Override public FluidStack drain(FluidStack resource, boolean doDrain) { return this.tank.drain(resource, doDrain); } @Override public FluidStack drain(int maxDrain, boolean doDrain) { return this.tank.drain(maxDrain, doDrain); } @Override public void update() { String[] debug = new String[] { "server", "client" }; if (--this.countdown == 0) { this.countdown = this.maxCountdown; System.out.println("Countdown on " + debug[this.worldObj.isRemote ? 1 : 0] + " to " + this.countdown); // ...snipped out irrelevant code here... } } @Override public SPacketUpdateTileEntity getUpdatePacket() { NBTTagCompound tag = new NBTTagCompound(); this.writeToNBT(tag); IBlockState state = this.worldObj.getBlockState(this.pos); Block block = state.getBlock(); int metadata = block.getMetaFromState(state); return new SPacketUpdateTileEntity(this.pos, metadata, tag); } @Override public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity packet) { this.readFromNBT(packet.getNbtCompound()); } } If you look in the tile entity's update() method, you'll see the simple debug output I'm using. So why does it all work fine when I place the block, but after reloading the world, all the messages from the client-side tile entity stop, as though it's just removed and not re-created when the world loads again? Whatever Minecraft needs, it is most likely not yet another tool tier.
November 15, 20168 yr In 1.10.2 TE's also need to override TileEntity#getUpdateTag() . There is also a corresponding TileEntity#handleUpdateTag() method, which you will need to override if you want to do anything other than calling TileEntity#readFromNBT (This is what the default implementation does). I don't know for sure that this is your problem, but trying doesn't hurt. These methods are certainly involved in client-server communications though.
November 15, 20168 yr Author Huh. Weird that there are now two pairs of methods that seem to do the same thing for updates... well, I just overrode them and it indeed fixed the problem immediately Thank you! Whatever Minecraft needs, it is most likely not yet another tool tier.
November 15, 20168 yr It's not directly related to your question, but you're using IFluidHandler incorrectly. The whole point of the Capability system is that you don't implement interfaces on your TileEntity , instead you store the objects in the TileEntity and override the ICapabilityProvider methods to return them. Forge's documentation explains this in more detail here. Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
November 15, 20168 yr Also don't implement ITileEntityProvider. You want the hasTileEntity and getTileEntity methods that exist in the Block class (hint: the one you're using is wrong). Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable. If you think this is the case, JUST REPORT ME. Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice. Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked. DO NOT PM ME WITH PROBLEMS. No help will be given.
November 16, 20168 yr Author Also don't implement ITileEntityProvider. You want the hasTileEntity and getTileEntity methods that exist in the Block class (hint: the one you're using is wrong). Okay, I've switched to using the ones from the Block class; but it seems like the default implementations of those methods check for ITileEntityProvider anyway and ultimately end up doing the same thing; what's the benefit to switching over? It's not directly related to your question, but you're using IFluidHandler incorrectly. The whole point of the Capability system is that you don't implement interfaces on your TileEntity , instead you store the objects in the TileEntity and override the ICapabilityProvider methods to return them. Forge's documentation explains this in more detail here. I'll admit, after reading the documentation for Capabilities, as well as some example code elsewhere...I'm totally confused by the system. So basically, I would attach an instance of the IFluidHandler Capability to the tile entity in the AttachCapabilities event, one instance per tank in the tile entity, then override the tile entity's getCapability/hasCapability methods to return those fluid handler instances? And then whenever it needs to fill/drain anything, it would...uh, pick one of the capabilities and call the fill/drain methods on them? I really don't understand what's going on there, nor do I understand what the system is supposed to improve over simply implementing the handler interface on the tile entity itself... Whatever Minecraft needs, it is most likely not yet another tool tier.
November 16, 20168 yr I'll admit, after reading the documentation for Capabilities, as well as some example code elsewhere...I'm totally confused by the system. So basically, I would attach an instance of the IFluidHandler Capability to the tile entity in the AttachCapabilities event, one instance per tank in the tile entity, then override the tile entity's getCapability/hasCapability methods to return those fluid handler instances? And then whenever it needs to fill/drain anything, it would...uh, pick one of the capabilities and call the fill/drain methods on them? I really don't understand what's going on there, nor do I understand what the system is supposed to improve over simply implementing the handler interface on the tile entity itself... Only use AttachCapabilitiesEvent to attach capabilities to external objects. For your own objects, simply store the capability instance in a field and override the ICapabilityProvider methods. Let's say your TileEntity has a fluid tank, so it stores an instance of FluidTank in a field. If you want to expose that tank as a capability (so other mods can interact with it), override the following methods: hasCapability : Return true if the Capability argument is CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY and the EnumFacing is correct. getCapability : Return the FluidTank instance if the Capability argument is CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY and the EnumFacing is correct. Use Capability#cast to cast the object you return from this method to the correct type (this is to work around the limitations of generics, Java doesn't know that the object you're returning is an instance of the generic return type). For both methods, return the result of the super method if the Capability or EnumFacing isn't one that you handle. If the tank can be accessed from all sides, you can ignore the EnumFacing argument entirely. Some of the main advantages of the capability system are the ability to attach your own capabilities to external objects (e.g. give a vanilla item a fluid tank or inventory) and the ability to split up your code into multiple classes and use existing classes (e.g. you create a TileEntity that uses the existing FluidTank and ItemStackHandler classes instead of a TileEntity that implements IFluidHandler and IItemHandler itself). Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
November 16, 20168 yr Okay, I've switched to using the ones from the Block class; but it seems like the default implementations of those methods check for ITileEntityProvider anyway and ultimately end up doing the same thing; what's the benefit to switching over? Its only still there for compatibility on things that haven't been updated to the new style yet. The method you're using takes in a metadata value, not a blockstate, thus is out of date. Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable. If you think this is the case, JUST REPORT ME. Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice. Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked. DO NOT PM ME WITH PROBLEMS. No help will be given.
November 17, 20168 yr Author ...snip... Thank you for your clear explanation! I've switched over to the Capability system for my tile entity now, and everything still works, which I'll take as a good sign. I can certainly see the advantage of better mod interoperability. Now if only I could get the fluid tank rendering to work properly...but that's a different bug for a different day, and one for me to try and figure out first before asking! Thanks again. Whatever Minecraft needs, it is most likely not yet another tool tier.
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.