Handling Packets in 1.7.x and up - Making the SimpleNetworkWrapper Not So Simple
Note about terminology: I will be using the terms 'packet' and 'message' interchangeably in this tutorial.
Today, I will show you how to make a 'convenient-and-annoyance-free-but-possibly-too-complicated' version of the SimpleNetworkWrapper. If you prefer a simple solution, please see this tutorial by diesieben07 - he'll have you up and running with packets in no time. You should check out that tutorial anyway if you are going to follow along, as it provides an excellent explanation of everything you need to know about the SimpleNetworkWrapper.
IMPORTANT: The solutions presented in this tutorial are wholly unnecessary for a functioning packet handling system - they are purely stylistic. Take this tutorial with a grain of salt - it's one of those projects that was more a 'can I do this?' than 'should I do this?', and since I did it, I figured heck, maybe someone else will get a kick out of this.
Now, why would anyone want to do something different when it's so simple? Well, there were a couple points about the process that annoyed me (in comparison to the good old days when everything was better :P) and I wanted to fix:
1. There isn't an EntityPlayer passed directly to the onMessage method - when nearly every packet sent is going to want to reference either a player or world object, this seemed like a huge oversight and inconvenience to me, even though it CAN be retrieved (and stored) in one line, why should we have to do that when it can just be passed as a parameter like it always has been?
2. There is no indication of which side you are on when handling the message - you must either remember to which side you registered it, go check, or check the MessageContext#side field, all of which seem kind of lame, whereas before, at least the way I had it set up, the side you were on was embedded in the name of the handling method. Clear, concise, no chance of getting confused and using Minecraft.getMinecraft() for something when on the server. It was awesome.
3. Registering requires a byte discriminator - wait, what? Why couldn't it just automatically increment each time a message is registered, since they are all registered per mod (i.e. everyone gets to start at 0) ? What a useless parameter to provide, and boy, is that sure annoying.
Ok... so maybe those points aren't that convincing to you, and they really aren't that big of a deal. If you find that's the case, then stop now and use the earlier tutorial. If, on the other hand, the above points irk you to no end and you really wish you could do something about them, then here we go! Please remember, you can always change your mind later if you decide that the extra work isn't worth it
Step 1: Create a PacketDispatcher
I guess we're starting with the last point first, but we will also store the SimpleNetworkWrapper instance here and, for added convenience, make some wrapper methods of our own. You can use this class without any modifications even if you decide not to bother with the abstract message classes used in the next steps.
NOTE: If you don't know how SimpleNetworkWrapper / IMessage / IMessageHandler classes work, please go back and read diesieben's tutorial before reading any further!
/**
*
* This class will house the SimpleNetworkWrapper instance, which I will name 'dispatcher',
* as well as give us a logical place from which to register our packets. These two things
* could be done anywhere, however, even in your Main class, but I will be adding other
* functionality (see below) that gives this class a bit more utility.
*
* While unnecessary, I'm going to turn this class into a 'wrapper' for SimpleNetworkWrapper
* so that instead of writing "PacketDispatcher.dispatcher.{method}" I can simply write
* "PacketDispatcher.{method}" All this does is make it quicker to type and slightly shorter;
* if you do not care about that, then make the 'dispatcher' field public instead of private,
* or, if you do not want to add a new class just for one field and one static method that
* you could put anywhere, feel free to put them wherever.
*
* For further convenience, I have also added two extra sendToAllAround methods: one which
* takes an EntityPlayer and one which takes coordinates.
*
*/
public class PacketDispatcher
{
// a simple counter will allow us to get rid of 'magic' numbers used during packet registration
private static byte packetId = 0;
/**
* The SimpleNetworkWrapper instance is used both to register and send packets.
* Since I will be adding wrapper methods, this field is private, but you should
* make it public if you plan on using it directly.
*/
private static final SimpleNetworkWrapper dispatcher = NetworkRegistry.INSTANCE.newSimpleChannel(TutorialMain.MOD_ID);
/**
* Call this during pre-init or loading and register all of your packets (messages) here
*/
public static final void registerPackets() {
// Using an incrementing field instead of hard-coded numerals, I don't need to think
// about what number comes next or if I missed on should I ever rearrange the order
// of registration (for instance, if you wanted to alphabetize them... yeah...)
// It's even easier if you create a convenient 'registerMessage' method:
PacketDispatcher.registerMessage(OpenGuiMessage.OpenGuiMessageHandler.class, OpenGuiMessage.class, Side.SERVER);
PacketDispatcher.registerMessage(SyncPlayerPropsMessage.SyncPlayerPropsMessageHandler.class, SyncPlayerPropsMessage.class, Side.CLIENT);
// If you don't want to make a 'registerMessage' method, you can do it directly:
//PacketDispatcher.dispatcher.registerMessage(OpenGuiMessage.OpenGuiMessageHandler.class, OpenGuiMessage.class, packetId++, Side.SERVER);
//PacketDispatcher.dispatcher.registerMessage(SyncPlayerPropsMessage.SyncPlayerPropsMessageHandler.class, SyncPlayerPropsMessage.class, packetId++, Side.CLIENT);
}
/**
* Registers a message and message handler
*/
private static final void registerMessage(Class handlerClass, Class messageClass, Side side) {
PacketDispatcher.dispatcher.registerMessage(handlerClass, messageClass, packetId++, side);
}
//========================================================//
// The following methods are the 'wrapper' methods; again,
// this just makes sending a message slightly more compact
// and is purely a matter of stylistic preference
//========================================================//
/**
* Send this message to the specified player.
* See {@link SimpleNetworkWrapper#sendTo(IMessage, EntityPlayerMP)}
*/
public static final void sendTo(IMessage message, EntityPlayerMP player) {
PacketDispatcher.dispatcher.sendTo(message, player);
}
/**
* Send this message to everyone within a certain range of a point.
* See {@link SimpleNetworkWrapper#sendToDimension(IMessage, NetworkRegistry.TargetPoint)}
*/
public static final void sendToAllAround(IMessage message, NetworkRegistry.TargetPoint point) {
PacketDispatcher.dispatcher.sendToAllAround(message, point);
}
/**
* Sends a message to everyone within a certain range of the coordinates in the same dimension.
*/
public static final void sendToAllAround(IMessage message, int dimension, double x, double y, double z,
double range) {
PacketDispatcher.sendToAllAround(message, new NetworkRegistry.TargetPoint(dimension, x, y, z,
range));
}
/**
* Sends a message to everyone within a certain range of the player provided.
*/
public static final void sendToAllAround(IMessage message, EntityPlayer player, double range) {
PacketDispatcher.sendToAllAround(message, player.worldObj.provider.dimensionId, player.posX,
player.posY, player.posZ, range);
}
/**
* Send this message to everyone within the supplied dimension.
* See {@link SimpleNetworkWrapper#sendToDimension(IMessage, int)}
*/
public static final void sendToDimension(IMessage message, int dimensionId) {
PacketDispatcher.dispatcher.sendToDimension(message, dimensionId);
}
/**
* Send this message to the server.
* See {@link SimpleNetworkWrapper#sendToServer(IMessage)}
*/
public static final void sendToServer(IMessage message) {
PacketDispatcher.dispatcher.sendToServer(message);
}
}
Step 2: Create an AbstractMessage class
We're going to create an abstract IMessageHandler that all of our static nested IMessageHandler classes will extend, so that we can get ourselves both an EntityPlayer passed to our onMessage method as well as rename the method according to which side we are handling it on.
One very important point to mention about this is that the nested inner class MUST be static or you will see failure to instantiate a {insert your message handler here} class messages and crash your game.
Now for some great fun with generics! We will create three abstract message classes: a base, a client, and a server message, the latter two extending the first. The reasons for this will be explained in comments in the code.
Here are the class signatures:
// the base class:
public abstract class AbstractMessageHandler<T extends IMessage> implements IMessageHandler <T, IMessage>
// class for messages sent to and handled on the client:
public abstract class AbstractClientMessageHandler<T extends IMessage> extends AbstractMessageHandler<T>
// class for messages sent to and handled on the server:
public abstract class AbstractServerMessageHandler<T extends IMessage> extends AbstractMessageHandler<T>
AbstractMessageHandler (note that it's much more compact if you delete all the extraneous comments!)
/**
*
* Syntax for extending this class is "public class YourMessageHandler extends AbstractMessageHandler"
*
* I prefer to have an EntityPlayer readily available when handling packets, as well as to
* know which side I'm on without having to check every time, so I handle those operations
* here and pass off the rest of the work to abstract methods to be handled in each sub-class.
*
* We do not want to have to implement client handling for server side messages (and vice-versa),
* however, so we will abstractify even further, as well as create separate packages to organize
* our client vs. server messages. If you only have a few packets, you may opt not to, but once
* you have more than a handful, keeping them separate makes it easier to remember on which side
* to register, which side you can send to, and so on.
*
*/
public abstract class AbstractMessageHandler<T extends IMessage> implements IMessageHandler <T, IMessage>
{
/**
* Handle a message received on the client side
* @return a message to send back to the Server, or null if no reply is necessary
*/
@SideOnly(Side.CLIENT)
public abstract IMessage handleClientMessage(EntityPlayer player, T message, MessageContext ctx);
/**
* Handle a message received on the server side
* @return a message to send back to the Client, or null if no reply is necessary
*/
public abstract IMessage handleServerMessage(EntityPlayer player, T message, MessageContext ctx);
/*
* Here is where I parse the side and get the player to pass on to the abstract methods.
* This way it is immediately clear which side received the packet without having to
* remember or check on which side it was registered and the player is immediately
* available without a lengthy syntax.
*/
@Override
public IMessage onMessage(T message, MessageContext ctx) {
// due to compile-time issues, FML will crash if you try to use Minecraft.getMinecraft() here,
// even when you restrict this code to the client side and before the code is ever accessed;
// a solution is to use your proxy classes to get the player (see below).
if (ctx.side.isClient()) {
// the only reason to check side here is to use our more aptly named handling methods
// client side proxy will return the client side EntityPlayer
return handleClientMessage(TutorialMain.proxy.getPlayerEntity(ctx), message, ctx);
} else {
// server side proxy will return the server side EntityPlayer
return handleServerMessage(TutorialMain.proxy.getPlayerEntity(ctx), message, ctx);
}
}
}
Let's take a quick look at the Proxy class method used above; remember this is necessary to prevent FML from attempting to load EntityClientPlayerMP on the server, even though that section of code should not even have been accessed during startup - somehow it is, and it crashes unless you do the following:
// In your server proxy (mine is named CommonProxy):
/**
* Returns a side-appropriate EntityPlayer for use during message handling
*/
public EntityPlayer getPlayerEntity(MessageContext ctx) {
return ctx.getServerHandler().playerEntity;
}
// In your client proxy:
@Override
public EntityPlayer getPlayerEntity(MessageContext ctx) {
// Note that if you simply return 'Minecraft.getMinecraft().thePlayer',
// your packets will not work because you will be getting a client
// player even when you are on the server! Sounds absurd, but it's true.
// Solution is to double-check side before returning the player:
return (ctx.side.isClient() ? Minecraft.getMinecraft().thePlayer : super.getPlayerEntity(ctx));
}
AbstractClientMessageHandler
/**
*
* This is just a convenience class that will prevent the server-side message handling
* method from appearing in our client message handler classes.
*
*/
public abstract class AbstractClientMessageHandler<T extends IMessage> extends AbstractMessageHandler<T>
{
// implementing a final version of the server message handler both prevents it from
// appearing automatically and prevents us from ever accidentally overriding it
public final IMessage handleServerMessage(EntityPlayer player, T message, MessageContext ctx) {
return null;
}
}
AbstractServerMessageHandler
/**
*
* This is just a convenience class that will prevent the client-side message handling
* method from appearing in our server message handler classes.
*
*/
public abstract class AbstractServerMessageHandler<T extends IMessage> extends AbstractMessageHandler<T>
{
// implementing a final version of the client message handler both prevents it from
// appearing automatically and prevents us from ever accidentally overriding it
public final IMessage handleClientMessage(EntityPlayer player, T message, MessageContext ctx) {
return null;
}
}
I find it logical to organize my packages in a way that separates the client and server messages from each other - this way you can tell simply by glancing at the package name how your message can be used and, if you have lots of packets, it will be easier to check if you registered them all, especially if you register your packets in alphabetical order and register the two sides separately.
My package structure is as follows (but you should organize however you like):
Finally (and completely unnecessarily), now that you have all the abstract message classes, you can make message registration even more automatic by letting it detect which side it should register on for you:
private static final void registerMessage(Class<? extends IMessageHandler<REQ, REPLY>> handlerClass, Class<REQ> messageClass) {
Side side = AbstractClientMessageHandler.class.isAssignableFrom(handlerClass) ? Side.CLIENT : Side.SERVER;
PacketDispatcher.dispatcher.registerMessage(handlerClass, messageClass, packetId++, side);
}
If you really wanted to go all out, I bet you could read the file names from your network.packet.client|server directories and have the registration process entirely automated, but I'll leave that up to you xD
Step 3: Real Examples
Here I will show two real packets that I use in my tutorial mod, one each for client and server. The first is sent to the client in order to synchronize extended property data, and the second is sent when a key is pressed (on the client) and opens a GUI (on the server).
SyncPlayerPropsMessage
// I send this packet whenever the player joins the world, specifically from ExtendedPlayer#loadProxyData:
PacketDispatcher.sendTo(new SyncPlayerPropsMessage(player), (EntityPlayerMP) player);
/**
*
* A packet to send ALL data stored in your extended properties to the client.
* This is handy if you only need to send your data once per game session or
* all of your data needs to be synchronized together; it's also handy while
* first starting, since you only need one packet for everything - however,
* you should NOT use such a packet in your final product!!!
*
* Each packet should handle one thing and one thing only, in order to minimize
* network traffic as much as possible. There is no point sending 20+ fields'
* worth of data when you just need the current mana amount; conversely, it's
* foolish to send 20 packets for all the data when the player first loads, when
* you could send it all in one single packet.
*
* TL;DR - make separate packets for each piece of data, and one big packet for
* those times when you need to send everything.
*
*/
public class SyncPlayerPropsMessage implements IMessage
// remember - the IMessageHandler will be implemented as a static inner class
{
// Previously, we've been writing each field in our properties one at a time,
// but that is really annoying, and we've already done it in the save and load
// NBT methods anyway, so here's a slick way to efficiently send all of your
// extended data, and no matter how much you add or remove, you'll never have
// to change the packet / synchronization of your data.
// this will store our ExtendedPlayer data, allowing us to easily read and write
private NBTTagCompound data;
// The basic, no-argument constructor MUST be included to use the new automated handling
public SyncPlayerPropsMessage() {}
// We need to initialize our data, so provide a suitable constructor:
public SyncPlayerPropsMessage(EntityPlayer player) {
// create a new tag compound
data = new NBTTagCompound();
// and save our player's data into it
ExtendedPlayer.get(player).saveNBTData(data);
}
@Override
public void fromBytes(ByteBuf buffer) {
// luckily, ByteBufUtils provides an easy way to read the NBT
data = ByteBufUtils.readTag(buffer);
}
@Override
public void toBytes(ByteBuf buffer) {
// ByteBufUtils provides a convenient method for writing the compound
ByteBufUtils.writeTag(buffer, data);
}
/**
* Remember: this class MUST be static or you will crash
*
* Don't forget to add as well - it is required
*/
public static class Handler extends AbstractClientMessageHandler<SyncPlayerPropsMessage> {
// the fruits of our labor: we immediately know from the method name that we are handling
// a message on the client side, and we have our EntityPlayer right there ready for use. Awesome.
@Override
public IMessage handleClientMessage(EntityPlayer player, SyncPlayerPropsMessage message, MessageContext ctx) {
// now we can just load the NBTTagCompound data directly; one and done, folks
ExtendedPlayer.get(player).loadNBTData(message.data);
return null;
}
// Note here that we don't (and can't) implement the handleServerMessage method
// since we extended AbstractClientMessage. This is exactly what we want.
}
}
OpenGuiMessage
// sent using the following code from KeyInputEvent:
PacketDispatcher.sendToServer(new OpenGuiMessage(TutorialMain.GUI_CUSTOM_INV));
/**
* If you are going with the more simple route, the only difference is your inner handler class signature
* will look like this:
*
* public static class Handler implements IMessageHandler<OpenGuiMessage, IMessage>
*
* and the only method will be:
*
* public IMessage onMessage(OpenGuiMessage, MessageContext)
*
* Note that you can't immediately tell what side you are supposed to be on just from looking at the method,
* and you have to do some work to get the player if you want it. But we fixed all that xD
*/
public class OpenGuiMessage implements IMessage {
// this will store the id of the gui to open
private int id;
// The basic, no-argument constructor MUST be included to use the new automated handling
public OpenGuiMessage() {}
// if there are any class fields, be sure to provide a constructor that allows
// for them to be initialized, and use that constructor when sending the packet
public OpenGuiMessage(int id) {
this.id = id;
}
@Override
public void fromBytes(ByteBuf buffer) {
// basic Input/Output operations, very much like DataInputStream
id = buffer.readInt();
}
@Override
public void toBytes(ByteBuf buffer) {
// basic Input/Output operations, very much like DataOutputStream
buffer.writeInt(id);
}
public static class Handler extends AbstractServerMessageHandler<OpenGuiMessage> {
@Override
public IMessage handleServerMessage(EntityPlayer player, OpenGuiMessage message,
MessageContext ctx) {
// because we sent the gui's id with the packet, we can handle all cases with one line:
player.openGui(TutorialMain.instance, message.id, player.worldObj, (int) player.posX, (int) player.posY, (int) player.posZ);
return null;
}
}
}
Bi-Directional Packets
The same packet can be sent both ways, but you must ensure that you register a handler for both sides. You could do this by creating two classes, e.g. YourMessageClientHandler and YourMessageServerHandler, but we can do better:
/**
* For messages which can be sent to either Side
*/
public abstract class AbstractBiMessageHandler<T extends IMessage> extends AbstractMessageHandler<T> {
}
Yep, that's it, empty... why would we do that? This lets us implement BOTH our onMessage handlers, but more importantly, gives us a way to distinguish packets during registration:
/**
* Registers a message and message handler on both sides
* @param handlerClass Must extend {@link AbstractBiMessageHandler}
* @param messageClass must implement {@link IMessage}
*/
private static final void registerBiMessage(Class handlerClass, Class messageClass) {
if (AbstractBiMessageHandler.class.isAssignableFrom(handlerClass)) {
PacketDispatcher.dispatcher.registerMessage(handlerClass, messageClass, packetId, Side.CLIENT);
PacketDispatcher.dispatcher.registerMessage(handlerClass, messageClass, packetId++, Side.SERVER);
} else {
throw new IllegalArgumentException("Cannot register " + handlerClass.getName() + " on both sides - must extend AbstractBiMessageHandler!");
}
}
Then we create and register a bidirectional packet:
registerBiMessage(PlaySoundPacket.Handler.class, PlaySoundPacket.class);
// Sound packet handler code:
public static class Handler extends AbstractBiMessageHandler<PlaySoundPacket> {
@Override
public IMessage handleClientMessage(EntityPlayer player, PlaySoundPacket message, MessageContext ctx) {
// This will play a sound that only the client player will hear:
player.playSound(message.sound, message.volume, message.pitch);
return null;
}
@Override
public IMessage handleServerMessage(EntityPlayer player, PlaySoundPacket message, MessageContext ctx) {
// This will play a sound that everyone nearby will hear:
player.worldObj.playSoundEffect(message.x, message.y, message.z, message.sound, message.volume, message.pitch);
return null;
}
}
Setting up the Server
Once everything is ready, you will want to test it on the debug server in Eclipse or whatever IDE you are using. To do so, you need to modify two files found in your working directory: eula.txt and server.properties. If you have different working directories for different mods, you will have to do this in each of those directories.
1. eula.txt: simply change the value from 'false' to 'true'
2. server.properties: change 'online-mode' to 'false'
You also need to make sure you are using the new 'GradleStart' and 'GradleStartServer' instead of the previous cpw tweak classes as your 'Main Class' in your run configurations, and remove all program arguments. Be sure to keep the VM argument, but only for the client configuration: "-Dfml.ignoreInvalidMinecraftCertificates=true"
That's it!
Well, I hope you enjoyed that little exercise as much as I did, and hopefully this helped you customize the packet handling experience to your liking. Again, I feel the need to stress that the solutions presented in this tutorial are wholly unnecessary for a functioning packet handling system, so please do not think that it has to be done this way.
FOOTNOTE
Now that you've gone through all of that, you may be thinking, as I did, that it seems really stupid to have to write a static IMessageHandler class for every single IMessage. I mean, can't it be generalized somehow? ABSOLUTELY!
Now, there are some who prefer to keep the message handler completely separate from the message class, since they are technically two different things. That's fine. However, my opinion is that the message should be completely self-contained as much as possible so that when you add a new message, you write one single class that does everything it needs to do to accomplish what it's supposed to do, and that includes handling itself when it is received on the other side. It makes the code simple and easy to understand, and you'll never have to write another stupid IMessageHandler class again! Sweet.
A few notes about this method of self-contained messages:
1. You should still choose to extend either the Server or Client message class in most cases
2. You won't ever need to check Side again (unless implementing a bidirectional message, which is very rare)
3. You CAN use 'this' in your message class when processing itself, because the way the system is set up the message instance that is constructed on the receiving side calls the processing method after reading in its data. I personally find this more intuitive than writing 'msg.whatever' in an external handling class.
Finally, again, let me reiterate the fact that NONE OF THE ABOVE is necessary. At all. You can have a perfectly functioning network system without doing any of it. In fact, I recommend you try the 'normal' way first, as only then will you truly appreciate how awesome this tutorial is. Er, I mean, as only then will you be able to determine for yourself which way you prefer... Happy modding!
MY TUTORIALS
Need help with modding? Check out my tutorials on Github, or read them on the forums:
That's an error in the forum's bb code >.< It keeps messing up my syntax, which should be:
// first layer of abstraction:
public abstract class AbstractMessageHandler<T extends IMessage> implements IMessageHandler <T, IMessage>
// second layer (two classes: one for server, one for client)
public abstract class AbstractServerMessageHandler<T extends IMessage> extends AbstractMessageHandler<T>
public abstract class AbstractClientMessageHandler<T extends IMessage> extends AbstractMessageHandler<T>
Ahh, I hate being such a noob when it comes to these but I'm guessing your AbstractMessageHandlerIMessage class should look like this:
public abstract class AbstractMessageHandlerIMessageHandler< T extends IMessage > extends AbstractMessageHandler< T >{
public abstract IMessage handleClientMessage(EntityPlayer player, T message, MessageContext ctx);
public abstract IMessage handleServerMessage(EntityPlayer player, T message, MessageContext ctx);
@Override
public IMessage onMessage(T message, MessageContext ctx) {
if (ctx.side == Side.CLIENT) {
return handleClientMessage(FastTravel.proxy.getPlayerEntity(ctx), message, ctx);
} else {
return handleServerMessage(FastTravel.proxy.getPlayerEntity(ctx), message, ctx);
}
}
}
instead of this?
public abstract class AbstractMessageHandlerIMessageHandler <t, imessage="">
{
public abstract IMessage handleClientMessage(EntityPlayer player, T message, MessageContext ctx);
public abstract IMessage handleServerMessage(EntityPlayer player, T message, MessageContext ctx);
@Override
public IMessage onMessage(T message, MessageContext ctx) {
if (ctx.side == Side.CLIENT) {
return handleClientMessage(TutorialMain.proxy.getPlayerEntity(ctx), message, ctx);
} else {
return handleServerMessage(TutorialMain.proxy.getPlayerEntity(ctx)
(Don't forget to update your thread)
This Rich Editor is really annoying, I had a bit of trouble actually posting the code. I liked BlenderArtists.org setup - it's always in the Mark Up form. It should be like that in this forum since it is full of coders.
Edit:
I don't know what I'm doing wrong but unlike your examples for Handlers and Messages, I need to do something like this:
public static class TeleportHandle extends AbstractServerMessageHandler{
public IMessage handleServerMessage(EntityPlayer player, TeleportMessage message, MessageContext ctx){
}
@Override
public IMessage onMessage(IMessage message, MessageContext ctx) {
// TODO Auto-generated method stub
return null;
}
}
}
I have to override onMessage even though I don't want to (since it holds the code to run the handles, and I can't just run the super code because the super is an abstract class) and I'm getting an error with the "static" indicator and eclipse wants me to remove it (even though I need it).
Any ideas?
Edit 2:
I tried running the code, taking off the static and leaving the onMessage override. I registered the message and handler and the onMessage isn't triggered, I did a simple print on it but nothing happens when I send the message - there aren't even any errors...
You missed the second layer of abstraction from my last post:
// Abstract Handler class for server; note the <T extends IMessage> and <T>
public abstract class AbstractServerMessageHandler<T extends IMessage> extends AbstractMessageHandler<T>
// so your actual handler class will need to have a signature like this,
// with your message class to be handled in brackets: <YourMessageClass>
public static class OpenGuiMessageHandler extends AbstractServerMessageHandler<OpenGuiMessage>
And yeah, I need to edit and fix the tutorial, but it seems every time I edit it it gets worse
Ahh, I see, I'm doing my best to fix it but I'm still having some problems - I'll probably rewrite it on my own now that I know what I'm doing (kind of). I'm into things like these that make it nicer to write code.
Though, I'd like to see a full example of this in your ExampleMod on GitHub so I can see where everything goes - I'd be much easier to debug that way and I think it'd benefit more than just me.
Anyway, thanks for the tutorial I've learned a lot about packets just from going through it so many times hahahahah
I will be putting it on Github eventually - it's ready to go, but I wanted to test it more thoroughly before uploading it to the example mod.
I'm sure the tutorial is not easy to follow right now, with so many pieces of code butchered by the editor :\ I'll try to edit it again when I get home from work tonight, and possibly upload the whole tutorial to my Git as well so there is at least one correct copy online.
*@[email protected]$^ Man, I spent about 20 minutes yesterday fixing the OP in BBCode mode and it STILL destroyed my text!!! This is very frustrating >.<
Hey! Any updates on getting this code up on Github?
Well, I could put it up there, but honestly I am extremely unhappy with the way SimpleNetworkWrapper fails to perform in various cases and is basically just more difficult to use than it should be. The code from the 'old' tutorial on Netty Packet Handling, while it may indeed have a memory leak (if so, it is a very small leak), has never caused me any problems, is way easier to use than SNW, and just plain works every single time, so I plan to continue to use it until I either find time to implement my own network code or a better one comes along.
If you're really worried about the integrity of the older code, or you're just worried about cpw frowning upon you, you can still use SNW, either as outlined above or with a more standard implementation, and it should work 90% or more of the time. Personally, I don't care for it one bit.
Well, I updated it again, but much of the formatting still got broken when I saved >.<
Important note: Your packets will not work properly if you return Minecraft.getMinecraft().thePlayer from the ClientProxy directly, as the method overrides that in the CommonProxy and is called EVEN ON THE SERVER SIDE - which means you'll end up with a client player and probably a crash if you are not playing single player.
To fix that and have your packets working as expected, simply do a quick check for side and return super if you're handling a server message:
Whoa, I fixed the code formatting and it finally worked!!! EDIT: Well, mostly lol.
Also, for those of you venturing into 1.8, the network code has not changed so you can use the exact same code*! Pretty awesome, no?
* Except for any imports beginning with 'cpw.mods.fml' need to be changed to 'net.minecraftforge.fml'
Errata:
You may notice a discrepancy in message handler names, e.g. OpenGuiMessage.OpenGuiMessageHandler.class during registration, but the actual class name is just 'OpenGuiMessage.Handler.class'. This is because I changed all my message handler names to just 'Handler', as that is much easier to manage, but forgot to edit that part in the OP and, well, given my recent success at finally getting the generics (e.g. <T>) to format correctly, I didn't want to push my luck.
For 100% accurate code examples, check the demo mod on Github.
EDIT: I'm a dingus, ignore my post. I had the answer all along, I was supposed to send the packet to the server and then the server plays it everywhere. Not sendtoAllAround()
Hi, I'm having trouble handling any messages received on the server side when using SendToAllAround. What I'm trying to do is play a sound when a player presses a button on one of my GUIs and any nearby players will hear this sound. Here's how I've been trying to do it using your networking classes PacketDispatcher, AbstractMessageHandler, AbstractBiMessageHandler, PlaySoundPacket.
Inside of my Gui class, in the constructor:
//player is the player that is passed in when constructing the GUI
When playing Singleplayer, this works fine. I can hear the sounds, and BiMessages work. Although, the moment it becomes multiplayer it gives an error and plays no sound. Any BiMessages will do this, and sending messages to the server works. I am using Minecraft Forge for 1.7.2 (10.13.2.1230).
Client thread/ERROR] [FML]: SimpleChannelHandlerWrapper exception
java.lang.NullPointerException
at cpw.mods.fml.common.network.FMLOutboundHandler$OutboundTarget$7.selectNetworks(FMLOutboundHandler.java:193) ~[FMLOutboundHandler$OutboundTarget$7.class:?]
at cpw.mods.fml.common.network.FMLOutboundHandler.write(FMLOutboundHandler.java:273) ~[FMLOutboundHandler.class:?]
at io.netty.channel.DefaultChannelHandlerContext.invokeWrite(DefaultChannelHandlerContext.java:644) ~[DefaultChannelHandlerContext.class:?]
at io.netty.channel.DefaultChannelHandlerContext.write(DefaultChannelHandlerContext.java:698) ~[DefaultChannelHandlerContext.class:?]
at io.netty.channel.DefaultChannelHandlerContext.write(DefaultChannelHandlerContext.java:637) ~[DefaultChannelHandlerContext.class:?]
at io.netty.handler.codec.MessageToMessageEncoder.write(MessageToMessageEncoder.java:115) ~[MessageToMessageEncoder.class:?]
at io.netty.handler.codec.MessageToMessageCodec.write(MessageToMessageCodec.java:116) ~[MessageToMessageCodec.class:?]
at io.netty.channel.DefaultChannelHandlerContext.invokeWrite(DefaultChannelHandlerContext.java:644) ~[DefaultChannelHandlerContext.class:?]
at io.netty.channel.DefaultChannelHandlerContext.write(DefaultChannelHandlerContext.java:698) ~[DefaultChannelHandlerContext.class:?]
at io.netty.channel.DefaultChannelHandlerContext.writeAndFlush(DefaultChannelHandlerContext.java:688) ~[DefaultChannelHandlerContext.class:?]
at io.netty.channel.DefaultChannelHandlerContext.writeAndFlush(DefaultChannelHandlerContext.java:717) ~[DefaultChannelHandlerContext.class:?]
at io.netty.channel.DefaultChannelPipeline.writeAndFlush(DefaultChannelPipeline.java:893) ~[DefaultChannelPipeline.class:?]
at io.netty.channel.AbstractChannel.writeAndFlush(AbstractChannel.java:239) ~[AbstractChannel.class:?]
at cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper.sendToAllAround(SimpleNetworkWrapper.java:210) [SimpleNetworkWrapper.class:?]
at jason.network.PacketDispatcher.sendToAllAround(PacketDispatcher.java:98) [PacketDispatcher.class:?]
at jason.network.PacketDispatcher.sendToAllAround(PacketDispatcher.java:105) [PacketDispatcher.class:?]
at jason.network.PacketDispatcher.sendToAllAround(PacketDispatcher.java:112) [PacketDispatcher.class:?]
at jason.client.gui.GuiTest.(GuiTest.java:37) [GuiTest.class:?]
at jason.handler.GuiHandler.getClientGuiElement(GuiHandler.java:28) [GuiHandler.class:?]
at cpw.mods.fml.common.network.NetworkRegistry.getLocalGuiContainer(NetworkRegistry.java:263) [NetworkRegistry.class:?]
at cpw.mods.fml.common.network.internal.FMLNetworkHandler.openGui(FMLNetworkHandler.java:93) [FMLNetworkHandler.class:?]
at net.minecraft.entity.player.EntityPlayer.openGui(EntityPlayer.java:2501) [EntityPlayer.class:?]
at cpw.mods.fml.common.network.internal.OpenGuiHandler.channelRead0(OpenGuiHandler.java:16) [OpenGuiHandler.class:?]
at cpw.mods.fml.common.network.internal.OpenGuiHandler.channelRead0(OpenGuiHandler.java:11) [OpenGuiHandler.class:?]
at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:98) [SimpleChannelInboundHandler.class:?]
at io.netty.channel.DefaultChannelHandlerContext.invokeChannelRead(DefaultChannelHandlerContext.java:337) [DefaultChannelHandlerContext.class:?]
at io.netty.channel.DefaultChannelHandlerContext.fireChannelRead(DefaultChannelHandlerContext.java:323) [DefaultChannelHandlerContext.class:?]
at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:101) [SimpleChannelInboundHandler.class:?]
at io.netty.channel.DefaultChannelHandlerContext.invokeChannelRead(DefaultChannelHandlerContext.java:337) [DefaultChannelHandlerContext.class:?]
at io.netty.channel.DefaultChannelHandlerContext.fireChannelRead(DefaultChannelHandlerContext.java:323) [DefaultChannelHandlerContext.class:?]
at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103) [MessageToMessageDecoder.class:?]
at io.netty.handler.codec.MessageToMessageCodec.channelRead(MessageToMessageCodec.java:111) [MessageToMessageCodec.class:?]
at io.netty.channel.DefaultChannelHandlerContext.invokeChannelRead(DefaultChannelHandlerContext.java:337) [DefaultChannelHandlerContext.class:?]
at io.netty.channel.DefaultChannelHandlerContext.fireChannelRead(DefaultChannelHandlerContext.java:323) [DefaultChannelHandlerContext.class:?]
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:785) [DefaultChannelPipeline.class:?]
at io.netty.channel.embedded.EmbeddedChannel.writeInbound(EmbeddedChannel.java:169) [EmbeddedChannel.class:?]
at cpw.mods.fml.common.network.internal.FMLProxyPacket.processPacket(FMLProxyPacket.java:86) [FMLProxyPacket.class:?]
at net.minecraft.network.NetworkManager.processReceivedPackets(NetworkManager.java:241) [NetworkManager.class:?]
at net.minecraft.client.multiplayer.PlayerControllerMP.updateController(PlayerControllerMP.java:317) [PlayerControllerMP.class:?]
at net.minecraft.client.Minecraft.runTick(Minecraft.java:1682) [Minecraft.class:?]
at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:1028) [Minecraft.class:?]
at net.minecraft.client.Minecraft.run(Minecraft.java:951) [Minecraft.class:?]
at net.minecraft.client.main.Main.main(Main.java:164) [Main.class:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.7.0_72]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.7.0_72]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.7.0_72]
at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.7.0_72]
at net.minecraft.launchwrapper.Launch.launch(Launch.java:135) [launchwrapper-1.11.jar:?]
at net.minecraft.launchwrapper.Launch.main(Launch.java:28) [launchwrapper-1.11.jar:?]
at net.minecraftforge.gradle.GradleStartCommon.launch(GradleStartCommon.java:78) [start/:?]
at GradleStart.main(GradleStart.java:45)
Hi, I'm having trouble handling any messages received on the server side when using SendToAllAround. What I'm trying to do is play a sound when a player presses a button on one of my GUIs and any nearby players will hear this sound. Here's how I've been trying to do it using your networking classes PacketDispatcher, AbstractMessageHandler, AbstractBiMessageHandler, PlaySoundPacket.
Inside of my Gui class, in the constructor:
//player is the player that is passed in when constructing the GUI
When playing Singleplayer, this works fine. I can hear the sounds, and BiMessages work. Although, the moment it becomes multiplayer it gives an error and plays no sound. Any BiMessages will do this, and sending messages to the server works. I am using Minecraft Forge for 1.7.2 (10.13.2.1230).
You must mean 1.7.10, judging by Forge version.
I have never seen such console output, but then I don't know that I've ever used sendToAllAround with a sound packet, since in that case I always just play the sound right away on the server side using world.playSoundAtEntity or world.playSoundEffect, both of which will automatically notify all nearby players.
Thanks for the report, though - I will look into it. In the meantime, I suggest you do as I mentioned above, as there is really no point to sending a play sound packet to all around (99% of the time, anyway :P).
Ha, I just realized this is because sendToAllAround can only be used on the server. The only method that can be used to send a packet from the client side is #sendToServer. The linked SimpleNetworkWrapper method docs explain this kind of, but I didn't make it very clear in my own docs.
So, if you want to send a sound packet to all around, you have to first send a single packet to the server player of the current client player, and have the server relay that message to all around.
How would I send a packet that included a variable that was changed client side, to the server?
example code:
int a = 2;
if (world.isRemote){
a = 5;
}
else{
//print variable.
}
You create a packet that can be sent to the server, and that packet should contain the integer field you want. When you instantiate the packet on the client, you provide it with the data you need (e.g. '5') and send it to the server; the server receives it and you can print out the value when the packet processes.
Look at the example packets, such as the OpenGuiPacket - what you want is exactly the same.
ou create a packet that can be sent to the server, and that packet should contain the integer field you want. When you instantiate the packet on the client, you provide it with the data you need (e.g. '5') and send it to the server; the server receives it and you can print out the value when the packet processes.
Look at the example packets, such as the OpenGuiPacket - what you want is exactly the same.
Thanks:) Would the same go for updating the world. (Places block via command and tell the server I placed a block?)
Handling Packets in 1.7.x and up - Making the SimpleNetworkWrapper Not So Simple
Note about terminology: I will be using the terms 'packet' and 'message' interchangeably in this tutorial.
Today, I will show you how to make a 'convenient-and-annoyance-free-but-possibly-too-complicated' version of the SimpleNetworkWrapper. If you prefer a simple solution, please see this tutorial by diesieben07 - he'll have you up and running with packets in no time. You should check out that tutorial anyway if you are going to follow along, as it provides an excellent explanation of everything you need to know about the SimpleNetworkWrapper.
IMPORTANT: The solutions presented in this tutorial are wholly unnecessary for a functioning packet handling system - they are purely stylistic. Take this tutorial with a grain of salt - it's one of those projects that was more a 'can I do this?' than 'should I do this?', and since I did it, I figured heck, maybe someone else will get a kick out of this.
Now, why would anyone want to do something different when it's so simple? Well, there were a couple points about the process that annoyed me (in comparison to the good old days when everything was better :P) and I wanted to fix:
1. There isn't an EntityPlayer passed directly to the onMessage method - when nearly every packet sent is going to want to reference either a player or world object, this seemed like a huge oversight and inconvenience to me, even though it CAN be retrieved (and stored) in one line, why should we have to do that when it can just be passed as a parameter like it always has been?
2. There is no indication of which side you are on when handling the message - you must either remember to which side you registered it, go check, or check the MessageContext#side field, all of which seem kind of lame, whereas before, at least the way I had it set up, the side you were on was embedded in the name of the handling method. Clear, concise, no chance of getting confused and using Minecraft.getMinecraft() for something when on the server. It was awesome.
3. Registering requires a byte discriminator - wait, what? Why couldn't it just automatically increment each time a message is registered, since they are all registered per mod (i.e. everyone gets to start at 0) ? What a useless parameter to provide, and boy, is that sure annoying.
Ok... so maybe those points aren't that convincing to you, and they really aren't that big of a deal. If you find that's the case, then stop now and use the earlier tutorial. If, on the other hand, the above points irk you to no end and you really wish you could do something about them, then here we go! Please remember, you can always change your mind later if you decide that the extra work isn't worth it
Step 1: Create a PacketDispatcher
NOTE: If you don't know how SimpleNetworkWrapper / IMessage / IMessageHandler classes work, please go back and read diesieben's tutorial before reading any further!
Step 2: Create an AbstractMessage class
One very important point to mention about this is that the nested inner class MUST be static or you will see failure to instantiate a {insert your message handler here} class messages and crash your game.
Now for some great fun with generics! We will create three abstract message classes: a base, a client, and a server message, the latter two extending the first. The reasons for this will be explained in comments in the code.
Here are the class signatures:
AbstractMessageHandler (note that it's much more compact if you delete all the extraneous comments!)
/**
* Handle a message received on the client side
* @return a message to send back to the Server, or null if no reply is necessary
*/
@SideOnly(Side.CLIENT)
public abstract IMessage handleClientMessage(EntityPlayer player, T message, MessageContext ctx);
/**
* Handle a message received on the server side
* @return a message to send back to the Client, or null if no reply is necessary
*/
public abstract IMessage handleServerMessage(EntityPlayer player, T message, MessageContext ctx);
/*
* Here is where I parse the side and get the player to pass on to the abstract methods.
* This way it is immediately clear which side received the packet without having to
* remember or check on which side it was registered and the player is immediately
* available without a lengthy syntax.
*/
@Override
public IMessage onMessage(T message, MessageContext ctx) {
// due to compile-time issues, FML will crash if you try to use Minecraft.getMinecraft() here,
// even when you restrict this code to the client side and before the code is ever accessed;
// a solution is to use your proxy classes to get the player (see below).
if (ctx.side.isClient()) {
// the only reason to check side here is to use our more aptly named handling methods
// client side proxy will return the client side EntityPlayer
return handleClientMessage(TutorialMain.proxy.getPlayerEntity(ctx), message, ctx);
} else {
// server side proxy will return the server side EntityPlayer
return handleServerMessage(TutorialMain.proxy.getPlayerEntity(ctx), message, ctx);
}
}
}
AbstractClientMessageHandler
{
// implementing a final version of the server message handler both prevents it from
// appearing automatically and prevents us from ever accidentally overriding it
public final IMessage handleServerMessage(EntityPlayer player, T message, MessageContext ctx) {
return null;
}
}
{
// implementing a final version of the client message handler both prevents it from
// appearing automatically and prevents us from ever accidentally overriding it
public final IMessage handleClientMessage(EntityPlayer player, T message, MessageContext ctx) {
return null;
}
}
My package structure is as follows (but you should organize however you like):
|__{various packages}
|__network
|__PacketDispatcher.java
|__packet
|__AbstractMessageHandler.java
|__client
|__AbstractClientMessageHandler.java
|__AClientMessage.java
|__AnotherClientMessage.java
|__server
|__AbstractServerMessageHandler.java
|__AServerMessage.java
|__AnotherServerMessage.java
Finally (and completely unnecessarily), now that you have all the abstract message classes, you can make message registration even more automatic by letting it detect which side it should register on for you:
If you really wanted to go all out, I bet you could read the file names from your network.packet.client|server directories and have the registration process entirely automated, but I'll leave that up to you xD
Step 3: Real Examples
SyncPlayerPropsMessage
OpenGuiMessage
Bi-Directional Packets
Yep, that's it, empty... why would we do that? This lets us implement BOTH our onMessage handlers, but more importantly, gives us a way to distinguish packets during registration:
Then we create and register a bidirectional packet:
Setting up the Server
1. eula.txt: simply change the value from 'false' to 'true'
2. server.properties: change 'online-mode' to 'false'
You also need to make sure you are using the new 'GradleStart' and 'GradleStartServer' instead of the previous cpw tweak classes as your 'Main Class' in your run configurations, and remove all program arguments. Be sure to keep the VM argument, but only for the client configuration: "-Dfml.ignoreInvalidMinecraftCertificates=true"
That's it!
Well, I hope you enjoyed that little exercise as much as I did, and hopefully this helped you customize the packet handling experience to your liking. Again, I feel the need to stress that the solutions presented in this tutorial are wholly unnecessary for a functioning packet handling system, so please do not think that it has to be done this way.
FOOTNOTE
Now that you've gone through all of that, you may be thinking, as I did, that it seems really stupid to have to write a static IMessageHandler class for every single IMessage. I mean, can't it be generalized somehow? ABSOLUTELY!
Now, there are some who prefer to keep the message handler completely separate from the message class, since they are technically two different things. That's fine. However, my opinion is that the message should be completely self-contained as much as possible so that when you add a new message, you write one single class that does everything it needs to do to accomplish what it's supposed to do, and that includes handling itself when it is received on the other side. It makes the code simple and easy to understand, and you'll never have to write another stupid IMessageHandler class again! Sweet.
A few notes about this method of self-contained messages:
1. You should still choose to extend either the Server or Client message class in most cases
2. You won't ever need to check Side again (unless implementing a bidirectional message, which is very rare)
3. You CAN use 'this' in your message class when processing itself, because the way the system is set up the message instance that is constructed on the receiving side calls the processing method after reading in its data. I personally find this more intuitive than writing 'msg.whatever' in an external handling class.
Finally, again, let me reiterate the fact that NONE OF THE ABOVE is necessary. At all. You can have a perfectly functioning network system without doing any of it. In fact, I recommend you try the 'normal' way first, as only then will you truly appreciate how awesome this tutorial is. Er, I mean, as only then will you be able to determine for yourself which way you prefer... Happy modding!
MY TUTORIALS
Need help with modding? Check out my tutorials on Github, or read them on the forums:
Custom Armors, the OOP Way (GitHub only)
1.7.2 Example Mod (GitHub only)
Modding With APIs
EventHandler and IExtendedEntityProperties
Custom Inventories in Items and Players
Multi-Input/Output Furnace
Custom Projectile: Getting it to Render
Overriding shift-click in custom Containers
Using Potions in Crafting Recipes
Enchanted Books and Crafting Recipes
Art by me: [email protected]
Thanks - it should all be ready to go now if you want to give it a try!
I PMed you only to realize that you don't take help via PMs - so I'll post here.
This line gives me an error for some reason, something seems to be wrong in the <t, imessage="">
Here is what eclipse says:
Could you help me out with this?
instead of this?
(Don't forget to update your thread)
This Rich Editor is really annoying, I had a bit of trouble actually posting the code. I liked BlenderArtists.org setup - it's always in the Mark Up form. It should be like that in this forum since it is full of coders.
Edit:
I don't know what I'm doing wrong but unlike your examples for Handlers and Messages, I need to do something like this:
I have to override onMessage even though I don't want to (since it holds the code to run the handles, and I can't just run the super code because the super is an abstract class) and I'm getting an error with the "static" indicator and eclipse wants me to remove it (even though I need it).
Any ideas?
Edit 2:
I tried running the code, taking off the static and leaving the onMessage override. I registered the message and handler and the onMessage isn't triggered, I did a simple print on it but nothing happens when I send the message - there aren't even any errors...
And yeah, I need to edit and fix the tutorial, but it seems every time I edit it it gets worse
Though, I'd like to see a full example of this in your ExampleMod on GitHub so I can see where everything goes - I'd be much easier to debug that way and I think it'd benefit more than just me.
Anyway, thanks for the tutorial I've learned a lot about packets just from going through it so many times hahahahah
I'm sure the tutorial is not easy to follow right now, with so many pieces of code butchered by the editor :\ I'll try to edit it again when I get home from work tonight, and possibly upload the whole tutorial to my Git as well so there is at least one correct copy online.
Well, I could put it up there, but honestly I am extremely unhappy with the way SimpleNetworkWrapper fails to perform in various cases and is basically just more difficult to use than it should be. The code from the 'old' tutorial on Netty Packet Handling, while it may indeed have a memory leak (if so, it is a very small leak), has never caused me any problems, is way easier to use than SNW, and just plain works every single time, so I plan to continue to use it until I either find time to implement my own network code or a better one comes along.
If you're really worried about the integrity of the older code, or you're just worried about cpw frowning upon you, you can still use SNW, either as outlined above or with a more standard implementation, and it should work 90% or more of the time. Personally, I don't care for it one bit.
Important note: Your packets will not work properly if you return Minecraft.getMinecraft().thePlayer from the ClientProxy directly, as the method overrides that in the CommonProxy and is called EVEN ON THE SERVER SIDE - which means you'll end up with a client player and probably a crash if you are not playing single player.
To fix that and have your packets working as expected, simply do a quick check for side and return super if you're handling a server message:
Everything seems to work as it should now - huzzah!
Also, for those of you venturing into 1.8, the network code has not changed so you can use the exact same code*! Pretty awesome, no?
* Except for any imports beginning with 'cpw.mods.fml' need to be changed to 'net.minecraftforge.fml'
Errata:
You may notice a discrepancy in message handler names, e.g. OpenGuiMessage.OpenGuiMessageHandler.class during registration, but the actual class name is just 'OpenGuiMessage.Handler.class'. This is because I changed all my message handler names to just 'Handler', as that is much easier to manage, but forgot to edit that part in the OP and, well, given my recent success at finally getting the generics (e.g. <T>) to format correctly, I didn't want to push my luck.
For 100% accurate code examples, check the demo mod on Github.
Hi, I'm having trouble handling any messages received on the server side when using SendToAllAround. What I'm trying to do is play a sound when a player presses a button on one of my GUIs and any nearby players will hear this sound. Here's how I've been trying to do it using your networking classes PacketDispatcher, AbstractMessageHandler, AbstractBiMessageHandler, PlaySoundPacket.
Inside of my Gui class, in the constructor:
//player is the player that is passed in when constructing the GUI
PacketDispatcher.sendToAllAround(new PlaySoundPacket(Constants.MODID + ":fx.ui.test", 1, 1, player), player, 50.0);
When playing Singleplayer, this works fine. I can hear the sounds, and BiMessages work. Although, the moment it becomes multiplayer it gives an error and plays no sound. Any BiMessages will do this, and sending messages to the server works. I am using Minecraft Forge for 1.7.2 (10.13.2.1230).
You must mean 1.7.10, judging by Forge version.
I have never seen such console output, but then I don't know that I've ever used sendToAllAround with a sound packet, since in that case I always just play the sound right away on the server side using world.playSoundAtEntity or world.playSoundEffect, both of which will automatically notify all nearby players.
Thanks for the report, though - I will look into it. In the meantime, I suggest you do as I mentioned above, as there is really no point to sending a play sound packet to all around (99% of the time, anyway :P).
Ha, I just realized this is because sendToAllAround can only be used on the server. The only method that can be used to send a packet from the client side is #sendToServer. The linked SimpleNetworkWrapper method docs explain this kind of, but I didn't make it very clear in my own docs.
So, if you want to send a sound packet to all around, you have to first send a single packet to the server player of the current client player, and have the server relay that message to all around.
How would I send a packet that included a variable that was changed client side, to the server?
example code:
if (world.isRemote){
world.setBlockState(pos, block)
//tell server I edited blocks.
}
I have pretty much everything you have from github in my environment and the game runs fine.
I'm not sure how to tell the server about changes in the world.
I will look at my code again in a few hours and try to figure out.
Very great tutorial:) It really helped me out! Thanks:D
I complicate easy problems, come begging for help, and leave with a simple solution.

Anywhere you want. I put my registration code in the PacketDispatcher class to keep everything in the same place.
You create a packet that can be sent to the server, and that packet should contain the integer field you want. When you instantiate the packet on the client, you provide it with the data you need (e.g. '5') and send it to the server; the server receives it and you can print out the value when the packet processes.
Look at the example packets, such as the OpenGuiPacket - what you want is exactly the same.
Thanks:) Would the same go for updating the world. (Places block via command and tell the server I placed a block?)
I complicate easy problems, come begging for help, and leave with a simple solution.
