mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 01:58:19 +00:00
Merge #3669
3669: Local ranking info viewable in-game r=def- a=houseme-kyle The feature is based on the issue below: https://github.com/ddnet/ddnet/issues/3655 The main concern was the impact it would have if we had to double our ranking/point commands to support it. I took the feedback into account and combined the info into one command. The PR is in draft as I don't want to implement it for the remaining commands before getting thoughts on the approach taken. **Feature** Display local stats alongside global while using the same commands **Implementation** - /rank, /top5 etc to display their local position alongside global. - Top 5 changed to top 3 due to space limitations - Message limit increased by 1 - Don't show "requested by" when the request was made by the same individual **Reason for feature** - Foster growth in the smaller communities as they compete amongst each other in their own leaderboard - The names become more relevant to all communities as they can better relate to the names displayed **TODO** ~- Tested with production sqlite data but not MySQL storage~ ~- Expand concept to other relevant ranking commands~ (Example of NovaShock who is the top player in South Africa) ![image](https://user-images.githubusercontent.com/25198124/109824890-f03a4080-7c41-11eb-84dc-ad319cb98f1e.png) ![image](https://user-images.githubusercontent.com/25198124/109824910-f6302180-7c41-11eb-9013-f3254c08f068.png) ## Checklist - [x] Tested the change ingame - [x] 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 - [ ] Considered possible null pointers and out of bounds array indexing - [ ] 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: houseme-kyle <kyle@houseme.co.za> Co-authored-by: Kyle Bradley <kyle@house.me> Co-authored-by: = <=>
This commit is contained in:
commit
e2cb24b7d9
|
@ -35,7 +35,8 @@ CHAT_COMMAND("teamrank", "?r[player name]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConTe
|
|||
CHAT_COMMAND("rank", "?r[player name]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConRank, this, "Shows the rank of player with name r (your rank by default)")
|
||||
CHAT_COMMAND("top5team", "?s[player name] ?i[rank to start with]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConTeamTop5, this, "Shows five team ranks of the ladder or of a player beginning with rank i (1 by default, -1 for worst)")
|
||||
CHAT_COMMAND("teamtop5", "?s[player name] ?i[rank to start with]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConTeamTop5, this, "Shows five team ranks of the ladder or of a player beginning with rank i (1 by default, -1 for worst)")
|
||||
CHAT_COMMAND("top5", "?i[rank to start with]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConTop5, this, "Shows five ranks of the ladder beginning with rank i (1 by default, -1 for worst)")
|
||||
CHAT_COMMAND("top", "?i[rank to start with]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConTop, this, "Shows five ranks of the global and regional ladder beginning with rank i (1 by default, -1 for worst)")
|
||||
CHAT_COMMAND("top5", "?i[rank to start with]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConTop, this, "Shows five ranks of the global and regional ladder beginning with rank i (1 by default, -1 for worst)")
|
||||
CHAT_COMMAND("times", "?s[player name] ?i[number of times to skip]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConTimes, this, "/times ?s?i shows last 5 times of the server or of a player beginning with name s starting with time i (i = 1 by default, -1 for first)")
|
||||
CHAT_COMMAND("points", "?r[player name]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConPoints, this, "Shows the global points of a player beginning with name r (your rank by default)")
|
||||
CHAT_COMMAND("top5points", "?i[number]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConTopPoints, this, "Shows five points of the global point ladder beginning with rank i (1 by default)")
|
||||
|
|
|
@ -423,7 +423,7 @@ void CGameContext::ConTeamTop5(IConsole::IResult *pResult, void *pUserData)
|
|||
}
|
||||
}
|
||||
|
||||
void CGameContext::ConTop5(IConsole::IResult *pResult, void *pUserData)
|
||||
void CGameContext::ConTop(IConsole::IResult *pResult, void *pUserData)
|
||||
{
|
||||
CGameContext *pSelf = (CGameContext *)pUserData;
|
||||
if(!CheckClientID(pResult->m_ClientID))
|
||||
|
@ -431,15 +431,15 @@ void CGameContext::ConTop5(IConsole::IResult *pResult, void *pUserData)
|
|||
|
||||
if(g_Config.m_SvHideScore)
|
||||
{
|
||||
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "top5",
|
||||
"Showing the top 5 is not allowed on this server.");
|
||||
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "top",
|
||||
"Showing the top is not allowed on this server.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(pResult->NumArguments() > 0)
|
||||
pSelf->Score()->ShowTop5(pResult->m_ClientID, pResult->GetInteger(0));
|
||||
pSelf->Score()->ShowTop(pResult->m_ClientID, pResult->GetInteger(0));
|
||||
else
|
||||
pSelf->Score()->ShowTop5(pResult->m_ClientID);
|
||||
pSelf->Score()->ShowTop(pResult->m_ClientID);
|
||||
}
|
||||
|
||||
void CGameContext::ConTimes(IConsole::IResult *pResult, void *pUserData)
|
||||
|
|
|
@ -346,7 +346,7 @@ private:
|
|||
static void ConToggleSpecVoted(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConForcePause(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConTeamTop5(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConTop5(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConTop(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConTimes(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConPoints(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConTopPoints(IConsole::IResult *pResult, void *pUserData);
|
||||
|
|
|
@ -933,13 +933,18 @@ void CPlayer::ProcessScoreResult(CScorePlayerResult &Result)
|
|||
}
|
||||
break;
|
||||
case CScorePlayerResult::ALL:
|
||||
{
|
||||
int MessageClientId = m_ClientID;
|
||||
for(auto &aMessage : Result.m_Data.m_aaMessages)
|
||||
{
|
||||
if(aMessage[0] == 0)
|
||||
break;
|
||||
GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aMessage, m_ClientID);
|
||||
|
||||
GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aMessage, MessageClientId);
|
||||
MessageClientId = -1; // Prevent multi-messages being flagged as spam.
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CScorePlayerResult::BROADCAST:
|
||||
if(Result.m_Data.m_Broadcast[0] != 0)
|
||||
GameServer()->SendBroadcast(Result.m_Data.m_Broadcast, -1);
|
||||
|
|
|
@ -126,6 +126,7 @@ void CScore::ExecPlayerThread(
|
|||
auto Tmp = std::unique_ptr<CSqlPlayerRequest>(new CSqlPlayerRequest(pResult));
|
||||
str_copy(Tmp->m_Name, pName, sizeof(Tmp->m_Name));
|
||||
str_copy(Tmp->m_Map, g_Config.m_SvMap, sizeof(Tmp->m_Map));
|
||||
str_copy(Tmp->m_Server, g_Config.m_SvSqlServerName, sizeof(Tmp->m_Server));
|
||||
str_copy(Tmp->m_RequestingPlayer, Server()->ClientName(ClientID), sizeof(Tmp->m_RequestingPlayer));
|
||||
Tmp->m_Offset = Offset;
|
||||
|
||||
|
@ -766,32 +767,63 @@ bool CScore::ShowRankThread(IDbConnection *pSqlServer, const ISqlData *pGameData
|
|||
const CSqlPlayerRequest *pData = dynamic_cast<const CSqlPlayerRequest *>(pGameData);
|
||||
CScorePlayerResult *pResult = dynamic_cast<CScorePlayerResult *>(pGameData->m_pResult.get());
|
||||
|
||||
char aServerLike[16];
|
||||
str_format(aServerLike, sizeof(aServerLike), "%%%s%%", pData->m_Server);
|
||||
|
||||
// check sort method
|
||||
char aBuf[600];
|
||||
|
||||
str_format(aBuf, sizeof(aBuf),
|
||||
"SELECT Rank, Time, PercentRank "
|
||||
"FROM ("
|
||||
" SELECT RANK() OVER w AS Rank, PERCENT_RANK() OVER w as PercentRank, Name, MIN(Time) AS Time "
|
||||
" FROM %s_race "
|
||||
" WHERE Map = ? "
|
||||
" AND Server LIKE ?"
|
||||
" GROUP BY Name "
|
||||
" WINDOW w AS (ORDER BY Time)"
|
||||
") as a "
|
||||
"WHERE Name = ?;",
|
||||
pSqlServer->GetPrefix());
|
||||
|
||||
if(pSqlServer->PrepareStatement(aBuf, pError, ErrorSize))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
pSqlServer->BindString(1, pData->m_Map);
|
||||
pSqlServer->BindString(2, pData->m_Name);
|
||||
pSqlServer->BindString(2, aServerLike);
|
||||
pSqlServer->BindString(3, pData->m_Name);
|
||||
|
||||
bool End;
|
||||
if(pSqlServer->Step(&End, pError, ErrorSize))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
char aRegionalRank[16];
|
||||
if(End)
|
||||
{
|
||||
str_copy(aRegionalRank, "unranked", sizeof(aRegionalRank));
|
||||
}
|
||||
else
|
||||
{
|
||||
str_format(aRegionalRank, sizeof(aRegionalRank), "rank %d", pSqlServer->GetInt(1));
|
||||
}
|
||||
|
||||
const char *pAny = "%";
|
||||
|
||||
if(pSqlServer->PrepareStatement(aBuf, pError, ErrorSize))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
pSqlServer->BindString(1, pData->m_Map);
|
||||
pSqlServer->BindString(2, pAny);
|
||||
pSqlServer->BindString(3, pData->m_Name);
|
||||
|
||||
if(pSqlServer->Step(&End, pError, ErrorSize))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!End)
|
||||
{
|
||||
int Rank = pSqlServer->GetInt(1);
|
||||
|
@ -807,9 +839,23 @@ bool CScore::ShowRankThread(IDbConnection *pSqlServer, const ISqlData *pGameData
|
|||
else
|
||||
{
|
||||
pResult->m_MessageKind = CScorePlayerResult::ALL;
|
||||
str_format(pResult->m_Data.m_aaMessages[0], sizeof(pResult->m_Data.m_aaMessages[0]),
|
||||
"%d. %s Time: %s, better than %d%%, requested by %s",
|
||||
Rank, pData->m_Name, aBuf, BetterThanPercent, pData->m_RequestingPlayer);
|
||||
|
||||
if(str_comp_nocase(pData->m_RequestingPlayer, pData->m_Name) == 0)
|
||||
{
|
||||
str_format(pResult->m_Data.m_aaMessages[0], sizeof(pResult->m_Data.m_aaMessages[0]),
|
||||
"%s Time: %s, better than %d%%",
|
||||
pData->m_Name, aBuf, BetterThanPercent);
|
||||
}
|
||||
else
|
||||
{
|
||||
str_format(pResult->m_Data.m_aaMessages[0], sizeof(pResult->m_Data.m_aaMessages[0]),
|
||||
"%s Time: %s, better than %d%%, requested by %s",
|
||||
pData->m_Name, aBuf, BetterThanPercent, pData->m_RequestingPlayer);
|
||||
}
|
||||
|
||||
str_format(pResult->m_Data.m_aaMessages[1], sizeof(pResult->m_Data.m_aaMessages[1]),
|
||||
"Global rank %d || %s %s",
|
||||
Rank, pData->m_Server, aRegionalRank);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -910,20 +956,21 @@ bool CScore::ShowTeamRankThread(IDbConnection *pSqlServer, const ISqlData *pGame
|
|||
return false;
|
||||
}
|
||||
|
||||
void CScore::ShowTop5(int ClientID, int Offset)
|
||||
void CScore::ShowTop(int ClientID, int Offset)
|
||||
{
|
||||
if(RateLimitPlayer(ClientID))
|
||||
return;
|
||||
ExecPlayerThread(ShowTop5Thread, "show top5", ClientID, "", Offset);
|
||||
ExecPlayerThread(ShowTopThread, "show top5", ClientID, "", Offset);
|
||||
}
|
||||
|
||||
bool CScore::ShowTop5Thread(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize)
|
||||
bool CScore::ShowTopThread(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize)
|
||||
{
|
||||
const CSqlPlayerRequest *pData = dynamic_cast<const CSqlPlayerRequest *>(pGameData);
|
||||
CScorePlayerResult *pResult = dynamic_cast<CScorePlayerResult *>(pGameData->m_pResult.get());
|
||||
|
||||
int LimitStart = maximum(abs(pData->m_Offset) - 1, 0);
|
||||
const char *pOrder = pData->m_Offset >= 0 ? "ASC" : "DESC";
|
||||
const char *pAny = "%";
|
||||
|
||||
// check sort method
|
||||
char aBuf[512];
|
||||
|
@ -933,23 +980,27 @@ bool CScore::ShowTop5Thread(IDbConnection *pSqlServer, const ISqlData *pGameData
|
|||
" SELECT RANK() OVER w AS Rank, Name, MIN(Time) AS Time "
|
||||
" FROM %s_race "
|
||||
" WHERE Map = ? "
|
||||
" AND Server LIKE ? "
|
||||
" GROUP BY Name "
|
||||
" WINDOW w AS (ORDER BY Time)"
|
||||
") as a "
|
||||
"ORDER BY Rank %s "
|
||||
"LIMIT %d, 5;",
|
||||
"LIMIT %d, 3;",
|
||||
pSqlServer->GetPrefix(),
|
||||
pOrder,
|
||||
LimitStart);
|
||||
|
||||
if(pSqlServer->PrepareStatement(aBuf, pError, ErrorSize))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
pSqlServer->BindString(1, pData->m_Map);
|
||||
pSqlServer->BindString(2, pAny);
|
||||
|
||||
// show top5
|
||||
str_copy(pResult->m_Data.m_aaMessages[0], "----------- Top 5 -----------", sizeof(pResult->m_Data.m_aaMessages[0]));
|
||||
// show top
|
||||
str_copy(pResult->m_Data.m_aaMessages[0], "-----------< Global Top 3 >-----------", sizeof(pResult->m_Data.m_aaMessages[0]));
|
||||
|
||||
char aTime[32];
|
||||
int Line = 1;
|
||||
bool End = false;
|
||||
while(!pSqlServer->Step(&End, pError, ErrorSize) && !End)
|
||||
|
@ -957,17 +1008,43 @@ bool CScore::ShowTop5Thread(IDbConnection *pSqlServer, const ISqlData *pGameData
|
|||
char aName[MAX_NAME_LENGTH];
|
||||
pSqlServer->GetString(1, aName, sizeof(aName));
|
||||
float Time = pSqlServer->GetFloat(2);
|
||||
str_time_float(Time, TIME_HOURS_CENTISECS, aBuf, sizeof(aBuf));
|
||||
str_time_float(Time, TIME_HOURS_CENTISECS, aTime, sizeof(aTime));
|
||||
int Rank = pSqlServer->GetInt(3);
|
||||
str_format(pResult->m_Data.m_aaMessages[Line], sizeof(pResult->m_Data.m_aaMessages[Line]),
|
||||
"%d. %s Time: %s", Rank, aName, aBuf);
|
||||
"%d. %s Time: %s", Rank, aName, aTime);
|
||||
Line++;
|
||||
}
|
||||
|
||||
char aServerLike[16];
|
||||
str_format(aServerLike, sizeof(aServerLike), "%%%s%%", pData->m_Server);
|
||||
|
||||
if(pSqlServer->PrepareStatement(aBuf, pError, ErrorSize))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
pSqlServer->BindString(1, pData->m_Map);
|
||||
pSqlServer->BindString(2, aServerLike);
|
||||
|
||||
str_format(pResult->m_Data.m_aaMessages[Line], sizeof(pResult->m_Data.m_aaMessages[Line]),
|
||||
"-----------< %s Top 3 >-----------", pData->m_Server);
|
||||
Line++;
|
||||
|
||||
// show top
|
||||
while(!pSqlServer->Step(&End, pError, ErrorSize) && !End)
|
||||
{
|
||||
char aName[MAX_NAME_LENGTH];
|
||||
pSqlServer->GetString(1, aName, sizeof(aName));
|
||||
float Time = pSqlServer->GetFloat(2);
|
||||
str_time_float(Time, TIME_HOURS_CENTISECS, aTime, sizeof(aTime));
|
||||
int Rank = pSqlServer->GetInt(3);
|
||||
str_format(pResult->m_Data.m_aaMessages[Line], sizeof(pResult->m_Data.m_aaMessages[Line]),
|
||||
"%d. %s Time: %s", Rank, aName, aTime);
|
||||
Line++;
|
||||
}
|
||||
if(!End)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
str_copy(pResult->m_Data.m_aaMessages[Line], "-------------------------------", sizeof(pResult->m_Data.m_aaMessages[Line]));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ struct CScorePlayerResult : ISqlResult
|
|||
|
||||
enum
|
||||
{
|
||||
MAX_MESSAGES = 7,
|
||||
MAX_MESSAGES = 8,
|
||||
};
|
||||
|
||||
enum Variant
|
||||
|
@ -167,6 +167,7 @@ struct CSqlPlayerRequest : ISqlData
|
|||
char m_RequestingPlayer[MAX_NAME_LENGTH];
|
||||
// relevant for /top5 kind of requests
|
||||
int m_Offset;
|
||||
char m_Server[5];
|
||||
};
|
||||
|
||||
struct CSqlRandomMapRequest : ISqlData
|
||||
|
@ -291,7 +292,7 @@ class CScore
|
|||
static bool MapInfoThread(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
|
||||
static bool ShowRankThread(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
|
||||
static bool ShowTeamRankThread(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
|
||||
static bool ShowTop5Thread(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
|
||||
static bool ShowTopThread(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
|
||||
static bool ShowTeamTop5Thread(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
|
||||
static bool ShowPlayerTeamTop5Thread(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
|
||||
static bool ShowTimesThread(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
|
||||
|
@ -340,7 +341,7 @@ public:
|
|||
|
||||
void SaveTeamScore(int *pClientIDs, unsigned int Size, float Time, const char *pTimestamp);
|
||||
|
||||
void ShowTop5(int ClientID, int Offset = 1);
|
||||
void ShowTop(int ClientID, int Offset = 1);
|
||||
void ShowRank(int ClientID, const char *pName);
|
||||
|
||||
void ShowTeamTop5(int ClientID, int Offset = 1);
|
||||
|
|
Loading…
Reference in a new issue