using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using CIG; using CIG.Translation.Native; using Inno.DLC; using SUISS.Core; using SUISS.Scheduling; using SUISS.Storage; using SUISSEngine; using UnityEngine; [RequireComponent(typeof(Serializing))] public sealed class CIGWebService : SingletonMonobehaviour { //[DebuggerBrowsable(DebuggerBrowsableState.Never)] public event CIGWebService.PullRequestCompletedHandler PullRequestCompleted; private void FirePullRequestCompleted() { if (this.PullRequestCompleted != null) { this.PullRequestCompleted(); } } protected override void Awake() { base.Awake(); this.UserKey = string.Empty; this.UpdateAvailable = false; this.Undelivered = Currencies.Zero; this.Sale = string.Empty; this.SaleExpiry = DateTime.MaxValue; this.UserId = -1L; this.IslandSyncChangePercent = 5; this._lastPullRequestTime = -3600L; this._extraWaitTime = 0L; this.Multipliers = new Multipliers(); this.VideoSpeedupEnabled = false; this.VideoSpeedupMaxCurrency = 2; this.VideoSpeedupMinSeconds = 60; this.HasPulled = false; } private void Start() { if (this.serializing == null) { UnityEngine.Debug.LogWarning("Web service requires a serializing."); SingletonMonobehaviour.Instance.StartRoutine(this._pullRequestRoutine = this.PullRequestRoutine(), base.gameObject); } } protected override void OnDestroy() { if (this._pullRequestRoutine != null && SingletonMonobehaviour.IsAvailable) { SingletonMonobehaviour.Instance.StopRoutine(this._pullRequestRoutine); this._pullRequestRoutine = null; } base.OnDestroy(); } private void OnSerialize(Dictionary values) { Dictionary root = Storage.Get(StorageLifecycle.Player).Root; if (string.IsNullOrEmpty(this.UserKey)) { root.Remove("userKey"); } else { root["userKey"] = this.UserKey; } if (this.UserId < 0L) { root.Remove("userId"); } else { root["userId"] = this.UserId; } values["sessionsPlayed"] = this._sessionsPlayed; values["undelivered"] = this.Undelivered; values["allow_iap_deals"] = this.UseIAPDeal; values["disable_sign_validation"] = this.DisableIAPSignValidation; values["sale"] = this.Sale; values["saleExpiry"] = this.SaleExpiry.Ticks; values["islandSyncChangePercent"] = this.IslandSyncChangePercent; values["lastRequest"] = this._lastPullRequestTime; values["lastResponse"] = this._lastResponse; values["commercialBuildingCostCashMultiplier"] = this.Multipliers.CommercialBuildingCostCashMultiplier; values["commercialBuildingCostGoldMultiplier"] = this.Multipliers.CommercialBuildingCostGoldMultiplier; values["communityBuildingCostCashMultiplier"] = this.Multipliers.CommunityBuildingCostCashMultiplier; values["communityBuildingCostGoldMultiplier"] = this.Multipliers.CommunityBuildingCostGoldMultiplier; values["residentialBuildingCostCashMultiplier"] = this.Multipliers.ResidentialBuildingCostCashMultiplier; values["residentialBuildingCostGoldMultiplier"] = this.Multipliers.ResidentialBuildingCostGoldMultiplier; values["upspeedCostCashMultiplier"] = this.Multipliers.UpspeedCostCashMultiplier; values["upspeedCostGoldMultiplier"] = this.Multipliers.UpspeedCostGoldMultiplier; values["commercialBuildingProfitMultiplier"] = this.Multipliers.CommercialBuildingProfitMultiplier; values["useBadge"] = this.UseBadge; values["cheater_value"] = this._cheaterType; values["video_speedup_building_enabled"] = this.VideoSpeedupEnabled; values["video_speedup_building_max_gold_cost"] = this.VideoSpeedupMaxCurrency; values["video_speedup_building_min_seconds"] = this.VideoSpeedupMinSeconds; values["timeDiffWithClientInMinutes"] = this._timeDiffWithClientInMinutes; } private void OnDeserialize(Dictionary values) { Dictionary root = Storage.Get(StorageLifecycle.Player).Root; this.UserKey = root.GetString("userKey", string.Empty); this.UserId = root.GetLong("userId", values.GetLong("userId", -1L)); object obj; if (values.TryGetValue("sessionsPlayed", out obj)) { this._sessionsPlayed = (int)obj; } if (values.TryGetValue("undelivered", out obj)) { this.Undelivered += (Currencies)obj; } if (values.TryGetValue("allow_iap_deals", out obj) && obj is bool) { this.UseIAPDeal = (bool)obj; } if (values.TryGetValue("disable_sign_validation", out obj) && obj is bool) { this.DisableIAPSignValidation = (bool)obj; } if (values.TryGetValue("sale", out obj)) { this.Sale = (string)obj; } if (values.TryGetValue("saleExpiry", out obj) && obj is long) { this.SaleExpiry = new DateTime((long)obj, DateTimeKind.Utc); } this.IslandSyncChangePercent = values.GetInt("islandSyncChangePercent", 5); if (values.TryGetValue("lastRequest", out obj)) { this._lastPullRequestTime = (long)obj; } if (values.TryGetValue("lastResponse", out obj)) { this._lastResponse = (string)obj; this._properties = Parser.ParsePropertyFileFormat(this._lastResponse); } if (values.TryGetValue("adjust_tokens_seen", out obj)) { this.MigrateAdjustTokensSeenToSUISSEngineStorage((List)obj); values.Remove("adjust_tokens_seen"); } this.UseBadge = values.GetBoolean("useBadge", true); this._cheaterType = values.GetInt("cheater_value", -1); this.VideoSpeedupEnabled = values.GetBoolean("video_speedup_building_enabled", false); this.VideoSpeedupMaxCurrency = values.GetInt("video_speedup_building_max_gold_cost", 2); this.VideoSpeedupMinSeconds = values.GetInt("video_speedup_building_min_seconds", 60); this._timeDiffWithClientInMinutes = values.GetLong("timeDiffWithClientInMinutes", 0L); this.Multipliers.SetStoredData(values); } private void MigrateAdjustTokensSeenToSUISSEngineStorage(List adjust_tokens) { EdwinServerEventHandler.StorableStringList storableStringList = new EdwinServerEventHandler.StorableStringList(); foreach (object obj in adjust_tokens) { storableStringList.Add((string)obj); } Dictionary dictionary = Storage.Get(StorageLifecycle.Player).GetDictionary("edwinserverEventStorage"); if (dictionary.ContainsKey("adjust_key")) { UnityEngine.Debug.LogError("Impossible! Old place still contained edwinserverEventStorage, but new place as well!"); dictionary["adjust_key"] = storableStringList; } else { dictionary.Add("adjust_key", storableStringList); } } private void OnDeserialized() { this._sessionsPlayed++; SingletonMonobehaviour.Instance.StartRoutine(this._pullRequestRoutine = this.PullRequestRoutine(), base.gameObject); } public void ForcePullRequest() { if (this._waitEnumerator != null) { SingletonMonobehaviour.Instance.StopRoutine(this._waitEnumerator); this._waitEnumerator = null; } } public WWW RedeemCode(string code) { WWWForm wwwform = this.GenerateRequestParameters(false); wwwform.AddField("action", "redeemcode_extended"); wwwform.AddField("code", code); return new WWW(CIGWebService.APIURL, wwwform); } public WWW PurchaseIntent(string item) { WWWForm wwwform = this.GenerateRequestParameters(false); wwwform.AddField("action", "purchase_intent"); wwwform.AddField("item", item); return new WWW(CIGWebService.APIURL, wwwform); } public WWW PurchaseSuccess_OldSchool(string item) { WWWForm wwwform = this.GenerateRequestParameters(false); wwwform.AddField("action", "purchase_success"); wwwform.AddField("item", item); return new WWW(CIGWebService.APIURL, wwwform); } public WWW PricePoints() { WWWForm wwwform = this.GenerateRequestParameters(false); wwwform.AddField("action", "getallpricepoints"); return new WWW(CIGWebService.APIURL, wwwform); } public WWW ValidatePurchase(string productID, string receipt, Dictionary requestParameters) { WWWForm wwwform = this.GenerateUserRequestParameters(); foreach (KeyValuePair keyValuePair in requestParameters) { wwwform.AddField(keyValuePair.Key, keyValuePair.Value); } wwwform.AddField("action", "purchase_receipt"); wwwform.AddField("sku", productID); wwwform.AddField("receipt", receipt); return new WWW(CIGWebService.APIURL, wwwform); } public Dictionary GeneratePurchaseReceiptParameters() { Dictionary dictionary = new Dictionary(); CIGGameState instance = SingletonMonobehaviour.Instance; dictionary.Add("xp", instance.Balance.Round(Currencies.RoundingMethod.Nearest).GetValue("XP").ToString()); dictionary.Add("level", instance.Level.ToString()); dictionary.Add("cash", instance.Balance.Round(Currencies.RoundingMethod.Nearest).GetValue("Cash").ToString()); dictionary.Add("gold", instance.Balance.Round(Currencies.RoundingMethod.Nearest).GetValue("Gold").ToString()); dictionary.Add("cashspent", instance.CurrenciesSpent.Round(Currencies.RoundingMethod.Nearest).GetValue("Cash").ToString()); dictionary.Add("goldspent", instance.CurrenciesSpent.Round(Currencies.RoundingMethod.Nearest).GetValue("Gold").ToString()); dictionary.Add("minutesplayed", instance.TotalMinutesPlayed.ToString()); dictionary.Add("sessions", this._sessionsPlayed.ToString()); dictionary.Add("clientTimestamp", CIGWebService.UnixTimestamp.ToString()); dictionary.Add("timeDiffMinutes", this._timeDiffWithClientInMinutes.ToString()); return dictionary; } public Coroutine WWWWithTimeout(WWW www, Action callback, float timeout = 5f) { return base.StartCoroutine(this.WWWWithTimeoutRoutine(www, timeout, callback)); } public WWW RegisterNewsLetter(string email) { WWWForm wwwform = this.GenerateRequestParameters(false); wwwform.AddField("action", "register"); wwwform.AddField("email", email); return new WWW(CIGWebService.APIURL, wwwform); } public static Currencies ParseCurrencies(string str, out int cranes) { Currencies currencies = Currencies.Zero; cranes = 0; foreach (string text in str.Split(new char[] { ',' })) { if (text.StartsWith("C")) { decimal value; if (decimal.TryParse(text.Substring(1), out value)) { currencies += new Currencies("Cash", value); } } else if (text.StartsWith("G")) { decimal value2; if (decimal.TryParse(text.Substring(1), out value2)) { currencies += new Currencies("Gold", value2); } } else if (text.StartsWith("D")) { UnityEngine.Debug.LogWarning("Cannot handle diamonds!"); } else if (text.StartsWith("XP")) { decimal value3; if (decimal.TryParse(text.Substring(2), out value3)) { currencies += new Currencies("XP", value3); } } else if (text.StartsWith("B") && !int.TryParse(text.Substring(1), out cranes)) { UnityEngine.Debug.LogError(string.Format("Unable to parse currencies part '{0}'", text)); } } return currencies; } public void ClearUndelivered() { this.Undelivered = Currencies.Zero; this.serializing.Serialize(); } public string GetApiProperty(string key) { if (this._properties.ContainsKey(key)) { return this._properties[key]; } return string.Empty; } public string UserKey { get; private set; } public long UserId { get; private set; } public bool UpdateAvailable { get; private set; } public bool DisableIAPSignValidation { get; private set; } public Currencies Undelivered { get; private set; } public bool UseIAPDeal { get; private set; } public string Sale { get; private set; } public DateTime SaleExpiry { get; private set; } public int IslandSyncChangePercent { get; private set; } public Dictionary Properties { get { return this._properties; } } public bool HasPulled { get; private set; } public Multipliers Multipliers { get; private set; } public bool UseBadge { get; private set; } public bool VideoSpeedupEnabled { get; private set; } public int VideoSpeedupMaxCurrency { get; private set; } public int VideoSpeedupMinSeconds { get; private set; } public static string APIURL { get { return CIGWebService.APIDomain + "api.php"; } } public static string UserPropsURL { get { return CIGWebService.APIDomain + "userprops.php"; } } private static long UnixTimestamp { get { return (long)(DateTime.UtcNow - CIGWebService.UnixOriginTimestamp).TotalSeconds; } } private static string APIDomain { get { if (string.IsNullOrEmpty(CIGWebService._apiDomain)) { CIGWebService.SetupDomains(); } return CIGWebService._apiDomain; } } public static void SetupDomains() { CIGWebService._apiDomain = "https://gamesapi.sparklingsociety.net/cig3-google/"; } private IEnumerator WaitForPullRequest() { long waitTime = this._lastPullRequestTime - (long)Timing.UtcNow + 900L + this._extraWaitTime; double tfinish = Timing.UtcNow + (double)Math.Max(10L, waitTime); while (Timing.UtcNow < tfinish) { double tleft = tfinish - Timing.UtcNow; yield return Timing.time + tleft; } yield break; } private IEnumerator WWWWithTimeoutRoutine(WWW www, float timeout, Action callback) { float timeoutAt = Time.realtimeSinceStartup + timeout; yield return new WaitUntil(() => www.isDone || timeoutAt < Time.realtimeSinceStartup); if (callback != null) { if (www.isDone) { callback(new CIGWebService.WWWResponse(false, www.error, www.text)); } else { callback(new CIGWebService.WWWResponse(true, "Request timeout.", string.Empty)); } } yield return www; www.Dispose(); yield break; } private IEnumerator PullRequestRoutine() { for (;;) { yield return this._waitEnumerator = this.WaitForPullRequest(); this._waitEnumerator = null; WWWForm parameters = this.GenerateRequestParameters(true); WWW request = new WWW(CIGWebService.UserPropsURL, parameters); yield return request; if (request.error != null && request.error.Length > 0) { UnityEngine.Debug.LogWarning("Server error: " + request.error); this._extraWaitTime = Math.Min(600L, Math.Max(60L, 2L * this._extraWaitTime)); } else { this._extraWaitTime = 0L; this.HandlePullResponse(request.text); } } yield break; } private WWWForm GenerateUserRequestParameters() { WWWForm wwwform = new WWWForm(); wwwform.AddField("userKey", (this.UserKey != null) ? this.UserKey : string.Empty); wwwform.AddField("userId", this.UserId.ToString()); wwwform.AddField("game_version", CityIsland.VersionString); CIGSparkSocServices instance = SingletonMonobehaviour.Instance; string installUuid = instance.GetInstallUuid(); wwwform.AddField("sagid", (installUuid != null) ? installUuid : string.Empty); string playerUuid = instance.GetPlayerUuid(); wwwform.AddField("sauid", (playerUuid != null) ? playerUuid : string.Empty); string playerName = instance.GetPlayerName(); wwwform.AddField("ssuname", (playerName != null) ? playerName : string.Empty); return wwwform; } private WWWForm GenerateRequestParameters(bool extendedInfo) { WWWForm wwwform = this.GenerateUserRequestParameters(); if (extendedInfo) { string[] defaultLanguages = SystemLocale.DefaultLanguages; string str; if (defaultLanguages.Length > 0) { str = defaultLanguages[0]; } else { str = Application.systemLanguage.ToString(); } wwwform.AddField("brand", "Unity"); wwwform.AddField("device", SystemInfo.deviceName); wwwform.AddField("manufacturer", SystemInfo.graphicsDeviceVendor); wwwform.AddField("model", SystemInfo.deviceModel); wwwform.AddField("product", SystemInfo.deviceType.ToString()); wwwform.AddField("island", string.Empty); wwwform.AddField("gpu_memory", SystemInfo.graphicsMemorySize.ToString()); wwwform.AddField("sys_memory", SystemInfo.systemMemorySize.ToString()); wwwform.AddField("texture_scale", JPEGLoader.TextureScalingFactor.ToString()); CIGGameStats instance = SingletonMonobehaviour.Instance; wwwform.AddField("gold_CashBuildings", instance.GoldSpent_CashBuildings.ToString()); wwwform.AddField("gold_CashExchange", instance.GoldSpent_CashExchange.ToString()); wwwform.AddField("gold_CraneHire", instance.GoldSpent_CraneHire.ToString()); wwwform.AddField("gold_CranePurchase", instance.GoldSpent_CranePurchase.ToString()); wwwform.AddField("gold_CraneQueue", instance.GoldSpent_CraneQueue.ToString()); wwwform.AddField("gold_Expansions", instance.GoldSpent_Expansions.ToString()); wwwform.AddField("gold_GoldBuildings", instance.GoldSpent_GoldBuildings.ToString()); wwwform.AddField("gold_ImmediateBuild", instance.GoldSpent_ImmediateBuild.ToString()); wwwform.AddField("gold_Speedups", instance.GoldSpent_Speedups.ToString()); wwwform.AddField("cash_BulldozerQueue", instance.CashSpent_BulldozerQueue.ToString()); wwwform.AddField("gold_Ports", instance.GoldEarned_Ports.ToString()); CIGBuilderManager instanceIfAvailable = SingletonMonobehaviour.InstanceIfAvailable; if (instanceIfAvailable != null) { wwwform.AddField("cranes", instanceIfAvailable.MaxBuildCount); } string value2 = "null"; DLCManager dlcmanager = ServiceLocator.Find(); if (dlcmanager != null) { value2 = ((!dlcmanager.IsEnabled) ? "disabled" : "enabled"); } wwwform.AddField("dlc_status", value2); wwwform.AddField("videos_watched", instance.NumberOfVideosWatched.ToString()); wwwform.AddField("total_roads", instance.TotalNumberOfRoads); wwwform.AddField("total_decorations", instance.TotalNumberOfDecorations); wwwform.AddField("total_max_level_buildings", instance.TotalNumberOfMaxLevelBuildings); wwwform.AddField("total_unique_buildings", instance.GetUniqueBuildingTypeCount()); CIGVisitingIslandsManager instance2 = SingletonMonobehaviour.Instance; wwwform.AddField("total_islands_synced", instance2.SentIslandCount); } CIGGameState instance3 = SingletonMonobehaviour.Instance; wwwform.AddField("xp", instance3.Balance.Round(Currencies.RoundingMethod.Nearest).GetValue("XP").ToString()); wwwform.AddField("level", instance3.Level); wwwform.AddField("islands_unlocked", instance3.GetIslandsUnlockedCount().ToString()); wwwform.AddField("cash", instance3.Balance.Round(Currencies.RoundingMethod.Nearest).GetValue("Cash").ToString()); wwwform.AddField("gold", instance3.Balance.Round(Currencies.RoundingMethod.Nearest).GetValue("Gold").ToString()); wwwform.AddField("cashspent", instance3.CurrenciesSpent.Round(Currencies.RoundingMethod.Nearest).GetValue("Cash").ToString()); wwwform.AddField("goldspent", instance3.CurrenciesSpent.Round(Currencies.RoundingMethod.Nearest).GetValue("Gold").ToString()); wwwform.AddField("minutesplayed", instance3.TotalMinutesPlayed.ToString()); wwwform.AddField("sessions", this._sessionsPlayed.ToString()); return wwwform; } private void HandlePullResponse(string response) { Dictionary dictionary = Parser.ParsePropertyFileFormat(response); this.UserKey = dictionary.GetString("userKey", string.Empty); if (!string.IsNullOrEmpty(this.UserKey)) { } this.UserId = dictionary.GetLong("userId", -1L); if (dictionary.ContainsKey("undelivered")) { int num; this.Undelivered += CIGWebService.ParseCurrencies(dictionary["undelivered"], out num); if (num != 0 && (num < 5 || num > 15)) { UnityEngine.Debug.LogError(string.Format("B-value from ParseCurrencies is bad ({0}).", num)); } else { CIGBuilderManager instanceIfAvailable = SingletonMonobehaviour.InstanceIfAvailable; if (instanceIfAvailable != null) { while (instanceIfAvailable.MaxBuildCount < num) { instanceIfAvailable.AddCrane(false); } } else { UnityEngine.Debug.LogError(string.Format("Received undelivered B-value of {0}, but we have no Builder Manager.", num)); } } } this.UseIAPDeal = dictionary.GetBoolean("allow_iap_deals", true); this.DisableIAPSignValidation = dictionary.GetBoolean("disable_sign_validation", false); this.UpdateAvailable = dictionary.GetBoolean("update_available", false); this.Sale = dictionary.GetString("saletype", string.Empty); string s; double value; if (dictionary.TryGetValue("saleseconds", out s) && double.TryParse(s, out value)) { this.SaleExpiry = DateTime.UtcNow.AddSeconds(value); } this.IslandSyncChangePercent = dictionary.GetInt("islandSyncChangePercent", 5); this.UseBadge = dictionary.GetBoolean("use_badge", true); int @int = dictionary.GetInt("cheater_value", -1); if (@int != -1 && this._cheaterType != @int) { this._cheaterType = @int; } this.VideoSpeedupEnabled = dictionary.GetBoolean("video_speedup_building_enabled", false); this.VideoSpeedupMaxCurrency = dictionary.GetInt("video_speedup_building_max_gold_cost", 2); this.VideoSpeedupMinSeconds = dictionary.GetInt("video_speedup_building_min_seconds", 60); this._timeDiffWithClientInMinutes = (CIGWebService.UnixTimestamp - dictionary.GetLong("time", 0L)) / 60L; string @string = dictionary.GetString("adjust_registered_token", null); string string2 = dictionary.GetString("adjust_tokens", null); if (!string.IsNullOrEmpty(string2)) { EdwinServerEventHandler.HandleAdjust(string2, @string); } this._lastResponse = response; this._properties = dictionary; this._lastPullRequestTime = (long)Timing.UtcNow; this.serializing.Serialize(); this.HasPulled = true; if (this.PullRequestCompleted != null) { this.PullRequestCompleted(); } this.Multipliers.HandlePullResponse(dictionary); } public const string AndroidApiDomain = "https://gamesapi.sparklingsociety.net/cig3-google/"; public const string IOSApiDomain = "https://gamesapi.sparklingsociety.net/cig3-ios/"; public const string WindowsApiDomain = "https://gamesapi.sparklingsociety.net/cig3-windows/"; public const string CafeBazaarApiDomain = "https://gamesapi.sparklingsociety.net/cig3-cafebazaar/"; public const string OSXApiDomain = "https://gamesapi.sparklingsociety.net/cig3-osx/"; public const string AmazonApiDomain = "https://gamesapi.sparklingsociety.net/cig3-amazon/"; private const long TimeBetweenPullRequests = 900L; private const long MinimumWaitTime = 10L; private const long MinimumExtraWaitTime = 60L; private const long MaximumExtraWaitTime = 600L; private const float DefaultWebTimeoutSeconds = 5f; private static readonly DateTime UnixOriginTimestamp = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); private const int CheaterTypeNotSet = -1; private const int DefaultIslandSyncChangePercent = 5; private const bool DefaultVideoSpeedupEnabled = false; private const int DefaultVideoSpeedupMaxCurrency = 2; private const int DefaultVideoSpeedupMinSeconds = 60; private const string ServerUNIXTimeStampKey = "time"; private const string UpdateAvailableKey = "update_available"; private const string SalesTypeKey = "saletype"; private const string SalesSecondsKey = "saleseconds"; private const string UseBadgeServerKey = "use_badge"; private const string AdjustRegisteredTokenKey = "adjust_registered_token"; private const string AdjustTokensKey = "adjust_tokens"; private const string LastRequestKey = "lastRequest"; private const string LastResponseKey = "lastResponse"; private const string AdjustTokensSeenKey = "adjust_tokens_seen"; private const string TimeDiffWithClientInMinutesKey = "timeDiffWithClientInMinutes"; private const string UserIdKey = "userId"; private const string UserKeyKey = "userKey"; private const string SessionsPlayedKey = "sessionsPlayed"; private const string UndeliveredKey = "undelivered"; private const string SaleKey = "sale"; private const string SaleExpiryKey = "saleExpiry"; private const string IslandSyncChangePercentKey = "islandSyncChangePercent"; private const string UseIAPDealsKey = "allow_iap_deals"; private const string DisableIAPSignValidationKey = "disable_sign_validation"; private const string UseBadgeKey = "useBadge"; private const string CheaterTypeKey = "cheater_value"; private const string VideoSpeedupEnabledKey = "video_speedup_building_enabled"; private const string VideoSpeedupMaxCurrencyKey = "video_speedup_building_max_gold_cost"; private const string VideoSpeedupMinSecondsKey = "video_speedup_building_min_seconds"; [SerializeField] private Serializing serializing; private long _timeDiffWithClientInMinutes; private IEnumerator _pullRequestRoutine; private IEnumerator _waitEnumerator; private int _sessionsPlayed; private long _lastPullRequestTime; private long _extraWaitTime; private Dictionary _properties = new Dictionary(); private string _lastResponse = string.Empty; private int _cheaterType = -1; private static string _apiDomain = null; public delegate void PullRequestCompletedHandler(); public struct WWWResponse { public WWWResponse(bool timedout, string error, string text) { this.TimedOut = timedout; this.Error = error; this.Text = text; } public readonly bool TimedOut; public readonly string Error; public readonly string Text; } }