/** * @see \Simresults\Data_Reader::readSessions() */ protected function readSessions() { // Get data $data = json_decode($this->data, TRUE); // Get date preg_match('/\\d{10}/i', $data['Time'], $time_matches); $date = new \DateTime(); $date->setTimestamp($time_matches[0]); $date->setTimezone(new \DateTimeZone(self::$default_timezone)); // Get other settings $other_settings = array(); $known_setting_keys = array('Experience', 'Difficulty', 'FuelUsage', 'MechanicalDamage', 'FlagRules', 'CutRules', 'RaceSeriesFormat', 'WreckerPrevention', 'MandatoryPitstop', 'MandatoryPitstop'); foreach ($known_setting_keys as $setting) { if ($setting_value = Helper::arrayGet($data, $setting)) { $other_settings[$setting] = $setting_value; } } // Init sessions array $sessions = array(); // Gather all sessions foreach ($data['Sessions'] as $session_data) { // Init session $session = Session::createInstance(); // Practice session by default $type = Session::TYPE_PRACTICE; // Check session type switch (strtolower($name = $session_data['Type'])) { case 'qualify': $type = Session::TYPE_QUALIFY; break; case 'warmup': $type = Session::TYPE_WARMUP; break; case 'race': $type = Session::TYPE_RACE; break; } // Set session values $session->setType($type)->setDate($date)->setOtherSettings($other_settings); // Set game $game = new Game(); $game->setName('RaceRoom Racing Experience'); $session->setGame($game); // Set server $server = new Server(); $server->setName(Helper::arrayGet($data, 'Server')); $session->setServer($server); // Set track $track = new Track(); $track->setVenue(Helper::arrayGet($data, 'Track')); $session->setTrack($track); // Get participants and their best lap (only lap) $participants = array(); $players_data = Helper::arrayGet($session_data, 'Players', array()); foreach ($players_data as $player_index => $player_data) { // Create driver $driver = new Driver(); // Has name if ($name = Helper::arrayGet($player_data, 'Username') or $name = Helper::arrayGet($player_data, 'FullName')) { $driver->setName($name); } else { $driver->setName('unknown'); } // Create participant and add driver $participant = Participant::createInstance(); $participant->setDrivers(array($driver))->setPosition(Helper::arrayGet($player_data, 'Position', null)); // Has finish status if ($status = Helper::arrayGet($player_data, 'FinishStatus')) { // Figure out status switch (strtolower($status)) { case 'finished': $participant->setFinishStatus(Participant::FINISH_NORMAL); break; case 'disqualified': $participant->setFinishStatus(Participant::FINISH_DQ); break; default: $participant->setFinishStatus(Participant::FINISH_DNF); break; } } else { $participant->setFinishStatus(Participant::FINISH_NORMAL); } // Has total time if ($total_time = Helper::arrayGet($player_data, 'TotalTime')) { $participant->setTotalTime(round($player_data['TotalTime'] / 1000, 4)); } // Create vehicle and add to participant $vehicle = new Vehicle(); $vehicle->setName(Helper::arrayGet($player_data, 'Car')); $participant->setVehicle($vehicle); // Has laps if ($laps = Helper::arrayGet($player_data, 'RaceSessionLaps')) { foreach ($laps as $lap_key => $lap_data) { // Negative lap time, skip if ($lap_data['Time'] < 0) { continue; } // Init new lap $lap = new Lap(); // Set participant $lap->setParticipant($participant); // Set first driver of participant as lap driver. RR does // not support swapping $lap->setDriver($participant->getDriver()); // Set lap data $lap->setNumber($lap_key + 1); $lap->setPosition($lap_data['Position']); $lap->setPitLap($lap_data['PitStopOccured']); $lap->setTime(round($lap_data['Time'] / 1000, 4)); // Add lap to participant $participant->addLap($lap); } } elseif (0 < ($best_lap = Helper::arrayGet($player_data, 'BestLapTime'))) { // Init new lap $lap = new Lap(); // Set participant $lap->setParticipant($participant); // Set first driver of participant as lap driver. RR does // not support swapping $lap->setDriver($participant->getDriver()); // Set lap number $lap->setNumber(1); // Set lap time in seconds $lap->setTime(round($best_lap / 1000, 4)); // Add lap to participant $participant->addLap($lap); } // Add participant to collection $participants[] = $participant; } // Add participants to session $session->setParticipants($participants); // Add session to collection $sessions[] = $session; } // Return all sessions return $sessions; }
/** * @see \Simresults\Data_Reader::readSessions() */ protected function readSessions() { // Get array data $data = $this->array_data; // Init sessions array $sessions = array(); // Loop each session from data foreach ($data as $session_data) { // Remember which vehicles are parsed $vehicle_names = array(); // Init session $session = Session::createInstance(); // Set session type $type = null; switch ($session_data['type']) { case 'qualify': $type = Session::TYPE_QUALIFY; break; case 'practice': $type = Session::TYPE_PRACTICE; break; case 'warmup': $type = Session::TYPE_PRACTICE; break; case 'race': $type = Session::TYPE_RACE; break; } $session->setType($type); // Set session name if (isset($session_data['name'])) { $session->setName($session_data['name']); } // Set max time if (isset($session_data['time'])) { $session->setMaxMinutes($session_data['time']); } // Set max laps if (isset($session_data['laps'])) { $session->setMaxLaps($session_data['laps']); } // Set game $game = new Game(); $game->setName('Assetto Corsa'); $session->setGame($game); // Has track if (isset($session_data['track'])) { $track = new Track(); $track->setVenue($session_data['track']); $session->setTrack($track); } // Has date if (isset($session_data['date'])) { // Set it $session->setDateString($session_data['date']); } // Set server $server = new Server(); $server->setDedicated(true); if (isset($session_data['server'])) { $server->setName($session_data['server']); } else { $server->setName('Unknown'); } $session->setServer($server); // Add allowed vehicles foreach ($session_data['car_list'] as $vehicle_name) { $vehicle = new Vehicle(); $vehicle->setName($vehicle_name); $session->addAllowedVehicle($vehicle); } // Set chats foreach ($session_data['chats'] as $chat_message) { $chat = new Chat(); $chat->setMessage($chat_message); $session->addChat($chat); } // Set participants $participants = array(); foreach ($session_data['participants'] as $part_data) { // No name if (!Helper::arrayGet($part_data, 'name')) { continue; } // Create driver $driver = new Driver(); $driver->setName($part_data['name']); // Total time not greater than 0 if (0 >= ($total_time = Helper::arrayGet($part_data, 'total_time'))) { // Total time is null $total_time = null; } // Create participant and add driver $participant = Participant::createInstance(); $participant->setDrivers(array($driver))->setTotalTime($total_time); // Has total time parsed data and should not be a forced DNF if ($total_time and !Helper::arrayGet($part_data, 'force_dnf')) { $participant->setFinishStatus(Participant::FINISH_NORMAL); } else { $participant->setFinishStatus(Participant::FINISH_DNF); } // Remember vehicle instances by vehicle name $vehicles = array(); // Create vehicle and add to participant $vehicle = null; if (isset($part_data['vehicle'])) { // Init vehicle $vehicle = new Vehicle(); $vehicle->setName($part_data['vehicle']); $participant->setVehicle($vehicle); // Remember vehicle instance $vehicles[$part_data['vehicle']] = $vehicle; // Remember vehicle names for this entire session $vehicle_names[$part_data['vehicle']] = 1; } // Has team if (isset($part_data['team'])) { $participant->setTeam($part_data['team']); } // Has guid if (isset($part_data['guid'])) { $driver->setDriverId($part_data['guid']); } // Collect laps foreach (Helper::arrayGet($part_data, 'laps', array()) as $lap_i => $lap_data) { // Init new lap $lap = new Lap(); // Set participant $lap->setParticipant($participant); // Set first driver of participant as lap driver. AC does // not support swapping $lap->setDriver($participant->getDriver()); // Set lap number $lap->setNumber($lap_i + 1); // Set lap times $lap->setTime($lap_data['time']); // No lap vehicle if (!$lap_data['vehicle']) { // Just use participant vehicle if it is available if ($vehicle) { $lap->setVehicle($vehicle); } } elseif (isset($vehicles[$v = $lap_data['vehicle']])) { // Set vehicle instance $lap->setVehicle($vehicles[$v]); } else { // Init vehicle $vehicle = new Vehicle(); $vehicle->setName($lap_data['vehicle']); $lap->setVehicle($vehicle); // Remember vehicle $vehicles[$lap_data['vehicle']] = $vehicle; } // Add lap to participant $participant->addLap($lap); } // No laps and race result if (!$participant->getLaps() and $session->getType() === Session::TYPE_RACE) { // Did not finish $participant->setFinishStatus(Participant::FINISH_DNF); } // Add participant to collection $participants[] = $participant; } // Sort participants $this->sortParticipantsAndFixPositions($participants, $session); // Set participants to session $session->setParticipants($participants); // Only one vehicle type in this session if (count($vehicle_names) === 1) { // Find any participant without vehicle and fix missing. // This is an easy last resort fix when parsing was bugged // We assume everybody has this vehicle foreach ($session->getParticipants() as $participant) { if (!$participant->getVehicle()) { // Init vehicle $vehicle = new Vehicle(); $vehicle->setName(key($vehicle_names)); $participant->setVehicle($vehicle); } } } // Add session to collection $sessions[] = $session; } // Return all sessions return $sessions; }
/** * @see \Simresults\Data_Reader::readSessions() */ protected function readSessions() { // Get data $data = json_decode($this->data, TRUE); // No session data if (!($sessions_data = Helper::arrayGet($data, 'sessions'))) { // Throw exception throw new Exception\Reader('Cannot read the session data'); } // Init sessions array $sessions = array(); // Get extra data for all sessions $extras = array(); foreach (Helper::arrayGet($data, 'extras', array()) as $extras_data) { // Get name $name = Helper::arrayGet($extras_data, 'name'); // Loop all values and add as extra settings foreach ($extras_data as $extra_data_key => $extra_data_value) { // Is name if ($extra_data_key === 'name') { // Skip this continue; } // Add to extras collection $extras[ucfirst($name) . ' ' . $extra_data_key] = $extra_data_value; } } // Gather all sessions foreach ($sessions_data as $session_data) { // Init session $session = Session::createInstance(); // Get participants (do for each session to prevent re-used objects // between sessions) $participants = array(); $players_data = Helper::arrayGet($data, 'players', array()); foreach ($players_data as $player_index => $player_data) { // Create driver $driver = new Driver(); $driver->setName(Helper::arrayGet($player_data, 'name')); // Create participant and add driver $participant = Participant::createInstance(); $participant->setDrivers(array($driver))->setFinishStatus(Participant::FINISH_NORMAL); // Create vehicle and add to participant $vehicle = new Vehicle(); $vehicle->setName(Helper::arrayGet($player_data, 'car')); $participant->setVehicle($vehicle); // Add participant to collection $participants[] = $participant; } // Practice session by default $type = Session::TYPE_PRACTICE; // Check session name to get type // TODO: Should be checked when full game is released. Also create // tests for it! switch (strtolower($name = Helper::arrayGet($session_data, 'name'))) { case 'qualify session': case 'qualify': $type = Session::TYPE_QUALIFY; break; case 'warmup session': $type = Session::TYPE_WARMUP; break; case 'race session': case 'quick race': case 'race': $type = Session::TYPE_RACE; break; } // Set session values $session->setType($type)->setName($name)->setMaxLaps((int) Helper::arrayGet($session_data, 'lapsCount'))->setMaxMinutes((int) Helper::arrayGet($session_data, 'duration')); // Set game $game = new Game(); $game->setName('Assetto Corsa'); $session->setGame($game); // Set server (we do not know...) $server = new Server(); $server->setName('Unknown or offline'); $session->setServer($server); // Set track $track = new Track(); $track->setVenue(Helper::arrayGet($data, 'track')); $session->setTrack($track); // Get the laps $laps_data = Helper::arrayGet($session_data, 'laps', array()); // No laps data if (!$laps_data) { // Use best laps if possible $laps_data = Helper::arrayGet($session_data, 'bestLaps', array()); } // Process laps foreach ($laps_data as $lap_data) { // Init new lap $lap = new Lap(); // Set participant $lap->setParticipant($lap_participant = $participants[$lap_data['car']]); // Set first driver of participant as lap driver. AC does // not support swapping $lap->setDriver($lap_participant->getDriver()); // Set lap number (+1 because AC is zero based) $lap->setNumber($lap_data['lap'] + 1); // Set lap time in seconds $lap->setTime(round($lap_data['time'] / 1000, 4)); // Set sector times in seconds foreach (Helper::arrayGet($lap_data, 'sectors', array()) as $sector_time) { $lap->addSectorTime(round($sector_time / 1000, 4)); } // Add lap to participant $lap_participant->addLap($lap); } // Session has predefined race result positions if ($race_result = Helper::arrayGet($session_data, 'raceResult')) { // Create new participants order $participants_sorted = array(); foreach ($race_result as $race_position => $race_position_driver) { $participants_sorted[] = $participants[$race_position_driver]; } $participants = $participants_sorted; } else { // Sort participants $this->sortParticipantsAndFixPositions($participants, $session); } // Add extras to session $session->setOtherSettings($extras); // Set participants (sorted) $session->setParticipants($participants); // Add session to collection $sessions[] = $session; } // Return all sessions return $sessions; }
/** * @see \Simresults\Data_Reader::readSessions() */ protected function readSessions() { // Get data $data = json_decode($this->data, TRUE); // Init session $session = Session::createInstance(); // Practice session by default $type = Session::TYPE_PRACTICE; // Check session name to get type // TODO: Could we prevent duplicate code for this with other readers? switch (strtolower($name = Helper::arrayGet($data, 'Type'))) { case 'qualify session': case 'qualify': $type = Session::TYPE_QUALIFY; break; case 'warmup session': $type = Session::TYPE_WARMUP; break; case 'race session': case 'quick race': case 'race': $type = Session::TYPE_RACE; break; } // Set session values $session->setType($type)->setName($name)->setMaxLaps((int) Helper::arrayGet($data, 'RaceLaps')); // Has Duration if ($seconds = (int) Helper::arrayGet($data, 'DurationSecs')) { $session->setMaxMinutes(round($seconds / 60)); } else { $session->setMaxMinutes(0); } // Set game $game = new Game(); $game->setName('Assetto Corsa'); $session->setGame($game); // Set server (we do not know...) $server = new Server(); $server->setName('Unknown'); $session->setServer($server); // Set track $track = new Track(); $track->setVenue(Helper::arrayGet($data, 'TrackName')); $session->setTrack($track); // Get participants from Cars data $participants_by_name = array(); $players_data = Helper::arrayGet($data, 'Cars', array()); foreach ($players_data as $player_index => $player_data) { // Build participant $participant = $this->getParticipant($name = $player_data['Driver']['Name'], $player_data['Driver']['Guid'], $player_data['Model'], $player_data['Driver']['Team'], $player_data['BallastKG'], $player_data['Skin']); // Add participant to collection $participants_by_name[$name] = $participant; } // Get participants from result data. // WARNING: This should be orded by position but these logs are BUGGED. // DO NOT TRUST! $players_data = Helper::arrayGet($data, 'Result', array()); foreach ($players_data as $player_index => $player_data) { // No participant found if (!isset($participants_by_name[$player_data['DriverName']])) { // Build participant $participant = $this->getParticipant($name = $player_data['DriverName'], $player_data['DriverGuid'], $player_data['CarModel'], null, $player_data['BallastKG']); // Add participant to collection $participants_by_name[$name] = $participant; } $participant = $participants_by_name[$player_data['DriverName']]; // Total time available if ($total_time = $player_data['TotalTime']) { $participant->setTotalTime(round($total_time / 1000, 4)); } else { // DNF $participant->setFinishStatus(Participant::FINISH_DNF); } // Set total time and position (but we can't trust, so we will // fix later again) $participant->setPosition($player_index + 1); } // Process laps foreach ($data['Laps'] as $lap_data) { // Init new lap $lap = new Lap(); // No participant found if (!isset($participants_by_name[$lap_data['DriverName']])) { // Build participant $participant = $this->getParticipant($name = $lap_data['DriverName'], $lap_data['DriverGuid'], $lap_data['CarModel'], null, $lap_data['BallastKG']); // Add participant to collection $participants_by_name[$name] = $participant; } $lap_participant = $participants_by_name[$lap_data['DriverName']]; // Set participant $lap->setParticipant($lap_participant); // Set first driver of participant as lap driver. AC does // not support swapping $lap->setDriver($lap_participant->getDriver()); // Set lap time in seconds if ($lap_data['LapTime'] !== 99999) { $lap->setTime(round($lap_data['LapTime'] / 1000, 4)); } // Set sector times in seconds foreach (Helper::arrayGet($lap_data, 'Sectors', array()) as $sector_time) { $lap->addSectorTime(round($sector_time / 1000, 4)); } // Add lap to participant $lap_participant->addLap($lap); } // Get car incidents from events if ($data['Events']) { foreach ($data['Events'] as $event) { // Not car collision. continue to next if ($event['Type'] !== 'COLLISION_WITH_CAR') { continue; } $incident = new Incident(); $incident->setMessage(sprintf('%s reported contact with another vehicle ' . '%s. Impact speed: %s', $event['Driver']['Name'], $event['OtherDriver']['Name'], $event['ImpactSpeed'])); $session->addIncident($incident); } } /** * Data fixing */ // Get participant with normal array keys $participants = array_values($participants_by_name); // Sort participants $this->sortParticipantsAndFixPositions($participants, $session); // Set participants (sorted) $session->setParticipants($participants); // Return session return array($session); }
/** * @see \Simresults\Data_Reader::readSessions() */ protected function readSessions() { // Get data $data = json_decode(self::cleanJSON($this->data), TRUE); $data = $data['stats']; // No session data if (!($history_data = $data['history'])) { // Throw exception throw new Exception\Reader('Cannot read the session data'); } // Remove sessions without stages $history_data = array_filter($history_data, function ($data) { return (bool) $data['stages']; }); // Get attribute info of project cars to figure out vehicle names etc $this->attribute_names = $this->getAttributeNames(); // Init sessions array $sessions = array(); // Loop each history item foreach ($history_data as $history) { /** * Collect all participants */ $initial_participants_by_ref = array(); // Depricated! TODO: Remove $initial_participants_by_id = array(); // Loop all member entries and create participants foreach ($history['members'] as $part_ref => $part_data) { // Get participant $participant = $this->getParticipant($part_data); // Add participant to collection // $initial_participants_by_ref[$part_ref] = $participant; $initial_participants_by_id[$part_data['participantid']] = $participant; } // Get additional info from participants entries // Disabled due to duplicate refids bugs 2015-12-14 // foreach ($history['participants'] as $part_data) // { // // Get previously parsed participant // $participant = $initial_participants_by_ref[ // $part_data['RefId']]; // // Set whether participant is human // $participant->getDriver()->setHuman( // (bool) $part_data['IsPlayer']); // } // Get server configuration $read_settings = array('DamageType', 'FuelUsageType', 'PenaltiesType', 'ServerControlsSetup', 'ServerControlsTrack', 'ServerControlsVehicle', 'ServerControlsVehicleClass', 'TireWearType'); $session_settings = array(); foreach ($history['setup'] as $setup_key => $setup_value) { if (in_array($setup_key, $read_settings)) { $session_settings[$setup_key] = $setup_value; } } // Loop all stages data foreach ($history['stages'] as $type_key => $session_data) { // Make new unique array of participants to prevent reference // issues across multiple sessions $participants_by_ref = array(); $participants_by_id = array(); foreach ($initial_participants_by_ref as $part_key => $part) { $participants_by_ref[$part_key] = clone $part; } foreach ($initial_participants_by_id as $part_key => $part) { $participants_by_id[$part_key] = clone $part; } // Init session $session = Session::createInstance(); // Practice session by default $type = Session::TYPE_PRACTICE; // Setup name for session type $type_setup_name = ucfirst($type_key); // Check session name to get type // TODO: Could we prevent duplicate code for this with other readers? switch (strtolower(preg_replace('#\\d#', '', $type_key))) { case 'qualifying': $type = Session::TYPE_QUALIFY; $type_setup_name = 'Qualify'; break; case 'warmup': $type = Session::TYPE_WARMUP; break; case 'race': $type = Session::TYPE_RACE; break; } // Date of this session $date = new \DateTime(); $date->setTimestamp($session_data['start_time']); $date->setTimezone(new \DateTimeZone(self::$default_timezone)); // Set session values $session->setType($type)->setName($type_key)->setMaxLaps($history['setup'][$type_setup_name . 'Length'])->setDate($date)->setOtherSettings($session_settings); // Set game $game = new Game(); $game->setName('Project Cars'); $session->setGame($game); // Set server // TODO: Set configurations $server = new Server(); $server->setName($data['server']['name']); $session->setServer($server); // Set track $track = new Track(); // Have friendly track name if (isset($this->attribute_names['tracks'][$history['setup']['TrackId']])) { $track->setVenue($this->attribute_names['tracks'][$history['setup']['TrackId']]['name']); } else { // TODO: We should test this works too? Same for vehicles // when our json attribute config is missing items $track->setVenue((string) $history['setup']['TrackId']); } $session->setTrack($track); // Remember participants with actual events $participants_with_events = array(); // Parse events first only to collect missing participants foreach ($session_data['events'] as $event) { // Participant unknown if (!isset($participants_by_id[$event['participantid']])) { // Build it and fallback to less info $part = $this->getParticipant($event); $participants_by_id[$event['participantid']] = $part; } } // Parse event data such as laps $cut_data = array(); foreach ($session_data['events'] as $event) { // Get participant $part = $participants_by_id[$event['participantid']]; // Remember this participant $participants_with_events[] = $part; // Is lap and the lap is valid (only checked for non-race) if ($event['event_name'] === 'Lap' and ($session->getType() === Session::TYPE_RACE or $event['attributes']['CountThisLapTimes'])) { // Init new lap $lap = new Lap(); // Set participant $lap->setParticipant($part); // Set first driver of participant as lap driver. PJ // does not support swapping $lap->setDriver($part->getDriver()); // Set total time $lap->setTime(round($event['attributes']['LapTime'] / 1000, 4)); // Add sectors for ($sector_i = 1; $sector_i <= 3; $sector_i++) { $lap->addSectorTime(round($event['attributes']['Sector' . $sector_i . 'Time'] / 1000, 4)); } // Set lap position $lap->setPosition($event['attributes']['RacePosition']); // Set number $lap->setNumber($event['attributes']['Lap'] + 1); // Add lap to participant $part->addLap($lap); } elseif ($event['event_name'] === 'Impact') { // Other participant is unknown by default $other_participant_name = 'unknown'; // Other participant known if (-1 != ($other_id = $event['attributes']['OtherParticipantId']) and isset($participants_by_id[$other_id])) { // Set other name $other_participant_name = $participants_by_id[$other_id]->getDriver()->getName(); } else { // Skip for now until we know what -1 means continue; } $incident = new Incident(); $incident->setMessage(sprintf('%s reported contact with another vehicle ' . '%s. CollisionMagnitude: %s', $part->getDriver()->getName(), $other_participant_name, $event['attributes']['CollisionMagnitude'])); // TODO: Add elapsed time $date = new \DateTime(); $date->setTimestamp($event['time']); $incident->setDate($date); $incident->setElapsedSeconds($date->getTimestamp() - $session->getDate()->getTimestamp()); $session->addIncident($incident); } elseif (in_array($event['event_name'], array('CutTrackStart', 'CutTrackEnd'))) { $cut_data[] = $event; } elseif ($event['event_name'] === 'State' and $event['attributes']['NewState'] === 'Retired') { $part->setFinishStatus(Participant::FINISH_DNF); } } /** * TODO: So many duplicate code below regarding results array * reading! Fix this */ // Has results array we can read finish statusses from if ($results = $session_data['results']) { // Loop each result and process the lap foreach ($results as $result) { // Participant not found, continue to next if (!isset($participants_by_id[$result['participantid']])) { continue; } // Has DNF state if (in_array(strtolower($result['attributes']['State']), array('dnf', 'retired'))) { // Get participant $part = $participants_by_id[$result['participantid']]; // Set DNF $part->setFinishStatus(Participant::FINISH_DNF); } } } // We did not have any events data to process but we have // final results. Let's use this data to atleast get 1 best // lap of these participants if (!$session_data['events'] and $results = $session_data['results']) { // Loop each result and process the lap foreach ($results as $result) { // Participant not found, continue to next if (!isset($participants_by_id[$result['participantid']])) { continue; } // Get participant $part = $participants_by_id[$result['participantid']]; // Remember this participant (fake it had events) $participants_with_events[] = $part; // Has best lap if ($result['attributes']['FastestLapTime']) { // Init new lap $lap = new Lap(); // Set participant $lap->setParticipant($part); // Set first driver of participant as lap driver. PJ // does not support swapping $lap->setDriver($part->getDriver()); // Set total time $lap->setTime(round($result['attributes']['FastestLapTime'] / 1000, 4)); // Set number $lap->setNumber(1); // Add lap to participant $part->addLap($lap); } } } /** * Process cut info */ foreach ($cut_data as $key => $event) { // Get participant $part = $participants_by_id[$event['participantid']]; // Start of cut and lap actually exists if ($event['event_name'] === 'CutTrackStart' and $lap = $part->getLap($event['attributes']['Lap'] + 1)) { // Find the end of cutting by looping following events for ($end_key = $key + 1; $end_key < count($cut_data); $end_key++) { $next_event = $cut_data[$end_key]; // Next event is another cut start. Ignore current // cut as theres no proper end data if ($next_event['event_name'] === 'CutTrackStart' and $next_event['participantid'] == $event['participantid']) { // Theres no end break; } elseif ($next_event['event_name'] === 'CutTrackEnd' and $next_event['participantid'] == $event['participantid']) { $cut = new Cut(); $cut->setCutTime(round($next_event['attributes']['ElapsedTime'] / 1000, 4)); $cut->setTimeSkipped(round($next_event['attributes']['SkippedTime'] / 1000, 4)); $date = new \DateTime(); $date->setTimestamp($next_event['time']); $date->setTimezone(new \DateTimeZone(self::$default_timezone)); $cut->setDate($date); $cut->setLap($lap); $cut->setElapsedSeconds($date->getTimestamp() - $session->getDate()->getTimestamp()); $cut->setElapsedSecondsInLap(round($event['attributes']['LapTime'] / 1000, 4)); $lap->addCut($cut); // Stop searching break; } } } } /** * Cleanup */ $participants = $participants_by_id; // Remove any participant who did not participate foreach ($participants as $part_id => $part) { // No laps and not marked as participated // TODO: Make test for strict comparison (true arg), log // is on Project Cars forum for test if (!in_array($part, $participants_with_events, true)) { unset($participants[$part_id]); } } // Get participant with normal array keys $participants = array_values($participants); // Session has predefined race result positions // WARNING: We only do this for race sessions because for // qualify and practice some drivers are missing from the // result if ($results = $session_data['results'] and $session->getType() === Session::TYPE_RACE) { // Sort participants using our own sort $tmp_sort = Helper::sortParticipantsByTotalTime($participants); // Find whether our leading participant using our own sort // is in the result $leading_is_in_result = false; foreach ($results as $result) { // Participant not found, continue to next if (!isset($participants_by_id[$result['participantid']])) { continue; } // Get participant $participant = $participants_by_id[$result['participantid']]; // Leading found if ($participant === $tmp_sort[0]) { $leading_is_in_result = true; } } // Leading participant is in the results array if ($leading_is_in_result) { // Init sorted result array $participants_resultsorted = array(); foreach ($results as $result) { // Participant not found, continue to next if (!isset($participants_by_id[$result['participantid']])) { continue; } // Get participant $participant = $participants_by_id[$result['participantid']]; // Set total time $participant->setTotalTime(round($result['attributes']['TotalTime'] / 1000, 4)); // Add to sorted array and remove from normal array $participants_resultsorted[] = $participant; unset($participants[array_search($participant, $participants, true)]); } // Sort participants not sorted by result by total time $participants = Helper::sortParticipantsByTotalTime($participants); // Merge the sorted participants result with normal sort // array. Merge them and remove any duplicates // NOTE: We are not using array_unique as it's causing // recursive depedency $merged = array_merge($participants_resultsorted, $participants); $final = array(); foreach ($merged as $current) { if (!in_array($current, $final, true)) { $final[] = $current; } } $participants = $final; } else { // Sort participants $this->sortParticipantsAndFixPositions($participants, $session); } } else { // Is race if ($session->getType() === Session::TYPE_RACE) { // Set all participants on unknown finish status // We should of had a result for proper statusses foreach ($participants as $part) { $part->setFinishStatus(Participant::FINISH_NONE); } } // Sort participants $this->sortParticipantsAndFixPositions($participants, $session, TRUE); } // Set participants (sorted) $session->setParticipants($participants); $sessions[] = $session; } // End stages loop } // End history loop // Swap warmup and race positions if wrong $prevous_session = null; foreach ($sessions as $key => $session) { // Found warmup after race session if ($prevous_session and $prevous_session->getType() === Session::TYPE_RACE and $session->getType() === Session::TYPE_WARMUP) { // Swap them $sessions[$key] = $prevous_session; $sessions[$key - 1] = $session; } // Remember previous session $prevous_session = $session; } // Return sessions return $sessions; }
/** * Get participants sorted by ending position * * @return array */ protected function getParticipants() { // Drivers already read if ($this->participants !== null) { // Return already read participants return $this->participants; } // Init drivers array $participants = array(); // Remember all lap positions to detect corruption later $lap_positions = array(); // Remeber all lap instances per lap number so we fix position // corruption on them $all_laps_by_lap_number = array(); // Loop each driver (if any) /* @var $driver_xml \DOMNode */ foreach ($this->dom->getElementsByTagName('Driver') as $driver_xml) { // Create new driver $main_driver = new Driver(); // Get position $position = (int) $this->dom_value('Position', $driver_xml); // Set driver values $main_driver->setName($this->dom_value('Name', $driver_xml))->setHuman((bool) $this->dom_value('isPlayer', $driver_xml)); // Create new vehicle $vehicle = new Vehicle(); // Set vehicle values $vehicle->setName($this->dom_value('VehName', $driver_xml))->setType($this->dom_value('CarType', $driver_xml))->setClass($this->dom_value('CarClass', $driver_xml))->setNumber((int) $this->dom_value('CarNumber', $driver_xml)); // Create participant $participant = Participant::createInstance(); // Set participant values $participant->setTeam($this->dom_value('TeamName', $driver_xml))->setPosition((int) $this->dom_value('Position', $driver_xml))->setClassPosition((int) $this->dom_value('ClassPosition', $driver_xml))->setGridPosition((int) $this->dom_value('GridPos', $driver_xml))->setClassGridPosition((int) $this->dom_value('ClassGridPos', $driver_xml))->setPitstops((int) $this->dom_value('Pitstops', $driver_xml)); // Has finish time if ($finish_time = (double) $this->dom_value('FinishTime', $driver_xml)) { // Overwrite total time, because rfactor results tend to be // corrupted at times $participant->setTotalTime($finish_time); } // Get finish status value $finish_status = strtolower($this->dom_value('FinishStatus', $driver_xml)); // Has finished if ($finish_status === 'finished normally') { // Set finish status $participant->setFinishStatus(Participant::FINISH_NORMAL); } elseif ($finish_status === 'dq') { // Set disqualified status $participant->setFinishStatus(Participant::FINISH_DQ); } elseif ($finish_status === 'dnf') { // Set did not finish status $participant->setFinishStatus(Participant::FINISH_DNF); // Set finish comment (if any) if ($finish_status = $this->dom_value('DNFReason', $driver_xml)) { $participant->setFinishComment($finish_status); } } else { // Set no finish status $participant->setFinishStatus(Participant::FINISH_NONE); } // Get the driver swaps $swaps_xml = $driver_xml->getElementsByTagName('Swap'); // Init drivers array, a participant can have multiple $drivers = array(); // Remember the drivers per laps $drivers_per_laps = array(); // Remember drivers by name so we can re-use them $drivers_by_name = array(); // Remember the number of swaps (always -1 of number of swap // elements in XML, because first driver starts on grid, which is // actually not really a swap) $number_of_swaps = 0; // Loop each swap $first_swap = true; // First swap reminder, can't use $swap_xml_key // to detect because it is bugged in hhvm! foreach ($swaps_xml as $swap_xml_key => $swap_xml) { // Empty driver name if (!($driver_name = $swap_xml->nodeValue)) { // Skip this swap continue; } // Driver already processed if (array_key_exists($driver_name, $drivers_by_name)) { // Use existing found driver instance $swap_driver = $drivers_by_name[$driver_name]; } else { // Create new driver $swap_driver = new Driver(); // Set name $swap_driver->setName($driver_name); // Use human state the same of main driver within XML $swap_driver->setHuman($main_driver->isHuman()); // Add swap driver to drivers array $drivers[] = $swap_driver; // Remember swap driver by name $drivers_by_name[$driver_name] = $swap_driver; } // Add swap driver to drivers per lap $drivers_per_laps[] = array('start_lap' => (int) $swap_xml->getAttribute('startLap'), 'end_lap' => (int) $swap_xml->getAttribute('endLap'), 'driver' => $swap_driver); // Not first swap element, so this is a real swap that happend // within pits if (!$first_swap) { // Increment the number of swaps $number_of_swaps++; } // Not first swap anymore $first_swap = false; } // No drivers yet, so no drivers through swap info if (!$drivers) { // Add main driver to drivers array because we could not get // it from the swap info $drivers[] = $main_driver; } // Pitcounter is lower than number of swaps if ($participant->getPitstops() < $number_of_swaps) { // Set pitstop counter to the number of swaps $participant->setPitstops($number_of_swaps); } // Add vehicle to participant $participant->setVehicle($vehicle); // Add drivers to participant $participant->setDrivers($drivers); // Remember whether the drivers are human or not from the look at // the aids $is_human_by_aids = null; // Get lap aids information and convert to friendly array $aids_xml = $driver_xml->getElementsByTagName('ControlAndAids'); $aids = array(); foreach ($aids_xml as $aid_xml) { // Match the aids $matches = array(); preg_match_all('/([a-z]+)(=([a-z0-9]))?[,]?/i', (string) $aid_xml->nodeValue, $matches); // Prepare aid items array $aid_items = array(); // Loop each matched aid if (isset($matches[1])) { foreach ($matches[1] as $key => $aid_name) { // Get value $aid_value = $matches[3][$key]; // Is float if (is_float($aid_value)) { // Cast to float $aid_value = (double) $aid_value; } elseif (is_numeric($aid_value)) { // Cast to int $aid_value = (int) $aid_value; } // Is a human player if ($aid_name === 'PlayerControl') { // Remember this driver is human $is_human_by_aids = true; } elseif (($aid_name === 'UnknownControl' or $aid_name === 'AIControl') and $is_human_by_aids === null) { // Remember this driver is not human $is_human_by_aids = false; } // Set key => value of aid $aid_items[$aid_name] = $aid_value ? $aid_value : null; } } // Add aid information per lap $aids[] = array('start_lap' => (int) $aid_xml->getAttribute('startLap'), 'end_lap' => (int) $aid_xml->getAttribute('endLap'), 'aids' => $aid_items); } // No aids if (!$aids) { // Always human $is_human_by_aids = true; } //-- Set laps // Loop each available lap /* @var $lap_xml \DOMNode */ foreach ($driver_xml->getElementsByTagName('Lap') as $lap_xml) { // Create new lap $lap = new Lap(); // Lap time zero or lower if (($lap_time = (double) $lap_xml->nodeValue) <= 0.0) { // No lap time $lap_time = null; } // Get lap position and add it to known positions $lap_positions[] = $lap_position = (int) $lap_xml->getAttribute('p'); // Elapsed seconds by default null $elapsed_seconds = null; // Valid value if ($lap_xml->getAttribute('et') !== '--.---') { // Create float value $elapsed_seconds = (double) $lap_xml->getAttribute('et'); } // Default compound values $front_compound = null; $rear_compound = null; // Front compound isset if (($fcompound = $lap_xml->getAttribute('fcompound')) !== '') { $front_compound = $fcompound; } // Rear compound isset if (($rcompound = $lap_xml->getAttribute('rcompound')) !== '') { $rear_compound = $rcompound; } // Has fuel info and is positive $fuel = NULL; if ($fuel_data = (double) $lap_xml->getAttribute('fuel') and $fuel_data > 0) { // Get proper percentage $fuel = $fuel_data * 100; } $front_compound_left_wear = NULL; if ($wear_data = (double) $lap_xml->getAttribute('twfl') and $wear_data > 0) { // Get proper percentage $front_compound_left_wear = $wear_data * 100; } $front_compound_right_wear = NULL; if ($wear_data = (double) $lap_xml->getAttribute('twfr') and $wear_data > 0) { // Get proper percentage $front_compound_right_wear = $wear_data * 100; } $rear_compound_left_wear = NULL; if ($wear_data = (double) $lap_xml->getAttribute('twrl') and $wear_data > 0) { // Get proper percentage $rear_compound_left_wear = $wear_data * 100; } $rear_compound_right_wear = NULL; if ($wear_data = (double) $lap_xml->getAttribute('twrr') and $wear_data > 0) { // Get proper percentage $rear_compound_right_wear = $wear_data * 100; } // Set lap values $lap->setTime($lap_time)->setPosition($lap_position)->setNumber((int) $lap_xml->getAttribute('num'))->setParticipant($participant)->setElapsedSeconds($elapsed_seconds)->setFrontCompound($front_compound)->setRearCompound($rear_compound)->setFrontCompoundLeftWear($front_compound_left_wear)->setFrontCompoundRightWear($front_compound_right_wear)->setRearCompoundLeftWear($rear_compound_left_wear)->setRearCompoundRightWear($rear_compound_right_wear)->setFuel($fuel)->setPitLap((bool) $lap_xml->getAttribute('pit')); // Find lap aids foreach ($aids as $aid) { // Lap match if ($aid['start_lap'] <= $lap->getNumber() and $aid['end_lap'] >= $lap->getNumber()) { // Set aids $lap->setAids($aid['aids']); // Stop searching break; } } // Find lap driver foreach ($drivers_per_laps as $driver_lap) { // Lap match if ($driver_lap['start_lap'] <= $lap->getNumber() and $driver_lap['end_lap'] >= $lap->getNumber()) { // Set driver $lap->setDriver($driver_lap['driver']); // Stop searching break; } } // No driver yet if (!$lap->getDriver()) { // Just put first driver on lap $lap->setDriver($drivers[0]); } // Add each sector available $sector = 1; while ($lap_xml->hasAttribute($sector_attribute = 's' . $sector)) { // Add sector $lap->addSectorTime((double) $lap_xml->getAttribute($sector_attribute)); // Increment number of sector $sector++; } // Add lap to participant $participant->addLap($lap); // Remember lap $all_laps_by_lap_number[$lap->getNumber()][] = $lap; } // Detected human state by aids if ($is_human_by_aids !== null) { // Force human mark on all drivers foreach ($drivers as $driver) { $driver->setHuman($is_human_by_aids); } } // Set driver to drivers array based on his position $participants[$position - 1] = $participant; } // Make positions array unique and sort it $lap_positions = array_unique($lap_positions); sort($lap_positions); // Loop each position and detect corrupted and wrong position laps $corrupted_lap_positions = array(); $wrong_lap_positions = array(); foreach ($lap_positions as $key => $lap_position) { // Lap is 10 positions higher than previous, it's a too big gap, // we consider this full corruption if ($key > 0 and $lap_position - $lap_positions[$key - 1] > 9) { // Add lap position to corrupted list $corrupted_lap_positions[] = $lap_position; } // First position aint 1 OR lap is 2 positions higher than previous, // we have some wrong lap positions if ($key === 0 and $lap_position > 1 or $key > 0 and $lap_position - $lap_positions[$key - 1] > 1) { // Add lap position to wrong list $wrong_lap_positions[] = $lap_position; } } // We have corrupted lap positions if ($corrupted_lap_positions) { // Whether we need to refill $all_laps_by_lap_number $refill_all_laps_by_lap_number = false; // Loop each participant to find out if they are really all // corrupted foreach ($participants as $participant) { // By default all laps of participant are corrupted $all_corrupted = true; // By default we have no corruption at all $corruption = false; // Loop each lap to see whether all laps are corrupted foreach ($participant->getLaps() as $lap) { // Lap position is not corrupted if (!in_array($lap->getPosition(), $corrupted_lap_positions)) { // Not all corrupted $all_corrupted = false; } else { // No position known $lap->setPosition(null); // We have corruption $corruption = true; } } // All are corrupted if ($all_corrupted) { // Unset all participant laps $participant->setLaps(array()); // We need to refill all laps by lap number array $refill_all_laps_by_lap_number = true; } } // Refill all laps by lap number array because laps are removed if ($refill_all_laps_by_lap_number) { $all_laps_by_lap_number = array(); foreach ($participants as $participant) { // Loop each lap foreach ($participant->getLaps() as $lap) { // Remember lap $all_laps_by_lap_number[$lap->getNumber()][] = $lap; } } } } //--- Fix wrong positions of laps // We have wrong lap positions, we need to fix this if ($wrong_lap_positions) { // Loop all lap numbers and their laps foreach ($all_laps_by_lap_number as $lap_number => $laps) { // Lap number 1 if ($lap_number === 1) { // Just set each lap position to grid position foreach ($laps as $lap) { $lap->setPosition($lap->getParticipant()->getGridPosition()); } // Ignore futher continue; } // Sort the laps by elapsed time $laps = Helper::sortLapsByElapsedTime($laps); // Fix the positions foreach ($laps as $lap_key => $lap) { // Set new position if it's not null (null = corruption) if ($lap->getPosition() !== null) { $lap->setPosition($lap_key + 1); } } } } // Sort participants by key ksort($participants); // Fix array keys to 0 - n $participants = array_values($participants); // Cache and return all participants return $this->participants = $participants; }
/** * @see \Simresults\Data_Reader::getSessions() */ public function getSessions() { // Get array data $data = $this->array_data; // Init sessions array $sessions = array(); // Remember last qualify session to make up grid positions $last_qualify_session = null; // Loop each session from data foreach ($data as $session_data) { // Remember which vehicles are parsed $vehicle_names = array(); // Init session $session = new Session(); // Set session type $type = null; switch ($session_data['type']) { case 'qualify': $type = Session::TYPE_QUALIFY; break; case 'practice': $type = Session::TYPE_PRACTICE; break; case 'warmup': $type = Session::TYPE_PRACTICE; break; case 'race': $type = Session::TYPE_RACE; break; } $session->setType($type); // Set session name if (isset($session_data['name'])) { $session->setName($session_data['name']); } // Set max time if (isset($session_data['time'])) { $session->setMaxMinutes($session_data['time']); } // Set max laps if (isset($session_data['laps'])) { $session->setMaxLaps($session_data['laps']); } // Set game $game = new Game(); $game->setName('Assetto Corsa'); $session->setGame($game); // Has track if (isset($session_data['track'])) { $track = new Track(); $track->setVenue($session_data['track']); $session->setTrack($track); } // Has date if (isset($session_data['date'])) { // Set it $session->setDateString($session_data['date']); } // Set server $server = new Server(); $server->setDedicated(true); if (isset($session_data['server'])) { $server->setName($session_data['server']); } else { $server->setName('Unknown'); } $session->setServer($server); // Add allowed vehicles foreach ($session_data['car_list'] as $vehicle_name) { $vehicle = new Vehicle(); $vehicle->setName($vehicle_name); $session->addAllowedVehicle($vehicle); } // Set chats foreach ($session_data['chats'] as $chat_message) { $chat = new Chat(); $chat->setMessage($chat_message); $session->addChat($chat); } // Set participants $participants = array(); foreach ($session_data['participants'] as $part_data) { // No name if (!Helper::arrayGet($part_data, 'name')) { continue; } // Create driver $driver = new Driver(); $driver->setName($part_data['name']); // Total time not greater than 0 if (0 >= ($total_time = Helper::arrayGet($part_data, 'total_time'))) { // Total time is null $total_time = null; } // Create participant and add driver $participant = new Participant(); $participant->setDrivers(array($driver))->setTotalTime($total_time); // Has total time parsed data and should not be a forced DNF if ($total_time and !Helper::arrayGet($part_data, 'force_dnf')) { $participant->setFinishStatus(Participant::FINISH_NORMAL); } else { $participant->setFinishStatus(Participant::FINISH_DNF); } // Remember vehicle instances by vehicle name $vehicles = array(); // Create vehicle and add to participant $vehicle = null; if (isset($part_data['vehicle'])) { // Init vehicle $vehicle = new Vehicle(); $vehicle->setName($part_data['vehicle']); $participant->setVehicle($vehicle); // Remember vehicle instance $vehicles[$part_data['vehicle']] = $vehicle; // Remember vehicle names for this entire session $vehicle_names[$part_data['vehicle']] = 1; } // Has team if (isset($part_data['team'])) { $participant->setTeam($part_data['team']); } // Has guid if (isset($part_data['guid'])) { $driver->setDriverId($part_data['guid']); } // Collect laps foreach (Helper::arrayGet($part_data, 'laps', array()) as $lap_i => $lap_data) { // Init new lap $lap = new Lap(); // Set participant $lap->setParticipant($participant); // Set first driver of participant as lap driver. AC does // not support swapping $lap->setDriver($participant->getDriver()); // Set lap number $lap->setNumber($lap_i + 1); // Set lap times $lap->setTime($lap_data['time']); // No lap vehicle if (!$lap_data['vehicle']) { // Just use participant vehicle if it is available if ($vehicle) { $lap->setVehicle($vehicle); } } elseif (isset($vehicles[$v = $lap_data['vehicle']])) { // Set vehicle instance $lap->setVehicle($vehicles[$v]); } else { // Init vehicle $vehicle = new Vehicle(); $vehicle->setName($lap_data['vehicle']); $lap->setVehicle($vehicle); // Remember vehicle $vehicles[$lap_data['vehicle']] = $vehicle; } // Add lap to participant $participant->addLap($lap); } // No laps and race result if (!$participant->getLaps() and $session->getType() === Session::TYPE_RACE) { // Did not finish $participant->setFinishStatus(Participant::FINISH_DNF); } // Add participant to collection $participants[] = $participant; } // Is race result if ($session->getType() === Session::TYPE_RACE) { // Sort participants by total time $participants = Helper::sortParticipantsByTotalTime($participants); } else { // Sort by best lap $participants = Helper::sortParticipantsByBestLap($participants); } // Fix participant positions foreach ($participants as $key => $part) { $part->setPosition($key + 1); } // Set participants to session $session->setParticipants($participants); // Fix elapsed seconds for all participant laps foreach ($session->getParticipants() as $participant) { $elapsed_time = 0; foreach ($participant->getLaps() as $lap) { // Set elapsed seconds and increment it $lap->setElapsedSeconds($elapsed_time); $elapsed_time += $lap->getTime(); } } // Is qualify if ($session->getType() === Session::TYPE_QUALIFY) { // Remember last qualify session $last_qualify_session = $session; } else { if ($session->getType() === Session::TYPE_RACE and $last_qualify_session) { // Get pairticpants of last qualify session and store names $last_qualify_session_participants = array(); foreach ($last_qualify_session->getParticipants() as $part) { $last_qualify_session_participants[] = $part->getDriver()->getName(); } // Loop this session participants foreach ($participants as $part) { // Found participant in qualify array if (false !== ($key = array_search($part->getDriver()->getName(), $last_qualify_session_participants))) { $part->setGridPosition($key + 1); } } } } // Fix driver positions for laps $session_lasted_laps = $session->getLastedLaps(); // Loop each lap number, beginning from 2, because we can't // figure out positions for lap 1 in AC // TODO: Duplicate code with RACE07 and AC normal reader for ($i = 2; $i <= $session_lasted_laps; $i++) { // Get laps sorted by elapsed time $laps_sorted = $session->getLapsByLapNumberSortedByTime($i); // Sort laps by elapsed time $laps_sorted = Helper::sortLapsByElapsedTime($laps_sorted); // Loop each lap and fix position data foreach ($laps_sorted as $lap_key => $lap) { // Only fix position if lap has a time, this way users of this // library can easier detect whether it's a dummy lap and // decide how to show them if ($lap->getTime() or $lap->getElapsedSeconds()) { $lap->setPosition($lap_key + 1); } } } // Only one vehicle type in this session if (count($vehicle_names) === 1) { // Find any participant without vehicle and fix missing. // This is an easy last resort fix when parsing was bugged // We assume everybody has this vehicle foreach ($session->getParticipants() as $participant) { if (!$participant->getVehicle()) { // Init vehicle $vehicle = new Vehicle(); $vehicle->setName(key($vehicle_names)); $participant->setVehicle($vehicle); } } } // Add session to collection $sessions[] = $session; } // Return all sessions return $sessions; }