strumshot Posted September 18, 2014 Posted September 18, 2014 So I'm working with a custom block/gui and have some buttons on the gui that are changing a value stored in a TileEntity. Unless I am mistaken the button actions and therefore the value changes corresponding to pressing them occur on client side only. I have, then, set up a packet to be sent to the server so this value can be updated globally and other players "see" the updated values. I am using the SimpleNetworkWrapper so I am simply sending a string. I imagine I will be sending a constructed string containing an id for the TileEntity and the updated value, and then grabbing that TileEntity by that id on the server/packet receive? Should I be using hashCode? And what list/map am I looking it up in? Or should I be approaching this some other way... thanks for any pointers! (I mean tips, not programmatic 'pointers' lol - bad programming joke) Quote I'll need help, and I'll give help. Just ask, you know I will!
strumshot Posted September 18, 2014 Author Posted September 18, 2014 Yeah, it sure does. I'd be happy to share code, but I have no errors so to speak.. Its a block, TileEntity, Container, and gui. The setup displays the player inventory, the 12-slot 'inventory' of the container, the 'value' in question (currently held by the TileEntity) and buttons to change the value. I have a blank string packet sent to server upon button click. Quote I'll need help, and I'll give help. Just ask, you know I will!
strumshot Posted September 18, 2014 Author Posted September 18, 2014 ..so at the end of the day I know the button clicks happen privately on the client, but I need them to adjust the value on the server so any other player would see the updated value when opening the gui. Maybe I misunderstand what automatically gets synced to the server, and maybe I am storing the value in the wrong spot (I have options. Container, gui, TileEntity...) but I can't find a solution and its a hard topic to google! Quote I'll need help, and I'll give help. Just ask, you know I will!
strumshot Posted September 18, 2014 Author Posted September 18, 2014 Thanks for the quick responses! I should clarify that when I say 'button' I mean a clickable gui control. And then my follow-up question is such: my packet handling method currently has no objects to access aside from the packet class and the message text. That is why I imagined I would be sending an ID of sorts in the string, and then looking that ID up in a static server list. Does that make sense? Something like... protected void actionPerformed(GuiButton button) { ++chest.Cost; IShop.network.sendToServer(new ChangeCost(chest.hashCode() + ":" + chest.Cost)); break; public static class Handler implements IMessageHandler<ChangeCost, IMessage> { @Override public IMessage onMessage(ChangeCost message, MessageContext ctx) { String[] incoming = message.text.split(":"); if(incoming != null && incoming.length == 2){ int hash = Integer.parseInt(incoming[0]); int cost = Integer.parseInt(incoming[1]); // lookup chest # here and adjust value of 'cost' Quote I'll need help, and I'll give help. Just ask, you know I will!
strumshot Posted September 18, 2014 Author Posted September 18, 2014 Aha! I hadn't found that in the context, and was therefore trying to invent a solution. Thank you so much! Quote I'll need help, and I'll give help. Just ask, you know I will!
strumshot Posted September 18, 2014 Author Posted September 18, 2014 this is extra nice because I can shorten the message to one character - no numbers - and do all the logic on the server side! whenever I find my code getting long and myself inventing solutions there is usually a simpler already available approach.and it usually gets pointed out to me by you. Haha at this point you have virtually written half of my mods and I'm sure I'm not the only one on this forum with that experience. Quote I'll need help, and I'll give help. Just ask, you know I will!
strumshot Posted September 18, 2014 Author Posted September 18, 2014 right that is basically my point. I guess I could make 2 separate packet classes as there are two different buttons. I was using one packet for both buttons; the character would be either a plus or a minus - or whatever. An increment button and a decrement button. i 100% see your point although in this scenario cheating isn't really possible but I will still take that advice! Quote I'll need help, and I'll give help. Just ask, you know I will!
strumshot Posted September 19, 2014 Author Posted September 19, 2014 Okay.. Setting up the custom packets and sending safe information to the server upon button click, I'm good. But that's as far as I can get! Ugh. I have a few issues. Ultimately I'm not sure how to send that update back to the client (and others who may open this container.) I've played with many methods and approaches, such as simply reversing the process; but there seems to be no direct access to the player through the client side of the handler to grab the open container from. Also, upon closing the container, the value seems to reset on the server side - I am logging the server-side value every time the server receives the packet. I'm writing the value to NBT in the generic writeToNBT method, which doesn't seem to affect it. Again, I'm sure there's some simple mechanism already in place for updating custom values in a TileEntity and then propogating that update back to nearby clients; maybe through a custom packet and sendToAllAround()? Any tutorials I've found near this topic are deprecated which is frustrating! Quote I'll need help, and I'll give help. Just ask, you know I will!
strumshot Posted September 19, 2014 Author Posted September 19, 2014 Update: of course, the moment I posted I solved the issue of getting the info back to the client... at least I think. player.worldObj.markBlockForUpdate(container.tileEntity.xCoord, container.tileEntity.yCoord, container.tileEntity.zCoord); return new SendCost(container.tileEntity.Cost); public static class Handler implements IMessageHandler<SendCost, IMessage> { @Override public IMessage onMessage(SendCost message, MessageContext ctx) { EntityPlayer player = Minecraft.getMinecraft().thePlayer; ContainerShop container = (ContainerShop)player.openContainer; container.tileEntity.Cost = message.value; But the value is still resetting upon close. Quote I'll need help, and I'll give help. Just ask, you know I will!
strumshot Posted September 19, 2014 Author Posted September 19, 2014 TileEntity package com.kotr.ishop.tile_entity; import java.util.Iterator; import java.util.List; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; import com.kotr.ishop.block.ShopChest; import com.kotr.ishop.container.ContainerShop; public class TileEntityShopChest extends TileEntity implements IInventory{ private ItemStack[] chestContents = new ItemStack[39]; private String customName; public int numPlayersUsing; public float prevLidAngle; public float lidAngle; private int ticksSinceSync; private Object adjacentChestZNeg; private Object adjacentChestXNeg; private Object adjacentChestZPos; private Object adjacentChestXPos; public int Cost; /** * Returns the name of the inventory */ public String getInventoryName() { return this.hasCustomInventoryName() ? this.customName : "Shop Inventory"; } /** * Returns the stack in slot i */ public ItemStack getStackInSlot(int p_70301_1_) { return this.chestContents[p_70301_1_]; } /** * Removes from an inventory slot (first arg) up to a specified number (second arg) of items and returns them in a * new stack. */ public ItemStack decrStackSize(int p_70298_1_, int p_70298_2_) { if (this.chestContents[p_70298_1_] != null) { ItemStack itemstack; if (this.chestContents[p_70298_1_].stackSize <= p_70298_2_) { itemstack = this.chestContents[p_70298_1_]; this.chestContents[p_70298_1_] = null; this.markDirty(); return itemstack; } else { itemstack = this.chestContents[p_70298_1_].splitStack(p_70298_2_); if (this.chestContents[p_70298_1_].stackSize == 0) { this.chestContents[p_70298_1_] = null; } this.markDirty(); return itemstack; } } else { return null; } } /** * When some containers are closed they call this on each slot, then drop whatever it returns as an EntityItem - * like when you close a workbench GUI. */ public ItemStack getStackInSlotOnClosing(int p_70304_1_) { if (this.chestContents[p_70304_1_] != null) { ItemStack itemstack = this.chestContents[p_70304_1_]; this.chestContents[p_70304_1_] = null; return itemstack; } else { return null; } } /** * Sets the given item stack to the specified slot in the inventory (can be crafting or armor sections). */ public void setInventorySlotContents(int p_70299_1_, ItemStack p_70299_2_) { this.chestContents[p_70299_1_] = p_70299_2_; if (p_70299_2_ != null && p_70299_2_.stackSize > this.getInventoryStackLimit()) { p_70299_2_.stackSize = this.getInventoryStackLimit(); } this.markDirty(); } public void readFromNBT(NBTTagCompound p_145839_1_) { super.readFromNBT(p_145839_1_); NBTTagList nbttaglist = p_145839_1_.getTagList("Items", 10); this.chestContents = new ItemStack[this.getSizeInventory()]; if (p_145839_1_.hasKey("Shop Chest", ) { this.customName = p_145839_1_.getString("Shop Chest"); } this.Cost = p_145839_1_.getByte("Cost"); for (int i = 0; i < nbttaglist.tagCount(); ++i) { NBTTagCompound nbttagcompound1 = nbttaglist.getCompoundTagAt(i); int j = nbttagcompound1.getByte("Slot") & 255; if (j >= 0 && j < this.chestContents.length) { this.chestContents[j] = ItemStack.loadItemStackFromNBT(nbttagcompound1); } } } public void writeToNBT(NBTTagCompound p_145841_1_) { super.writeToNBT(p_145841_1_); NBTTagList nbttaglist = new NBTTagList(); for (int i = 0; i < this.chestContents.length; ++i) { if (this.chestContents[i] != null) { NBTTagCompound nbttagcompound1 = new NBTTagCompound(); nbttagcompound1.setByte("Slot", (byte)i); this.chestContents[i].writeToNBT(nbttagcompound1); nbttaglist.appendTag(nbttagcompound1); } } p_145841_1_.setTag("Items", nbttaglist); p_145841_1_.setByte("Cost", (byte)this.Cost); if (this.hasCustomInventoryName()) { p_145841_1_.setString("Shop Chest", this.customName); } } // private boolean func_145977_a(int p_145977_1_, int p_145977_2_, int p_145977_3_) // { // if (this.worldObj == null) // { // return false; // } // else // { // Block block = this.worldObj.getBlock(p_145977_1_, p_145977_2_, p_145977_3_); // return block instanceof ShopChest && ((ShopChest)block).field_149956_a == this.func_145980_j(); // } // } public void func_145976_a(String p_145976_1_) { this.customName = p_145976_1_; } public void openInventory() { if (this.numPlayersUsing < 0) { this.numPlayersUsing = 0; } ++this.numPlayersUsing; this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, this.getBlockType(), 1, this.numPlayersUsing); this.worldObj.notifyBlocksOfNeighborChange(this.xCoord, this.yCoord, this.zCoord, this.getBlockType()); this.worldObj.notifyBlocksOfNeighborChange(this.xCoord, this.yCoord - 1, this.zCoord, this.getBlockType()); } public void closeInventory() { if (this.getBlockType() instanceof ShopChest) { --this.numPlayersUsing; this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, this.getBlockType(), 1, this.numPlayersUsing); this.worldObj.notifyBlocksOfNeighborChange(this.xCoord, this.yCoord, this.zCoord, this.getBlockType()); this.worldObj.notifyBlocksOfNeighborChange(this.xCoord, this.yCoord - 1, this.zCoord, this.getBlockType()); } } @Override public int getSizeInventory() { // TODO Auto-generated method stub return this.chestContents.length; } @Override public boolean hasCustomInventoryName() { // TODO Auto-generated method stub return false; } @Override public int getInventoryStackLimit() { // TODO Auto-generated method stub return 64; } @Override public boolean isUseableByPlayer(EntityPlayer p_70300_1_) { return this.worldObj.getTileEntity(this.xCoord, this.yCoord, this.zCoord) != this ? false : p_70300_1_.getDistanceSq((double)this.xCoord + 0.5D, (double)this.yCoord + 0.5D, (double)this.zCoord + 0.5D) <= 64.0D; } @Override public boolean isItemValidForSlot(int p_94041_1_, ItemStack p_94041_2_) { // TODO Auto-generated method stub return true; } public void updateEntity() { super.updateEntity(); ++this.ticksSinceSync; float f; if (!this.worldObj.isRemote && this.numPlayersUsing != 0 && (this.ticksSinceSync + this.xCoord + this.yCoord + this.zCoord) % 200 == 0) { this.numPlayersUsing = 0; f = 5.0F; List list = this.worldObj.getEntitiesWithinAABB(EntityPlayer.class, AxisAlignedBB.getBoundingBox((double)((float)this.xCoord - f), (double)((float)this.yCoord - f), (double)((float)this.zCoord - f), (double)((float)(this.xCoord + 1) + f), (double)((float)(this.yCoord + 1) + f), (double)((float)(this.zCoord + 1) + f))); Iterator iterator = list.iterator(); while (iterator.hasNext()) { EntityPlayer entityplayer = (EntityPlayer)iterator.next(); if (entityplayer.openContainer instanceof ContainerShop) { ++this.numPlayersUsing; } } } this.prevLidAngle = this.lidAngle; f = 0.1F; double d2; if (this.numPlayersUsing > 0 && this.lidAngle == 0.0F && this.adjacentChestZNeg == null && this.adjacentChestXNeg == null) { double d1 = (double)this.xCoord + 0.5D; d2 = (double)this.zCoord + 0.5D; if (this.adjacentChestZPos != null) { d2 += 0.5D; } if (this.adjacentChestXPos != null) { d1 += 0.5D; } this.worldObj.playSoundEffect(d1, (double)this.yCoord + 0.5D, d2, "random.chestopen", 0.5F, this.worldObj.rand.nextFloat() * 0.1F + 0.9F); } if (this.numPlayersUsing == 0 && this.lidAngle > 0.0F || this.numPlayersUsing > 0 && this.lidAngle < 1.0F) { float f1 = this.lidAngle; if (this.numPlayersUsing > 0) { this.lidAngle += f; } else { this.lidAngle -= f; } if (this.lidAngle > 1.0F) { this.lidAngle = 1.0F; } float f2 = 0.5F; if (this.lidAngle < f2 && f1 >= f2 && this.adjacentChestZNeg == null && this.adjacentChestXNeg == null) { d2 = (double)this.xCoord + 0.5D; double d0 = (double)this.zCoord + 0.5D; if (this.adjacentChestZPos != null) { d0 += 0.5D; } if (this.adjacentChestXPos != null) { d2 += 0.5D; } this.worldObj.playSoundEffect(d2, (double)this.yCoord + 0.5D, d0, "random.chestclosed", 0.5F, this.worldObj.rand.nextFloat() * 0.1F + 0.9F); } if (this.lidAngle < 0.0F) { this.lidAngle = 0.0F; } } } } there are other variables in this class such as the contents that save just fine when closing the chest, but I cant find anywhere they are referenced that the 'Cost' is not... could it be associated to it being updated within the network handler? Like its another instance of the TileEntity or something? Quote I'll need help, and I'll give help. Just ask, you know I will!
strumshot Posted September 19, 2014 Author Posted September 19, 2014 Thats actually exactly what I have done. The server side thread prints out the value immediately after incrementing in the server packet handler, and I can watch it climb with each bitton click. I can also watch the client reflect the change visually on the gui, which is only adjusted in the SendCost handler as shown above, which is the return value of successfully handling the server packet. Then I close the chest, reopen, and its at 0. My current guesses are that Coin gets reinitialized somehow or I am changing a temporary instance/clone of the TileEntity? I tried MarkDirty, markforUpdate. .. im lost for words! Quote I'll need help, and I'll give help. Just ask, you know I will!
strumshot Posted September 19, 2014 Author Posted September 19, 2014 Well when I get at my computer I will post my container, gui, and packets; TileEntity is above. Surely the issue is there or missing from there. Maybe I have a lost line of code that reinitialized Coin somewhere I am overlooking or something. Are there any classes that could be related that I am not thinking of? Quote I'll need help, and I'll give help. Just ask, you know I will!
strumshot Posted September 19, 2014 Author Posted September 19, 2014 Sure enough, I had a lost tileEntity,Cost = 0; initialization in my Container of all places from an earlier approach! Search and replace caused the issue, and search fixed the issue! Thanks again for your continued help! Quote I'll need help, and I'll give help. Just ask, you know I will!
Recommended Posts
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.