Skip to content

Commit fb256fc

Browse files
committed
v1.01 - Keep track of quest completion times
Now register the time a player takes to complete a quest. If the player beats his/her PB, it's also updated. You may need to do a small SQL migration, see migration file in _private
1 parent 1cda1f2 commit fb256fc

File tree

6 files changed

+87
-16
lines changed

6 files changed

+87
-16
lines changed

GetBoard.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,27 @@
3939
require(__DIR__."/includes/dbConfig.php");
4040
$dbh = databaseConnect($dbHost, $dbUser, $dbPwd, $dbName); // Database Handler
4141
}
42-
$sql = "SELECT login, nickname FROM players WHERE quest_id = :questid AND status = 1 ORDER BY completion_date ASC";
42+
$sql = "SELECT login, nickname, completion_time_best as bestTime, completion_time_first as firstTime FROM players WHERE quest_id = :questid AND status = 1 ORDER BY completion_date_first ASC";
4343
$params = array(":questid" => $QuestId);
4444
$qh = $dbh->prepare($sql);
4545
$qexec = $qh->execute($params);
4646
$players = $qh->fetchAll(PDO::FETCH_ASSOC);
4747
$qh = null;
48+
// Format the players' times
49+
foreach ($players as $key => $player) {
50+
$tmpTime = gameTimeToDateInterval($player["firstTime"]);
51+
if ($tmpTime->h >= 1) {
52+
$players[$key]["firstTime"] = "more than 1 hour";
53+
} else {
54+
$players[$key]["firstTime"] = $tmpTime->format("%I:%S.").substr($tmpTime->f, 0, 3);
55+
}
56+
$tmpTime = gameTimeToDateInterval($player["bestTime"]);
57+
if ($tmpTime->h >= 1) {
58+
$players[$key]["bestTime"] = "more than 1 hour";
59+
} else {
60+
$players[$key]["bestTime"] = $tmpTime->format("%I:%S.").substr($tmpTime->f, 0, 3);
61+
}
62+
}
4863
// Return the results
4964
$playersList = array("Players" => $players,
5065
"QuestShortDesc" => ($QuestInfo["description_short"] != null) ? htmlentities($QuestInfo["description_short"], ENT_XML1) : "",

Main.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@
124124
#Struct SPlayer {
125125
Text login;
126126
Text nickname;
127+
Text firstTime;
128+
Text bestTime;
127129
}
128130
#Struct SBoardJsonResponse {
129131
SPlayer[] Players;
@@ -177,12 +179,14 @@
177179
declare persistent Text[][Integer] Pe_LocalTokensIds for Map;
178180
declare persistent Text[][Integer] Pe_PlayerPos for Map;
179181
declare persistent Integer[Integer] P_PlayerRaceStartTime for Map;
182+
declare persistent Integer[Integer] P_PlayerQuestStartTime for Map;
180183
declare persistent Text[Integer] P_QuestMapUid for Map;
181184
182185
Per_LocalTokensCollected[G_QuestId] = [];
183186
Pe_LocalTokensIds[G_QuestId] = [];
184187
Pe_PlayerPos[G_QuestId] = [];
185188
P_PlayerRaceStartTime[G_QuestId] = 0;
189+
P_PlayerQuestStartTime[G_QuestId] = 0;
186190
P_QuestMapUid[G_QuestId] = "";
187191
}
188192
@@ -204,9 +208,9 @@
204208
if (nbPlayers >= 1) {
205209
declare Text QuestTitleToDisplay = TextLib::Replace(QuestTitleList, "[count]", nbPlayers ^ " " ^ PluralPlayers);
206210
Message.SetText(Message.Value ^ QuestTitleToDisplay ^ "\n");
207-
if (CurPage == 1) Message.SetText(Message.Value ^ "$iNote: This list is cached 10 minutes.$z\n");
211+
if (CurPage == 1) Message.SetText(Message.Value ^ "$nNote: This list is cached 10 minutes, ordered by first completion date.$z\n");
208212
} else {
209-
Message.SetText(Message.Value ^ QuestTitleEmptyList ^ "\n$iNote: This list is cached 10 minutes.$z");
213+
Message.SetText(Message.Value ^ QuestTitleEmptyList ^ "\n$nNote: This list is cached 10 minutes.$z");
210214
}
211215
for(i, StartPos, MaxLimit)
212216
{
@@ -219,7 +223,7 @@
219223
{
220224
declare Text RankDisplay = TextLib::ToText(i+1);
221225
if (i < 9) RankDisplay = "0" ^ TextLib::ToText(i+1);
222-
Message.SetText(Message.Value ^ "\t\t\t\t\t\t\t$fff#" ^ RankDisplay ^ ":$g\t\t" ^ playersList[i].nickname ^ "$z $fff(" ^ playersList[i].login ^ ")\n");
226+
Message.SetText(Message.Value ^ "$fff#" ^ RankDisplay ^ ":$g\t" ^ playersList[i].nickname ^ "$z $fff(" ^ playersList[i].login ^ ") in " ^ playersList[i].firstTime ^ " (PB: " ^ playersList[i].bestTime ^ ")\n");
223227
}
224228
}
225229
}
@@ -261,6 +265,7 @@
261265
declare persistent Text[][Integer] Pe_LocalTokensIds for Map;
262266
declare persistent Text[][Integer] Pe_PlayerPos for Map;
263267
declare persistent Integer[Integer] P_PlayerRaceStartTime for Map;
268+
declare persistent Integer[Integer] P_PlayerQuestStartTime for Map;
264269
declare persistent Text[Integer] P_QuestMapUid for Map;
265270
declare persistent Boolean DebugMode for Map;
266271
G_QuestId = '.htmlentities($QuestId, ENT_XML1).';
@@ -346,6 +351,7 @@
346351
if (P_QuestMapUid[G_QuestId] == "") P_QuestMapUid[G_QuestId] = "'.htmlentities($QuestInfo["map_uid"], ENT_XML1).'";
347352
if (DebugMode) log("MapUid for quest " ^ TextLib::ToText(G_QuestId) ^ " set to: " ^ P_QuestMapUid[G_QuestId]);
348353
P_PlayerRaceStartTime[G_QuestId] = GUIPlayer.'.$startTimeVar.';
354+
P_PlayerQuestStartTime[G_QuestId] = GameTime;
349355
350356
// Check if the MapUid matches
351357
if (P_QuestMapUid[G_QuestId] != Map.MapInfo.MapUid) {
@@ -382,7 +388,7 @@
382388
}
383389
else
384390
{
385-
Message.SetText(QuestFullDesc ^ "\n\nHave fun!\n\nNote: You need to complete the quest in the same race from where you started it. If you restart (e.g. by pressing del), you\'ll need to start the quest again. Several quests can be done in the same race.");
391+
Message.SetText(QuestFullDesc ^ "\n\nHave fun!\n\n$nNote: You need to complete the quest in the same race from where you started it. If you restart (e.g. by pressing del), you\'ll need to start the quest again. Several quests can be done in the same race.$z");
386392
}
387393
388394
// Log all tokens for quest if debug activated
@@ -440,6 +446,7 @@
440446
if (P_QuestMapUid[G_QuestId] == "") P_QuestMapUid[G_QuestId] = "'.htmlentities($QuestInfo["map_uid"], ENT_XML1).'";
441447
if (DebugMode) log("MapUid for quest " ^ TextLib::ToText(G_QuestId) ^ " set to: " ^ P_QuestMapUid[G_QuestId]);
442448
P_PlayerRaceStartTime[G_QuestId] = GUIPlayer.'.$startTimeVar.';
449+
P_PlayerQuestStartTime[G_QuestId] = GameTime;
443450
444451
// Check if the MapUid matches
445452
if (P_QuestMapUid[G_QuestId] != Map.MapInfo.MapUid) {
@@ -506,9 +513,12 @@
506513
// Log all tokens for quest if debug activated
507514
declare Text Pe_PlayerPosString = LogMissingTokens();
508515
516+
// Get quest completion time
517+
declare Integer QuestCompletionTime = GameTime - P_PlayerQuestStartTime[G_QuestId];
518+
509519
declare CHttpRequest request;
510520
if (DebugMode) log("Quest " ^ TextLib::ToText(G_QuestId) ^ " completed. Processing...");
511-
request = Http.CreateGet("'.$urlToRequest.'UpdateQuest.php?QuestId=" ^ TextLib::ToText(G_QuestId) ^ "&Login=" ^ LocalUser.Login ^ "&UserName=" ^ LocalUser.Name ^ "&Positions=" ^ TextLib::URLEncode(Pe_PlayerPosString) ^ "&MapUid=" ^ P_QuestMapUid[G_QuestId] ^ "&" ^ Now);
521+
request = Http.CreateGet("'.$urlToRequest.'UpdateQuest.php?QuestId=" ^ TextLib::ToText(G_QuestId) ^ "&Login=" ^ LocalUser.Login ^ "&UserName=" ^ LocalUser.Name ^ "&Positions=" ^ TextLib::URLEncode(Pe_PlayerPosString) ^ "&MapUid=" ^ P_QuestMapUid[G_QuestId] ^ "&Time=" ^ TextLib::ToText(QuestCompletionTime) ^ "&" ^ Now);
512522
clearPersistentData();
513523
wait(request.IsCompleted);
514524
if (DebugMode) {

UpdateQuest.php

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
$UserName = filter_input(INPUT_GET, 'UserName');
1717
$Positions = urldecode(filter_input(INPUT_GET, 'Positions'));
1818
$MapUid = filter_input(INPUT_GET, 'MapUid');
19+
$Time = intval(filter_input(INPUT_GET, 'Time'));
1920

2021
// Check the player already finished or not
2122
$sql = "SELECT COUNT(*) FROM players WHERE quest_id = :questid AND login = :login";
@@ -24,9 +25,24 @@
2425
$qh = $dbh->prepare($sql);
2526
$qexec = $qh->execute($params);
2627
$alreadyFinished = $qh->fetchColumn();
28+
29+
$processingRequired = false; // See if we need to either insert completion or update best time
30+
31+
// Get completion info if the player already finished, see if the completime time is better
32+
if ($alreadyFinished == 1) {
33+
$sql = "SELECT completion_time_best FROM players WHERE quest_id = :questid AND login = :login";
34+
$params = array(":questid" => $QuestId,
35+
":login" => $Login);
36+
$qh = $dbh->prepare($sql);
37+
$qexec = $qh->execute($params);
38+
$bestTime = $qh->fetchColumn();
39+
if ($bestTime > $Time) $processingRequired = true;
40+
} else {
41+
$processingRequired = true;
42+
}
2743

28-
// Player never finished yet, it's the first time, starting the checks
29-
if($alreadyFinished == 0) {
44+
// Player never finished yet (first time) or best time beaten, starting the checks
45+
if($processingRequired == true) {
3046
// MapUid check //
3147
// Quest info are cached for 1h
3248
$cacheId = "tm2ml_board_q".$QuestId."_info";
@@ -102,25 +118,34 @@
102118

103119
if ($tokensPassed == count($tokens)) $checkPassed = true;
104120

105-
// Check OK, insert completion in db
121+
// Check OK, insert completion in db for a first time, update otherwise
106122
if ($checkPassed) {
107-
$sql = "INSERT INTO players (quest_id, login, nickname) VALUES (:questid, :login, :nickname)";
123+
if ($alreadyFinished == 0) {
124+
$sql = "INSERT INTO players (quest_id, login, nickname, completion_time_first, completion_time_best) VALUES (:questid, :login, :nickname, :time, :time)";
125+
} else {
126+
$sql = "UPDATE players SET nickname = :nickname, completion_date_best = NOW(), completion_time_best = :time WHERE quest_id = :questid AND login = :login";
127+
}
108128
$params = array(":login" => $Login,
109129
":questid" => $QuestId,
110-
":nickname" => $UserName);
130+
":nickname" => $UserName,
131+
":time" => $Time);
111132
$qh = $dbh->prepare($sql);
112133
$qexec = $qh->execute($params);
113134
$qh = null;
114135
if ($qexec) {
115-
echo "Congrats, you completed this quest!";
136+
if ($alreadyFinished == 0) {
137+
echo "Congrats, you completed this quest!";
138+
} else {
139+
echo "Congrats, you beat your best time for this quest!";
140+
}
116141
} else {
117142
echo "An error occured while processing your completion, sorry :/. Error: ".htmlentities($qh->errorInfo()[2], ENT_XML1)." (s".intval($qh->errorInfo()[0])."d".$qh->errorInfo()[1].")";
118143
}
119144
} else {
120145
echo "Sorry, seems like you didn't collect all the tokens or an error occured.";
121146
}
122147
} else {
123-
echo "You already completed this quest, hope you enjoyed!";
148+
echo "You already completed this quest and didn't beat your best time, hope you still enjoyed!";
124149
}
125150

126151
} else {
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
ALTER TABLE `players` CHANGE `completion_date` `completion_date_first` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;
2+
ALTER TABLE `players` ADD `completion_date_best` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `completion_date_first`, ADD `completion_time_first` INT NOT NULL AFTER `completion_date_best`, ADD `completion_time_best` INT NOT NULL AFTER `completion_time_first`;
3+
4+
-- You'll need to update the completion times yourself if values already exist in the table

_private/tm2ml_sidequests.sql

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
-- https://www.phpmyadmin.net/
44
--
55
-- Hôte : localhost
6-
-- Généré le : jeu. 14 nov. 2019 à 11:32
6+
-- Généré le : Dim 24 nov. 2019 à 20:22
77
-- Version du serveur : 10.3.17-MariaDB-0+deb10u1-log
8-
-- Version de PHP : 7.3.9-1~deb10u1
8+
-- Version de PHP : 7.3.11-1~deb10u1
99

1010
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
1111
SET AUTOCOMMIT = 0;
@@ -32,7 +32,10 @@ CREATE TABLE `players` (
3232
`login` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Login ManiaPlanet',
3333
`nickname` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'In-game NickName',
3434
`quest_id` smallint(5) UNSIGNED NOT NULL,
35-
`completion_date` datetime NOT NULL DEFAULT current_timestamp(),
35+
`completion_date_first` datetime NOT NULL DEFAULT current_timestamp(),
36+
`completion_date_best` datetime NOT NULL DEFAULT current_timestamp(),
37+
`completion_time_first` int(11) NOT NULL COMMENT 'Completion time for the first time (in milliseconds)',
38+
`completion_time_best` int(11) NOT NULL COMMENT 'Best completion time (in milliseconds)',
3639
`status` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '1 (default) = OK, 0 = NOK (cheat, ...)'
3740
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Players who finished a quest';
3841

includes/functions.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,18 @@ function isClientName($clientName) {
1919
// Check if a string starts with $needle
2020
function startsWith($haystack, $needle) {
2121
return ($needle === "" || strpos($haystack, $needle) === 0);
22+
}
23+
24+
// Convert a time in milliseconds as a DateInterval
25+
function gameTimeToDateInterval($time) {
26+
$seconds = floor($time / 1000);
27+
$dateInterval = new \DateInterval('P0D');
28+
$dateInterval->y = floor($seconds / (365 * 24 * 60 * 60));
29+
$dateInterval->m = floor(($seconds - ($dateInterval->y * 365 * 24 * 60 * 60)) / (30 * 24 * 60 * 60));
30+
$dateInterval->d = floor(($seconds - ($dateInterval->m * 30 * 24 * 60 * 60)) / (24 * 60 * 60));
31+
$dateInterval->h = floor(($seconds - ($dateInterval->d * 24 * 60 * 60)) / (60 * 60));
32+
$dateInterval->i = floor(($seconds - ($dateInterval->h * 60 * 60)) / (60));
33+
$dateInterval->s = floor($seconds % 60);
34+
$dateInterval->f = $time - ($seconds * 1000);
35+
return $dateInterval;
2236
}

0 commit comments

Comments
 (0)