/**
  * @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;
 }
Exemple #6
0
 /**
  * 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;
 }