2022-12-03 20:40:43 +02:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using UnityEngine;
|
2022-12-07 12:13:57 +02:00
|
|
|
using Unity.Services.Core;
|
|
|
|
|
using Unity.Services.Authentication;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using Unity.Netcode;
|
|
|
|
|
using Unity.Services.Lobbies;
|
|
|
|
|
using Unity.Services.Lobbies.Models;
|
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Net.Sockets;
|
2022-12-08 14:35:27 +02:00
|
|
|
using UnityEngine.SceneManagement;
|
2022-12-08 17:39:21 +02:00
|
|
|
using Unity.Netcode.Transports.UTP;
|
2023-01-01 12:50:59 +02:00
|
|
|
using STUN.Attributes;
|
|
|
|
|
using STUN;
|
2022-12-03 20:40:43 +02:00
|
|
|
|
2022-12-04 21:35:31 +02:00
|
|
|
public class NetworkManagerLobby : MonoBehaviour
|
2022-12-03 20:40:43 +02:00
|
|
|
{
|
2023-02-02 02:10:38 +02:00
|
|
|
[SerializeField] private OnlineErrorMenu _onlineErrorMenu = default;
|
2022-12-07 12:13:57 +02:00
|
|
|
[SerializeField] private int _maxPlayers = 2;
|
|
|
|
|
private Lobby _hostLobby;
|
|
|
|
|
private Lobby _clientLobby;
|
|
|
|
|
private float _lobbyUpdateTimer;
|
2022-12-08 03:28:59 +02:00
|
|
|
public Action OnLobbyUpdate;
|
2023-01-15 01:54:52 +02:00
|
|
|
|
|
|
|
|
//Initialize the Unity services and authenticate the user anonymously
|
|
|
|
|
//Will change when accounts are introduced
|
2022-12-07 12:13:57 +02:00
|
|
|
public static async void Authenticate()
|
|
|
|
|
{
|
|
|
|
|
await UnityServices.InitializeAsync();
|
|
|
|
|
AuthenticationService.Instance.SignedIn += () =>
|
|
|
|
|
{
|
|
|
|
|
Debug.Log("Signed in:" + AuthenticationService.Instance.PlayerId);
|
|
|
|
|
};
|
|
|
|
|
await AuthenticationService.Instance.SignInAnonymouslyAsync();
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-15 01:54:52 +02:00
|
|
|
//Host a lobby
|
2022-12-07 12:13:57 +02:00
|
|
|
public async Task<string> CreateLobby(DemonData demonData)
|
|
|
|
|
{
|
2023-02-02 02:10:38 +02:00
|
|
|
Lobby lobby;
|
2023-02-12 03:29:03 +02:00
|
|
|
Unity.Services.Lobbies.Models.Player player = null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
player = GetPlayer(demonData);
|
|
|
|
|
}
|
|
|
|
|
catch (System.Exception e)
|
|
|
|
|
{
|
2023-02-22 15:36:55 +02:00
|
|
|
_onlineErrorMenu.Show("Host:" + e.Message);
|
2023-02-12 03:29:03 +02:00
|
|
|
return null;
|
|
|
|
|
}
|
2022-12-07 12:13:57 +02:00
|
|
|
CreateLobbyOptions createLobbyOptions = new CreateLobbyOptions
|
|
|
|
|
{
|
|
|
|
|
IsPrivate = false,
|
2023-03-29 02:13:49 +03:00
|
|
|
Player = player,
|
|
|
|
|
Data = new Dictionary<string, DataObject>
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
"Code", new DataObject(
|
|
|
|
|
visibility: DataObject.VisibilityOptions.Public,
|
|
|
|
|
value: "111111",
|
|
|
|
|
index: DataObject.IndexOptions.S1)
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-12-07 12:13:57 +02:00
|
|
|
};
|
2023-02-02 02:10:38 +02:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
lobby = await LobbyService.Instance.CreateLobbyAsync("darklings", _maxPlayers, createLobbyOptions);
|
|
|
|
|
_hostLobby = lobby;
|
|
|
|
|
}
|
|
|
|
|
catch (LobbyServiceException e)
|
|
|
|
|
{
|
2023-02-22 15:36:55 +02:00
|
|
|
_onlineErrorMenu.Show("Lobby:" + e.Reason.ToString());
|
2023-02-02 02:10:38 +02:00
|
|
|
return null;
|
|
|
|
|
}
|
2022-12-07 12:13:57 +02:00
|
|
|
return lobby.LobbyCode;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-08 03:28:59 +02:00
|
|
|
public Lobby GetHostLobby()
|
|
|
|
|
{
|
|
|
|
|
return _hostLobby;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-08 14:35:27 +02:00
|
|
|
public Lobby GetClientLobby()
|
|
|
|
|
{
|
|
|
|
|
return _clientLobby;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-27 12:11:17 +03:00
|
|
|
|
2023-03-29 02:13:49 +03:00
|
|
|
//Quick join the first found lobby
|
2023-03-27 12:11:17 +03:00
|
|
|
public async Task<Lobby> QuickJoinLobby(DemonData demonData)
|
|
|
|
|
{
|
|
|
|
|
Lobby lobby;
|
|
|
|
|
Unity.Services.Lobbies.Models.Player player = null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
player = GetPlayer(demonData);
|
|
|
|
|
}
|
|
|
|
|
catch (System.Exception e)
|
|
|
|
|
{
|
|
|
|
|
_onlineErrorMenu.Show(e.Message);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
QuickJoinLobbyOptions quickJoinLobbyByCodeOptions = new QuickJoinLobbyOptions
|
|
|
|
|
{
|
|
|
|
|
Player = GetPlayer(demonData)
|
|
|
|
|
};
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
lobby = await Lobbies.Instance.QuickJoinLobbyAsync(quickJoinLobbyByCodeOptions);
|
|
|
|
|
_clientLobby = lobby;
|
|
|
|
|
}
|
|
|
|
|
catch (LobbyServiceException e)
|
|
|
|
|
{
|
|
|
|
|
_onlineErrorMenu.Show(e.Reason.ToString());
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return lobby;
|
|
|
|
|
}
|
2023-03-29 02:13:49 +03:00
|
|
|
//Search for lobbies
|
|
|
|
|
public async Task<Lobby[]> SearchLobbies()
|
|
|
|
|
{
|
|
|
|
|
Lobby[] lobbies;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
QueryLobbiesOptions queryLobbiesOptions = new QueryLobbiesOptions();
|
|
|
|
|
queryLobbiesOptions.Count = 20;
|
|
|
|
|
queryLobbiesOptions.Filters = new List<QueryFilter>();
|
|
|
|
|
{
|
|
|
|
|
new QueryFilter(
|
|
|
|
|
field: QueryFilter.FieldOptions.AvailableSlots,
|
|
|
|
|
op: QueryFilter.OpOptions.GT,
|
|
|
|
|
value: "0");
|
|
|
|
|
}
|
|
|
|
|
QueryResponse query = await Lobbies.Instance.QueryLobbiesAsync(queryLobbiesOptions);
|
|
|
|
|
lobbies = query.Results.ToArray();
|
|
|
|
|
}
|
|
|
|
|
catch (LobbyServiceException e)
|
|
|
|
|
{
|
|
|
|
|
_onlineErrorMenu.Show(e.Reason.ToString());
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
if (lobbies.Length == 0)
|
|
|
|
|
{
|
2023-09-26 01:27:40 +03:00
|
|
|
_onlineErrorMenu.Show("No rooms found");
|
2023-03-29 02:13:49 +03:00
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return lobbies;
|
|
|
|
|
}
|
2023-03-27 12:11:17 +03:00
|
|
|
|
2023-01-15 01:54:52 +02:00
|
|
|
//Join the lobby given a lobby Id
|
2022-12-07 12:13:57 +02:00
|
|
|
public async Task<Lobby> JoinLobby(DemonData demonData, string lobbyId)
|
2023-03-29 02:13:49 +03:00
|
|
|
{
|
|
|
|
|
Lobby lobby;
|
|
|
|
|
Unity.Services.Lobbies.Models.Player player = null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
player = GetPlayer(demonData);
|
|
|
|
|
}
|
|
|
|
|
catch (System.Exception e)
|
|
|
|
|
{
|
|
|
|
|
_onlineErrorMenu.Show(e.Message);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
JoinLobbyByIdOptions joinLobbyByIdOptions = new JoinLobbyByIdOptions
|
|
|
|
|
{
|
|
|
|
|
Player = GetPlayer(demonData)
|
|
|
|
|
};
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
lobby = await Lobbies.Instance.JoinLobbyByIdAsync(lobbyId, joinLobbyByIdOptions);
|
|
|
|
|
_clientLobby = lobby;
|
|
|
|
|
}
|
|
|
|
|
catch (LobbyServiceException e)
|
|
|
|
|
{
|
|
|
|
|
_onlineErrorMenu.Show(e.Reason.ToString());
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return lobby;
|
|
|
|
|
}
|
|
|
|
|
//Join the lobby given a lobby code
|
|
|
|
|
public async Task<Lobby> JoinLobbyByCode(DemonData demonData, string lobbyCode)
|
2022-12-07 12:13:57 +02:00
|
|
|
{
|
2023-02-02 02:10:38 +02:00
|
|
|
Lobby lobby;
|
2023-02-12 03:29:03 +02:00
|
|
|
Unity.Services.Lobbies.Models.Player player = null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
player = GetPlayer(demonData);
|
|
|
|
|
}
|
|
|
|
|
catch (System.Exception e)
|
|
|
|
|
{
|
|
|
|
|
_onlineErrorMenu.Show(e.Message);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2022-12-07 12:13:57 +02:00
|
|
|
JoinLobbyByCodeOptions joinLobbyByCodeOptions = new JoinLobbyByCodeOptions
|
|
|
|
|
{
|
|
|
|
|
Player = GetPlayer(demonData)
|
|
|
|
|
};
|
2023-02-02 02:10:38 +02:00
|
|
|
try
|
|
|
|
|
{
|
2023-03-29 02:13:49 +03:00
|
|
|
lobby = await Lobbies.Instance.JoinLobbyByCodeAsync(lobbyCode, joinLobbyByCodeOptions);
|
2023-02-02 02:10:38 +02:00
|
|
|
_clientLobby = lobby;
|
|
|
|
|
}
|
|
|
|
|
catch (LobbyServiceException e)
|
|
|
|
|
{
|
|
|
|
|
_onlineErrorMenu.Show(e.Reason.ToString());
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2022-12-08 03:28:59 +02:00
|
|
|
return lobby;
|
2022-12-07 12:13:57 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-15 01:54:52 +02:00
|
|
|
//Update when a player is ready
|
2022-12-08 14:35:27 +02:00
|
|
|
public async void UpdateLobbyReady(bool ready, bool isHost)
|
|
|
|
|
{
|
|
|
|
|
string lobbyId;
|
|
|
|
|
string playerId;
|
|
|
|
|
if (isHost)
|
|
|
|
|
{
|
|
|
|
|
lobbyId = _hostLobby.Id;
|
|
|
|
|
playerId = _hostLobby.Players[0].Id;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
lobbyId = _clientLobby.Id;
|
|
|
|
|
playerId = _clientLobby.Players[1].Id;
|
|
|
|
|
}
|
|
|
|
|
UpdateLobbyOptions updateLobbyOptions = new UpdateLobbyOptions();
|
|
|
|
|
await LobbyService.Instance.UpdatePlayerAsync(lobbyId, AuthenticationService.Instance.PlayerId, new UpdatePlayerOptions
|
|
|
|
|
{
|
|
|
|
|
Data = new Dictionary<string, PlayerDataObject>{
|
|
|
|
|
{ "Ready", new PlayerDataObject(PlayerDataObject.VisibilityOptions.Public, ready.ToString())}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-15 01:54:52 +02:00
|
|
|
//Delete lobby as a host
|
2022-12-09 17:21:06 +02:00
|
|
|
public async Task DeleteLobby()
|
2022-12-07 12:13:57 +02:00
|
|
|
{
|
2022-12-08 17:39:21 +02:00
|
|
|
string id = _hostLobby.Id;
|
|
|
|
|
_hostLobby = null;
|
2022-12-09 17:21:06 +02:00
|
|
|
await LobbyService.Instance.DeleteLobbyAsync(id);
|
2022-12-07 12:13:57 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-15 01:54:52 +02:00
|
|
|
//Leave lobby as a client
|
2022-12-09 17:44:02 +02:00
|
|
|
public async Task LeaveLobby()
|
2022-12-07 12:13:57 +02:00
|
|
|
{
|
|
|
|
|
await LobbyService.Instance.RemovePlayerAsync(_clientLobby.Id, AuthenticationService.Instance.PlayerId);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-15 01:54:52 +02:00
|
|
|
void Update()
|
|
|
|
|
{
|
|
|
|
|
HandleLobbyPollForUpdates();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Poll for updates, because there is a certain limit given by Unity, we have to wait before we check for an update
|
2022-12-07 12:13:57 +02:00
|
|
|
private async void HandleLobbyPollForUpdates()
|
|
|
|
|
{
|
2022-12-08 17:39:21 +02:00
|
|
|
if (_hostLobby != null)
|
2022-12-07 12:13:57 +02:00
|
|
|
{
|
|
|
|
|
_lobbyUpdateTimer -= Time.unscaledDeltaTime;
|
|
|
|
|
if (_lobbyUpdateTimer < 0)
|
|
|
|
|
{
|
|
|
|
|
_lobbyUpdateTimer = 1.1f;
|
2023-02-13 21:47:46 +02:00
|
|
|
Lobby lobby = await LobbyService.Instance.GetLobbyAsync(_hostLobby.Id);
|
|
|
|
|
_hostLobby = lobby;
|
|
|
|
|
OnLobbyUpdate?.Invoke();
|
2022-12-08 14:35:27 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (_clientLobby != null)
|
|
|
|
|
{
|
|
|
|
|
_lobbyUpdateTimer -= Time.unscaledDeltaTime;
|
|
|
|
|
if (_lobbyUpdateTimer < 0)
|
|
|
|
|
{
|
|
|
|
|
_lobbyUpdateTimer = 1.1f;
|
|
|
|
|
Lobby lobby = await LobbyService.Instance.GetLobbyAsync(_clientLobby.Id);
|
|
|
|
|
_clientLobby = lobby;
|
|
|
|
|
OnLobbyUpdate?.Invoke();
|
2022-12-07 12:13:57 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-15 01:54:52 +02:00
|
|
|
//Get the Player data required for the lobby and P2P connection
|
2022-12-07 12:13:57 +02:00
|
|
|
private Unity.Services.Lobbies.Models.Player GetPlayer(DemonData demonData)
|
|
|
|
|
{
|
2023-02-22 15:36:55 +02:00
|
|
|
string address = "";
|
|
|
|
|
string port = "";
|
2023-02-12 03:29:03 +02:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
PublicIp(out address, out port);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception)
|
|
|
|
|
{
|
|
|
|
|
throw;
|
|
|
|
|
}
|
2022-12-07 12:13:57 +02:00
|
|
|
return new Unity.Services.Lobbies.Models.Player
|
|
|
|
|
{
|
|
|
|
|
Data = new Dictionary<string, PlayerDataObject>{
|
|
|
|
|
{ "DemonName", new PlayerDataObject(PlayerDataObject.VisibilityOptions.Public, demonData.demonName)},
|
|
|
|
|
{ "Character", new PlayerDataObject(PlayerDataObject.VisibilityOptions.Public, demonData.character.ToString())},
|
|
|
|
|
{ "Assist", new PlayerDataObject(PlayerDataObject.VisibilityOptions.Public, demonData.assist.ToString())},
|
|
|
|
|
{ "Color", new PlayerDataObject(PlayerDataObject.VisibilityOptions.Public, demonData.color.ToString())},
|
2022-12-08 14:35:27 +02:00
|
|
|
{ "Ready", new PlayerDataObject(PlayerDataObject.VisibilityOptions.Public, "False")},
|
2023-01-30 15:36:37 +02:00
|
|
|
{ "Ip", new PlayerDataObject(PlayerDataObject.VisibilityOptions.Public, address)},
|
|
|
|
|
{ "Port", new PlayerDataObject(PlayerDataObject.VisibilityOptions.Public,port)},
|
|
|
|
|
{ "PrivateIp", new PlayerDataObject(PlayerDataObject.VisibilityOptions.Public, PrivateIp())},
|
2022-12-07 12:13:57 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
2023-01-15 01:54:52 +02:00
|
|
|
|
|
|
|
|
//Use DNS to get the private Ip, this is done for LAN P2P connections
|
2023-01-01 12:50:59 +02:00
|
|
|
private string PrivateIp()
|
2022-12-07 12:13:57 +02:00
|
|
|
{
|
|
|
|
|
var host = Dns.GetHostEntry(Dns.GetHostName());
|
|
|
|
|
foreach (var ip in host.AddressList)
|
|
|
|
|
{
|
|
|
|
|
if (ip.AddressFamily == AddressFamily.InterNetwork)
|
|
|
|
|
{
|
|
|
|
|
return ip.ToString();
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-01 12:50:59 +02:00
|
|
|
return "127.0.0.1";
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-15 01:54:52 +02:00
|
|
|
//Use a STUN server for port forwarding, this is done for WAN P2P connections
|
2023-01-01 12:50:59 +02:00
|
|
|
private void PublicIp(out string address, out string port)
|
|
|
|
|
{
|
2023-02-12 03:29:03 +02:00
|
|
|
if (!STUNUtils.TryParseHostAndPort("stun1.l.google.com:19302", out IPEndPoint stunEndPoint))
|
|
|
|
|
throw new Exception("Failed to establish connection");
|
2023-01-01 12:50:59 +02:00
|
|
|
|
|
|
|
|
STUNClient.ReceiveTimeout = 500;
|
|
|
|
|
var queryResult = STUNClient.Query(stunEndPoint, STUNQueryType.ExactNAT, true, NATTypeDetectionRFC.Rfc3489);
|
|
|
|
|
if (queryResult.QueryError != STUNQueryError.Success)
|
2023-02-12 03:29:03 +02:00
|
|
|
throw new Exception("Connection Failed");
|
2023-02-11 23:18:02 +02:00
|
|
|
|
2023-01-01 12:50:59 +02:00
|
|
|
|
|
|
|
|
address = queryResult.PublicEndPoint.Address.ToString();
|
|
|
|
|
port = queryResult.PublicEndPoint.Port.ToString();
|
2022-12-07 12:13:57 +02:00
|
|
|
}
|
2022-12-03 20:40:43 +02:00
|
|
|
}
|