346 lines
12 KiB
C#
346 lines
12 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Threading;
|
|
using Mirror;
|
|
using UnityEngine;
|
|
using Random = UnityEngine.Random;
|
|
namespace Edgegap
|
|
{
|
|
[HelpURL("https://mirror-networking.gitbook.io/docs/manual/transports/edgegap-transports/edgegap-relay")]
|
|
public class EdgegapLobbyKcpTransport : EdgegapKcpTransport
|
|
{
|
|
[Header("Lobby Settings")]
|
|
[Tooltip("URL to the Edgegap lobby service, automatically filled in after completing the creation process via button below (or enter manually)")]
|
|
public string lobbyUrl;
|
|
[Tooltip("How long to wait for the relay to be assigned after starting a lobby")]
|
|
public float lobbyWaitTimeout = 60;
|
|
|
|
public LobbyApi Api;
|
|
private LobbyCreateRequest? _request;
|
|
private string _lobbyId;
|
|
private string _playerId;
|
|
private TransportStatus _status = TransportStatus.Offline;
|
|
public enum TransportStatus
|
|
{
|
|
Offline,
|
|
CreatingLobby,
|
|
StartingLobby,
|
|
JoiningLobby,
|
|
WaitingRelay,
|
|
Connecting,
|
|
Connected,
|
|
Error,
|
|
}
|
|
public TransportStatus Status
|
|
{
|
|
get
|
|
{
|
|
if (!NetworkClient.active && !NetworkServer.active)
|
|
{
|
|
return TransportStatus.Offline;
|
|
}
|
|
if (_status == TransportStatus.Connecting)
|
|
{
|
|
if (NetworkServer.active)
|
|
{
|
|
switch (((EdgegapKcpServer)this.server).state)
|
|
{
|
|
case ConnectionState.Valid:
|
|
return TransportStatus.Connected;
|
|
case ConnectionState.Invalid:
|
|
case ConnectionState.SessionTimeout:
|
|
case ConnectionState.Error:
|
|
return TransportStatus.Error;
|
|
}
|
|
}
|
|
else if (NetworkClient.active)
|
|
{
|
|
switch (((EdgegapKcpClient)this.client).connectionState)
|
|
{
|
|
case ConnectionState.Valid:
|
|
return TransportStatus.Connected;
|
|
case ConnectionState.Invalid:
|
|
case ConnectionState.SessionTimeout:
|
|
case ConnectionState.Error:
|
|
return TransportStatus.Error;
|
|
}
|
|
}
|
|
}
|
|
return _status;
|
|
}
|
|
}
|
|
|
|
protected override void Awake()
|
|
{
|
|
base.Awake();
|
|
Api = new LobbyApi(lobbyUrl);
|
|
}
|
|
|
|
private void Reset()
|
|
{
|
|
this.relayGUI = false;
|
|
}
|
|
|
|
public override void ServerStart()
|
|
{
|
|
if (!_request.HasValue)
|
|
{
|
|
throw new Exception("No lobby request set. Call SetServerLobbyParams");
|
|
}
|
|
_status = TransportStatus.CreatingLobby;
|
|
Api.CreateLobby(_request.Value, lobby =>
|
|
{
|
|
_lobbyId = lobby.lobby_id;
|
|
_status = TransportStatus.StartingLobby;
|
|
Api.StartLobby(new LobbyIdRequest(_lobbyId), () =>
|
|
{
|
|
StartCoroutine(WaitForLobbyRelay(_lobbyId, true));
|
|
}, error =>
|
|
{
|
|
_status = TransportStatus.Error;
|
|
string errorMsg = $"Could not start lobby: {error}";
|
|
Debug.LogError(errorMsg);
|
|
OnServerError?.Invoke(0, TransportError.Unexpected, errorMsg);
|
|
ServerStop();
|
|
});
|
|
},
|
|
error =>
|
|
{
|
|
_status = TransportStatus.Error;
|
|
string errorMsg = $"Couldn't create lobby: {error}";
|
|
Debug.LogError(errorMsg);
|
|
OnServerError?.Invoke(0, TransportError.Unexpected, errorMsg);
|
|
});
|
|
}
|
|
|
|
public override void ServerStop()
|
|
{
|
|
base.ServerStop();
|
|
|
|
Api.DeleteLobby(_lobbyId, () =>
|
|
{
|
|
// yay
|
|
}, error =>
|
|
{
|
|
OnServerError?.Invoke(0, TransportError.Unexpected, $"Failed to delete lobby: {error}");
|
|
});
|
|
}
|
|
|
|
public override void ClientDisconnect()
|
|
{
|
|
base.ClientDisconnect();
|
|
// this gets called for host mode as well
|
|
if (!NetworkServer.active)
|
|
{
|
|
Api.LeaveLobby(new LobbyJoinOrLeaveRequest
|
|
{
|
|
player = new LobbyJoinOrLeaveRequest.Player
|
|
{
|
|
id = _playerId
|
|
},
|
|
lobby_id = _lobbyId
|
|
}, () =>
|
|
{
|
|
// yay
|
|
}, error =>
|
|
{
|
|
string errorMsg = $"Failed to leave lobby: {error}";
|
|
OnClientError?.Invoke(TransportError.Unexpected, errorMsg);
|
|
Debug.LogError(errorMsg);
|
|
});
|
|
}
|
|
}
|
|
|
|
public override void ClientConnect(string address)
|
|
{
|
|
_lobbyId = address;
|
|
_playerId = RandomPlayerId();
|
|
_status = TransportStatus.JoiningLobby;
|
|
Api.JoinLobby(new LobbyJoinOrLeaveRequest
|
|
{
|
|
player = new LobbyJoinOrLeaveRequest.Player
|
|
{
|
|
id = _playerId,
|
|
},
|
|
lobby_id = address
|
|
}, () =>
|
|
{
|
|
StartCoroutine(WaitForLobbyRelay(_lobbyId, false));
|
|
}, error =>
|
|
{
|
|
_status = TransportStatus.Offline;
|
|
string errorMsg = $"Failed to join lobby: {error}";
|
|
OnClientError?.Invoke(TransportError.Unexpected, errorMsg);
|
|
Debug.LogError(errorMsg);
|
|
OnClientDisconnected?.Invoke();
|
|
});
|
|
}
|
|
|
|
private IEnumerator WaitForLobbyRelay(string lobbyId, bool forServer)
|
|
{
|
|
_status = TransportStatus.WaitingRelay;
|
|
double startTime = NetworkTime.localTime;
|
|
bool running = true;
|
|
while (running)
|
|
{
|
|
if (NetworkTime.localTime - startTime >= lobbyWaitTimeout)
|
|
{
|
|
_status = TransportStatus.Error;
|
|
string errorMsg = "Timed out waiting for lobby.";
|
|
Debug.LogError(errorMsg);
|
|
if (forServer)
|
|
{
|
|
_status = TransportStatus.Error;
|
|
OnServerError?.Invoke(0, TransportError.Unexpected, errorMsg);
|
|
ServerStop();
|
|
}
|
|
else
|
|
{
|
|
_status = TransportStatus.Error;
|
|
OnClientError?.Invoke(TransportError.Unexpected, errorMsg);
|
|
ClientDisconnect();
|
|
}
|
|
yield break;
|
|
}
|
|
bool waitingForResponse = true;
|
|
Api.GetLobby(lobbyId, lobby =>
|
|
{
|
|
waitingForResponse = false;
|
|
if (string.IsNullOrEmpty(lobby.assignment.ip))
|
|
{
|
|
// no lobby deployed yet, have the outer loop retry
|
|
return;
|
|
}
|
|
relayAddress = lobby.assignment.ip;
|
|
foreach (Lobby.Port aport in lobby.assignment.ports)
|
|
{
|
|
if (aport.protocol == "UDP")
|
|
{
|
|
if (aport.name == "server")
|
|
{
|
|
relayGameServerPort = (ushort)aport.port;
|
|
|
|
}
|
|
else if (aport.name == "client")
|
|
{
|
|
relayGameClientPort = (ushort)aport.port;
|
|
}
|
|
}
|
|
}
|
|
bool found = false;
|
|
foreach (Lobby.Player player in lobby.players)
|
|
{
|
|
if (player.id == _playerId)
|
|
{
|
|
userId = player.authorization_token;
|
|
sessionId = lobby.assignment.authorization_token;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
running = false;
|
|
if (!found)
|
|
{
|
|
string errorMsg = $"Couldn't find my player ({_playerId})";
|
|
Debug.LogError(errorMsg);
|
|
|
|
if (forServer)
|
|
{
|
|
_status = TransportStatus.Error;
|
|
OnServerError?.Invoke(0, TransportError.Unexpected, errorMsg);
|
|
ServerStop();
|
|
}
|
|
else
|
|
{
|
|
_status = TransportStatus.Error;
|
|
OnClientError?.Invoke(TransportError.Unexpected, errorMsg);
|
|
ClientDisconnect();
|
|
}
|
|
return;
|
|
}
|
|
_status = TransportStatus.Connecting;
|
|
if (forServer)
|
|
{
|
|
base.ServerStart();
|
|
}
|
|
else
|
|
{
|
|
base.ClientConnect("");
|
|
}
|
|
}, error =>
|
|
{
|
|
running = false;
|
|
waitingForResponse = false;
|
|
_status = TransportStatus.Error;
|
|
string errorMsg = $"Failed to get lobby info: {error}";
|
|
Debug.LogError(errorMsg);
|
|
if (forServer)
|
|
{
|
|
OnServerError?.Invoke(0, TransportError.Unexpected, errorMsg);
|
|
ServerStop();
|
|
}
|
|
else
|
|
{
|
|
OnClientError?.Invoke(TransportError.Unexpected, errorMsg);
|
|
ClientDisconnect();
|
|
}
|
|
});
|
|
while (waitingForResponse)
|
|
{
|
|
yield return null;
|
|
}
|
|
yield return new WaitForSeconds(0.2f);
|
|
}
|
|
}
|
|
private static string RandomPlayerId()
|
|
{
|
|
return $"mirror-player-{Random.Range(1, int.MaxValue)}";
|
|
}
|
|
|
|
public void SetServerLobbyParams(string lobbyName, int capacity)
|
|
{
|
|
SetServerLobbyParams(new LobbyCreateRequest
|
|
{
|
|
player = new LobbyCreateRequest.Player
|
|
{
|
|
id = RandomPlayerId(),
|
|
},
|
|
annotations = new LobbyCreateRequest.Annotation[]
|
|
{
|
|
},
|
|
capacity = capacity,
|
|
is_joinable = true,
|
|
name = lobbyName,
|
|
tags = new string[]
|
|
{
|
|
}
|
|
});
|
|
}
|
|
|
|
public void SetServerLobbyParams(LobbyCreateRequest request)
|
|
{
|
|
_playerId = request.player.id;
|
|
_request = request;
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
// attempt to clean up lobbies, if active
|
|
if (NetworkServer.active)
|
|
{
|
|
ServerStop();
|
|
// Absolutely make sure there's time for the network request to hit edgegap servers.
|
|
// sorry. this can go once the lobby service can timeout lobbies itself
|
|
Thread.Sleep(300);
|
|
}
|
|
else if (NetworkClient.active)
|
|
{
|
|
ClientDisconnect();
|
|
// Absolutely make sure there's time for the network request to hit edgegap servers.
|
|
// sorry. this can go once the lobby service can timeout lobbies itself
|
|
Thread.Sleep(300);
|
|
}
|
|
}
|
|
}
|
|
}
|