3504: Display median time instead of average (fixes #3399) r=heinrich5991 a=def-

Do we want average too or just median?

## Checklist

- [x] Tested the change ingame
- [ ] Provided screenshots if it is a visual change
- [x] Tested in combination with possibly related configuration options
- [ ] Written a unit test if it works standalone, system.c especially
- [x] Considered possible null pointers and out of bounds array indexing
- [x] Changed no physics that affect existing maps
- [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional)


Co-authored-by: def <dennis@felsin9.de>
This commit is contained in:
bors[bot] 2021-01-17 16:40:50 +00:00 committed by GitHub
commit 174eeab80d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 45 additions and 10 deletions

View file

@ -21,7 +21,7 @@ public:
virtual IDbConnection *Copy() = 0;
// returns the database prefix
const char *GetPrefix() { return m_aPrefix; }
const char *GetPrefix() const { return m_aPrefix; }
virtual const char *BinaryCollate() const = 0;
// can be inserted into queries to convert a timestamp variable to the unix timestamp
virtual void ToUnixTimestamp(const char *pTimestamp, char *aBuf, unsigned int BufferSize) = 0;
@ -35,6 +35,8 @@ public:
virtual const char *InsertIgnore() const = 0;
// ORDER BY RANDOM()/RAND()
virtual const char *Random() const = 0;
// Get Median Map Time from l.Map
virtual const char *MedianMapTime(char *pBuffer, int BufferSize) const = 0;
enum Status
{

View file

@ -309,6 +309,18 @@ int CMysqlConnection::GetBlob(int Col, unsigned char *pBuffer, int BufferSize) c
#endif
}
const char *CMysqlConnection::MedianMapTime(char *pBuffer, int BufferSize) const
{
str_format(pBuffer, BufferSize,
"SELECT MEDIAN(Time) "
"OVER (PARTITION BY Map) "
"FROM %s_race "
"GROUP BY Map "
"HAVING Map = l.Map",
GetPrefix());
return pBuffer;
}
void CMysqlConnection::AddPoints(const char *pPlayer, int Points)
{
char aBuf[512];

View file

@ -35,6 +35,7 @@ public:
virtual const char *CollateNocase() const { return "CONVERT(? USING utf8mb4) COLLATE utf8mb4_general_ci"; }
virtual const char *InsertIgnore() const { return "INSERT IGNORE"; };
virtual const char *Random() const { return "RAND()"; };
virtual const char *MedianMapTime(char *pBuffer, int BufferSize) const;
virtual Status Connect();
virtual void Disconnect();

View file

@ -218,6 +218,23 @@ int CSqliteConnection::GetBlob(int Col, unsigned char *pBuffer, int BufferSize)
return Size;
}
const char *CSqliteConnection::MedianMapTime(char *pBuffer, int BufferSize) const
{
str_format(pBuffer, BufferSize,
"SELECT AVG("
" CASE counter %% 2 "
" WHEN 0 THEN CASE WHEN rn IN (counter / 2, counter / 2 + 1) THEN Time END "
" WHEN 1 THEN CASE WHEN rn = counter / 2 + 1 THEN Time END END) "
" OVER (PARTITION BY Map) AS Median "
"FROM ("
" SELECT *, ROW_NUMBER() "
" OVER (PARTITION BY Map ORDER BY Time) rn, COUNT(*) "
" OVER (PARTITION BY Map) counter "
" FROM %s_race where Map = l.Map) as r",
GetPrefix());
return pBuffer;
}
bool CSqliteConnection::Execute(const char *pQuery)
{
char *pErrorMsg;

View file

@ -22,6 +22,7 @@ public:
virtual const char *CollateNocase() const { return "? COLLATE NOCASE"; }
virtual const char *InsertIgnore() const { return "INSERT OR IGNORE"; };
virtual const char *Random() const { return "RANDOM()"; };
virtual const char *MedianMapTime(char *pBuffer, int BufferSize) const;
virtual Status Connect();
virtual void Disconnect();

View file

@ -355,12 +355,13 @@ bool CScore::MapInfoThread(IDbConnection *pSqlServer, const ISqlData *pGameData)
char aTimestamp[512];
pSqlServer->ToUnixTimestamp("l.Timestamp", aTimestamp, sizeof(aTimestamp));
char aBuf[1024];
char aMedianMapTime[2048];
char aBuf[4096];
str_format(aBuf, sizeof(aBuf),
"SELECT l.Map, l.Server, Mapper, Points, Stars, "
" (SELECT COUNT(Name) FROM %s_race WHERE Map = l.Map) AS Finishes, "
" (SELECT COUNT(DISTINCT Name) FROM %s_race WHERE Map = l.Map) AS Finishers, "
" (SELECT ROUND(AVG(Time)) FROM %s_race WHERE Map = l.Map) AS Average, "
" (%s) AS Median, "
" %s AS Stamp, "
" %s-%s AS Ago, "
" (SELECT MIN(Time) FROM %s_race WHERE Map = l.Map AND Name = ?) AS OwnTime "
@ -374,7 +375,8 @@ bool CScore::MapInfoThread(IDbConnection *pSqlServer, const ISqlData *pGameData)
" Map "
" LIMIT 1"
") as l;",
pSqlServer->GetPrefix(), pSqlServer->GetPrefix(), pSqlServer->GetPrefix(),
pSqlServer->GetPrefix(), pSqlServer->GetPrefix(),
pSqlServer->MedianMapTime(aMedianMapTime, sizeof(aMedianMapTime)),
aTimestamp, aCurrentTimestamp, aTimestamp,
pSqlServer->GetPrefix(), pSqlServer->GetPrefix(),
pSqlServer->CollateNocase());
@ -396,7 +398,7 @@ bool CScore::MapInfoThread(IDbConnection *pSqlServer, const ISqlData *pGameData)
int Stars = pSqlServer->GetInt(5);
int Finishes = pSqlServer->GetInt(6);
int Finishers = pSqlServer->GetInt(7);
int Average = pSqlServer->GetInt(8);
float Median = pSqlServer->GetInt(8);
int Stamp = pSqlServer->GetInt(9);
int Ago = pSqlServer->GetInt(10);
float OwnTime = pSqlServer->GetFloat(11);
@ -409,11 +411,11 @@ bool CScore::MapInfoThread(IDbConnection *pSqlServer, const ISqlData *pGameData)
str_format(aReleasedString, sizeof(aReleasedString), ", released %s ago", aAgoString);
}
char aAverageString[60] = "\0";
if(Average > 0)
char aMedianString[60] = "\0";
if(Median > 0)
{
str_time((int64)Average * 100, TIME_HOURS, aBuf, sizeof(aBuf));
str_format(aAverageString, sizeof(aAverageString), " in %s average", aBuf);
str_time((int64)Median * 100, TIME_HOURS, aBuf, sizeof(aBuf));
str_format(aMedianString, sizeof(aMedianString), " in %s median", aBuf);
}
char aStars[20];
@ -443,7 +445,7 @@ bool CScore::MapInfoThread(IDbConnection *pSqlServer, const ISqlData *pGameData)
aReleasedString,
Finishes, Finishes == 1 ? "finish" : "finishes",
Finishers, Finishers == 1 ? "tee" : "tees",
aAverageString, aOwnFinishesString);
aMedianString, aOwnFinishesString);
}
else
{