Trying Sending and Receiving messages (Networking)

Hi everyone, I have a question about networking and I was hoping If you could help me. I´m trying a multiplayer game and it´s working fine, but now I want to “broadcast” a message between the players. Like sending a JSON or simply sending a string. I was checking the documentation but there is not an example about sending and receiving messages.
Currently this is my code for each player instance :

//Some other fields 
@syncField()
    mySyncedValue : number = 1; 

    onEnable(): void {
this.context.connection.beginListen("player-names-changed", (data) => this.mySyncedValue = data)
        
    }

    onDisable(): void {
                this.context.connection.stopListen("player-names-changed", (data) => this.mySyncedValue = data)
    }
  
//Some controller methods 
jump(){
        //console.log(this.grounded)
        if(this.grounded && (this.context.input.isKeyDown("Space") ){
            console.log(this.grounded);
            this._rb?.applyImpulse(new Vector3(0,this.jumpForce,0)); 
      this.context.connection.send("player-names-changed", this.mySyncedValue+1);
            console.log(this.mySyncedValue);
             
        }
    }

I´m trying to send a message (which is incrementing a number) when the player jumps. So if the P1 jumps, then the mySyncedValue varible of the P2 should be 2 and again. But is not changing.

Thank you :slightly_smiling_face:

Original Post on Discord

by user 632418299711324161

Hey :wave: all code that uses that function sends objects instead of direct values such as strings or numbers.

So, maybe you could try that as well?

class Data {
    value: number;

    constructor(value: number) {
         this.value = value;
    }
}

// sending
this.context.connection.send("player-names-changed", new Data(this.mySyncedValue+1));

// recieving
this.context.connection.stopListen("player-names-changed", (data) => this.mySyncedValue = data.value)

Edit: sending just numbers, strings, boolean and other object values is supported.
.
.

Do you want to receive this information on all instances of player? Or on the replicated player on other clients?

If you want to synchronize some room state, you could potentially make a GameManager class with sync fields and hook into PlayerSync to set a referce to itself in all spawned players.

This way all players could call API on the GameManager and that could have simple SyncFields which should make your life much much easier :slight_smile:

Hi :slightly_smiling_face: , thanks for you answer. What do you men whit the replicated player on other clients ? Initially I want to receive this information on all instances of player.

by user 632418299711324161

Srry, I didn´t understand where to set the reference of the GameManager .

by user 632418299711324161

You mean, in the script of each player set a reference of the GameManager ? And the GameManager is attached in the object which has the PlayerSync ?

by user 632418299711324161

Sorry if that wasn’t clear, here are the changes in points:

  • add Game Manager class that has the number value synced via @syncField
  • add a handler to PlayerSync
  • the handler references GameManager that is on a gameobject in the scene
  • handle the player, get the player script and set GameManager variable on the player with the variable on the handler
  • all players now can use GameManager API to potentially adjus the synchronized values.

One question about this code. The receiving code where do I put it ? In the onDisable() ?

by user 632418299711324161

Thank you :slightly_smiling_face:

by user 632418299711324161

no, not onDisable.

The game manager should start listening to the events onEnable and stop onDisable. That’s because if you would want to destroy the gameobject (game manager) or load another scene, without the stopListening call, it could cause errors.

It’s the exact same as subscribing to OnRoomJoined and other events.


In this context, you don’t need any send or listen subscribes, because the suggestion is to use syncFields, which only have the onValueChange method. (see samples or documentation).

Let me know again if anything wouldn’t be clear :+1: :cactus:

@DiegoEloko you’ll also find examples for this in our samples when you download the package Needle Engine Samples

I´m following this example. How do I set GameManager variable on the player with the variable on the handler if in PlayerSync the Player is a prefab ?
Look. This is my set up :

  1. My GameManager object with a GameManagerNetworking script
export class GameManagerNetwork extends Behaviour{

    @syncField()
    num : number = 0; 

    update(): void {
        console.log(this.num);
        
    }

}

by user 632418299711324161

  1. A handler for PlayerSync with a reference to the object with the GameManagerNetwork :
export class GameManagerNetworkHandler extends Behaviour{

    @serializable(GameObject)
    gameManager !: GameObject;

    public gameManagerLink !: GameManagerNetwork  | null;


    start(): void {
        if(this.gameManager){
            this.gameManagerLink = this.gameManager.getComponent(GameManagerNetwork); 
            console.log("Game Manger Connected");
            
        }
    }

}

by user 632418299711324161

About this step : handle the player, get the player script and set GameManager variable on the player with the variable on the handler
I understand that, I have to add a line @serialiazable(GameObject) gameManager !: GameManager but how do I find that object from my Player Prefabs since PlayerSync?

by user 632418299711324161

Refer to the SpawnHandler.ts AND SpawnHandler.cs in the FirstPersonController sample (released) to understand how to handle a spawned instanced by PlayerSync.

Also, you can reference the GameManagerNetwork directly like so:

    @serializable(GameManagerNetwork )
    gameManagerNetwork !: GameManagerNetwork ;

Also, update to the latest engine, the clallback has changed so it’s simpler to interact with it.

You should see that in the inspector it is accepting GameObject handlers.
image.png

So, you just need to declare a method that accepts GameObject and hook that in the inspector. No need for editing the .cs counterpart or to mark your handler as nonSerialized.

Hi again :grin: your comments were too useful, thanks for the help. Now , I’m trying to send some data when another player enter or left the room. I’m trying the enter player → send data first. This is my current code and the problem is that when the second player enters, the secondPlayer.localNumeber should be 2 but it doesn’t change. Hope you can help :slightly_smiling_face:

class Data{
  
  num : number; 
  constructor(num : number){
    this.num = num;
  }
}

export class MultiplayerScript extends Behavior{
  
  localNumber !: number; 
  net !: NetworkConnection; 
  firstPlayer !: boolean;
  start(){
    
    this.net = this.context.connection;
    if(this.net.usersInRoom().length == 1){
        this.firstPlayer=true;
    }  
    if(this.firstPlayer){
       this.localNumber = 1;
    }
    setNetworkEvents();
  }
  
  someoneConnects(){
    this.net.send("player-spawn", new Data(this.localNumber+1));
  }

  setNetworkEvents(){
    
    this.net.beginListen(RoomEvents.UserJoinedRoom, this.someoneConnects);

    this.net.beginListen("player-spawn", (data) => {
      this.localNumber = data.num; 
    }
  }
  onDisable(){

    this.net.stopListen(RoomEvents.UserJoinedRoom, this.someoneConnects);

    this.net.stopListen("player-spawn", (data) => {
      this.localNumber = data.num; 
    }
  }
}

by user 632418299711324161

Change “this.net.beginListen(…, this.someoneConnects);” to
this.net.beginListen(…, this.someoneConnects.bind(this))"
To make it work

You should read this to learn what it means and how to properly unsubscribe in onDisable: Typescript Essentials | Needle Engine Documentation