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