Jump to content

Recommended Posts

Posted

Hello All,

 

What is necessary to make a Tile Entity spawn stacks in the world from a GUI button press? I can make it work in the client side only, so I get phantom stacks in the world, and the inventory slot for the TileEntity refills to what it was if the GUI is closed and reopened. How do I make it happen server-side, too? Is there a standard way of communicating client-side changes to server-side?

Posted

Use the Simple Network Implementation to send a packet to the server telling it that the player clicked the button. In the packet handler, check that the player has permission to spawn the stacks (e.g. is within interaction range of the block) before spawning them.

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.

Posted (edited)

Thanks, this looks like what I need. Do I need to make the changes on the client side, too, in this case? Like, when they click the button should I be modifying the stack amounts client-side AND server-side, and then spawning the stacks server-side only? (I assume spawning client and server at the same time would cause one phantom stack and one real stack.)

Edited by BlameTaw
Posted
  On 5/12/2017 at 11:02 AM, BlameTaw said:

Thanks, this looks like what I need. Do I need to make the changes on the client side, too, in this case? Like, when they click the button should I be modifying the stack amounts client-side AND server-side, and then spawning the stacks server-side only? (I assume spawning client and server at the same time would cause one phantom stack and one real stack.)

Expand  

 

Just modify it on the server, the changes will be synced to the necessary clients.

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.

Posted

Is there a way to make it only happen when the GUI is closed, rather than when the button is pressed? I'm realizing that might make for a better user experience with this. If not, I'll try making it work on button press.

Posted

Oh wow I can't believe I missed that when I was looking at the other classes. Thanks!

 

For the message itself, is there a common way people structure them? Is there a reason not to just convert a string to a byte array, or is it a better practice to just send relevant values and decode them on the other side?

Posted

A string will ultimately get converted to byte array anyway in the end, as will everything else you send. The only reason I see to not convert it manually is a chance that the charset can be messed up. For example if you are sending to the server a string client has typed and it contains unicode characters. If you are converting the arriving byte array to a string manually on the server you might use say, ASCII charset and mess that string up completely. There is a ByteBufUtils class that handles that for you though.

Apart from that I think that structuring your packets is just a personal preference. If your packets are intended to be sent very frequiently and contain not much data it is the best to just write the data 'raw'. If your packets are big and contain bunch of data, especially if that data can vary (so you never know which parts the server/client will recieve) it might just be best to structure your data into an NBTTagCompound and just write/read that.

Obviously sending only relevant data that has changed and needs to be synced is the best practice :)

Posted

Ah that makes sense. I didn't think about character encodings. Either way I'm only sending a BlockPos so it's easy to just send three ints.

 

I'm having an InstantiationException when I close the GUI, however. Seems to be coming from a NoSuchMethodException on my BufferEjectMessage.<init>() implying an issue during construction, maybe? I don't know why that would be. (stacktrace in the spoiler below the code)

 

Here is my IMessage implemention and IMessageHandler implementation:

package com.blametaw.itembuffers.blocks;

import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.util.math.BlockPos;
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.items.CapabilityItemHandler;

public class BufferEjectMessage implements IMessage {

	public BlockPos pos; //TileEntity Block Coordinates
	public BufferEjectMessage(int x, int y, int z){
		this.pos = new BlockPos(x,y,z);
	}
	
	public BufferEjectMessage(BlockPos pos){
		this.pos = pos;
	}
	
	@Override
	public void fromBytes(ByteBuf buf) {
		this.pos = new BlockPos(buf.readInt(), buf.readInt(), buf.readInt());
	}

	@Override
	public void toBytes(ByteBuf buf) {
		buf.writeInt(pos.getX());
		buf.writeInt(pos.getY());
		buf.writeInt(pos.getZ());
	}
	
	public static class MyMessageHandler implements IMessageHandler<BufferEjectMessage, IMessage> {
		
		@Override
		public IMessage onMessage(BufferEjectMessage message, MessageContext ctx) {
			
			//I assume this is wrong because I'd be greatly surprised if I was allowed to access game objects from within messages like this...
			
			TileEntityBasicBuffer te = (TileEntityBasicBuffer) Minecraft.getMinecraft().world.getTileEntity(message.pos);
			BufferStackHandler handler = (BufferStackHandler)te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null);
			handler.ejectExcessItems();
			return null;
		}
		
	}

}

 

Error stacktrace:

  Reveal hidden contents

 

Posted (edited)

I actually just figured it out. I didn't have a default constructor on the BufferEjectMessage. It is ejecting items now, however they are still phantom items. Is my message not actually executing server side, even though I'm calling sendToServer: Reference.networkWrapperInstance.sendToServer(new BufferEjectMessage(container.getTileEntity().getPos()));

 

Does Minecraft.getMinecraft() not work for server-side? Is there another way I should be referencing the tile entity itself to call the eject functions? Also, is my assumption correct that I shouldn't be directly referencing the TileEntity from the message? That just seems like a threading nightmare waiting to happen...

Edited by BlameTaw
Posted

Yeah, Minecraft::getMinecraft() is client-side only. As is Minecraft class in general. Use FMLCommonHandler::getMinecraftServerInstance() to obtain the server in general if you need it, or DimensionManager::getWorld(int id) to get a specific WorldServer.

All networking is done on a specific thread so you should not do anything on the networking thread that is world/entities related. Wrap your actions in a Runnable and pass that runnable to a valid IThreadListener using IThreadListener::addScheduledTask(Runnable). A IThreadListener is either a WorldServer instance(server-side) or Minecraft instance(client-side)

Posted

Ah okay that makes sense! So do I have to have the world ID passed in with the BlockPos in order to know which world to get? I see that both MinecraftServer and DimensionManager require an ID.

 

Thanks for all the quick responses! Sorry for so many questions, I'm still very new to this API.

Posted (edited)

Well, to be fair, if you simply want your items to be dropped upon your gui being closed: if you are using a custom Container for your inventory you can simply override container's onContainerClosed method and drop your items there. No packets needed as Container is server-sided.

Granted, that will only work if you are using a container in the first place :)

You haven't specified if you are using a container or not in your question, so I haven't thought about it immidiately

Edited by V0idWa1k3r
Posted (edited)
  On 5/13/2017 at 12:27 AM, V0idWa1k3r said:

Well, to be fair, if you simply want your items to be dropped upon your gui being closed: if you are using a custom Container for your inventory you can simply override container's onContainerClosed method and drop your items there. No packets needed as Container is server-sided.

Granted, that will only work if you are using a container in the first place :)

You haven't specified if you are using a container or not in your question, so I haven't thought about it immidiately

Expand  

Oh well maybe I'll try putting it there then...

 

In the mean time, I just found that if you save/quit and reopen the game after modifying the TE's inventory through the GUI, the changes aren't saved so you can duplicate items... Why would changes to the slots not be happening server-side? Isn't that handled by the slot-click method in either Container or GuiContainer? (I forget what it's called and which one)

 

Edit: it DOES make the proper item changes when ejecting items, it's only when manually moving stacks that changes are not made server-side.

Edited by BlameTaw
Posted (edited)

You are either not changing the inventory of your tileentity in your packet handling (as in you are spawning items in the world but not settings the itemstacks to empty in your tile entity... or setting them to empty but only on client side) or there is an error in saving/loading your Tile's inventory. You can debug those two spots in your code using breakpoints to see where the problem lies

Edited by V0idWa1k3r
Posted

In that case, maybe I have no idea how to ensure my entity's packet handling is correct...It's certainly something I didn't fully understand when researching about it.

 

Here is everything I have for the packet handling in my tile entity class. Is there more that I need than just this? I will fully admit that I copy-pasted this from an example class I found, though I did make an effort to understand what it is doing. I assumed these functions are being automatically called by the Container and GuiContainer manipulations. Am I wrong?

 

//	// When the world loads from disk, the server needs to send the TileEntity information to the client
//	//  it uses getUpdatePacket(), getUpdateTag(), onDataPacket(), and handleUpdateTag() to do this
	@Override
	@Nullable
	public SPacketUpdateTileEntity getUpdatePacket(){
		NBTTagCompound updateTagDescribingTileEntityState = getUpdateTag();
		final int METADATA = 0;
		return new SPacketUpdateTileEntity(this.pos, METADATA, updateTagDescribingTileEntityState);
	}

	@Override
	public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) {
		NBTTagCompound updateTagDescribingTileEntityState = pkt.getNbtCompound();
		handleUpdateTag(updateTagDescribingTileEntityState);
	}

  /* Creates a tag containing the TileEntity information, used by vanilla to transmit from server to client
     Warning - although our getUpdatePacket() uses this method, vanilla also calls it directly, so don't remove it.
   */
	@Override
	public NBTTagCompound getUpdateTag(){
		NBTTagCompound nbtTagCompound = new NBTTagCompound();
		writeToNBT(nbtTagCompound);
		return nbtTagCompound;
	}

  /* Populates this TileEntity with information from the tag, used by vanilla to transmit from server to client
   Warning - although our onDataPacket() uses this method, vanilla also calls it directly, so don't remove it.
 */
	@Override
	public void handleUpdateTag(NBTTagCompound tag){
		this.readFromNBT(tag);
	}

 

Posted

Oh, when you are manually moving items? Then most likely there is an error in your container implementation. How exactly are you opening you gui + container? You need to do it using your proxy, providing a Container instance for the server and a GuiContainer instance for your client.

Posted

I have an IGuiHandler that gets the server/client GUI elements.

package com.blametaw.gui;

import com.blametaw.itembuffers.blocks.TileEntityBasicBuffer;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.network.IGuiHandler;

public class BasicBufferGuiHandler implements IGuiHandler {

	private static final int GUIID = 1;
	public static int getGuiID(){return GUIID;}
	
	@Override
	public Object getServerGuiElement(int ID, EntityPlayer player, World world,
			int x, int y, int z) {
		if (ID != getGuiID()) {
			System.err.println("Invalid ID: expected " + getGuiID() + ", received " + ID);
		}
		
		BlockPos xyz = new BlockPos(x, y, z);
		TileEntity tileEntity = world.getTileEntity(xyz);
		if (tileEntity instanceof TileEntityBasicBuffer) {
			TileEntityBasicBuffer tebb = (TileEntityBasicBuffer) tileEntity;
			return new ContainerBasicBuffer(player.inventory, tebb);
		}
		return null;
	}

	@Override
	public Object getClientGuiElement(int ID, EntityPlayer player, World world,
			int x, int y, int z) {
		if (ID != getGuiID()) {
			System.err.println("Invalid ID: expected " + getGuiID() + ", received " + ID);
		}
		
		BlockPos xyz = new BlockPos(x, y, z);
		TileEntity tileEntity = world.getTileEntity(xyz);
		if (tileEntity instanceof TileEntityBasicBuffer) {
			TileEntityBasicBuffer tebb = (TileEntityBasicBuffer) tileEntity;
			return new GuiContainerBasicBuffer(new ContainerBasicBuffer(player.inventory, tebb));
		}
		return null;
	}

}

 

 

And I'm calling it with this function in my Block:

@Override
	public boolean onBlockActivated(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, EnumHand hand,
			EnumFacing side, float hitX, float hitY, float hitZ){
		if (worldIn.isRemote) return true;
		playerIn.openGui(ItemBuffers.instance, BasicBufferGuiHandler.getGuiID(), worldIn, pos.getX(), pos.getY(), pos.getZ());
		return true;
	}

 

 

From what I understand, this should be opening it on both server and client. Also, I'd be surprised if this was the issue because moving things from the TE slots into the player inventory slots DOES work, and those changes are persistent. It's just that quit/reload resets the TE's NBT data.

 

Also there are GUI buttons that update some other values in the NBT data, so I assume that should be done with IMessages as well, right? I didn't think about that before... When you quit/reload those numbers are also reset which implies they're also only happening client side.

 

I've attached a picture of my GUI to give you more of an idea what I'm referring to. That might help a bit.

2017-05-12 18_10_53-Minecraft 1.11.2.png

Posted

Yes, the buttons need an IMessage to sync their presses.

I'm not really sure why that issue is happening then. Try just debugging your code in 'problematic' spots that could potentially be causing the issue. See if any TileEntity data is persistent at all upon reloading the world. If it is not then the problem is within saving/loading your tileentity. Also check the log for any error messages - your tile entity will not be saved if it is not registered with GameRegistry::registerTileEntity. Container indeed handles all slot itemstacks changes, and your IGuiHandler seems fine.

 

Posted (edited)

I am registering the TileEntity. No errors in the logs. From some trace messages in the writeToNBT and readFromNBT functions it seems that the client never calls either function, and the server only ever calls the writeToNBT function...That's confusing to me.

 

Okay I feel like I have messed up a lot of things...I'll have to play around with this a bit and get back to you. Whoops.

Edited by BlameTaw

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.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • 🌍 TITLE:   Herobrine vs Null – Rise of Prime   > “Rewrite Minecraft history. Witness the rise… and the fall.”         ---   🧩 MOD VERSION:   Minecraft Forge 1.20.1   Built with MCreator   Ultra-cinematic style, custom mobs, weapons, quests, effects, emotional cutscenes       ---   📖 FULL STORY (Final Draft):   ⚔️ ACT 1: Alex’s Death – The Beginning of Darkness   Peaceful world. Alex and Steve explore a new cave.   Suddenly, Null appears from the shadows, corrupts the land, and strikes Alex with a void blade.   A powerful cinematic cutscene plays. Alex dies in Steve’s arms.   Steve collapses crying, lightning and rain begin, music swells.       ---   ⚔️ ACT 2: Broken and Angry – The Herobrine Awakening   Days pass. Steve visits ancient ruins. He decides to face Null without powers.   He finds Null again — and gets hit violently, almost dying.   This triggers a transformation:   > “Awaken... Herobrine.”       Cinematic animation: glowing eyes, thunderstorm, ancient symbols appear. Steve transforms into Herobrine Ascendant.   Emotional soundtrack + glowing UI changes.       ---   ⚔️ ACT 3: The Rift of Eternity – The Legendary Path   The world is glitching. Shadow minions appear.   Herobrine travels through corrupted biomes: 🔹 Glitch Wastes 🔹 Soul Caverns 🔹 Void Forest 🔹 Darkstorm Mountains   Defeats powerful bosses:   🩸 Glitch Revenant   🔥 Flamebound Brute   ⚡ Stormcaller   🧊 Entity 303 (secret optional boss!)   🗡️ Shadowbound Warrior         ---   ⚔️ FINAL ACT: Rise of Prime – The Final War Begins   Arrives at The Rift of Eternity (final battle map): ⛰️ Floating mountains 🔥 Lava eruptions ⛓️ Chains from sky 🌩️ Storm and glitch FX everywhere   Final Cutscene Begins:   > "So… Herobrine finally wakes." – Prime Null         👑 FINAL BATTLE:   Boss Fight: Prime Null vs Herobrine Ascendant   3 Phases: Teleportation, Blade of the Void, Summon Minions   Environment collapses: blocks fly, camera shakes   Epic soundtrack plays with subtitles (EN + Sinhala)   Cutscene finishes with Null screaming:   > “I… will come back… from the Dark Prison!”       Black screen: “Season 2 Coming Soon”         ---   💥 WHAT’S INCLUDED IN THE MOD:   🎮 GAMEPLAY   4–6 hours of playtime   Hard mode with permadeath   Questline system with cutscene triggers   Save/load checkpoints       ---   🦸 CHARACTERS (Fully Animated)   Steve / Herobrine Ascendant   Alex (dies early)   Null (base + Prime Null final form)   Optional Bosses:   Entity 303   Flamebound Brute   Soul Warden   Shadowbound Warrior   Stormcaller   Glitch Revenant         ---   🧩 QUESTS   12+ main quests   6+ side quests (some unlock alternate endings)   Ritual quests to summon Herobrine powers       ---   🔫 WEAPONS / ITEMS   ⚡ Lightning Staff   🗡️ Void Piercer Blade   🔥 Flame Reaper   🧊 Frozen Thunder Shuriken   💎 Alex’s Amulet (used in story)       ---   💥 PARTICLE EFFECTS & CINEMATICS   Crying, rain, lightning, fire   Custom shaders (optional but NOT required)   Camera shake, blur, glow   Flying blocks during battles   Realistic terrain destruction       ---   🎬 TRAILER   Cinematic shots   Text overlays   Background score   Your name and mine: Developed by: Navindu Heshan & The Golden Friend   Ends with🌍 TITLE:   Herobrine vs Null – Rise of Prime   > “Rewrite Minecraft history. Witness the rise… and the fall.”         ---   🧩 MOD VERSION:   Minecraft Forge 1.20.1   Built with MCreator   Ultra-cinematic style, custom mobs, weapons, quests, effects, emotional cutscenes       ---   📖 FULL STORY (Final Draft):   ⚔️ ACT 1: Alex’s Death – The Beginning of Darkness   Peaceful world. Alex and Steve explore a new cave.   Suddenly, Null appears from the shadows, corrupts the land, and strikes Alex with a void blade.   A powerful cinematic cutscene plays. Alex dies in Steve’s arms.   Steve collapses crying, lightning and rain begin, music swells.       ---   ⚔️ ACT 2: Broken and Angry – The Herobrine Awakening   Days pass. Steve visits ancient ruins. He decides to face Null without powers.   He finds Null again — and gets hit violently, almost dying.   This triggers a transformation:   > “Awaken... Herobrine.”       Cinematic animation: glowing eyes, thunderstorm, ancient symbols appear. Steve transforms into Herobrine Ascendant.   Emotional soundtrack + glowing UI changes.       ---   ⚔️ ACT 3: The Rift of Eternity – The Legendary Path   The world is glitching. Shadow minions appear.   Herobrine travels through corrupted biomes: 🔹 Glitch Wastes 🔹 Soul Caverns 🔹 Void Forest 🔹 Darkstorm Mountains   Defeats powerful bosses:   🩸 Glitch Revenant   🔥 Flamebound Brute   ⚡ Stormcaller   🧊 Entity 303 (secret optional boss!)   🗡️ Shadowbound Warrior         ---   ⚔️ FINAL ACT: Rise of Prime – The Final War Begins   Arrives at The Rift of Eternity (final battle map): ⛰️ Floating mountains 🔥 Lava eruptions ⛓️ Chains from sky 🌩️ Storm and glitch FX everywhere   Final Cutscene Begins:   > "So… Herobrine finally wakes." – Prime Null         👑 FINAL BATTLE:   Boss Fight: Prime Null vs Herobrine Ascendant   3 Phases: Teleportation, Blade of the Void, Summon Minions   Environment collapses: blocks fly, camera shakes   Epic soundtrack plays with subtitles (EN + Sinhala)   Cutscene finishes with Null screaming:   > “I… will come back… from the Dark Prison!”       Black screen: “Season 2 Coming Soon”         ---   💥 WHAT’S INCLUDED IN THE MOD:   🎮 GAMEPLAY   4–6 hours of playtime   Hard mode with permadeath   Questline system with cutscene triggers   Save/load checkpoints       ---   🦸 CHARACTERS (Fully Animated)   Steve / Herobrine Ascendant   Alex (dies early)   Null (base + Prime Null final form)   Optional Bosses:   Entity 303   Flamebound Brute   Soul Warden   Shadowbound Warrior   Stormcaller   Glitch Revenant         ---   🧩 QUESTS   12+ main quests   6+ side quests (some unlock alternate endings)   Ritual quests to summon Herobrine powers       ---   🔫 WEAPONS / ITEMS   ⚡ Lightning Staff   🗡️ Void Piercer Blade   🔥 Flame Reaper   🧊 Frozen Thunder Shuriken   💎 Alex’s Amulet (used in story)       ---   💥 PARTICLE EFFECTS & CINEMATICS   Crying, rain, lightning, fire   Custom shaders (optional but NOT required)   Camera shake, blur, glow   Flying blocks during battles   Realistic terrain destruction       ---   🎬 TRAILER   Cinematic shots   Text overlays   Background score   Your name and mine: Developed by: Navindu Heshan & The Golden Friend   Ends with release date: JULY 16       ---   🧠 SPECIAL FEATURES:   Secret ending (if you don’t kill Null)   Killcam-style boss finishers   Hidden rooms + “Void Realm” area   Hard mode unlocks Glitched Steve as final secret .can you make it real.       ---   🧠 SPECIAL FEATURES:   Secret ending (if you don’t kill Null)   Killcam-style boss finishers   Hidden rooms + “Void Realm” area   Hard mode unlocks Glitched Steve as final secret .can you make it
    • Make a test with another Launcher like the Curseforge Launcher, MultiMC or AT Launcher
    • can anyone help me i am opening forge and add modpacks and then it says unable to update native luancher and i redownlaod java and the luancher it self?
    • The problem occurs also in 1.20.1 Forge, but with an "Error executing task on client" instead. I have "Sinytra Connector" installed. On 1.21.5 Fabric, there is no problem. When this happens, the chat message before the death screen appears gets sent, with an extra dash added.
    • Well, as usual, it was user error. Naming mismatch in sounds.json.  Please delete this post if you find it necessary. 
  • Topics

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.