The server RPC runs on the server. Only the server. Therefore, IsServer will be true and the method exits out early.
The conditional is superfluous in a ServerRPC. Only the server runs this, that is guaranteed.
Generally, I advise against using the IsServer/IsClient conditionals as much as possible. Instead, create separate scripts for server and client logic. You can then simply enable or disable them when spawning (this still allows to call RPC methods on them):
I guess probably because in my case i have playerModelPrefab and PlayerPrefab
And i want when players spawning apply playermodelprefab`s mesh to playerPrefab mesh
What I do is to simply Instantiate the mesh into the player prefab instance.
Player spawns an “empty” (no visualization) prefab. Upon spawn there’s also a “Avatar index” sent along, the index refers to a ScriptableObject with a list of valid avatar prefabs. Thus when the player prefab spawns, it simply and only locally instantiates the corresponding PlayerAvater prefab and sets the player prefab as its parent. The PlayerAvatar prefabs have no NetworkObject nor NetworkBehaviour - any networking they may have to do is routed through the player prefab interface scripts.
When changing the “model” I’d simply destroy any existing avatar instance and instantiate the new one.
Traffic-wise, you only need to send an index, and assuming you have less than 256 avatars to choose from (or individual pieces eg head, torso, legs) you may want to send a byte for the index to save some traffic.
using System.Collections.Generic;
using System.Linq;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
public class LobbyManager : NetworkBehaviour
{
[SerializeField] private List<Transform> playerSpawnPointParent;
[SerializeField] private GameObject playerModelPrefab;
[SerializeField] private List<MeshFilter> availableMeshes;
public List<MeshFilter> AvailableMeshes
{
get => availableMeshes;
set => availableMeshes = value;
}
public GameObject PlayerModelPrefab
{
get => playerModelPrefab;
set => playerModelPrefab = value;
}
public Dictionary<ulong, GameObject> InstanceList { get; } = new Dictionary<ulong, GameObject>();
public static LobbyManager Instance;
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
public override void OnNetworkSpawn()
{
if (IsServer)
{
NetworkManager.Singleton.OnClientConnectedCallback += OnPlayerConnected;
SpawnPlayerModelOnPedestal(NetworkManager.Singleton.LocalClientId, 0);
}
}
public void OnHostCreated()
{
if (IsServer)
{
SpawnPlayerModelOnPedestal(NetworkManager.Singleton.LocalClientId, 0);
}
}
public void OnPlayerConnected(ulong clientId)
{
if (!IsServer) return;
int pedestalIndex = InstanceList.Count;
if (pedestalIndex < playerSpawnPointParent.Count)
{
SpawnPlayerModelOnPedestal(clientId, pedestalIndex);
}
else
{
Debug.LogError("Нет доступных пьедесталов для спавна нового игрока.");
}
}
private void SpawnPlayerModelOnPedestal(ulong clientId, int pedestalIndex)
{
Transform spawnPoint = playerSpawnPointParent[pedestalIndex];
if (InstanceList.ContainsKey(clientId))
return;
GameObject modelInstance = Instantiate(playerModelPrefab, spawnPoint.position, spawnPoint.rotation);
InstanceList[clientId] = modelInstance;
NetworkObject networkObject = modelInstance.GetComponent<NetworkObject>();
networkObject.SpawnWithOwnership(clientId);
Debug.Log($"Игрок с clientId {clientId} спавнен на пьедестале {pedestalIndex}.");
}
public override void OnDestroy()
{
base.OnDestroy();
if (NetworkManager.Singleton != null)
{
NetworkManager.Singleton.OnClientConnectedCallback -= OnPlayerConnected;
}
}
}
using Unity.Netcode;
using UnityEngine;
public class PlayerMeshChanger : NetworkBehaviour
{
private MeshFilter playerMeshFilter;
private void Awake()
{
playerMeshFilter = GetComponent<MeshFilter>();
NetworkObject networkObject = GetComponent<NetworkObject>();
if (networkObject == null)
{
Debug.LogError("NetworkObject не найден на объекте!");
}
if (playerMeshFilter == null)
{
Debug.LogError("MeshFilter не найден на объекте!");
}
}
[ServerRpc(RequireOwnership = false)]
public void ChangeMeshServerRpc(int meshIndex)
{
if (!IsServer)
return;
playerMeshFilter.sharedMesh = LobbyManager.Instance.AvailableMeshes[meshIndex].sharedMesh;
ChangeMeshOnClientRpc(meshIndex);
}
[ClientRpc]
private void ChangeMeshOnClientRpc(int meshIndex)
{
if (playerMeshFilter != null)
{
playerMeshFilter.sharedMesh = LobbyManager.Instance.AvailableMeshes[meshIndex].sharedMesh;
}
}
public override void OnNetworkSpawn()
{
base.OnNetworkSpawn();
LobbyManager.Instance.InstanceList[OwnerClientId] = gameObject;
}
}
using Unity.Netcode;
using UnityEngine;
public class PlayerMeshChanger : NetworkBehaviour
{
private MeshFilter playerMeshFilter;
private void Awake()
{
playerMeshFilter = GetComponent<MeshFilter>();
NetworkObject networkObject = GetComponent<NetworkObject>();
if (networkObject == null)
{
Debug.LogError("NetworkObject не найден на объекте!");
}
if (playerMeshFilter == null)
{
Debug.LogError("MeshFilter не найден на объекте!");
}
}
[ServerRpc(RequireOwnership = false)]
public void ChangeMeshServerRpc(int meshIndex)
{
if (!IsServer)
return;
playerMeshFilter.sharedMesh = LobbyManager.Instance.AvailableMeshes[meshIndex].sharedMesh;
ChangeMeshOnClientRpc(meshIndex);
}
[ClientRpc]
private void ChangeMeshOnClientRpc(int meshIndex)
{
if (playerMeshFilter != null)
{
playerMeshFilter.sharedMesh = LobbyManager.Instance.AvailableMeshes[meshIndex].sharedMesh;
}
}
public override void OnNetworkSpawn()
{
base.OnNetworkSpawn();
LobbyManager.Instance.InstanceList[OwnerClientId] = gameObject;
}
}
using System;
using DefaultNamespace;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameManager : NetworkBehaviour
{
[SerializeField] private GameObject playerPrefab;
public static GameManager Instance;
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else if (Instance != this)
{
Destroy(gameObject);
}
}
public void GameStart()
{
if (IsServer)
{
NetworkManager.SceneManager.LoadScene(SceneContainer.Lobby, LoadSceneMode.Single);
NetworkManager.SceneManager.OnLoadComplete += OnSceneLoadComplete;
}
}
private void OnSceneLoadComplete(ulong clientid, string scenename, LoadSceneMode loadscenemode)
{
if (IsServer)
{
SpawnPlayers();
NetworkManager.SceneManager.OnLoadComplete -= OnSceneLoadComplete;
}
}
private void SpawnPlayers()
{
foreach (var client in NetworkManager.ConnectedClientsList)
{
var playerInstance = Instantiate(playerPrefab);
var playerNetworkObject = playerInstance.GetComponent<NetworkObject>();
if (LobbyManager.Instance.InstanceList.TryGetValue(client.ClientId, out var lobbyPlayer))
{
var playerMesh = playerInstance.GetComponent<MeshFilter>();
playerMesh.sharedMesh = lobbyPlayer.GetComponent<MeshFilter>().sharedMesh;
}
playerNetworkObject.SpawnAsPlayerObject(client.ClientId);
}
}
}
And the Game Manager script which managing PlayerPrefab not a ModelPlayerPrefab, this script is spawning my playerPrefab with ModelPlayerPrefab`s mesh, so i want to find a solution how can i make synchronization for meshes for each client
This is my playerMeshChanger that assigned for playerModelPrefab to change their Mesh in Lobby