您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

193 行
6.4 KiB

using System;
using System.Threading.Tasks;
using LobbyRelaySample.lobby;
using Unity.Services.Lobbies.Models;
using UnityEngine;
namespace LobbyRelaySample
{
/// <summary>
/// Keep updated on changes to a joined lobby, at a speed compliant with Lobby's rate limiting.
/// </summary>
public class LobbySynchronizer : IReceiveMessages, IDisposable
{
LocalLobby m_LocalLobby;
LocalPlayer m_LocalUser;
LobbyManager m_LobbyManager;
bool m_LocalChanges = false;
const int
k_approvalMaxMS = 10000; // Used for determining if a user should timeout if they are unable to connect.
int m_lifetime = 0;
const int k_UpdateIntervalMS = 1000;
public LobbySynchronizer(LobbyManager lobbyManager)
{
m_LobbyManager = lobbyManager;
}
public void StartSynch(LocalLobby localLobby, LocalPlayer localUser)
{
m_LocalUser = localUser;
m_LocalLobby = localLobby;
m_LocalLobby.LobbyID.onChanged += OnLobbyIdChanged;
m_LocalChanges = true;
Locator.Get.Messenger.Subscribe(this);
#pragma warning disable 4014
UpdateLoopAsync();
#pragma warning restore 4014
m_lifetime = 0;
}
public void EndSynch()
{
m_LocalChanges = false;
Locator.Get.Messenger.Unsubscribe(this);
if (m_LocalLobby != null)
m_LocalLobby.LobbyID.onChanged -= OnLobbyIdChanged;
m_LocalLobby = null;
}
//TODO Stop players from joining lobby while game is underway.
public void OnReceiveMessage(MessageType type, object msg)
{
// if (type == MessageType.ClientUserSeekingDisapproval)
// {
// bool shouldDisapprove =
// m_LocalLobby.LocalLobbyState !=
// LocalLobbyState.Lobby; // By not refreshing, it's possible to have a lobby in the lobby list UI after its countdown starts and then try joining.
// if (shouldDisapprove)
// (msg as Action<relay.Approval>)?.Invoke(relay.Approval.GameAlreadyStarted);
// }
}
/// <summary>
/// If there have been any data changes since the last update, push them to Lobby. Regardless, pull for the most recent data.
/// (Unless we're already awaiting a query, in which case continue waiting.)
/// </summary>
async Task UpdateLoopAsync()
{
Lobby latestLobby = null;
while (m_LocalLobby != null)
{
latestLobby = await GetLatestRemoteLobby();
if (IfRemoteLobbyChanged(latestLobby))
{
//Pulling remote changes, and applying them to the local lobby usually flags it as changed,
//Causing another pull, the RemoteToLocal converter ensures this does not happen by flagging the lobby.
LobbyConverters.RemoteToLocal(latestLobby, m_LocalLobby, false);
}
if (!LobbyHasHost())
{
LeaveLobbyBecauseNoHost();
break;
}
var areAllusersReady = AreAllUsersReady();
if (areAllusersReady && m_LocalLobby.LocalLobbyState.Value == LobbyState.Lobby)
{
GameManager.Instance.BeginCountdown();
}
else if (!areAllusersReady && m_LocalLobby.LocalLobbyState.Value == LobbyState.CountDown)
{
GameManager.Instance.CancelCountDown();
}
m_lifetime += k_UpdateIntervalMS;
await Task.Delay(k_UpdateIntervalMS);
}
}
async Task<Lobby> GetLatestRemoteLobby()
{
Lobby latestLobby = null;
if (m_LocalLobby.IsLobbyChanged())
{
latestLobby = await PushDataToLobby();
}
else
{
latestLobby = await m_LobbyManager.GetLobbyAsync();
}
return latestLobby;
}
bool IfRemoteLobbyChanged(Lobby remoteLobby)
{
var remoteLobbyTime = remoteLobby.LastUpdated.ToFileTimeUtc();
var localLobbyTime = m_LocalLobby.LastUpdated.Value;
var isLocalOutOfDate = remoteLobbyTime > localLobbyTime;
return isLocalOutOfDate;
}
async Task<Lobby> PushDataToLobby()
{
m_LocalChanges = false;
if (m_LocalUser.IsHost.Value)
await m_LobbyManager.UpdateLobbyDataAsync(
LobbyConverters.LocalToRemoteData(m_LocalLobby));
return await m_LobbyManager.UpdatePlayerDataAsync(
LobbyConverters.LocalToRemoteUserData(m_LocalUser));
}
bool AreAllUsersReady()
{
foreach (var lobbyUser in m_LocalLobby.LocalPlayers.Values)
{
if (lobbyUser.UserStatus.Value != UserStatus.Ready)
{
return false;
}
}
return true;
}
bool LobbyHasHost()
{
if (!m_LocalUser.IsHost.Value)
{
foreach (var lobbyUser in m_LocalLobby.LocalPlayers)
{
if (lobbyUser.Value.IsHost.Value)
return true;
}
return false;
}
return true;
}
void LeaveLobbyBecauseNoHost()
{
Locator.Get.Messenger.OnReceiveMessage(MessageType.DisplayErrorPopup,
"Host left the lobby! Disconnecting...");
Locator.Get.Messenger.OnReceiveMessage(MessageType.EndGame, null);
Locator.Get.Messenger.OnReceiveMessage(MessageType.ChangeMenuState, GameState.JoinMenu);
}
public void OnLobbyIdChanged(string lobbyID)
{
if (string.IsNullOrEmpty(lobbyID)
) // When the player leaves, their LocalLobby is cleared out.
{
EndSynch();
}
}
public void Dispose()
{
EndSynch();
}
}
}