aha
This commit is contained in:
8
Assets/Mirror/Transports/Edgegap/EdgegapLobby.meta
Normal file
8
Assets/Mirror/Transports/Edgegap/EdgegapLobby.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 447b4ad1a3db7cf4fa5a0709d297ba9b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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
|
8
Assets/Mirror/Transports/Edgegap/EdgegapRelay.meta
Normal file
8
Assets/Mirror/Transports/Edgegap/EdgegapRelay.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 62c28e855fc644011b4079c268b46b71
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,141 @@
|
||||
// overwrite RawSend/Receive
|
||||
using System;
|
||||
using System.Net.Sockets;
|
||||
using Mirror;
|
||||
using UnityEngine;
|
||||
using kcp2k;
|
||||
|
||||
namespace Edgegap
|
||||
{
|
||||
public class EdgegapKcpClient : KcpClient
|
||||
{
|
||||
// need buffer larger than KcpClient.rawReceiveBuffer to add metadata
|
||||
readonly byte[] relayReceiveBuffer;
|
||||
|
||||
// authentication
|
||||
public uint userId;
|
||||
public uint sessionId;
|
||||
public ConnectionState connectionState = ConnectionState.Disconnected;
|
||||
|
||||
// ping
|
||||
double lastPingTime;
|
||||
|
||||
public EdgegapKcpClient(
|
||||
Action OnConnected,
|
||||
Action<ArraySegment<byte>, KcpChannel> OnData,
|
||||
Action OnDisconnected,
|
||||
Action<ErrorCode, string> OnError,
|
||||
KcpConfig config)
|
||||
: base(OnConnected, OnData, OnDisconnected, OnError, config)
|
||||
{
|
||||
relayReceiveBuffer = new byte[config.Mtu + Protocol.Overhead];
|
||||
}
|
||||
|
||||
// custom start function with relay parameters; connects udp client.
|
||||
public void Connect(string relayAddress, ushort relayPort, uint userId, uint sessionId)
|
||||
{
|
||||
// reset last state
|
||||
connectionState = ConnectionState.Checking;
|
||||
this.userId = userId;
|
||||
this.sessionId = sessionId;
|
||||
|
||||
// reuse base connect
|
||||
base.Connect(relayAddress, relayPort);
|
||||
}
|
||||
|
||||
// parse metadata, then pass to kcp
|
||||
protected override bool RawReceive(out ArraySegment<byte> segment)
|
||||
{
|
||||
segment = default;
|
||||
if (socket == null) return false;
|
||||
|
||||
try
|
||||
{
|
||||
if (socket.ReceiveNonBlocking(relayReceiveBuffer, out ArraySegment<byte> content))
|
||||
{
|
||||
using (NetworkReaderPooled reader = NetworkReaderPool.Get(content))
|
||||
{
|
||||
// parse message type
|
||||
if (reader.Remaining == 0)
|
||||
{
|
||||
Debug.LogWarning($"EdgegapClient: message of {content.Count} is too small to parse.");
|
||||
return false;
|
||||
}
|
||||
byte messageType = reader.ReadByte();
|
||||
|
||||
// handle message type
|
||||
switch (messageType)
|
||||
{
|
||||
case (byte)MessageType.Ping:
|
||||
{
|
||||
// parse state
|
||||
if (reader.Remaining < 1) return false;
|
||||
ConnectionState last = connectionState;
|
||||
connectionState = (ConnectionState)reader.ReadByte();
|
||||
|
||||
// log state changes for debugging.
|
||||
if (connectionState != last) Debug.Log($"EdgegapClient: state updated to: {connectionState}");
|
||||
|
||||
// return true indicates Mirror to keep checking
|
||||
// for further messages.
|
||||
return true;
|
||||
}
|
||||
case (byte)MessageType.Data:
|
||||
{
|
||||
segment = reader.ReadBytesSegment(reader.Remaining);
|
||||
return true;
|
||||
}
|
||||
// wrong message type. return false, don't throw.
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
Log.Info($"EdgegapClient: looks like the other end has closed the connection. This is fine: {e}");
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void RawSend(ArraySegment<byte> data)
|
||||
{
|
||||
using (NetworkWriterPooled writer = NetworkWriterPool.Get())
|
||||
{
|
||||
writer.WriteUInt(userId);
|
||||
writer.WriteUInt(sessionId);
|
||||
writer.WriteByte((byte)MessageType.Data);
|
||||
writer.WriteBytes(data.Array, data.Offset, data.Count);
|
||||
base.RawSend(writer);
|
||||
}
|
||||
}
|
||||
|
||||
void SendPing()
|
||||
{
|
||||
using (NetworkWriterPooled writer = NetworkWriterPool.Get())
|
||||
{
|
||||
writer.WriteUInt(userId);
|
||||
writer.WriteUInt(sessionId);
|
||||
writer.WriteByte((byte)MessageType.Ping);
|
||||
base.RawSend(writer);
|
||||
}
|
||||
}
|
||||
|
||||
public override void TickOutgoing()
|
||||
{
|
||||
if (connected)
|
||||
{
|
||||
// ping every interval for keepalive & handshake
|
||||
if (NetworkTime.localTime >= lastPingTime + Protocol.PingInterval)
|
||||
{
|
||||
SendPing();
|
||||
lastPingTime = NetworkTime.localTime;
|
||||
}
|
||||
}
|
||||
|
||||
base.TickOutgoing();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0d6fba7098f4ea3949d0195e8276adc
|
||||
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/EdgegapRelay/EdgegapKcpClient.cs
|
||||
uploadId: 736421
|
@ -0,0 +1,203 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Mirror;
|
||||
using UnityEngine;
|
||||
using kcp2k;
|
||||
|
||||
namespace Edgegap
|
||||
{
|
||||
public class EdgegapKcpServer : KcpServer
|
||||
{
|
||||
// need buffer larger than KcpClient.rawReceiveBuffer to add metadata
|
||||
readonly byte[] relayReceiveBuffer;
|
||||
|
||||
// authentication
|
||||
public uint userId;
|
||||
public uint sessionId;
|
||||
public ConnectionState state = ConnectionState.Disconnected;
|
||||
|
||||
// server is an UDP client talking to relay
|
||||
protected Socket relaySocket;
|
||||
public EndPoint remoteEndPoint;
|
||||
|
||||
// ping
|
||||
double lastPingTime;
|
||||
|
||||
// custom 'active'. while connected to relay
|
||||
bool relayActive;
|
||||
|
||||
public EdgegapKcpServer(
|
||||
Action<int, IPEndPoint> OnConnected,
|
||||
Action<int, ArraySegment<byte>, KcpChannel> OnData,
|
||||
Action<int> OnDisconnected,
|
||||
Action<int, ErrorCode, string> OnError,
|
||||
KcpConfig config)
|
||||
// TODO don't call base. don't listen to local UdpServer at all?
|
||||
: base(OnConnected, OnData, OnDisconnected, OnError, config)
|
||||
{
|
||||
relayReceiveBuffer = new byte[config.Mtu + Protocol.Overhead];
|
||||
}
|
||||
|
||||
public override bool IsActive() => relayActive;
|
||||
|
||||
// custom start function with relay parameters; connects udp client.
|
||||
public void Start(string relayAddress, ushort relayPort, uint userId, uint sessionId)
|
||||
{
|
||||
// reset last state
|
||||
state = ConnectionState.Checking;
|
||||
this.userId = userId;
|
||||
this.sessionId = sessionId;
|
||||
|
||||
// try resolve host name
|
||||
if (!Common.ResolveHostname(relayAddress, out IPAddress[] addresses))
|
||||
{
|
||||
OnError(0, ErrorCode.DnsResolve, $"Failed to resolve host: {relayAddress}");
|
||||
return;
|
||||
}
|
||||
|
||||
// create socket
|
||||
remoteEndPoint = new IPEndPoint(addresses[0], relayPort);
|
||||
relaySocket = new Socket(remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
|
||||
relaySocket.Blocking = false;
|
||||
|
||||
// configure buffer sizes
|
||||
Common.ConfigureSocketBuffers(relaySocket, config.RecvBufferSize, config.SendBufferSize);
|
||||
|
||||
// bind to endpoint for Send/Receive instead of SendTo/ReceiveFrom
|
||||
relaySocket.Connect(remoteEndPoint);
|
||||
relayActive = true;
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
relayActive = false;
|
||||
}
|
||||
|
||||
protected override bool RawReceiveFrom(out ArraySegment<byte> segment, out int connectionId)
|
||||
{
|
||||
segment = default;
|
||||
connectionId = 0;
|
||||
|
||||
if (relaySocket == null) return false;
|
||||
|
||||
try
|
||||
{
|
||||
// TODO need separate buffer. don't write into result yet. only payload
|
||||
|
||||
if (relaySocket.ReceiveNonBlocking(relayReceiveBuffer, out ArraySegment<byte> content))
|
||||
{
|
||||
using (NetworkReaderPooled reader = NetworkReaderPool.Get(content))
|
||||
{
|
||||
// parse message type
|
||||
if (reader.Remaining == 0)
|
||||
{
|
||||
Debug.LogWarning($"EdgegapServer: message of {content.Count} is too small to parse header.");
|
||||
return false;
|
||||
}
|
||||
byte messageType = reader.ReadByte();
|
||||
|
||||
// handle message type
|
||||
switch (messageType)
|
||||
{
|
||||
case (byte)MessageType.Ping:
|
||||
{
|
||||
// parse state
|
||||
if (reader.Remaining < 1) return false;
|
||||
ConnectionState last = state;
|
||||
state = (ConnectionState)reader.ReadByte();
|
||||
|
||||
// log state changes for debugging.
|
||||
if (state != last) Debug.Log($"EdgegapServer: state updated to: {state}");
|
||||
|
||||
// return true indicates Mirror to keep checking
|
||||
// for further messages.
|
||||
return true;
|
||||
}
|
||||
case (byte)MessageType.Data:
|
||||
{
|
||||
// parse connectionId and payload
|
||||
if (reader.Remaining <= 4)
|
||||
{
|
||||
Debug.LogWarning($"EdgegapServer: message of {content.Count} is too small to parse connId.");
|
||||
return false;
|
||||
}
|
||||
|
||||
connectionId = reader.ReadInt();
|
||||
segment = reader.ReadBytesSegment(reader.Remaining);
|
||||
// Debug.Log($"EdgegapServer: receiving from connId={connectionId}: {segment.ToHexString()}");
|
||||
return true;
|
||||
}
|
||||
// wrong message type. return false, don't throw.
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
Log.Info($"EdgegapServer: looks like the other end has closed the connection. This is fine: {e}");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void RawSend(int connectionId, ArraySegment<byte> data)
|
||||
{
|
||||
using (NetworkWriterPooled writer = NetworkWriterPool.Get())
|
||||
{
|
||||
// Debug.Log($"EdgegapServer: sending to connId={connectionId}: {data.ToHexString()}");
|
||||
writer.WriteUInt(userId);
|
||||
writer.WriteUInt(sessionId);
|
||||
writer.WriteByte((byte)MessageType.Data);
|
||||
writer.WriteInt(connectionId);
|
||||
writer.WriteBytes(data.Array, data.Offset, data.Count);
|
||||
ArraySegment<byte> message = writer;
|
||||
|
||||
try
|
||||
{
|
||||
relaySocket.SendNonBlocking(message);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
Log.Error($"KcpRleayServer: RawSend failed: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SendPing()
|
||||
{
|
||||
using (NetworkWriterPooled writer = NetworkWriterPool.Get())
|
||||
{
|
||||
writer.WriteUInt(userId);
|
||||
writer.WriteUInt(sessionId);
|
||||
writer.WriteByte((byte)MessageType.Ping);
|
||||
ArraySegment<byte> message = writer;
|
||||
|
||||
try
|
||||
{
|
||||
relaySocket.SendNonBlocking(message);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
Debug.LogWarning($"EdgegapServer: failed to ping. perhaps the relay isn't running? {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void TickOutgoing()
|
||||
{
|
||||
if (relayActive)
|
||||
{
|
||||
// ping every interval for keepalive & handshake
|
||||
if (NetworkTime.localTime >= lastPingTime + Protocol.PingInterval)
|
||||
{
|
||||
SendPing();
|
||||
lastPingTime = NetworkTime.localTime;
|
||||
}
|
||||
}
|
||||
|
||||
// base processing
|
||||
base.TickOutgoing();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd8551078397248b0848950352c208ee
|
||||
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/EdgegapRelay/EdgegapKcpServer.cs
|
||||
uploadId: 736421
|
@ -0,0 +1,162 @@
|
||||
// edgegap relay transport.
|
||||
// reuses KcpTransport with custom KcpServer/Client.
|
||||
|
||||
//#if MIRROR <- commented out because MIRROR isn't defined on first import yet
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine;
|
||||
using Mirror;
|
||||
using kcp2k;
|
||||
|
||||
namespace Edgegap
|
||||
{
|
||||
[HelpURL("https://mirror-networking.gitbook.io/docs/manual/transports/edgegap-transports/edgegap-relay")]
|
||||
public class EdgegapKcpTransport : KcpTransport
|
||||
{
|
||||
[Header("Relay")]
|
||||
public string relayAddress = "127.0.0.1";
|
||||
public ushort relayGameServerPort = 8888;
|
||||
public ushort relayGameClientPort = 9999;
|
||||
|
||||
// mtu for kcp transport. respects relay overhead.
|
||||
public const int MaxPayload = Kcp.MTU_DEF - Protocol.Overhead;
|
||||
|
||||
[Header("Relay")]
|
||||
public bool relayGUI = true;
|
||||
public uint userId = 11111111;
|
||||
public uint sessionId = 22222222;
|
||||
|
||||
// helper
|
||||
internal static String ReParse(String cmd, String pattern, String defaultValue)
|
||||
{
|
||||
Match match = Regex.Match(cmd, pattern);
|
||||
return match.Success ? match.Groups[1].Value : defaultValue;
|
||||
}
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
// logging
|
||||
// Log.Info should use Debug.Log if enabled, or nothing otherwise
|
||||
// (don't want to spam the console on headless servers)
|
||||
if (debugLog)
|
||||
Log.Info = Debug.Log;
|
||||
else
|
||||
Log.Info = _ => {};
|
||||
Log.Warning = Debug.LogWarning;
|
||||
Log.Error = Debug.LogError;
|
||||
|
||||
// create config from serialized settings.
|
||||
// with MaxPayload as max size to respect relay overhead.
|
||||
config = new KcpConfig(DualMode, RecvBufferSize, SendBufferSize, MaxPayload, NoDelay, Interval, FastResend, false, SendWindowSize, ReceiveWindowSize, Timeout, MaxRetransmit);
|
||||
|
||||
// client (NonAlloc version is not necessary anymore)
|
||||
client = new EdgegapKcpClient(
|
||||
() => OnClientConnected.Invoke(),
|
||||
(message, channel) => OnClientDataReceived.Invoke(message, FromKcpChannel(channel)),
|
||||
() => OnClientDisconnected?.Invoke(), // may be null in StopHost(): https://github.com/MirrorNetworking/Mirror/issues/3708
|
||||
(error, reason) => OnClientError.Invoke(ToTransportError(error), reason),
|
||||
config
|
||||
);
|
||||
|
||||
// server
|
||||
server = new EdgegapKcpServer(
|
||||
(connectionId, endPoint) => OnServerConnectedWithAddress.Invoke(connectionId, endPoint.PrettyAddress()),
|
||||
(connectionId, message, channel) => OnServerDataReceived.Invoke(connectionId, message, FromKcpChannel(channel)),
|
||||
(connectionId) => OnServerDisconnected.Invoke(connectionId),
|
||||
(connectionId, error, reason) => OnServerError.Invoke(connectionId, ToTransportError(error), reason),
|
||||
config);
|
||||
|
||||
if (statisticsLog)
|
||||
InvokeRepeating(nameof(OnLogStatistics), 1, 1);
|
||||
|
||||
Debug.Log("EdgegapTransport initialized!");
|
||||
}
|
||||
|
||||
protected override void OnValidate()
|
||||
{
|
||||
// show max message sizes in inspector for convenience.
|
||||
// 'config' isn't available in edit mode yet, so use MTU define.
|
||||
ReliableMaxMessageSize = KcpPeer.ReliableMaxMessageSize(MaxPayload, ReceiveWindowSize);
|
||||
UnreliableMaxMessageSize = KcpPeer.UnreliableMaxMessageSize(MaxPayload);
|
||||
}
|
||||
|
||||
// client overwrites to use EdgegapClient instead of KcpClient
|
||||
public override void ClientConnect(string address)
|
||||
{
|
||||
// connect to relay address:port instead of the expected server address
|
||||
EdgegapKcpClient client = (EdgegapKcpClient)this.client;
|
||||
client.userId = userId;
|
||||
client.sessionId = sessionId;
|
||||
client.connectionState = ConnectionState.Checking; // reset from last time
|
||||
client.Connect(relayAddress, relayGameClientPort);
|
||||
}
|
||||
public override void ClientConnect(Uri uri)
|
||||
{
|
||||
if (uri.Scheme != Scheme)
|
||||
throw new ArgumentException($"Invalid url {uri}, use {Scheme}://host:port instead", nameof(uri));
|
||||
|
||||
// connect to relay address:port instead of the expected server address
|
||||
EdgegapKcpClient client = (EdgegapKcpClient)this.client;
|
||||
client.Connect(relayAddress, relayGameClientPort, userId, sessionId);
|
||||
}
|
||||
|
||||
// server overwrites to use EdgegapServer instead of KcpServer
|
||||
public override void ServerStart()
|
||||
{
|
||||
// start the server
|
||||
EdgegapKcpServer server = (EdgegapKcpServer)this.server;
|
||||
server.Start(relayAddress, relayGameServerPort, userId, sessionId);
|
||||
}
|
||||
|
||||
void OnGUIRelay()
|
||||
{
|
||||
// if (server.IsActive()) return;
|
||||
|
||||
GUILayout.BeginArea(new Rect(300, 30, 200, 100));
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("SessionId:");
|
||||
sessionId = Convert.ToUInt32(GUILayout.TextField(sessionId.ToString()));
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("UserId:");
|
||||
userId = Convert.ToUInt32(GUILayout.TextField(userId.ToString()));
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
if (NetworkServer.active)
|
||||
{
|
||||
EdgegapKcpServer server = (EdgegapKcpServer)this.server;
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("State:");
|
||||
GUILayout.Label(server.state.ToString());
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
else if (NetworkClient.active)
|
||||
{
|
||||
EdgegapKcpClient client = (EdgegapKcpClient)this.client;
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("State:");
|
||||
GUILayout.Label(client.connectionState.ToString());
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
GUILayout.EndArea();
|
||||
}
|
||||
|
||||
// base OnGUI only shows in editor & development builds.
|
||||
// here we always show it because we need the sessionid & userid buttons.
|
||||
#pragma warning disable CS0109
|
||||
new void OnGUI()
|
||||
{
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
base.OnGUI();
|
||||
#endif
|
||||
if (relayGUI) OnGUIRelay();
|
||||
}
|
||||
|
||||
public override string ToString() => "Edgegap Kcp Transport";
|
||||
}
|
||||
#pragma warning restore CS0109
|
||||
}
|
||||
//#endif MIRROR <- commented out because MIRROR isn't defined on first import yet
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2d1e0e17f753449798fa27474d6b86b
|
||||
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/EdgegapRelay/EdgegapKcpTransport.cs
|
||||
uploadId: 736421
|
29
Assets/Mirror/Transports/Edgegap/EdgegapRelay/Protocol.cs
Normal file
29
Assets/Mirror/Transports/Edgegap/EdgegapRelay/Protocol.cs
Normal file
@ -0,0 +1,29 @@
|
||||
// relay protocol definitions
|
||||
namespace Edgegap
|
||||
{
|
||||
public enum ConnectionState : byte
|
||||
{
|
||||
Disconnected = 0, // until the user calls connect()
|
||||
Checking = 1, // recently connected, validation in progress
|
||||
Valid = 2, // validation succeeded
|
||||
Invalid = 3, // validation rejected by tower
|
||||
SessionTimeout = 4, // session owner timed out
|
||||
Error = 5, // other error
|
||||
}
|
||||
|
||||
public enum MessageType : byte
|
||||
{
|
||||
Ping = 1,
|
||||
Data = 2
|
||||
}
|
||||
|
||||
public static class Protocol
|
||||
{
|
||||
// MTU: relay adds up to 13 bytes of metadata in the worst case.
|
||||
public const int Overhead = 13;
|
||||
|
||||
// ping interval should be between 100 ms and 1 second.
|
||||
// faster ping gives faster authentication, but higher bandwidth.
|
||||
public const float PingInterval = 0.5f;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eac30312ba61470b849e368af3c3b0e9
|
||||
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/EdgegapRelay/Protocol.cs
|
||||
uploadId: 736421
|
20
Assets/Mirror/Transports/Edgegap/EdgegapRelay/README.md
Normal file
20
Assets/Mirror/Transports/Edgegap/EdgegapRelay/README.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Edgegap Relay for Mirror
|
||||
Documentation: https://docs.edgegap.com/docs/distributed-relay-manager/
|
||||
|
||||
## Prerequisites
|
||||
- Unity project set up with the Mirror networking library installed
|
||||
- Supported Versions: [Mirror](https://assetstore.unity.com/packages/tools/network/mirror-129321) and [Mirror LTS](https://assetstore.unity.com/packages/tools/network/mirror-lts-102631)
|
||||
- EdgegapTransport module downloaded and extracted
|
||||
|
||||
## Steps
|
||||
1. Open your Unity project and navigate to the "Assets" folder.
|
||||
2. Locate the "Mirror" folder within "Assets" and open it.
|
||||
3. Within the "Mirror" folder, open the "Transports" folder.
|
||||
4. Drag and drop the "Unity" folder from the extracted EdgegapTransport files into the "Transports" folder.
|
||||
5. Open your NetworkManager script in the Unity Editor and navigate to the "Inspector" panel.
|
||||
6. In the "Inspector" panel, locate the "Network Manager" component and click the "+" button next to the "Transport" property.
|
||||
7. In the "Add Component" menu that appears, select "Edgegap Transport" to add it to the NetworkManager.
|
||||
8. Drag the newly added "Edgegap Transport" component into the "Transport" property in the "Inspector" panel.
|
||||
|
||||
## Notes
|
||||
- The EdgegapTransport module is only compatible with Mirror and Mirror LTS versions.
|
14
Assets/Mirror/Transports/Edgegap/EdgegapRelay/README.md.meta
Normal file
14
Assets/Mirror/Transports/Edgegap/EdgegapRelay/README.md.meta
Normal file
@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ade7c960d8fe4e94970ddd88ede3bca
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Transports/Edgegap/EdgegapRelay/README.md
|
||||
uploadId: 736421
|
@ -0,0 +1,25 @@
|
||||
// parse session_id and user_id from command line args.
|
||||
// mac: "open mirror.app --args session_id=123 user_id=456"
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Edgegap
|
||||
{
|
||||
public class RelayCredentialsFromArgs : MonoBehaviour
|
||||
{
|
||||
void Awake()
|
||||
{
|
||||
String cmd = Environment.CommandLine;
|
||||
|
||||
// parse session_id via regex
|
||||
String sessionId = EdgegapKcpTransport.ReParse(cmd, "session_id=(\\d+)", "111111");
|
||||
String userID = EdgegapKcpTransport.ReParse(cmd, "user_id=(\\d+)", "222222");
|
||||
Debug.Log($"Parsed sessionId: {sessionId} user_id: {userID}");
|
||||
|
||||
// configure transport
|
||||
EdgegapKcpTransport transport = GetComponent<EdgegapKcpTransport>();
|
||||
transport.sessionId = UInt32.Parse(sessionId);
|
||||
transport.userId = UInt32.Parse(userID);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e9ec7091b26c4d3882f4b42f10f9b8c1
|
||||
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/EdgegapRelay/RelayCredentialsFromArgs.cs
|
||||
uploadId: 736421
|
BIN
Assets/Mirror/Transports/Edgegap/edgegap.png
Normal file
BIN
Assets/Mirror/Transports/Edgegap/edgegap.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
130
Assets/Mirror/Transports/Edgegap/edgegap.png.meta
Normal file
130
Assets/Mirror/Transports/Edgegap/edgegap.png.meta
Normal file
@ -0,0 +1,130 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ea6ff15cda674a57b0c7c8b7dc1878c
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 12
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMasterTextureLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 0
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 16
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Server
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Transports/Edgegap/edgegap.png
|
||||
uploadId: 736421
|
Reference in New Issue
Block a user