Skip to content

Commit 10bfe7f

Browse files
authored
Add scoreboard popup with map info (ddnet#11668)
2 parents fceea87 + b4f6206 commit 10bfe7f

File tree

10 files changed

+120
-4
lines changed

10 files changed

+120
-4
lines changed

datasrc/network.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,4 +659,8 @@
659659
NetMessageEx("Cl_EnableSpectatorCount", "[email protected]", [
660660
NetBool("m_Enable"),
661661
]),
662+
663+
NetMessageEx("Sv_MapInfo", "[email protected]", [
664+
NetString("m_pDescription"),
665+
]),
662666
]

src/game/client/components/scoreboard.cpp

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,32 @@ bool CScoreboard::OnInput(const IInput::CEvent &Event)
143143

144144
void CScoreboard::RenderTitle(CUIRect TitleLabel, int Team, const char *pTitle, float TitleFontSize)
145145
{
146+
const bool IsMapTitle = !GameClient()->IsTeamPlay();
147+
if(IsMapTitle && m_MouseUnlocked && GameClient()->m_aMapDescription[0] != '\0')
148+
{
149+
const int ButtonResult = Ui()->DoButtonLogic(&m_MapTitleButtonId, 0, &TitleLabel, BUTTONFLAG_LEFT | BUTTONFLAG_RIGHT);
150+
if(ButtonResult != 0)
151+
{
152+
m_MapTitlePopupContext.m_pScoreboard = this;
153+
154+
m_MapTitlePopupContext.m_FontSize = 12.0f;
155+
const float MaxWidth = 300.0f;
156+
const float Margin = 5.0f;
157+
const char *pDescription = GameClient()->m_aMapDescription;
158+
const float TextWidth = minimum(std::ceil(TextRender()->TextWidth(m_MapTitlePopupContext.m_FontSize, pDescription) + 0.5f), MaxWidth);
159+
float TextHeight = 0.0f;
160+
STextSizeProperties TextSizeProps{};
161+
TextSizeProps.m_pHeight = &TextHeight;
162+
TextRender()->TextWidth(m_MapTitlePopupContext.m_FontSize, pDescription, -1, TextWidth, 0, TextSizeProps);
163+
164+
Ui()->DoPopupMenu(&m_MapTitlePopupContext, Ui()->MouseX(), Ui()->MouseY(), TextWidth + Margin * 2, TextHeight + Margin * 2, &m_MapTitlePopupContext, CMapTitlePopupContext::Render);
165+
}
166+
if(Ui()->HotItem() == &m_MapTitleButtonId)
167+
{
168+
TitleLabel.Draw(ColorRGBA(0.7f, 0.7f, 0.7f, 0.3f), IGraphics::CORNER_ALL, 5.0f);
169+
}
170+
}
171+
146172
SLabelProperties Props;
147173
Props.m_MaxWidth = TitleLabel.w;
148174
Props.m_EllipsisAtEnd = true;
@@ -204,18 +230,21 @@ void CScoreboard::RenderTitleBar(CUIRect TitleBar, int Team, const char *pTitle)
204230

205231
const float TitleFontSize = 20.0f;
206232
const float ScoreTextWidth = TextRender()->TextWidth(TitleFontSize, "00:00:00");
233+
const float TitleTextWidth = TextRender()->TextWidth(TitleFontSize, pTitle);
207234

208235
TitleBar.VMargin(10.0f, &TitleBar);
209236
CUIRect TitleLabel, ScoreLabel;
210237
if(Team == TEAM_RED)
211238
{
212239
TitleBar.VSplitRight(ScoreTextWidth, &TitleLabel, &ScoreLabel);
213240
TitleLabel.VSplitRight(5.0f, &TitleLabel, nullptr);
241+
TitleLabel.VSplitLeft(minimum(TitleTextWidth + 2.0f, TitleLabel.w), &TitleLabel, nullptr);
214242
}
215243
else
216244
{
217245
TitleBar.VSplitLeft(ScoreTextWidth, &ScoreLabel, &TitleLabel);
218246
TitleLabel.VSplitLeft(5.0f, nullptr, &TitleLabel);
247+
TitleLabel.VSplitRight(minimum(TitleTextWidth + 2.0f, TitleLabel.w), nullptr, &TitleLabel);
219248
}
220249

221250
RenderTitle(TitleLabel, Team, pTitle, TitleFontSize);
@@ -378,7 +407,7 @@ void CScoreboard::RenderSpectators(CUIRect Spectators)
378407
m_ScoreboardPopupContext.m_IsSpectating = true;
379408

380409
Ui()->DoPopupMenu(&m_ScoreboardPopupContext, Ui()->MouseX(), Ui()->MouseY(), 110.0f,
381-
m_ScoreboardPopupContext.m_IsLocal ? 30.0f : 60.0f, &m_ScoreboardPopupContext, PopupScoreboard);
410+
m_ScoreboardPopupContext.m_IsLocal ? 30.0f : 60.0f, &m_ScoreboardPopupContext, CScoreboardPopupContext::Render);
382411
}
383412

384413
if(Ui()->HotItem() == &m_aPlayers[pInfo->m_ClientId].m_PlayerButtonId || Ui()->HotItem() == &m_aPlayers[pInfo->m_ClientId].m_SpectatorSecondLineButtonId)
@@ -630,7 +659,7 @@ void CScoreboard::RenderScoreboard(CUIRect Scoreboard, int Team, int CountStart,
630659
m_ScoreboardPopupContext.m_IsSpectating = false;
631660

632661
Ui()->DoPopupMenu(&m_ScoreboardPopupContext, Ui()->MouseX(), Ui()->MouseY(), 110.0f,
633-
m_ScoreboardPopupContext.m_IsLocal ? 58.5f : 87.5f, &m_ScoreboardPopupContext, PopupScoreboard);
662+
m_ScoreboardPopupContext.m_IsLocal ? 58.5f : 87.5f, &m_ScoreboardPopupContext, CScoreboardPopupContext::Render);
634663
}
635664

636665
if(Ui()->HotItem() == &m_aPlayers[pInfo->m_ClientId].m_PlayerButtonId)
@@ -1044,7 +1073,7 @@ const char *CScoreboard::GetTeamName(int Team) const
10441073
return nullptr;
10451074
}
10461075

1047-
CUi::EPopupMenuFunctionResult CScoreboard::PopupScoreboard(void *pContext, CUIRect View, bool Active)
1076+
CUi::EPopupMenuFunctionResult CScoreboard::CScoreboardPopupContext::Render(void *pContext, CUIRect View, bool Active)
10481077
{
10491078
CScoreboardPopupContext *pPopupContext = static_cast<CScoreboardPopupContext *>(pContext);
10501079
CScoreboard *pScoreboard = pPopupContext->m_pScoreboard;
@@ -1152,3 +1181,13 @@ CUi::EPopupMenuFunctionResult CScoreboard::PopupScoreboard(void *pContext, CUIRe
11521181

11531182
return CUi::POPUP_KEEP_OPEN;
11541183
}
1184+
1185+
CUi::EPopupMenuFunctionResult CScoreboard::CMapTitlePopupContext::Render(void *pContext, CUIRect View, bool Active)
1186+
{
1187+
CMapTitlePopupContext *pPopupContext = static_cast<CMapTitlePopupContext *>(pContext);
1188+
CScoreboard *pScoreboard = pPopupContext->m_pScoreboard;
1189+
1190+
pScoreboard->TextRender()->Text(View.x, View.y, pPopupContext->m_FontSize, pScoreboard->GameClient()->m_aMapDescription, View.w);
1191+
1192+
return CUi::POPUP_KEEP_OPEN;
1193+
}

src/game/client/components/scoreboard.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,20 @@ class CScoreboard : public CComponent
5858
int m_ClientId;
5959
bool m_IsLocal;
6060
bool m_IsSpectating;
61+
62+
static CUi::EPopupMenuFunctionResult Render(void *pContext, CUIRect View, bool Active);
6163
} m_ScoreboardPopupContext;
6264

63-
static CUi::EPopupMenuFunctionResult PopupScoreboard(void *pContext, CUIRect View, bool Active);
65+
class CMapTitlePopupContext : public SPopupMenuId
66+
{
67+
public:
68+
CScoreboard *m_pScoreboard = nullptr;
69+
70+
float m_FontSize;
71+
72+
static CUi::EPopupMenuFunctionResult Render(void *pContext, CUIRect View, bool Active);
73+
} m_MapTitlePopupContext;
74+
char m_MapTitleButtonId;
6475

6576
class CPlayerElement
6677
{

src/game/client/gameclient.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,7 @@ void CGameClient::OnReset()
696696

697697
m_MapBestTimeSeconds = FinishTime::UNSET;
698698
m_MapBestTimeMillis = 0;
699+
m_aMapDescription[0] = '\0';
699700

700701
// m_MapBugs and m_aTuningList are reset in LoadMapSettings
701702

@@ -1229,6 +1230,11 @@ void CGameClient::OnMessage(int MsgId, CUnpacker *pUnpacker, int Conn, bool Dumm
12291230
// some PvP mods based on DDNet accidentally send a best time of 0, despite having no finished races
12301231
}
12311232
}
1233+
else if(MsgId == NETMSGTYPE_SV_MAPINFO)
1234+
{
1235+
CNetMsg_Sv_MapInfo *pMsg = static_cast<CNetMsg_Sv_MapInfo *>(pRawMsg);
1236+
str_copy(m_aMapDescription, pMsg->m_pDescription);
1237+
}
12321238
}
12331239

12341240
void CGameClient::OnStateChange(int NewState, int OldState)

src/game/client/gameclient.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,7 @@ class CGameClient : public IGameClient
887887
void CleanMultiViewId(int ClientId);
888888
int m_MapBestTimeSeconds;
889889
int m_MapBestTimeMillis;
890+
char m_aMapDescription[512];
890891

891892
private:
892893
std::vector<CSnapEntities> m_vSnapEntities;

src/game/server/ddracechat.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,14 @@ void CGameContext::ConMapInfo(IConsole::IResult *pResult, void *pUserData)
576576
if(!pPlayer)
577577
return;
578578

579+
// use cached map info for current map
580+
const bool IsCurrentMap = pResult->NumArguments() == 0 || str_comp_nocase(pResult->GetString(0), pSelf->Server()->GetMapName()) == 0;
581+
if(IsCurrentMap && pSelf->m_aMapInfoMessage[0] != '\0')
582+
{
583+
pSelf->SendChatTarget(pResult->m_ClientId, pSelf->m_aMapInfoMessage);
584+
return;
585+
}
586+
579587
if(pResult->NumArguments() > 0)
580588
pSelf->Score()->MapInfo(pResult->m_ClientId, pResult->GetString(0));
581589
else

src/game/server/gamecontext.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ CGameContext::CGameContext(bool Resetting) :
9393

9494
m_SqlRandomMapResult = nullptr;
9595

96+
m_pLoadMapInfoResult = nullptr;
97+
m_aMapInfoMessage[0] = '\0';
98+
9699
m_pScore = nullptr;
97100

98101
m_VoteCreator = -1;
@@ -1371,6 +1374,19 @@ void CGameContext::OnTick()
13711374
m_SqlRandomMapResult = nullptr;
13721375
}
13731376

1377+
// check for map info result from database
1378+
if(m_pLoadMapInfoResult != nullptr && m_pLoadMapInfoResult->m_Completed)
1379+
{
1380+
if(m_pLoadMapInfoResult->m_Success && m_pLoadMapInfoResult->m_Data.m_aaMessages[0][0] != '\0')
1381+
{
1382+
str_copy(m_aMapInfoMessage, m_pLoadMapInfoResult->m_Data.m_aaMessages[0]);
1383+
CNetMsg_Sv_MapInfo MapInfoMsg;
1384+
MapInfoMsg.m_pDescription = m_aMapInfoMessage;
1385+
Server()->SendPackMsg(&MapInfoMsg, MSGFLAG_VITAL | MSGFLAG_NORECORD, -1);
1386+
}
1387+
m_pLoadMapInfoResult = nullptr;
1388+
}
1389+
13741390
// Record player position at the end of the tick
13751391
if(m_TeeHistorianActive)
13761392
{
@@ -1692,6 +1708,14 @@ void CGameContext::OnClientEnter(int ClientId)
16921708

16931709
Server()->ExpireServerInfo();
16941710

1711+
// send map info if loaded from database
1712+
if(m_aMapInfoMessage[0] != '\0')
1713+
{
1714+
CNetMsg_Sv_MapInfo MapInfoMsg;
1715+
MapInfoMsg.m_pDescription = m_aMapInfoMessage;
1716+
Server()->SendPackMsg(&MapInfoMsg, MSGFLAG_VITAL | MSGFLAG_NORECORD, ClientId);
1717+
}
1718+
16951719
CPlayer *pNewPlayer = m_apPlayers[ClientId];
16961720
mem_zero(&m_aLastPlayerInput[ClientId], sizeof(m_aLastPlayerInput[ClientId]));
16971721
m_aPlayerHasInput[ClientId] = false;
@@ -4268,6 +4292,9 @@ void CGameContext::OnInit(const void *pPersistentData)
42684292
m_pScore = new CScore(this, ((CServer *)Server())->DbPool());
42694293
}
42704294

4295+
// load map info from database
4296+
Score()->LoadMapInfo();
4297+
42714298
// create all entities from the game layer
42724299
CreateAllEntities(true);
42734300

src/game/server/gamecontext.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class IEngine;
5656
class IStorage;
5757
struct CAntibotRoundData;
5858
struct CScoreRandomMapResult;
59+
struct CScorePlayerResult;
5960

6061
struct CSnapContext
6162
{
@@ -410,6 +411,10 @@ class CGameContext : public IGameServer
410411

411412
std::shared_ptr<CScoreRandomMapResult> m_SqlRandomMapResult;
412413

414+
// cached map info from database
415+
std::shared_ptr<CScorePlayerResult> m_pLoadMapInfoResult;
416+
char m_aMapInfoMessage[512];
417+
413418
private:
414419
// starting 1 to make 0 the special value "no client id"
415420
uint32_t m_NextUniqueClientId = 1;

src/game/server/score.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,20 @@ void CScore::LoadBestTime()
124124
m_pPool->Execute(CScoreWorker::LoadBestTime, std::move(Tmp), "load best time");
125125
}
126126

127+
void CScore::LoadMapInfo()
128+
{
129+
if(m_pGameServer->m_pLoadMapInfoResult)
130+
return; // already in progress
131+
132+
auto pResult = std::make_shared<CScorePlayerResult>();
133+
m_pGameServer->m_pLoadMapInfoResult = pResult;
134+
135+
auto Tmp = std::make_unique<CSqlPlayerRequest>(pResult);
136+
str_copy(Tmp->m_aName, Server()->GetMapName(), sizeof(Tmp->m_aName));
137+
Tmp->m_aRequestingPlayer[0] = '\0'; // no player, so no "your time" in result
138+
m_pPool->Execute(CScoreWorker::MapInfo, std::move(Tmp), "load map info");
139+
}
140+
127141
void CScore::LoadPlayerData(int ClientId, const char *pName)
128142
{
129143
ExecPlayerThread(CScoreWorker::LoadPlayerData, "load player data", ClientId, pName, 0);

src/game/server/score.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class CScore
4444
CPlayerData *PlayerData(int Id) { return &m_aPlayerData[Id]; }
4545

4646
void LoadBestTime();
47+
void LoadMapInfo();
4748
void MapInfo(int ClientId, const char *pMapName);
4849
void MapVote(int ClientId, const char *pMapName);
4950
void LoadPlayerData(int ClientId, const char *pName = "");

0 commit comments

Comments
 (0)