Posted April 13, 20178 yr So I have created a coloured block which saves its colour in a tile entity and can be coloured using a coloured item (paint brush) using its NBT. Currently my tile entity works fine and the block works correctly until I break the block where when I do, I don't get the coloured block but a white version of my coloured block. I have tried to fix this using a SimpleNetworkWrapper having a message sent to the server when I break the block to then receive the tile entity data to then spawn the item. Unfortunately this does not work as the following error occurs: [Netty Server IO #1/ERROR] [FML]: FMLIndexedMessageCodec exception caught io.netty.handler.codec.DecoderException: java.lang.NullPointerException: Undefined message for discriminator 0 in channel boe at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:99) ~[MessageToMessageDecoder.class:4.0.23.Final] at io.netty.handler.codec.MessageToMessageCodec.channelRead(MessageToMessageCodec.java:111) ~[MessageToMessageCodec.class:4.0.23.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:333) [AbstractChannelHandlerContext.class:4.0.23.Final] at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:319) [AbstractChannelHandlerContext.class:4.0.23.Final] at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:787) [DefaultChannelPipeline.class:4.0.23.Final] at io.netty.channel.embedded.EmbeddedChannel.writeInbound(EmbeddedChannel.java:169) [EmbeddedChannel.class:4.0.23.Final] at net.minecraftforge.fml.common.network.internal.FMLProxyPacket.processPacket(FMLProxyPacket.java:111) [FMLProxyPacket.class:?] at net.minecraft.network.NetworkManager.channelRead0(NetworkManager.java:157) [NetworkManager.class:?] at net.minecraft.network.NetworkManager.channelRead0(NetworkManager.java:51) [NetworkManager.class:?] at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105) [SimpleChannelInboundHandler.class:4.0.23.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:333) [AbstractChannelHandlerContext.class:4.0.23.Final] at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:319) [AbstractChannelHandlerContext.class:4.0.23.Final] at net.minecraftforge.fml.common.network.handshake.NetworkDispatcher.handleServerSideCustomPacket(NetworkDispatcher.java:452) [NetworkDispatcher.class:?] at net.minecraftforge.fml.common.network.handshake.NetworkDispatcher.channelRead0(NetworkDispatcher.java:274) [NetworkDispatcher.class:?] at net.minecraftforge.fml.common.network.handshake.NetworkDispatcher.channelRead0(NetworkDispatcher.java:73) [NetworkDispatcher.class:?] at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105) [SimpleChannelInboundHandler.class:4.0.23.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:333) [AbstractChannelHandlerContext.class:4.0.23.Final] at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:319) [AbstractChannelHandlerContext.class:4.0.23.Final] at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:787) [DefaultChannelPipeline.class:4.0.23.Final] at io.netty.channel.local.LocalChannel.finishPeerRead(LocalChannel.java:326) [LocalChannel.class:4.0.23.Final] at io.netty.channel.local.LocalChannel.access$400(LocalChannel.java:45) [LocalChannel.class:4.0.23.Final] at io.netty.channel.local.LocalChannel$5.run(LocalChannel.java:312) [LocalChannel$5.class:4.0.23.Final] at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:380) [SingleThreadEventExecutor.class:4.0.23.Final] at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:357) [NioEventLoop.class:4.0.23.Final] at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116) [SingleThreadEventExecutor$2.class:4.0.23.Final] at java.lang.Thread.run(Unknown Source) [?:1.8.0_121] Caused by: java.lang.NullPointerException: Undefined message for discriminator 0 in channel boe at net.minecraftforge.fml.common.network.FMLIndexedMessageToMessageCodec.decode(FMLIndexedMessageToMessageCodec.java:99) ~[FMLIndexedMessageToMessageCodec.class:?] at net.minecraftforge.fml.common.network.FMLIndexedMessageToMessageCodec.decode(FMLIndexedMessageToMessageCodec.java:40) ~[FMLIndexedMessageToMessageCodec.class:?] at io.netty.handler.codec.MessageToMessageCodec$2.decode(MessageToMessageCodec.java:81) ~[MessageToMessageCodec$2.class:4.0.23.Final] at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:89) ~[MessageToMessageDecoder.class:4.0.23.Final] ... 25 more I have seen people have this error before and by looking on the forums I have found a solution which stops the error but does not fix the block dropping correctly. I have noticed that when (in my ClientProxy) I register the message like so: BitOfEverything.network.registerMessage(CanvasItemDropMessageHandler.class, CanvasItemDropMessage.class, Reference.PACKET_ID_CANVAS, Side.CLIENT); the error does not occur. When I remove this I get the previous error. And my packet id is not 0 it is actually 30. I currently send my packet here: In my block class: @Override public void harvestBlock(World world, EntityPlayer player, BlockPos pos, IBlockState state, TileEntity te, ItemStack stack) { if(!world.isRemote) BitOfEverything.network.sendToServer(new CanvasItemDropMessage(pos)); } Now one thing I do notice is that in my packet handler: public static class CanvasItemDropMessageHandler implements IMessageHandler<CanvasItemDropMessage, IMessage> { @Override public IMessage onMessage(CanvasItemDropMessage message, MessageContext ctx) { Utils.getLogger().info("Handling message! " + ctx.side.toString()); if(ctx.side == Side.SERVER && message.messageValid) { if(ctx.getServerHandler().playerEntity == null) return null; ctx.getServerHandler().playerEntity.getServerWorld().addScheduledTask(new Runnable() { @Override public void run() { Utils.getLogger().info("Server side!"); TileEntityCanvas canvas = (TileEntityCanvas) ctx.getServerHandler().playerEntity.getServerWorld().getTileEntity(message.pos); spawnEntity(ctx.getServerHandler().playerEntity.getServerWorld(), canvas, message.pos); } }); } return null; } void spawnEntity(World world, TileEntityCanvas canvas, BlockPos pos) { if(!world.isRemote && world.getGameRules().getBoolean("doTileDrops") && !world.restoringBlockSnapshots) { ItemStack stack = new ItemStack(ModBlocks.canvas); NBTTagCompound nbt = new NBTTagCompound(); nbt.setInteger("colour", canvas.getColour()); stack.setTagCompound(nbt); float f = 0.5F; double d0 = (double)(world.rand.nextFloat() * 0.5F) + 0.25D; double d1 = (double)(world.rand.nextFloat() * 0.5F) + 0.25D; double d2 = (double)(world.rand.nextFloat() * 0.5F) + 0.25D; EntityItem entityitem = new EntityItem(world, (double)pos.getX() + d0, (double)pos.getY() + d1, (double)pos.getZ() + d2, stack); entityitem.setDefaultPickupDelay(); world.spawnEntity(entityitem); Utils.getLogger().info("Spawned the item!"); } } } The handling message console message never occurs. It seems like my message either never hits the server or there is an error on the way. I'm not to sure what it is and I am intrigued to know my error. Just for use here is the code which seems relevant: The message and handler: package cjminecraft.bitofeverything.packets; import cjminecraft.bitofeverything.BitOfEverything; import cjminecraft.bitofeverything.init.ModBlocks; import cjminecraft.bitofeverything.tileentity.TileEntityCanvas; import cjminecraft.bitofeverything.util.Utils; import io.netty.buffer.ByteBuf; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.WorldClient; import net.minecraft.entity.item.EntityItem; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.play.server.SPacketSpawnObject; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraftforge.fml.common.network.simpleimpl.IMessage; import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; import net.minecraftforge.fml.relauncher.Side; public class CanvasItemDropMessage implements IMessage { private boolean messageValid; private BlockPos pos; public CanvasItemDropMessage() { this.messageValid = false; } public CanvasItemDropMessage(BlockPos pos) { this.pos = pos; this.messageValid = true; } @Override public void fromBytes(ByteBuf buf) { try { this.pos = new BlockPos(buf.readInt(), buf.readInt(), buf.readInt()); } catch (IndexOutOfBoundsException ioe) { Utils.getLogger().info(ioe.getMessage()); return; } this.messageValid = true; } @Override public void toBytes(ByteBuf buf) { if(!this.messageValid) return; buf.writeInt(this.pos.getX()); buf.writeInt(this.pos.getY()); buf.writeInt(this.pos.getZ()); } public static class CanvasItemDropMessageHandler implements IMessageHandler<CanvasItemDropMessage, IMessage> { @Override public IMessage onMessage(CanvasItemDropMessage message, MessageContext ctx) { Utils.getLogger().info("Handling message! " + ctx.side.toString()); if(ctx.side == Side.SERVER && message.messageValid) { if(ctx.getServerHandler().playerEntity == null) return null; ctx.getServerHandler().playerEntity.getServerWorld().addScheduledTask(new Runnable() { @Override public void run() { Utils.getLogger().info("Server side!"); TileEntityCanvas canvas = (TileEntityCanvas) ctx.getServerHandler().playerEntity.getServerWorld().getTileEntity(message.pos); spawnEntity(ctx.getServerHandler().playerEntity.getServerWorld(), canvas, message.pos); } }); } return null; } void spawnEntity(World world, TileEntityCanvas canvas, BlockPos pos) { if(!world.isRemote && world.getGameRules().getBoolean("doTileDrops") && !world.restoringBlockSnapshots) { ItemStack stack = new ItemStack(ModBlocks.canvas); NBTTagCompound nbt = new NBTTagCompound(); nbt.setInteger("colour", canvas.getColour()); stack.setTagCompound(nbt); float f = 0.5F; double d0 = (double)(world.rand.nextFloat() * 0.5F) + 0.25D; double d1 = (double)(world.rand.nextFloat() * 0.5F) + 0.25D; double d2 = (double)(world.rand.nextFloat() * 0.5F) + 0.25D; EntityItem entityitem = new EntityItem(world, (double)pos.getX() + d0, (double)pos.getY() + d1, (double)pos.getZ() + d2, stack); entityitem.setDefaultPickupDelay(); world.spawnEntity(entityitem); Utils.getLogger().info("Spawned the item!"); } } } } The block: package cjminecraft.bitofeverything.blocks; import java.util.ArrayList; import java.util.List; import cjminecraft.bitofeverything.BitOfEverything; import cjminecraft.bitofeverything.Reference; import cjminecraft.bitofeverything.init.ModBlocks; import cjminecraft.bitofeverything.init.ModItems; import cjminecraft.bitofeverything.packets.CanvasItemDropMessage; import cjminecraft.bitofeverything.tileentity.TileEntityCanvas; import cjminecraft.bitofeverything.util.Utils; import net.minecraft.block.BlockContainer; import net.minecraft.block.ITileEntityProvider; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.I18n; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumBlockRenderType; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.text.TextFormatting; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import net.minecraftforge.fml.common.network.NetworkRegistry.TargetPoint; /** * A block which can be coloured when clicked on by a paint brush * @author CJMinecraft * */ public class BlockCanvas extends BlockContainer implements ITileEntityProvider { /** * Default block constructor * @param unlocalizedName The unlocalised name of the block */ public BlockCanvas(String unlocalizedName) { super(Material.CLOTH); this.setUnlocalizedName(unlocalizedName); this.setRegistryName(new ResourceLocation(Reference.MODID, unlocalizedName)); this.setHardness(1); this.setResistance(5); this.isBlockContainer = true; //Says it is a block container } /** * This will change the blocks colour when clicked on by a paint brush */ @Override public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) { if(player.getHeldItem(hand).getItem() == ModItems.paintBrush) { TileEntityCanvas canvas = (TileEntityCanvas) world.getTileEntity(pos); canvas.setColour(player.getHeldItem(hand).getTagCompound().getInteger("colour")); world.markBlockRangeForRenderUpdate(pos, pos); } return false; } /** * Says what colour the block is */ @Override public void addInformation(ItemStack stack, EntityPlayer player, List<String> tooltip, boolean advanced) { if(stack.hasTagCompound()) if(stack.getTagCompound().hasKey("colour")) tooltip.add(TextFormatting.GRAY + I18n.format(getUnlocalizedName() + ".tooltip", String.format("#%06X", (0xFFFFFF & stack.getTagCompound().getInteger("colour"))))); } /** * Makes it so that when you pick block, you get the correct block */ @Override public ItemStack getPickBlock(IBlockState state, RayTraceResult target, World world, BlockPos pos, EntityPlayer player) { TileEntityCanvas canvas = (TileEntityCanvas) world.getTileEntity(pos); ItemStack stack = new ItemStack(ModBlocks.canvas); NBTTagCompound nbt = new NBTTagCompound(); if(canvas != null) nbt.setInteger("colour", canvas.getColour()); else nbt.setInteger("colour", 0xFFFFFF); stack.setTagCompound(nbt); return stack; } /** * Needed to make sure our block is rendered correctly */ @Override public EnumBlockRenderType getRenderType(IBlockState state) { return EnumBlockRenderType.MODEL; } /** * Create the tile entity */ @Override public TileEntity createTileEntity(World world, IBlockState state) { return new TileEntityCanvas(); } /** * Also create the tile entity */ @Override public TileEntity createNewTileEntity(World world, int meta) { return new TileEntityCanvas(); } /** * When placed it will update the colour to that of the tile entity which the tile entity inherits from the item block */ @Override public void onBlockPlacedBy(World world, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack) { if(!world.isRemote) { TileEntityCanvas canvas = (TileEntityCanvas) world.getTileEntity(pos); if(canvas != null && stack.hasTagCompound() && stack.getTagCompound().hasKey("colour")) canvas.setColour(stack.getTagCompound().getInteger("colour")); } world.markBlockRangeForRenderUpdate(pos, pos); //Update the blocks render } /** * Not fully functioning. Works with <code> /setblock <x> <y> <z> air 0 destroy </code> but not when breaking normally with your hand */ @Override public List<ItemStack> getDrops(IBlockAccess world, BlockPos pos, IBlockState state, int fortune) { List<ItemStack> drops = new ArrayList<ItemStack>(); TileEntityCanvas canvas = (TileEntityCanvas) world.getTileEntity(pos); ItemStack stack = new ItemStack(ModBlocks.canvas); NBTTagCompound nbt = new NBTTagCompound(); if(canvas != null) //When you break it with a command nbt.setInteger("colour", canvas.getColour()); else //When you break it with your hand nbt.setInteger("colour", 0xFFFFFF); stack.setTagCompound(nbt); drops.add(stack); return drops; } @Override public void harvestBlock(World world, EntityPlayer player, BlockPos pos, IBlockState state, TileEntity te, ItemStack stack) { if(!world.isRemote) BitOfEverything.network.sendToServer(new CanvasItemDropMessage(pos)); } } The Tile Entity: package cjminecraft.bitofeverything.tileentity; import net.minecraft.block.state.IBlockState; 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.math.BlockPos; import net.minecraft.world.World; /** * The canvas tile entity which stores the blocks colour * @author CJMinecraft * */ public class TileEntityCanvas extends TileEntity { //The colour as an int private int colour; /** * Initialise the colour to be white */ public TileEntityCanvas() { this.colour = 0xFFFFFF; } /** * Read the colour from NBT data */ @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); this.colour = nbt.getInteger("colour"); } /** * Write the correct NBT data */ @Override public NBTTagCompound writeToNBT(NBTTagCompound nbt) { nbt.setInteger("colour", this.colour); return super.writeToNBT(nbt); } /** * Get the colour of the canvas * @return The colour of the canvas */ public int getColour() { return colour; } /** * Set the colour of the canvas * @param colour The new colour of the canvas */ public void setColour(int colour) { this.colour = colour; } /** * The packet which is used to update the tile entity which holds all of the * tileentities data */ @Override public SPacketUpdateTileEntity getUpdatePacket() { NBTTagCompound nbt = new NBTTagCompound(); this.writeToNBT(nbt); int metadata = getBlockMetadata(); return new SPacketUpdateTileEntity(this.pos, metadata, nbt); } /** * Reads the nbt when it receives a packet */ @Override public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) { this.readFromNBT(pkt.getNbtCompound()); } /** * Gets the nbt for a new packet */ @Override public NBTTagCompound getUpdateTag() { NBTTagCompound nbt = new NBTTagCompound(); this.writeToNBT(nbt); return nbt; } /** * Handles when you get an update */ @Override public void handleUpdateTag(NBTTagCompound tag) { this.readFromNBT(tag); } /** * Gets the tile entities nbt with all of the data stored in it */ @Override public NBTTagCompound getTileData() { NBTTagCompound nbt = new NBTTagCompound(); this.writeToNBT(nbt); return nbt; } } Registration of the packet: (called in pre init in the common proxy) BitOfEverything.network.registerMessage(CanvasItemDropMessageHandler.class, CanvasItemDropMessage.class, Reference.PACKET_ID_CANVAS, Side.SERVER); All help is greatly appreciated. I hope someone will be able to help me figure this out!
April 13, 20178 yr You shouldn't need a packet to do this. getDrops is called after the Block has been removed, which means the TileEntity has also already been removed so you can't get the colour information there. Instead you can override harvestBlock and use the TileEntity parameter (which should be an instance of your TE) to get the colour and spawn the drops yourself.
April 13, 20178 yr Author Just tried that with this code: @Override public void harvestBlock(World world, EntityPlayer player, BlockPos pos, IBlockState state, TileEntity te, ItemStack stack) { TileEntityCanvas canvas = (TileEntityCanvas) te; ItemStack itemstack = new ItemStack(ModBlocks.canvas); NBTTagCompound nbt = new NBTTagCompound(); if(canvas != null) nbt.setInteger("colour", canvas.getColour()); else nbt.setInteger("colour", 0xFFFFFF); stack.setTagCompound(nbt); spawnAsEntity(world, pos, itemstack); } Still doesn't work. It leaves it with the white colour while the block I am breaking is orange in the tile entity Edited April 13, 20178 yr by CJMinecraft
April 14, 20178 yr Author Thanks. That fixed the block drop. Unfortunately when I place the coloured block the tile entity receives the colour correctly. The particles of the block are coloured correctly but the actual block remain white until another canvas block is placed.
April 14, 20178 yr You need to update the current chunk using World markRangeForRenderUpdate catch(Exception e) { } Yay, Pokémon exception handling, gotta catch 'em all (and then do nothing with 'em).
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.