aha
This commit is contained in:
@ -0,0 +1,345 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa9d4c3f48a245ed89f122f44e1e81ea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 3ea6ff15cda674a57b0c7c8b7dc1878c, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Transports/Edgegap/EdgegapLobby/EdgegapLobbyKcpTransport.cs
|
||||
uploadId: 736421
|
295
Assets/Mirror/Transports/Edgegap/EdgegapLobby/LobbyApi.cs
Normal file
295
Assets/Mirror/Transports/Edgegap/EdgegapLobby/LobbyApi.cs
Normal file
@ -0,0 +1,295 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace Edgegap
|
||||
{
|
||||
// Implements the edgegap lobby api: https://docs.edgegap.com/docs/lobby/functions
|
||||
public class LobbyApi
|
||||
{
|
||||
[Header("Lobby Config")]
|
||||
public string LobbyUrl;
|
||||
public LobbyBrief[] Lobbies;
|
||||
|
||||
public LobbyApi(string url)
|
||||
{
|
||||
LobbyUrl = url;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static UnityWebRequest SendJson<T>(string url, T data, string method = "POST")
|
||||
{
|
||||
string body = JsonUtility.ToJson(data);
|
||||
UnityWebRequest request = new UnityWebRequest(url, method);
|
||||
request.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(body));
|
||||
request.downloadHandler = new DownloadHandlerBuffer();
|
||||
request.SetRequestHeader("Accept", "application/json");
|
||||
request.SetRequestHeader("Content-Type", "application/json");
|
||||
return request;
|
||||
}
|
||||
|
||||
private static bool CheckErrorResponse(UnityWebRequest request, Action<string> onError)
|
||||
{
|
||||
#if UNITY_2020_3_OR_NEWER
|
||||
if (request.result != UnityWebRequest.Result.Success)
|
||||
{
|
||||
// how I hate http libs that think they need to be smart and handle status code errors.
|
||||
if (request.result != UnityWebRequest.Result.ProtocolError || request.responseCode == 0)
|
||||
{
|
||||
onError?.Invoke(request.error);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (request.isNetworkError)
|
||||
{
|
||||
onError?.Invoke(request.error);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
if (request.responseCode < 200 || request.responseCode >= 300)
|
||||
{
|
||||
onError?.Invoke($"non-200 status code: {request.responseCode}. Body:\n {request.downloadHandler.text}");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void RefreshLobbies(Action<LobbyBrief[]> onLoaded, Action<string> onError)
|
||||
{
|
||||
UnityWebRequest request = UnityWebRequest.Get($"{LobbyUrl}/lobbies");
|
||||
request.SendWebRequest().completed += operation =>
|
||||
{
|
||||
using (request)
|
||||
{
|
||||
if (CheckErrorResponse(request, onError)) return;
|
||||
ListLobbiesResponse lobbies = JsonUtility.FromJson<ListLobbiesResponse>(request.downloadHandler.text);
|
||||
Lobbies = lobbies.data;
|
||||
onLoaded?.Invoke(lobbies.data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void CreateLobby(LobbyCreateRequest createData, Action<Lobby> onResponse, Action<string> onError)
|
||||
{
|
||||
UnityWebRequest request = SendJson($"{LobbyUrl}/lobbies", createData);
|
||||
request.SetRequestHeader("Content-Type", "application/json");
|
||||
request.SendWebRequest().completed += (op) =>
|
||||
{
|
||||
using (request)
|
||||
{
|
||||
if (CheckErrorResponse(request, onError)) return;
|
||||
Lobby lobby = JsonUtility.FromJson<Lobby>(request.downloadHandler.text);
|
||||
onResponse?.Invoke(lobby);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void UpdateLobby(string lobbyId, LobbyUpdateRequest updateData, Action<LobbyBrief> onResponse, Action<string> onError)
|
||||
{
|
||||
UnityWebRequest request = SendJson($"{LobbyUrl}/lobbies/{lobbyId}", updateData, "PATCH");
|
||||
request.SetRequestHeader("Content-Type", "application/json");
|
||||
request.SendWebRequest().completed += (op) =>
|
||||
{
|
||||
using (request)
|
||||
{
|
||||
if (CheckErrorResponse(request, onError)) return;
|
||||
LobbyBrief lobby = JsonUtility.FromJson<LobbyBrief>(request.downloadHandler.text);
|
||||
onResponse?.Invoke(lobby);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void GetLobby(string lobbyId, Action<Lobby> onResponse, Action<string> onError)
|
||||
{
|
||||
UnityWebRequest request = UnityWebRequest.Get($"{LobbyUrl}/lobbies/{lobbyId}");
|
||||
request.SendWebRequest().completed += (op) =>
|
||||
{
|
||||
using (request)
|
||||
{
|
||||
if (CheckErrorResponse(request, onError)) return;
|
||||
Lobby lobby = JsonUtility.FromJson<Lobby>(request.downloadHandler.text);
|
||||
onResponse?.Invoke(lobby);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void JoinLobby(LobbyJoinOrLeaveRequest data, Action onResponse, Action<string> onError)
|
||||
{
|
||||
UnityWebRequest request = SendJson($"{LobbyUrl}/lobbies:join", data);
|
||||
request.SendWebRequest().completed += (op) =>
|
||||
{
|
||||
using (request)
|
||||
{
|
||||
if (CheckErrorResponse(request, onError)) return;
|
||||
onResponse?.Invoke();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void LeaveLobby(LobbyJoinOrLeaveRequest data, Action onResponse, Action<string> onError)
|
||||
{
|
||||
UnityWebRequest request = SendJson($"{LobbyUrl}/lobbies:leave", data);
|
||||
request.SendWebRequest().completed += (op) =>
|
||||
{
|
||||
using (request)
|
||||
{
|
||||
if (CheckErrorResponse(request, onError)) return;
|
||||
onResponse?.Invoke();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void StartLobby(LobbyIdRequest data, Action onResponse, Action<string> onError)
|
||||
{
|
||||
UnityWebRequest request = SendJson($"{LobbyUrl}/lobbies:start", data);
|
||||
request.SendWebRequest().completed += (op) =>
|
||||
{
|
||||
using (request)
|
||||
{
|
||||
if (CheckErrorResponse(request, onError)) return;
|
||||
onResponse?.Invoke();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void DeleteLobby(string lobbyId, Action onResponse, Action<string> onError)
|
||||
{
|
||||
UnityWebRequest request = SendJson($"{LobbyUrl}/lobbies/{lobbyId}", "", "DELETE");
|
||||
request.SetRequestHeader("Content-Type", "application/json");
|
||||
request.SendWebRequest().completed += (op) =>
|
||||
{
|
||||
using (request)
|
||||
{
|
||||
if (CheckErrorResponse(request, onError)) return;
|
||||
onResponse?.Invoke();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct CreateLobbyServiceRequest
|
||||
{
|
||||
public string name;
|
||||
}
|
||||
public struct LobbyServiceResponse
|
||||
{
|
||||
public string name;
|
||||
public string url;
|
||||
public string status;
|
||||
}
|
||||
|
||||
public static void TrimApiKey(ref string apiKey)
|
||||
{
|
||||
if (apiKey == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (apiKey.StartsWith("token "))
|
||||
{
|
||||
apiKey = apiKey.Substring("token ".Length);
|
||||
}
|
||||
apiKey = apiKey.Trim();
|
||||
}
|
||||
|
||||
public static void CreateAndDeployLobbyService(string apiKey, string name, Action<LobbyServiceResponse> onResponse, Action<string> onError)
|
||||
{
|
||||
TrimApiKey(ref apiKey);
|
||||
|
||||
// try to get the lobby first
|
||||
GetLobbyService(apiKey, name, response =>
|
||||
{
|
||||
if (response == null)
|
||||
{
|
||||
CreateLobbyService(apiKey, name, onResponse, onError);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(response.Value.url))
|
||||
{
|
||||
onResponse(response.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeployLobbyService(apiKey, name, onResponse, onError);
|
||||
}
|
||||
}, onError);
|
||||
}
|
||||
|
||||
private static void CreateLobbyService(string apiKey, string name, Action<LobbyServiceResponse> onResponse, Action<string> onError)
|
||||
{
|
||||
UnityWebRequest request = SendJson("https://api.edgegap.com/v1/lobbies", new CreateLobbyServiceRequest
|
||||
{
|
||||
name = name
|
||||
});
|
||||
request.SetRequestHeader("Authorization", $"token {apiKey}");
|
||||
request.SendWebRequest().completed += (op) =>
|
||||
{
|
||||
using (request)
|
||||
{
|
||||
if (CheckErrorResponse(request, onError)) return;
|
||||
DeployLobbyService(apiKey, name, onResponse, onError);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static void GetLobbyService(string apiKey, string name, Action<LobbyServiceResponse?> onResponse, Action<string> onError)
|
||||
{
|
||||
TrimApiKey(ref apiKey);
|
||||
|
||||
var request = UnityWebRequest.Get($"https://api.edgegap.com/v1/lobbies/{name}");
|
||||
request.SetRequestHeader("Authorization", $"token {apiKey}");
|
||||
request.SendWebRequest().completed += (op) =>
|
||||
{
|
||||
using (request)
|
||||
{
|
||||
if (request.responseCode == 404)
|
||||
{
|
||||
onResponse(null);
|
||||
return;
|
||||
}
|
||||
if (CheckErrorResponse(request, onError)) return;
|
||||
LobbyServiceResponse response = JsonUtility.FromJson<LobbyServiceResponse>(request.downloadHandler.text);
|
||||
onResponse(response);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static void TerminateLobbyService(string apiKey, string name, Action<LobbyServiceResponse> onResponse, Action<string> onError)
|
||||
{
|
||||
TrimApiKey(ref apiKey);
|
||||
|
||||
var request = SendJson("https://api.edgegap.com/v1/lobbies:terminate", new CreateLobbyServiceRequest
|
||||
{
|
||||
name = name
|
||||
});
|
||||
request.SetRequestHeader("Authorization", $"token {apiKey}");
|
||||
request.SendWebRequest().completed += (op) =>
|
||||
{
|
||||
using (request)
|
||||
{
|
||||
if (CheckErrorResponse(request, onError)) return;
|
||||
LobbyServiceResponse response = JsonUtility.FromJson<LobbyServiceResponse>(request.downloadHandler.text);
|
||||
onResponse?.Invoke(response);
|
||||
}
|
||||
};
|
||||
}
|
||||
private static void DeployLobbyService(string apiKey, string name, Action<LobbyServiceResponse> onResponse, Action<string> onError)
|
||||
{
|
||||
var request = SendJson("https://api.edgegap.com/v1/lobbies:deploy", new CreateLobbyServiceRequest
|
||||
{
|
||||
name = name
|
||||
});
|
||||
request.SetRequestHeader("Authorization", $"token {apiKey}");
|
||||
request.SendWebRequest().completed += (op) =>
|
||||
{
|
||||
using (request)
|
||||
{
|
||||
if (CheckErrorResponse(request, onError)) return;
|
||||
LobbyServiceResponse response = JsonUtility.FromJson<LobbyServiceResponse>(request.downloadHandler.text);
|
||||
onResponse?.Invoke(response);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 64510fc75d0d75f4185fec1cf4d12206
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 3ea6ff15cda674a57b0c7c8b7dc1878c, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Transports/Edgegap/EdgegapLobby/LobbyApi.cs
|
||||
uploadId: 736421
|
@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
namespace Edgegap
|
||||
{
|
||||
public class LobbyServiceCreateDialogue : EditorWindow
|
||||
{
|
||||
public Action<string> onLobby;
|
||||
public bool waitingCreate;
|
||||
public bool waitingStatus;
|
||||
private string _name;
|
||||
private string _key;
|
||||
private string _lastStatus;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
minSize = maxSize = new Vector2(450, 300);
|
||||
titleContent = new GUIContent("Edgegap Lobby Service Setup");
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if (waitingCreate)
|
||||
{
|
||||
EditorGUILayout.LabelField("Waiting for lobby to create . . . ");
|
||||
return;
|
||||
}
|
||||
if (waitingStatus)
|
||||
{
|
||||
EditorGUILayout.LabelField("Waiting for lobby to deploy . . . ");
|
||||
EditorGUILayout.LabelField($"Latest status: {_lastStatus}");
|
||||
return;
|
||||
}
|
||||
_key = EditorGUILayout.TextField("Edgegap API key", _key);
|
||||
LobbyApi.TrimApiKey(ref _key);
|
||||
EditorGUILayout.HelpBox(new GUIContent("Your API key won't be saved."));
|
||||
if (GUILayout.Button("I have no api key?"))
|
||||
{
|
||||
Application.OpenURL("https://app.edgegap.com/user-settings?tab=tokens");
|
||||
}
|
||||
EditorGUILayout.Separator();
|
||||
EditorGUILayout.HelpBox("There's currently a bug where lobby names longer than 5 characters can fail to deploy correctly and will return a \"503 Service Temporarily Unavailable\"\nIt's recommended to limit your lobby names to 4-5 characters for now", UnityEditor.MessageType.Warning);
|
||||
_name = EditorGUILayout.TextField("Lobby Name", _name);
|
||||
EditorGUILayout.HelpBox(new GUIContent("The lobby name is your games identifier for the lobby service"));
|
||||
|
||||
if (GUILayout.Button("Create"))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_key) || string.IsNullOrWhiteSpace(_name))
|
||||
{
|
||||
EditorUtility.DisplayDialog("Error", "Key and Name can't be empty.", "Ok");
|
||||
}
|
||||
else
|
||||
{
|
||||
waitingCreate = true;
|
||||
Repaint();
|
||||
|
||||
LobbyApi.CreateAndDeployLobbyService(_key.Trim(), _name.Trim(), res =>
|
||||
{
|
||||
waitingCreate = false;
|
||||
waitingStatus = true;
|
||||
_lastStatus = res.status;
|
||||
RefreshStatus();
|
||||
Repaint();
|
||||
}, error =>
|
||||
{
|
||||
EditorUtility.DisplayDialog("Failed to create lobby", $"The following error happened while trying to create (&deploy) the lobby service:\n\n{error}", "Ok");
|
||||
waitingCreate = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Cancel"))
|
||||
Close();
|
||||
|
||||
EditorGUILayout.HelpBox(new GUIContent("Note: If you forgot your lobby url simply re-create it with the same name!\nIt will re-use the existing lobby service"));
|
||||
EditorGUILayout.Separator();
|
||||
EditorGUILayout.Separator();
|
||||
|
||||
|
||||
if (GUILayout.Button("Terminate existing deploy"))
|
||||
{
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_key) || string.IsNullOrWhiteSpace(_name))
|
||||
{
|
||||
EditorUtility.DisplayDialog("Error", "Key and Name can't be empty.", "Ok");
|
||||
}
|
||||
else
|
||||
{
|
||||
LobbyApi.TerminateLobbyService(_key.Trim(), _name.Trim(), res =>
|
||||
{
|
||||
EditorUtility.DisplayDialog("Success", $"The lobby service will start terminating (shutting down the deploy) now", "Ok");
|
||||
}, error =>
|
||||
{
|
||||
EditorUtility.DisplayDialog("Failed to terminate lobby", $"The following error happened while trying to terminate the lobby service:\n\n{error}", "Ok");
|
||||
});
|
||||
}
|
||||
}
|
||||
EditorGUILayout.HelpBox(new GUIContent("Done with your lobby?\nEnter the same name as creation to shut it down"));
|
||||
}
|
||||
private void RefreshStatus()
|
||||
{
|
||||
// Stop if window is closed
|
||||
if (!this)
|
||||
{
|
||||
return;
|
||||
}
|
||||
LobbyApi.GetLobbyService(_key, _name, res =>
|
||||
{
|
||||
if (!res.HasValue)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Failed to create lobby", $"The lobby seems to have vanished while waiting for it to deploy.", "Ok");
|
||||
waitingStatus = false;
|
||||
Repaint();
|
||||
return;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(res.Value.url))
|
||||
{
|
||||
onLobby(res.Value.url);
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
_lastStatus = res.Value.status;
|
||||
Repaint();
|
||||
Thread.Sleep(100); // :( but this is a lazy editor script, its fiiine
|
||||
RefreshStatus();
|
||||
}, error =>
|
||||
{
|
||||
EditorUtility.DisplayDialog("Failed to create lobby", $"The following error happened while trying to create (&deploy) a lobby:\n\n{error}", "Ok");
|
||||
waitingStatus = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25579cc004424981bf0b05bcec65df0a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 3ea6ff15cda674a57b0c7c8b7dc1878c, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Transports/Edgegap/EdgegapLobby/LobbyServiceCreateDialogue.cs
|
||||
uploadId: 736421
|
@ -0,0 +1,64 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using kcp2k;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
namespace Edgegap
|
||||
{
|
||||
[CustomEditor(typeof(EdgegapLobbyKcpTransport))]
|
||||
public class EncryptionTransportInspector : UnityEditor.Editor
|
||||
{
|
||||
SerializedProperty lobbyUrlProperty;
|
||||
SerializedProperty lobbyWaitTimeoutProperty;
|
||||
private List<SerializedProperty> kcpProperties = new List<SerializedProperty>();
|
||||
|
||||
|
||||
// Assuming proper SerializedProperty definitions for properties
|
||||
// Add more SerializedProperty fields related to different modes as needed
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
lobbyUrlProperty = serializedObject.FindProperty("lobbyUrl");
|
||||
lobbyWaitTimeoutProperty = serializedObject.FindProperty("lobbyWaitTimeout");
|
||||
// Get public fields from KcpTransport
|
||||
kcpProperties.Clear();
|
||||
FieldInfo[] fields = typeof(KcpTransport).GetFields(BindingFlags.Public | BindingFlags.Instance);
|
||||
foreach (var field in fields)
|
||||
{
|
||||
SerializedProperty prop = serializedObject.FindProperty(field.Name);
|
||||
if (prop == null)
|
||||
{
|
||||
// callbacks have no property
|
||||
continue;
|
||||
}
|
||||
kcpProperties.Add(prop);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
EditorGUILayout.PropertyField(lobbyUrlProperty);
|
||||
if (GUILayout.Button("Create & Deploy Lobby"))
|
||||
{
|
||||
var input = CreateInstance<LobbyServiceCreateDialogue>();
|
||||
input.onLobby = (url) =>
|
||||
{
|
||||
lobbyUrlProperty.stringValue = url;
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
};
|
||||
input.ShowUtility();
|
||||
}
|
||||
EditorGUILayout.PropertyField(lobbyWaitTimeoutProperty);
|
||||
EditorGUILayout.Separator();
|
||||
foreach (SerializedProperty prop in kcpProperties)
|
||||
{
|
||||
EditorGUILayout.PropertyField(prop);
|
||||
}
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d7cc53263184754a4682335440df515
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 3ea6ff15cda674a57b0c7c8b7dc1878c, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Transports/Edgegap/EdgegapLobby/LobbyTransportInspector.cs
|
||||
uploadId: 736421
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9b459cf5e084bdd8b196df849a2c519
|
||||
timeCreated: 1709953502
|
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
namespace Edgegap
|
||||
{
|
||||
// https://docs.edgegap.com/docs/lobby/functions#functions
|
||||
[Serializable]
|
||||
public struct ListLobbiesResponse
|
||||
{
|
||||
public int count;
|
||||
public LobbyBrief[] data;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fdb37041d9464f8c90ac86942b940565
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 3ea6ff15cda674a57b0c7c8b7dc1878c, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Transports/Edgegap/EdgegapLobby/Models/ListLobbiesResponse.cs
|
||||
uploadId: 736421
|
@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
namespace Edgegap
|
||||
{
|
||||
// https://docs.edgegap.com/docs/lobby/functions#getting-a-specific-lobbys-information
|
||||
[Serializable]
|
||||
public struct Lobby
|
||||
{
|
||||
[Serializable]
|
||||
public struct Player
|
||||
{
|
||||
public uint authorization_token;
|
||||
public string id;
|
||||
public bool is_host;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct Port
|
||||
{
|
||||
public string name;
|
||||
public int port;
|
||||
public string protocol;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct Assignment
|
||||
{
|
||||
public uint authorization_token;
|
||||
public string host;
|
||||
public string ip;
|
||||
public Port[] ports;
|
||||
}
|
||||
|
||||
public Assignment assignment;
|
||||
public string name;
|
||||
public string lobby_id;
|
||||
public bool is_joinable;
|
||||
public bool is_started;
|
||||
public int player_count;
|
||||
public int capacity;
|
||||
public int available_slots => capacity - player_count;
|
||||
public string[] tags;
|
||||
public Player[] players;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 64db55f096cd4ace83e1aa1c0c0588f7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 3ea6ff15cda674a57b0c7c8b7dc1878c, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Transports/Edgegap/EdgegapLobby/Models/Lobby.cs
|
||||
uploadId: 736421
|
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
namespace Edgegap
|
||||
{
|
||||
// Brief lobby data, returned by the list function
|
||||
[Serializable]
|
||||
public struct LobbyBrief
|
||||
{
|
||||
public string lobby_id;
|
||||
public string name;
|
||||
public bool is_joinable;
|
||||
public bool is_started;
|
||||
public int player_count;
|
||||
public int capacity;
|
||||
public int available_slots => capacity - player_count;
|
||||
public string[] tags;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6018ece006144e719c6b3f0d4e256d7b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 3ea6ff15cda674a57b0c7c8b7dc1878c, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Transports/Edgegap/EdgegapLobby/Models/LobbyBrief.cs
|
||||
uploadId: 736421
|
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
namespace Edgegap
|
||||
{
|
||||
// https://docs.edgegap.com/docs/lobby/functions#creating-a-new-lobby
|
||||
[Serializable]
|
||||
public struct LobbyCreateRequest
|
||||
{
|
||||
[Serializable]
|
||||
public struct Player
|
||||
{
|
||||
public string id;
|
||||
}
|
||||
[Serializable]
|
||||
public struct Annotation
|
||||
{
|
||||
public bool inject;
|
||||
public string key;
|
||||
public string value;
|
||||
}
|
||||
public Annotation[] annotations; // todo
|
||||
public int capacity;
|
||||
public bool is_joinable;
|
||||
public string name;
|
||||
public Player player;
|
||||
public string[] tags;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4040c1adafc3449eaebd3bd22aa3ff26
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 3ea6ff15cda674a57b0c7c8b7dc1878c, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Transports/Edgegap/EdgegapLobby/Models/LobbyCreateRequest.cs
|
||||
uploadId: 736421
|
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
namespace Edgegap
|
||||
{
|
||||
// https://docs.edgegap.com/docs/lobby/functions/#starting-a-lobby
|
||||
[Serializable]
|
||||
public struct LobbyIdRequest
|
||||
{
|
||||
public string lobby_id;
|
||||
public LobbyIdRequest(string lobbyId)
|
||||
{
|
||||
lobby_id = lobbyId;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 219c7fba8724473caf170c6254e6dc45
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 3ea6ff15cda674a57b0c7c8b7dc1878c, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Transports/Edgegap/EdgegapLobby/Models/LobbyIdRequest.cs
|
||||
uploadId: 736421
|
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
namespace Edgegap
|
||||
{
|
||||
// https://docs.edgegap.com/docs/lobby/functions#updating-a-lobby
|
||||
// https://docs.edgegap.com/docs/lobby/functions#leaving-a-lobby
|
||||
[Serializable]
|
||||
public struct LobbyJoinOrLeaveRequest
|
||||
{
|
||||
[Serializable]
|
||||
public struct Player
|
||||
{
|
||||
public string id;
|
||||
}
|
||||
public string lobby_id;
|
||||
public Player player;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4091d555e62341f0ac30479952d517aa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 3ea6ff15cda674a57b0c7c8b7dc1878c, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Transports/Edgegap/EdgegapLobby/Models/LobbyJoinOrLeaveRequest.cs
|
||||
uploadId: 736421
|
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
namespace Edgegap
|
||||
{
|
||||
// https://docs.edgegap.com/docs/lobby/functions#updating-a-lobby
|
||||
[Serializable]
|
||||
public struct LobbyUpdateRequest
|
||||
{
|
||||
public int capacity;
|
||||
public bool is_joinable;
|
||||
public string[] tags;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee158bc379f44cdf9904578f37a5e7a4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 3ea6ff15cda674a57b0c7c8b7dc1878c, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Transports/Edgegap/EdgegapLobby/Models/LobbyUpdateRequest.cs
|
||||
uploadId: 736421
|
Reference in New Issue
Block a user