using System;
using System.Collections.Generic;
using System.Linq;
using Unity.Netcode;
using UnityEngine.Events;
namespace LobbyRelaySample.ngo
{
///
/// A place to store data needed by networked behaviors. Each client has an instance so they can retrieve data, but the server's instance stores the actual data.
///
public class NetworkedDataStore : NetworkBehaviour
{
// Using a singleton here since we need spawned PlayerCursors to be able to find it, but we don't need the flexibility offered by the Locator.
public static NetworkedDataStore Instance;
Dictionary m_playerData = new Dictionary();
ulong m_localId;
// Clients will need to retrieve the host's player data since it isn't synchronized. During that process, they will supply these callbacks.
// Since we use RPC calls to retrieve data, these callbacks need to be retained (since the scope of the method that the client calls to request
// data will be left in order to make the server RPC call).
Action m_onGetCurrentCallback;
UnityEvent m_onEachPlayerCallback;
public void Awake()
{
Instance = this;
}
public override void OnDestroy()
{
base.OnDestroy();
if (Instance == this)
Instance = null;
}
public override void OnNetworkSpawn()
{
m_localId = NetworkManager.Singleton.LocalClientId;
}
public void AddPlayer(ulong id, string name)
{
if (!IsServer)
return;
if (!m_playerData.ContainsKey(id))
m_playerData.Add(id, new PlayerData(name, id, 0));
else
m_playerData[id] = new PlayerData(name, id, 0);
}
/// The updated score for the player matching the id after adding the delta, or int.MinValue otherwise.
public int UpdateScore(ulong id, int delta)
{
if (!IsServer)
return int.MinValue;
if (m_playerData.ContainsKey(id))
{
m_playerData[id].score += delta;
return m_playerData[id].score;
}
return int.MinValue;
}
///
/// Retrieve the data for all players in order from 1st to last place, calling onEachPlayer for each.
///
public void GetAllPlayerData(UnityEvent onEachPlayer)
{
m_onEachPlayerCallback = onEachPlayer;
GetAllPlayerData_ServerRpc(m_localId);
}
[ServerRpc(RequireOwnership = false)]
void GetAllPlayerData_ServerRpc(ulong callerId)
{
var sortedData = m_playerData.Select(kvp => kvp.Value).OrderByDescending(data => data.score);
GetAllPlayerData_ClientRpc(callerId, sortedData.ToArray());
}
[ClientRpc]
void GetAllPlayerData_ClientRpc(ulong callerId, PlayerData[] sortedData)
{
if (callerId != m_localId)
return;
int rank = 1;
foreach (var data in sortedData)
{
m_onEachPlayerCallback.Invoke(data);
rank++;
}
m_onEachPlayerCallback = null;
}
///
/// Retreive the data for one player, passing it to the onGet callback.
///
public void GetPlayerData(ulong targetId, Action onGet)
{
m_onGetCurrentCallback = onGet;
GetPlayerData_ServerRpc(targetId, m_localId);
}
[ServerRpc(RequireOwnership = false)]
void GetPlayerData_ServerRpc(ulong id, ulong callerId)
{
if (m_playerData.ContainsKey(id))
GetPlayerData_ClientRpc(callerId, m_playerData[id]);
else
GetPlayerData_ClientRpc(callerId, new PlayerData(null, 0));
}
[ClientRpc]
public void GetPlayerData_ClientRpc(ulong callerId, PlayerData data)
{
if (callerId == m_localId)
{ m_onGetCurrentCallback?.Invoke(data);
m_onGetCurrentCallback = null;
}
}
}
}