The asynchronous Services APIs are fully awaitable, Netcode RPCs aren’t.
I wondered, why not? I made a simple test …
My LocalPlayers class spawns multiple players, awaiting for their object to be returned and instantly applying changes to the returned object. This is so convenient:
public override async void OnNetworkSpawn()
{
base.OnNetworkSpawn();
if (!IsOwner)
return;
m_Players[0] = await m_Spawner.Spawn(0, 0);
m_Players[0].transform.position = new Vector3(-3, 0, 0);
m_Players[1] = await m_Spawner.Spawn(1, 1);
m_Players[1].transform.position = new Vector3(-1, 0, 0);
m_Players[2] = await m_Spawner.Spawn(2, 2);
m_Players[2].transform.position = new Vector3(1, 0, 0);
m_Players[3] = await m_Spawner.Spawn(3, 3);
m_Players[3].transform.position = new Vector3(3, 0, 0);
}
Why multiple players? I can haz multiple splitscreen players on a single client.
Client Spawner creates a TaskCompletionSource and calls the Server RPC:
public Task<LocalPlayer> Spawn(Int32 localPlayerIndex, Int32 prefabIndex)
{
m_SpawnTcs[localPlayerIndex] = new TaskCompletionSource<LocalPlayer>();
m_ServerPlayerSpawner.SpawnPlayerServerRpc(OwnerClientId, localPlayerIndex, prefabIndex);
return m_SpawnTcs[localPlayerIndex].Task;
}
Server spawner instantiates, spawns and sends a “did spawn” client RPC:
[Rpc(SendTo.Server, DeferLocal = true)]
internal void SpawnPlayerServerRpc(UInt64 ownerId, Int32 localPlayerIndex, Int32 avatarIndex)
{
avatarIndex = Mathf.Clamp(avatarIndex, 0, m_AvatarPrefabs.Count - 1);
var playerPrefab = m_AvatarPrefabs[avatarIndex];
var playerNetObject = Instantiate(playerPrefab).GetComponent<NetworkObject>();
playerNetObject.SpawnAsPlayerObject(ownerId);
m_ClientPlayerSpawner.DidSpawnPlayerClientRpc(playerNetObject, localPlayerIndex);
}
Back in client spawner, if we’re the owner we set the task result and remove that task source:
[Rpc(SendTo.ClientsAndHost, DeferLocal = true)]
internal void DidSpawnPlayerClientRpc(NetworkObjectReference playerObjectRef, Int32 localPlayerIndex)
{
if (IsOwner)
{
var net = NetworkManager.Singleton;
var player = net.SpawnManager.SpawnedObjects[playerObjectRef.NetworkObjectId];
m_SpawnTcs[localPlayerIndex].SetResult(player.GetComponent<LocalPlayer>());
m_SpawnTcs[localPlayerIndex] = null;
}
}
This also works on WebGL. Lower ones are host’s players, top row are web client players. The mirror effect is entirely occidental.
I mainly wonder if there’s anything speaking against making such ping-pong RPCs awaitable? Maybe garbage?
Because if there’s no big concerns I’d really like to use this pattern.