RPC calls not working

So I have made a Host-client network model for my game where the host will also act as a player.
Within the lobby system I am using RPCs to send a message to the host to update everyone’s member list on the network.

However, the server RPC for requesting a new member list is not being called at all.

Here’s my script

Calling the RPC (it gets called near the bottom)


This script is a callback using the Facepunch steamworks callback system. And Yes, the callback IS being called fine and so is the function. It’s just not being called on the host, instead on the client.

Am I missing some components? Or is there anything else causing this issue

I am more than happy to send extra information, just ask!

Please post code as text and put it in code tags. Images are not searchable or quotable and often hard to read.

Do you get any logs in the console?
What type is “Lobby”? Does it implement INetworkSerializable? (I assume Netcode for GameObjects)

Note that you can only send RPCs after StartHost/StartClient, and in each script only in or after OnNetworkSpawn.

Thanks for the reply, Lobby is for Facepunch Transport but shouldn’t be related to my issue I don’t think.

I have two scripts. One is called GameNetworkManager which handles all the networking stuff and MainMenuScript which handles the main menu. I will write them both below

All I need to do is, send a ServerRPC from a newly joined client to the host and request an update to the member list. Then the ServerRPC should send a clientRPC to all clients which will actually visually update the member lists for everyone (MainMenuScript)

GameNetworkManager:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using Steamworks;
using TMPro;
using Unity.Netcode;
using Unity.Netcode.Transports.UTP;
using Unity.Services.Authentication;
using Unity.Services.Core;
using UnityEngine;
using UnityEngine.Networking;
using Netcode.Transports.Facepunch;
using Steamworks.Data;

public class GameNetworkManager : MonoBehaviour {

[SerializeField] private MainMenuScript mainMenuScript;
[SerializeField] private DefaultData defaults;
[SerializeField] private Transform CreateOptions;
[SerializeField] private UtilityHandler utilities;
[SerializeField] private GameObject playerPrefab;

private NetworkManager network;
private FacepunchTransport transport;
private Dictionary<SteamId, Dictionary<string, object>> memberData = new Dictionary<SteamId, Dictionary<string, object>>();

public Lobby CurrentLobby;
public bool InLobby = false;
public bool Host = false;


#region LOBBIES

//START

async void Start() {
DontDestroyOnLoad(gameObject); //make sure this script stays forever

// try {
// SteamClient.Init(2721690, asyncCallbacks: true);
// } catch (Exception e) {
// Debug.Log("ERROR INITIALISING STEAM :: " + e);
// }

await UnityServices.InitializeAsync(); //intialising unity services

//SignInWithSteam();

//manage callbacks

SteamMatchmaking.OnLobbyCreated += LobbyJustCreated;
SteamMatchmaking.OnLobbyEntered += LobbyJustJoined;

//other startup variables

network = GetComponent<NetworkManager>();
transport = GetComponent<FacepunchTransport>();
}

//TESTING

private void Update() {
if (Input.GetKeyDown(KeyCode.F)) {
RequestMemberListUpdateServerRpc(CurrentLobby);
}
}

/// <summary>
/// Check if the user has been authenticated through steam
/// </summary>
/// <returns></returns>

public bool IsAuthenticated() {
if (network) {
return true;
} else {
Debug.Log("Cannot create lobby. Not authenticated yet.");
return false;
}
}

/// <summary>
/// Handles creating and hosting a lobby
/// </summary>
/// <param name="lobbyName">The custom name of the lobby</param>

public async void CreateLobby(string lobbyTypePassed) {
if (IsAuthenticated()) {
try {
//CREATE LOBBY

mainMenuScript.SwitchPage("Loading"); //show loading screen
await SteamMatchmaking.CreateLobbyAsync(5);

} catch (Exception e) {
Debug.Log("ERROR CREATING LOBBY :: " + e);
}
}
}

/// <summary>
/// Handles joining lobbies
/// </summary>
/// <param name="code">The lobby code used to identify said lobby</param>

public async void JoinLobby(SteamId lobbyId) {
try {
mainMenuScript.SwitchPage("Loading"); //show loading screen
await SteamMatchmaking.JoinLobbyAsync(lobbyId);
} catch (Exception e) {
Debug.Log("ERROR JOINING LOBBY :: " + e);
}
}

/// <summary>
/// Find a new list of the updated current lobbies available, then will update them on UI
/// </summary>

public async void GetLobbyList() {
try {
Lobby[] lobbies = await SteamMatchmaking.LobbyList.FilterDistanceFar().WithSlotsAvailable(1).RequestAsync();

Debug.Log(lobbies.Length);

mainMenuScript.UpdateLobbies(lobbies);

} catch (Exception e) {
Debug.Log("ERROR GETTING LOBBIES :: " + e);
}
}


//CALLBACK FOR WHEN THE PLAYER HAS JUST CREATED A LOBBY

private void LobbyJustCreated(Result result, Lobby lobby) {
string StoreName = defaults.GetRandomLobbyName();
string NameWithoutSpaces = CreateOptions.Find("ServerName/Value/Text Area/Text").GetComponent<TextMeshProUGUI>().text.Replace(" ", "");

if (NameWithoutSpaces.Length > 1) { //Check if server name is empty (a length of 1 means its empty and idk why)
Debug.Log("HAS CUSTOM NAME :: '" + NameWithoutSpaces + "'");
StoreName = CreateOptions.Find("ServerName/Value/Text Area/Text").GetComponent<TextMeshProUGUI>().text;
}

//ADDING DEFAULT LOBBY DATA FROM "DefaultData" SCRIPT

foreach (KeyValuePair<string, object> data in defaults.GetDefaultLobbyData()) {
lobby.SetData(data.Key, data.Value.ToString());
}

//ADDING ALL SETTINGS FROM CREATE PAGE

foreach (Transform setting in CreateOptions) {
object value = utilities.GettingValueFromSetting(setting);
lobby.SetData(setting.name, value.ToString());
}
}

//CALLBACK FOR WHEN THE PLAYER JOINS A LOBBY

private void LobbyJustJoined(Lobby lobby) {

//Decide if player is HOST or CLIENT

try {
if (lobby.Owner.Id == SteamClient.SteamId) { //IS HOST
Debug.Log("Is host");
CurrentLobby = lobby;
Host = false;
InLobby = true;

CreateNetworkHost(lobby.Owner.Id);
}
else { //IS CLIENT
Debug.Log("Is client");
CurrentLobby = lobby;
Host = true;
InLobby = true;

CreateNetworkClient(lobby.Owner.Id);
}
} catch (Exception e) {
Debug.Log(e);
}

//Open lobby page

RequestMemberListUpdateServerRpc(lobby);
mainMenuScript.SwitchPage("Lobby");
}

[ServerRpc (RequireOwnership = false)]
public void AddMemberDataServerRpc(SteamId steamId, string key, object newValue) {
if (memberData.ContainsKey(steamId)) { //if member is in MemberList
if (memberData[steamId].ContainsKey(key)) { //if the member already has this key
memberData[steamId][key] = newValue;
} else {
memberData[steamId].Add(key, newValue);
}
}
}

[ServerRpc (RequireOwnership = false)]
private void RequestMemberListUpdateServerRpc(Lobby lobby) {
Debug.Log("Member List Requested! Member count: " + lobby.MemberCount);
mainMenuScript.UpdateLobbyMemberClientRpc("All", lobby);
}

#endregion

#region NETWORKING

private void CreateNetworkClient(SteamId targetSteamId) {
transport.targetSteamId = targetSteamId;
network.StartClient();
}

private void CreateNetworkHost(SteamId targetSteamId) {
transport.targetSteamId = targetSteamId;
network.StartHost();
}

#endregion
}

MainMenuScript (Only the RPC part)

[ClientRpc (RequireOwnership = false)]
    public void UpdateLobbyMemberClientRpc(string ChangeToMake, Lobby lobby) {
        Transform memberList = LobbyPage.Find("PlayerList/Content");

        Debug.Log("Updating member list")

        if (ChangeToMake == "All") {
            foreach (Friend member in lobby.Members) {
                if (!memberList.Find(member.Name)) {
                    Transform newLobbyPlayer = Instantiate(LobbyPlayerPrefab);
                    newLobbyPlayer.name = member.Name;
                    newLobbyPlayer.Find("PlayerName").GetComponent<TextMeshProUGUI>().text = member.Name;
                    newLobbyPlayer.Find("Rank").GetComponent<TextMeshProUGUI>().text = "Rank " + lobby.GetMemberData(member, "Rank");

                    //Kick button
                   
                    if (lobby.Owner.Id == SteamClient.SteamId) {
                        newLobbyPlayer.Find("Kick").gameObject.SetActive(true);
                    } else {
                        newLobbyPlayer.Find("Kick").gameObject.SetActive(false);                       
                    }

                    //Parenting

                    newLobbyPlayer.SetParent(memberList, false);
                }
            }
        }
    }

In MainMenuScript, the RPC procedure doesn’t Log the “Updating member list” on all clients. Only on the client that called the RPC.

Hmmm I would expect a Lobby service to handle this for you. Is this perhaps already built-in (eg shared lobby data)?

Anyway, to receive RPC methods, the script must be of type NetworkBehaviour. At least your Manager is a MonoBehaviour and will not receive RPCs.

Also note that NGO v1.8 has the more versatile [Rpc] attribute. Afaik this allows you to send to all clients directly.

So I’ve made both NetworkGameManager and MainMenuScript a NetworkBehaviour.


Now I receive this error, and I have no clue what it means or how to fix it…
May I have some help with this? Also could you explain what’s happening here so I can learn a little.

Please post text as text, not images! This is hard to read and neither searchable nor quotable.

This error is what I expected to occur when I asked about the Lobby type. RPCs cannot send object references, only value types, and any value type that is not built-in needs custom serialization. For NGO this means implementing the INetworkSerializable interface.

Could you send me maybe a snippet or link to code which shows how to structure this properly? I am struggling to find out how to structure it correctly.