This commit is contained in:
2025-06-16 15:14:23 +02:00
committed by devbeni
parent 60fe4620ff
commit 4ff561284f
3174 changed files with 428263 additions and 0 deletions

View File

@ -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);
}
}
}
}

View File

@ -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

View 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);
}
};
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b9b459cf5e084bdd8b196df849a2c519
timeCreated: 1709953502

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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