public IEnumerator StopHeat(string hash, HeatModel heat)
        {
            if (pendingRequest)
            {
                Debug.Log("[BDAScoreClient] Request already pending");
                yield break;
            }
            pendingRequest = true;

            this.activeHeat = null;

            string uri = string.Format("{0}/competitions/{1}/heats/{2}/stop", baseUrl, hash, heat.id);

            using (UnityWebRequest webRequest = new UnityWebRequest(uri))
            {
                yield return(webRequest.SendWebRequest());

                if (!webRequest.isHttpError)
                {
                    Debug.Log(string.Format("[BDAScoreClient] Stopped heat {1} in {0}", hash, heat.order));
                }
                else
                {
                    Debug.Log(string.Format("[BDAScoreClient] Failed to stop heat {1} in {0}: {2}", hash, heat.order, webRequest.error));
                }
            }

            pendingRequest = false;
        }
        public IEnumerator StartHeat(string hash, HeatModel heat)
        {
            if (pendingRequest)
            {
                Debug.Log("[BDArmory.BDAScoreClient] Request already pending");
                yield break;
            }
            pendingRequest = true;

            this.activeHeat = heat;
            UI.RemoteOrchestrationWindow.Instance.UpdateClientStatus();

            string uri = string.Format("{0}/competitions/{1}/heats/{2}/start", baseUrl, hash, heat.id);

            using (UnityWebRequest webRequest = new UnityWebRequest(uri))
            {
                yield return(webRequest.SendWebRequest());

                if (!webRequest.isHttpError)
                {
                    Debug.Log(string.Format("[BDArmory.BDAScoreClient] Started heat {1} in {0}", hash, heat.order));
                }
                else
                {
                    Debug.Log(string.Format("[BDArmory.BDAScoreClient] Failed to start heat {1} in {0}: {2}", hash, heat.order, webRequest.error));
                }
            }

            pendingRequest = false;
        }
        public IEnumerator GetVessels(string hash, HeatModel heat)
        {
            if (pendingRequest)
            {
                Debug.Log("[BDAScoreClient] Request already pending");
                yield break;
            }
            pendingRequest = true;

            string uri = string.Format("{0}/competitions/{1}/heats/{2}/vessels.csv", baseUrl, hash, heat.id);

            Debug.Log(string.Format("[BDAScoreClient] GET {0}", uri));
            using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
            {
                yield return(webRequest.SendWebRequest());

                if (!webRequest.isHttpError)
                {
                    ReceiveVessels(webRequest.downloadHandler.text);
                }
                else
                {
                    Debug.Log(string.Format("[BDAScoreClient] Failed to get vessels {0}/{1}: {2}", hash, heat, webRequest.error));
                }
            }

            pendingRequest = false;
        }
        private IEnumerator FetchAndExecuteHeat(string hash, HeatModel model)
        {
            status = StatusType.FetchingVessels;
            // fetch vessel metadata for heat
            yield return(client.GetVessels(hash, model));

            status = StatusType.DownloadingCraftFiles;
            // fetch craft files for vessels
            yield return(client.GetCraftFiles(hash, model));

            status = StatusType.StartingHeat;
            // notify web service to start heat
            yield return(client.StartHeat(hash, model));

            // execute heat
            int attempts = 0;

            competitionStarted = false;
            while (!competitionStarted && attempts++ < 3) // 3 attempts is plenty
            {
                yield return(ExecuteHeat(hash, model));

                if (!competitionStarted)
                {
                    switch (Control.VesselSpawner.Instance.spawnFailureReason)
                    {
                    case Control.VesselSpawner.SpawnFailureReason.None:     // Successful spawning, but competition failed to start for some reason.
                        BDACompetitionMode.Instance.competitionStatus.Add("Failed to start heat due to " + BDACompetitionMode.Instance.competitionStartFailureReason + ", trying again.");
                        break;

                    case Control.VesselSpawner.SpawnFailureReason.VesselLostParts: // Recoverable spawning failure.
                    case Control.VesselSpawner.SpawnFailureReason.TimedOut:        // Recoverable spawning failure.
                        BDACompetitionMode.Instance.competitionStatus.Add("Failed to start heat due to " + Control.VesselSpawner.Instance.spawnFailureReason + ", trying again.");
                        break;

                    default:     // Spawning is unrecoverable.
                        BDACompetitionMode.Instance.competitionStatus.Add("Failed to start heat due to " + Control.VesselSpawner.Instance.spawnFailureReason + ", aborting.");
                        attempts = 3;
                        break;
                    }
                }
            }

            status = StatusType.ReportingResults;
            // report scores
            yield return(SendScores(hash, model));

            status = StatusType.StoppingHeat;
            // notify web service to stop heat
            yield return(client.StopHeat(hash, model));

            status = StatusType.Waiting;
            yield return(RetryFind(hash));
        }
        public IEnumerator GetCraftFiles(string hash, HeatModel model)
        {
            pendingRequest = true;
            // DO NOT DELETE THE DIRECTORY. Delete the craft files inside it.
            // This is much safer.
            if (Directory.Exists(vesselPath))
            {
                Debug.Log("[BDArmory.BDAScoreClient] Deleting existing craft in spawn directory " + vesselPath);
                DirectoryInfo info       = new DirectoryInfo(vesselPath);
                FileInfo[]    craftFiles = info.GetFiles("*.craft")
                                           .Where(e => e.Extension == ".craft")
                                           .ToArray();
                foreach (FileInfo file in craftFiles)
                {
                    File.Delete(file.FullName);
                }
            }
            else
            {
                Directory.CreateDirectory(vesselPath);
            }

            playerVessels.Clear();
            // already have the vessels in memory; just need to fetch the files
            foreach (VesselModel v in vessels.Values)
            {
                Debug.Log(string.Format("[BDArmory.BDAScoreClient] GET {0}", v.craft_url));
                using (UnityWebRequest webRequest = UnityWebRequest.Get(v.craft_url))
                {
                    yield return(webRequest.SendWebRequest());

                    if (!webRequest.isHttpError)
                    {
                        byte[] rawBytes = webRequest.downloadHandler.data;
                        SaveCraftFile(v, rawBytes);
                    }
                    else
                    {
                        Debug.Log(string.Format("[BDArmory.BDAScoreClient] Failed to get craft for {0}: {1}", v.id, webRequest.error));
                    }
                }
            }
            pendingRequest = false;
        }
        private void ReceiveHeats(string response)
        {
            if (response == null || "".Equals(response))
            {
                Debug.Log(string.Format("[BDAScoreClient] Received empty heat collection response"));
                return;
            }
            List <HeatModel> collection = HeatModel.FromCsv(response);

            heats.Clear();
            if (collection == null)
            {
                Debug.Log(string.Format("[BDAScoreClient] Failed to parse heat collection: {0}", response));
                return;
            }
            foreach (HeatModel heatModel in collection)
            {
                Debug.Log(string.Format("[BDAScoreClient] Heat: {0}", heatModel.ToString()));
                heats.Add(heatModel.id, heatModel);
            }
            Debug.Log(string.Format("[BDAScoreClient] Heats: {0}", heats.Count));
        }
        private IEnumerator FindNextHeat(string hash)
        {
            Debug.Log(string.Format("[BDAScoreService] Find next heat for {0}", hash));

            status = StatusType.FetchingHeat;
            // fetch heat metadata
            yield return(client.GetHeats(hash));

            // find an unstarted heat
            HeatModel model = client.heats.Values.FirstOrDefault(e => e.Available());

            if (model == null)
            {
                status = StatusType.StalledNoPendingHeats;
                Debug.Log(string.Format("[BDAScoreService] No inactive heat found {0}", hash));
                yield return(RetryFind(hash));
            }
            else
            {
                Debug.Log(string.Format("[BDAScoreService] Found heat {1} in {0}", hash, model.order));
                yield return(FetchAndExecuteHeat(hash, model));
            }
        }
Example #8
0
        public static List <HeatModel> FromCsv(string csv)
        {
            List <HeatModel> results = new List <HeatModel>();

            string[] lines = csv.Split('\n');
            if (lines.Length > 0)
            {
                for (var k = 1; k < lines.Length; k++)
                {
                    //                    Debug.Log(string.Format("[BDArmory.BDAScoreModels]: HeatModel.FromCsv line {0}", lines[k]));
                    if (!lines[k].Contains(","))
                    {
                        continue;
                    }
                    try
                    {
                        string[] values = lines[k].Split(',');
                        if (values.Length > 0)
                        {
                            HeatModel model = new HeatModel();
                            model.id             = int.Parse(values[0]);
                            model.competition_id = int.Parse(values[1]);
                            model.stage          = int.Parse(values[2]);
                            model.order          = int.Parse(values[3]);
                            model.started_at     = values[4];
                            model.ended_at       = values[5];
                            results.Add(model);
                        }
                    }
                    catch (Exception e)
                    {
                        Debug.Log("[BDArmory.BDAScoreModels]: HeatModel.FromCsv error: " + e);
                    }
                }
            }
            return(results);
        }
        private List <RecordModel> BuildRecords(string hash, HeatModel heat)
        {
            List <RecordModel> results = new List <RecordModel>();
            var playerNames            = activePlayers;

            Debug.Log(string.Format("[BDAScoreService] Building records for {0} players", playerNames.Count));
            foreach (string playerName in playerNames)
            {
                var separatorIndex = playerName.IndexOf('_');
                if (separatorIndex < 0)
                {
                    Debug.Log(string.Format("[BDAScoreService] Unmatched player {0}", playerName));
                    Debug.Log("DEBUG players were " + string.Join(", ", client.players.Values));
                    continue;
                }

                var         playerNamePart = playerName.Substring(0, separatorIndex);
                PlayerModel player         = client.players.Values.FirstOrDefault(e => e.name == playerNamePart);
                if (player == null)
                {
                    Debug.Log(string.Format("[BDAScoreService] Unmatched player {0}", playerNamePart));
                    Debug.Log("DEBUG players were " + string.Join(", ", client.players.Values));
                    continue;
                }

                var         vesselNamePart = playerName.Substring(separatorIndex + 1);
                VesselModel vessel         = client.vessels.Values.FirstOrDefault(e => e.player_id == player.id && e.name == vesselNamePart);
                if (vessel == null)
                {
                    Debug.Log(string.Format("[BDAScoreService] Unmatched vessel for playerId {0}", player.id));
                    Debug.Log("DEBUG vessels were " + string.Join(", ", client.vessels.Values.Select(p => p.id)));
                    continue;
                }

                RecordModel record = new RecordModel();
                record.vessel_id      = vessel.id;
                record.competition_id = int.Parse(hash);
                record.heat_id        = heat.id;
                record.hits_out       = ComputeTotalHitsOut(playerName);
                record.hits_in        = ComputeTotalHitsIn(playerName);
                record.dmg_out        = ComputeTotalDamageOut(playerName);
                record.dmg_in         = ComputeTotalDamageIn(playerName);
                record.ram_parts_out  = ComputeTotalRammedPartsOut(playerName);
                record.ram_parts_in   = ComputeTotalRammedPartsIn(playerName);
                record.mis_parts_out  = ComputeTotalMissilePartsOut(playerName);
                record.mis_parts_in   = ComputeTotalMissilePartsIn(playerName);
                record.mis_dmg_out    = ComputeTotalMissileDamageOut(playerName);
                record.mis_dmg_in     = ComputeTotalMissileDamageIn(playerName);
                record.wins           = ComputeWins(playerName);
                record.kills          = ComputeTotalKills(playerName);
                record.deaths         = ComputeTotalDeaths(playerName);
                record.assists        = ComputeTotalAssists(playerName);
                record.death_order    = ComputeDeathOrder(playerName);
                record.death_time     = ComputeDeathTime(playerName);
                if (longestHitDistance.ContainsKey(playerName) && longestHitWeapon.ContainsKey(playerName))
                {
                    record.distance = (float)longestHitDistance[playerName];
                    record.weapon   = longestHitWeapon[playerName];
                }
                results.Add(record);
            }
            Debug.Log(string.Format("[BDAScoreService] Built records for {0} players", results.Count));
            return(results);
        }
        private IEnumerator SendScores(string hash, HeatModel heat)
        {
            var records = BuildRecords(hash, heat);

            yield return(client.PostRecords(hash, heat.id, records.ToList()));
        }
        private IEnumerator ExecuteHeat(string hash, HeatModel model)
        {
            Debug.Log(string.Format("[BDAScoreService] Running heat {0}/{1}", hash, model.order));
            Control.VesselSpawner spawner = Control.VesselSpawner.Instance;

            // orchestrate the match
            activePlayers.Clear();
            assists.Clear();
            damageIn.Clear();
            damageOut.Clear();
            deaths.Clear();
            hitsIn.Clear();
            hitsOnTarget.Clear();
            hitsOut.Clear();
            killsOnTarget.Clear();
            longestHitDistance.Clear();
            longestHitWeapon.Clear();
            missileDamageIn.Clear();
            missileDamageOut.Clear();
            missilePartsIn.Clear();
            missilePartsOut.Clear();
            rammedPartsIn.Clear();
            rammedPartsOut.Clear();

            status = StatusType.SpawningVessels;
            spawner.SpawnAllVesselsOnce(BDArmorySettings.VESSEL_SPAWN_GEOCOORDS.x, BDArmorySettings.VESSEL_SPAWN_GEOCOORDS.y, BDArmorySettings.VESSEL_SPAWN_ALTITUDE, BDArmorySettings.VESSEL_SPAWN_DISTANCE_TOGGLE ? BDArmorySettings.VESSEL_SPAWN_DISTANCE : BDArmorySettings.VESSEL_SPAWN_DISTANCE_FACTOR, BDArmorySettings.VESSEL_SPAWN_DISTANCE_TOGGLE, BDArmorySettings.VESSEL_SPAWN_EASE_IN_SPEED, true, true, 0, null, null, hash);
            while (spawner.vesselsSpawning)
            {
                yield return(new WaitForFixedUpdate());
            }
            if (!spawner.vesselSpawnSuccess)
            {
                Debug.Log("[BDAScoreService] Vessel spawning failed.");
                yield break;
            }
            yield return(new WaitForFixedUpdate());

            status = StatusType.RunningHeat;
            // NOTE: runs in separate coroutine
            BDACompetitionMode.Instance.StartCompetitionMode(BDArmorySettings.COMPETITION_DISTANCE);
            yield return(new WaitForFixedUpdate()); // Give the competition start a frame to get going.

            // start timer coroutine for the duration specified in settings UI
            var duration = Core.BDArmorySettings.COMPETITION_DURATION * 60d;
            var message  = "Starting " + (duration > 0 ? "a " + duration.ToString("F0") + "s" : "an unlimited") + " duration competition.";

            Debug.Log("[BDAScoreService]: " + message);
            BDACompetitionMode.Instance.competitionStatus.Add(message);
            while (BDACompetitionMode.Instance.competitionStarting)
            {
                yield return(new WaitForFixedUpdate()); // Wait for the competition to actually start.
            }
            if (!BDACompetitionMode.Instance.competitionIsActive)
            {
                message = "Competition failed to start for heat " + hash + ".";
                BDACompetitionMode.Instance.competitionStatus.Add(message);
                Debug.Log("[BDAScoreService]: " + message);
                yield break;
            }
            competitionStarted = true;
            while (BDACompetitionMode.Instance.competitionIsActive) // Wait for the competition to finish (limited duration and log dumping is handled directly by the competition now).
            {
                yield return(new WaitForSeconds(1));
            }
        }