您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
193 行
6.4 KiB
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();
|
|
}
|
|
}
|
|
}
|