forked from Mirrors/openclonk
714 lines
24 KiB
C++
714 lines
24 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 2006-2009, RedWolf Design GmbH, http://www.clonk.de/
|
|
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
|
|
*
|
|
* Distributed under the terms of the ISC license; see accompanying file
|
|
* "COPYING" for details.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
|
* See accompanying file "TRADEMARK" for details.
|
|
*
|
|
* To redistribute this file separately, substitute the full license texts
|
|
* for the above references.
|
|
*/
|
|
|
|
/* engine handler of league system */
|
|
|
|
#include "C4Include.h"
|
|
#include "network/C4League.h"
|
|
|
|
#include "control/C4RoundResults.h"
|
|
#include "graphics/C4GraphicsResource.h"
|
|
|
|
// *** C4LeagueRequestHead
|
|
|
|
void C4LeagueRequestHead::CompileFunc(StdCompiler *pComp)
|
|
{
|
|
|
|
StdEnumEntry<C4LeagueAction> Actions[] =
|
|
{
|
|
{ "Start", C4LA_Start },
|
|
{ "Update", C4LA_Update },
|
|
{ "End", C4LA_End },
|
|
{ "Join", C4LA_PlrAuthCheck },
|
|
|
|
{ "", C4LA_RefQuery },
|
|
{ "Auth", C4LA_PlrAuth },
|
|
|
|
{ "ReportDisconnect", C4LA_ReportDisconnect },
|
|
};
|
|
|
|
pComp->Value(mkNamingAdapt(mkEnumAdaptT<uint8_t>(eAction, Actions), "Action", C4LA_RefQuery));
|
|
pComp->Value(mkNamingAdapt(mkParAdapt(CSID, StdCompiler::RCT_IdtfAllowEmpty), "CSID", ""));
|
|
pComp->Value(mkNamingAdapt(mkParAdapt(AUID, StdCompiler::RCT_IdtfAllowEmpty), "AUID", ""));
|
|
|
|
// Auth
|
|
pComp->Value(mkNamingAdapt(mkParAdapt(Account, StdCompiler::RCT_All), "Account", ""));
|
|
pComp->Value(mkNamingAdapt(mkParAdapt(Password, StdCompiler::RCT_All), "Password", ""));
|
|
pComp->Value(mkNamingAdapt(mkParAdapt(NewAccount, StdCompiler::RCT_All), "NewAccount", ""));
|
|
pComp->Value(mkNamingAdapt(mkParAdapt(NewPassword, StdCompiler::RCT_All), "NewPassword", ""));
|
|
pComp->Value(mkNamingAdapt(fRememberLogin, "RememberLogin", false));
|
|
|
|
}
|
|
|
|
void C4LeagueRequestHead::SetAuth(const char *szAccount, const char *szPassword, bool fRememberLogin)
|
|
{
|
|
Account = szAccount;
|
|
Password = szPassword;
|
|
this->fRememberLogin = fRememberLogin;
|
|
}
|
|
|
|
void C4LeagueRequestHead::SetNewAccount(const char *szNewAccount)
|
|
{
|
|
NewAccount = szNewAccount;
|
|
}
|
|
|
|
void C4LeagueRequestHead::SetNewPassword(const char *szNewPassword)
|
|
{
|
|
NewPassword = szNewPassword;
|
|
}
|
|
|
|
// *** C4LeagueReportDisconnectHead
|
|
|
|
void C4LeagueReportDisconnectHead::CompileFunc(StdCompiler *pComp)
|
|
{
|
|
// inherited fields
|
|
C4LeagueRequestHead::CompileFunc(pComp);
|
|
// reason
|
|
StdEnumEntry<C4LeagueDisconnectReason> Reasons[] =
|
|
{
|
|
{ "", C4LDR_Unknown },
|
|
{ "ConnectionFailed", C4LDR_ConnectionFailed},
|
|
{ "Desync", C4LDR_Desync },
|
|
};
|
|
pComp->Value(mkNamingAdapt(mkEnumAdaptT<uint8_t>(eReason, Reasons), "Reason", C4LDR_Unknown));
|
|
}
|
|
|
|
// *** C4LeagueRequestHeadEnd
|
|
|
|
void C4LeagueRequestHeadEnd::CompileFunc(StdCompiler *pComp)
|
|
{
|
|
C4LeagueRequestHead::CompileFunc(pComp);
|
|
pComp->Value(mkNamingAdapt(mkParAdapt(RecordName, StdCompiler::RCT_All), "RecordName", ""));
|
|
if (RecordName.getLength())
|
|
pComp->Value(mkNamingAdapt(mkHexAdapt(RecordSHA, sizeof(RecordSHA)), "RecordSHA"));
|
|
}
|
|
|
|
// *** C4LeagueResponseHead
|
|
|
|
void C4LeagueResponseHead::CompileFunc(StdCompiler *pComp)
|
|
{
|
|
pComp->Value(mkNamingAdapt(mkParAdapt(Status, StdCompiler::RCT_IdtfAllowEmpty), "Status", ""));
|
|
pComp->Value(mkNamingAdapt(mkParAdapt(CSID, StdCompiler::RCT_IdtfAllowEmpty), "CSID", ""));
|
|
pComp->Value(mkNamingAdapt(mkParAdapt(Message, StdCompiler::RCT_All), "Message", ""));
|
|
|
|
// Auth
|
|
pComp->Value(mkNamingAdapt(mkParAdapt(Account, StdCompiler::RCT_All), "Account", ""));
|
|
pComp->Value(mkNamingAdapt(mkParAdapt(AUID, StdCompiler::RCT_All), "AUID", ""));
|
|
pComp->Value(mkNamingAdapt(mkParAdapt(FBID, StdCompiler::RCT_All), "FBID", ""));
|
|
pComp->Value(mkNamingAdapt(mkParAdapt(LoginToken, StdCompiler::RCT_All), "LoginToken", ""));
|
|
}
|
|
|
|
// *** C4LeagueResponseHeadStart
|
|
|
|
void C4LeagueResponseHeadStart::CompileFunc(StdCompiler *pComp)
|
|
{
|
|
// Base members
|
|
C4LeagueResponseHead::CompileFunc(pComp);
|
|
|
|
// League name
|
|
pComp->Value(mkNamingAdapt(League, "League", ""));
|
|
pComp->Value(mkNamingAdapt(StreamingAddr, "StreamTo", ""));
|
|
pComp->Value(mkNamingCountAdapt(fHaveSeed, "Seed"));
|
|
if (fHaveSeed)
|
|
pComp->Value(mkNamingAdapt(iSeed, "Seed", (int32_t)0));
|
|
pComp->Value(mkNamingAdapt(iMaxPlayers, "MaxPlayers", (int32_t)0));
|
|
}
|
|
|
|
// *** C4LeagueResponseHeadUpdate
|
|
|
|
void C4LeagueResponseHeadUpdate::CompileFunc(StdCompiler *pComp)
|
|
{
|
|
// Base members
|
|
C4LeagueResponseHead::CompileFunc(pComp);
|
|
|
|
// League name
|
|
pComp->Value(mkNamingAdapt(League, "League", ""));
|
|
|
|
// player infos
|
|
pComp->Value(mkNamingAdapt(PlrInfos, "PlayerInfos"));
|
|
}
|
|
|
|
// *** C4LeagueRequestHeadAuthCheck
|
|
|
|
int32_t C4LeagueResponseHeadAuthCheck::getScore(const char *szLeague) const
|
|
{
|
|
for (int32_t i = 0; i < C4NetMaxLeagues; i++)
|
|
if (Leagues[i] == szLeague)
|
|
return Scores[i];
|
|
return 0;
|
|
}
|
|
|
|
int32_t C4LeagueResponseHeadAuthCheck::getRank(const char *szLeague) const
|
|
{
|
|
for (int32_t i = 0; i < C4NetMaxLeagues; i++)
|
|
if (Leagues[i] == szLeague)
|
|
return Ranks[i];
|
|
return 0;
|
|
}
|
|
|
|
int32_t C4LeagueResponseHeadAuthCheck::getRankSymbol(const char *szLeague) const
|
|
{
|
|
for (int32_t i = 0; i < C4NetMaxLeagues; i++)
|
|
if (Leagues[i] == szLeague)
|
|
return RankSymbols[i];
|
|
return 0;
|
|
}
|
|
|
|
const char *C4LeagueResponseHeadAuthCheck::getProgressData(const char *szLeague) const
|
|
{
|
|
// progress data is the same for all leagues
|
|
return ProgressData.getData();
|
|
}
|
|
|
|
void C4LeagueResponseHeadAuthCheck::CompileFunc(StdCompiler *pComp)
|
|
{
|
|
// Base members
|
|
C4LeagueResponseHead::CompileFunc(pComp);
|
|
|
|
// Leagues, Scores, Ranks
|
|
pComp->Value(mkNamingAdapt(mkArrayAdapt(Leagues, C4NetMaxLeagues, ""), "League"));
|
|
pComp->Value(mkNamingAdapt(mkArrayAdapt(Scores, C4NetMaxLeagues, 0), "Score"));
|
|
pComp->Value(mkNamingAdapt(mkArrayAdapt(Ranks, C4NetMaxLeagues, 0), "Rank"));
|
|
pComp->Value(mkNamingAdapt(mkArrayAdapt(RankSymbols, C4NetMaxLeagues, 0), "RankSymbol"));
|
|
|
|
// Progress data (per scenario; not per league)
|
|
pComp->Value(mkNamingAdapt(mkParAdapt(ProgressData, StdCompiler::RCT_All), "ProgressData", ""));
|
|
|
|
// Clan tag
|
|
pComp->Value(mkNamingAdapt(mkParAdapt(ClanTag, StdCompiler::RCT_All), "ClanTag", ""));
|
|
|
|
}
|
|
|
|
// *** C4LeagueFBIDList
|
|
|
|
void C4LeagueFBIDList::Clear()
|
|
{
|
|
while (pFirst)
|
|
{
|
|
FBIDItem *pDel = pFirst;
|
|
pFirst = pDel->pNext;
|
|
delete pDel;
|
|
}
|
|
}
|
|
|
|
bool C4LeagueFBIDList::FindFBIDByAccount(const char *szAccount, StdStrBuf *pFBIDOut)
|
|
{
|
|
assert(szAccount);
|
|
if (!szAccount) return false;
|
|
for (FBIDItem *pItem = pFirst; pItem; pItem = pItem->pNext)
|
|
if (pItem->Account == szAccount)
|
|
{
|
|
if (pFBIDOut) pFBIDOut->Copy(pItem->FBID);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void C4LeagueFBIDList::RemoveFBIDByAccount(const char *szAccount)
|
|
{
|
|
FBIDItem *pPrev = nullptr, *pItem = pFirst;
|
|
while (pItem)
|
|
{
|
|
// Delete?
|
|
if (pItem->Account == szAccount)
|
|
{
|
|
(pPrev ? pPrev->pNext : pFirst) = pItem->pNext;
|
|
delete pItem;
|
|
return;
|
|
}
|
|
// Next
|
|
pPrev = pItem; pItem = pItem->pNext;
|
|
}
|
|
}
|
|
|
|
void C4LeagueFBIDList::AddFBID(const char *szFBID, const char *szAccount)
|
|
{
|
|
// add new FBID item to head of list
|
|
assert(szFBID); assert(szAccount);
|
|
// remove any existing FBIDs
|
|
RemoveFBIDByAccount(szAccount);
|
|
// add new entry
|
|
FBIDItem *pNewItem = new FBIDItem();
|
|
pNewItem->FBID.Copy(szFBID);
|
|
pNewItem->Account.Copy(szAccount);
|
|
pNewItem->pNext = pFirst;
|
|
pFirst = pNewItem;
|
|
}
|
|
|
|
class DisconnectData
|
|
{
|
|
private:
|
|
C4LeagueFBIDList &rFBIDList;
|
|
const C4ClientPlayerInfos &rPlayerInfos;
|
|
|
|
public:
|
|
DisconnectData(C4LeagueFBIDList &rFBIDList, const C4ClientPlayerInfos &rPlayerInfos)
|
|
: rFBIDList(rFBIDList), rPlayerInfos(rPlayerInfos) {}
|
|
|
|
void CompileFunc(StdCompiler *pComp)
|
|
{
|
|
if (pComp->isDeserializer())
|
|
{
|
|
// compiling not yet needed
|
|
assert(!"DisconnectData::CompileFunc not defined for compiler!");
|
|
}
|
|
else
|
|
{
|
|
// decompiling: Compile all joined and not removed player infos.
|
|
// Compile them even if they're not in the FBID-List, but omit
|
|
// the FBID (used for host message)
|
|
int32_t i=0; C4PlayerInfo *pInfo;
|
|
while ((pInfo = rPlayerInfos.GetPlayerInfo(i++)))
|
|
if (pInfo->IsJoined() && !pInfo->IsRemoved())
|
|
{
|
|
pComp->Name("Player");
|
|
try
|
|
{
|
|
pComp->Value(mkNamingAdapt(mkDecompileAdapt(pInfo->GetID()), "ID"));
|
|
StdCopyStrBuf sFBID;
|
|
if (rFBIDList.FindFBIDByAccount(pInfo->getLeagueAccount(), &sFBID)) pComp->Value(mkNamingAdapt(mkParAdapt(sFBID, StdCompiler::RCT_IdtfAllowEmpty), "FBID"));
|
|
}
|
|
catch (StdCompiler::Exception *)
|
|
{
|
|
pComp->NameEnd();
|
|
throw;
|
|
}
|
|
pComp->NameEnd();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
// *** C4LeagueClient
|
|
|
|
bool C4LeagueClient::Start(const C4Network2Reference &Ref)
|
|
{
|
|
// Create query
|
|
eCurrAction = C4LA_Start;
|
|
C4LeagueRequestHead Head(eCurrAction);
|
|
StdStrBuf QueryText = DecompileToBuf<StdCompilerINIWrite>(
|
|
mkInsertAdapt(
|
|
mkNamingAdapt(Head, "Request"),
|
|
mkNamingAdapt(mkDecompileAdapt(Ref), "Reference"),
|
|
false));
|
|
// Perform query
|
|
return Query(QueryText.getData(), false);
|
|
}
|
|
|
|
bool C4LeagueClient::GetStartReply(StdStrBuf *pMessage, StdStrBuf *pLeague, StdStrBuf *pStreamingAddr, int32_t *pSeed, int32_t *pMaxPlayers)
|
|
{
|
|
if (!isSuccess() || eCurrAction != C4LA_Start) return false;
|
|
// Parse response head
|
|
C4LeagueResponseHeadStart Head;
|
|
if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(Head, "Response"), ResultString, "Start Reply"))
|
|
return false;
|
|
// Get message, league and seed
|
|
if (pMessage)
|
|
pMessage->Copy(Head.getMessage());
|
|
if (pLeague)
|
|
pLeague->Copy(Head.getLeague());
|
|
if (pStreamingAddr)
|
|
pStreamingAddr->Copy(Head.getStreamingAddr());
|
|
if (pSeed && Head.haveSeed())
|
|
*pSeed = Head.getSeed();
|
|
if (pMaxPlayers)
|
|
*pMaxPlayers = Head.getMaxPlayers();
|
|
// No success?
|
|
if (!Head.isSuccess())
|
|
return false;
|
|
// Got no CSID or empty CSID?
|
|
if (!Head.getCSID() || !*Head.getCSID() || *Head.getCSID() == '\0')
|
|
{
|
|
if (pMessage)
|
|
pMessage->Copy(LoadResStr("IDS_LGA_INVALIDRESPONSE3"));
|
|
return false;
|
|
}
|
|
// So save back CSID
|
|
CSID = Head.getCSID();
|
|
return true;
|
|
}
|
|
|
|
bool C4LeagueClient::Update(const C4Network2Reference &Ref)
|
|
{
|
|
assert(CSID.getLength());
|
|
// Create query
|
|
eCurrAction = C4LA_Update;
|
|
C4LeagueRequestHead Head(eCurrAction, CSID.getData());
|
|
StdStrBuf QueryText = DecompileToBuf<StdCompilerINIWrite>(
|
|
mkInsertAdapt(
|
|
mkNamingAdapt(Head, "Request"),
|
|
mkNamingAdapt(mkDecompileAdapt(Ref), "Reference"),
|
|
false));
|
|
// Perform query
|
|
return Query(QueryText.getData(), false);
|
|
}
|
|
|
|
bool C4LeagueClient::GetUpdateReply(StdStrBuf *pMessage, C4ClientPlayerInfos *pPlayerLeagueInfos)
|
|
{
|
|
C4LeagueResponseHeadUpdate Reply;
|
|
if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(Reply, "Response"), ResultString, "Update Reply"))
|
|
return false;
|
|
// Get message
|
|
if (pMessage)
|
|
pMessage->Copy(Reply.getMessage());
|
|
// get plr infos
|
|
if (pPlayerLeagueInfos)
|
|
*pPlayerLeagueInfos = Reply.GetPlrInfos();
|
|
// Success
|
|
return true;
|
|
}
|
|
|
|
bool C4LeagueClient::End(const C4Network2Reference &Ref, const char *szRecordName, const BYTE *pRecordSHA)
|
|
{
|
|
assert(CSID.getLength());
|
|
// Create query
|
|
eCurrAction = C4LA_End;
|
|
C4LeagueRequestHeadEnd Head(eCurrAction, CSID.getData(), szRecordName, pRecordSHA);
|
|
StdStrBuf QueryText = DecompileToBuf<StdCompilerINIWrite>(
|
|
mkInsertAdapt(
|
|
mkNamingAdapt(Head, "Request"),
|
|
mkNamingAdapt(mkDecompileAdapt(Ref), "Reference"),
|
|
false));
|
|
// Perform query
|
|
return Query(QueryText.getData(), false);
|
|
}
|
|
|
|
bool C4LeagueClient::GetEndReply(StdStrBuf *pMessage, C4RoundResultsPlayers *pRoundResults)
|
|
{
|
|
// Parse response head
|
|
C4LeagueResponseHead Head;
|
|
if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(Head, "Response"), ResultString, "End Reply"))
|
|
return false;
|
|
// Get message
|
|
if (pMessage)
|
|
pMessage->Copy(Head.getMessage());
|
|
if (pRoundResults)
|
|
CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(mkNamingAdapt(*pRoundResults, "PlayerInfos"), "Response"), ResultString, "Round Results");
|
|
// Done
|
|
return Head.isSuccess();
|
|
}
|
|
|
|
bool C4LeagueClient::Auth(const C4PlayerInfo &PlrInfo, const char *szAccount, const char *szPassword, const char *szNewAccount, const char *szNewPassword, bool fRememberLogin)
|
|
{
|
|
// Build header
|
|
eCurrAction = C4LA_PlrAuth;
|
|
C4LeagueRequestHead Head(eCurrAction);
|
|
Head.SetAuth(szAccount, szPassword, fRememberLogin);
|
|
if (szNewAccount)
|
|
Head.SetNewAccount(szNewAccount);
|
|
if (szNewPassword)
|
|
Head.SetNewPassword(szNewPassword);
|
|
// Create query
|
|
StdStrBuf QueryText = DecompileToBuf<StdCompilerINIWrite>(
|
|
mkInsertAdapt(
|
|
mkNamingAdapt(Head, "Request"),
|
|
mkNamingAdapt(mkDecompileAdapt(PlrInfo), "PlrInfo"),
|
|
false));
|
|
// Perform query
|
|
return Query(QueryText.getData(), false);
|
|
}
|
|
|
|
bool C4LeagueClient::GetAuthReply(StdStrBuf *pMessage, StdStrBuf *pAUID, StdStrBuf *pAccount, bool *pRegister, StdStrBuf *pLoginToken)
|
|
{
|
|
C4LeagueResponseHead Head;
|
|
if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(Head, "Response"), ResultString, "Auth Reply"))
|
|
return false;
|
|
// Get message & account
|
|
if (pMessage)
|
|
pMessage->Copy(Head.getMessage());
|
|
if (pAccount)
|
|
pAccount->Copy(Head.getAccount());
|
|
if (pRegister)
|
|
*pRegister = Head.isStatusRegister();
|
|
if (pLoginToken)
|
|
pLoginToken->Copy(Head.getLoginToken());
|
|
// No success?
|
|
if (!Head.isSuccess())
|
|
return false;
|
|
// Check AUID
|
|
if (!Head.getAUID() || !*Head.getAUID())
|
|
{
|
|
pMessage->Ref(LoadResStr("IDS_MSG_LEAGUESERVERREPLYWITHOUTA"));
|
|
return false;
|
|
}
|
|
// Success
|
|
if (pAUID)
|
|
pAUID->Copy(Head.getAUID());
|
|
FBIDList.AddFBID(Head.getFBID(), Head.getAccount());
|
|
return true;
|
|
}
|
|
|
|
bool C4LeagueClient::AuthCheck(const C4PlayerInfo &PlrInfo)
|
|
{
|
|
assert(CSID.getLength());
|
|
// Build header
|
|
eCurrAction = C4LA_PlrAuthCheck;
|
|
C4LeagueRequestHead Head(eCurrAction, CSID.getData(), PlrInfo.getAuthID());
|
|
// Create query
|
|
StdStrBuf QueryText = DecompileToBuf<StdCompilerINIWrite>(
|
|
mkInsertAdapt(
|
|
mkNamingAdapt(Head, "Request"),
|
|
mkNamingAdapt(mkDecompileAdapt(PlrInfo), "PlrInfo"),
|
|
false));
|
|
// Perform query
|
|
return Query(QueryText.getData(), false);
|
|
}
|
|
|
|
bool C4LeagueClient::GetAuthCheckReply(StdStrBuf *pMessage, const char *szLeague, C4PlayerInfo *pPlrInfo)
|
|
{
|
|
// Parse response head
|
|
C4LeagueResponseHeadAuthCheck Head;
|
|
if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(Head, "Response"), ResultString, "Auth Check Reply"))
|
|
return false;
|
|
// Get message and additional data
|
|
if (pMessage)
|
|
pMessage->Copy(Head.getMessage());
|
|
if (szLeague && pPlrInfo)
|
|
pPlrInfo->SetLeagueData(Head.getAccount(), Head.getClanTag(), Head.getScore(szLeague), Head.getRank(szLeague), Head.getRankSymbol(szLeague), Head.getProgressData(szLeague));
|
|
return Head.isSuccess();
|
|
}
|
|
|
|
bool C4LeagueClient::ReportDisconnect(const C4ClientPlayerInfos &rFeedbackClient, C4LeagueDisconnectReason eReason)
|
|
{
|
|
// Build header
|
|
eCurrAction = C4LA_ReportDisconnect;
|
|
C4LeagueReportDisconnectHead Head(CSID.getData(), eReason);
|
|
// Create query
|
|
StdStrBuf QueryText = DecompileToBuf<StdCompilerINIWrite>(
|
|
mkInsertAdapt(
|
|
mkNamingAdapt(Head, "Request"),
|
|
mkNamingAdapt(DisconnectData(FBIDList, rFeedbackClient), "PlayerInfos"),
|
|
false));
|
|
// Perform query
|
|
return Query(QueryText.getData(), false);
|
|
}
|
|
|
|
bool C4LeagueClient::GetReportDisconnectReply(StdStrBuf *pMessage)
|
|
{
|
|
// Parse response head
|
|
C4LeagueResponseHead Head;
|
|
if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(Head, "Response"), ResultString, "Report Disconnect"))
|
|
return false;
|
|
// Get message
|
|
if (pMessage)
|
|
pMessage->Copy(Head.getMessage());
|
|
// Done
|
|
return Head.isSuccess();
|
|
}
|
|
|
|
// *** C4LeagueSignupDialog
|
|
|
|
C4LeagueSignupDialog::C4LeagueSignupDialog(const char *szPlayerName, const char *szLeagueName, const char *szLeagueServerName, const char *szAccountPref, const char *szPassPref, bool fWarnThirdParty, bool fRegister, bool fRememberLogin)
|
|
: C4GUI::Dialog(C4GUI_MessageDlgWdt, 100 /* will be resized as needed */, FormatString(LoadResStr("IDS_DLG_LEAGUESIGNUPON"), szLeagueServerName).getData(), false), strPlayerName(szPlayerName), pChkRememberLogin(nullptr)
|
|
{
|
|
// get positions
|
|
C4GUI::ComponentAligner caMain(GetClientRect(), C4GUI_DefDlgIndent, C4GUI_DefDlgIndent, true);
|
|
// place icon
|
|
C4Rect rcIcon = caMain.GetFromLeft(C4GUI_IconWdt); rcIcon.Hgt = C4GUI_IconHgt;
|
|
C4GUI::Icon *pIcon = new C4GUI::Icon(rcIcon, C4GUI::Ico_Ex_League); AddElement(pIcon);
|
|
caMain.GetFromRight(C4GUI_IconWdt/2);
|
|
// place message label
|
|
// use text with line breaks
|
|
StdStrBuf sMsg, sMsgBroken;
|
|
if (fRegister)
|
|
sMsg.Format(LoadResStr("IDS_MSG_LEAGUE_REGISTRATION"), szPlayerName);
|
|
else
|
|
sMsg.Format(LoadResStr("IDS_MSG_PASSWORDFORPLAYER"), szPlayerName);
|
|
int32_t iLabelHgt = ::GraphicsResource.TextFont.BreakMessage(sMsg.getData(), caMain.GetInnerWidth(), &sMsgBroken, true);
|
|
C4GUI::Label *pLblMessage = new C4GUI::Label(sMsgBroken.getData(), caMain.GetFromTop(iLabelHgt), ALeft, C4GUI_MessageFontClr, &::GraphicsResource.TextFont);
|
|
AddElement(pLblMessage);
|
|
// registering and no account pref available
|
|
if (fRegister && (!szAccountPref || !szAccountPref[0]))
|
|
// use player name as default for league name
|
|
szAccountPref = szPlayerName;
|
|
// place username input box
|
|
bool fSideEdits=true; int iCtrlHeight;
|
|
StdStrBuf sAccountTxt; sAccountTxt.Copy(LoadResStr("IDS_CTL_LEAGUE_ACCOUNT"));
|
|
C4GUI::LabeledEdit::GetControlSize(nullptr, &iCtrlHeight, sAccountTxt.getData(), nullptr, fSideEdits);
|
|
AddElement(pEdtAccount = new C4GUI::LabeledEdit(caMain.GetFromTop(iCtrlHeight), sAccountTxt.getData(), fSideEdits, szAccountPref));
|
|
// registering? Make password field optional
|
|
if (fRegister)
|
|
{
|
|
// place the checkbox
|
|
const char *szChkPasswordCaption = LoadResStr("IDS_CTL_LEAGUE_CHK_PLRPW");
|
|
C4GUI::CheckBox::GetStandardCheckBoxSize(nullptr, &iCtrlHeight, szChkPasswordCaption, nullptr);
|
|
AddElement(pChkPassword = new C4GUI::CheckBox(caMain.GetFromTop(iCtrlHeight), szChkPasswordCaption, false));
|
|
pChkPassword->SetOnChecked(new C4GUI::CallbackHandlerNoPar<C4LeagueSignupDialog>(this, &C4LeagueSignupDialog::OnChkPassword));
|
|
pChkPassword->SetToolTip(LoadResStr("IDS_DESC_LEAGUECHECKPASSWORD"));
|
|
// place password edit boxes
|
|
C4GUI::ComponentAligner caTemp = caMain;
|
|
const char *szEdtPassCaption = LoadResStr("IDS_CTL_LEAGUE_PLRPW");
|
|
const char *szEdtPass2Caption = LoadResStr("IDS_CTL_LEAGUE_PLRPW2");
|
|
C4GUI::LabeledEdit::GetControlSize(nullptr, &iCtrlHeight, szEdtPassCaption, nullptr, fSideEdits);
|
|
AddElement(pEdtPass = new C4GUI::LabeledEdit(caTemp.GetFromTop(iCtrlHeight), szEdtPassCaption, fSideEdits, szPassPref));
|
|
AddElement(pEdtPass2 = new C4GUI::LabeledEdit(caTemp.GetFromTop(iCtrlHeight), szEdtPass2Caption, fSideEdits, szPassPref));
|
|
// hide them
|
|
pEdtPass->SetVisibility(false);
|
|
pEdtPass2->SetVisibility(false);
|
|
// save how much to move the controls later
|
|
iEdtPassSpace = caTemp.GetHeight() - caMain.GetHeight();
|
|
}
|
|
else
|
|
{
|
|
// No password checkbox
|
|
pChkPassword = nullptr;
|
|
// But a password edit box
|
|
const char *szEdtPassCaption = LoadResStr("IDS_CTL_LEAGUE_PLRPW");
|
|
C4GUI::LabeledEdit::GetControlSize(nullptr, &iCtrlHeight, szEdtPassCaption, nullptr, fSideEdits);
|
|
AddElement(pEdtPass = new C4GUI::LabeledEdit(caMain.GetFromTop(iCtrlHeight), szEdtPassCaption, fSideEdits, szPassPref));
|
|
// No second password edit box
|
|
pEdtPass2 = nullptr;
|
|
// remember login-checkbox
|
|
const char *szRememberPasswordCaption = LoadResStr("IDS_CTL_LEAGUE_CHK_REMEMBERLOGIN");
|
|
C4GUI::CheckBox::GetStandardCheckBoxSize(nullptr, &iCtrlHeight, szRememberPasswordCaption, nullptr);
|
|
AddElement(pChkRememberLogin = new C4GUI::CheckBox(caMain.GetFromTop(iCtrlHeight), szRememberPasswordCaption, fRememberLogin));
|
|
pChkRememberLogin->SetToolTip(LoadResStr("IDS_DESC_REMEMBERLOGIN"));
|
|
}
|
|
// Set password box options
|
|
pEdtPass->GetEdit()->SetPasswordMask('*');
|
|
if (pEdtPass2)
|
|
{
|
|
pEdtPass2->GetEdit()->SetPasswordMask('*');
|
|
}
|
|
// place confirmation buttons
|
|
C4GUI::ComponentAligner caButtonArea(caMain.GetFromTop(C4GUI_ButtonAreaHgt), 0,0);
|
|
C4Rect rcBtn = caButtonArea.GetCentered(2*C4GUI_DefButton2Wdt+C4GUI_DefButton2HSpace, C4GUI_ButtonHgt);
|
|
rcBtn.Wdt = C4GUI_DefButton2Wdt;
|
|
pBtnOK = new C4GUI::OKButton(rcBtn);
|
|
AddElement(pBtnOK);
|
|
rcBtn.x += C4GUI_DefButton2Wdt+C4GUI_DefButton2HSpace;
|
|
pBtnAbort = new C4GUI::CancelButton(rcBtn);
|
|
AddElement(pBtnAbort);
|
|
// resize to actually needed size
|
|
SetClientSize(GetClientRect().Wdt, GetClientRect().Hgt - caMain.GetHeight());
|
|
// initial focus
|
|
SetFocus(fRegister ? pEdtAccount->GetEdit() : pEdtPass->GetEdit(), false);
|
|
}
|
|
|
|
void C4LeagueSignupDialog::UserClose(bool fOK)
|
|
{
|
|
// Abort? That's always okay
|
|
if (!fOK)
|
|
{
|
|
Dialog::UserClose(fOK);
|
|
::pGUI->ShowMessageModal(FormatString(LoadResStr("IDS_MSG_LEAGUESIGNUPCANCELLED"), strPlayerName.getData()).getData(), LoadResStr("IDS_DLG_LEAGUESIGNUP"), C4GUI::MessageDialog::btnOK, C4GUI::Ico_Notify);
|
|
return;
|
|
}
|
|
// Check for empty account name
|
|
const char *szAccount = pEdtAccount->GetText();
|
|
if (!szAccount || !*szAccount)
|
|
{
|
|
SetFocus(pEdtAccount->GetEdit(), false);
|
|
::pGUI->ShowMessageModal(LoadResStr("IDS_MSG_LEAGUEMISSINGUSERNAME"), LoadResStr("IDS_DLG_INVALIDENTRY"), C4GUI::MessageDialog::btnOK, C4GUI::Ico_Error);
|
|
return;
|
|
}
|
|
// Check for valid username if this is registration
|
|
if (pEdtPass2)
|
|
{
|
|
// Username contains invalid characters
|
|
if (SCharCountEx(szAccount, C4League_Name_Valid_Characters) != SLen(szAccount))
|
|
{
|
|
SetFocus(pEdtAccount->GetEdit(), false);
|
|
::pGUI->ShowMessageModal(LoadResStr("IDS_MSG_LEAGUEINVALIDUSERNAME"), LoadResStr("IDS_DLG_INVALIDENTRY"), C4GUI::MessageDialog::btnOK, C4GUI::Ico_Error);
|
|
return;
|
|
}
|
|
// Username is too short
|
|
if (SLen(szAccount) < 3)
|
|
{
|
|
SetFocus(pEdtAccount->GetEdit(), false);
|
|
::pGUI->ShowMessageModal(LoadResStr("IDS_MSG_LEAGUEUSERNAMETOOSHORT"), LoadResStr("IDS_DLG_INVALIDENTRY"), C4GUI::MessageDialog::btnOK, C4GUI::Ico_Error);
|
|
return;
|
|
}
|
|
}
|
|
// Check password
|
|
if (!pChkPassword || pChkPassword->GetChecked())
|
|
{
|
|
// Check for empty password
|
|
const char *szPassword = pEdtPass->GetText();
|
|
if (!szPassword || !*szPassword)
|
|
{
|
|
SetFocus(pEdtPass->GetEdit(), false);
|
|
::pGUI->ShowMessageModal(LoadResStr("IDS_MSG_LEAGUEMISSINGPASSWORD"), LoadResStr("IDS_DLG_INVALIDENTRY"), C4GUI::MessageDialog::btnOK, C4GUI::Ico_Error);
|
|
return;
|
|
}
|
|
// Check second password
|
|
if (pEdtPass2 && !SEqual(szPassword, pEdtPass2->GetText()))
|
|
{
|
|
SetFocus(pEdtPass2->GetEdit(), false);
|
|
pEdtPass2->GetEdit()->SetText("", false);
|
|
::pGUI->ShowMessageModal(LoadResStr("IDS_MSG_LEAGUEMISMATCHPASSWORD"), LoadResStr("IDS_DLG_INVALIDENTRY"), C4GUI::MessageDialog::btnOK, C4GUI::Ico_Error);
|
|
return;
|
|
}
|
|
}
|
|
// Okay then
|
|
Dialog::UserClose(fOK);
|
|
}
|
|
|
|
bool C4LeagueSignupDialog::ShowModal(const char *szPlayerName, const char *szLeagueName, const char *szLeagueServerName, StdStrBuf *psCUID, StdStrBuf *psPass, bool fWarnThirdParty, bool fRegister, bool *pfRememberLogin)
|
|
{
|
|
// show league signup dlg modally; return whether user pressed OK and change user and pass buffers in that case
|
|
assert(psCUID); assert(psPass);
|
|
if (!psCUID || !psPass) return false;
|
|
C4LeagueSignupDialog *pDlg = new C4LeagueSignupDialog(szPlayerName, szLeagueName, szLeagueServerName, psCUID->getData(), psPass->getData(), fWarnThirdParty, fRegister, *pfRememberLogin);
|
|
bool fResult = ::pGUI->ShowModalDlg(pDlg, false);
|
|
if (fResult)
|
|
{
|
|
psCUID->Copy(pDlg->GetAccount());
|
|
if (pDlg->HasPass())
|
|
psPass->Copy(pDlg->GetPass());
|
|
else
|
|
psPass->Clear();
|
|
*pfRememberLogin = pDlg->GetRememberLogin();
|
|
}
|
|
delete pDlg;
|
|
return fResult;
|
|
}
|
|
|
|
void C4LeagueSignupDialog::OnChkPassword()
|
|
{
|
|
if (!pChkPassword) return;
|
|
// Show password elements?
|
|
if (!pChkPassword->GetChecked())
|
|
{
|
|
// Enlarge dialog
|
|
C4Rect bnds = GetClientRect();
|
|
SetClientSize(bnds.Wdt, bnds.Hgt + iEdtPassSpace);
|
|
// Show edit controls
|
|
pEdtPass->SetVisibility(false);
|
|
pEdtPass2->SetVisibility(false);
|
|
// Move controls down
|
|
bnds = pBtnOK->GetBounds();
|
|
pBtnOK->SetPos(bnds.x, bnds.y + iEdtPassSpace);
|
|
bnds = pBtnAbort->GetBounds();
|
|
pBtnAbort->SetPos(bnds.x, bnds.y + iEdtPassSpace);
|
|
}
|
|
else
|
|
{
|
|
// Shrink dialog
|
|
C4Rect bnds = GetClientRect();
|
|
SetClientSize(bnds.Wdt, bnds.Hgt - iEdtPassSpace);
|
|
// Hide edit controls
|
|
pEdtPass->SetVisibility(true);
|
|
pEdtPass2->SetVisibility(true);
|
|
// Move controls down
|
|
bnds = pBtnOK->GetBounds();
|
|
pBtnOK->SetPos(bnds.x, bnds.y - iEdtPassSpace);
|
|
bnds = pBtnAbort->GetBounds();
|
|
pBtnAbort->SetPos(bnds.x, bnds.y - iEdtPassSpace);
|
|
}
|
|
}
|