How do I send a message server to client with Unet?

I was following this guide: http://docs.unity3d.com/Manual/UNetMessages.html to try to send a message from the server to the client when the client joined the game. So far I have not been able to make it work. All the debug messages for sending show up, so it seems the problem isn’t there. Not a single message from the ReceiveMaze function shows up in the log. Just the error message (at bottom).

Here is my code:

void Start () {

        if (isLocalPlayer) {
            myClient = new NetworkClient();
            Debug.Log("created network client, before register handler");
            myClient.RegisterHandler(MyMsgType.Maze, ReceiveMaze);
            Debug.Log ("registered handler");
            CmdRequestMaze();
            Debug.Log ("finished command");
        }

    }

[Client]
    void ReceiveMaze(NetworkMessage netMsg){
        Debug.Log ("got message");
        MazeMessage msg = netMsg.ReadMessage<MazeMessage>();
        Debug.Log ("read message");
        MazeArray = msg.mazeArray;
        Debug.Log ("setting array");
        Debug.Log (MazeArray.Length);
    }
   

[Command]
    void CmdRequestMaze(){
        Debug.Log ("calling send function");
        SendMaze (MazeArray, Vector2.zero);
        Debug.Log ("after send function");
    }


    public void SendMaze(sbyte[,] maze, Vector2 end)
    {
        Debug.Log ("constructing message");
        MazeMessage msg = new MazeMessage();
        Debug.Log ("constructed message");
        msg.mazeArray = maze;
        msg.end = end;
        Debug.Log ("sending message");
        NetworkServer.SendToAll(MyMsgType.Maze, msg);
        Debug.Log ("sent message");
    }

Then outside this class I have these:

public class MazeMessage : MessageBase
{
    public sbyte[,] mazeArray;
    public Vector2 end;
}

public class MyMsgType {
    public static short Maze = MsgType.Highest + 1;
    //I have tried replacing this with a constant and 
    //it still didn't work. Gave the same error
};

Error code:
Unknown message ID 47 connId:1
UnityEngine.Networking.NetworkIdentity:UNetStaticUpdate()

Another thread suggested that it was because it wasn’t registered with the client, but I did that in the Start() method.

Did you get it work? I’m also trying to learn to send messages over the Network but I fail so far.

People, you need to send the messages to the existing client connections.

In your code:

  • myClient = new NetworkClient();
  • Debug.Log(“created network client, before register handler”);
  • myClient.RegisterHandler(MyMsgType.Maze, ReceiveMaze);

Line 1), is creating a new network connecton that gods only know where is connected.You can use this if your game has 2 players:

if (isLocalPlayer)
{
NetworkClient.allClients[0].RegisterHandler(MyMsgType.Maze, ReceiveMaze);

Index = 0 contains the second player always, if you find the way to get the actual client connection please share. (in short remove the myClient)

Actually I Found It:

NetworkManager.singleton.client.connection.RegisterHandler(....)
//does the trick to get your client connection that is connected to the server :)

Greetings

I also dont understand how to send and register message for server and client.

For the high level Networking code, its really quite simple to send and receive packets.

The Server will register on a known port say 4500 with
NetworkServer.Listen (SERVER_PORT);

And will listen to Message Requests from Clients.
Each Message will have a known structure and will have a Message ID.

Say you have a NetworkInitializationMessage that the Cilent always sends to the Server on Connection. To Hear that message the NetworkServer must “Listen” for it

NetworkServer.RegisterHandler(INIT_MSG_ID, ReceiveNetInitMessage);

Where INIT_MSG_ID is a known number (eg 100) and ReceiveNetInitMessage is the Code to be run when that message is received.

Create a file NetMessages.cs and put all your messages in the same file and that file will exist in your client and server builds.

Messages are really simple. Create the Data Structure that you want to send and then define it as a SubClass of MessageBase (I hope I’m using the correct terminology as I’m not a C# programmer, but see Below). The MessageBase class is just a wrapper for your data.

public const short SERVER_PORT   4500
public const short INIT_MSG_ID   100

public class NetInit : MessageBase
{
public short sNetVersion;
public short sMajorVersion;
public short sMinorVersion;
public short sPatchVersion;
}

In your ServerNetworking have Send/Receive Pairs for handling communications transactions.

public void ReceiveNetInitMsg(NetworkMessage netMsg)
{
NetInit msg = netMsg.ReadMessage<NetInit>(); // Read the Incoming Message Packet.
SendNetInitMsg(netMsg.conn.connectionId, sNetVersion, sMajorVersion, sMinorVersion, sPatchVersion);
}

public void SendNetInitMsg(int nc, string sNetVersion, string sMajorVersion, string sMinorVersion, string sPatchVersion)
{
Net Init msg = new <NetInit>();
msg.sNetVersion = sNetVersion;
msg.sMajorVersion = sMajorVersion;
msg.sMinorVersion = sMinorVersion;
msg.sPatchVersion = sPatchVersion;

NetworkServer.SendToClient (nc, INIT_MSG_ID, msg);

}

CLIENT SIDE…

For the Client to Connect it needs to know the IP Address of the Server (domain name) and the port.

NetworkClient.connect(“test.myproject.org”, SERVER_PORT);
then set up listeners same as the server
NetworkClient.RegisterHandler(INIT_MSG_ID, ReceiveNetInitMessage);

and have a paid of routines for sending and receiving.

SendNetInitMsg()
{
NetInit msg = new <NetInit> ();
NetworkClient.send(INIT_MSG_ID, msg);
}

ReceiveNetIntMsg(NetworkMessage netMsg)
{
NetInit msg = new NetInit (); // Containers for incoming data.

var message = netMsg.ReadMessage<NetInit> ();   // Read the incoming Message

msg.sNetVersion = message.sNetVersion;
msg.sMajorVersion = message.sMajorVersion;
msg.sMinorVersion = message.sMinorVersion;
msg.sPatchVersion = message.sPatchVersion;
}

There is also some standard messages like On Connected

NetworkClient.RegisterHandler(MsgType.Connect, OnConnected);
 
public void OnConnected(NetworkMessage netMsg)
{
Debug.Log ("ClientConnectionis:" + client.isConnected);
SendNetInitMsg ();

}

So in the above…

Client knows the Server Domain/Port and sends a Connection Request.
UNITY handles the connection and once its successful Client->OnConnected is returned
Our OnConnected sends a SendNetInitMessage() to the Server.
As there is already a connection open, we don’t need to worry about passing an address with the packet. Unity just says… Send a Message along this connection. All we need to say is, its Message 100 and its Structure is NetInit.
On the Server we are listening for Message 100’s and when we here one… We call ReceiveNetInitMessage which is passed a copy of the NetworkMessage as a parameter. Another REALLY useful thing for the Server is the networkMessage has the ConnectionID of the sender, so we know WHO we can send a response back to.

In this example, the Send Packet has no values in its datapacket, because the Client is asking the Server “What Version Are You”. The Receive Procedure gets the information it needs and then Sends the Response Packet back to the client “Hey I am Version X” BUT the Receive Procedure has to pass the networkconnection to the Send Response so Send response knows WHO the packet will be sent to.

The Client is listening for Messages of type 100, when it gets a response it calls ReceiveNetInitMsg, which passes a NetworkMessage with datapacket NetInit.

So the above is really good for you to be able to check that both the client and server are using the SAME NetworkMessages file… Cause if Client is talking Version 1 Packets and Server is talking Version 2 Packets… BOOM !

GENERAL INFORMATION

Clients only need 1 connection to talk with a server. But Clients can open connections to multiple servers. Say you want Registration Server, Lobby Server, Play Servers… The Client can open 3 connections 1 to each server.

Servers can have multiple connections open, and the Server will allocate a connectionID to each client (which so far) seems to persist with the connection. So in my example above, Servers can only instigate Message to the Clients if you have kept a record for each Client and their Connection ID. (I know this information is also kept in NetworkServer lists, but its poorly documented and may change so I keep my own structure for it).

In My example above. The Server Receiving Message calls the Server Sending Message, but it doesn’t need to. It could for instance call an ASYNC DB call that when returning calls the Server Sending Message, PROVIDED you send the networking connection ID along for the ride !

It doesn’t appear at the HighLevel calls that you need to serialize / deserialize data, although that may be because I am using simple structures.

Hope that Helps.

Mark

3 Likes

@Arkayn code tags would make it easier to read :slight_smile:

Thanks for the tip… didn’t know how to do it !.. .Updated.

Thanks!

You wrote:

How do I do that “DoSomethig()” call only after Server receive “answer”? I need check “answer” and return in “SendNetInitMsg()” different results depending on it.

P.S. sorry for my English

You have Pairs of Routines.

So the Client will have a Send Message and have a corresponding Receive Message.
You Register the ReceiveMessageCall for each MessageType when you Initialise the Connection.

So your DoSomething(); needs to be placed in the ReceiveMessageHandler so you know the result that the server returns. (See below)

On the Server you will also have a pair of Send and Receive Messages and a Receive Message Handler.

So Client will Send Message and the Server will Receive Message… then do some processing then the Server will Send Message and the Client will Receive Message.

As Long as the Client Send and Server Receive are using the same MessageStructure (eg NetInit below) then you wont have any problems.

Each Message class needs to have a unique Message ID (INIT_MSG_ID) for each Registered Receive Handler

So the Client might send Message INIT_MSG_ID to the Server, but the Server could send back a different message RESULT_ID

public class NetInit : MessageBase  // INIT_MSG_ID 1000
{
public short sNetVersion;
public short sMajorVersion;
public short sMinorVersion;
public short sPatchVersion;
}

public class Result: MessageBase  // RESULT_MSG_ID 1001
{
public int iResult;
}

public NetworkClient client;
public int iServerPort = 4000;
public string sHostName = "localhost";
public const short sNetVersion 12;
public const int INIT_MSG_ID  1000;
public const int RESULT_MSG_ID;

void Awake()
{
Application.RunInBackGround = true;
InitialiseClient();
DontDestroyOnLoad(gameObject);
}

public void InitialiseClient()
{

client = new NetworkClient();
client.Connect(sHostName, iServerPort).

NetworkClient.RegisterHandler(INIT_MSG_ID, ReceiveNetInitMessage);  // CLIENT SETS UP LISTENER FOR RETURN MESSAGE

}

public void SendNetInitMsg()    // CLIENT SENDS THIS TO SERVER
{
  NetInit msg = new NetInit ();
client.Send (MMT.csNetworkInit, msg);
}

public void ReceiveNetInitResult(NetworkMessage netMsg)  // PROCEDURE FOR SERVER SENT PACKET
{
var message = netMsg.ReadMessage<NetInit> ();

DoSomething();

if (sNetVersion != message.sNetVersion) {

Debug.Log ("NeedtoUpdateCLIENT:NETWORKVERSION");

}
}

On the Server Side it is a little trickier as the Server will have multiple clients so you need to keep a record of the Connection ID from any client that you want to send a message to.

When the Server Receives ANY MessagePacket it gets the Client Connection ID in the structure and you use this to respond (or initiate) any correspondence with the client.

netMsg.conn.connectionId (See Below)

// SERVER SIDE

public void SendNetInitMsg(int nc, short sPassword, short iNC, short iResult, short iPlayerID)
{
NetInit msg = new NetInit ();

msg.sNetVersion = 14;
msg.sMajorVersion = 0
msg.sMinorVersion = 1;
msg.sPatchVersion = 4;

NetworkServer.SendToClient (nc, NET_MSG_ID, msg);

}

public void ReceiveNetInitMsg(NetworkMessage netMsg)
{

SendNetInitMsg(netMsg.conn.connectionId, MMT.sNetVersion, MMT.sMajorVersion, MMT.sMinorVersion, MMT.sPatchVersion);

}

Finally just because the Client Sends a Message to the Server does not mean that the Server needs to send a Message Back. I have heaps of logging code where the Client sends stuff to the Server but I dont care if I hear back from the server or not.

Its probably more likely that after your initial connection, you are going to need Server Initiated Messages so on some guaranteed early Message ( eg Login or NetInit) that the client will always send, you record the whatever player identifier you are using (ID, Email etc) and the ConnectionID (and anything else you need) and that way you can always send a packet from the Server to the any player you need to.

Hope that helps

Thank you very much!

Hi,
I want to use xml commands for communication between server and client. The xml commands are like these:
CLIENT SEND:
SERVER SEND:
Do you know how should I use them in the below “MessageBase.cs” file?
Thanks

using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Networking.NetworkSystem;
using System;

public abstract class MessageBase
{
    // De-serialize the contents of the reader into this message
    public virtual void Deserialize(NetworkReader reader) { }

    // Serialize the contents of this message into the writer
    public virtual void Serialize(NetworkWriter writer) { }
}

Your XML really has nothing to do with the networking portion. You read your XML file like any other XML file, and then interpret the text within it to tell your game what to do. In your case you would then send messages as already described above, but there is nothing special about using XML for this.

1 Like

Ok thanks

Hi,

Is there a way to register multiple functions on the client side.

  1. message type : messageType.highest + 1, function:ReceiveHeader
  2. message type : messageType.highest + 2, function:ReceiveData

I tried registering both but messages are going only to the first one. Is it even possible or are we supposed to use one single handle ?