using System; using System.Threading.Tasks; using UnityEngine; using UnityEngine.Networking; public static class Cupid { private static string serverAddress; public static string ServerAddress => serverAddress; private static string cupidBaseUri; public static string CupidURI => cupidBaseUri; private static int port; public static int Port => port; private static string bearerToken; public static string BearerToken => bearerToken; private static string settingsPassword = ""; public static string SettingsPassword => settingsPassword; private static CupidSettings settings = null; public static CupidSettings Settings => settings; public static bool isInitialized => settings != null; public static int RoomPort = -1; public static async Task Init(string _serverAddress, int _port, string _bearerToken, string _settingsPassword) { if (!TryBuildEndpoints(_serverAddress, _port, out serverAddress, out cupidBaseUri, out port)) { Logger.Log("Cupid: invalid or empty server address."); bearerToken = _bearerToken ?? ""; settings = null; return; } bearerToken = _bearerToken ?? ""; if (string.IsNullOrEmpty(bearerToken)) { Logger.Log("Cupid: no bearer token; configure auth before matchmaking."); settings = null; return; } settingsPassword = _settingsPassword ?? ""; string settingsUrl = CupidURI + "/settings?password=" + UnityWebRequest.EscapeURL(settingsPassword); using (UnityWebRequest www = UnityWebRequest.Get(settingsUrl)) { www.SetRequestHeader("Authorization", "Bearer " + bearerToken); var operation = www.SendWebRequest(); while (!operation.isDone) { await Task.Yield(); } try { if (www.result != UnityWebRequest.Result.Success) { Logger.Log("Cupid settings request failed: " + www.error); Logger.Log(www.downloadHandler != null ? www.downloadHandler.text : ""); settings = null; return; } string body = www.downloadHandler != null ? www.downloadHandler.text : ""; if (string.IsNullOrWhiteSpace(body)) { settings = null; return; } if (body.Trim() == "403 Unauthorized") { Logger.Log("Cupid settings: wrong or missing password query (server rejected /settings)."); settings = null; return; } settings = JsonUtility.FromJson(body); if (settings == null) { throw new NullReferenceException(); } Logger.Log("Cupid init success"); } catch (Exception e) { Logger.Log("Error retreiving settings from server " + e.Message); Logger.Log(www.downloadHandler != null ? www.downloadHandler.text : ""); settings = null; } } } /// /// Plain host/IP + HTTP port, or an absolute URL (https://host with no port uses 443). /// Sets for game client transport; has no trailing slash. /// static bool TryBuildEndpoints(string input, int fallbackHttpPort, out string hostOnly, out string baseUri, out int resolvedPort) { hostOnly = ""; baseUri = ""; resolvedPort = fallbackHttpPort; input = (input ?? "").Trim(); if (string.IsNullOrEmpty(input)) return false; if (input.IndexOf("://", StringComparison.Ordinal) >= 0 && Uri.TryCreate(input, UriKind.Absolute, out Uri uri) && (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)) { hostOnly = uri.Host; string authority = uri.GetLeftPart(UriPartial.Authority); string path = uri.AbsolutePath; if (!string.IsNullOrEmpty(path) && path != "/") baseUri = authority.TrimEnd('/') + path.TrimEnd('/'); else baseUri = authority; resolvedPort = uri.Port; return true; } hostOnly = input; baseUri = "http://" + input + ":" + fallbackHttpPort; resolvedPort = fallbackHttpPort; return true; } public static string RandomUsername{ get{ string pool="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; string output =""; for(int i=0; i < 5; i++){ output += pool.ToCharArray()[UnityEngine.Random.Range(0,pool.Length)]; } return output; } } /// Matchmaker GET / body: "0" while waiting; otherwise a JSON room object. public static CupidRoom? ParseRoom(string data) { if (string.IsNullOrWhiteSpace(data)) return null; string t = data.Trim(); if (t == "0") return null; try { CupidRoom room = JsonUtility.FromJson(t); if (room.Port <= 0) return null; return room; } catch { return null; } } } [System.Serializable] public class CupidSettings { public int minimum_players; public int maximum_players; public int waiting_time; } [System.Serializable] public struct CupidRoom { public CupidQueueEntry[] Players; public string GameName; public int Port; public long InitTime; public int match_id; public string your_team; } [System.Serializable] public struct CupidQueueEntry { public string Name; public long LastSeen; public int UserId; }