浏览代码

Switching over to the RelayUnityTransport component, although I'm still using the other Relay allocation to send the NGO relay code so that needs to change. Also note that consecutive games in one lobby fail now, for using the wrong relay code.

/main/staging/ngo_minigame_cleanup
nathaniel.buck@unity3d.com 3 年前
当前提交
119e8e33
共有 12 个文件被更改,包括 182 次插入35 次删除
  1. 3
      Assets/Prefabs/GameManager.prefab
  2. 2
      Assets/Prefabs/InGame/InGameLogic.prefab
  3. 13
      Assets/Scripts/Game/InGame/InGameRunner.cs
  4. 26
      Assets/Scripts/Game/InGame/SetupInGame.cs
  5. 48
      Assets/Scripts/Game/InGame/SymbolContainer.cs
  6. 13
      Assets/Scripts/Game/LocalLobby.cs
  7. 1
      Assets/Scripts/Infrastructure/Messenger.cs
  8. 1
      Assets/Scripts/Lobby/LobbyContentHeartbeat.cs
  9. 1
      Assets/Scripts/Lobby/ToLocalLobby.cs
  10. 8
      Assets/Scripts/Relay/RelayUtpClient.cs
  11. 5
      Assets/Scripts/Relay/RelayUtpHost.cs
  12. 96
      Assets/Scripts/Relay/RelayUtpSetup.cs

3
Assets/Prefabs/GameManager.prefab


m_Name:
m_EditorClassIdentifier:
m_prefabNetworkManager: {fileID: 238192747445020667, guid: 73173c97c128d614aa2a1167a2eaea68, type: 3}
m_lobbyObserver: {fileID: 5235782363599194820}
m_disableWhileInGame: []
--- !u!114 &5235782363599194820
MonoBehaviour:

m_Calls:
- m_Target: {fileID: 6265861362966661484}
m_TargetAssemblyTypeName: LobbyRelaySample.inGame.SetupInGame, LobbyRelaySample
m_MethodName: SetRelayAddress
m_MethodName: OnLobbyChange
m_Mode: 0
m_Arguments:
m_ObjectArgument: {fileID: 0}

2
Assets/Prefabs/InGame/InGameLogic.prefab


m_Script: {fileID: 11500000, guid: 6960e84d07fb87f47956e7a81d71c4e6, type: 3}
m_Name:
m_EditorClassIdentifier:
m_ProtocolType: 0
m_ProtocolType: 1
m_MaximumPacketSize: 1400
m_MaxPacketQueueSize: 128
m_SendQueueBatchSize: 6144

13
Assets/Scripts/Game/InGame/InGameRunner.cs


private ulong m_localId; // This is not necessarily the same as the OwnerClientId, since all clients will see all spawned objects regardless of ownership.
private float m_timeout = 10;
public void Initialize(Action onConnectionVerified, int expectedPlayerCount, Action onGameEnd)
{
m_onConnectionVerified = onConnectionVerified;

if (clientId == m_localId)
m_onConnectionVerified?.Invoke();
if (shouldStartImmediately)
Locator.Get.Messenger.OnReceiveMessage(MessageType.GameBeginning, null);
{
m_timeout = -1;
Locator.Get.Messenger.OnReceiveMessage(MessageType.GameBeginning, null); // TODO: Might need to delay this a frame to ensure the client finished initializing (since I sometimes hit the "failed to join" message even when joining).
}
if (m_timeout >= 0)
{
m_timeout -= Time.deltaTime;
if (m_timeout < 0)
Locator.Get.Messenger.OnReceiveMessage(MessageType.GameBeginning, null);
}
// TODO: BSP for choosing symbol spawn positions?
// TODO: Remove the timer to test for packet loss.

26
Assets/Scripts/Game/InGame/SetupInGame.cs


public class SetupInGame : MonoBehaviour, IReceiveMessages
{
[SerializeField] private GameObject m_prefabNetworkManager = default;
[SerializeField] private LocalLobbyObserver m_lobbyObserver = default;
private int m_playerCount; // The server will need to know this.
private bool m_isHost;
private bool m_doesNeedCleanup = false;

private ServerAddress m_serverAddress;
private LocalLobby m_lobby;
/*

public void Start()
{
Locator.Get.Messenger.Subscribe(this);
m_lobbyObserver.OnObservedUpdated.AddListener(UpdatePlayerCount);
m_lobbyObserver.OnObservedUpdated.RemoveListener(UpdatePlayerCount);
}
private void SetMenuVisibility(bool areVisible)

m_inGameManagerObj = GameObject.Instantiate(m_prefabNetworkManager);
m_networkManager = m_inGameManagerObj.GetComponentInChildren<NetworkManager>();
m_inGameRunner = m_inGameManagerObj.GetComponentInChildren<InGameRunner>();
m_inGameRunner.Initialize(OnConnectionVerified, m_playerCount, OnGameEnd);
m_inGameRunner.Initialize(OnConnectionVerified, m_lobby.PlayerCount, OnGameEnd);
// TODO: I'll need this when we switch to the Relay Unity Transport option.
//UnityTransport transport = m_inGameManagerObj.GetComponentInChildren<UnityTransport>();
//transport.SetConnectionData(m_serverAddress.IP, (ushort)m_serverAddress.Port);
//m_initializeTransport(transport);
UnityTransport transport = m_inGameManagerObj.GetComponentInChildren<UnityTransport>();
m_networkManager.StartHost();
m_inGameManagerObj.AddComponent<relay.RelayUtpNGOSetupHost>().Initialize(this, m_lobby, () => { m_initializeTransport(transport); m_networkManager.StartHost(); });
m_networkManager.StartClient();
m_inGameManagerObj.AddComponent<relay.RelayUtpNGOSetupClient>().Initialize(this, m_lobby, () => { m_initializeTransport(transport); m_networkManager.StartClient(); });
private void UpdatePlayerCount(LocalLobby lobby)
{ m_playerCount = lobby.PlayerCount;
}
private void OnConnectionVerified()
{

public void SetRelayAddress(LocalLobby changed)
public void OnLobbyChange(LocalLobby lobby)
m_serverAddress = changed.RelayServer; // Note that this could be null.
m_lobby = lobby; // Most of the time this is redundant, but we need to get multiple members of the lobby to the Relay setup components, so might as well just hold onto the whole thing.
public void OnLocalUserChange(LobbyUser user)
{
m_isHost = user.IsHost;

Locator.Get.Messenger.OnReceiveMessage(MessageType.DisplayErrorPopup, "Failed to join the game.");
// TODO: Need to handle both failing to connect and connecting but failing to initialize.
// I.e. cleaning up networked objects *might* be necessary.
OnGameEnd(); // TODO: This returns to the lobby. I think that's desirable?
}
}

48
Assets/Scripts/Game/InGame/SymbolContainer.cs


namespace LobbyRelaySample.inGame
{
[RequireComponent(typeof(Rigidbody))]
public class SymbolContainer : NetworkBehaviour
public class SymbolContainer : NetworkBehaviour, IReceiveMessages
private Rigidbody m_rb;
private bool m_isConnected = false;
private bool m_hasGameStarted = false;
private void OnGameStarted()
{
m_hasGameStarted = true;
if (m_isConnected)
BeginMotion();
}
if (IsHost)
GetComponent<NetworkObject>().Spawn();
if (!IsHost)
{ this.enabled = false; // Just disabling this script.
return;
}
m_rb = this.GetComponent<Rigidbody>();
Locator.Get.Messenger.Subscribe(this);
GetComponent<NetworkObject>().Spawn();
}
public override void OnNetworkSpawn()

// Note: The SymbolObjects, which will be children of this object, need their NetworkTransforms to have IsLocalSpace set to true. Otherwise, they might get desynced.
// (This would manifest as packet loss errors.)
// Also note: The initial position of the SymbolObject prefab is set to be outside the camera view in the Z-direction, so that it doesn't interpolate past the actual
// position when it spawns on a client (as opposed to in the Y-direction, since this SymbolContainer is also moving downward).
// TODO: Does that matter if we delay for the instructions?
Rigidbody m_rb = this.GetComponent<Rigidbody>();
m_isConnected = true;
m_rb.velocity = Vector3.down;
if (m_hasGameStarted)
BeginMotion();
}
}
private void BeginMotion()
{
// Note: The SymbolObjects, which will be children of this object, need their NetworkTransforms to have IsLocalSpace set to true. Otherwise, they might get desynced.
// (This would manifest as packet loss errors.)
// Also note: The initial position of the SymbolObject prefab is set to be outside the camera view in the Z-direction, so that it doesn't interpolate past the actual
// position when it spawns on a client (as opposed to in the Y-direction, since this SymbolContainer is also moving downward).
m_rb.velocity = Vector3.down;
}
public void OnReceiveMessage(MessageType type, object msg)
{
if (type == MessageType.GameBeginning)
{ Locator.Get.Messenger.Unsubscribe(this);
OnGameStarted();
}
}
}

13
Assets/Scripts/Game/LocalLobby.cs


public string LobbyID { get; set; }
public string LobbyCode { get; set; }
public string RelayCode { get; set; }
public string RelayNGOCode { get; set; }
public string LobbyName { get; set; }
public bool Private { get; set; }
public int MaxPlayerCount { get; set; }

LobbyID = existing.LobbyID;
LobbyCode = existing.LobbyCode;
RelayCode = existing.RelayCode;
RelayNGOCode = existing.RelayNGOCode;
LobbyName = existing.LobbyName;
Private = existing.Private;
MaxPlayerCount = existing.MaxPlayerCount;

LobbyID = null;
LobbyCode = lobbyCode;
RelayCode = null;
RelayNGOCode = null;
LobbyName = null;
Private = false;
MaxPlayerCount = -1;

set
{
m_data.RelayCode = value;
OnChanged(this);
}
}
public string RelayNGOCode
{
get => m_data.RelayNGOCode;
set
{
m_data.RelayNGOCode = value;
OnChanged(this);
}
}

1
Assets/Scripts/Infrastructure/Messenger.cs


CancelCountdown = 201,
CompleteCountdown = 202,
GameBeginning = 203,
NGORelayCode = 204,
DisplayErrorPopup = 300,
}

1
Assets/Scripts/Lobby/LobbyContentHeartbeat.cs


{
Dictionary<string, string> data = new Dictionary<string, string>();
data.Add("RelayCode", lobby.RelayCode);
data.Add("RelayNGOCode", lobby.RelayNGOCode);
data.Add("State", ((int)lobby.State).ToString()); // Using an int is smaller than using the enum state's name.
data.Add("Color", ((int)lobby.Color).ToString());
data.Add("State_LastEdit", lobby.Data.State_LastEdit.ToString());

1
Assets/Scripts/Lobby/ToLocalLobby.cs


LobbyName = lobby.Name,
MaxPlayerCount = lobby.MaxPlayers,
RelayCode = lobby.Data?.ContainsKey("RelayCode") == true ? lobby.Data["RelayCode"].Value : null, // By providing RelayCode through the lobby data with Member visibility, we ensure a client is connected to the lobby before they could attempt a relay connection, preventing timing issues between them.
RelayNGOCode = lobby.Data?.ContainsKey("RelayNGOCode") == true ? lobby.Data["RelayNGOCode"].Value : null,
State = lobby.Data?.ContainsKey("State") == true ? (LobbyState) int.Parse(lobby.Data["State"].Value) : LobbyState.Lobby,
Color = lobby.Data?.ContainsKey("Color") == true ? (LobbyColor) int.Parse(lobby.Data["Color"].Value) : LobbyColor.None,
State_LastEdit = lobby.Data?.ContainsKey("State_LastEdit") == true ? long.Parse(lobby.Data["State_LastEdit"].Value) : 0,

8
Assets/Scripts/Relay/RelayUtpClient.cs


protected bool m_hasSentInitialMessage = false;
private const float k_heartbeatPeriod = 5;
protected enum MsgType { Ping = 0, NewPlayer, PlayerApprovalState, ReadyState, PlayerName, Emote, StartCountdown, CancelCountdown, ConfirmInGame, EndInGame, PlayerDisconnect }
protected enum MsgType { Ping = 0, NewPlayer, PlayerApprovalState, ReadyState, PlayerName, Emote, StartCountdown, CancelCountdown, ConfirmInGame, EndInGame, PlayerDisconnect, NGORelayCode }
public virtual void Initialize(NetworkDriver networkDriver, List<NetworkConnection> connections, LobbyUser localUser, LocalLobby localLobby)
{

{
UserStatus status = (UserStatus)msgContents[0];
m_localLobby.LobbyUsers[id].UserStatus = status;
}
else if (msgType == MsgType.NGORelayCode)
{
int codeLength = msgContents[0];
string code = System.Text.Encoding.UTF8.GetString(msgContents.GetRange(1, codeLength).ToArray());
m_localLobby.RelayNGOCode = code;
}
else if (msgType == MsgType.StartCountdown)
Locator.Get.Messenger.OnReceiveMessage(MessageType.StartCountdown, null);

5
Assets/Scripts/Relay/RelayUtpHost.cs


foreach (NetworkConnection connection in m_connections)
WriteByte(m_networkDriver, connection, m_localUser.ID, MsgType.EndInGame, 0);
}
else if (type == MessageType.NGORelayCode)
{
foreach (NetworkConnection connection in m_connections)
WriteString(m_networkDriver, connection, m_localUser.ID, MsgType.NGORelayCode, (string)msg);
}
}
private void CheckIfAllUsersReady()

96
Assets/Scripts/Relay/RelayUtpSetup.cs


protected LobbyUser m_localUser;
protected Action<bool, RelayUtpClient> m_onJoinComplete;
protected static string AddressFromEndpoint(NetworkEndPoint endpoint)
{
return endpoint.Address.Split(':')[0];
}
public void BeginRelayJoin(LocalLobby localLobby, LobbyUser localUser, Action<bool, RelayUtpClient> onJoinComplete)
{
m_localLobby = localLobby;

private void OnRelayCode(string relayCode)
{
m_localLobby.RelayCode = relayCode;
m_localLobby.RelayServer = new ServerAddress(m_endpointForServer.Address.Split(':')[0], m_endpointForServer.Port);
m_localLobby.RelayServer = new ServerAddress(AddressFromEndpoint(m_endpointForServer), m_endpointForServer.Port);
m_joinState |= JoinState.Joined;
CheckForComplete();
}

bool isSecure = false;
m_endpointForServer = GetEndpointForAllocation(joinAllocation.ServerEndpoints, joinAllocation.RelayServer.IpV4, joinAllocation.RelayServer.Port, out isSecure);
BindToAllocation(m_endpointForServer, joinAllocation.AllocationIdBytes, joinAllocation.ConnectionData, joinAllocation.HostConnectionData, joinAllocation.Key, 1, isSecure);
m_localLobby.RelayServer = new ServerAddress(m_endpointForServer.Address.Split(':')[0], m_endpointForServer.Port);
m_localLobby.RelayServer = new ServerAddress(AddressFromEndpoint(m_endpointForServer), m_endpointForServer.Port);
}
protected override void OnBindingComplete()

LobbyAsyncRequests.Instance.UpdatePlayerRelayInfoAsync(m_allocation.AllocationId.ToString(), m_localLobby.RelayCode, null);
}
}
}
// TODO: These don't need to be children of RelayUtpSetup, right? Since the binding step isn't used
/*
* When using the Relay adapter for UTP to connect the NetworkManager for Netcode for GameObjects (NGO), we need to provide the Allocation info without manually binding to it.
* In actual use, if you are using NGO for your game's networking, you would not also use the RelayUtpSetupHost/RelayUtpSetupClient at all, since their direct data transmission would be unnecessary.
* We keep both versions for this sample to demonstrate how each is set up, whether you want to just use Lobby + Relay or use NGO as well.
*/
/// <summary>
/// Host logic: Request a new Allocation, and then pass its info to the UTP adapter for NGO.
/// </summary>
public class RelayUtpNGOSetupHost : RelayUtpSetup
{
private inGame.SetupInGame m_setupInGame;
private Action m_onJoin;
public void Initialize(inGame.SetupInGame setupInGame, LocalLobby lobby, Action onJoin)
{
m_setupInGame = setupInGame;
m_localLobby = lobby;
m_onJoin = onJoin;
JoinRelay();
}
protected override void JoinRelay()
{
RelayAPIInterface.AllocateAsync(m_localLobby.MaxPlayerCount, OnAllocation);
}
private void OnAllocation(Allocation allocation)
{
RelayAPIInterface.GetJoinCodeAsync(allocation.AllocationId, OnRelayCode);
bool isSecure = false;
m_endpointForServer = GetEndpointForAllocation(allocation.ServerEndpoints, allocation.RelayServer.IpV4, allocation.RelayServer.Port, out isSecure);
m_setupInGame.SetRelayServerData(AddressFromEndpoint(m_endpointForServer), m_endpointForServer.Port, allocation.AllocationIdBytes, allocation.Key, allocation.ConnectionData, allocation.ConnectionData, isSecure);
m_onJoin?.Invoke();
}
private void OnRelayCode(string relayCode)
{
Locator.Get.Messenger.OnReceiveMessage(MessageType.NGORelayCode, relayCode);
m_localLobby.RelayNGOCode = relayCode;
}
protected override void OnBindingComplete() { /*No-op*/ }
}
public class RelayUtpNGOSetupClient : RelayUtpSetup
{
private inGame.SetupInGame m_setupInGame;
private Action m_onJoin;
public void Initialize(inGame.SetupInGame setupInGame, LocalLobby lobby, Action onJoin)
{
m_setupInGame = setupInGame;
m_localLobby = lobby;
m_onJoin = onJoin;
JoinRelay();
}
protected override void JoinRelay()
{
m_localLobby.onChanged += OnLobbyChange; // need to unregister to be destroyed
}
private void OnLobbyChange(LocalLobby lobby)
{
if (m_localLobby.RelayNGOCode != null)
{
RelayAPIInterface.JoinAsync(m_localLobby.RelayNGOCode, OnJoin);
m_localLobby.onChanged -= OnLobbyChange;
}
}
private void OnJoin(JoinAllocation joinAllocation)
{
if (joinAllocation == null || this == null) // The returned JoinAllocation is null if allocation failed. this would be destroyed already if you quit the lobby while Relay is connecting.
return;
bool isSecure = false;
m_endpointForServer = GetEndpointForAllocation(joinAllocation.ServerEndpoints, joinAllocation.RelayServer.IpV4, joinAllocation.RelayServer.Port, out isSecure);
m_setupInGame.SetRelayServerData(AddressFromEndpoint(m_endpointForServer), m_endpointForServer.Port, joinAllocation.AllocationIdBytes, joinAllocation.Key, joinAllocation.ConnectionData, joinAllocation.HostConnectionData, isSecure);
m_onJoin?.Invoke();
}
protected override void OnBindingComplete() { /*No-op*/ }
}
}
正在加载...
取消
保存