excludeCalibrationSessionsFromStats == true ? " AND " . $sessionsTableIndex . ".ScenarioName NOT LIKE '%Calibration%'" : ""; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected $minimumSessionDuration = 10; protected function getSessionDurationConstraint ($sessionsTableIndex="SD", $allowZero=false) : string { return $this->minimumSessionDuration > 0 ? " AND (" . $sessionsTableIndex . ".timeToFinish > " . $this->minimumSessionDuration . ($allowZero ? " OR " . $sessionsTableIndex . ".timeToFinish = 0" : "") . ")" : ""; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected function getSessionsWithUserConstraint ($userId) : string { return $userId > 0 ? "SELECT DISTINCT UP.SessionId FROM " . PARTICIPATES_TABLE_NAME . " UP WHERE UP.userId=" . $userId : ""; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public function toArray () : array { return $this->elements; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // get users who took place in given session public function fixDurations () { $query = "SELECT S.id AS SessionId FROM " . SESSIONS_TABLE_NAME . " S, " . TRIGGEREVENTS_TABLE_NAME . " TE WHERE S.timeToFinish=0 AND S.id=TE.sessionId"; // prepare and execute query $stmt = $this->conn->prepare($query); $stmt->execute(); $nb = 0; // get max time for trigger events in these sessions while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $subQuery = "SELECT COALESCE(MAX(TE.timeStamp),0) AS LastTriggerEvent FROM " . TRIGGEREVENTS_TABLE_NAME . " TE WHERE TE.sessionId=" . $row['SessionId']; // prepare and execute query $subStmt = $this->conn->prepare($subQuery); $subStmt->execute(); while ($subRow = $subStmt->fetch(PDO::FETCH_ASSOC)) // should be only 1 row { if ($subRow['LastTriggerEvent'] != 0) // should always be true { $subQuery = "UPDATE " . SESSIONS_TABLE_NAME . " SET timeToFinish=" . $subRow['LastTriggerEvent'] . " WHERE id=" . $row['SessionId']; // prepare and execute query $subStmt = $this->conn->prepare($subQuery); $subStmt->execute(); $nb++; } } } return $nb; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // get users who took place in given session public function getRawUsers ($sessionId) { // Check if we are looking for a specific session $sessionConstraint = $sessionId > 0 ? " AND P.sessionId=" . $sessionId : ""; $query = "SELECT DISTINCT U.* FROM " . USERS_TABLE_NAME . " U LEFT JOIN " . PARTICIPATES_TABLE_NAME . " P ON U.id = P.userId " . "LEFT JOIN " . SESSIONS_TABLE_NAME . " SD ON P.sessionId=SD.id " . "WHERE U.id > 0" . $sessionConstraint . $this->getSessionDurationConstraint("SD"); //$query = "SELECT DISTINCT U.* FROM " . USERS_TABLE_NAME . " U, " . PARTICIPATES_TABLE_NAME . " P WHERE U.id = P.userId AND U.id > 0" . $sessionConstraint; // prepare and execute query $stmt = $this->conn->prepare($query); $stmt->execute(); return $stmt; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // get users who took place in given session public function getUsersInSession ($sessionId) { // Check if we are looking for a specific session $stmt = $this->getRawUsers($sessionId); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { //$sess = new GameSession($this->conn); //$sess->readRow($row); $user = User::withRow($this->conn, $row); array_push( $this->elements, $user->toArray() ); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // get sessions of given type that given user has taken part in public function getRawSessions ($userId=-1, $typeId=-1) { // Check if we are looking for a specific user and/or type of session $userConstraint = $userId > 0 ? " AND P.userId=" . $userId : ""; $typeConstraint = $typeId >= 0 ? " AND SD.sessionType=" . $typeId : ""; // Order results from newest to oldest session $orderConstraint = " ORDER BY SD.id DESC"; //$query = "SELECT DISTINCT S.* FROM " . SESSIONS_TABLE_NAME . " S LEFT JOIN " . PARTICIPATES_TABLE_NAME . " P ON S.id = P.sessionId WHERE 1 " . $query = "SELECT DISTINCT SD.* FROM " . SESSIONS_TABLE_NAME . " SD, " . PARTICIPATES_TABLE_NAME . " P WHERE SD.id = P.sessionId " . $this->getCalibrationSessionsConstraint("SD") . $this->getSessionDurationConstraint("SD") . $userConstraint . $typeConstraint . $orderConstraint; // prepare and execute query $stmt = $this->conn->prepare($query); $stmt->execute(); return $stmt; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // get sessions that given user has taken part in public function getSessionsForUser ($userId, $typeId=-1) { // Check if we are looking for a specific user and/or session $stmt = $this->getRawSessions($userId, $typeId); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { //$sess = new GameSession($this->conn); //$sess->readRow($row); $sess = GameSession::withRow($this->conn, $row); array_push( $this->elements, $sess->toArray() ); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // debrief mission : get stats for given session public function getRawStatsForSession ($sessionId, $userId, $fromUser=-1) { $sessionStats = array(); // first create expected rows for results, so that every user will get stats (even if user didn't fire or received any shot) if ($sessionId > 0 && $userId > 0) { // we want stats for a specific user in a specific session // => we only need to create one default row, just in case user didn't fire or received any shot $defaultRow = new UserStatsInSessionRow(); $defaultRow->createSessionAndUser($this->conn, $sessionId, $userId); array_push( $sessionStats, $defaultRow ); } else if ($userId == -1) { // we want stats for all users in a specific session // => we need to create one default row for each user $stmt_usersInSession = $this->getRawUsers($sessionId); while ($row = $stmt_usersInSession->fetch(PDO::FETCH_ASSOC)) { $defaultRow = new UserStatsInSessionRow(); $defaultRow->createSessionAndUser($this->conn, $sessionId, $row['id']); array_push( $sessionStats, $defaultRow ); } } else if ($sessionId == -1) { // we want stats for a specific user in all sessions he took place in // => we need to create one default row for each user $stmt_sessionsForUser = $this->getRawSessions($userId); while ($row = $stmt_sessionsForUser->fetch(PDO::FETCH_ASSOC)) { $defaultRow = new UserStatsInSessionRow(); $defaultRow->createSessionAndUser($this->conn, $row['id'], $userId); array_push( $sessionStats, $defaultRow ); } } else { // both $sessionId and $userId are set to -1 // => do nothing, this will (should) not happen } // Get fired shots $stmt_firedShots = $this->getShotsFiredByUser($sessionId, $userId, $fromUser); while ($row = $stmt_firedShots->fetch(PDO::FETCH_ASSOC)) { // Only create stats row for shooters with id > 0 if ((int)$row['UserId'] > 0) { $statsRow = $this->findStats( $sessionStats, (int)$row['UserId'], (int)$row['SessionId'] ); if ($statsRow == null) { $statsRow = UserStatsInSessionRow::withRow($this->conn, $row); $statsRow->setFiredShots($row); array_push( $sessionStats, $statsRow ); } else $statsRow->setFiredShots($row); } } // Get received shots $stmt_receivedShots = $this->getReceivedHitsForUser($sessionId, $userId, $fromUser); while ($row = $stmt_receivedShots->fetch(PDO::FETCH_ASSOC)) { // Only create stats row for hit users with id > 0 if ((int)$row['UserId'] > 0) { $statsRow = $this->findStats ( $sessionStats, (int)$row['UserId'], (int)$row['SessionId'] ); if ($statsRow == null) { $statsRow = UserStatsInSessionRow::withRow($this->conn, $row); $statsRow->setReceivedHits($row); array_push( $sessionStats, $statsRow ); } else $statsRow->setReceivedHits($row); } } // Get average precision and reaction time $stmt_averages = $this->getPrecisionAndReactionTimeForUser($sessionId, $userId, $fromUser); while ($row = $stmt_averages->fetch(PDO::FETCH_ASSOC)) { // Only create stats row for hit users with id > 0 if ((int)$row['UserId'] > 0) { $statsRow = $this->findStats ( $sessionStats, (int)$row['UserId'], (int)$row['SessionId'] ); if ($statsRow == null) { $statsRow = UserStatsInSessionRow::withRow($this->conn, $row); $statsRow->setPrecisionAndReactionTime($row); array_push( $sessionStats, $statsRow ); } else $statsRow->setPrecisionAndReactionTime($row); } } // Get total of targets killed in the session $stmt_targetKilled = $this->getTargetKilledInSession($sessionId); while ($row = $stmt_targetKilled->fetch(PDO::FETCH_ASSOC)) { // Add totals stats to all rows of the same session foreach ($sessionStats as $statsRow) { if ($statsRow->sessionId == (int)$row['SessionId']) $statsRow->setTotalKills($row); } } return $sessionStats; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // debrief mission : get stats for given session public function getStatsForSession ($sessionId, $userId, $fromUser=-1) { $sessionStats = $this->getRawStatsForSession($sessionId, $userId, $fromUser); // Parse workingStats array to output results foreach ($sessionStats as $outputStats) array_push( $this->elements, $outputStats->toArray() ); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // find a row of stats for given user and stats function findStats (array $statsArray, int $userId, int $sessionId) { foreach ($statsArray as $row) { if (call_user_func_array(array($row, 'isForUserInSession'), array($userId, $sessionId)) === true) return $row; } return null; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // get stats : main call for getting stats from Unreal public function get ($sessionId, $userId, $sessionType, $fromUserId=-1) { if ($sessionType == 0 || $sessionType == 1 || $sessionType == 7) // Firerange, Challenge or Long Range $this->getResultsForSession ($sessionId, $userId, $fromUserId); else if ($sessionId > 0 || $userId > 0) $this->getStatsForSession ($sessionId, $userId, $fromUserId); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // user history : get user "all-time" totals public function getUserHistory ($userId, $sessionId, $quickMode) { $userGlobals = new UserGlobalStats(); if ($quickMode == 1 && $userId > 0) { $userGlobals->totals->createSessionAndUser($this->conn, $sessionId, $userId); $userGlobals->totals->averagePrecision = $userGlobals->totals->user->avgPrecision; $userGlobals->totals->averageReactionTime = $userGlobals->totals->user->avgReaction; $userSubConstraint = $userId >= 0 ? " AND P.UserId = " . $userId : ""; // always true // Calculate date of first and last sessions $query = "SELECT P.userID AS UserId, MIN(SD.SessionDate) AS MinDate, MAX(SD.SessionDate) AS MaxDate, " . " COUNT(DISTINCT P.UserId, P.sessionId) AS NbSessions, SUM(SD.timeToFinish) AS TotalDuration " . " FROM " . PARTICIPATES_TABLE_NAME . " P, " . SESSIONS_TABLE_NAME . " SD WHERE SD.id=P.sessionId " . $this->getCalibrationSessionsConstraint("SD") . $this->getSessionDurationConstraint("SD") . $userSubConstraint . " GROUP BY P.UserId"; // prepare and execute query $stmt = $this->conn->prepare($query); $stmt->execute(); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) // should be only 1 row { $userGlobals->setSessionTotals($row); //$userGlobals->nbSessions = $row['NbSessions']; //$userGlobals->totalDuration = $row['TotalDuration']; //$userGlobals->firstSession = $row['MinDate']; //$userGlobals->lastSession = $row['MaxDate']; } // Calculate shots fired $userConstraint = $userId > 0 ? " AND SDTE.srcUserId=" . $userId : ""; $query = "SELECT COALESCE(SDTE.srcUserId,-1) AS UserId, COALESCE(X.NbShotsFired,0) AS NbShotsFired, COALESCE(X.NbShotsFired,0) - COALESCE(SUM(CASE WHEN Y.ReactTypeId>=0 AND Y.ReactTypeId<6 AND Y.ReactPrecision>0 THEN Y.NbHits ELSE 0 END),0) AS NbMissedShots, SUM(CASE WHEN Y.ReactTypeId=0 AND Y.ReactPrecision>0 THEN Y.NbHits ELSE 0 END) AS NbEnemyHits, SUM(CASE WHEN Y.ReactTypeId=1 AND Y.ReactPrecision>0 THEN Y.NbHits ELSE 0 END) AS NbCivilHits, SUM(CASE WHEN Y.ReactTypeId=2 AND Y.ReactPrecision>0 THEN Y.NbHits ELSE 0 END) AS NbPoliceHits, SUM(CASE WHEN (Y.ReactTypeId=4 OR Y.ReactTypeId=5) AND Y.ReactPrecision>0 THEN Y.NbHits ELSE 0 END) AS NbObjectHits, SUM(CASE WHEN Y.ReactTypeId=6 AND Y.ReactPrecision>0 THEN Y.NbHits ELSE 0 END) AS NbDeadBodyHits FROM " . SESSIONS_TABLE_NAME . " SD LEFT JOIN " . TRIGGEREVENTS_TABLE_NAME . " SDTE ON (SDTE.sessionId=SD.id) LEFT JOIN " . REACTEVENTS_TABLE_NAME . " SDRE ON (SDTE.sessionId=SDRE.srcEventSessionId AND SDTE.indexCount=SDRE.srcEventIndex) LEFT JOIN " . " (SELECT TE.srcUserId AS ShooterId, COUNT(DISTINCT TE.sessionId, TE.indexCount) AS NbShotsFired FROM " . TRIGGEREVENTS_TABLE_NAME . " TE GROUP BY TE.srcUserId) AS X ON X.ShooterId=SDTE.srcUserId LEFT JOIN " . " (SELECT TE.srcUserId AS ShooterId, RE.ReactType AS ReactTypeId, COUNT(DISTINCT TE.sessionId, TE.indexCount, RE.hitTargetName) AS NbHits, RE.id AS ReactId, RE.hitPrecision AS ReactPrecision FROM " . TRIGGEREVENTS_TABLE_NAME . " TE, " . REACTEVENTS_TABLE_NAME . " RE WHERE TE.sessionId = RE.srcEventSessionId AND TE.indexCount = RE.srcEventIndex AND TE.indexCount!=-1 AND RE.ReactType!=-1 GROUP BY ShooterId, ReactTypeId, ReactID) AS Y ON (Y.ShooterId=SDTE.srcUserId AND Y.ReactId=SDRE.id) LEFT JOIN " . " (SELECT TE1.srcUserId AS ShooterId, COALESCE(COUNT(DISTINCT TE1.sessionId, TE1.indexCount),0) AS NbMissedShots FROM " . TRIGGEREVENTS_TABLE_NAME . " TE1, " . REACTEVENTS_TABLE_NAME . " RE1 WHERE TE1.sessionId=RE1.srcEventSessionId AND TE1.indexCount NOT IN (SELECT RE2.srcEventIndex FROM " . REACTEVENTS_TABLE_NAME . " RE2 WHERE RE2.srcEventSessionId=RE1.srcEventSessionId)) AS Z ON (Z.ShooterId=SDTE.srcUserId) " . " WHERE 1 " . $this->getCalibrationSessionsConstraint("SD") . $this->getSessionDurationConstraint("SD") . $userConstraint . " GROUP BY SDTE.srcUserId"; // prepare and execute query $stmt = $this->conn->prepare($query); $stmt->execute(); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $userGlobals->totals->setFiredShots($row); //$userGlobals->totals->nbFiredShotsByUser = $row['NbShotsFired']; //$userGlobals->totals->nbMissedShotsByUser = $row['NbMissedShots']; //$userGlobals->totals->nbEnemyHitsByUser = $row['NbEnemyHits']; //$userGlobals->totals->nbCivilHitsByUser = $row['NbCivilHits']; //$userGlobals->totals->nbPoliceHitsByUser = $row['NbPoliceHits']; //$userGlobals->totals->nbObjectHitsByUser = $row['NbObjectHits']; //$userGlobals->totals->nbDeadBodyHitsByUser = $row['NbDeadBodyHits']; } // Calculate received hits $userConstraint = $userId > 0 ? " AND X.TargetUserId=" . $userId : ""; $query = "SELECT COALESCE(X.TargetUserId, -1) AS UserId, SUM(CASE WHEN X.ShooterRoleId=3 THEN X.NbHits ELSE 0 END) AS NbEnemyShotsIA, SUM(CASE WHEN X.ShooterRoleId=1 THEN X.NbHits ELSE 0 END) AS NbEnemyShotsUser, SUM(CASE WHEN X.ShooterRoleId=0 THEN X.NbHits ELSE 0 END) AS NbPoliceHits FROM (SELECT TE.SessionId AS SessionId, RE.hitUserId AS TargetUserId, (SELECT IFNULL((SELECT P.role FROM " . PARTICIPATES_TABLE_NAME . " P WHERE P.sessionId=TE.sessionId AND P.userId=TE.srcUserId),3)) AS ShooterRoleId, COUNT(DISTINCT TE.indexCount, RE.hitTargetName) AS NbHits FROM " . TRIGGEREVENTS_TABLE_NAME . " TE, " . REACTEVENTS_TABLE_NAME . " RE, " . SESSIONS_TABLE_NAME . " SD " . "WHERE SD.id=TE.sessionId AND TE.sessionId = RE.srcEventSessionId AND TE.indexCount = RE.srcEventIndex AND TE.indexCount!=-1 AND RE.ReactType!=-1 " . $this->getCalibrationSessionsConstraint("SD") . $this->getSessionDurationConstraint("SD") . "GROUP BY SessionId, TargetUserId, ShooterRoleId) AS X WHERE 1 " . $userConstraint . " GROUP BY X.TargetUserId"; // prepare and execute query $stmt = $this->conn->prepare($query); $stmt->execute(); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $userGlobals->totals->setReceivedHits($row); } // Calculate totals hits by type $sessionConstraint = $sessionId > 0 ? " AND SDTE.SessionId = " . $sessionId : ""; $userConstraint = $userId > 0 ? " AND SDTE.SessionId IN (" . $this->getSessionsWithUserConstraint($userId) . ")" : ""; $query = "SELECT SUM(CASE WHEN X.ReactTypeId=0 THEN X.NbHits ELSE 0 END) AS NbEnemyKilled, SUM(CASE WHEN X.ReactTypeId=1 THEN X.NbHits ELSE 0 END) AS NbCivilKilled, SUM(CASE WHEN X.ReactTypeId=2 THEN X.NbHits ELSE 0 END) AS NbPoliceKilled FROM " . SESSIONS_TABLE_NAME . " SD LEFT JOIN " . TRIGGEREVENTS_TABLE_NAME . " SDTE ON (SD.id = SDTE.sessionId) LEFT JOIN (SELECT TE.SessionId AS SessionId, RE.hitUserId AS TargetUserId, TE.srcUserId AS ShooterRoleId, COUNT(DISTINCT TE.indexCount, RE.hitTargetName) AS NbHits, RE.srcEventIndex AS TriggerIndex, RE.ReactType AS ReactTypeId FROM " . TRIGGEREVENTS_TABLE_NAME . " TE, " . REACTEVENTS_TABLE_NAME . " RE WHERE TE.sessionId = RE.srcEventSessionId AND TE.indexCount = RE.srcEventIndex AND TE.indexCount!=-1 AND RE.ReactType!=-1 AND RE.TargetKilled=1 GROUP BY SessionId, TargetUserId, ShooterRoleId, ReactTypeId) AS X ON (X.SessionId=SDTE.SessionId AND X.TriggerIndex=SDTE.indexCount) WHERE 1 " . $this->getCalibrationSessionsConstraint("SD") . $this->getSessionDurationConstraint("SD") . $userConstraint; // prepare and execute query $stmt = $this->conn->prepare($query); $stmt->execute(); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $userGlobals->totals->setTotalKills($row); } $query = "SELECT X.*, " . "SUM(X.HitPrecision)/COALESCE(COUNT(DISTINCT X.SessionId, X.ShotIndex),1) AS AvgPrecision, " . "SUM(X.ReactionTime)/COALESCE(SUM(CASE WHEN X.ReactionTime > 0 THEN CEILING(X.HitPrecision) ELSE 0 END),1) AS AvgReactTime " . "FROM " . "(SELECT TE.sessionId AS SessionId, S.sessionType AS SessionTypeId, ST.displayName AS SessionType, S.sessionName AS SessionName, S.sessionDate AS SessionDate, S.mapName AS MapName, S.scenarioName AS ScenarioName, " . "S.success AS SessionSuccessful, S.timeToFinish AS SessionDuration, 0 AS TriggerTypeId, 'Fire' AS TriggerType, TE.srcUserId AS ShooterId, '' AS ShooterName, -1 AS ShooterRoleId, '' AS ShooterRole, " . "TE.indexCount AS ShotIndex, RE.id AS ReactId, -1 AS ReactModeId, '' AS ReactMode, -1 AS ReactTypeId, '' AS ReactType, RE.hitUserId AS TargetUserId, " . "'' AS TargetUserName, -1 AS TargetRoleId, '' AS TargetRole, RE.hitTargetName AS TargetName, RE.hitBoneName AS TargetBoneName, 0 AS TargetKilled, 0 AS HitLocationX, 0 AS HitLocationY, " . "'' AS HitLocationTag, RE.hitPrecision AS HitPrecision, 0 AS HitTargetDistance, RE.reactTime as ReactionTime, 0 AS TimeStamp, 0 AS NbHit, 0 AS NbKilled " . "FROM " . SESSIONS_TABLE_NAME . " S " . "LEFT JOIN " . TRIGGEREVENTS_TABLE_NAME . " TE ON (S.id=TE.sessionId) " . "LEFT JOIN " . REACTEVENTS_TABLE_NAME . " RE ON (TE.sessionId=RE.srcEventSessionId AND TE.indexCount=RE.srcEventIndex) " . "LEFT JOIN " . SESSIONTYPES_TABLE_NAME . " ST ON (S.sessionType=ST.id) " . "WHERE 1 " . $this->getSessionDurationConstraint("S") . $this->getCalibrationSessionsConstraint("S") . " GROUP BY ShooterId, SessionId, TE.indexCount) AS X " . "WHERE X.ShooterId = " . $userId . " AND X.SessionId IN (" . $this->getSessionsWithUserConstraint($userId) . ") GROUP BY X.ShooterId, X.SessionId ORDER BY X.SessionId ASC"; // prepare and execute query $stmt = $this->conn->prepare($query); $stmt->execute(); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $row['HitPrecision'] = $row['AvgPrecision']; $row['ReactionTime'] = $row['AvgReactTime']; $stats = SessionDebriefRow::withRow($row); array_push( $userGlobals->sessionDebriefRows, $stats->toArray() ); } } else { // create SQL constraints for query $sessionConstraint = $sessionId > 0 ? " AND SD.SessionId = " . $sessionId : ""; $userConstraint = $userId >= 0 ? " AND SD.ShooterId = " . $userId : ""; $userSubConstraint = $userId >= 0 ? " AND P.UserId = " . $userId : ""; // get raw results from query $sessionStats = $this->getRawStatsForSession ($sessionId, $userId, -1); $newNbFiredShotsForReactionTime = 0; $nbSessions = 0; foreach ($sessionStats as $row) { if ( ($userId == -1 || $row->userId == $userId) && ($sessionId == -1 || $row->sessionId == $sessionId) ) { // Increase number of sessions for this user $nbSessions++; // Update precision average $newPrecision = $userGlobals->totals->averagePrecision*$userGlobals->totals->nbFiredShotsByUser + $row->averagePrecision*$row->nbFiredShotsByUser; $userGlobals->totals->averagePrecision = $newPrecision/max($userGlobals->totals->nbFiredShotsByUser + $row->nbFiredShotsByUser, 1.0); // Update reactionTime average if ($row->averageReactionTime > 0) { $newReactionTime = $userGlobals->totals->averageReactionTime*$newNbFiredShotsForReactionTime + $row->averageReactionTime*$row->nbFiredShotsByUser; $newNbFiredShotsForReactionTime += $row->nbFiredShotsByUser; $userGlobals->totals->averageReactionTime = $newReactionTime/max($newNbFiredShotsForReactionTime, 1.0); } // Update other fields $userGlobals->totals->nbFiredShotsByUser = $userGlobals->totals->nbFiredShotsByUser + $row->nbFiredShotsByUser; $userGlobals->totals->nbEnemyHitsByUser += $row->nbEnemyHitsByUser; $userGlobals->totals->nbCivilHitsByUser += $row->nbCivilHitsByUser; $userGlobals->totals->nbPoliceHitsByUser += $row->nbPoliceHitsByUser; $userGlobals->totals->nbObjectHitsByUser += $row->nbObjectHitsByUser; $userGlobals->totals->nbMissedShotsByUser += $row->nbMissedShotsByUser; $userGlobals->totals->nbDeadBodyHitsByUser += $row->nbDeadBodyHitsByUser; $userGlobals->totals->nbReceivedHitsFromEnemyIA += $row->nbReceivedHitsFromEnemyIA; $userGlobals->totals->nbReceivedHitsFromEnemyUser += $row->nbReceivedHitsFromEnemyUser; $userGlobals->totals->nbReceivedHitsFromPoliceUser +=$row->nbReceivedHitsFromPoliceUser; $userGlobals->totals->totalEnemyKilled += $row->totalEnemyKilled; $userGlobals->totals->totalCivilKilled += $row->totalCivilKilled; $userGlobals->totals->totalPoliceKilled += $row->totalPoliceKilled; // Update duration $userGlobals->totalDuration += $row->session->timeToFinish; // Update user and session ids $userGlobals->totals->userId = $row->userId; $userGlobals->totals->sessionId = $row->sessionId; } } // Get number of sessions user took part in $userGlobals->nbSessions = $nbSessions; // Calculate averages for precision and reaction time $query = "SELECT SD.*, SUM(SD.HitPrecision) AS TotalPrecision, COUNT(DISTINCT SD.ShotIndex, SD.TargetName) AS TotalShots" . ", SUM(SD.ReactionTime) AS TotalReactionTime, SUM(CEILING(SD.HitPrecision)) AS TotalHits, X.MinDate AS MinDate, X.MaxDate AS MaxDate" . " FROM " . SESSIONDEBRIEFS_VIEW_NAME . " SD, " . " (SELECT P.userID AS UserId, MIN(S.SessionDate) AS MinDate, MAX(S.SessionDate) AS MaxDate" . //" FROM " . PARTICIPATES_TABLE_NAME . " P INNER JOIN " . SESSIONS_TABLE_NAME . " S ON S.id=P.sessionId WHERE 1 " . $userSubConstraint . " GROUP BY P.userId) AS X" . " FROM " . PARTICIPATES_TABLE_NAME . " P, " . SESSIONS_TABLE_NAME . " S WHERE S.id=P.sessionId " . $userSubConstraint . " GROUP BY P.userId) AS X" . " WHERE 1 " . $sessionConstraint . $userConstraint . " GROUP BY SD.SessionId, SD.ShooterId"; // prepare and execute query $stmt = $this->conn->prepare($query); $stmt->execute(); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { // read MIN and MAX dates $userGlobals->firstSession = $row['MinDate']; $userGlobals->lastSession = $row['MaxDate']; // replace HitPrecision, ReactionTime and NbHit fields so that we don't have to create a new type for returning the results $row['HitPrecision'] = (float)(($row['TotalPrecision'] ?? 0.0) / max(($row['TotalShots'] ?? 1.0), 1.0) ); $row['ReactionTime'] = (float)(($row['TotalReactionTime'] ?? 0.0) / max(($row['TotalHits'] ?? 1.0), 1.0) ); $row['NbHit'] = $row['TotalHits'] ?? 0; $stats = SessionDebriefRow::withRow($row); array_push( $userGlobals->sessionDebriefRows, $stats->toArray() ); //array_push( $this->elements, $stats->toArray() ); // old } if ($userGlobals->nbSessions == 1) { $userGlobals->firstSession = $sessionStats[0]->session->sessionDate; $userGlobals->lastSession = $userGlobals->firstSession; } } array_push( $this->elements, $userGlobals->toArray() ); // new //return $userGlobals->toArray(); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // debrief mission : get totals for given session and/or user public function getReceivedHitsForUser ($sessionId, $userId, $fromUser=-1) { // create SQL constraints for query $sessionConstraint = $sessionId > 0 ? " AND SDRE.srcEventSessionId = " . $sessionId : ""; $userConstraint = $userId > 0 ? " AND SDRE.hitUserId=" . $userId : ""; $query = "SELECT SDRE.srcEventSessionId AS SessionId, COALESCE(SDRE.hitUserId, -1) AS UserId, SUM(CASE WHEN X.ShooterRoleId=3 THEN X.NbHits ELSE 0 END) AS NbEnemyShotsIA, SUM(CASE WHEN X.ShooterRoleId=1 THEN X.NbHits ELSE 0 END) AS NbEnemyShotsUser, SUM(CASE WHEN X.ShooterRoleId=0 THEN X.NbHits ELSE 0 END) AS NbPoliceHits FROM " . REACTEVENTS_TABLE_NAME . " SDRE LEFT JOIN (SELECT TE.SessionId AS SessionId, RE.hitUserId AS TargetUserId, TE.srcUserId AS ShooterRoleId, COUNT(DISTINCT TE.indexCount, RE.hitTargetName) AS NbHits, RE.id AS ReactId FROM " . TRIGGEREVENTS_TABLE_NAME . " TE, " . REACTEVENTS_TABLE_NAME . " RE WHERE TE.sessionId = RE.srcEventSessionId AND TE.indexCount = RE.srcEventIndex AND TE.indexCount!=-1 AND RE.ReactType!=-1 GROUP BY SessionId, TargetUserId, ShooterRoleId) AS X ON (X.SessionId=SDRE.srcEventSessionId AND X.TargetUserId=SDRE.hitUserId AND X.ReactId=SDRE.id) " . " LEFT JOIN " . SESSIONS_TABLE_NAME . " SD ON (SD.id=SDRE.srcEventSessionId) " . "WHERE 1 " . $sessionConstraint . $userConstraint . $this->getSessionDurationConstraint("SD") . " GROUP BY SDRE.srcEventSessionId, SDRE.hitUserId"; // prepare and execute query $stmt = $this->conn->prepare($query); $stmt->execute(); return $stmt; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // debrief mission : get totals for given session and/or user public function getShotsFiredByUser ($sessionId, $userId, $fromUser=-1) { // create SQL constraints for query $sessionConstraint = $sessionId > 0 ? " AND SDTE.SessionId = " . $sessionId : ""; $userConstraint = $userId > 0 ? " AND SDTE.srcUserId=" . $userId : ""; $query = "SELECT SDTE.SessionId AS SessionId, SDTE.srcUserId AS UserId, COALESCE(X.NbShotsFired,0) AS NbShotsFired, COALESCE(X.NbShotsFired,0) - COALESCE(SUM(CASE WHEN Y.ReactTypeId>=0 AND Y.ReactTypeId<6 AND Y.ReactPrecision>0 THEN Y.NbHits ELSE 0 END),0) AS NbMissedShots, SUM(CASE WHEN Y.ReactTypeId=0 AND Y.ReactPrecision>0 THEN Y.NbHits ELSE 0 END) AS NbEnemyHits, SUM(CASE WHEN Y.ReactTypeId=1 AND Y.ReactPrecision>0 THEN Y.NbHits ELSE 0 END) AS NbCivilHits, SUM(CASE WHEN Y.ReactTypeId=2 AND Y.ReactPrecision>0 THEN Y.NbHits ELSE 0 END) AS NbPoliceHits, SUM(CASE WHEN (Y.ReactTypeId=4 OR Y.ReactTypeId=5) AND Y.ReactPrecision>0 THEN Y.NbHits ELSE 0 END) AS NbObjectHits, SUM(CASE WHEN Y.ReactTypeId=6 AND Y.ReactPrecision>0 THEN Y.NbHits ELSE 0 END) AS NbDeadBodyHits FROM " . SESSIONS_TABLE_NAME . " SD LEFT JOIN " . TRIGGEREVENTS_TABLE_NAME . " SDTE ON (SD.id = SDTE.sessionId) LEFT JOIN " . REACTEVENTS_TABLE_NAME . " SDRE ON (SDTE.sessionId=SDRE.srcEventSessionId AND SDTE.indexCount=SDRE.srcEventIndex) LEFT JOIN (SELECT TE.sessionId AS SessionId, TE.srcUserId AS ShooterId, COUNT(DISTINCT TE.indexCount) AS NbShotsFired FROM " . TRIGGEREVENTS_TABLE_NAME . " TE GROUP BY TE.sessionId, TE.srcUserId) AS X ON (X.SessionId=SDTE.SessionId AND X.ShooterId=SDTE.srcUserId) LEFT JOIN (SELECT TE.sessionId AS SessionId, TE.srcUserId AS ShooterId, RE.ReactType AS ReactTypeId, COUNT(DISTINCT TE.indexCount, RE.hitTargetName) AS NbHits, RE.id AS ReactId, RE.hitPrecision AS ReactPrecision FROM " . TRIGGEREVENTS_TABLE_NAME . " TE, " . REACTEVENTS_TABLE_NAME . " RE WHERE TE.sessionId = RE.srcEventSessionId AND TE.indexCount = RE.srcEventIndex AND TE.indexCount!=-1 AND RE.ReactType!=-1 GROUP BY SessionId, ShooterId, ReactTypeId, ReactID) AS Y ON (Y.SessionId=SDTE.SessionId AND Y.ShooterId=SDTE.srcUserId AND Y.ReactId=SDRE.id) LEFT JOIN (SELECT TE1.sessionId AS SessionId, TE1.srcUserId AS ShooterId, COALESCE(COUNT(DISTINCT TE1.sessionId, TE1.indexCount),0) AS NbMissedShots FROM " . TRIGGEREVENTS_TABLE_NAME . " TE1, " . REACTEVENTS_TABLE_NAME . " RE1 WHERE TE1.sessionId=RE1.srcEventSessionId AND TE1.indexCount NOT IN (SELECT RE2.srcEventIndex FROM " . REACTEVENTS_TABLE_NAME . " RE2 WHERE RE2.srcEventSessionId=RE1.srcEventSessionId)) AS Z ON (Z.SessionId=SDTE.SessionId AND Z.ShooterId=SDTE.srcUserId) WHERE 1 " . $this->getCalibrationSessionsConstraint("SD") . $this->getSessionDurationConstraint("SD") . $sessionConstraint . $userConstraint . " GROUP BY SDTE.SessionId, SDTE.srcUserId"; // prepare and execute query $stmt = $this->conn->prepare($query); $stmt->execute(); return $stmt; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // debrief mission : get totals for given session and/or user public function getTargetKilledInSession ($sessionId) { // create SQL constraints for query $sessionConstraint = $sessionId > 0 ? " AND SDTE.SessionId = " . $sessionId : ""; $query = "SELECT SDTE.SessionId AS SessionId, COALESCE(SDTE.srcUserId,-1) AS UserId, SUM(CASE WHEN X.ReactTypeId=0 THEN X.NbHits ELSE 0 END) AS NbEnemyKilled, SUM(CASE WHEN X.ReactTypeId=1 THEN X.NbHits ELSE 0 END) AS NbCivilKilled, SUM(CASE WHEN X.ReactTypeId=2 THEN X.NbHits ELSE 0 END) AS NbPoliceKilled FROM " . SESSIONS_TABLE_NAME . " SD LEFT JOIN " . TRIGGEREVENTS_TABLE_NAME . " SDTE ON (SD.id = SDTE.sessionId) LEFT JOIN " . REACTEVENTS_TABLE_NAME . " SDRE ON (SDTE.sessionId=SDRE.srcEventSessionId AND SDTE.indexCount=SDRE.srcEventIndex) LEFT JOIN (SELECT TE.SessionId AS SessionId, RE.hitUserId AS TargetUserId, TE.srcUserId AS ShooterRoleId, COUNT(DISTINCT TE.indexCount, RE.hitTargetName) AS NbHits, RE.id AS ReactId, RE.ReactType AS ReactTypeId FROM " . TRIGGEREVENTS_TABLE_NAME . " TE, " . REACTEVENTS_TABLE_NAME . " RE WHERE TE.sessionId = RE.srcEventSessionId AND TE.indexCount = RE.srcEventIndex AND TE.indexCount!=-1 AND RE.ReactType!=-1 AND RE.TargetKilled=1 GROUP BY SessionId, TargetUserId, ShooterRoleId, ReactTypeId) AS X ON (X.SessionId=SDTE.SessionId AND X.TargetUserId=SDRE.hitUserId AND X.ReactId=SDRE.id) WHERE 1 " . $this->getCalibrationSessionsConstraint("SD") . $this->getSessionDurationConstraint("SD") . $sessionConstraint . " GROUP BY SDTE.SessionId"; // prepare and execute query $stmt = $this->conn->prepare($query); $stmt->execute(); return $stmt; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // debrief mission : get averages for given session and/or user public function getPrecisionAndReactionTimeForUser ($sessionId, $userId, $fromUser=-1) { // create SQL constraints for query $sessionConstraint = $sessionId > 0 ? " AND SDTE.SessionId = " . $sessionId : ""; $query = "SELECT SDTE.SessionId AS SessionId, SDTE.srcUserId AS UserId, AVG(X.AvgHitPrec) AS AveragePrecision, AVG(CASE WHEN X.HitPrecision > 0 AND X.ReactId > 0 THEN X.AvgReactTime END) AS AverageReactionTime FROM " . SESSIONS_TABLE_NAME . " SD LEFT JOIN " . TRIGGEREVENTS_TABLE_NAME . " SDTE ON (SD.id = SDTE.sessionId) LEFT JOIN " . REACTEVENTS_TABLE_NAME . " SDRE ON SDTE.sessionId=SDRE.srcEventSessionId AND SDTE.indexCount=SDRE.srcEventIndex LEFT JOIN (SELECT DISTINCT TE.SessionId AS SessionId, TE.srcUserId AS ShooterId, TE.indexCount, RE.hitTargetName, COALESCE(AVG(RE.hitPrecision),0) AS AvgHitPrec, COALESCE(AVG(RE.reactTime),0) AS AvgReactTime, COALESCE(RE.id,-1) AS ReactId, COALESCE(RE.hitPrecision,0) AS HitPrecision FROM " . TRIGGEREVENTS_TABLE_NAME . " TE LEFT JOIN " . REACTEVENTS_TABLE_NAME . " RE ON TE.indexCount=RE.srcEventIndex AND TE.sessionId=RE.srcEventSessionId GROUP BY TE.SessionId, TE.srcUserId, TE.indexCount, RE.id, RE.hitTargetName) AS X ON X.SessionId=SDTE.SessionId AND X.ShooterId=SDTE.srcUserId WHERE 1 " . $this->getCalibrationSessionsConstraint("SD") . $this->getSessionDurationConstraint("SD") . $sessionConstraint . " GROUP BY SDTE.SessionId, SDTE.srcUserId"; // prepare and execute query $stmt = $this->conn->prepare($query); $stmt->execute(); return $stmt; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // debrief mission : get given users results in given session public function getResultsForSession ($sessionId, $userId, $fromUserId=-1) { $userConstraint = $userId > 0 ? " AND SD.ShooterId=" . $userId : ""; $sessionConstraint = $sessionId > 0 ? " AND SD.SessionId=" . $sessionId : ""; $query = "SELECT SD.* FROM " . SESSIONDEBRIEFS_VIEW_NAME . " SD WHERE 1 " . $this->getCalibrationSessionsConstraint() . $userConstraint . $sessionConstraint . " GROUP BY SD.SessionId, SD.ShooterId, SD.ShotIndex"; //$query = "SELECT SD.* FROM (" . SESSIONDEBRIEFS_VIEW_QUERY . ") AS SD WHERE 1 " . $userConstraint . $sessionConstraint . " GROUP BY SD.SessionId, SD.ShooterId, SD.ShotIndex"; // prepare and execute query $stmt = $this->conn->prepare($query); $stmt->execute(); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $shot = SessionDebriefRow::withRow($row); array_push( $this->elements, $shot->toArray() ); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // debrief mission : get stats for given session and/or user /*public function getSessionDebrief ($sessionId, $userId, $fromUser=-1) { // prepare and execute query $stmt = $this->getDebriefRows($sessionId, $userId); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { // create a SessionDebriefRow with this data $stats = SessionDebriefRow::withRow($row); array_push( $this->elements, $stats->toArray() ); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public function getDebriefRows ($sessionId, $userId) { // create SQL constraints for query $sessionConstraint = $sessionId > 0 ? " AND SD.SessionId = " . $sessionId : ""; $userConstraint = $userId >= 0 ? " AND (SD.ShooterId = " . $userId . " OR SD.TargetUserId = " . $userId . ")" : ""; //$query = "SELECT SD.* FROM " . SESSIONDEBRIEFS_VIEW_NAME . " SD WHERE 1 " . $sessionConstraint . $userConstraint; $query = "SELECT SD.* FROM (" . SESSIONDEBRIEFS_VIEW_QUERY . ") AS SD WHERE 1 " . $sessionConstraint . $userConstraint; // prepare and execute query $stmt = $this->conn->prepare($query); $stmt->execute(); return $stmt; }*/ } ?>