Jump to content

Recommended Posts

Posted

I am having some trouble with a tile entity.

I am using nbt to store a data value but it seems nbt is only read on the server and i need this data on the client aswell can someone tell me how to synchronize the client with the server when the nbt is read?

 

this is my tile entity class

 

  Reveal hidden contents

 

 

the field i am trying to save is "charges"

I am the author of Draconic Evolution

Posted

NBT is mostly for saving and loading information either when game starts/quits or when chunks get loaded/unloaded.

 

Syncing client with server is entirely different.  The client side and the server side (as far as I understand) are pretty much separate programs and don't know anything about the classes in the other side.  So you need to send packets.

 

It is a little bit complicated, but there are a few tutorials out there.  I recently just learned it myself -- I had a render animation that I needed to do on the client but the information was based on entity AI (which runs on the server).

 

Here's how I did it (trust me, my way is one of the more simple, but feel free to look at other tutorials).  I used a system that took advantage of the FMLProxyPacket class and subscribed to some available packet events.

 

Basically, you’ll need a packet system that:

a. Registers a networking channel specific for your mod (this allows you to ignore packets that may come from other mods)

b. Has packet handler classes with Subscribes to the onClientPacketReceived and the onServerPacketReceived events.

c. Has a packet class that can distinguish packet type and what side it is on and process the packet accordingly

d. Sends the packets as needed (ideally just when there are changes, which is often in a setter method).

 

(In my examples, my mod is called WildAnimals -- wherever you see that you should replace with something relevant to your mod.)

Register a networking channel:

  - in your mod's main class create variables:

 

    
    public static final String networkChannelName = "WildAnimals"; // put the name of your mod here
public static FMLEventChannel channel;

  - in your mod's init event handler method (probably in your proxy class) you need:

		WildAnimals.channel = NetworkRegistry.INSTANCE.newEventDrivenChannel(WildAnimals.networkChannelName); // replace WildAnimals with the name of your main class

 

Create and Register Your Packet Handler Classes:

  - also in same init event handler method register your packet handler classes.  In the common proxy init() you need:

		WildAnimals.channel.register(new ServerPacketHandler());  // replace WildAnimals with name of your main class

  - and in your client proxy init() you need to additionally register the packet handler class:

		WildAnimals.channel.register(new ClientPacketHandler()); // replace WildAnimals with name of your main class

 

Create a new class called ServerPacketHandler which contains subscription to a server packet event with something like:

package wildanimals.networking;

import java.io.IOException;

import wildanimals.WildAnimals;
import wildanimals.network.entities.PacketWildAnimals;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.network.FMLNetworkEvent.ServerCustomPacketEvent;

public class ServerPacketHandler 
{
protected String channelName;

@SubscribeEvent
public void onServerPacket(ServerCustomPacketEvent event) throws IOException 
{
	channelName = event.packet.channel();

	if (channelName == WildAnimals.networkChannelName)
	{
		PacketWildAnimals.processPacketOnServerSide(event.packet.payload(), event.packet.getTarget());
	}
}
}

 

And create a ClientPacketHandler class that extends the ServerPacketHandler class and subscribes to a client packet event:

package wildanimals.networking;

import java.io.IOException;

import wildanimals.WildAnimals;
import wildanimals.network.entities.PacketWildAnimals;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.network.FMLNetworkEvent.ClientCustomPacketEvent;

// Remember client run configuration includes server side too
public class ClientPacketHandler extends ServerPacketHandler
{
@SubscribeEvent
public void onClientPacket(ClientCustomPacketEvent event) throws IOException 
{
	channelName = event.packet.channel();

	if (channelName == WildAnimals.networkChannelName)
	{
		PacketWildAnimals.processPacketOnClientSide(event.packet.payload(), event.packet.getTarget());
	}
}
}

 

Create a Packet Class

Create a packet class that allows you to generate and process received packets as needed.  Note that the actual data you create and receive will need to be changed for your particular need.  Hopefully you can understand how my example packet could be modified for your need.

 

There are a few things to understand -- a packet has a payload that consists of a byte stream.  It is totally up to you what bytes you put in the packet, but you have to come up with a useful format.  I expect to have other packet types so my first data sent consists of a packet type identifier.  I know it is a packet from my mod because of the channel it comes on (I check for the channel).  After that, I want to be able to put information about any of my entities into the packet so my next information is the entityID.  But since I have multiple types of entities, I have to get the class from the entityID and then change my processing based on that.  In that processing I simply take the information I want to send and put it in.  The key thing is to read back the data in the same order that you put it in when you create the packet.

 

package wildanimals.network.entities;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;

import java.io.IOException;

import net.minecraft.client.Minecraft;
import net.minecraft.entity.Entity;
import net.minecraft.world.World;
import wildanimals.WildAnimals;
import wildanimals.entities.bigcats.EntityBigCat;
import wildanimals.entities.herdanimals.EntityHerdAnimal;
import wildanimals.entities.serpents.EntitySerpent;
import cpw.mods.fml.common.network.internal.FMLProxyPacket;
import cpw.mods.fml.relauncher.Side;

// this class is intended to be sent from server to client to keep custom entities synced
public class PacketWildAnimals
{
// define IDs for custom packet types
public final static int packetTypeIDEntity = 1;

public PacketWildAnimals()
{
	// don't need anything here
}

public static FMLProxyPacket createEntityPacket(Entity parEntity) throws IOException
{
	// DEBUG
	System.out.println("Sending PacketWildAnimals on Server Side");

	ByteBufOutputStream bbos = new ByteBufOutputStream(Unpooled.buffer());

	// create payload by writing to data stream
	// first identity packet type
	bbos.writeInt(packetTypeIDEntity);

	// write entity instance id (not the class registry id!)
	bbos.writeInt(parEntity.getEntityId());

	// now write entity-specific custom fields
	// process herd animals
	if (parEntity instanceof EntityHerdAnimal)
	{
		EntityHerdAnimal entityHerdAnimal = (EntityHerdAnimal)parEntity;
		bbos.writeFloat(entityHerdAnimal.getScaleFactor());
		bbos.writeBoolean(entityHerdAnimal.isRearing());			
	}
	// process serpents
	else if (parEntity instanceof EntitySerpent)
	{
		EntitySerpent entitySerpent = (EntitySerpent)parEntity;
		bbos.writeFloat(entitySerpent.getScaleFactor());
	}
	// process big cats
	else if (parEntity instanceof EntityBigCat)
	{
		EntityBigCat entityBigCat = (EntityBigCat)parEntity;
		bbos.writeFloat(entityBigCat.getScaleFactor());
	}

	// put payload into a packet		
	FMLProxyPacket thePacket = new FMLProxyPacket(bbos.buffer(), WildAnimals.networkChannelName);

	// don't forget to close stream to avoid memory leak
	bbos.close();

	return thePacket;
}

public static void processPacketOnClientSide(ByteBuf parBB, Side parSide) throws IOException
{
	if (parSide == Side.CLIENT)
	{
		// DEBUG
		System.out.println("Received PacketWildAnimals on Client Side");

		World theWorld = Minecraft.getMinecraft().theWorld;
		ByteBufInputStream bbis = new ByteBufInputStream(parBB);

		// process data stream
		// first read packet type
		int packetTypeID = bbis.readInt();

		switch (packetTypeID)
		{
			case packetTypeIDEntity:  // a packet sent from server to sync entity custom fields
			{
				// find entity instance
				int entityID = bbis.readInt();
				// DEBUG
				System.out.println("Entity ID = "+entityID);
				Entity foundEntity = getEntityByID(entityID, theWorld);
				// DEBUG
				System.out.println("Entity Class Name = "+foundEntity.getClass().getSimpleName());

				// process based on type of entity class
				// process herd animals
				if (foundEntity instanceof EntityHerdAnimal)
				{
					EntityHerdAnimal foundEntityHerdAnimal = (EntityHerdAnimal)foundEntity;
					// apply custom fields to entity instance
					foundEntityHerdAnimal.setScaleFactor(bbis.readFloat());
					foundEntityHerdAnimal.setRearing(bbis.readBoolean());
					// DEBUG
					System.out.println("Is rearing = "+foundEntityHerdAnimal.isRearing());
				}
				// process serpents
				else if (foundEntity instanceof EntitySerpent)
				{
					EntitySerpent foundEntitySerpent = (EntitySerpent)foundEntity;
					// apply custom fields to entity instance
					foundEntitySerpent.setScaleFactor(bbis.readFloat());
				}
				// process big cats
				else if (foundEntity instanceof EntityBigCat)
				{
					EntityBigCat foundEntityBigCat = (EntityBigCat)foundEntity;
					// apply custom fields to entity instance
					foundEntityBigCat.setScaleFactor(bbis.readFloat());
				}
				break;
			}
		}

		// don't forget to close stream to avoid memory leak
		bbis.close();			
	}
}

public static void processPacketOnServerSide(ByteBuf payload, Side parSide) 
{
	if (parSide == Side.SERVER)
	{
		// currently haven't defined any packets from client

	}
}

// some helper functions
public static Entity getEntityByID(int entityID, World world)        
{         
	for(Object o: world.getLoadedEntityList())                
	{                        
		if(((Entity)o).getEntityId() == entityID)                        
		{                                
			System.out.println("Found the entity");                                
			return ((Entity)o);                        
		}                
	}                
	return null;        
}	
}

 

Send the Packet When Needed:

Now all you need to do is to send the packet when you need to.  You could just send it in the onUpdate() method of your entity, but that is a bad idea because it is wasteful -- it sends a packet every tick.  Instead you should send it every time the variables you are tracking change.  Personally it can be tricky to remember to send a packet every time, so I rely on encapsulation -- I only change my variables by using a "setter" method and I put the sending packet inside that sender. 

 

Anyway, it is up to you when you want to send the packets to sync up the client.  The code you need depends on whether you're sending it to just one player (e.g. for their personal GUI) or to all the players (like in my case where I want all the players to see the entity animations at the same time).  Here are some examples of the code you can use to send the packet:

   // method to send sync packet from server to client, should send whenever a custom field is set (from setter method)
// send sync packet to client, if on server side
public void sendSyncPacket()
{
	if (!this.worldObj.isRemote)
	{
    	try 
    	{
			WildAnimals.channel.sendToAll(PacketWildAnimals.createEntityPacket(this));
		} 
    	catch (IOException e) 
    	{
			e.printStackTrace();
		}
	}
}

 

You should check out the other methods for the channel, because you can sendToServer, etc.

 

Hope this helps.  I know it is annoying that you have to do so much work in order to simply send a couple values to the client, but trust me that you have to do it and you might as well learn.  I won't feel bad if you use a different tutorial to do it, but I'm pretty happy with my implementation so far.

 

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted

YOU ARE AWESOME! Thats the first tutorial I have been able to understand all the other tutorials i looked at were insanely complex and impossible for me to figure out but i think i understand how this works now.

 

I just have one thing i cant figure out and that is the "Create and Register Your Packet Handler Classes" part can you please explain in more detail. i dont use init() methods in my proxys and im not sure how to set them up.

 

My proxys:

common

 

  Reveal hidden contents

 

client

 

  Reveal hidden contents

 

And my main mod class:

 

  Reveal hidden contents

 

 

Thank you for your help!

I am the author of Draconic Evolution

Posted
  On 5/3/2014 at 5:44 PM, Jacky2611 said:

@jabelar this is awesome. Could you please post this tutorial somewhere on the wiki?

 

Thanks.  I've just learned modding recently and finally have confidence to share my understanding on a couple topics.  I'm just figuring out best place to post my tutorials and plan to post a few.  I think this packet handling approach deserves a tutorial, and also my entity modeling has gotten pretty good (check out my snake and elephant entities in this video:

).

 

I don't like posting mis-information though, so want to make sure I thoroughly understand something before deciding to teach others.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted
  On 5/3/2014 at 9:50 PM, brandon3055 said:

so would you be able to help me out with my proxys? im not sure how you set up your init() methods.

 

When you start your tutorials i would very much like to take a look the way you explained this was awesome.

 

Well, as larsgerrit said for TileEntities they are specifically made for syncing extra data between client and server (for instance they are the intermediary for container GUIs and such.  So you don't need to do my packets for that.  But you still can, and mine are a useful general packet that I use a lot for regular Entities.

 

Anyway, for proxies you can do it a few different ways.  This is one of the tricky points to understand when first modding -- the proxies are related to your run configuration (i.e. in Eclipse).  When you run Minecraft, there is always a server but you may or not have a client running locally.  When you use the Client run configuration, it also includes the server classes.  So this idea of proxy helps organize the case where your run configuration is only server, or also server with client. 

 

The server stuff goes in the "Common" proxy because it is common to both the Client and the Server run configurations.  Then any client-specific stuff goes into a "Client" proxy, but that actually extends "Common" proxy to cover both sides that will execute in the run configuration.

 

I know that is kinda confusing, but keep reading it over until it makes sense.

 

Okay, with that being said I personally organize my proxy like this.  Note I took out a lot of stuff to make so you can just see the organization.

 

CommonProxy:

 

  Reveal hidden contents

 

 

And ClientProxy:

 

  Reveal hidden contents

 

 

Then in my mod's main class the organization is very simple and clean.  Basically you call the proxy's related method as appropriate.  Here's just the event handler stuff (not quite my whole main class):

 

  Reveal hidden contents

 

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted

ok now i understand. If you look at my earlier post that is how ALL of the tutorials i have seen so far have set up the proxys. I have never seen anyone add the FMLPreInit/Init/PostInit events to their proxys. After i get some sleep i will try setting up my proxys like yours and will let you know how i go.

 

I will also have to look into what larsgerrits said and see if i can figure that out. 

I am the author of Draconic Evolution

Posted

Ok im calling this one solved! I ended up going with larsgerrits suggestion because it was simple and its designed for this exact application although it did take a bit to figure out because it seems to have changed a little in 1.7.2 For anyone else having the same problem.

@Override
public Packet getDescriptionPacket() {
//Debug
System.out.println("[DEBUG]:Server sent tile sync packet");

NBTTagCompound tagCompound = new NBTTagCompound();
this.writeToNBT(tagCompound);
return new S35PacketUpdateTileEntity(this.xCoord, this.yCoord, this.zCoord, 1, tagCompound);
}

@Override
public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) {
//Debug
System.out.println("[DEBUG]:Client recived tile sync packet");

readFromNBT(pkt.func_148857_g());
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
}

@jabelar I will still be using your packet handler because the next thing on my list is adding buttons to the gui which requires a packet handler. If i have any more problems figuring it out i will pm you (if that's ok with you?) but i think i have it figured out.

 

I am the author of Draconic Evolution

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

    • Add crash-reports with sites like https://mclo.gs/ Make a test without createaddition
    • Add the crash-report or latest.log (logs-folder) with sites like https://mclo.gs/ and paste the link to it here  
    • my server is running perfectly fine but the problem is I can't connect to it. https://drive.google.com/file/d/1pn3oyAoS6J9mYVQlTJs7nKSSOHRo-9NX/view?usp=sharing. Here is the bug report when I tried to join the server.  
    • I'm trying to create split out some common code from my current mod project into a new library. For dependency management, I'm attempting to use AWS S3 as a Maven repo. I've done this successfully with other projects in the past, but ForgeGradle doesn't seem to like the s3:// URL for my repository. Specifically, it's throwing the following exception when trying to resolve the net.minecraftforge:forge:1.21.1-52.1.0:userdev dependency:   My understanding is that modern versions of Gradle support this use case. Does ForgeGradle not? Is there a way that I can make this work? Thank you for any help you can offer.
    • Codice Sconto Temu 100$ DI SCONTO → [acu639380] per Clienti Esistenti   Ottieni  100$ di sconto con il Codice Promozionale Temu (acu639380) Temu continua a dominare il mondo dell’e-commerce con sconti imbattibili e prodotti di tendenza – e giugno 2025 non fa eccezione. Con il codice sconto Temu (acu639380), puoi ottenere fino a  100$ di sconto, sia che tu sia un nuovo cliente sia che tu stia tornando a fare acquisti. Grazie alla consegna ultra-rapida, spedizione gratuita in 67 paesi e sconti fino al 90%, Temu propone pacchetti esclusivi e codici promozionali imperdibili questo giugno. Ecco come sfruttare al meglio il codice (acu639380) e iniziare subito a risparmiare. Perché Giugno 2025 è il Momento Migliore per Acquistare su Temu Giugno è ricco di offerte a tempo limitato, nuovi arrivi di tendenza e sconti nascosti in tutte le categorie. Dalla moda all’elettronica, dalla bellezza agli articoli per la casa, Temu offre prodotti indispensabili a prezzi imbattibili. Usa il codice (acu639380) per accedere a:  100$ di sconto per nuovi utenti  100$ di sconto per clienti esistenti 40% di sconto extra su categorie selezionate Pacchetto di buoni da  100$ per nuovi e vecchi clienti Regalo di benvenuto gratuito per chi acquista per la prima volta Vantaggi Esclusivi dei Codici Sconto Temu Questi sconti sono pensati per ogni tipo di acquirente. Ottieni il massimo con: Codice Temu (acu639380)  100$ di sconto – Riduci il totale sui tuoi acquisti in blocco Codice Temu per utenti esistenti – Offerte premium riservate ai clienti fedeli Codice Temu per nuovi utenti – Grandi risparmi sul primo ordine Codice Temu 40% di sconto – Perfetto per moda e prodotti stagionali Pacchetto coupon da  100$ Temu – Risparmia su più ordini Coupon per nuovi utenti Temu – Inizia con un regalo + sconto Offerte Localizzate con il Codice Temu (acu639380) Grazie alla presenza globale di Temu, puoi accedere a offerte personalizzate ovunque ti trovi: Codice Temu  100$ di sconto – USA Codice Temu  100$ di sconto – Canada Codice Temu  100$ di sconto – Regno Unito Codice Temu  100$ di sconto – Giappone Codice Temu 40% di sconto – Messico Codice Temu 40% di sconto – Brasile Codice Temu  100$ di sconto – Germania Codice Temu  100$ di sconto – Francia Codice Temu per nuovi utenti – Argentina Coupon Temu per utenti esistenti – Italia Codice promozionale Temu (acu639380) – Spagna, giugno 2025 Cosa Comprare su Temu a Giugno 2025 L’ampio catalogo Temu include migliaia di categorie. Ecco alcuni articoli su cui usare il codice sconto (acu639380): Gadget intelligenti e accessori Moda per tutte le età Decorazioni per la casa e soluzioni salvaspazio Prodotti per il benessere, fitness e bellezza Utensili da cucina e pentolame Articoli per ufficio, giochi e regali La Mia Esperienza Risparmiando  100$ con il Codice Temu (acu639380) Quando ho usato il codice (acu639380) da cliente abituale, ho ricevuto immediatamente  100$ di sconto. Combinandolo con la promozione del 40% e il pacchetto da  100$, ho ottenuto oltre 200 $ di valore per meno di 80 $. Anche tu puoi farlo. Ti basta inserire (acu639380) al checkout, e lo sconto si applica automaticamente – con spedizione gratuita in tutto il mondo inclusa. Altri Sconti Temu per Giugno 2025 Questo mese è pieno di offerte a rotazione, pacchetti a sorpresa e vendite flash giornaliere. Resta aggiornato su: Nuove uscite con il coupon per nuovi utenti Temu Aggiornamenti settimanali dei codici per clienti esistenti Promozioni regionali con il codice (acu639380) per giugno 2025 Conclusione Ovunque ti trovi – in Nord America, Europa o Sud America – il codice Temu (acu639380) è la chiave per risparmiare alla grande. Con offerte per nuovi utenti e clienti fedeli, questo è il momento perfetto per approfittare dei prezzi imbattibili di Temu. Usa il codice acu639380 oggi stesso per ottenere vantaggi esclusivi e trasformare il tuo shopping in un’esperienza smart e conveniente.      
  • Topics

×
×
  • Create New...

Important Information

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