Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

Using the Simple Network Implementation (SimpleNetworkWrapper) [1.7+]


Recommended Posts

This tutorial is old, please refer to the official documentation instead!

 

This is the recommended way to do Packets as of 1.7, it is not advised to use Netty directly, because it can cause Problems.

 

First, we are going to need to obtain a

SimpleNetworkWrapper
 

instance for our Channel. You get it by calling

NetworkRegistry.INSTANCE.newSimpleChannel("myChannel")
 

. Store the result in a static field in your Mod class.

 

Registering a Packet

Packets are done via separate classes for every Packet-ID (in the code also referred to as "discriminator"). If you are used to the huge

switch
 

-

case
 

statements like many people did it in 1.6, well, it's time to move on.

For each Packet you want to send, we are going to need 2 classes, one for the Packet (implementing

IMessage
 

) and one for handling the Packet (implementing

IMessageHandler
 

). For a cleaner setup I usually implement the handler as an inner class of the Packet (see below).

To register your Packet, call

registerMessage(MyMessageHandler.class, MyMessage.class, packetID, receivingSide)
 

on the wrapper you obtained. The first two parameters should be self-explanatory. PacketID is the same as it was in 1.6, usually you can start at 0 and just increment by one for each packet you add. Keep in mind that the maximum PacketID you can use is 255. The receiving side is, well, the side that should receive the packet. Can be one of

Side.CLIENT
 

or

Side.SERVER
 

.

 

Implementing the packet class

The IMessage interface requires you to implement two methods.

fromBytes
 

and

toBytes
 

. You can think of the IMessage as a blob of data that gets send over the network. Because the network is only a stream bytes, you have to serialize and deserialize your message into this stream of bytes. That is what these two methods do.

toBytes
 

writes your data to the stream (use the

write***
 

methods on the ByteBuf for that) and

fromBytes
 

reads your data back into the message class (use the

read***
 

methods for that).

Keep in mind that your packet class must always have a default constructor, otherwise FML cannot use it.

 

Implementing the packet handler

The IMessageHandler simply requires one method,

onMessage
 

. This method is called when your packet is received, after the IMessage instance has been created and

fromBytes
 

has been called.

Do whatever you want your packet to do in this method.

Warning for Minecraft 1.8: As of Minecraft 1.8

onMessage
 

is no longer called on the main thread but on the Netty-IO-Thread instead. Therefor you must not interact with the World or other "normal Minecraft-y things" in here directly. Instead you must schedule your code to be run on the main thread, which is done through the

IThreadListener
 

interface and it's

addScheduledTask
 

method. The

Runnable
 

you pass to this method will be executed on the main thread. The

IThreadListener
 

instance is

Minecraft.getMinecraft()
 

on the client and

(WorldServer) ctx.getServerHandler().playerEntity.worldObj
 

on the server. See below for examples.

 

Sending packets

The SimpleNetworkWrapper provides you with methods to send IMessage instances to various targets. They should be pretty self-explanatory (

sendToServer
 

,

sendTo
 

, etc.).

If a vanilla packet is needed from your IMessage (for use e.g. as a TileEntity description packet) you can use the

getPacketFrom
 

method.

 

Packet-Responses

The IMessageHandler has a built-in response functionality. Say you send a packet from the client to the server, then you can easily just send a response directly from the

onMessage
 

method, by returning it. That is also what the 2nd type-parameter on IMessageHandler is used for, although leaving it at IMessage doesn't hurt, even if you do have a reply message.

 

Code-Examples (not made for copy & paste)

@Mod
class MyMod {
    
    public static SimpleNetworkWrapper network;
    
    @EventHandler
    public void preInit(FMLPreInitializationEvent event) {
       network = NetworkRegistry.INSTANCE.newSimpleChannel("MyChannel");
       network.registerMessage(MyMessage.Handler.class, MyMessage.class, 0, Side.SERVER);
       // network.registerMessage(SecondMessage.Handler.class, SecondMessage.class, 1, Side.CLIENT);
       // ...
    }

}

class MyMessage implements IMessage {
    
    private String text;

    public MyMessage() { }

    public MyMessage(String text) {
        this.text = text;
    }

    @Override
    public void fromBytes(ByteBuf buf) {
        text = ByteBufUtils.readUTF8String(buf); // this class is very useful in general for writing more complex objects
    }

    @Override
    public void toBytes(ByteBuf buf) {
        ByteBufUtils.writeUTF8String(buf, text);
    }

    public static class Handler implements IMessageHandler<MyMessage, IMessage> {
        
        @Override
        public IMessage onMessage(MyMessage message, MessageContext ctx) {
            System.out.println(String.format("Received %s from %s", message.text, ctx.getServerHandler().playerEntity.getDisplayName()));
            return null; // no response in this case
        }

        // or in 1.8:
        @Override
        public IMessage onMessage(MyMessage message, MessageContext ctx) {
            IThreadListener mainThread = (WorldServer) ctx.getServerHandler().playerEntity.worldObj; // or Minecraft.getMinecraft() on the client
            mainThread.addScheduledTask(new Runnable() {
                @Override
                public void run() {
                    System.out.println(String.format("Received %s from %s", message.text, ctx.getServerHandler().playerEntity.getDisplayName()));
                }
            });
            return null; // no response in this case
        }
    }
}

// Sending packets:
MyMod.network.sendToServer(new MyMessage("foobar"));
MyMod.network.sendTo(new SomeMessage(), somePlayer);
 
Edited by diesieben07
  • Like 1
Link to comment
Share on other sites

  • 5 weeks later...
  • 1 month later...
  • 2 months later...

Something I discovered the hard way - obvious in retrospect, but hey.

If you intend to send a one-way message from the server to the client, your registerMessage should run on the server side code.  Otherwise, if your registerMessage is called from the client side code (logical, since you only need a client side handler), then registerMessage is not called on the dedicated server, and when the dedicated server sends a message, it uses the wrong discriminator.

 

In summary- call registerMessage from common or server-side-only code.

 

-TGG

 

Link to comment
Share on other sites

If you intend to send a one-way message from the server to the client, your registerMessage should run on the server side code.  Otherwise, if your registerMessage is called from the client side code (logical, since you only need a client side handler), then registerMessage is not called on the dedicated server, and when the dedicated server sends a message, it uses the wrong discriminator.

I am not sure I follow you here.

First of all, messages are always one-way.

And then you should call registerMethod for every message on both sides, no matter where it is received.

Link to comment
Share on other sites

If you intend to send a one-way message from the server to the client, your registerMessage should run on the server side code.  Otherwise, if your registerMessage is called from the client side code (logical, since you only need a client side handler), then registerMessage is not called on the dedicated server, and when the dedicated server sends a message, it uses the wrong discriminator.

I am not sure I follow you here.

First of all, messages are always one-way.

Well, yes, except that you can use the same message class to communicate from server to client and then from client back to server again.

 

And then you should call registerMethod for every message on both sides, no matter where it is received.

Yeah, I guess that's what I mean.  I think the registerMessage is confusing because it looks like a handler registration.

 

For example, if I'm sending a message from the server to the client, it's obvious that I need to call

registerMessage(myClientSideHandler, MyMessage, myMessageTypeID, Side.CLIENT);

in order for my client side to receive messages.  A logical place for this is from the constructor or initialiser of the client-side class which needs to receive these messages, or perhaps from the clientproxy init.

 

If I never send this message from the client to the server, I might not bother to call registerMessage on the server side.  Why would I, since there is no server side handler?

registerMessage(null, MyMessage, myMessageTypeID, Side.SERVER);  -?

 

This works fine in Integrated Server.  It fails silently on the dedicated server, because the message sends with a default myMessageTypeID (discriminator) of zero.

 

-TGG

 

 

 

 

 

 

 

 

 

 

 

 

Link to comment
Share on other sites

Well, yes, except that you can use the same message class to communicate from server to client and then from client back to server again.

Well, no, you can't.

For example, if I'm sending a message from the server to the client, it's obvious that I need to call

registerMessage(myClientSideHandler, MyMessage, myMessageTypeID, Side.CLIENT);

in order for my client side to receive messages.  A logical place for this is from the constructor or initialiser of the client-side class which needs to receive these messages, or perhaps from the clientproxy init.

 

If I never send this message from the client to the server, I might not bother to call registerMessage on the server side.  Why would I, since there is no server side handler?

registerMessage(null, MyMessage, myMessageTypeID, Side.SERVER);  -?

 

This works fine in Integrated Server.  It fails silently on the dedicated server, because the message sends with a default myMessageTypeID (discriminator) of zero.

No no no no no.

You always call registerMessage for both sides. Always. The sending side needs the registration, too, otherwise it would not know the PacketID to use for that IMessage class.

Link to comment
Share on other sites

For interested folks, a summary after further discussion:

 

Recommended to call registerMessage in either Mod.preInit or CommonProxy.preInit.

 

for example

CommonProxy::
preInit() {
           // for handlers which receive the message on the Client side:
  registerMessage(MyClientMessageHandler.class, MyMessage.class, MY_MESSAGE_DISCRIMINATOR_BYTE, Side.CLIENT);      
          // for handlers which receive the message on the Server side:
  registerMessage(MyServerMessageHandler.class, MyMessage.class, MY_MESSAGE_DISCRIMINATOR_BYTE, Side.SERVER);      
}

It's ok to send the same message from client to server and then back from server to client.  Just registerMessage on both Side.CLIENT and Side.SERVER, preferably using two different handler classes. 

 

Link to comment
Share on other sites

  • 2 weeks later...

Ok, so let me make sure I understand this, because I've been struggling on this for a while.

 

Every time that we want to make a new packet we have to make a new class and handler for that packet?

Lets say a "button pressed", for lack of better example.

It would need to be.

 

 

public class ButtonPressed implements IMessage{
     public void fromBytes(ByteBuf buf){
     //CODE
    }

      public void toBytes(ByteBuf buf){
     //CODE
    }

}

 

Does that mean that we have to add another handler since the one in the example implements MyMessage class.

 

The it would be register as such in the main mod class preInit()

network.registerMessage(ButtonPressed.Handler.class, ButtonPressed.class, 3, Side.CLIENT);

 

 

Link to comment
Share on other sites

  • 3 weeks later...

I think I'm using the Simple Network Implementation the wrong way. I want to change a boolean of a TileEntity when I click a button in the GUI. I now have this but I don't think I'm supposed to do it like this.

 

 

package com.ives.supermod.network;

import com.ives.supermod.block.TileEntityConductor;
import cpw.mods.fml.common.network.ByteBufUtils;
import cpw.mods.fml.common.network.simpleimpl.IMessage;
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;
import cpw.mods.fml.common.network.simpleimpl.MessageContext;
import io.netty.buffer.ByteBuf;
import net.minecraft.tileentity.TileEntity;

/**
* Created by Ives on 11/24/2014.
*/
public class ConductorPacket implements IMessage {
    String message;
    int x, y, z;
    int importMode = 0;

    public ConductorPacket() {
    }

    public ConductorPacket(String message, int x, int y, int z, boolean importMode) {
        this.message = message;
        this.x = x;
        this.y = y;
        this.z = z;
        this.importMode = importMode ? 1 : 0;
    }

    @Override
    public void fromBytes(ByteBuf buf) {
        message = ByteBufUtils.readUTF8String(buf);
        x = ByteBufUtils.readVarInt(buf, 5);
        y = ByteBufUtils.readVarInt(buf, 5);
        z = ByteBufUtils.readVarInt(buf, 5);
        importMode = ByteBufUtils.readVarInt(buf, 1);
    }

    @Override
    public void toBytes(ByteBuf buf) {
        ByteBufUtils.writeUTF8String(buf, message);
        ByteBufUtils.writeVarInt(buf, x, 5);
        ByteBufUtils.writeVarInt(buf, y, 5);
        ByteBufUtils.writeVarInt(buf, z, 5);
        ByteBufUtils.writeVarInt(buf, importMode, 1);
    }


    public static class ConductorHandler implements IMessageHandler<ConductorPacket, IMessage> {

        @Override
        public IMessage onMessage(ConductorPacket message, MessageContext ctx) {
            System.out.println("Got packet: "  + message.message);
            TileEntity te = ctx.getServerHandler().playerEntity.getEntityWorld().getTileEntity(message.x, message.y, message.z);
            boolean modeImport;
            if(message.importMode == 1)
                modeImport = true;
            else
                modeImport = false;

            if(te instanceof TileEntityConductor) {
                ((TileEntityConductor) te).setImportMode(modeImport);
            }
            return null;
        }
    }
}

 

Link to comment
Share on other sites

@Ives: That is more or less correct, but you don't need to send the coordinates of the TileEntity, as the server knows which TE the player is interacting with through the player's current Container. That also ensures that they cannot just change the values in any arbitrary TileEntity (prevents possible hacks).

If you have further questions, please open a new Thread.

Link to comment
Share on other sites

  • 3 weeks later...

Well, yes, except that you can use the same message class to communicate from server to client and then from client back to server again.

Well, no, you can't.

What is the reason that you say one cannot use the same message/handler class to communicate both ways? In my experience, you most certainly can - I've even used the exact same IMessageHandler to handle both sides in certain cases when the message acts identically regardless of where it is received (rare, but it can happen), and can also use one handler that just splits handling based on current side (i.e. if world.isRemote handle client stuff, else server).

 

As for registration, I think people are confusing 'sides': you need to register messages somewhere that both client and server will know how you registered it (i.e. NOT in your ClientProxy), BUT you CAN register a message/handler to a single side, e.g. if you are only going to send it to the server, only register it to the Side.SERVER, or register it twice using the same id if you are going to be sending it both directions.

 

Keep in mind that my comments here are based on my experience from using it, not from actual knowledge of SNW's implementation details - if it shouldn't be done this way, can you please explain why? Thanks!

Link to comment
Share on other sites

  • 1 month later...

I tried to implement it, but somehow it doesn't recognize my AddedCyberneticMessage class as proper argument for registerMessage :/

 

Any Idea why?

    public void preInit(FMLPreInitializationEvent e) {
    	proxy.preInit(e);
    	network = NetworkRegistry.INSTANCE.newSimpleChannel("MyChannel");
        network.registerMessage(AddedCyberneticsMessageHandler.class, AddedCyberneticsMessage.class, 0, Side.CLIENT);
    }

 

public class AddedCyberneticsMessageHandler implements IMessageHandler {

@Override
public IMessage onMessage(IMessage message, MessageContext ctx) {
	// TODO Auto-generated method stub
	return null;
}

public static class AddedCyberneticsMessage implements IMessage{

	String addedCybernetic;

	public AddedCyberneticsMessage(){

	}

	public AddedCyberneticsMessage(String cybernetic){

	}

	@Override
	public void fromBytes(ByteBuf buf) {
		// TODO Auto-generated method stub

	}

	@Override
	public void toBytes(ByteBuf buf) {
		// TODO Auto-generated method stub

	}



}

}

Link to comment
Share on other sites

  • 3 months later...

Great tutorial!  I had a question though.

 

I am forced to using a client side only method and it gets position of a block, then destroys it.

On single player it works wonderfully, but on multiplayer it doesn't.  How would I update the variable on the server side to hold the BlockPos.

 

My code that uses the Client Side Only method

 

 

        //when Right clicked

public ItemStack onItemRightClick(ItemStack itemStack, World world, EntityPlayer player)

    {

if(world.isRemote){

lastPos = player.rayTrace(200, 1.0f);

    }

Link to comment
Share on other sites

You can't. Destroying a block must happen on the server, so you cannot use a client-only method. In this case you can just copy the contents of the method.

Not sure what it has to do with this tutorial, if you have more issues, please make a separate topic.

Link to comment
Share on other sites

  • 2 months later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
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.

 Share



  • Recently Browsing

    No registered users viewing this page.

  • Posts

    • where can i get help for 1.7? please
    • 1.7 is no longer supported on this forum. Please update to a modern version of Minecraft to receive support.
    • I am assuming you meant the forge installer log, not sure how to get a previous install log so I ran the installer again. Log below   Launcher Screenshot(s):     Installer Log:    
    • ---- Minecraft Crash Report ---- // Why is it breaking :( Time: 26/10/21 15:07 Description: There was a severe problem during mod loading that has caused the game to fail cpw.mods.fml.common.LoaderException: java.lang.NoClassDefFoundError: net/minecraftforge/fml/common/event/FMLInitializationEvent     at cpw.mods.fml.common.LoadController.transition(LoadController.java:163)     at cpw.mods.fml.common.Loader.loadMods(Loader.java:544)     at cpw.mods.fml.client.FMLClientHandler.beginMinecraftLoading(FMLClientHandler.java:208)     at net.minecraft.client.Minecraft.func_71384_a(Minecraft.java:480)     at net.minecraft.client.Minecraft.func_99999_d(Minecraft.java:878)     at net.minecraft.client.main.Main.main(SourceFile:148)     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)     at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)     at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)     at java.lang.reflect.Method.invoke(Unknown Source)     at net.minecraft.launchwrapper.Launch.launch(Launch.java:135)     at net.minecraft.launchwrapper.Launch.main(Launch.java:28) Caused by: java.lang.NoClassDefFoundError: net/minecraftforge/fml/common/event/FMLInitializationEvent     at java.lang.Class.getDeclaredMethods0(Native Method)     at java.lang.Class.privateGetDeclaredMethods(Unknown Source)     at java.lang.Class.getDeclaredMethods(Unknown Source)     at cpw.mods.fml.common.FMLModContainer.gatherAnnotations(FMLModContainer.java:317)     at cpw.mods.fml.common.FMLModContainer.constructMod(FMLModContainer.java:505)     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)     at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)     at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)     at java.lang.reflect.Method.invoke(Unknown Source)     at com.google.common.eventbus.EventSubscriber.handleEvent(EventSubscriber.java:74)     at com.google.common.eventbus.SynchronizedEventSubscriber.handleEvent(SynchronizedEventSubscriber.java:47)     at com.google.common.eventbus.EventBus.dispatch(EventBus.java:322)     at com.google.common.eventbus.EventBus.dispatchQueuedEvents(EventBus.java:304)     at com.google.common.eventbus.EventBus.post(EventBus.java:275)     at cpw.mods.fml.common.LoadController.sendEventToModContainer(LoadController.java:212)     at cpw.mods.fml.common.LoadController.propogateStateMessage(LoadController.java:190)     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)     at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)     at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)     at java.lang.reflect.Method.invoke(Unknown Source)     at com.google.common.eventbus.EventSubscriber.handleEvent(EventSubscriber.java:74)     at com.google.common.eventbus.SynchronizedEventSubscriber.handleEvent(SynchronizedEventSubscriber.java:47)     at com.google.common.eventbus.EventBus.dispatch(EventBus.java:322)     at com.google.common.eventbus.EventBus.dispatchQueuedEvents(EventBus.java:304)     at com.google.common.eventbus.EventBus.post(EventBus.java:275)     at cpw.mods.fml.common.LoadController.distributeStateMessage(LoadController.java:119)     at cpw.mods.fml.common.Loader.loadMods(Loader.java:513)     ... 10 more Caused by: java.lang.ClassNotFoundException: net.minecraftforge.fml.common.event.FMLInitializationEvent     at net.minecraft.launchwrapper.LaunchClassLoader.findClass(LaunchClassLoader.java:191)     at java.lang.ClassLoader.loadClass(Unknown Source)     at java.lang.ClassLoader.loadClass(Unknown Source)     ... 37 more Caused by: java.lang.NullPointerException     at net.minecraft.launchwrapper.LaunchClassLoader.findClass(LaunchClassLoader.java:182)     ... 39 more A detailed walkthrough of the error, its code path and all known details is as follows: --------------------------------------------------------------------------------------- -- System Details -- Details:     Minecraft Version: 1.7.10     Operating System: Windows 8.1 (amd64) version 6.3     Java Version: 1.8.0_51, Oracle Corporation     Java VM Version: Java HotSpot(TM) 64-Bit Server VM (mixed mode), Oracle Corporation     Memory: 137040984 bytes (130 MB) / 369098752 bytes (352 MB) up to 2147483648 bytes (2048 MB)     JVM Flags: 9 total; -Xverify:none -Xmx2G -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:G1NewSizePercent=20 -XX:G1ReservePercent=20 -XX:MaxGCPauseMillis=50 -XX:G1HeapRegionSize=32M -XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump     AABB Pool Size: 0 (0 bytes; 0 MB) allocated, 0 (0 bytes; 0 MB) used     IntCache: cache: 0, tcache: 0, allocated: 0, tallocated: 0     FML: MCP v9.05 FML v7.10.99.99 Minecraft Forge 10.13.4.1614 Optifine OptiFine_1.7.10_HD_U_E7 11 mods loaded, 11 mods active     States: 'U' = Unloaded 'L' = Loaded 'C' = Constructed 'H' = Pre-initialized 'I' = Initialized 'J' = Post-initialized 'A' = Available 'D' = Disabled 'E' = Errored     UC    mcp{9.05} [Minecraft Coder Pack] (minecraft.jar)      UC    FML{7.10.99.99} [Forge Mod Loader] (forge-1.7.10-10.13.4.1614-1.7.10.jar)      UC    Forge{10.13.4.1614} [Minecraft Forge] (forge-1.7.10-10.13.4.1614-1.7.10.jar)      UC    perspectivemod{3.0} [Perspective Mod 3.0] (minecraft.jar)      UC    PlayerAPI{1.4} [Player API] (minecraft.jar)      UC    clearglassmod{1.0} [Clear Glass Mod] ([1.7.10] Clearglass Mod.jar)      UC    Powns' CheatbreakerHud{1.0} [Powns' CheatbreakerHud] ([1.7.10] Powns CheatbreakerHud - 1.0.jar)      UE    reachdisplaymod{1.0} [Reach Display Mod] ([1.7.10] ReachDisplayMod-1.0.jar)      UC    sidebarmod{2.0} [Sidebar Mod Revamp] ([1.7.10] Sidebar Mod Revamp.jar)      UC    timechanger{1.0} [TimeChanger] ([1.7.10] TimeChanger-1.0.jar)      UC    pownstogglesneak{3.0} [pownstogglesneak] ([1.7.10] ToggleSneak-3.0.jar)      GL info: ' Vendor: 'Intel' Version: '4.0.0 - Build 10.18.10.4276' Renderer: 'Intel(R) HD Graphics 4000'
  • Topics

  • Who's Online (See full list)

×
×
  • Create New...

Important Information

By using this site, you agree to our Privacy Policy.