Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3027,6 +3027,8 @@ if(CLIENT)
components/bestclient/gif_bubbles.h
components/bestclient/ego_finished_maps.cpp
components/bestclient/ego_finished_maps.h
components/bestclient/legit_finished_maps.cpp
components/bestclient/legit_finished_maps.h
components/bestclient/ego_tiles_prediction.cpp
components/bestclient/ego_tiles_prediction.h
components/bestclient/edgehelper.cpp
Expand Down
4 changes: 4 additions & 0 deletions data/BestClient/languages/russian.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,10 @@ Short EGO/KoG server name
Show finished map on EGO
== Показывать пройденную карту на EGO

[BestClient]
Show finished map on Legit Network
== Показывать пройденную карту на Legit Network

[BestClient]
Use BestClient MasterServer
== Использовать мастерсервер BestClient
Expand Down
4 changes: 4 additions & 0 deletions data/BestClient/languages/simplified_chinese.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,10 @@ Short EGO/KoG server name
Show finished map on EGO
== 在 EGO 上显示已完成地图

[BestClient]
Show finished map on Legit Network
== 在 Legit Network 上显示已完成地图

[BestClient]
Use BestClient MasterServer
== 使用 BestClient 主服务器
Expand Down
1 change: 1 addition & 0 deletions src/engine/shared/config_variables_bestclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ MACRO_CONFIG_INT(BcAutoServerListRefresh, bc_auto_server_list_refresh, 0, 0, 1,
MACRO_CONFIG_INT(BcAutoServerListRefreshSeconds, bc_auto_server_list_refresh_seconds, 10, 1, 300, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Auto refresh interval for the server browser list in seconds")
MACRO_CONFIG_INT(BcUseShortKogServerName, bc_use_short_kog_server_name, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Use short names for E-Gores and KoG servers in browser list")
MACRO_CONFIG_INT(BcShowFinishedMapOnEgo, bc_show_finished_map_on_ego, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show completed EGO maps in the server browser")
MACRO_CONFIG_INT(BcShowFinishedMapOnLegit, bc_show_finished_map_on_legit, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show completed Legit Network maps in the server browser")
MACRO_CONFIG_INT(BcMastersrv, bc_mastersrv, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Use the BestClient master server mirror for the server browser")
MACRO_CONFIG_INT(BcServerMapPreview, bc_server_map_preview, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show a live map preview under the selected server")
MACRO_CONFIG_INT(BrFilterBestclient, br_filter_bestclient, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Filter out servers with no BestClient users")
Expand Down
159 changes: 159 additions & 0 deletions src/game/client/components/bestclient/legit_finished_maps.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#include "legit_finished_maps.h"

#include <base/str.h>
#include <base/time.h>

#include <engine/client.h>
#include <engine/http.h>
#include <engine/serverbrowser.h>
#include <engine/shared/config.h>
#include <engine/shared/json.h>

namespace
{
enum class ELegitMode
{
NONE,
GORES,
DDRACE,
};

ELegitMode LegitMode(const CServerInfo *pServer)
{
if(!pServer || (str_comp_nocase(pServer->m_aCommunityId, "legit") != 0 && str_find_nocase(pServer->m_aName, "Legit Network") == nullptr))
return ELegitMode::NONE;

if(str_find_nocase(pServer->m_aGameType, "Gores") != nullptr || str_find_nocase(pServer->m_aName, "Gores") != nullptr)
return ELegitMode::GORES;
if(str_find_nocase(pServer->m_aGameType, "DDraceNetwork") != nullptr)
return ELegitMode::DDRACE;
return ELegitMode::NONE;
}

bool ParseMapArray(const json_value *pRoot, const char *pKey, std::unordered_set<std::string> *pMaps)
{
const json_value *pArray = json_object_get(pRoot, pKey);
if(pArray == &json_value_none || pArray->type != json_array)
return false;

for(unsigned i = 0; i < pArray->u.array.length; ++i)
{
const json_value *pMap = pArray->u.array.values[i];
if(pMap && pMap->type == json_string && pMap->u.string.ptr[0] != '\0')
pMaps->emplace(pMap->u.string.ptr);
}
return true;
}

bool ParseFinishedMaps(const json_value *pRoot, std::unordered_set<std::string> *pGoresMaps, std::unordered_set<std::string> *pDDraceMaps)
{
if(!pRoot || pRoot == &json_value_none || pRoot->type != json_object)
return false;
return ParseMapArray(pRoot, "gores", pGoresMaps) && ParseMapArray(pRoot, "ddrace", pDDraceMaps);
}
}

bool CLegitFinishedMaps::Enabled() const
{
return g_Config.m_BcShowFinishedMapOnLegit != 0;
}

bool CLegitFinishedMaps::IsFinishedMap(const CServerInfo *pServer) const
{
switch(LegitMode(pServer))
{
case ELegitMode::GORES:
return Enabled() && m_GoresFinishedMaps.contains(pServer->m_aMap);
case ELegitMode::DDRACE:
return Enabled() && m_DDraceFinishedMaps.contains(pServer->m_aMap);
case ELegitMode::NONE:
return false;
}
return false;
}

void CLegitFinishedMaps::ResetRequest()
{
if(m_pTask)
m_pTask->Abort();
m_pTask.reset();
}

void CLegitFinishedMaps::OnReset()
{
ResetRequest();
}

void CLegitFinishedMaps::StartRequest()
{
char aEscapedPlayerName[256];
EscapeUrl(aEscapedPlayerName, sizeof(aEscapedPlayerName), m_aPlayerName);

char aUrl[512];
str_format(aUrl, sizeof(aUrl), "https://legit.tw/api_finished_maps.php?name=%s", aEscapedPlayerName);

m_pTask = HttpGet(aUrl);
m_pTask->Timeout(CTimeout{8000, 0, 500, 5});
m_pTask->LogProgress(HTTPLOG::FAILURE);
m_pTask->FailOnErrorStatus(false);
Http()->Run(m_pTask);
}

void CLegitFinishedMaps::ProcessRequest()
{
if(!m_pTask || !m_pTask->Done())
return;

bool Success = false;
if(m_pTask->State() == EHttpState::DONE && m_pTask->StatusCode() == 200)
{
json_value *pRoot = m_pTask->ResultJson();
std::unordered_set<std::string> GoresMaps;
std::unordered_set<std::string> DDraceMaps;
Success = ParseFinishedMaps(pRoot, &GoresMaps, &DDraceMaps);
if(Success)
{
m_GoresFinishedMaps = std::move(GoresMaps);
m_DDraceFinishedMaps = std::move(DDraceMaps);
str_copy(m_aLoadedPlayerName, m_aPlayerName, sizeof(m_aLoadedPlayerName));
}
if(pRoot)
json_value_free(pRoot);
}

if(!Success)
m_NextRetry = time_get() + RETRY_DELAY_SECONDS * time_freq();
m_pTask.reset();
}

void CLegitFinishedMaps::OnUpdate()
{
if(!Enabled())
{
ResetRequest();
m_GoresFinishedMaps.clear();
m_DDraceFinishedMaps.clear();
m_aPlayerName[0] = '\0';
m_aLoadedPlayerName[0] = '\0';
m_NextRetry = 0;
return;
}

const char *pPlayerName = Client()->PlayerName();
if(str_comp(m_aPlayerName, pPlayerName) != 0)
{
ResetRequest();
m_GoresFinishedMaps.clear();
m_DDraceFinishedMaps.clear();
m_aLoadedPlayerName[0] = '\0';
m_NextRetry = 0;
str_copy(m_aPlayerName, pPlayerName, sizeof(m_aPlayerName));
}

ProcessRequest();

if(!m_aPlayerName[0] || m_pTask || str_comp(m_aPlayerName, m_aLoadedPlayerName) == 0 || time_get() < m_NextRetry)
return;

StartRequest();
}
43 changes: 43 additions & 0 deletions src/game/client/components/bestclient/legit_finished_maps.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#ifndef GAME_CLIENT_COMPONENTS_BESTCLIENT_LEGIT_FINISHED_MAPS_H
#define GAME_CLIENT_COMPONENTS_BESTCLIENT_LEGIT_FINISHED_MAPS_H

#include <game/client/component.h>

#include <memory>
#include <string>
#include <unordered_set>

class IHttpRequest;
class CServerInfo;

class CLegitFinishedMaps : public CComponent
{
public:
int Sizeof() const override { return sizeof(*this); }

void OnReset() override;
void OnUpdate() override;

bool IsFinishedMap(const CServerInfo *pServer) const;

private:
enum
{
PLAYER_NAME_LENGTH = 64,
RETRY_DELAY_SECONDS = 30,
};

std::unordered_set<std::string> m_GoresFinishedMaps;
std::unordered_set<std::string> m_DDraceFinishedMaps;
std::shared_ptr<IHttpRequest> m_pTask;
char m_aPlayerName[PLAYER_NAME_LENGTH] = "";
char m_aLoadedPlayerName[PLAYER_NAME_LENGTH] = "";
int64_t m_NextRetry = 0;

bool Enabled() const;
void ResetRequest();
void StartRequest();
void ProcessRequest();
};

#endif
7 changes: 6 additions & 1 deletion src/game/client/components/bestclient/menus_bestclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3962,7 +3962,7 @@ void CMenus::RenderSettingsBestClientOthers(CUIRect MainView)

Column.HSplitTop(MarginBetweenViews, nullptr, &Column);

const float BrowserUtilsBlockHeight = 6.0f * LineSize + 5.0f * MarginSmall;
const float BrowserUtilsBlockHeight = 7.0f * LineSize + 6.0f * MarginSmall;

CUIRect BrowserUtilsBlock;
Column.HSplitTop(BrowserUtilsBlockHeight, &BrowserUtilsBlock, &Column);
Expand Down Expand Up @@ -3995,6 +3995,11 @@ void CMenus::RenderSettingsBestClientOthers(CUIRect MainView)
BcMenuBadges::DrawNew(Graphics(), Ui(), TextRender(), &Content, MarginSmall);
DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_BcShowFinishedMapOnEgo, BcLocalize("Show finished map on EGO"), &g_Config.m_BcShowFinishedMapOnEgo, &Content, LineSize);

BrowserUtilsBlock.HSplitTop(MarginSmall, nullptr, &BrowserUtilsBlock);
BrowserUtilsBlock.HSplitTop(LineSize, &Content, &BrowserUtilsBlock);
BcMenuBadges::DrawNew(Graphics(), Ui(), TextRender(), &Content, MarginSmall);
DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_BcShowFinishedMapOnLegit, BcLocalize("Show finished map on Legit Network"), &g_Config.m_BcShowFinishedMapOnLegit, &Content, LineSize);

BrowserUtilsBlock.HSplitTop(MarginSmall, nullptr, &BrowserUtilsBlock);
BrowserUtilsBlock.HSplitTop(LineSize, &Content, &BrowserUtilsBlock);
DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_BcMastersrv, BcLocalize("Use BestClient MasterServer"), &g_Config.m_BcMastersrv, &Content, LineSize);
Expand Down
2 changes: 1 addition & 1 deletion src/game/client/components/menus_browser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View, bool &WasListboxItemAct
Button.VMargin(4.0f, &Button);
Button.VSplitLeft(Button.h, &Icon, &Button);
// bestclient
if((g_Config.m_BrIndicateFinished && pItem->m_HasRank == CServerInfo::RANK_RANKED) || GameClient()->m_EgoFinishedMaps.IsFinishedMap(pItem))
if((g_Config.m_BrIndicateFinished && pItem->m_HasRank == CServerInfo::RANK_RANKED) || GameClient()->m_EgoFinishedMaps.IsFinishedMap(pItem) || GameClient()->m_LegitFinishedMaps.IsFinishedMap(pItem))
// bestclient
{
Icon.Margin(2.0f, &Icon);
Expand Down
1 change: 1 addition & 0 deletions src/game/client/gameclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ void CGameClient::OnConsoleInit()
&m_BrowserUtils, // bestclient
&m_TwitchChat, // bestclient
&m_EgoFinishedMaps, // bestclient
&m_LegitFinishedMaps, // bestclient
&m_ShowPoints, // bestclient
&m_ClientIndicator, // bestclient
&m_VoiceChat, // bestclient
Expand Down
2 changes: 2 additions & 0 deletions src/game/client/gameclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include "components/bestclient/music_player.h" // bestclient
#include "components/bestclient/browser_utils.h" // bestclient
#include "components/bestclient/ego_finished_maps.h" // bestclient
#include "components/bestclient/legit_finished_maps.h" // bestclient
#include "components/bestclient/ego_tiles_prediction.h" // bestclient
#include "components/bestclient/edgehelper.h" // bestclient
#include "components/bestclient/fast_practice.h" // bestclient
Expand Down Expand Up @@ -256,6 +257,7 @@ class CGameClient : public IGameClient
CBrowserUtils m_BrowserUtils; // bestclient
CTwitchChat m_TwitchChat; // bestclient
CEgoFinishedMaps m_EgoFinishedMaps; // bestclient
CLegitFinishedMaps m_LegitFinishedMaps; // bestclient
CEdgeHelper m_EdgeHelper; // bestclient
CFinishPrediction m_FinishPrediction; // bestclient
CKeystrokes m_Keystrokes; // bestclient
Expand Down
Loading